dsh-vidi 0.8.8 → 0.8.9
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 +94 -12
- package/lib/index.mjs +5 -3
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -34384,14 +34384,9 @@ const CLOCK$1 = (s) => {
|
|
|
34384
34384
|
return `${h ? `${h}:` : ""}${String(m).padStart(h ? 2 : 1, "0")}:${String(ss).padStart(2, "0")}`;
|
|
34385
34385
|
};
|
|
34386
34386
|
const host = (s) => s.replace(/^[a-z]+:\/\//i, "").split("/")[0] ?? s;
|
|
34387
|
-
/**
|
|
34388
|
-
* The player's restream menu: saved destinations (tick the ones to push to),
|
|
34389
|
-
* adding one (platform, server address, stream key — copied from the
|
|
34390
|
-
* platform's own streaming tool), subtitles drawn in or not, picture size,
|
|
34391
|
-
* then start. While running, each destination shows its own state.
|
|
34392
|
-
*/
|
|
34393
34387
|
function RestreamPanel(props) {
|
|
34394
|
-
const { jobId, status, now, style, t } = props;
|
|
34388
|
+
const { jobId, status, now, style, timing, t } = props;
|
|
34389
|
+
const fromNow = () => timing ? timing.fromFor(timing.mode) : now;
|
|
34395
34390
|
const running = !!status?.running;
|
|
34396
34391
|
const [targets, setTargets] = (0, react.useState)([]);
|
|
34397
34392
|
const [picked, setPicked] = (0, react.useState)(() => {
|
|
@@ -34463,7 +34458,7 @@ function RestreamPanel(props) {
|
|
|
34463
34458
|
jobId,
|
|
34464
34459
|
action: "start",
|
|
34465
34460
|
targets: picked.filter((id) => targets.some((x) => x.id === id)),
|
|
34466
|
-
from:
|
|
34461
|
+
from: fromNow(),
|
|
34467
34462
|
burn,
|
|
34468
34463
|
height,
|
|
34469
34464
|
style
|
|
@@ -34491,12 +34486,39 @@ function RestreamPanel(props) {
|
|
|
34491
34486
|
jobId,
|
|
34492
34487
|
action: "start",
|
|
34493
34488
|
targets: [id],
|
|
34494
|
-
from:
|
|
34489
|
+
from: fromNow(),
|
|
34495
34490
|
burn,
|
|
34496
34491
|
height,
|
|
34497
34492
|
style
|
|
34498
34493
|
}).catch((e) => setError(e.message));
|
|
34499
34494
|
};
|
|
34495
|
+
const switchMode = async (m) => {
|
|
34496
|
+
if (!timing || m === timing.mode) return;
|
|
34497
|
+
timing.onMode(m);
|
|
34498
|
+
if (!running) return;
|
|
34499
|
+
const ids = status.targets.map((x) => x.id);
|
|
34500
|
+
setBusy(true);
|
|
34501
|
+
setError("");
|
|
34502
|
+
try {
|
|
34503
|
+
await post$2("restream", {
|
|
34504
|
+
jobId,
|
|
34505
|
+
action: "stop"
|
|
34506
|
+
});
|
|
34507
|
+
await post$2("restream", {
|
|
34508
|
+
jobId,
|
|
34509
|
+
action: "start",
|
|
34510
|
+
targets: ids,
|
|
34511
|
+
from: timing.fromFor(m),
|
|
34512
|
+
burn,
|
|
34513
|
+
height,
|
|
34514
|
+
style
|
|
34515
|
+
});
|
|
34516
|
+
} catch (e) {
|
|
34517
|
+
setError(e.message);
|
|
34518
|
+
} finally {
|
|
34519
|
+
setBusy(false);
|
|
34520
|
+
}
|
|
34521
|
+
};
|
|
34500
34522
|
const stateOf = (id) => running ? status.targets.find((x) => x.id === id) : void 0;
|
|
34501
34523
|
const chosen = picked.filter((id) => targets.some((x) => x.id === id));
|
|
34502
34524
|
if (editing) {
|
|
@@ -34678,6 +34700,32 @@ function RestreamPanel(props) {
|
|
|
34678
34700
|
className: "rs-err",
|
|
34679
34701
|
children: status.targets.filter((x) => x.message && x.state !== "live").map((x) => `${pname(x.platform)}:${x.message}`).join(";")
|
|
34680
34702
|
}),
|
|
34703
|
+
timing && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
34704
|
+
className: "rs-mode",
|
|
34705
|
+
children: [
|
|
34706
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
34707
|
+
className: "rs-sub",
|
|
34708
|
+
children: t("rs.mode")
|
|
34709
|
+
}),
|
|
34710
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
34711
|
+
className: "seg",
|
|
34712
|
+
children: [
|
|
34713
|
+
"live",
|
|
34714
|
+
"realtime",
|
|
34715
|
+
"polished"
|
|
34716
|
+
].map((m) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
34717
|
+
className: timing.mode === m ? "on" : "",
|
|
34718
|
+
disabled: busy,
|
|
34719
|
+
onClick: () => void switchMode(m),
|
|
34720
|
+
children: t(`mode.${m}`)
|
|
34721
|
+
}, m))
|
|
34722
|
+
}),
|
|
34723
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
34724
|
+
className: "rs-hint",
|
|
34725
|
+
children: t(`rs.mode.${timing.mode}`).replace("{s}", String(Math.max(1, Math.round(timing.behindFor(timing.mode)))))
|
|
34726
|
+
})
|
|
34727
|
+
]
|
|
34728
|
+
}),
|
|
34681
34729
|
!running && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
34682
34730
|
className: "rs-opts",
|
|
34683
34731
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -36014,6 +36062,18 @@ function LiveView(props) {
|
|
|
36014
36062
|
const missing = deps.filter((d) => !d.ok && (d.name === "ffmpeg" || d.name === "yt-dlp" || (engine || "local") === "local" && d.name.startsWith("whisper")));
|
|
36015
36063
|
const needsModel = !hasTranslator && shownLines.some((l) => l !== "src");
|
|
36016
36064
|
const hasMain = !!job && !(job.status === "error" && !job.files.some((f) => /\.(mp4|m4a|srt|vtt)$/.test(f)));
|
|
36065
|
+
const liveEdge = job?.recorded ?? now;
|
|
36066
|
+
const pushFrom = (m) => {
|
|
36067
|
+
const firstPass = subs.ready ?? subs.at;
|
|
36068
|
+
const at = m === "live" ? liveEdge - 2 : m === "realtime" ? firstPass : Math.max(lastPolished, firstPass - 150);
|
|
36069
|
+
return Math.max(0, Math.min(at, liveEdge));
|
|
36070
|
+
};
|
|
36071
|
+
const pushTiming = job && !job.vod && job.status === "running" ? {
|
|
36072
|
+
mode,
|
|
36073
|
+
onMode: changeMode,
|
|
36074
|
+
fromFor: pushFrom,
|
|
36075
|
+
behindFor: (m) => liveEdge - pushFrom(m)
|
|
36076
|
+
} : void 0;
|
|
36017
36077
|
(0, react.useEffect)(() => {
|
|
36018
36078
|
if (job && !hasMain) setSrcTabRaw(job.source === "device" ? "camera" : job.source === "push" ? "push" : job.source === "file" ? "file" : "link");
|
|
36019
36079
|
}, [job?.id, hasMain]);
|
|
@@ -36594,6 +36654,7 @@ function LiveView(props) {
|
|
|
36594
36654
|
status: job.restream,
|
|
36595
36655
|
now,
|
|
36596
36656
|
style: subStyle,
|
|
36657
|
+
timing: pushTiming,
|
|
36597
36658
|
t
|
|
36598
36659
|
})
|
|
36599
36660
|
},
|
|
@@ -37418,6 +37479,10 @@ const en = {
|
|
|
37418
37479
|
"dev.blocked": "No picture or sound from the device: DeepSeek Harness needs the system’s permission. Open System Settings, switch DeepSeek Harness on, then retry.",
|
|
37419
37480
|
"dev.denied": "DeepSeek Harness needs permission to use the {d}. Open System Settings, switch it on in the list, then retry.",
|
|
37420
37481
|
"dev.and": " and ",
|
|
37482
|
+
"rs.mode": "Subtitle mode of the push-out",
|
|
37483
|
+
"rs.mode.live": "Pushed out about {s} s behind; subtitles come a little after the words.",
|
|
37484
|
+
"rs.mode.realtime": "Pushed out about {s} s behind, with first-pass subtitles in step.",
|
|
37485
|
+
"rs.mode.polished": "Pushed out about {s} s behind, with polished subtitles.",
|
|
37421
37486
|
"own.live": "Live",
|
|
37422
37487
|
"own.delayed": "As pushed out",
|
|
37423
37488
|
"own.toLive": "Show the live picture large",
|
|
@@ -37862,6 +37927,10 @@ const zh = {
|
|
|
37862
37927
|
"dev.blocked": "设备没有传来画面和声音:DeepSeek Harness 需要系统授权。点「打开系统设置」,在列表里打开 DeepSeek Harness,再点重试。",
|
|
37863
37928
|
"dev.denied": "DeepSeek Harness 需要{d}权限。点「打开系统设置」,在列表里打开它,再点重试。",
|
|
37864
37929
|
"dev.and": "和",
|
|
37930
|
+
"rs.mode": "推流用的字幕模式",
|
|
37931
|
+
"rs.mode.live": "推出去比实时晚约 {s} 秒,字幕会比说话稍晚出现。",
|
|
37932
|
+
"rs.mode.realtime": "推出去比实时晚约 {s} 秒,带对齐好的初版字幕。",
|
|
37933
|
+
"rs.mode.polished": "推出去比实时晚约 {s} 秒,带精修字幕。",
|
|
37865
37934
|
"own.live": "实时画面",
|
|
37866
37935
|
"own.delayed": "推流画面",
|
|
37867
37936
|
"own.toLive": "放大实时画面",
|
|
@@ -38402,6 +38471,9 @@ html[data-platform='darwin'] .dshl.page .dshl-top :is(button, input, label, a, .
|
|
|
38402
38471
|
outline: 1px solid rgba(255,255,255,.28); box-shadow: 0 6px 24px rgba(0,0,0,.5); cursor: pointer; transition: bottom .25s; }
|
|
38403
38472
|
.dshl-stage.idle .dshl-vbox.pip, .dshl-stage.idle .dshl-livecam.pip { bottom: 14px; }
|
|
38404
38473
|
.dshl-stage .pip .dshl-sub { transform: translateX(-50%) !important; }
|
|
38474
|
+
.dshl-rs .rs-mode { display: grid; gap: 6px; margin: 10px 0 4px; }
|
|
38475
|
+
.dshl-rs .rs-mode .seg { display: flex; }
|
|
38476
|
+
.dshl-rs .rs-mode .seg button { flex: 1; }
|
|
38405
38477
|
.dshl-piplabel { position: absolute; top: 6px; left: 6px; z-index: 3; padding: 2px 7px; border-radius: 5px; background: rgba(0,0,0,.6); color: #fff; font-size: 11px; pointer-events: none; }
|
|
38406
38478
|
.dshl-stage.idle { cursor: none; }
|
|
38407
38479
|
.dshl-holdnote { position: absolute; top: 14px; left: 50%; z-index: 4; display: flex; align-items: center; gap: 8px; padding: 6px 12px; border-radius: 999px; transform: translateX(-50%);
|
|
@@ -38893,8 +38965,14 @@ async function openPage(sessions, opts = {}) {
|
|
|
38893
38965
|
}
|
|
38894
38966
|
async function showPage(sessions, opts) {
|
|
38895
38967
|
const current = sessions.current();
|
|
38896
|
-
const
|
|
38897
|
-
|
|
38968
|
+
const fresh = () => pageSession(sessions, opts.workspaces, opts.title ?? "Video");
|
|
38969
|
+
let sid = opts.sessionId && sessions.has(opts.sessionId) ? opts.sessionId : await fresh();
|
|
38970
|
+
if (sid !== current) try {
|
|
38971
|
+
sessions.open(sid);
|
|
38972
|
+
} catch {
|
|
38973
|
+
sid = await fresh();
|
|
38974
|
+
sessions.open(sid);
|
|
38975
|
+
}
|
|
38898
38976
|
if (panelHost) {
|
|
38899
38977
|
pageStore.set({
|
|
38900
38978
|
open: true,
|
|
@@ -39037,7 +39115,11 @@ function SidebarButton(props) {
|
|
|
39037
39115
|
const pct = item?.subs === "running" && item.duration ? Math.min(99, Math.round(item.at / item.duration * 100)) : void 0;
|
|
39038
39116
|
const meta = !busy ? "" : item?.subs === "running" ? pct !== void 0 ? t("side.subs").replace("{p}", String(pct)) : t("side.subsLive") : item?.recording ? t("side.recording") : item?.loading ? t("side.loading") : t("side.downloading");
|
|
39039
39117
|
const thumb = busy && frame && (!item || frame.jobId === item.jobId) ? frame.dataUrl : void 0;
|
|
39040
|
-
const open = () => void openPage(sessions, item ? {
|
|
39118
|
+
const open = () => void openPage(sessions, item ? {
|
|
39119
|
+
sessionId: item.conversationId,
|
|
39120
|
+
workspaces,
|
|
39121
|
+
title: t("view.live")
|
|
39122
|
+
} : {
|
|
39041
39123
|
workspaces,
|
|
39042
39124
|
title: t("view.live")
|
|
39043
39125
|
});
|
package/lib/index.mjs
CHANGED
|
@@ -1912,8 +1912,6 @@ function deviceInput(c, q) {
|
|
|
1912
1912
|
if (process.platform === "darwin") return {
|
|
1913
1913
|
args: [
|
|
1914
1914
|
...q,
|
|
1915
|
-
"-use_wallclock_as_timestamps",
|
|
1916
|
-
"1",
|
|
1917
1915
|
"-f",
|
|
1918
1916
|
"avfoundation",
|
|
1919
1917
|
"-framerate",
|
|
@@ -1998,6 +1996,8 @@ var LiveJob = class LiveJob extends EventEmitter {
|
|
|
1998
1996
|
/** Real-time picture (own sources): the newest JPEG frame, and whether frames are coming. */
|
|
1999
1997
|
lastFrame;
|
|
2000
1998
|
previewOn = false;
|
|
1999
|
+
/** Closed by the user: no longer offered anywhere (its files stay). */
|
|
2000
|
+
closed = false;
|
|
2001
2001
|
id;
|
|
2002
2002
|
status = "resolving";
|
|
2003
2003
|
message;
|
|
@@ -2451,6 +2451,7 @@ var LiveJob = class LiveJob extends EventEmitter {
|
|
|
2451
2451
|
...picture,
|
|
2452
2452
|
"-map",
|
|
2453
2453
|
input.audio,
|
|
2454
|
+
...input.fps ? ["-af", "aresample=async=1:min_hard_comp=0.1:first_pts=0"] : [],
|
|
2454
2455
|
"-c:a",
|
|
2455
2456
|
"aac",
|
|
2456
2457
|
"-b:a",
|
|
@@ -6558,6 +6559,7 @@ function apply(ctx, config) {
|
|
|
6558
6559
|
const job = jobs.get(String(b.jobId));
|
|
6559
6560
|
if (!job) return send(res, 404, { error: "no such job" });
|
|
6560
6561
|
await job.close("closed");
|
|
6562
|
+
job.closed = true;
|
|
6561
6563
|
const conv = job.summary().conversationId;
|
|
6562
6564
|
if (byConversation.get(conv) === job.id) byConversation.set(conv, "");
|
|
6563
6565
|
try {
|
|
@@ -6601,7 +6603,7 @@ function apply(ctx, config) {
|
|
|
6601
6603
|
return send(res, 200, job.summary());
|
|
6602
6604
|
}
|
|
6603
6605
|
if (route === "activity") return send(res, 200, {
|
|
6604
|
-
items: [...jobs.values()].map((j) => j.summary()).filter((s) => s.status === "running" && !s.message || s.status === "resolving" || s.subs?.state === "running").map((s) => ({
|
|
6606
|
+
items: [...jobs.values()].filter((j) => !j.closed).map((j) => j.summary()).filter((s) => s.status === "running" && !s.message || s.status === "resolving" || s.subs?.state === "running").map((s) => ({
|
|
6605
6607
|
jobId: s.id,
|
|
6606
6608
|
conversationId: s.conversationId,
|
|
6607
6609
|
title: s.title,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-vidi",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.9",
|
|
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",
|