Skip to content

feat(tools): install yt-dlp, ffmpeg and ImageMagick - #448

Merged
ralyodio merged 1 commit into
mainfrom
add-media-tools
Aug 29, 2026
Merged

feat(tools): install yt-dlp, ffmpeg and ImageMagick#448
ralyodio merged 1 commit into
mainfrom
add-media-tools

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

The three odd ones out in TOOLS: not workflow CLIs, but the media toolchain the rest of the roster is built on. cli-tools fronts all of them — dl for yt-dlp, vid for ffmpeg, img for ImageMagick — and every one of those used to answer a missing binary by telling you to go and install a system package by hand. Now the registry that installs cli-tools installs what it runs on.

moshcode install yt-dlp       # static binary → ~/.local/bin
moshcode install ffmpeg       # your distro's package manager
moshcode install imagemagick  # likewise

Each arrives the way it actually ships, which is three different ways

yt-dlp comes from its own releases as a self-contained PyInstaller binary — no python, no package manager. Deliberately not the distro package: extractors break whenever a site changes its markup, upstream ships a fix within days, and a distro package is frozen for the life of a release. Its upgrade is yt-dlp -U, the project's own updater.

release-install.mjs grew a bare flag for it — the asset is the executable, with no archive to unpack — and yt-dlp must be unversioned, because it tags by date with no leading v, so the versioned URL would be /download/v2026.08.19/: a 404 on every release.

ffmpeg and ImageMagick exist only as distro packages. Static rebuilds are floating around, but they are unsigned third-party redistributions of somebody else's codec stack, on the two tools most likely to be pointed at a file from the internet — trading a sudo prompt for that is not a trade worth making. So src/pkg-install.mjs dispatches to apt/dnf/zypper/pacman/apk, or Homebrew on macOS, with needsRoot: { except: ["darwin"] } since brew refuses to run as root.

Three things in there are facts about someone else's archive rather than preference:

  • apt refreshes its index first — a box that has not updated in months 404s on the archive, and the error names a URL instead of the problem.
  • every manager runs non-interactively — a continue? [Y/n] inside moshcode upgrade tools parks the whole sweep.
  • Fedora ships ffmpeg-free in its own repositories with the full ffmpeg only in RPM Fusion, so package names are a list tried in order rather than one string.

bin can now be a list of names

ImageMagick is why: the command is magick on version 7 and convert on 6, both current across supported distros under the same package name, so a single name reports a good install as missing on half the fleet. resolveExecutable tries each; primaryBin() is what reaches a spawn fallback or a message, so a list never gets printed or executed as one.

Two existing tests moved

Both because their assumption expired, not because they were wrong. tools --json promises binary as a string, so it now emits primaryBin. And the install-plan test asserted a plan mentions curl|wget|npm|pip|brew — yt-dlp is the first entry with both a native updater and one of our own installers, which fetch with node and with the system package manager and so spell neither.

Testing

node --test — 2651 tests, 0 failures (12 new in test/pkg-install.test.mjs, plus yt-dlp and multi-name-bin coverage).

Verified against the real world rather than only the suite: all three yt-dlp release assets resolve 200, and installing into a temp MOSHCODE_BIN produced a working binary reporting 2026.08.19.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LkEHoAhsoDhqVeJ1MJH9yD

The three odd ones out in TOOLS: not workflow CLIs, but the media toolchain the
rest of the roster is built on. cli-tools fronts all of them — `dl` for yt-dlp,
`vid` for ffmpeg, `img` for ImageMagick — and every one of those used to answer
a missing binary by telling you to go and install a system package by hand. Now
the registry that installs cli-tools installs what it runs on.

Each arrives the way it actually ships, which is three different ways:

yt-dlp comes from its own releases as a self-contained PyInstaller binary, so it
needs no python and no package manager and lands in ~/.local/bin like
gh/supabase/doctl. Deliberately not the distro package: extractors break
whenever a site changes its markup, upstream ships a fix within days, and a
distro package is frozen for the life of a release. Its upgrade is `yt-dlp -U`,
the project's own updater. release-install.mjs grew a `bare` flag for it —
the asset is the executable, with no archive to unpack — and it must be
`unversioned`, because yt-dlp tags by date with no leading "v" and the versioned
URL would be /download/v2026.08.19/, a 404 on every release.

ffmpeg and ImageMagick exist only as distro packages. Static rebuilds are
floating around, but they are unsigned third-party redistributions of somebody
else's codec stack, on the two tools most likely to be pointed at a file from
the internet — trading a sudo prompt for that is not a trade worth making. So
src/pkg-install.mjs dispatches to apt/dnf/zypper/pacman/apk, or Homebrew on
macOS. Everything that decides what to run is pure and unit-tested; only the
loop that runs it is not.

Three things in there are facts about somebody else's archive rather than
preference. apt refreshes its index first, because a box that has not updated in
months 404s on the archive and the error names a URL instead of the problem.
Every manager is invoked non-interactively, because a "continue? [Y/n]" inside
`moshcode upgrade tools` parks the whole sweep. And Fedora ships `ffmpeg-free`
in its own repositories with the full `ffmpeg` only in RPM Fusion, so package
names are a list tried in order rather than one string.

`bin` can now be a list of names. ImageMagick is why: the command is `magick` on
version 7 and `convert` on 6, both current across supported distros under the
same package name, so a single name reports a good install as missing on half
the fleet. resolveExecutable tries each; primaryBin() is what reaches a spawn
fallback or a message, so a list never gets printed or executed as one.

Two existing tests moved with it, both because their assumption expired rather
than because they were wrong. `tools --json` promises `binary` as a string, so
it emits primaryBin. And the install-plan test asserted a plan mentions
curl/wget/npm/pip/brew — yt-dlp is the first entry with both a native updater
and one of our own installers, which fetch with node and with the system package
manager and so spell neither.

Verified end to end: all three release assets resolve 200, and installing yt-dlp
into a temp MOSHCODE_BIN produces a working binary (2026.08.19).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LkEHoAhsoDhqVeJ1MJH9yD
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

0 finding(s) in the 10 file(s) this pull request changes.

Nothing in the files this pull request changes.

81 pre-existing finding(s) elsewhere in the repository — **HIGH/CRITICAL**: 5 | **MEDIUM**: 67 | **LOW**: 9

Not introduced by this pull request. The full set is in the Security tab.

Severity Rule Location
HIGH js-ssrf-outbound-request apps/pwa/public/sw.js:45
HIGH tls-verification-disabled apps/pwa/src/lib/moshpit-gateway.mjs:299
HIGH sh-remote-script-execution install.sh:79
HIGH sh-remote-script-execution install.sh:83
HIGH tls-verification-disabled src/dns.mjs:766
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:138
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:152
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:178
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:365
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:369
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:414
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:663
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:859
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:861
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:920
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:966
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1036
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1139
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1162
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1184

…and 61 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 5a16bc3 into main Aug 29, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant