dsh-vidi 0.7.7 → 0.7.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 +200 -138
- package/lib/index.mjs +65 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -34790,9 +34790,10 @@ async function post$1(path, body) {
|
|
|
34790
34790
|
if (!r.ok) throw new Error(j.error ?? `HTTP ${r.status}`);
|
|
34791
34791
|
return j;
|
|
34792
34792
|
}
|
|
34793
|
-
/** Everything most people never touch, behind one gear: recognition and its key, mode, spoken language, site login. */
|
|
34794
34793
|
function Settings(props) {
|
|
34795
|
-
const { engine, onEngine, mode, onMode, spoken, onSpoken, langOptions, login, onLogin, browsers, providers, shared, name, onKey, disabled, open, onOpen, t } = props;
|
|
34794
|
+
const { engine, onEngine, mode, onMode, spoken, onSpoken, langOptions, login, onLogin, browsers, providers, shared, name, onKey, disabled, open, onOpen, update, onCheckUpdate, t } = props;
|
|
34795
|
+
const [checking, setChecking] = (0, react.useState)(false);
|
|
34796
|
+
const [checked, setChecked] = (0, react.useState)(false);
|
|
34796
34797
|
const box = (0, react.useRef)(null);
|
|
34797
34798
|
const [draft, setDraft] = (0, react.useState)("");
|
|
34798
34799
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
@@ -34890,9 +34891,9 @@ function Settings(props) {
|
|
|
34890
34891
|
className: "dshl-pick",
|
|
34891
34892
|
ref: box,
|
|
34892
34893
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
34893
|
-
className: `dshl-ctl icon ${open ? "on" : ""}`,
|
|
34894
|
+
className: `dshl-ctl icon ${open ? "on" : ""} ${update?.newer ? "dot" : ""}`,
|
|
34894
34895
|
onClick: () => onOpen(!open),
|
|
34895
|
-
title: t("in.settings"),
|
|
34896
|
+
title: update?.newer ? t("upd.available").replace("{v}", update.latest ?? "") : t("in.settings"),
|
|
34896
34897
|
"aria-expanded": open,
|
|
34897
34898
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon.gear, {})
|
|
34898
34899
|
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -35120,6 +35121,37 @@ function Settings(props) {
|
|
|
35120
35121
|
}
|
|
35121
35122
|
]
|
|
35122
35123
|
})]
|
|
35124
|
+
}),
|
|
35125
|
+
update && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
35126
|
+
className: "srow col version",
|
|
35127
|
+
children: [
|
|
35128
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
35129
|
+
className: "vline",
|
|
35130
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("upd.version").replace("{v}", update.current) }), update.newer ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
35131
|
+
className: "new",
|
|
35132
|
+
children: t("upd.available").replace("{v}", update.latest ?? "")
|
|
35133
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
35134
|
+
className: "dshl-link",
|
|
35135
|
+
disabled: checking,
|
|
35136
|
+
onClick: () => {
|
|
35137
|
+
setChecking(true);
|
|
35138
|
+
onCheckUpdate?.().finally(() => {
|
|
35139
|
+
setChecking(false);
|
|
35140
|
+
setChecked(true);
|
|
35141
|
+
});
|
|
35142
|
+
},
|
|
35143
|
+
children: checking ? t("upd.checking") : checked && !update.error ? t("upd.latest") : t("upd.check")
|
|
35144
|
+
})]
|
|
35145
|
+
}),
|
|
35146
|
+
update.newer && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
35147
|
+
className: "hint",
|
|
35148
|
+
children: t("upd.how")
|
|
35149
|
+
}),
|
|
35150
|
+
checked && update.error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
35151
|
+
className: "hint",
|
|
35152
|
+
children: t("upd.failed")
|
|
35153
|
+
})
|
|
35154
|
+
]
|
|
35123
35155
|
})
|
|
35124
35156
|
]
|
|
35125
35157
|
})]
|
|
@@ -35389,10 +35421,18 @@ function LiveView(props) {
|
|
|
35389
35421
|
const linesBox = (0, react.useRef)(null);
|
|
35390
35422
|
const fileInput = (0, react.useRef)(null);
|
|
35391
35423
|
const active = !!job && (job.status === "resolving" || job.status === "running" || job.status === "stopping");
|
|
35392
|
-
const [
|
|
35424
|
+
const [update, setUpdate] = (0, react.useState)();
|
|
35425
|
+
(0, react.useEffect)(() => {
|
|
35426
|
+
fetch("/live/api/version").then((r) => r.ok ? r.json() : void 0).then(setUpdate).catch(() => {});
|
|
35427
|
+
}, []);
|
|
35428
|
+
const checkUpdate = async () => {
|
|
35429
|
+
try {
|
|
35430
|
+
setUpdate(await post("version", {}));
|
|
35431
|
+
} catch {}
|
|
35432
|
+
};
|
|
35433
|
+
const [srcTab, setSrcTabRaw] = (0, react.useState)("link");
|
|
35393
35434
|
const setSrcTab = (v) => {
|
|
35394
35435
|
setSrcTabRaw(v);
|
|
35395
|
-
store.set("source", v);
|
|
35396
35436
|
setError("");
|
|
35397
35437
|
};
|
|
35398
35438
|
const devices = useDevices(srcTab === "camera");
|
|
@@ -35896,20 +35936,15 @@ function LiveView(props) {
|
|
|
35896
35936
|
title
|
|
35897
35937
|
}), "device");
|
|
35898
35938
|
};
|
|
35899
|
-
const emptyBox = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
35939
|
+
const emptyBox = () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
35900
35940
|
className: "dshl-drop",
|
|
35901
35941
|
children: [
|
|
35902
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: t(
|
|
35903
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
35904
|
-
|
|
35905
|
-
|
|
35906
|
-
onClick: () => fileInput.current?.click(),
|
|
35907
|
-
children: [
|
|
35908
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon.folder, {}),
|
|
35909
|
-
" ",
|
|
35910
|
-
t("src.file.pick")
|
|
35911
|
-
]
|
|
35942
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: t("empty.title") }),
|
|
35943
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
35944
|
+
className: "dshl-bar center",
|
|
35945
|
+
children: sourceBar
|
|
35912
35946
|
}),
|
|
35947
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: srcTab === "camera" && devices && !mics.length ? t("src.cam.none") : t(srcTab === "file" ? "src.file.body" : srcTab === "camera" ? "src.cam.body" : srcTab === "push" ? "src.push.body" : "empty.body") }),
|
|
35913
35948
|
srcTab === "push" && armed?.rtmp && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
35914
35949
|
className: "dshl-pushinfo",
|
|
35915
35950
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopyRow, {
|
|
@@ -36127,6 +36162,120 @@ function LiveView(props) {
|
|
|
36127
36162
|
children: t("lag.settings")
|
|
36128
36163
|
})]
|
|
36129
36164
|
}) : null;
|
|
36165
|
+
const sourceBar = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
36166
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Pick, {
|
|
36167
|
+
className: "srcpick",
|
|
36168
|
+
value: srcTab,
|
|
36169
|
+
onChange: (v) => setSrcTab(v),
|
|
36170
|
+
title: t("src.tip"),
|
|
36171
|
+
options: [
|
|
36172
|
+
"link",
|
|
36173
|
+
"file",
|
|
36174
|
+
"camera",
|
|
36175
|
+
"push"
|
|
36176
|
+
].map((k) => ({
|
|
36177
|
+
value: k,
|
|
36178
|
+
label: t(`src.${k}`)
|
|
36179
|
+
}))
|
|
36180
|
+
}),
|
|
36181
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
36182
|
+
ref: fileInput,
|
|
36183
|
+
type: "file",
|
|
36184
|
+
accept: "video/*,audio/*,.mkv,.ts,.flv",
|
|
36185
|
+
hidden: true,
|
|
36186
|
+
onChange: (e) => {
|
|
36187
|
+
const f = e.target.files?.[0];
|
|
36188
|
+
if (f) onFile(f);
|
|
36189
|
+
e.target.value = "";
|
|
36190
|
+
}
|
|
36191
|
+
}),
|
|
36192
|
+
(srcTab === "link" || srcTab === "file") && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
36193
|
+
className: "dshl-field",
|
|
36194
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
36195
|
+
type: "text",
|
|
36196
|
+
value: input,
|
|
36197
|
+
placeholder: t(srcTab === "file" ? "src.file.placeholder" : "in.placeholder"),
|
|
36198
|
+
onChange: (e) => setInput(e.target.value),
|
|
36199
|
+
onKeyDown: (e) => {
|
|
36200
|
+
if (e.key === "Enter") start();
|
|
36201
|
+
}
|
|
36202
|
+
}), srcTab === "file" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
36203
|
+
className: "ghost",
|
|
36204
|
+
title: t("in.file.tip"),
|
|
36205
|
+
onClick: () => fileInput.current?.click(),
|
|
36206
|
+
children: uploading !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
36207
|
+
className: "pct",
|
|
36208
|
+
children: t("in.uploading").replace("{p}", String(uploading))
|
|
36209
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon.folder, {})
|
|
36210
|
+
})]
|
|
36211
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
36212
|
+
className: "dshl-btn primary",
|
|
36213
|
+
onClick: () => start(),
|
|
36214
|
+
disabled: !classify(input),
|
|
36215
|
+
children: t("in.open")
|
|
36216
|
+
})] }),
|
|
36217
|
+
srcTab === "camera" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
36218
|
+
className: "dshl-devbar",
|
|
36219
|
+
children: devices === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
36220
|
+
className: "dshl-muted",
|
|
36221
|
+
children: t("src.cam.loading")
|
|
36222
|
+
}) : mics.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Pick, {
|
|
36223
|
+
value: camV,
|
|
36224
|
+
options: videoOptions,
|
|
36225
|
+
onChange: setCamVideo,
|
|
36226
|
+
title: t("dev.video")
|
|
36227
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Pick, {
|
|
36228
|
+
value: camA,
|
|
36229
|
+
options: mics.map((d) => ({
|
|
36230
|
+
value: d.id,
|
|
36231
|
+
label: d.name
|
|
36232
|
+
})),
|
|
36233
|
+
onChange: setCamAudio,
|
|
36234
|
+
title: t("dev.audio")
|
|
36235
|
+
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
36236
|
+
className: "dshl-muted",
|
|
36237
|
+
children: t("src.cam.none")
|
|
36238
|
+
})
|
|
36239
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
36240
|
+
className: "dshl-btn primary",
|
|
36241
|
+
onClick: () => void startDevice(),
|
|
36242
|
+
disabled: !camA,
|
|
36243
|
+
children: t("dev.start")
|
|
36244
|
+
})] }),
|
|
36245
|
+
srcTab === "push" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
36246
|
+
className: "dshl-field static",
|
|
36247
|
+
title: t("src.push.body"),
|
|
36248
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(armed?.rtmp ? "src.push.bar" : active ? "src.push.busy" : "src.push.wait") })
|
|
36249
|
+
}),
|
|
36250
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Settings, {
|
|
36251
|
+
engine: engine || "local",
|
|
36252
|
+
onEngine: setEngine,
|
|
36253
|
+
mode,
|
|
36254
|
+
onMode: changeMode,
|
|
36255
|
+
spoken,
|
|
36256
|
+
onSpoken: setSpoken,
|
|
36257
|
+
langOptions,
|
|
36258
|
+
login,
|
|
36259
|
+
onLogin: chooseLogin,
|
|
36260
|
+
browsers,
|
|
36261
|
+
providers,
|
|
36262
|
+
shared: keysShared,
|
|
36263
|
+
name: engineName,
|
|
36264
|
+
onKey: (id, k) => {
|
|
36265
|
+
setProviders((ps) => ps.map((p) => p.id === id ? {
|
|
36266
|
+
...p,
|
|
36267
|
+
key: k,
|
|
36268
|
+
ready: k.set
|
|
36269
|
+
} : p));
|
|
36270
|
+
if (k.set) setError("");
|
|
36271
|
+
},
|
|
36272
|
+
open: settingsOpen,
|
|
36273
|
+
onOpen: setSettingsOpen,
|
|
36274
|
+
update,
|
|
36275
|
+
onCheckUpdate: checkUpdate,
|
|
36276
|
+
t
|
|
36277
|
+
})
|
|
36278
|
+
] });
|
|
36130
36279
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
36131
36280
|
className: `dshl ${theater && !activeBench ? "theater" : ""} ${pageMode ? "page" : ""} ${activeBench ? "bench" : ""} ${pageMode && !sideOpen && !activeBench ? "noside" : ""}`,
|
|
36132
36281
|
style: pageMode ? { ["--side-w"]: `${sideW}px` } : void 0,
|
|
@@ -36154,7 +36303,7 @@ function LiveView(props) {
|
|
|
36154
36303
|
if (f && !active) onFile(f);
|
|
36155
36304
|
},
|
|
36156
36305
|
children: [
|
|
36157
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
|
|
36306
|
+
(hasMain || onClose) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
|
|
36158
36307
|
className: "dshl-top",
|
|
36159
36308
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
36160
36309
|
className: "dshl-bar",
|
|
@@ -36165,117 +36314,8 @@ function LiveView(props) {
|
|
|
36165
36314
|
title: t("page.back"),
|
|
36166
36315
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon.arrow, {})
|
|
36167
36316
|
}),
|
|
36168
|
-
|
|
36169
|
-
|
|
36170
|
-
value: srcTab,
|
|
36171
|
-
onChange: (v) => setSrcTab(v),
|
|
36172
|
-
title: t("src.tip"),
|
|
36173
|
-
options: [
|
|
36174
|
-
"link",
|
|
36175
|
-
"file",
|
|
36176
|
-
"camera",
|
|
36177
|
-
"push"
|
|
36178
|
-
].map((k) => ({
|
|
36179
|
-
value: k,
|
|
36180
|
-
label: t(`src.${k}`)
|
|
36181
|
-
}))
|
|
36182
|
-
}),
|
|
36183
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
36184
|
-
ref: fileInput,
|
|
36185
|
-
type: "file",
|
|
36186
|
-
accept: "video/*,audio/*,.mkv,.ts,.flv",
|
|
36187
|
-
hidden: true,
|
|
36188
|
-
onChange: (e) => {
|
|
36189
|
-
const f = e.target.files?.[0];
|
|
36190
|
-
if (f) onFile(f);
|
|
36191
|
-
e.target.value = "";
|
|
36192
|
-
}
|
|
36193
|
-
}),
|
|
36194
|
-
(srcTab === "link" || srcTab === "file") && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
36195
|
-
className: "dshl-field",
|
|
36196
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
36197
|
-
type: "text",
|
|
36198
|
-
value: input,
|
|
36199
|
-
placeholder: t(srcTab === "file" ? "src.file.placeholder" : "in.placeholder"),
|
|
36200
|
-
onChange: (e) => setInput(e.target.value),
|
|
36201
|
-
onKeyDown: (e) => {
|
|
36202
|
-
if (e.key === "Enter") start();
|
|
36203
|
-
}
|
|
36204
|
-
}), srcTab === "file" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
36205
|
-
className: "ghost",
|
|
36206
|
-
title: t("in.file.tip"),
|
|
36207
|
-
onClick: () => fileInput.current?.click(),
|
|
36208
|
-
children: uploading !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
36209
|
-
className: "pct",
|
|
36210
|
-
children: t("in.uploading").replace("{p}", String(uploading))
|
|
36211
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon.folder, {})
|
|
36212
|
-
})]
|
|
36213
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
36214
|
-
className: "dshl-btn primary",
|
|
36215
|
-
onClick: () => start(),
|
|
36216
|
-
disabled: !classify(input),
|
|
36217
|
-
children: t("in.open")
|
|
36218
|
-
})] }),
|
|
36219
|
-
srcTab === "camera" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
36220
|
-
className: "dshl-devbar",
|
|
36221
|
-
children: devices === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
36222
|
-
className: "dshl-muted",
|
|
36223
|
-
children: t("src.cam.loading")
|
|
36224
|
-
}) : mics.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Pick, {
|
|
36225
|
-
value: camV,
|
|
36226
|
-
options: videoOptions,
|
|
36227
|
-
onChange: setCamVideo,
|
|
36228
|
-
title: t("dev.video")
|
|
36229
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Pick, {
|
|
36230
|
-
value: camA,
|
|
36231
|
-
options: mics.map((d) => ({
|
|
36232
|
-
value: d.id,
|
|
36233
|
-
label: d.name
|
|
36234
|
-
})),
|
|
36235
|
-
onChange: setCamAudio,
|
|
36236
|
-
title: t("dev.audio")
|
|
36237
|
-
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
36238
|
-
className: "dshl-muted",
|
|
36239
|
-
children: t("src.cam.none")
|
|
36240
|
-
})
|
|
36241
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
36242
|
-
className: "dshl-btn primary",
|
|
36243
|
-
onClick: () => void startDevice(),
|
|
36244
|
-
disabled: !camA,
|
|
36245
|
-
children: t("dev.start")
|
|
36246
|
-
})] }),
|
|
36247
|
-
srcTab === "push" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
36248
|
-
className: "dshl-field static",
|
|
36249
|
-
title: t("src.push.body"),
|
|
36250
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(armed?.rtmp ? "src.push.bar" : active ? "src.push.busy" : "src.push.wait") })
|
|
36251
|
-
}),
|
|
36252
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Settings, {
|
|
36253
|
-
engine: engine || "local",
|
|
36254
|
-
onEngine: setEngine,
|
|
36255
|
-
mode,
|
|
36256
|
-
onMode: changeMode,
|
|
36257
|
-
spoken,
|
|
36258
|
-
onSpoken: setSpoken,
|
|
36259
|
-
langOptions,
|
|
36260
|
-
login,
|
|
36261
|
-
onLogin: chooseLogin,
|
|
36262
|
-
browsers,
|
|
36263
|
-
providers,
|
|
36264
|
-
shared: keysShared,
|
|
36265
|
-
name: engineName,
|
|
36266
|
-
onKey: (id, k) => {
|
|
36267
|
-
setProviders((ps) => ps.map((p) => p.id === id ? {
|
|
36268
|
-
...p,
|
|
36269
|
-
key: k,
|
|
36270
|
-
ready: k.set
|
|
36271
|
-
} : p));
|
|
36272
|
-
if (k.set) setError("");
|
|
36273
|
-
},
|
|
36274
|
-
open: settingsOpen,
|
|
36275
|
-
onOpen: setSettingsOpen,
|
|
36276
|
-
t
|
|
36277
|
-
}),
|
|
36278
|
-
pageMode && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
36317
|
+
hasMain && sourceBar,
|
|
36318
|
+
pageMode && hasMain && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
36279
36319
|
className: `dshl-ctl icon ${sideOpen ? "on" : ""}`,
|
|
36280
36320
|
title: t(sideOpen ? "side.hide" : "side.show"),
|
|
36281
36321
|
"aria-pressed": sideOpen,
|
|
@@ -36305,15 +36345,15 @@ function LiveView(props) {
|
|
|
36305
36345
|
children: t("st.retry")
|
|
36306
36346
|
})]
|
|
36307
36347
|
}),
|
|
36308
|
-
!hasMain
|
|
36348
|
+
!hasMain ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
36309
36349
|
className: "dshl-empty",
|
|
36310
|
-
children: emptyBox
|
|
36350
|
+
children: emptyBox()
|
|
36311
36351
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("main", {
|
|
36312
36352
|
className: "dshl-main",
|
|
36313
36353
|
ref: mainRef,
|
|
36314
36354
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
36315
36355
|
className: "dshl-col",
|
|
36316
|
-
children: !hasMain || !job ? emptyBox : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
36356
|
+
children: !hasMain || !job ? emptyBox() : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
36317
36357
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Player, {
|
|
36318
36358
|
ref: player,
|
|
36319
36359
|
src: job.status === "resolving" || job.status === "error" ? null : `/live/media/${encodeURIComponent(job.id)}/${job.hls ?? "hls"}/index.m3u8`,
|
|
@@ -37168,7 +37208,7 @@ const en = {
|
|
|
37168
37208
|
"in.uploading": "Uploading {p}%",
|
|
37169
37209
|
"in.drop": "Drop to start",
|
|
37170
37210
|
"empty.title": "Subtitles for any video",
|
|
37171
|
-
"empty.body": "
|
|
37211
|
+
"empty.body": "YouTube, Bilibili and other sites, videos and live streams.",
|
|
37172
37212
|
"st.resolving": "Opening…",
|
|
37173
37213
|
"st.login": "Using your {b} login… if macOS asks, choose “Always Allow”.",
|
|
37174
37214
|
"st.live": "Live",
|
|
@@ -37181,6 +37221,13 @@ const en = {
|
|
|
37181
37221
|
"note.noModel": "No model set up, so subtitles stay in the original language.",
|
|
37182
37222
|
"note.install": "Install first:",
|
|
37183
37223
|
"push.title": "Stream here from OBS",
|
|
37224
|
+
"upd.version": "Vidi {v}",
|
|
37225
|
+
"upd.check": "Check for updates",
|
|
37226
|
+
"upd.checking": "Checking…",
|
|
37227
|
+
"upd.latest": "Up to date",
|
|
37228
|
+
"upd.available": "Version {v} is out",
|
|
37229
|
+
"upd.how": "Update it from DSH’s Plugins page, then restart the app.",
|
|
37230
|
+
"upd.failed": "Could not reach npm just now; it checks again tomorrow.",
|
|
37184
37231
|
"src.tip": "Where the video comes from",
|
|
37185
37232
|
"src.link": "Link",
|
|
37186
37233
|
"src.file": "Local file",
|
|
@@ -37188,10 +37235,10 @@ const en = {
|
|
|
37188
37235
|
"src.push": "OBS stream",
|
|
37189
37236
|
"src.file.placeholder": "Path of a video or audio file",
|
|
37190
37237
|
"src.file.title": "Open a video on this computer",
|
|
37191
|
-
"src.file.body": "
|
|
37238
|
+
"src.file.body": "Choose a file with the folder button, or drop it anywhere on this page.",
|
|
37192
37239
|
"src.file.pick": "Choose a file",
|
|
37193
37240
|
"src.cam.title": "Talk to the camera",
|
|
37194
|
-
"src.cam.body": "Pick the picture and the microphone
|
|
37241
|
+
"src.cam.body": "Pick the picture and the microphone, press Start: subtitles appear as you speak.",
|
|
37195
37242
|
"src.cam.none": "No camera or microphone found on this computer.",
|
|
37196
37243
|
"src.cam.loading": "Looking for cameras…",
|
|
37197
37244
|
"src.push.title": "Stream from OBS",
|
|
@@ -37586,7 +37633,7 @@ const zh = {
|
|
|
37586
37633
|
"in.uploading": "上传中 {p}%",
|
|
37587
37634
|
"in.drop": "松手开始",
|
|
37588
37635
|
"empty.title": "给任何视频配字幕",
|
|
37589
|
-
"empty.body": "
|
|
37636
|
+
"empty.body": "支持 YouTube、B 站等网站的视频和直播",
|
|
37590
37637
|
"st.resolving": "正在打开…",
|
|
37591
37638
|
"st.login": "正在用 {b} 的登录状态…如果系统弹窗询问,点「始终允许」",
|
|
37592
37639
|
"st.live": "直播中",
|
|
@@ -37599,6 +37646,13 @@ const zh = {
|
|
|
37599
37646
|
"note.noModel": "还没配置模型,字幕只显示原文",
|
|
37600
37647
|
"note.install": "需要先安装:",
|
|
37601
37648
|
"push.title": "用 OBS 推流到这里",
|
|
37649
|
+
"upd.version": "Vidi {v}",
|
|
37650
|
+
"upd.check": "检查更新",
|
|
37651
|
+
"upd.checking": "正在检查…",
|
|
37652
|
+
"upd.latest": "已是最新",
|
|
37653
|
+
"upd.available": "有新版本 {v}",
|
|
37654
|
+
"upd.how": "到 DSH 的「插件」页面更新,再重启 app。",
|
|
37655
|
+
"upd.failed": "这会儿连不上 npm,明天会再查一次。",
|
|
37602
37656
|
"src.tip": "视频从哪里来",
|
|
37603
37657
|
"src.link": "链接",
|
|
37604
37658
|
"src.file": "本地文件",
|
|
@@ -37606,10 +37660,10 @@ const zh = {
|
|
|
37606
37660
|
"src.push": "OBS 推流",
|
|
37607
37661
|
"src.file.placeholder": "视频或音频文件的路径",
|
|
37608
37662
|
"src.file.title": "打开这台电脑上的视频",
|
|
37609
|
-
"src.file.body": "
|
|
37663
|
+
"src.file.body": "点文件夹按钮选择,或者直接把文件拖进来。",
|
|
37610
37664
|
"src.file.pick": "选择文件",
|
|
37611
37665
|
"src.cam.title": "对着摄像头说话",
|
|
37612
|
-
"src.cam.body": "
|
|
37666
|
+
"src.cam.body": "选好画面和麦克风,点开始,字幕边说边出。",
|
|
37613
37667
|
"src.cam.none": "这台电脑上没有找到摄像头或麦克风。",
|
|
37614
37668
|
"src.cam.loading": "正在找摄像头…",
|
|
37615
37669
|
"src.push.title": "用 OBS 推流过来",
|
|
@@ -37992,6 +38046,8 @@ const CSS = `
|
|
|
37992
38046
|
background: var(--l1); color: var(--muted); font-size: 13px; cursor: pointer; }
|
|
37993
38047
|
.dshl-ctl.icon { width: 40px; padding: 0; }
|
|
37994
38048
|
.dshl-ctl.icon svg { width: 18px; height: 18px; }
|
|
38049
|
+
.dshl-ctl.icon.dot { position: relative; }
|
|
38050
|
+
.dshl-ctl.icon.dot::after { content: ''; position: absolute; top: 8px; right: 8px; width: 7px; height: 7px; border-radius: 50%; background: #e5484d; box-shadow: 0 0 0 2px var(--bg); }
|
|
37995
38051
|
.dshl-ctl:hover, .dshl-ctl.on { background: var(--hover); color: var(--ink); }
|
|
37996
38052
|
|
|
37997
38053
|
.dshl-note { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 12px 20px 0; padding: 9px 14px; border: 1px solid var(--line); border-radius: 10px;
|
|
@@ -38010,6 +38066,10 @@ const CSS = `
|
|
|
38010
38066
|
|
|
38011
38067
|
/* ── Empty state ── */
|
|
38012
38068
|
.dshl-empty { padding: 28px 20px; }
|
|
38069
|
+
.dshl.page .dshl-empty { flex: 1; display: flex; align-items: center; justify-content: center; min-height: 0; overflow: auto; }
|
|
38070
|
+
.dshl.page .dshl-empty > .dshl-drop { border: 0; padding-top: 0; }
|
|
38071
|
+
.dshl-drop > .dshl-bar.center { display: flex; align-items: center; gap: 8px; width: 100%; max-width: 680px; margin: 22px 0 12px; text-align: left; }
|
|
38072
|
+
.dshl-drop > .dshl-bar.center + p { font-size: 13px; color: var(--faint); }
|
|
38013
38073
|
.dshl-drop { display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; max-width: 760px; margin: 0 auto; padding: 56px 24px 48px;
|
|
38014
38074
|
border: 1.5px dashed var(--line); border-radius: 16px; text-align: center; }
|
|
38015
38075
|
.dshl-col > .dshl-drop { max-width: none; min-height: 320px; aspect-ratio: 16 / 9; padding: 32px 24px; border-radius: 12px; }
|
|
@@ -38466,6 +38526,8 @@ body > .dshl-menu { z-index: 2147483002; }
|
|
|
38466
38526
|
.dshl .srow .duo { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); gap: 8px; }
|
|
38467
38527
|
.dshl .srow .duo > .dshl-select { width: 100%; }
|
|
38468
38528
|
.dshl-pop.settings .srow + .srow { border-top: 1px solid var(--line-soft); }
|
|
38529
|
+
.dshl-pop.settings .srow.version .vline { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; color: var(--faint); font-size: 12.5px; }
|
|
38530
|
+
.dshl-pop.settings .srow.version .new { color: #e5484d; font-weight: 550; }
|
|
38469
38531
|
.dshl-pop.settings .srow > .dshl-select, .dshl-pop.settings .srow > .dshl-seg { width: 168px; }
|
|
38470
38532
|
.dshl-pop.settings .srow > .dshl-seg button { flex: 1; padding: 0 6px; }
|
|
38471
38533
|
.dshl .srow.col { display: grid; justify-content: stretch; gap: 8px; padding: 10px 0; }
|
package/lib/index.mjs
CHANGED
|
@@ -5778,6 +5778,65 @@ function metered(llm, meter) {
|
|
|
5778
5778
|
}
|
|
5779
5779
|
};
|
|
5780
5780
|
}
|
|
5781
|
+
// src/updates.ts
|
|
5782
|
+
const DAY = 864e5;
|
|
5783
|
+
/** a > b for plain x.y.z versions (pre-releases count as older than the release). */
|
|
5784
|
+
function greater(a, b) {
|
|
5785
|
+
const parse = (v) => {
|
|
5786
|
+
const [core = "", pre] = v.split("-");
|
|
5787
|
+
return {
|
|
5788
|
+
n: core.split(".").map((x) => Number(x) || 0),
|
|
5789
|
+
pre
|
|
5790
|
+
};
|
|
5791
|
+
};
|
|
5792
|
+
const x = parse(a), y = parse(b);
|
|
5793
|
+
for (let i = 0; i < 3; i++) if ((x.n[i] ?? 0) !== (y.n[i] ?? 0)) return (x.n[i] ?? 0) > (y.n[i] ?? 0);
|
|
5794
|
+
return !x.pre && !!y.pre;
|
|
5795
|
+
}
|
|
5796
|
+
function updateChecker(log) {
|
|
5797
|
+
let current = "0.0.0";
|
|
5798
|
+
try {
|
|
5799
|
+
current = String(JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version);
|
|
5800
|
+
} catch {}
|
|
5801
|
+
let latest, checkedAt, error;
|
|
5802
|
+
let running;
|
|
5803
|
+
const state = () => ({
|
|
5804
|
+
current,
|
|
5805
|
+
latest,
|
|
5806
|
+
newer: !!latest && greater(latest, current),
|
|
5807
|
+
checkedAt,
|
|
5808
|
+
error
|
|
5809
|
+
});
|
|
5810
|
+
const check = () => running ??= (async () => {
|
|
5811
|
+
try {
|
|
5812
|
+
const r = await fetch("https://registry.npmjs.org/dsh-vidi/latest", {
|
|
5813
|
+
signal: AbortSignal.timeout(1e4),
|
|
5814
|
+
headers: { accept: "application/json" }
|
|
5815
|
+
});
|
|
5816
|
+
if (!r.ok) throw new Error(`registry ${r.status}`);
|
|
5817
|
+
latest = String((await r.json()).version ?? "") || void 0;
|
|
5818
|
+
error = void 0;
|
|
5819
|
+
if (latest && greater(latest, current)) log(`update: ${latest} available (running ${current})`);
|
|
5820
|
+
} catch (err) {
|
|
5821
|
+
error = err.message;
|
|
5822
|
+
} finally {
|
|
5823
|
+
checkedAt = Date.now();
|
|
5824
|
+
running = void 0;
|
|
5825
|
+
}
|
|
5826
|
+
return state();
|
|
5827
|
+
})();
|
|
5828
|
+
check();
|
|
5829
|
+
const timer = setInterval(() => void check(), DAY);
|
|
5830
|
+
timer.unref?.();
|
|
5831
|
+
return {
|
|
5832
|
+
state: () => {
|
|
5833
|
+
if (!checkedAt || Date.now() - checkedAt > DAY) check();
|
|
5834
|
+
return state();
|
|
5835
|
+
},
|
|
5836
|
+
check,
|
|
5837
|
+
stop: () => clearInterval(timer)
|
|
5838
|
+
};
|
|
5839
|
+
}
|
|
5781
5840
|
// src/index.ts
|
|
5782
5841
|
/**
|
|
5783
5842
|
* dsh-vidi host half: live stream jobs, local/online speech providers,
|
|
@@ -6389,6 +6448,7 @@ function apply(ctx, config) {
|
|
|
6389
6448
|
}
|
|
6390
6449
|
return send(res, 200, job.summary());
|
|
6391
6450
|
}
|
|
6451
|
+
if (route === "version") return send(res, 200, req.method === "POST" ? await updates.check() : updates.state());
|
|
6392
6452
|
if (route === "devices") {
|
|
6393
6453
|
const devices = await listDevices();
|
|
6394
6454
|
log(`devices: ${devices.filter((d) => d.kind !== "mic").length} picture, ${devices.filter((d) => d.kind === "mic").length} microphone`);
|
|
@@ -6828,6 +6888,11 @@ function apply(ctx, config) {
|
|
|
6828
6888
|
path: "/live/media",
|
|
6829
6889
|
handler: media
|
|
6830
6890
|
}), "dsh-vidi: media route");
|
|
6891
|
+
let updates;
|
|
6892
|
+
ctx.effect(() => {
|
|
6893
|
+
updates = updateChecker(log);
|
|
6894
|
+
return () => updates.stop();
|
|
6895
|
+
}, "dsh-vidi: update check");
|
|
6831
6896
|
ctx.effect(() => startOverlay(config.overlayPort, () => [...jobs.values()].filter((j) => j.summary().status === "running").at(-1) ?? [...jobs.values()].at(-1), (fn) => {
|
|
6832
6897
|
bus.on("job", fn);
|
|
6833
6898
|
return () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-vidi",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.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",
|