dsh-vidi 0.7.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +75 -7
- package/lib/index.mjs +93 -20
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -255,6 +255,23 @@ const Icon = {
|
|
|
255
255
|
strokeWidth: 2.6,
|
|
256
256
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M5 12.5l4.5 4.5L19 7.5" })
|
|
257
257
|
}),
|
|
258
|
+
sidePanel: () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
259
|
+
viewBox: "0 0 24 24",
|
|
260
|
+
width: "18",
|
|
261
|
+
height: "18",
|
|
262
|
+
fill: "none",
|
|
263
|
+
stroke: "currentColor",
|
|
264
|
+
strokeWidth: "1.8",
|
|
265
|
+
strokeLinecap: "round",
|
|
266
|
+
strokeLinejoin: "round",
|
|
267
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
268
|
+
x: "3",
|
|
269
|
+
y: "4",
|
|
270
|
+
width: "18",
|
|
271
|
+
height: "16",
|
|
272
|
+
rx: "2.5"
|
|
273
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M15 4v16" })]
|
|
274
|
+
}),
|
|
258
275
|
plus: () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
259
276
|
viewBox: "0 0 24 24",
|
|
260
277
|
width: "14",
|
|
@@ -844,7 +861,9 @@ function Pick(props) {
|
|
|
844
861
|
if (!list.current?.contains(e.target) && !btn.current?.contains(e.target)) close(false);
|
|
845
862
|
};
|
|
846
863
|
const gone = (e) => {
|
|
847
|
-
|
|
864
|
+
const t = e.target;
|
|
865
|
+
if (list.current?.contains(t)) return;
|
|
866
|
+
if (e.type === "resize" || t === document || t === document.documentElement || t instanceof Node && btn.current && t.contains(btn.current)) close(false);
|
|
848
867
|
};
|
|
849
868
|
document.addEventListener("pointerdown", away, true);
|
|
850
869
|
window.addEventListener("resize", gone);
|
|
@@ -35239,6 +35258,29 @@ function LiveView(props) {
|
|
|
35239
35258
|
const [subStyle, setSubStyle] = (0, react.useState)(() => loadStyle(lines));
|
|
35240
35259
|
const [delay, setDelay] = (0, react.useState)(8);
|
|
35241
35260
|
const [theater, setTheater] = (0, react.useState)(false);
|
|
35261
|
+
const [sideOpen, setSideOpenRaw] = (0, react.useState)(() => store.get("sideOpen", true));
|
|
35262
|
+
const setSideOpen = (v) => {
|
|
35263
|
+
setSideOpenRaw(v);
|
|
35264
|
+
store.set("sideOpen", v);
|
|
35265
|
+
};
|
|
35266
|
+
const [sideW, setSideWRaw] = (0, react.useState)(() => store.get("sideW", 380));
|
|
35267
|
+
const mainRef = (0, react.useRef)(null);
|
|
35268
|
+
const dragSide = (e) => {
|
|
35269
|
+
e.preventDefault();
|
|
35270
|
+
const box = mainRef.current?.getBoundingClientRect();
|
|
35271
|
+
if (!box) return;
|
|
35272
|
+
const move = (ev) => setSideWRaw(Math.round(Math.min(box.width * .6, Math.max(280, box.right - ev.clientX - 8))));
|
|
35273
|
+
const up = () => {
|
|
35274
|
+
window.removeEventListener("pointermove", move);
|
|
35275
|
+
window.removeEventListener("pointerup", up);
|
|
35276
|
+
setSideWRaw((w) => {
|
|
35277
|
+
store.set("sideW", w);
|
|
35278
|
+
return w;
|
|
35279
|
+
});
|
|
35280
|
+
};
|
|
35281
|
+
window.addEventListener("pointermove", move);
|
|
35282
|
+
window.addEventListener("pointerup", up);
|
|
35283
|
+
};
|
|
35242
35284
|
const [tab, setTab] = (0, react.useState)("transcript");
|
|
35243
35285
|
const [query, setQuery] = (0, react.useState)("");
|
|
35244
35286
|
const [trMode, setTrMode] = (0, react.useState)("lines");
|
|
@@ -35951,7 +35993,8 @@ function LiveView(props) {
|
|
|
35951
35993
|
})]
|
|
35952
35994
|
}) : null;
|
|
35953
35995
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
35954
|
-
className: `dshl ${theater && !activeBench ? "theater" : ""} ${pageMode ? "page" : ""} ${activeBench ? "bench" : ""}`,
|
|
35996
|
+
className: `dshl ${theater && !activeBench ? "theater" : ""} ${pageMode ? "page" : ""} ${activeBench ? "bench" : ""} ${pageMode && !sideOpen && !activeBench ? "noside" : ""}`,
|
|
35997
|
+
style: pageMode ? { ["--side-w"]: `${sideW}px` } : void 0,
|
|
35955
35998
|
"data-dsh-own-drop": "dsh-vidi",
|
|
35956
35999
|
onDragEnter: (e) => {
|
|
35957
36000
|
if (e.dataTransfer.types.includes("Files")) e.stopPropagation();
|
|
@@ -36053,6 +36096,13 @@ function LiveView(props) {
|
|
|
36053
36096
|
open: settingsOpen,
|
|
36054
36097
|
onOpen: setSettingsOpen,
|
|
36055
36098
|
t
|
|
36099
|
+
}),
|
|
36100
|
+
pageMode && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
36101
|
+
className: `dshl-ctl icon ${sideOpen ? "on" : ""}`,
|
|
36102
|
+
title: t(sideOpen ? "side.hide" : "side.show"),
|
|
36103
|
+
"aria-pressed": sideOpen,
|
|
36104
|
+
onClick: () => setSideOpen(!sideOpen),
|
|
36105
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon.sidePanel, {})
|
|
36056
36106
|
})
|
|
36057
36107
|
]
|
|
36058
36108
|
})
|
|
@@ -36082,6 +36132,7 @@ function LiveView(props) {
|
|
|
36082
36132
|
children: emptyBox
|
|
36083
36133
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("main", {
|
|
36084
36134
|
className: "dshl-main",
|
|
36135
|
+
ref: mainRef,
|
|
36085
36136
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
36086
36137
|
className: "dshl-col",
|
|
36087
36138
|
children: !hasMain || !job ? emptyBox : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
@@ -36233,9 +36284,13 @@ function LiveView(props) {
|
|
|
36233
36284
|
children: transcriptBox
|
|
36234
36285
|
})
|
|
36235
36286
|
] })
|
|
36236
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.
|
|
36287
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
|
|
36237
36288
|
className: "dshl-side",
|
|
36238
|
-
children:
|
|
36289
|
+
children: [pageMode && !activeBench && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
36290
|
+
className: "dshl-resize",
|
|
36291
|
+
onPointerDown: dragSide,
|
|
36292
|
+
title: t("side.resize")
|
|
36293
|
+
}), activeBench && view ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
36239
36294
|
className: "dshl-benchbox",
|
|
36240
36295
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Guard, {
|
|
36241
36296
|
name: `workbench ${activeBench.id}`,
|
|
@@ -36252,7 +36307,7 @@ function LiveView(props) {
|
|
|
36252
36307
|
hasVideo: !!job,
|
|
36253
36308
|
jobsDir,
|
|
36254
36309
|
t
|
|
36255
|
-
}) : transcriptBox
|
|
36310
|
+
}) : transcriptBox]
|
|
36256
36311
|
})]
|
|
36257
36312
|
}),
|
|
36258
36313
|
dragging && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -36823,6 +36878,9 @@ const en = {
|
|
|
36823
36878
|
"rs.overlay": "Subtitles for OBS (Browser Source)",
|
|
36824
36879
|
"rs.overlay.d": "Add a Browser Source in OBS with this address (1920×1080): the newest subtitles on a transparent background, over your camera or any picture. Add ?lines=zh,src&size=4.5 to choose lines and size.",
|
|
36825
36880
|
"rs.copy": "Copy",
|
|
36881
|
+
"side.hide": "Hide the chat",
|
|
36882
|
+
"side.show": "Show the chat",
|
|
36883
|
+
"side.resize": "Drag to change the width",
|
|
36826
36884
|
"rs.openPage": "Open",
|
|
36827
36885
|
"rs.screen": "Subtitle screen (16:9)",
|
|
36828
36886
|
"rs.screen.d": "Only the subtitles, large, for a big screen or projector: open it in any browser on this network and go full screen (on this computer: {local}). Add ?theme=light for a light screen; it can also be an OBS source of its own.",
|
|
@@ -37204,6 +37262,9 @@ const zh = {
|
|
|
37204
37262
|
"rs.overlay": "OBS 字幕叠加(浏览器源)",
|
|
37205
37263
|
"rs.overlay.d": "在 OBS 里加一个「浏览器源」,填这个地址(1920×1080):透明背景上只有最新的字幕,叠在摄像头或任何画面上。地址后加 ?lines=zh,src&size=4.5 可以选行和字号。",
|
|
37206
37264
|
"rs.copy": "复制",
|
|
37265
|
+
"side.hide": "收起对话",
|
|
37266
|
+
"side.show": "展开对话",
|
|
37267
|
+
"side.resize": "拖动调整宽度",
|
|
37207
37268
|
"rs.openPage": "打开",
|
|
37208
37269
|
"rs.screen": "字幕大屏(16:9)",
|
|
37209
37270
|
"rs.screen.d": "只有字幕、字很大,适合大屏和投影:在同一网络里任何浏览器打开、全屏即可(本机用 {local})。地址后加 ?theme=light 换浅色;也可以作为 OBS 的一个独立画面来源。",
|
|
@@ -37736,12 +37797,18 @@ html[data-platform='darwin'] .dshl-panelwrap .dshl.page .dshl-top { padding-top:
|
|
|
37736
37797
|
html[data-platform='darwin'] .dshl.page .dshl-top { -webkit-app-region: drag; app-region: drag; }
|
|
37737
37798
|
html[data-platform='darwin'] .dshl.page .dshl-top :is(button, input, label, a, .dshl-select, .dshl-pick) { -webkit-app-region: no-drag; app-region: no-drag; }
|
|
37738
37799
|
.dshl.page { display: flex; flex-direction: column; height: 100%; overflow: hidden; }
|
|
37739
|
-
.dshl.page .dshl-main { flex: 1; min-height: 0; grid-template-columns: minmax(0, 1fr) clamp(320px, 30vw, 420px); align-items: stretch; padding-bottom: 16px; }
|
|
37800
|
+
.dshl.page .dshl-main { flex: 1; min-height: 0; grid-template-columns: minmax(0, 1fr) var(--side-w, clamp(320px, 30vw, 420px)); align-items: stretch; padding-bottom: 16px; }
|
|
37740
37801
|
/* Video page: player on top, then the status row, then subtitles/glossary filling what is left; chat on the right. */
|
|
37741
37802
|
.dshl.page .dshl-col { display: flex; flex-direction: column; min-height: 0; container-type: size; }
|
|
37742
37803
|
.dshl.page .dshl-col > .dshl-drop { flex: 1; aspect-ratio: auto; min-height: 0; }
|
|
37743
37804
|
.dshl.page .dshl-stage { flex: none; width: 100%; max-height: max(240px, calc(100cqh - 270px)); }
|
|
37744
|
-
.dshl.page .dshl-side { position:
|
|
37805
|
+
.dshl.page .dshl-side { position: relative; top: auto; height: 100%; max-height: none; }
|
|
37806
|
+
.dshl.page.noside .dshl-main { grid-template-columns: minmax(0, 1fr); }
|
|
37807
|
+
.dshl.page.noside .dshl-side { display: none; }
|
|
37808
|
+
/* Drag the chat panel's left edge to change its width; the player and subtitles take the rest. */
|
|
37809
|
+
.dshl-resize { position: absolute; top: 0; bottom: 0; left: 0; z-index: 3; width: 8px; cursor: col-resize; }
|
|
37810
|
+
.dshl-resize::after { content: ''; position: absolute; top: 50%; left: 2px; width: 3px; height: 36px; border-radius: 2px; background: var(--line); transform: translateY(-50%); opacity: 0; transition: opacity .15s; }
|
|
37811
|
+
.dshl-resize:hover::after { opacity: 1; }
|
|
37745
37812
|
/* Workbench open: the video and its subtitles keep a column on the left; the bench takes the rest. */
|
|
37746
37813
|
.dshl.page.bench .dshl-main { grid-template-columns: clamp(340px, 32vw, 520px) minmax(0, 1fr); }
|
|
37747
37814
|
.dshl-lag { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; margin: -2px 0 8px; padding: 7px 12px; border-radius: 8px; background: var(--l1, rgba(0,0,0,.04)); color: var(--muted); font-size: 12.5px; }
|
|
@@ -37762,6 +37829,7 @@ html[data-platform='darwin'] .dshl.page .dshl-top :is(button, input, label, a, .
|
|
|
37762
37829
|
.dshl.page.theater .dshl-stage { max-height: 100cqh; }
|
|
37763
37830
|
@media (max-width: 1000px) {
|
|
37764
37831
|
.dshl.page { display: block; overflow: auto; }
|
|
37832
|
+
.dshl-resize { display: none; }
|
|
37765
37833
|
.dshl.page.bench .dshl-main { grid-template-columns: minmax(0, 1fr); }
|
|
37766
37834
|
.dshl.page.bench .dshl-side { height: auto; min-height: 0; }
|
|
37767
37835
|
.dshl-benchbox { overflow: visible; }
|
package/lib/index.mjs
CHANGED
|
@@ -4725,6 +4725,8 @@ var Stream = class {
|
|
|
4725
4725
|
cjk;
|
|
4726
4726
|
open = [];
|
|
4727
4727
|
done = [];
|
|
4728
|
+
/** When the last piece arrived (wall clock, ms). */
|
|
4729
|
+
lastWall = 0;
|
|
4728
4730
|
constructor(cjk) {
|
|
4729
4731
|
this.cjk = cjk;
|
|
4730
4732
|
}
|
|
@@ -4754,18 +4756,51 @@ var Stream = class {
|
|
|
4754
4756
|
this.push(p);
|
|
4755
4757
|
}
|
|
4756
4758
|
push(p) {
|
|
4759
|
+
this.lastWall = Date.now();
|
|
4757
4760
|
this.open.push(p);
|
|
4758
4761
|
const text = this.text.trim();
|
|
4759
4762
|
const len = this.cjk ? Array.from(text).length : text.length;
|
|
4760
4763
|
const soft = this.cjk ? SOFT_CJK : SOFT_LATIN, hard = this.cjk ? HARD_CJK : HARD_LATIN;
|
|
4761
4764
|
if (END.test(text) || len >= soft && /[,,、;;::]\s*$/.test(text) || len >= hard) this.close();
|
|
4762
4765
|
}
|
|
4766
|
+
/** End the sentence at its last comma or full stop (the rest starts the next one); false when it has none. */
|
|
4767
|
+
closeAtPause() {
|
|
4768
|
+
const text = this.text;
|
|
4769
|
+
let cut = -1;
|
|
4770
|
+
for (let i = text.length - 2; i > 0; i--) if (/[,。、;:!?,;:!?]/.test(text[i])) {
|
|
4771
|
+
cut = i + 1;
|
|
4772
|
+
break;
|
|
4773
|
+
}
|
|
4774
|
+
if (cut <= 0) return false;
|
|
4775
|
+
const head = [], tail = [];
|
|
4776
|
+
let n = 0;
|
|
4777
|
+
for (const p of this.open) {
|
|
4778
|
+
if (n + p.text.length <= cut) head.push(p);
|
|
4779
|
+
else if (n >= cut) tail.push(p);
|
|
4780
|
+
else {
|
|
4781
|
+
head.push({
|
|
4782
|
+
t: p.t,
|
|
4783
|
+
text: p.text.slice(0, cut - n)
|
|
4784
|
+
});
|
|
4785
|
+
tail.push({
|
|
4786
|
+
t: p.t,
|
|
4787
|
+
text: p.text.slice(cut - n)
|
|
4788
|
+
});
|
|
4789
|
+
}
|
|
4790
|
+
n += p.text.length;
|
|
4791
|
+
}
|
|
4792
|
+
this.open = head;
|
|
4793
|
+
this.close();
|
|
4794
|
+
this.open = tail;
|
|
4795
|
+
return true;
|
|
4796
|
+
}
|
|
4763
4797
|
close() {
|
|
4764
4798
|
const text = this.text.trim();
|
|
4765
4799
|
if (text) this.done.push({
|
|
4766
4800
|
text,
|
|
4767
4801
|
start: this.open[0].t,
|
|
4768
|
-
end: this.open.at(-1).t
|
|
4802
|
+
end: this.open.at(-1).t,
|
|
4803
|
+
wall: Date.now()
|
|
4769
4804
|
});
|
|
4770
4805
|
this.open = [];
|
|
4771
4806
|
}
|
|
@@ -4791,6 +4826,11 @@ var OpenAITranslateProvider = class {
|
|
|
4791
4826
|
reconnects = 0;
|
|
4792
4827
|
/** Audio sent so far (s); a session's positions count from `base`. */
|
|
4793
4828
|
sent = 0;
|
|
4829
|
+
/** Audio waiting to be sent: the service listens in real time, so bursts (a recording read from its
|
|
4830
|
+
* start) go up at most a little faster than that. */
|
|
4831
|
+
queue = [];
|
|
4832
|
+
queued = 0;
|
|
4833
|
+
drain;
|
|
4794
4834
|
base = 0;
|
|
4795
4835
|
src = new Stream(false);
|
|
4796
4836
|
tr;
|
|
@@ -4801,6 +4841,8 @@ var OpenAITranslateProvider = class {
|
|
|
4801
4841
|
seq = 0;
|
|
4802
4842
|
/** End of the last finished line (s): everything before it is on screen. */
|
|
4803
4843
|
readyAt = 0;
|
|
4844
|
+
/** Where the last line ends on screen (lines never overlap). */
|
|
4845
|
+
lastEnd = 0;
|
|
4804
4846
|
/** Arrival delay of text after its audio position, for the page (s). */
|
|
4805
4847
|
lag = 0;
|
|
4806
4848
|
connectedAt = 0;
|
|
@@ -4821,6 +4863,7 @@ var OpenAITranslateProvider = class {
|
|
|
4821
4863
|
this.onError = onError;
|
|
4822
4864
|
await this.connect();
|
|
4823
4865
|
this.quiet = setInterval(() => this.settle(), 400);
|
|
4866
|
+
this.drain = setInterval(() => this.sendDue(), 100);
|
|
4824
4867
|
}
|
|
4825
4868
|
connect() {
|
|
4826
4869
|
return new Promise((resolve, reject) => {
|
|
@@ -4874,35 +4917,52 @@ var OpenAITranslateProvider = class {
|
|
|
4874
4917
|
}
|
|
4875
4918
|
push(pcm) {
|
|
4876
4919
|
if (this.stopped) return;
|
|
4877
|
-
this.
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4920
|
+
this.queue.push(Buffer.from(pcm));
|
|
4921
|
+
this.queued += pcm.length / BYTES_PER_SEC;
|
|
4922
|
+
this.sendDue();
|
|
4923
|
+
}
|
|
4924
|
+
/** Send queued audio, everything including silence, no faster than 1.5× real time since the session began. */
|
|
4925
|
+
sendDue() {
|
|
4926
|
+
if (this.ws?.readyState !== WebSocket.OPEN) return;
|
|
4927
|
+
const allowed = this.base + (Date.now() - this.connectedAt) / 1e3 * 1.5 + 2;
|
|
4928
|
+
while (this.queue.length && this.sent < allowed) {
|
|
4929
|
+
const pcm = this.queue.shift();
|
|
4930
|
+
this.queued -= pcm.length / BYTES_PER_SEC;
|
|
4931
|
+
this.send({
|
|
4932
|
+
type: "session.input_audio_buffer.append",
|
|
4933
|
+
audio: this.up.push(pcm).toString("base64")
|
|
4934
|
+
});
|
|
4935
|
+
this.sent += pcm.length / BYTES_PER_SEC;
|
|
4936
|
+
}
|
|
4882
4937
|
}
|
|
4883
|
-
/** Audio
|
|
4938
|
+
/** Audio received but not yet in a finished line (queued, or sent and still being worked on). */
|
|
4884
4939
|
backlog() {
|
|
4885
|
-
return Math.max(0, this.sent - this.readyAt);
|
|
4940
|
+
return this.queued + Math.max(0, this.sent - this.readyAt);
|
|
4886
4941
|
}
|
|
4887
4942
|
onEvent(ev) {
|
|
4888
4943
|
if (ev.type === "session.input_transcript.delta" || ev.type === "session.output_transcript.delta") {
|
|
4889
4944
|
const t = this.base + (ev.elapsed_ms ?? 0) / 1e3;
|
|
4890
|
-
const
|
|
4891
|
-
this.lag = this.lag ? this.lag * .9 +
|
|
4945
|
+
const behind = Math.max(0, this.sent - t);
|
|
4946
|
+
this.lag = this.lag ? this.lag * .9 + behind * .1 : behind;
|
|
4892
4947
|
const piece = {
|
|
4893
4948
|
t,
|
|
4894
4949
|
text: String(ev.delta ?? "")
|
|
4895
4950
|
};
|
|
4896
4951
|
if (ev.type === "session.input_transcript.delta") this.src.add(piece);
|
|
4897
|
-
else
|
|
4952
|
+
else {
|
|
4953
|
+
const next = this.src.done[this.srcUsed];
|
|
4954
|
+
if (next && this.tr.text.trim() && t - next.end >= 1.2) {
|
|
4955
|
+
if (!this.tr.closeAtPause() && t - next.end >= 3) this.tr.close();
|
|
4956
|
+
}
|
|
4957
|
+
this.tr.add(piece);
|
|
4958
|
+
}
|
|
4898
4959
|
this.pair(false);
|
|
4899
4960
|
this.draft();
|
|
4900
4961
|
} else if (ev.type === "error") this.onError?.(/* @__PURE__ */ new Error(`OpenAI: ${ev.error?.message ?? "error"}`));
|
|
4901
4962
|
}
|
|
4902
4963
|
/** Close streams that went quiet (a pause without punctuation), then pair what is complete. */
|
|
4903
4964
|
settle() {
|
|
4904
|
-
const
|
|
4905
|
-
for (const s of [this.src, this.tr]) if (s.last !== void 0 && now - s.last > QUIET_SEC + this.lag) s.close();
|
|
4965
|
+
for (const s of [this.src, this.tr]) if (s.last !== void 0 && Date.now() - s.lastWall > QUIET_SEC * 1e3) s.close();
|
|
4906
4966
|
this.pair(false);
|
|
4907
4967
|
this.draft();
|
|
4908
4968
|
}
|
|
@@ -4913,7 +4973,10 @@ var OpenAITranslateProvider = class {
|
|
|
4913
4973
|
*/
|
|
4914
4974
|
pair(all) {
|
|
4915
4975
|
while (this.tr.done.length) {
|
|
4916
|
-
const t = this.tr.done
|
|
4976
|
+
const t = this.tr.done[0];
|
|
4977
|
+
const srcReady = this.srcUsed < this.src.done.length && this.src.done[this.srcUsed].end <= t.end + .5;
|
|
4978
|
+
if (!all && !srcReady && this.src.text.trim() && Date.now() - t.wall < 1500) break;
|
|
4979
|
+
this.tr.done.shift();
|
|
4917
4980
|
const want = Array.from(t.text).length * this.ratio;
|
|
4918
4981
|
const src = [];
|
|
4919
4982
|
let len = 0;
|
|
@@ -4936,28 +4999,37 @@ var OpenAITranslateProvider = class {
|
|
|
4936
4999
|
}
|
|
4937
5000
|
line(t, src) {
|
|
4938
5001
|
const srcText = src.map((s) => s.text).join(" ").trim();
|
|
4939
|
-
const
|
|
4940
|
-
|
|
5002
|
+
const spoken = src.length ? {
|
|
5003
|
+
start: src[0].start - .6,
|
|
5004
|
+
end: src.at(-1).end - .2
|
|
5005
|
+
} : {
|
|
5006
|
+
start: (t?.start ?? 0) - 1.4,
|
|
5007
|
+
end: (t?.end ?? 0) - .8
|
|
5008
|
+
};
|
|
5009
|
+
const start = Math.max(0, this.lastEnd, spoken.start);
|
|
5010
|
+
const end = Math.max(start + 1, spoken.end);
|
|
5011
|
+
this.lastEnd = end;
|
|
4941
5012
|
this.emit?.({
|
|
4942
5013
|
key: `ot:${this.seq++}`,
|
|
4943
5014
|
start,
|
|
4944
|
-
end
|
|
5015
|
+
end,
|
|
4945
5016
|
src: srcText,
|
|
4946
5017
|
tr: t ? { [this.opts.target]: t.text } : void 0,
|
|
4947
5018
|
final: true
|
|
4948
5019
|
});
|
|
4949
|
-
this.readyAt = Math.max(this.readyAt, end);
|
|
5020
|
+
this.readyAt = Math.max(this.readyAt, t?.end ?? end, end);
|
|
4950
5021
|
}
|
|
4951
5022
|
/** The sentence being translated, as a draft line. */
|
|
4952
5023
|
draft() {
|
|
4953
5024
|
const tr = this.tr.text.trim();
|
|
4954
5025
|
if (!tr) return;
|
|
4955
5026
|
const srcText = [...this.src.done.slice(this.srcUsed).map((s) => s.text), this.src.text.trim()].filter(Boolean).join(" ");
|
|
4956
|
-
const
|
|
5027
|
+
const from = this.src.done[this.srcUsed]?.start ?? this.src.start;
|
|
5028
|
+
const start = Math.max(0, this.lastEnd, from !== void 0 ? from - .6 : (this.tr.start ?? 0) - 1.4);
|
|
4957
5029
|
this.emit?.({
|
|
4958
5030
|
key: `ot:${this.seq}`,
|
|
4959
5031
|
start,
|
|
4960
|
-
end: Math.max(this.tr.last ?? start
|
|
5032
|
+
end: Math.max(start + 1, (this.src.last ?? this.tr.last ?? start) - .2),
|
|
4961
5033
|
src: srcText,
|
|
4962
5034
|
tr: { [this.opts.target]: tr },
|
|
4963
5035
|
final: false
|
|
@@ -4970,6 +5042,7 @@ var OpenAITranslateProvider = class {
|
|
|
4970
5042
|
async stop() {
|
|
4971
5043
|
await new Promise((r) => setTimeout(r, 2500));
|
|
4972
5044
|
if (this.quiet) clearInterval(this.quiet);
|
|
5045
|
+
if (this.drain) clearInterval(this.drain);
|
|
4973
5046
|
this.src.close();
|
|
4974
5047
|
this.tr.close();
|
|
4975
5048
|
this.pair(true);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-vidi",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "A video tool for DeepSeek Harness: watch live streams, videos and podcasts with real-time subtitles in any language, then clip and download",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "PolyForm-Noncommercial-1.0.0",
|