Skip to content
dsh.fish
Bundle

pic-host

DSH 图片/音视频托管:启动时自动拉起 8899 静态服务(历史 URL 兼容 + 媒体 Range/播放页)+ 3080 同源 /files/ 路由(Range 流式)+ WebUI 内点击即播(视频静音/音频语音)

Source
awa-123-cw
stars
2 stars
License
MIT
Updated
Updated 12 days ago

Readme

# pic-host — DSH 图片/视频托管插件

DSH(web profile)启动时**自动拉起本地图片/视频静态服务**,让 WebUI 里的图片 URL 始终可用。

GitHub: https://github.com/awa-123-cw/better-deepseek-harness(Release 附件含打包好的 tgz;旧名 pic-host 自动 301 重定向)

## 解决什么问题

历史会话消息里的图片形如 `http://127.0.0.1:8899/<目录>/<文件>`,由独立静态服务提供。
每次重启 DSH / 机器后该服务消失,WebUI 历史图片全部变成损坏图标,需要手动拉起。

本插件挂载后无需任何人工操作:

| 时机 | 行为 |
| --- | --- |
| DSH 启动 | 探测 `127.0.0.1:8899`;未监听则分离进程拉起 `pic_server.py`(历史 URL 立即恢复) |
| DSH 启动 | 注册 3080 同源路由 `/files/*`(映射 `D:\ds4`,新图用 `http://<dsh>:3080/files/...` 引用,浏览器加载最稳) |
| DSH 退出 | 自动结束自己拉起的 python 进程(不留孤儿);强杀后残留的 python 下次启动会被探测复用 |
| 状态查询 | 浏览器开 `http://127.0.0.1:8899/` → 可视化状态页(PID/运行时长/请求数/目录映射,5 秒自动刷新) |
| 状态查询 | `GET http://127.0.0.1:3080/pic-host/status` → 插件 JSON 状态(DSH 重启后可用) |
| WebUI 指示 | 侧栏左下角(设置按钮旁)出现**鲸鱼线条图标**按钮(DeepSeek 吉祥物,与 DSH 内置图标统一,无文字):绿点=在线/红点=离线(每 5 秒刷新);点击弹紧凑状态卡(渲染在 shell.overlay 全局最高层,不被会话区遮挡)——**「更好的DeepSeekHarness」** 名称 + 端口/PID/状态 + 打开状态页,点卡片外任意处关闭 |
| 图片预览 | 聊天里**双击任意图片** → 全屏 lightbox 放大预览(shell.overlay 最高层):**滚轮缩放**(0.2x–8x,**以光标位置为锚点**,光标下的画面内容缩放后纹丝不动;无过渡动画、连续快速滚轮零漂移——布局位置状态推导 + zoomRef 最新值,不读 DOM 实测位置)、拖拽平移、双击图片复位、点击遮罩或按 Esc 关闭。DSH 原生无预览功能,由本插件补齐 |

## 媒体能力(v0.4.0:视频 + 音频)

- **服务端(8899 + 3080 /files/ 双通道)全类型支持 HTTP Range**(206 Partial Content + `Accept-Ranges: bytes`):
  浏览器 `<video>/<audio>` 可直接引用媒体 URL 播放,**拖动进度条/seek 丝滑**。
- 视频/音频 MIME 完整:mp4 / m4v / webm / mov / mkv / avi / mpeg / ogv / 3gp / wmv / flv /
  mp3 / m4a / aac / wav / ogg / flac / opus / weba。
- **WebUI 内直接播放(不跳转)**(client):聊天消息里的视频/音频地址——markdown 链接(`[xx](xxx.mp4)`)、
  图片语法误用(`![xx](xxx.mp4)` 渲染成破图)、裸 URL 文本、**代码块整块=单个媒体 URL**、
  **本地路径描述**(AI 常输出 `📁 D:\IndexTTS2\gen_xxx.wav(1.31 秒)`,自动识别 `D:\ds4` / `D:\IndexTTS2`
  路径并转成可播放 URL)——五通道全部原地替换为
  **「点击即播」卡片**:点「在 WebUI 内播放」即在消息里内嵌播放(controls + 拖动进度条 + 全屏),
  **双击卡片** → 全屏播放器(倍速 + 全屏 + 下载文件),全程不离开 webui。
- **音频(语音)播放**:wav / mp3 / m4a / aac / ogg / flac / opus / weba 走同样链路,
  卡片与播放器使用 `<audio>` 元素(🎵 图标),**不默认静音**;视频保持默认静音。
- **单击链接兜底(100% 可靠)**:捕获阶段拦截视频/音频链接的单击——即使卡片替换被 React 重渲染撤销
  (链接复活),单击链接也绝不会新开标签页,而是直接在 WebUI 内打开播放器。
  任何会话、任何时刻,媒体链接单击即播,绝不离开 webui。
- **HTML5 播放页**(备用,独立开页):`http://127.0.0.1:8899/__watch__/<路径>` → 深色主题播放器页面
  (自动播放、进度拖动、全屏、原始文件直达),`<video src>` 指向原始文件 URL。
- 示例 URL:`http://127.0.0.1:8899/h3_outputs/xxx/files/xxx.mp4` 或
  `http://127.0.0.1:3080/files/h3_outputs/xxx/files/xxx.mp4`。
- 状态页增加视频引用示例与播放页入口提示。

### 性能控制(防 webui 卡死)

内嵌播放按「最省资源」原则设计,历史消息里几十上百个媒体链接也不会拖垮 WebUI:

| 机制 | 说明 |
| --- | --- |
| 点击即播 | 卡片只是轻量 div(文件名 + ▶/🎵 按钮),创建时**零网络请求**;点击才创建 `<video>/<audio>` |
| preload=metadata | 只拉文件索引,不预下载全片,点开即播、拖动进度条按需拉取 |
| 全局互斥播放 | 同一时刻只允许**一路**媒体解码:任意视频/音频开始播放 → 其他所有媒体自动暂停 |
| 滚动离视口暂停 | IntersectionObserver 监视,卡片滚出视口 300px 外立即暂停,解码器不空转 |
| rAF 帧合并 | MutationObserver 回调合并到每帧一次扫描,流式输出高频 DOM 变化不卡 |
| 四通道原地替换 | 链接 / 破图 img / 裸 URL / 整块=URL 的代码块都转卡片,多行代码块不处理,防误伤 |

## 8899 别名表(pic_server.py)

- `/` → `D:\ds4\anima_outputs`(历史 URL 根,兼容 `/furina_mast_001/xxx.png` 旧链接)
- `/anima_outputs` `/k2_outputs` `/krea2_outputs` `/comfy_outputs` `/h3_outputs`
  `/runninghub_outputs` `/runninghub_tmp` `/image2-test-a` `/anima_doujin` → 对应 `D:\ds4` 目录
- `/IndexTTS2` → `D:\IndexTTS2`(TTS 语音输出目录)
- `/workspace` → 整个 `D:\ds4`(兜底)

安全:只读 GET/HEAD;路径净化拒绝 `..`、`\`(Windows 穿越)→ 403。

## 文件

```
pic-host/
├── package.json          # dsh.bundle.patch + dsh.client 声明(cordis bundle 清单)
├── cordis.patch.yml      # 插件行(insert pic-host)
├── .dsh-plugin/
│   ├── index.mjs         # 宿主插件:探测/拉起 8899 + /files/ 路由(Range 流式)+ /pic-host/status
│   ├── pic_server.py     # 8899 多别名静态服务(python 标准库,零依赖;媒体 MIME + Range + 播放页)
│   └── client.js         # 浏览器半区:侧栏「更好的DeepSeekHarness」按钮 + 图片 lightbox + 媒体内嵌点击即播(五通道 + 互斥/懒加载性能控制)+ 双击全屏播放器
└── logs/                 # pic-host.log(插件日志)、pic-server.log(访问日志)
```

## 开发备忘

- 改动 `index.mjs` / `client.js` 后免重启生效:staging 工具 `reload_pic_host_pkg`
  (super-injector 后侧,`dev_stage_call reload_pic_host_pkg {}`)——purge 缓存 → import 新代码 →
  重建 fiber → 清 disabled 标记 → 重注册 client bundle;**刷新 WebUI 页面**后 client 新功能生效。
- `pic_server.py` 是独立 python 进程,改完需重启 8899(杀旧进程后由插件自愈或手动拉起)。

## 安装位置

- 插件本体:`D:\dsh-plugins\pic-host`
- profile 注册:`D:\dsh\profiles\web\package.json`(`dependencies` + `dsh.profile.bundles`)
- 卸载:从 package.json 移除依赖与 bundles 条目,`pnpm install`,删除插件目录即可

## 常用排查

- 图片/视频仍不显示 → `Invoke-WebRequest http://127.0.0.1:8899/<路径>` 看是否 200;
  或看 `logs/pic-host.log` 是否记录了"已拉起"。
- 视频不能拖进度条 → 请求必须带 `Range` 头返回 206;`Invoke-WebRequest -Headers @{Range="bytes=0-1023"} ...` 验证。
- 换端口 / 换工作区根目录 → 改 `index.mjs` 顶部常量(`PIC_PORT` / `WS_ROOT` / `ANIMA_ROOT`)与
  `pic_server.py` 的 `ALIASES`。

Install

dsh plugin --profile web add github:awa-123-cw/better-deepseek-harness

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source