dsh-video-player
A floating, draggable, resizable **video player** for DeepSeek Harness (DSH).
- Stars
- 1
- Language
- JavaScript
- Created
- Aug 25, 2026
- Updated
- Aug 25, 2026
Introduction
dsh-video-player


A floating, draggable, resizable video player for DeepSeek Harness (DSH). It mounts a small window over the web UI that plays scene-per-MP4 clips from a Stash-style scene server — propping a video feed beside your chat and paging through clips while you work. The window moves and resizes in real time, like a real PiP video. Stash is the star; other sources are best-effort.
Install
npm install -g pnpm@11 # the container image ships no pnpm
dsh plugin --profile web add /path/to/dsh-video-player
# restart the web profile (bundle changes apply at boot only):
sh ~/.dsh/dsh-guard/restart-web.sh
Linked installs need an import bridge.
dsh plugin add <dir>installs the plugin as alink:symlink, and Node resolves the host module's imports from its realpath (/path/to/dsh-video-player), which sits outside the profile'snode_modules. Before installing from a local dir, give it a bridge:mkdir -p /path/to/dsh-video-player/node_modules/@deepseek-ai ln -s ~/.dsh/profiles/node_modules/@deepseek-ai/dsh-settings /path/to/dsh-video-player/node_modules/@deepseek-ai/dsh-settings ln -s ~/.dsh/profiles/web/node_modules/schemastery /path/to/dsh-video-player/node_modules/schemastery
Features
- Floating, resizable window — drag the title bar to move it, drag the
bottom-right corner to resize live (min 200×140). Native
<video>player with controls (play/pause, scrub, fullscreen, volume). Inputs and buttons in the title bar are excluded from the drag handler, so they stay clickable/typable. - Stash scene navigation (the star) —
◀/▶step scenes, a number field +Go/ Enter jumps anywhere, and a status line shows the exact stream URL. The default server ishttp://host.docker.internal:9999with/scene/{no}/streamper clip; it supports Range requests (verified206 Partial Content), so seeking works. - Channel dropdown (custom widget) — Stash / Custom link / YouTube / Twitch
/ Jellyfin. Built as buttons, not a native
<select>(which was unreliable in DSH's dark UI). Picking a channel switches the source immediately. - Link field with auto-detect — paste a YouTube / Twitch / Jellyfin /
direct-file link and it auto-picks the channel and plays it (never appends a
/scene/N/streamsuffix). Enter or the ▶ button plays; paste auto-plays. - Gear panel (⚙) = window + Stash config only — width, height, opacity, and the Stash base URL (scene server root). Source/channel config deliberately does NOT live here.
- Persisted geometry — position, size, and opacity survive reloads (localStorage).
- Coexists with the pet & themes — it's a separate fixed overlay alongside the DSH pet and dream-skin wallpaper.
Architecture
- Host half (
lib/index.js) — registers thedsh-video-playersettings namespace (schema:base,channel,url,scene) and servesGET/POST /api/dsh-video-player/config. The POST handler writes through the DSH settings service, so the source config persists in the real settings store (not localStorage). - Client half (
lib/client.js) — self-contained (plain DOM,inject: [], activates immediately). It pollsGET /api/dsh-video-player/config(~2.5s) and pushes title-bar changes back withPOST, so the floating window and the settings store stay in sync.
Bugs we hit and fixed (the lessons)
- Host
export defaultdropsinject. DSH's bundle loader unwraps a module'sdefaultexport to the bare function, silently discardinginject/name— services then throwcannot get property "webServer" without injectat boot and take the whole app down. Fix: use named exports only (export function apply,export const inject,export const name), exactly like the dsh-pet host bundle. - A client that declares an inject it never uses hangs. The client once
declared
inject: ["@deepseek-ai/dsh-client-runtime"]without requiring it, and sat forever inpending (waiting for service: …). Fix: keep the client self-contained withinject: []. - Host imports don't resolve for
link:installs. See the bridge note above.
Roadmap
- Layer control — put the window in front of chat (default), behind it / tuck into the sidebar, or "minimize to sidebar".
- Transparency — window opacity (done: 0.3–1.0 in the gear panel) plus a click-through toggle for when you want to watch but not interact.
- Alarm / "someone walked in" button — one click plays a sound (TBA).
- Scene thumbnail rail — thumbnails for the current scene ± a few, for faster paging.
- Settings-menu card — the
dsh-video-playersettings namespace is registered; if the generic settings surface doesn't render it as its own section, add a client settings card (like the pet's) so channel/link are editable from Settings → dsh-video-player. - Stash scene list — probe the server for valid scene numbers and page within the real set instead of ±1.