dsh-vidi 0.7.3 → 0.7.5
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 +44 -19
- package/lib/index.mjs +32 -3
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -35577,24 +35577,26 @@ function LiveView(props) {
|
|
|
35577
35577
|
props.onTitle(job.title);
|
|
35578
35578
|
}, [job?.id, job?.title]);
|
|
35579
35579
|
const rateLog = (0, react.useRef)([]);
|
|
35580
|
-
const [
|
|
35580
|
+
const [behindRec, setBehindRec] = (0, react.useState)(null);
|
|
35581
35581
|
(0, react.useEffect)(() => {
|
|
35582
|
-
if (!job || job.vod || job.status !== "running" || subs.state !== "running") {
|
|
35582
|
+
if (!job || job.vod || job.status !== "running" || subs.state !== "running" || job.recorded === void 0) {
|
|
35583
35583
|
rateLog.current = [];
|
|
35584
|
-
|
|
35584
|
+
setBehindRec(null);
|
|
35585
35585
|
return;
|
|
35586
35586
|
}
|
|
35587
35587
|
const log = rateLog.current;
|
|
35588
|
+
const gap = Math.max(0, job.recorded - subs.at);
|
|
35588
35589
|
log.push({
|
|
35589
35590
|
wall: Date.now(),
|
|
35590
|
-
|
|
35591
|
+
gap
|
|
35591
35592
|
});
|
|
35592
35593
|
while (log.length > 2 && log[0].wall < Date.now() - 12e4) log.shift();
|
|
35593
|
-
const first = log[0]
|
|
35594
|
-
|
|
35594
|
+
const first = log[0];
|
|
35595
|
+
setBehindRec(Date.now() - first.wall > 45e3 && gap > 20 && gap - first.gap > 8 ? gap : null);
|
|
35595
35596
|
}, [
|
|
35596
35597
|
subs.at,
|
|
35597
35598
|
subs.state,
|
|
35599
|
+
job?.recorded,
|
|
35598
35600
|
job?.status,
|
|
35599
35601
|
job?.id
|
|
35600
35602
|
]);
|
|
@@ -36055,11 +36057,11 @@ function LiveView(props) {
|
|
|
36055
36057
|
onTerms: setTerms
|
|
36056
36058
|
})
|
|
36057
36059
|
] });
|
|
36058
|
-
const slow = !!job && job.status === "running" && !job.vod &&
|
|
36060
|
+
const slow = !!job && job.status === "running" && !job.vod && behindRec !== null;
|
|
36059
36061
|
const engineNow = providers.find((x) => x.id === usableEngine());
|
|
36060
36062
|
const liveNote = slow ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
36061
36063
|
className: "dshl-lag warn",
|
|
36062
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(engineNow && !engineNow.online ? "lag.slowLocal" : "lag.slow").replace("{
|
|
36064
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(engineNow && !engineNow.online ? "lag.slowLocal" : "lag.slow").replace("{s}", String(Math.round(behindRec))) }), engineNow && !engineNow.online && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
36063
36065
|
className: "dshl-link",
|
|
36064
36066
|
onClick: () => setSettingsOpen(true),
|
|
36065
36067
|
children: t("lag.settings")
|
|
@@ -36528,6 +36530,10 @@ function SubsStarter(props) {
|
|
|
36528
36530
|
});
|
|
36529
36531
|
}
|
|
36530
36532
|
const fmtTokens = (n, ui) => ui === "zh" && n >= 1e4 ? `${(n / 1e4).toFixed(n >= 1e5 ? 0 : 1)}万` : n >= 1e3 ? `${(n / 1e3).toFixed(n >= 1e5 ? 0 : 1)}k` : String(n);
|
|
36533
|
+
/** Engines billed by the minute of audio (no token counts come back): US$ per minute, from the provider's price list. */
|
|
36534
|
+
const PER_MINUTE = { "openai-translate": .034 };
|
|
36535
|
+
const audioCost = (audio) => audio.reduce((sum, [k, v]) => sum + (PER_MINUTE[k] ?? 0) * v / 60, 0);
|
|
36536
|
+
const usd = (v) => `$${v.toFixed(2)}`;
|
|
36531
36537
|
/** Total tokens for this video; hover or click for per-model input / output / cache. */
|
|
36532
36538
|
function UsagePill({ usage, t, ui }) {
|
|
36533
36539
|
const [open, setOpen] = (0, react.useState)(false);
|
|
@@ -36542,7 +36548,7 @@ function UsagePill({ usage, t, ui }) {
|
|
|
36542
36548
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
36543
36549
|
className: "dshl-pill",
|
|
36544
36550
|
onClick: () => setOpen((v) => !v),
|
|
36545
|
-
children: t("usage.tokens").replace("{n}", n(total))
|
|
36551
|
+
children: [total ? t("usage.tokens").replace("{n}", n(total)) : "", audioCost(audio) >= .01 ? usd(audioCost(audio)) : ""].filter(Boolean).join(" · ")
|
|
36546
36552
|
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
36547
36553
|
className: "dshl-pop right up usage",
|
|
36548
36554
|
children: [
|
|
@@ -36569,10 +36575,21 @@ function UsagePill({ usage, t, ui }) {
|
|
|
36569
36575
|
className: "dshl-muted",
|
|
36570
36576
|
children: t("usage.none")
|
|
36571
36577
|
}),
|
|
36572
|
-
audio.
|
|
36573
|
-
className: "
|
|
36574
|
-
children:
|
|
36575
|
-
|
|
36578
|
+
audio.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
|
|
36579
|
+
className: "audio",
|
|
36580
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
36581
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("usage.byAudio") }),
|
|
36582
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("usage.duration") }),
|
|
36583
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("usage.cost") })
|
|
36584
|
+
] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: audio.map(([k, v]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
36585
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: engineLabel(t, k) }),
|
|
36586
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: CLOCK(v) }),
|
|
36587
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
36588
|
+
title: PER_MINUTE[k] ? t("usage.rate").replace("{p}", String(PER_MINUTE[k])) : void 0,
|
|
36589
|
+
children: PER_MINUTE[k] ? "≈ " + usd(PER_MINUTE[k] * v / 60) : "—"
|
|
36590
|
+
})
|
|
36591
|
+
] }, k)) })]
|
|
36592
|
+
})
|
|
36576
36593
|
]
|
|
36577
36594
|
})]
|
|
36578
36595
|
});
|
|
@@ -37012,7 +37029,10 @@ const en = {
|
|
|
37012
37029
|
"usage.cache": "Cache",
|
|
37013
37030
|
"usage.calls": "Calls",
|
|
37014
37031
|
"usage.reasoning": "incl. {n} thinking",
|
|
37015
|
-
"usage.
|
|
37032
|
+
"usage.byAudio": "Billed by audio time",
|
|
37033
|
+
"usage.duration": "Audio",
|
|
37034
|
+
"usage.cost": "Cost",
|
|
37035
|
+
"usage.rate": "US${p} per minute of audio (estimate from the price list)",
|
|
37016
37036
|
"usage.none": "Nothing used yet",
|
|
37017
37037
|
"set.mode": "Mode",
|
|
37018
37038
|
"st.loading": "Loading",
|
|
@@ -37039,8 +37059,8 @@ const en = {
|
|
|
37039
37059
|
"hold.polished": "Waiting for polished subtitles…",
|
|
37040
37060
|
"hold.skip": "Don’t wait",
|
|
37041
37061
|
"hold.behind": "(subtitles run {s}s behind the sound)",
|
|
37042
|
-
"lag.slow": "
|
|
37043
|
-
"lag.slowLocal": "
|
|
37062
|
+
"lag.slow": "Subtitles are {s} s behind the stream and falling further behind.",
|
|
37063
|
+
"lag.slowLocal": "Subtitles are {s} s behind the stream and falling further behind: this computer transcribes too slowly. An online service keeps up.",
|
|
37044
37064
|
"lag.settings": "Choose one",
|
|
37045
37065
|
"in.uploading": "Uploading {p}%",
|
|
37046
37066
|
"in.drop": "Drop to start",
|
|
@@ -37400,7 +37420,10 @@ const zh = {
|
|
|
37400
37420
|
"usage.cache": "缓存",
|
|
37401
37421
|
"usage.calls": "次数",
|
|
37402
37422
|
"usage.reasoning": "含思考 {n}",
|
|
37403
|
-
"usage.
|
|
37423
|
+
"usage.byAudio": "按音频时长计费",
|
|
37424
|
+
"usage.duration": "时长",
|
|
37425
|
+
"usage.cost": "费用",
|
|
37426
|
+
"usage.rate": "按每分钟音频 {p} 美元估算(官方价目)",
|
|
37404
37427
|
"usage.none": "还没有用量",
|
|
37405
37428
|
"set.mode": "模式",
|
|
37406
37429
|
"st.loading": "加载中",
|
|
@@ -37427,8 +37450,8 @@ const zh = {
|
|
|
37427
37450
|
"hold.polished": "等精修字幕…",
|
|
37428
37451
|
"hold.skip": "不等了",
|
|
37429
37452
|
"hold.behind": "(字幕比声音晚 {s} 秒)",
|
|
37430
|
-
"lag.slow": "
|
|
37431
|
-
"lag.slowLocal": "
|
|
37453
|
+
"lag.slow": "字幕比直播晚了 {s} 秒,还在越拉越远。",
|
|
37454
|
+
"lag.slowLocal": "字幕比直播晚了 {s} 秒,还在越拉越远:本机转写太慢。换成在线转写就能跟上。",
|
|
37432
37455
|
"lag.settings": "去选一个",
|
|
37433
37456
|
"in.uploading": "上传中 {p}%",
|
|
37434
37457
|
"in.drop": "松手开始",
|
|
@@ -38303,6 +38326,8 @@ body > .dshl-menu { z-index: 2147483002; }
|
|
|
38303
38326
|
.dshl-pop.usage td { padding: 4px 0 4px 10px; border-top: 1px solid var(--line-soft); text-align: right; }
|
|
38304
38327
|
.dshl-pop.usage th:first-child, .dshl-pop.usage td:first-child { max-width: 140px; padding-left: 0; overflow: hidden; text-align: left; text-overflow: ellipsis; white-space: nowrap; }
|
|
38305
38328
|
.dshl-pop.usage p { margin: 8px 0 0; }
|
|
38329
|
+
.dshl-pop.usage table + table { margin-top: 12px; }
|
|
38330
|
+
.dshl-pop.usage table.audio td:first-child { max-width: 180px; }
|
|
38306
38331
|
@media (max-width: 600px) { .dshl-pop.settings.right { position: fixed; top: 58px; left: 12px; right: 12px; width: auto; max-width: none; } }
|
|
38307
38332
|
|
|
38308
38333
|
/* ── Login dialog ── */
|
package/lib/index.mjs
CHANGED
|
@@ -1902,6 +1902,7 @@ var LiveJob = class LiveJob extends EventEmitter {
|
|
|
1902
1902
|
ingest: this.ingest,
|
|
1903
1903
|
vod: this.vod,
|
|
1904
1904
|
duration: this.duration,
|
|
1905
|
+
recorded: this.vod ? void 0 : this.recordedSec(),
|
|
1905
1906
|
loginBrowser: this.loginBrowser,
|
|
1906
1907
|
needsLogin: this.needsLogin,
|
|
1907
1908
|
hls: this.hlsName,
|
|
@@ -2317,6 +2318,21 @@ var LiveJob = class LiveJob extends EventEmitter {
|
|
|
2317
2318
|
get m3u8() {
|
|
2318
2319
|
return join(this.deps.dir, this.hlsName, "index.m3u8");
|
|
2319
2320
|
}
|
|
2321
|
+
/** Seconds recorded so far (live), from the playlist; re-read at most once a second. */
|
|
2322
|
+
recCache = {
|
|
2323
|
+
at: 0,
|
|
2324
|
+
sec: 0
|
|
2325
|
+
};
|
|
2326
|
+
recordedSec() {
|
|
2327
|
+
if (Date.now() - this.recCache.at > 1e3) {
|
|
2328
|
+
const last = this.segments().at(-1);
|
|
2329
|
+
this.recCache = {
|
|
2330
|
+
at: Date.now(),
|
|
2331
|
+
sec: last ? last.start + last.dur : 0
|
|
2332
|
+
};
|
|
2333
|
+
}
|
|
2334
|
+
return this.recCache.sec;
|
|
2335
|
+
}
|
|
2320
2336
|
/** Recorded segments so far: start time on the playback timeline and duration. */
|
|
2321
2337
|
segments() {
|
|
2322
2338
|
if (!existsSync(this.m3u8)) return [];
|
|
@@ -4732,6 +4748,8 @@ const HARD_CJK = 34;
|
|
|
4732
4748
|
const HARD_LATIN = 110;
|
|
4733
4749
|
/** A stream quiet this long ends its sentence (a pause without punctuation). */
|
|
4734
4750
|
const QUIET_SEC = 1.6;
|
|
4751
|
+
/** …or this long when it is only a few words so far. */
|
|
4752
|
+
const QUIET_SHORT_SEC = 3.5;
|
|
4735
4753
|
/**
|
|
4736
4754
|
* Builds sentences from one text stream. The service stamps every delta
|
|
4737
4755
|
* with the audio position it was produced at (elapsed_ms); a sentence's
|
|
@@ -4813,7 +4831,13 @@ var Stream = class {
|
|
|
4813
4831
|
}
|
|
4814
4832
|
close() {
|
|
4815
4833
|
const text = this.text.trim();
|
|
4816
|
-
|
|
4834
|
+
const prev = this.done.at(-1);
|
|
4835
|
+
if (text && !/[\p{L}\p{N}]/u.test(text)) {
|
|
4836
|
+
if (prev) {
|
|
4837
|
+
prev.text += text;
|
|
4838
|
+
prev.end = Math.max(prev.end, this.open.at(-1).t);
|
|
4839
|
+
}
|
|
4840
|
+
} else if (text) this.done.push({
|
|
4817
4841
|
text,
|
|
4818
4842
|
start: this.open[0].t,
|
|
4819
4843
|
end: this.open.at(-1).t,
|
|
@@ -4821,6 +4845,11 @@ var Stream = class {
|
|
|
4821
4845
|
});
|
|
4822
4846
|
this.open = [];
|
|
4823
4847
|
}
|
|
4848
|
+
/** Long enough to stand as a line of its own (when cutting without punctuation). */
|
|
4849
|
+
get substantial() {
|
|
4850
|
+
const text = this.text.trim();
|
|
4851
|
+
return (this.cjk ? Array.from(text).length : text.length) >= (this.cjk ? 10 : 24);
|
|
4852
|
+
}
|
|
4824
4853
|
};
|
|
4825
4854
|
/**
|
|
4826
4855
|
* OpenAI live interpretation (gpt-realtime-translate): audio streams in, the
|
|
@@ -4969,7 +4998,7 @@ var OpenAITranslateProvider = class {
|
|
|
4969
4998
|
else {
|
|
4970
4999
|
const next = this.src.done[this.srcUsed];
|
|
4971
5000
|
if (next && this.tr.text.trim() && t - next.end >= 1.2) {
|
|
4972
|
-
if (!this.tr.closeAtPause() && t - next.end >= 3) this.tr.close();
|
|
5001
|
+
if (!this.tr.closeAtPause() && t - next.end >= 3 && this.tr.substantial) this.tr.close();
|
|
4973
5002
|
}
|
|
4974
5003
|
this.tr.add(piece);
|
|
4975
5004
|
}
|
|
@@ -4979,7 +5008,7 @@ var OpenAITranslateProvider = class {
|
|
|
4979
5008
|
}
|
|
4980
5009
|
/** Close streams that went quiet (a pause without punctuation), then pair what is complete. */
|
|
4981
5010
|
settle() {
|
|
4982
|
-
for (const s of [this.src, this.tr]) if (s.last !== void 0 && Date.now() - s.lastWall > QUIET_SEC * 1e3) s.close();
|
|
5011
|
+
for (const s of [this.src, this.tr]) if (s.last !== void 0 && Date.now() - s.lastWall > (s.substantial ? QUIET_SEC : QUIET_SHORT_SEC) * 1e3) s.close();
|
|
4983
5012
|
this.pair(false);
|
|
4984
5013
|
this.draft();
|
|
4985
5014
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-vidi",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5",
|
|
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",
|