vecnode/vn-code--packages-dsh-media ↗★ 9
dsh-media
The pack's media engine and the only owner of ffmpeg (host-only, no browser half): `media_probe`, `media_run` and `media_frames`, the Range-capable file route, remux jobs, and the pinned hash-verified ffmpeg provisioning that dsh-video's tab streams through. 作为多媒体处理的核心后端,适合需要分析音视频属性和转码的任务。
같은 패키지 이름의 다른 저장소
설치
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:vecnode/vn-code#066e7396f8c7d4abd3132329f32c1b7c9ad8c1fa&path:packages/dsh-mediadsh-media (alpha.3)
Media the agent can actually work with, and the one copy of ffmpeg this pack runs.
The read tool refuses a binary file, so a PNG's dimensions, a video's codec, an audio
file's sample rate and whether a browser can play any of it are questions nothing else
here answers. This package answers them and gives the agent a real ffmpeg; it is
host-only (no dsh.client, no browser bundle) - dsh-video is the surface, this is
the engine it and the agent share.
What it adds
media_probe- one ffprobe run as a report: container, duration, bitrate; per stream the codec and profile, exact pixel size and display aspect, frame rate as BOTH its rational (30000/1001) and its decimal, pixel format, bit depth, rotation, sample rate, channel layout, language/title tags and every chapter - ending in the verdictplayable/image/remux/transcodeplus the exact ffmpeg command that would fix the last two.imageis its own verdict, because ffprobe reports a PNG as one video stream at 25 fps.raw: trueadds ffprobe's own JSON.media_run- a REAL ffmpeg or ffprobe command as an argv array, never a shell string, so a path with a space, a filter graph and a literal&arrive as themselves. It injects-hide_banner,-loglevel warning(unless the caller sets a level),-nostdinand-n(never overwrite) unlessoverwrite: true, which becomes-y; ffprobe gets none of those. It runs in the conversation workspace by default, kills at a deadline (120 s, 600 s max), caps the output keeping head and tail and naming the drop, and reports the exit code, the elapsed time and the files ffmpeg named as outputs.media_frames- frames at named timestamps,count: Nsampled evenly, or ONE contact sheet, written create-exclusively, each file's REAL dimensions read back out of its own PNG/JPEG header. A-sspast the end exits 0 having written nothing, and says so.- The pin, not a binary:
lib/binaries.jsonrecords per platform-arch the exact official static build URL and its SHA-256. Resolution order:DSH_MEDIA_FFMPEG/DSH_MEDIA_FFPROBE, thenPATH, then$DSH_HOME/dsh-media/bin/ -/, then nothing - answered immediately with the pinned download started in the BACKGROUND (DSH_MEDIA_NO_INSTALL=1disables it;DSH_MEDIA_PREFER_BUNDLED=1makes the provisioned copy win overPATH). - Provisioning: a lock directory stolen after 30 minutes, a streamed download hashed as it
arrives with a stall detector, a deadline and a byte cap, the hash compared with the pin
BEFORE anything is executed (a mismatch deletes it and installs nothing), unpacking through
the host's own
tar, each binary copied in under a.partialname and RENAMED,chmod 0o755on POSIX, and aninstall.jsonstamp. - Two skills (
skills/ffmpeg-cli/SKILL.md,skills/ffprobe-cli/SKILL.md) registered at runtime from the package folder and copied into$DSH_HOME/skillsby both installers under a.vncode-dsh-mediamarker; every example in them is parsed and the runnable ones really run bycheck-media-examples.mjs.
How it plugs in
| Piece | Value |
|---|---|
| row | media, host-only (no dsh.client, no browser bundle) |
| routes | seven exact paths, GET/HEAD/POST only |
| touches | no core row disabled, no fork, no npm dependencies, no vendored engine |
| Route | What it answers |
|---|---|
GET /api/dsh-media/state, /health | which binary answered and from where, the provisioned copy's state, the caps and tools; POST /provision joins the download |
GET /api/dsh-media/report | the media_probe summary as JSON; 200 {ok:true,unavailable:true} with no ffprobe |
GET /api/dsh-media/file | the bytes with real HTTP Range support (206 + Content-Range, suffix ranges, 416, HEAD without a body); ?cache= serves a cached conversion by key |
POST /api/dsh-media/remux, GET /job | start or JOIN the background remux/transcode; progress from ffmpeg's own -progress pipe:1 out_time_us |
The job id is the content key (sha256(realpath + size + mtime + mode)): asking twice
joins the first, a finished copy comes from the cache, and an edited file never serves a
stale one.
Limits
- claims 15 video extensions - mp4, m4v, mov, webm, mkv, avi, wmv, flv, ogv, ts, m2ts, mpg, mpeg,
3gp, mts - and no audio format (WAV/AIFF/FLAC belong to
dsh-audio, MP3/M4A/Ogg to the shipped preview, so claiming them would take a surface away) and no image (dsh-imageowns those). - the playable cache under
$DSH_HOME/dsh-media/playable/is LRU-pruned at 8 GiB; a partial file from a failed conversion is deleted, not served. - darwin-arm64 is deliberately unpinned - no Apple-Silicon build publishes both a versioned URL
and a checksum - so that platform is told to use
brew install ffmpegor the two variables. - never a shell, an implicit transcode, a
-ywithoutoverwrite: true, or an unvalidated path.
Verify
node scripts/checks/check-media-node.mjs # the pin, offline; ffmpeg half skips loudly
node scripts/checks/check-media-examples.mjs # every skill example, parsed and run
Install
scripts\setup\install.bat -Force # Windows
./scripts/setup/install.sh -Force # macOS / Linux
Run the installer once, then restart the app and hard-refresh the browser; nothing has to be installed first, since the pinned copy is fetched only if the machine has none.