dsh-vidi 0.8.6 → 0.8.8
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 +96 -20
- package/lib/index.mjs +62 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -33608,13 +33608,22 @@ const fmt = (s) => {
|
|
|
33608
33608
|
* stage, so subtitles (sized in container units) and menus come along.
|
|
33609
33609
|
*/
|
|
33610
33610
|
const Player = (0, react.forwardRef)(function Player(props, ref) {
|
|
33611
|
-
const { trail, src, live, vod, duration, subsAt, waitingText, cues, siteCues, spoken, style, delay, onDelay, showDelay, theater, onTheater, onTime, action, menus = [], onSnapshot, onClip, onThumb, quality, t } = props;
|
|
33611
|
+
const { preview, trail, src, live, vod, duration, subsAt, waitingText, cues, siteCues, spoken, style, delay, onDelay, showDelay, theater, onTheater, onTime, action, menus = [], onSnapshot, onClip, onThumb, quality, t } = props;
|
|
33612
33612
|
const stage = (0, react.useRef)(null);
|
|
33613
33613
|
const video = (0, react.useRef)(null);
|
|
33614
33614
|
const hls = (0, react.useRef)(null);
|
|
33615
33615
|
const [ready, setReady] = (0, react.useState)(false);
|
|
33616
33616
|
const [playing, setPlaying] = (0, react.useState)(false);
|
|
33617
33617
|
const [muted, setMuted] = (0, react.useState)(false);
|
|
33618
|
+
const [bigLive, setBigLive] = (0, react.useState)(true);
|
|
33619
|
+
const liveBig = !!preview && bigLive;
|
|
33620
|
+
(0, react.useEffect)(() => {
|
|
33621
|
+
const el = video.current;
|
|
33622
|
+
if (el && liveBig) {
|
|
33623
|
+
el.muted = true;
|
|
33624
|
+
setMuted(true);
|
|
33625
|
+
}
|
|
33626
|
+
}, [liveBig]);
|
|
33618
33627
|
const [volume, setVolume] = (0, react.useState)(1);
|
|
33619
33628
|
const [now, setNow] = (0, react.useState)(0);
|
|
33620
33629
|
const [edge, setEdge] = (0, react.useState)(0);
|
|
@@ -33806,6 +33815,7 @@ const Player = (0, react.forwardRef)(function Player(props, ref) {
|
|
|
33806
33815
|
const pos = h?.liveSyncPosition ?? edge - delay;
|
|
33807
33816
|
if (pos != null && isFinite(pos)) el.currentTime = pos;
|
|
33808
33817
|
el.play();
|
|
33818
|
+
setBigLive(true);
|
|
33809
33819
|
}, [edge, delay]);
|
|
33810
33820
|
const applyDelay = (0, react.useCallback)((v) => {
|
|
33811
33821
|
onDelay(v);
|
|
@@ -33878,13 +33888,20 @@ const Player = (0, react.forwardRef)(function Player(props, ref) {
|
|
|
33878
33888
|
const behind = Math.max(0, edge - now);
|
|
33879
33889
|
const atLive = behind < delay + 10;
|
|
33880
33890
|
const current = cues.filter((c) => c.start - .2 <= now && now <= c.end + 1.2).at(-1) ?? (trail ? cues.filter((c) => c.start <= now && c.end > now - 8).at(-1) : void 0);
|
|
33891
|
+
const textsOf = (cue) => {
|
|
33892
|
+
const out = {};
|
|
33893
|
+
if (!cue) return out;
|
|
33894
|
+
out.src = noStop(cue.src);
|
|
33895
|
+
for (const [k, v] of Object.entries(cue.tr ?? {})) if (v && v.trim() !== cue.src.trim()) out[k] = noStop(v);
|
|
33896
|
+
if (spoken) out[spoken] = out.src;
|
|
33897
|
+
return out;
|
|
33898
|
+
};
|
|
33899
|
+
const newest = cues.at(-1);
|
|
33900
|
+
const liveCue = newest && (!newest.final || (subsAt ?? 0) - newest.end < 6) ? newest : void 0;
|
|
33901
|
+
const liveTexts = ccOn ? textsOf(liveCue) : {};
|
|
33881
33902
|
const texts = {};
|
|
33882
33903
|
if (ccOn) {
|
|
33883
|
-
if (current)
|
|
33884
|
-
texts.src = noStop(current.src);
|
|
33885
|
-
for (const [k, v] of Object.entries(current.tr ?? {})) if (v && v.trim() !== current.src.trim()) texts[k] = noStop(v);
|
|
33886
|
-
if (spoken) texts[spoken] = texts.src;
|
|
33887
|
-
}
|
|
33904
|
+
if (current) Object.assign(texts, textsOf(current));
|
|
33888
33905
|
for (const [k, list] of Object.entries(siteCues ?? {})) texts[k] = list.find((c) => c.start - .2 <= now && now <= c.end + .3)?.text;
|
|
33889
33906
|
}
|
|
33890
33907
|
const snapshot = async () => {
|
|
@@ -34027,16 +34044,52 @@ const Player = (0, react.forwardRef)(function Player(props, ref) {
|
|
|
34027
34044
|
if (!e.target.closest(".dshl-controls, .dshl-menu")) toggleFull();
|
|
34028
34045
|
},
|
|
34029
34046
|
children: [
|
|
34030
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
34031
|
-
|
|
34032
|
-
|
|
34033
|
-
|
|
34047
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
34048
|
+
className: `dshl-vbox ${liveBig ? "pip" : ""}`,
|
|
34049
|
+
onClick: liveBig ? () => setBigLive(false) : void 0,
|
|
34050
|
+
title: liveBig ? t("own.toDelayed") : void 0,
|
|
34051
|
+
children: [
|
|
34052
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("video", {
|
|
34053
|
+
ref: video,
|
|
34054
|
+
playsInline: true,
|
|
34055
|
+
onClick: liveBig ? void 0 : togglePlay
|
|
34056
|
+
}),
|
|
34057
|
+
liveBig && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SubtitleOverlay, {
|
|
34058
|
+
texts,
|
|
34059
|
+
style,
|
|
34060
|
+
draft: current ? !current.final : false
|
|
34061
|
+
}),
|
|
34062
|
+
liveBig && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
34063
|
+
className: "dshl-piplabel",
|
|
34064
|
+
children: t("own.delayed")
|
|
34065
|
+
})
|
|
34066
|
+
]
|
|
34034
34067
|
}),
|
|
34035
|
-
|
|
34068
|
+
preview && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
34069
|
+
className: `dshl-livecam ${liveBig ? "" : "pip"}`,
|
|
34070
|
+
onClick: liveBig ? void 0 : () => setBigLive(true),
|
|
34071
|
+
title: liveBig ? void 0 : t("own.toLive"),
|
|
34072
|
+
children: [
|
|
34073
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
34074
|
+
src: preview,
|
|
34075
|
+
alt: ""
|
|
34076
|
+
}),
|
|
34077
|
+
!liveBig && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SubtitleOverlay, {
|
|
34078
|
+
texts: liveTexts,
|
|
34079
|
+
style,
|
|
34080
|
+
draft: liveCue ? !liveCue.final : false
|
|
34081
|
+
}),
|
|
34082
|
+
!liveBig && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
34083
|
+
className: "dshl-piplabel",
|
|
34084
|
+
children: t("own.live")
|
|
34085
|
+
})
|
|
34086
|
+
]
|
|
34087
|
+
}),
|
|
34088
|
+
!ready && !liveBig && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
34036
34089
|
className: "dshl-overlay",
|
|
34037
34090
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "dshl-spinner" }), waitingText]
|
|
34038
34091
|
}),
|
|
34039
|
-
ready && held && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
34092
|
+
ready && held && !liveBig && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
34040
34093
|
className: "dshl-holdnote",
|
|
34041
34094
|
children: [
|
|
34042
34095
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", { className: "dshl-spinner sm" }),
|
|
@@ -34048,7 +34101,11 @@ const Player = (0, react.forwardRef)(function Player(props, ref) {
|
|
|
34048
34101
|
})
|
|
34049
34102
|
]
|
|
34050
34103
|
}),
|
|
34051
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SubtitleOverlay, {
|
|
34104
|
+
liveBig ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SubtitleOverlay, {
|
|
34105
|
+
texts: liveTexts,
|
|
34106
|
+
style,
|
|
34107
|
+
draft: liveCue ? !liveCue.final : false
|
|
34108
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SubtitleOverlay, {
|
|
34052
34109
|
texts,
|
|
34053
34110
|
style,
|
|
34054
34111
|
draft: current ? !current.final : false
|
|
@@ -34075,13 +34132,13 @@ const Player = (0, react.forwardRef)(function Player(props, ref) {
|
|
|
34075
34132
|
]
|
|
34076
34133
|
}),
|
|
34077
34134
|
ready && live && !vod && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
34078
|
-
className: `dshl-livebadge ${atLive ? "on" : ""}`,
|
|
34135
|
+
className: `dshl-livebadge ${atLive || liveBig ? "on" : ""}`,
|
|
34079
34136
|
onClick: goLive,
|
|
34080
34137
|
title: t("player.goLive"),
|
|
34081
34138
|
children: [
|
|
34082
34139
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon.live, {}),
|
|
34083
34140
|
" ",
|
|
34084
|
-
atLive ? t("player.live") : t("player.behind").replace("{s}", fmt(behind))
|
|
34141
|
+
atLive || liveBig ? t("player.live") : t("player.behind").replace("{s}", fmt(behind))
|
|
34085
34142
|
]
|
|
34086
34143
|
}),
|
|
34087
34144
|
openMenu && (() => {
|
|
@@ -34162,6 +34219,7 @@ const Player = (0, react.forwardRef)(function Player(props, ref) {
|
|
|
34162
34219
|
value: Math.min(now, total),
|
|
34163
34220
|
onChange: (e) => {
|
|
34164
34221
|
if (video.current) video.current.currentTime = Math.min(Number(e.target.value), edge);
|
|
34222
|
+
setBigLive(false);
|
|
34165
34223
|
},
|
|
34166
34224
|
"aria-label": "seek"
|
|
34167
34225
|
})
|
|
@@ -36474,6 +36532,7 @@ function LiveView(props) {
|
|
|
36474
36532
|
duration: job.duration,
|
|
36475
36533
|
subsAt: subs.state === "off" ? void 0 : subs.at,
|
|
36476
36534
|
waitingText: job.status === "resolving" ? statusText : t("player.waiting"),
|
|
36535
|
+
preview: job.preview && job.status === "running" ? `/live/api/preview?jobId=${encodeURIComponent(job.id)}` : void 0,
|
|
36477
36536
|
cues,
|
|
36478
36537
|
trail: mode === "live" && !job.vod,
|
|
36479
36538
|
siteCues: shownSite,
|
|
@@ -37359,6 +37418,10 @@ const en = {
|
|
|
37359
37418
|
"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.",
|
|
37360
37419
|
"dev.denied": "DeepSeek Harness needs permission to use the {d}. Open System Settings, switch it on in the list, then retry.",
|
|
37361
37420
|
"dev.and": " and ",
|
|
37421
|
+
"own.live": "Live",
|
|
37422
|
+
"own.delayed": "As pushed out",
|
|
37423
|
+
"own.toLive": "Show the live picture large",
|
|
37424
|
+
"own.toDelayed": "Show what is pushed out (with the subtitle delay) large",
|
|
37362
37425
|
"dev.stalled": "The camera sent pictures but they could not be recorded. Retry; if it happens again, pick another camera.",
|
|
37363
37426
|
"st.failedEarlier": "This one did not finish last time.",
|
|
37364
37427
|
"dev.k.camera": "camera",
|
|
@@ -37799,6 +37862,10 @@ const zh = {
|
|
|
37799
37862
|
"dev.blocked": "设备没有传来画面和声音:DeepSeek Harness 需要系统授权。点「打开系统设置」,在列表里打开 DeepSeek Harness,再点重试。",
|
|
37800
37863
|
"dev.denied": "DeepSeek Harness 需要{d}权限。点「打开系统设置」,在列表里打开它,再点重试。",
|
|
37801
37864
|
"dev.and": "和",
|
|
37865
|
+
"own.live": "实时画面",
|
|
37866
|
+
"own.delayed": "推流画面",
|
|
37867
|
+
"own.toLive": "放大实时画面",
|
|
37868
|
+
"own.toDelayed": "放大推流画面(按字幕模式延迟)",
|
|
37802
37869
|
"dev.stalled": "摄像头有画面传来,但没能录下来。点重试;还不行就换一个摄像头试试。",
|
|
37803
37870
|
"st.failedEarlier": "上次这个没有成功。",
|
|
37804
37871
|
"dev.k.camera": "摄像头",
|
|
@@ -38327,6 +38394,15 @@ html[data-platform='darwin'] .dshl.page .dshl-top :is(button, input, label, a, .
|
|
|
38327
38394
|
.dshl-stage.full { width: 100vw; height: 100vh; aspect-ratio: auto; border-radius: 0; }
|
|
38328
38395
|
.dshl-stage.pagefull { position: fixed; inset: 0; z-index: 2147483000; width: 100vw; height: 100vh; aspect-ratio: auto; border-radius: 0; }
|
|
38329
38396
|
.dshl-stage video { display: block; width: 100%; height: 100%; object-fit: contain; background: #000; }
|
|
38397
|
+
/* Own camera: the recording sits in a box that becomes a small window while the real-time picture is large. */
|
|
38398
|
+
.dshl-vbox { position: absolute; inset: 0; container-type: inline-size; }
|
|
38399
|
+
.dshl-livecam { position: absolute; inset: 0; z-index: 1; container-type: inline-size; background: #000; }
|
|
38400
|
+
.dshl-livecam img { display: block; width: 100%; height: 100%; object-fit: contain; }
|
|
38401
|
+
.dshl-vbox.pip, .dshl-livecam.pip { inset: auto 14px 74px auto; z-index: 3; width: 28%; min-width: 180px; height: auto; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 8px;
|
|
38402
|
+
outline: 1px solid rgba(255,255,255,.28); box-shadow: 0 6px 24px rgba(0,0,0,.5); cursor: pointer; transition: bottom .25s; }
|
|
38403
|
+
.dshl-stage.idle .dshl-vbox.pip, .dshl-stage.idle .dshl-livecam.pip { bottom: 14px; }
|
|
38404
|
+
.dshl-stage .pip .dshl-sub { transform: translateX(-50%) !important; }
|
|
38405
|
+
.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; }
|
|
38330
38406
|
.dshl-stage.idle { cursor: none; }
|
|
38331
38407
|
.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%);
|
|
38332
38408
|
background: rgba(20,20,22,.78); color: #f2f2f3; font-size: 12.5px; white-space: nowrap; backdrop-filter: blur(8px); }
|
|
@@ -38350,11 +38426,11 @@ html[data-platform='darwin'] .dshl.page .dshl-top :is(button, input, label, a, .
|
|
|
38350
38426
|
.dshl-stage:not(.idle) .dshl-sub[data-pos=bottom] { transform: translate(-50%, -64px); }
|
|
38351
38427
|
.dshl-stage:not(.idle) .dshl-sub[data-pos=top] { transform: translate(-50%, 34px); }
|
|
38352
38428
|
|
|
38353
|
-
.dshl-livebadge { position: absolute; top: 12px; left: 12px; z-index: 3; display: inline-flex; align-items: center; gap: 6px; height:
|
|
38354
|
-
background: rgba(0,0,0,.
|
|
38355
|
-
.dshl-livebadge
|
|
38356
|
-
.dshl-livebadge
|
|
38357
|
-
.dshl-livebadge.on svg { color: #
|
|
38429
|
+
.dshl-livebadge { position: absolute; top: 12px; left: 12px; z-index: 3; display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 10px 0 8px; border: 0; border-radius: 999px;
|
|
38430
|
+
background: rgba(0,0,0,.42); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); color: #fff !important; font-size: 11.5px; font-weight: 600; letter-spacing: .06em; line-height: 1; cursor: pointer; transition: opacity .25s, background .15s; }
|
|
38431
|
+
.dshl-livebadge:hover { background: rgba(0,0,0,.6); }
|
|
38432
|
+
.dshl-livebadge svg { width: 10px; height: 10px; flex: none; color: rgba(255,255,255,.5); }
|
|
38433
|
+
.dshl-livebadge.on svg { color: #ff4d4f; filter: drop-shadow(0 0 3px rgba(255,77,79,.8)); animation: dshl-pulse 1.6s ease-in-out infinite; }
|
|
38358
38434
|
|
|
38359
38435
|
.dshl-toast { position: absolute; top: 12px; left: 50%; z-index: 6; display: inline-flex; align-items: center; gap: 10px; max-width: calc(100% - 24px); padding: 8px 14px;
|
|
38360
38436
|
border-radius: 10px; background: rgba(20,20,22,.86); color: #fff; font-size: 13px; box-shadow: 0 6px 20px rgba(0,0,0,.3); transform: translateX(-50%); }
|
package/lib/index.mjs
CHANGED
|
@@ -1990,9 +1990,14 @@ const AHEAD_MAX_LINES = 10;
|
|
|
1990
1990
|
*/
|
|
1991
1991
|
/** Drop the first `skip` seconds after decoding (an output -ss; empty when there is nothing to drop). */
|
|
1992
1992
|
const seekAfter = (skip) => skip > .01 ? ["-ss", skip.toFixed(3)] : [];
|
|
1993
|
+
const SOI = Buffer.from([255, 216]);
|
|
1994
|
+
const EOI = Buffer.from([255, 217]);
|
|
1993
1995
|
var LiveJob = class LiveJob extends EventEmitter {
|
|
1994
1996
|
opts;
|
|
1995
1997
|
deps;
|
|
1998
|
+
/** Real-time picture (own sources): the newest JPEG frame, and whether frames are coming. */
|
|
1999
|
+
lastFrame;
|
|
2000
|
+
previewOn = false;
|
|
1996
2001
|
id;
|
|
1997
2002
|
status = "resolving";
|
|
1998
2003
|
message;
|
|
@@ -2076,6 +2081,7 @@ var LiveJob = class LiveJob extends EventEmitter {
|
|
|
2076
2081
|
vod: this.vod,
|
|
2077
2082
|
duration: this.duration,
|
|
2078
2083
|
recorded: this.vod ? void 0 : this.recordedSec(),
|
|
2084
|
+
preview: this.previewOn,
|
|
2079
2085
|
loginBrowser: this.loginBrowser,
|
|
2080
2086
|
needsLogin: this.needsLogin,
|
|
2081
2087
|
hls: this.hlsName,
|
|
@@ -2463,11 +2469,43 @@ var LiveJob = class LiveJob extends EventEmitter {
|
|
|
2463
2469
|
join(hls, "seg_%05d.ts"),
|
|
2464
2470
|
join(hls, "index.m3u8")
|
|
2465
2471
|
];
|
|
2472
|
+
const preview = (this.opts.source === "device" || this.opts.source === "push") && !!input.video;
|
|
2473
|
+
if (preview) args.push("-map", input.video, "-an", "-vf", "scale=-2:540,fps=15", "-c:v", "mjpeg", "-q:v", "6", "-f", "image2pipe", "pipe:3");
|
|
2466
2474
|
const ff = spawn("ffmpeg", args, { stdio: [
|
|
2467
2475
|
"ignore",
|
|
2468
2476
|
"ignore",
|
|
2469
|
-
"pipe"
|
|
2477
|
+
"pipe",
|
|
2478
|
+
preview ? "pipe" : "ignore"
|
|
2470
2479
|
] });
|
|
2480
|
+
this.previewOn = false;
|
|
2481
|
+
if (preview) {
|
|
2482
|
+
let tail = Buffer.alloc(0);
|
|
2483
|
+
ff.stdio[3].on("data", (chunk) => {
|
|
2484
|
+
if (this.ffmpeg !== ff) return;
|
|
2485
|
+
let buf = tail.length ? Buffer.concat([tail, chunk]) : chunk;
|
|
2486
|
+
for (;;) {
|
|
2487
|
+
const start = buf.indexOf(SOI);
|
|
2488
|
+
if (start < 0) {
|
|
2489
|
+
buf = Buffer.alloc(0);
|
|
2490
|
+
break;
|
|
2491
|
+
}
|
|
2492
|
+
const end = buf.indexOf(EOI, start + 2);
|
|
2493
|
+
if (end < 0) {
|
|
2494
|
+
buf = buf.subarray(start);
|
|
2495
|
+
break;
|
|
2496
|
+
}
|
|
2497
|
+
const frame = Buffer.from(buf.subarray(start, end + 2));
|
|
2498
|
+
buf = buf.subarray(end + 2);
|
|
2499
|
+
this.lastFrame = frame;
|
|
2500
|
+
if (!this.previewOn) {
|
|
2501
|
+
this.previewOn = true;
|
|
2502
|
+
this.emit("state", this.summary());
|
|
2503
|
+
}
|
|
2504
|
+
this.emit("frame", frame);
|
|
2505
|
+
}
|
|
2506
|
+
tail = buf.length > 4e6 ? Buffer.alloc(0) : Buffer.from(buf);
|
|
2507
|
+
});
|
|
2508
|
+
}
|
|
2471
2509
|
this.ffmpeg = ff;
|
|
2472
2510
|
if (this.opts.source === "device") setTimeout(() => {
|
|
2473
2511
|
if (this.ffmpeg !== ff || this.segments().length) return;
|
|
@@ -2498,6 +2536,10 @@ var LiveJob = class LiveJob extends EventEmitter {
|
|
|
2498
2536
|
}
|
|
2499
2537
|
});
|
|
2500
2538
|
ff.on("exit", (code) => {
|
|
2539
|
+
if (this.previewOn && this.ffmpeg === ff) {
|
|
2540
|
+
this.previewOn = false;
|
|
2541
|
+
this.lastFrame = void 0;
|
|
2542
|
+
}
|
|
2501
2543
|
if (this.ffmpeg !== ff) return;
|
|
2502
2544
|
this.ffmpeg = void 0;
|
|
2503
2545
|
if (this.status !== "running") return;
|
|
@@ -6846,6 +6888,25 @@ function apply(ctx, config) {
|
|
|
6846
6888
|
saveAs: typeof b.saveAs === "string" ? b.saveAs.replace(/[\\/:*?"<>|]+/g, " ").slice(0, 150) : "audio"
|
|
6847
6889
|
}));
|
|
6848
6890
|
}
|
|
6891
|
+
if (route === "preview") {
|
|
6892
|
+
const job = jobs.get(url.searchParams.get("jobId") ?? "");
|
|
6893
|
+
if (!job) return send(res, 404, { error: "no such job" });
|
|
6894
|
+
res.writeHead(200, {
|
|
6895
|
+
"content-type": "multipart/x-mixed-replace; boundary=vidiframe",
|
|
6896
|
+
"cache-control": "no-store",
|
|
6897
|
+
connection: "keep-alive"
|
|
6898
|
+
});
|
|
6899
|
+
const write = (f) => {
|
|
6900
|
+
if (res.writableLength > 2e6) return;
|
|
6901
|
+
res.write(`--vidiframe\r\nContent-Type: image/jpeg\r\nContent-Length: ${f.length}\r\n\r\n`);
|
|
6902
|
+
res.write(f);
|
|
6903
|
+
res.write("\r\n");
|
|
6904
|
+
};
|
|
6905
|
+
if (job.lastFrame) write(job.lastFrame);
|
|
6906
|
+
job.on("frame", write);
|
|
6907
|
+
req.on("close", () => job.off("frame", write));
|
|
6908
|
+
return;
|
|
6909
|
+
}
|
|
6849
6910
|
if (route === "job") {
|
|
6850
6911
|
const jid = url.searchParams.get("jobId");
|
|
6851
6912
|
const job = jid ? jobs.get(jid) : jobForConversation(url.searchParams.get("conversationId") ?? "");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-vidi",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.8",
|
|
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",
|