Skip to content
dsh.fish
Bundle

dsh-video-player

Floating, draggable, resizable video scene player for DeepSeek Harness. Plays scene-per-MP4 clips from a Stash-style scene server, plus best-effort YouTube / Twitch / Jellyfin / custom links.

Source
IP050
stars
1 stars
License
MIT
Updated
Updated 22 hours ago

Readme

# dsh-video-player

![Video player mid-Stash](assets/when_u_mid_stash.png)

![Video player on Stash](assets/when_u_stash.png)

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

```sh
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 a `link:` symlink, and Node resolves the host module's imports from
> its realpath (`/path/to/dsh-video-player`), which sits outside the profile's
> `node_modules`. Before installing from a local dir, give it a bridge:
>
> ```sh
> 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 is `http://host.docker.internal:9999` with
  `/scene/{no}/stream` per clip; it supports Range requests (verified
  `206 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/stream` suffix). 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 the `dsh-video-player` settings
  namespace (schema: `base`, `channel`, `url`, `scene`) and serves
  `GET/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 polls `GET /api/dsh-video-player/config` (~2.5s)
  and pushes title-bar changes back with `POST`, so the floating window and the
  settings store stay in sync.

## Bugs we hit and fixed (the lessons)

1. **Host `export default` drops `inject`.** DSH's bundle loader unwraps a
   module's `default` export to the bare function, silently discarding
   `inject`/`name` — services then throw `cannot get property "webServer"
   without inject` at 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.
2. **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 in `pending (waiting for service: …)`. Fix: keep the client
   self-contained with `inject: []`.
3. **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-player` settings 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.

Install

dsh plugin --profile web add github:IP050/dsh-video-player

Profile: web

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