dsh-vidi 0.7.4 → 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 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 [rate, setRate] = (0, react.useState)(null);
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
- setRate(null);
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
- at: subs.at
35591
+ gap
35591
35592
  });
35592
35593
  while (log.length > 2 && log[0].wall < Date.now() - 12e4) log.shift();
35593
- const first = log[0], last = log.at(-1);
35594
- setRate(last.wall - first.wall > 45e3 ? (last.at - first.at) / ((last.wall - first.wall) / 1e3) : null);
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 && rate !== null && rate < .95;
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("{x}", rate.toFixed(2)) }), engineNow && !engineNow.online && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
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.map(([k, v]) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
36573
- className: "dshl-muted small",
36574
- children: t("usage.audio").replace("{s}", engineLabel(t, k)).replace("{t}", CLOCK(v))
36575
- }, k))
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.audio": "Recognised by {s}: {t}",
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": "Transcription runs slower than the stream ({x}×), so subtitles fall further behind.",
37043
- "lag.slowLocal": "This computer transcribes slower than the stream ({x}×), so subtitles fall further behind. An online service keeps up.",
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.audio": "{s} 识别 {t}",
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": "转写比直播慢({x} 倍速),字幕会越落越远。",
37431
- "lag.slowLocal": "本机转写比直播慢({x} 倍速),字幕会越落越远。换成在线转写就能跟上。",
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 [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-vidi",
3
- "version": "0.7.4",
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",