DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

JackAIStudio /

JackAIStudio/dsh-video-preview

Verified

dsh-better-sidebar 的视频文件预览插件(zemul/dsh-video-preview 的 fork):内置 Range 路由,桌面版进度条可正常拖动

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHubProject homepage
READMESource: main@c99a991d

dsh-video-preview

dsh-better-sidebar 的视频文件预览插件: 在侧边栏打开 .mp4 / .webm / .mov / .mkv / .avi 等视频文件时,内联渲染一个可播放、可拖拽进度条的 <video> 播放器。

本仓是 zemul/dsh-video-preview 的 fork。 上游停在 0.1.4(2026-08-27),在当前的 DSH 上装好也不工作;这里保留了让它跑起来、 以及在桌面版恢复进度条的改动。npm 上的 dsh-video-preview 仍是上游 0.1.4, 所以不要用 npm 包名安装,装本仓(见「安装」)。差异和原因写在下面两节「本机改动」里。

背景

better-sidebar 内置的文件预览器只有 image / pdf / markdown / html / code / binary-download, 没有视频预览——视频文件目前会落到 code 视图(乱码)或下载按钮。

直接复用内置的 /sidebar/file 媒体路由也不行:

  • 该路由不支持 HTTP Range(整个文件读进内存、返回 200),浏览器无法拖动进度条(seek);
  • 受 mediaLimit(默认 20MB)限制,更大的视频会被直接拒绝。

因此本插件自带一条 支持 Range(206 Partial Content)的 /video/* 宿主路由, 用 createReadStream 分片流式下发,seek / 拖进度条 / 倍速都能正常工作。

功能

  • 通过 ctx.betterSidebar.registerFileViewer 注册 video 预览器(id video,与内置 viewer 同权):
    • .mp4/.m4v/.webm/.mov/.qt/.mkv/.avi/.wmv/.flv/.ogv/.ogg/.mpeg/.mpg/.3gp/.3g2/.m2ts
    • 内联 <video controls>:播放/暂停、拖进度条(Range 206)、倍速、全屏、音量、preload=metadata
    • 底部文件名 + 「下载」链接(浏览器无法解码的容器格式仍可下载原文件)
  • 桌面版不可 seek 时自动整段载入(Blob)恢复进度条,详见「本机改动(0.1.6)」
  • 自带 /video/* 宿主路由:HTTP Range / If-Range / suffix-range(bytes=-N),视频大小不受 mediaLimit 限制
  • viewer 描述符带 title/icon,Side card 设置页自动显示启用开关(含 16px 播放器图标)
  • 客户端组件自包含:不依赖 better-sidebar 的 client 内部实现,fetchStrategy: 'none'

依赖 better-sidebar 版本

需要 better-sidebar >= 0.4.0(ctx.betterSidebar.registerFileViewer 服务)。

本机改动(0.1.5)

0.1.4 在当前 DSH 0.2.1-rc.2 上装好后,侧边栏里能看到播放器,但画面一直黑、时长 0:00, 控制台里 /video 请求是 404。原因有两个:

  • 宿主半声明 inject = ['webServer', 'sessions', 'webRuntime'],而 webRuntime 这个服务 在当前版本已经不存在,宿主半永远等不到依赖,/video 路由从未注册;
  • 路由把路径限制在会话工作目录内,侧边栏打开 ~/Downloads 这类目录里的视频会被 403 拒掉。

本版本的处理:

  • 请求准入改用宿主自带的 connection 服务(ctx.connection.requestRejection), 和 better-sidebar 自己的 /sidebar/file 走同一套 cookie / Origin / Host 围栏;
  • 路径按 better-sidebar 的规则解析:绝对路径保持绝对,相对路径落到会话工作目录, ~ 展开为家目录,因此侧边栏能打开的任意视频都能播;
  • 客户端改用相对地址请求路由,反代带目录前缀时也能命中。

实测:/video 返回 206 + content-range,侧边栏内播放到 0:43 且可拖拽 seek。

本机改动(0.1.6):桌面端进度条拖不动

现象:Web 端拖进度条正常;桌面版(DeepSeek Harness.app)里把滑块拖到中间, 松手后时间立刻弹回 0,画面也不跳。不是偶发的鼠标问题,换哪个视频都这样。

原因在桌面端的协议转发,不在 /video 路由:

  • 桌面壳把 dsh-app://app/* 转发给本地 Host 时用 new Response(response.body, { headers }) 重新构造响应,并把 content-length 列进了不转发的响应头(连同 set-cookie、transfer-encoding 等);
  • 于是渲染进程拿到的是「有 content-range、没有长度」的 206 分片, Chromium 的媒体栈算不出总长度,把 video.seekable 收缩成 [0,0];
  • 不可 seek 的媒体元素会丢弃 currentTime 的写入,所以拖动后滑块回弹。

同一份 client.js 在 Web 端(真实 HTTP,长度保留)不受影响,所以「有时能拖、 有时不能」取决于视频是由 Web 端还是桌面版打开。

本版本的处理(客户端兜底,不改桌面壳):

  • 播放器监听 loadedmetadata / durationchange / canplay,用 video.seekable 判断自己是否处在「知道时长但不可 seek」的状态;
  • 命中后把同一份 /video 内容整段读成 Blob(URL.createObjectURL)再交给播放器, 媒体栈对 Blob 能算出完整可 seek 区间,进度条恢复正常;切换前保留播放位置与播放状态;
  • 整段读取有 256 MiB 上限(先用 Range: bytes=0-1 从 content-range 读总长度来判断), 超过就继续流式播放并提示「进度条可能拖不动」, 避免大文件把渲染进程内存吃满;读取失败时同样退回流式播放,并在播放器下方说明;
  • Web 端 seekable 在元数据到达时就是完整区间,兜底不会触发,仍走原来的 Range 流式路径。

实测(Electron 43 / Chromium 150,模拟桌面端丢 content-length 的转发): 修复前 seekable = [0,0],currentTime = 20 立即回到 0;修复后 seekable = [0,40], 拖到 20s、35s 都命中,并从 35s 继续播放。同一份代码在保留长度的转发下 seekable = [0,40],仍按 Range 分片请求(bytes=0-、bytes=10190848-…),未触发兜底。

安装(profile)

前置:DSH 已装好且 dsh web 能运行,已安装 dsh-better-sidebar。

# 从本仓安装(推荐;需要先推到 GitHub):
dsh plugin --profile web add github:JackAIStudio/dsh-video-preview

# 本地开发(link: 热更新,宿主半只有内置模块,可以用 link):
dsh plugin --profile web add "link:<本目录>"

# 离线 / 未发布(先 npm pack 出 tarball):
dsh plugin --profile web add file:<本目录>/dsh-video-preview-0.1.6.tgz

安装后重启 dsh web 进程并浏览器硬刷新(Cmd/Ctrl+Shift+R)。

说明:新增宿主半(/video 路由)需要重启一次;之后仅 client 改动硬刷新即可。 桌面版装在 ~/.dsh/profiles/desktop,用的是 DSH 自带的那个 profile, 命令里的 --profile web 要换成 --profile desktop。

运行

  • 打开侧边栏 → 在文件资源管理器中打开任意视频文件,video 预览器自动命中;
  • Side card 设置页 →「文件预览」清单中可见 Video 卡片(带播放器图标),可单独启用/禁用。

开发与检查

node test/route.test.mjs   # 宿主路由回归测试(Range/206/416/403/HEAD/405,20 项)
检查项 命令 预期
宿主路由测试 node test/route.test.mjs 全部通过
client bundle node --check client.js 语法 OK
bundle 形态 产物首行 window.__ModuleLoader__.load({ id: 'dsh-video-preview', ... })
安装状态 node_modules/dsh-video-preview/client.js 存在 True

桌面版那条 seek 兜底没法用 node 测,它是渲染进程里的行为。改动 client.js 里判断 seekable 或 Blob 的部分时,至少用桌面版打开一个几十分钟的视频,拖到中段、暂停后再拖一次, 确认时间停在拖到的位置而不是回到 0:00。Web 端打开同一个文件,应当仍在网络面板里看到 bytes=… 的 206 分片请求(说明没有误走整段载入)。

与上游的差异

index.js 与 client.js 都是本地改过的版本,不是 npm 0.1.4 的原样拷贝:

版本 改了什么 原因
0.1.5 index.js 换掉 webRuntime 依赖、改用 connection 准入;路径解析跟随侧边栏;客户端改相对地址 上游在当前 DSH 上 /video 从未注册,且拒绝 cwd 外的路径
0.1.6 client.js 增加不可 seek 时的 Blob 兜底 桌面壳转发时丢掉 content-length,seekable 变 [0,0]

上游没有本地 skill、没有构建步骤,client.js 是手写的 bundle(不是打包产物), 所以改完直接生效,不需要 pnpm build。

结构

文件 作用
index.js 宿主半:注册 /video/* 前缀路由(connection 请求准入 + 会话 cwd 解析 + Range/suffix-range)
client.js 客户端 bundle:经 ctx.betterSidebar.registerFileViewer 注册 video 预览器(fetchStrategy: 'none',含 16px 图标)
cordis.patch.yml dsh.bundle.patch:挂载行(entry name 与包名一致,供 client-modules 发现 client bundle)
test/route.test.mjs 宿主路由回归测试(自包含,无需 ffmpeg)

安全

  • 请求准入复用宿主的 connection 服务:只接受已登录的同源浏览器请求,跨站标记直接 403;
  • 路径按侧边栏自己的规则解析(绝对路径 + 会话 cwd 相对路径 + ~ 展开),能读的文件范围与文件树一致;
  • 只读流式下发,不写文件、不设身份。

说明与限制

  • 支持浏览器可解码的容器/编码(H.264 的 mp4/m4v、VP8/VP9 的 webm、mov、OGV 等); Chromium 内核不内置解码的编码(如 HEVC)会在播放器内报错,可用「下载」取回原文件;
  • 视频大小不受 better-sidebar mediaLimit 限制;
  • 不新建 Tab 页,仅作为文件预览器注册(也可按需再加 registerTab)。

许可证

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit c99a991d9343

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout