dsh-bili-watch 0.2.5 → 0.2.7
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/README.md +2 -2
- package/client.js +82 -2
- package/index.js +37 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ dsh plugin --profile web add dsh-bili-watch
|
|
|
25
25
|
|
|
26
26
|
插件依赖 DSH 自带的 `@deepseek-ai/dsh-tools`(peer,optional)。profile 里缺这个包时 `index.js` 会报 `ERR_MODULE_NOT_FOUND`,先补装再重启。
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
插件市场已收录(2026-10-21):在市场里搜 `dsh-bili-watch` 直接安装,这是最省事的方式;市场里暂时搜不到时,按上面的命令装。
|
|
29
29
|
|
|
30
30
|
### 只当命令行工具
|
|
31
31
|
|
|
@@ -47,7 +47,7 @@ dsh plugin --profile web remove dsh-bili-watch
|
|
|
47
47
|
|
|
48
48
|
## 插件面板
|
|
49
49
|
|
|
50
|
-
装进 DSH
|
|
50
|
+
装进 DSH 后,打开插件列表,点 `dsh-bili-watch` 卡片进详情页,里面有一块面板(不用先进设置),常规操作无需去对话里敲命令:
|
|
51
51
|
|
|
52
52
|
- **依赖状态**:ffmpeg、yt-dlp、ASR 环境、OCR 环境、转写模型、登录凭证逐项列出是否就绪、实际路径、占用体积。缺的点击行末「下载」,后台跑 `scripts/fetch-deps`(体积是估算值,实际以跑完为准)。
|
|
53
53
|
- **扫码登录**:凭证未登录时点「重新扫码」,面板里弹出二维码,手机 B 站扫完自动刷新。凭证保存在本机 `deps/credential.json`。
|
package/client.js
CHANGED
|
@@ -364,22 +364,102 @@ window.__ModuleLoader__.load({
|
|
|
364
364
|
)
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
+
// 缺依赖横幅(shell.overlay)
|
|
368
|
+
function OverlayBanner() {
|
|
369
|
+
const [st, setSt] = React.useState(null)
|
|
370
|
+
const [visible, setVisible] = React.useState(false)
|
|
371
|
+
const [dismissed, setDismissed] = React.useState(false)
|
|
372
|
+
|
|
373
|
+
React.useEffect(() => {
|
|
374
|
+
jget('/status').then((j) => {
|
|
375
|
+
if (!j.ok) return
|
|
376
|
+
setSt(j.data)
|
|
377
|
+
const missing = (j.data.deps || []).filter((d) => !d.ok)
|
|
378
|
+
if (missing.length > 0 && j.data.ui?.hintDismissed !== true) {
|
|
379
|
+
setVisible(true)
|
|
380
|
+
}
|
|
381
|
+
}).catch(() => {})
|
|
382
|
+
}, [])
|
|
383
|
+
|
|
384
|
+
if (!visible || dismissed) return null
|
|
385
|
+
const missing = (st?.deps || []).filter((d) => !d.ok)
|
|
386
|
+
const names = missing.map((d) => d.name).join('、')
|
|
387
|
+
|
|
388
|
+
return h('div', { 'data-bl-overlay': '' },
|
|
389
|
+
h('span', { 'data-bl-overlay-text': '' }, `bili-watch 还缺 ${missing.length} 个依赖(${names})`),
|
|
390
|
+
h('button', { 'data-bl-btn': 'primary', onClick: async () => { await jpost('/download', { target: 'all' }) } }, '去安装'),
|
|
391
|
+
h('button', { 'data-bl-btn': '', onClick: () => setDismissed(true) }, '×'),
|
|
392
|
+
h('button', { 'data-bl-btn': '', onClick: async () => {
|
|
393
|
+
await jpost('/config', { hintDismissed: true })
|
|
394
|
+
setDismissed(true)
|
|
395
|
+
} }, '不再提示'),
|
|
396
|
+
)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// 启用指引(plugins.bundle.activation)
|
|
400
|
+
function ActivationGuide() {
|
|
401
|
+
const [seen, setSeen] = React.useState(false)
|
|
402
|
+
React.useEffect(() => {
|
|
403
|
+
jget('/status').then((j) => {
|
|
404
|
+
if (j.ok && j.data.ui?.activationSeen === true) setSeen(true)
|
|
405
|
+
}).catch(() => {})
|
|
406
|
+
}, [])
|
|
407
|
+
if (seen) return null
|
|
408
|
+
return h('div', { 'data-bl-activation': '' },
|
|
409
|
+
h('div', { 'data-bl-title': '' }, 'B站看视频插件已启用'),
|
|
410
|
+
h('div', { 'data-bl-note': '' }, '用它之前建议先装依赖:ffmpeg(抽帧/取音轨)、转写模型(本地转写)。没装也能用:元数据、评论、弹幕、搜索、热榜。'),
|
|
411
|
+
h('div', { 'data-bl-row': '' },
|
|
412
|
+
h('button', { 'data-bl-btn': 'primary', onClick: async () => { await jpost('/download', { target: 'all' }) } }, '去安装'),
|
|
413
|
+
h('button', { 'data-bl-btn': '', onClick: async () => {
|
|
414
|
+
await jpost('/config', { activationSeen: true })
|
|
415
|
+
setSeen(true)
|
|
416
|
+
} }, '先不装')),
|
|
417
|
+
)
|
|
418
|
+
}
|
|
419
|
+
|
|
367
420
|
return {
|
|
368
421
|
inject: ['slots'],
|
|
369
422
|
apply(ctx) {
|
|
370
423
|
const style = document.createElement('style')
|
|
371
424
|
style.setAttribute('data-plugin-css', 'dsh-bili-watch')
|
|
372
|
-
style.textContent = CSS
|
|
425
|
+
style.textContent = CSS + `
|
|
426
|
+
[data-bl-overlay]{position:fixed;top:0;left:0;right:0;z-index:9999;display:flex;align-items:center;gap:12px;padding:8px 16px;background:var(--dsw-alias-bg-strong,#2d2d2d);color:var(--dsw-alias-label-primary,#e5e7eb);font-size:13px;box-shadow:0 2px 8px rgba(0,0,0,.3);}
|
|
427
|
+
[data-bl-overlay-text]{flex:1;}
|
|
428
|
+
[data-bl-activation]{display:flex;flex-direction:column;gap:8px;padding:12px;border:1px solid var(--dsw-alias-border-l2,rgba(128,128,128,.18));border-radius:8px;background:var(--dsw-alias-bg2,rgba(255,255,255,.04));}
|
|
429
|
+
`
|
|
373
430
|
document.head.appendChild(style)
|
|
374
431
|
if (typeof ctx.effect === 'function') ctx.effect(() => () => style.remove(), 'dsh-bili-watch: 面板样式')
|
|
375
432
|
|
|
376
433
|
const slots = (typeof ctx.get === 'function' ? ctx.get('slots') : undefined) || ctx.slots
|
|
377
434
|
if (!slots) return
|
|
378
|
-
|
|
435
|
+
|
|
436
|
+
// ① 插件详情页配置区(原有)
|
|
379
437
|
slots.inject('plugins.bundle.config', () => slots.register(
|
|
380
438
|
{ name: 'plugins.bundle.config', key: PKG },
|
|
381
439
|
Panel,
|
|
382
440
|
))
|
|
441
|
+
|
|
442
|
+
// ② 启用指引(用户从插件列表点「启用」后渲染)
|
|
443
|
+
slots.inject('plugins.bundle.activation', () => slots.register(
|
|
444
|
+
{ name: 'plugins.bundle.activation', key: PKG },
|
|
445
|
+
ActivationGuide,
|
|
446
|
+
))
|
|
447
|
+
|
|
448
|
+
// ③ 缺依赖横幅(全页浮层)
|
|
449
|
+
slots.inject('shell.overlay', () => slots.register(
|
|
450
|
+
{ name: 'shell.overlay', key: PKG },
|
|
451
|
+
OverlayBanner,
|
|
452
|
+
))
|
|
453
|
+
|
|
454
|
+
// ④ 设置页双面板(读 showInSettings 决定是否注册)
|
|
455
|
+
jget('/status').then((j) => {
|
|
456
|
+
if (j.ok && j.data.ui?.showInSettings !== false) {
|
|
457
|
+
slots.inject('settings.section', () => slots.register(
|
|
458
|
+
{ name: 'settings.section', key: PKG, title: '刷B站' },
|
|
459
|
+
Panel,
|
|
460
|
+
))
|
|
461
|
+
}
|
|
462
|
+
}).catch(() => {})
|
|
383
463
|
},
|
|
384
464
|
}
|
|
385
465
|
},
|
package/index.js
CHANGED
|
@@ -14,7 +14,14 @@ import { appendFileSync, existsSync, readFileSync, readdirSync, rmSync, statSync
|
|
|
14
14
|
import { tmpdir } from 'node:os'
|
|
15
15
|
import { dirname, join } from 'node:path'
|
|
16
16
|
import { fileURLToPath } from 'node:url'
|
|
17
|
-
|
|
17
|
+
// @deepseek-ai/dsh-tools 是可选 peer:桌面版 autoInstallPeers:false,市场安装时不会自动装上。
|
|
18
|
+
// 缺了只少一个 bili_watch 工具,/bili 命令、插件面板与路由照常工作,不能因此整块加载失败。
|
|
19
|
+
let defineTool = null
|
|
20
|
+
try {
|
|
21
|
+
({ defineTool } = await import('@deepseek-ai/dsh-tools'))
|
|
22
|
+
} catch {
|
|
23
|
+
defineTool = null
|
|
24
|
+
}
|
|
18
25
|
import QRCode from 'qrcode'
|
|
19
26
|
|
|
20
27
|
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
@@ -38,6 +45,21 @@ const FFMPEG_CANDS = [
|
|
|
38
45
|
const HF_HOME = [join(DEPS, '.hf-home'), join(PROJ, '.hf-home')]
|
|
39
46
|
const SCRIPTS = join(HERE, 'scripts')
|
|
40
47
|
const CACHE_CFG = join(DEPS, 'cache-config.json')
|
|
48
|
+
const UI_CFG = join(DEPS, 'ui-config.json')
|
|
49
|
+
const DEFAULT_UI_CFG = { hintDismissed: false, activationSeen: false, showInSettings: true }
|
|
50
|
+
|
|
51
|
+
function uiCfg() {
|
|
52
|
+
try {
|
|
53
|
+
const raw = JSON.parse(readFileSync(UI_CFG, 'utf8'))
|
|
54
|
+
return { ...DEFAULT_UI_CFG, ...raw }
|
|
55
|
+
} catch { return { ...DEFAULT_UI_CFG } }
|
|
56
|
+
}
|
|
57
|
+
function writeUiCfg(patch) {
|
|
58
|
+
const cur = uiCfg()
|
|
59
|
+
const next = { ...cur, ...patch }
|
|
60
|
+
writeFileSync(UI_CFG, JSON.stringify(next, null, 2), 'utf8')
|
|
61
|
+
return next
|
|
62
|
+
}
|
|
41
63
|
const DEFAULT_CACHE_DAYS = 7
|
|
42
64
|
const DEFAULT_CACHE_MAX_MB = 2048
|
|
43
65
|
const DOWNLOAD_LOG = join(DEPS, 'download.log')
|
|
@@ -565,6 +587,7 @@ function apply(ctx) {
|
|
|
565
587
|
// ② 同名工具:模型可调,需要看视频时自己来,不依赖用户敲命令
|
|
566
588
|
// 必须用 defineTool 包一层:ctx.tools.register 收的是编译好的 tool 对象,
|
|
567
589
|
// 直接传 options 会让发往模型的 parameters 变成 null(报 INVALID_REQUEST: schema must be type "object")。
|
|
590
|
+
if (defineTool) {
|
|
568
591
|
ctx.tools.register(defineTool({
|
|
569
592
|
name: 'bili_watch',
|
|
570
593
|
description:
|
|
@@ -643,6 +666,9 @@ function apply(ctx) {
|
|
|
643
666
|
return { ok: r.ok, text: r.text }
|
|
644
667
|
},
|
|
645
668
|
}))
|
|
669
|
+
} else {
|
|
670
|
+
console.warn('[dsh-bili-watch] 未找到 @deepseek-ai/dsh-tools,bili_watch 工具未注册(/bili 命令与插件面板不受影响)')
|
|
671
|
+
}
|
|
646
672
|
|
|
647
673
|
// ③ 设置页:读盘状态 + 清缓存(只读优先;唯一写操作是删本机抽帧缓存目录)
|
|
648
674
|
try {
|
|
@@ -663,6 +689,7 @@ function apply(ctx) {
|
|
|
663
689
|
deps: depRows(),
|
|
664
690
|
cache: { ...dirStat(CACHE), ...cacheCfg(), lastAutoClean: cleaned },
|
|
665
691
|
lastRun: readLastRun(),
|
|
692
|
+
ui: uiCfg(),
|
|
666
693
|
},
|
|
667
694
|
})
|
|
668
695
|
}
|
|
@@ -703,6 +730,15 @@ function apply(ctx) {
|
|
|
703
730
|
if (r.state === 'ok') writeLastRun('login', 0)
|
|
704
731
|
return sendJson(res, 200, r)
|
|
705
732
|
}
|
|
733
|
+
if (req.method === 'POST' && url.pathname === '/api/bili-watch/config') {
|
|
734
|
+
const body = await readJsonBody(req)
|
|
735
|
+
const patch = {}
|
|
736
|
+
if (typeof body?.hintDismissed === 'boolean') patch.hintDismissed = body.hintDismissed
|
|
737
|
+
if (typeof body?.activationSeen === 'boolean') patch.activationSeen = body.activationSeen
|
|
738
|
+
if (typeof body?.showInSettings === 'boolean') patch.showInSettings = body.showInSettings
|
|
739
|
+
const next = writeUiCfg(patch)
|
|
740
|
+
return sendJson(res, 200, { ok: true, data: next })
|
|
741
|
+
}
|
|
706
742
|
sendJson(res, 404, { ok: false, why: 'not found' })
|
|
707
743
|
} catch (e) {
|
|
708
744
|
sendJson(res, 500, { ok: false, why: String(e?.message || e) })
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-bili-watch",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Reads Bilibili (and generic URLs via yt-dlp) into text for DSH agents: search, hot, metadata, comments, danmaku, AI subtitles, local ASR, frame OCR.",
|
|
6
6
|
"type": "module",
|