Summary
Move static-ffmpeg onto a freshly built, full-feature FFmpeg (all features enabled, including GPL/nonfree/licensed codecs), built reproducibly via zackees/forge, published to a new zackees/ffmpeg-bins2 repo (content stored via Git LFS), and cataloged with a zackees/manifest.json-format manifest so the Python client resolves the correct artifact per platform.
Hard constraint: existing static-ffmpeg installs that download from the legacy zackees/ffmpeg_bins repo must keep working forever, and we must be able to ship future FFmpeg upgrades without breaking already-deployed installs.
Goals
- Build FFmpeg + FFprobe with all features enabled, including options that require GPL and
--enable-nonfree (see Licensing risk below).
- Build the full target matrix (see below), including Linux glibc 2.17 and musl variants.
- Create
zackees/ffmpeg-bins2, store binaries via Git LFS.
- Publish a
manifest.json catalog (per the zackees/manifest.json schema) describing every artifact with sha256, size_bytes, and download urls[].
- Update the
static-ffmpeg client to resolve + verify artifacts through the manifest.
- Backward compatibility: never mutate
ffmpeg_bins or its raw URLs; old installs keep resolving their pinned binaries.
- Forward compatibility: future FFmpeg upgrades are a manifest/channel-pointer change, not a client release, and don't disturb existing installs.
Target build matrix
| Key (platform tuple) |
os |
arch |
libc |
Notes |
win_x64 |
windows |
x86_64 |
– |
|
win_arm64 |
windows |
arm64 |
– |
forge windows_arm64 |
darwin_x64 |
macos |
x86_64 |
– |
|
darwin_arm64 |
macos |
arm64 |
– |
|
linux_x64_glibc217 |
linux |
x86_64 |
glibc 2.17 |
manylinux2014 baseline |
linux_arm64_glibc217 |
linux |
arm64 |
glibc 2.17 |
manylinux2014 baseline |
linux_x64_musl |
linux |
x86_64 |
musl |
forge linux_x64_musl |
linux_arm64_musl |
linux |
arm64 |
musl |
forge linux_arm64_musl |
8 targets. Each artifact packages ffmpeg + ffprobe (+ LICENSE/build-info).
Build: zackees/forge
- Author/point to a Conan recipe for FFmpeg configured with the full option set (GPL + nonfree + all external codec libs the recipe supports).
- Drive builds via the reusable workflow
zackees/forge/.github/workflows/forge-conan.yml@main, toggling the platform inputs (windows_arm64, linux_x64_musl, linux_arm64_musl, etc.).
- forge uploads a max-compression archive per platform → collect these and republish into
ffmpeg-bins2.
⚠️ glibc 2.17 risk: forge builds on native GitHub runners (Ubuntu with glibc 2.35+). A native build will not run on glibc 2.17 systems. The glibc-2.17 targets need a manylinux2014 / CentOS 7 container (or an equivalent sysroot toolchain) so the resulting binaries carry a 2.17 floor. Decide whether this is a forge job variant or a separate container build feeding the same manifest.
New repo: zackees/ffmpeg-bins2
- Create the repo (owner note: the goal text says "fmpeg-bins2" — assuming
ffmpeg-bins2).
.gitattributes: * binary binaries tracked by LFS (*.zip/*.tar.zst filter=lfs diff=lfs merge=lfs -text).
- Layout: versioned dirs, e.g.
ffmpeg/<version>/<platform-key>.<ext> plus a top-level manifest.json.
⚠️ LFS + download-URL pitfall (learned the hard way in this repo): do not assume github.com/.../raw/... will serve LFS content the way it serves plain blobs. The manifest must carry the canonical download URLs plus sha256, and the client must verify the hash — so integrity never depends on raw-URL smudging behavior.
Transport (decided): serve the LFS files behind a www site fronted by a CDN. The binaries are stored in-repo via LFS, but the manifest's Asset.urls[] point at CDN-fronted https://<www-site>/... URLs (not GitHub raw/LFS-media or Release-asset endpoints). This puts delivery on a CDN with no bandwidth/quota limits (avoiding both GitHub LFS bandwidth caps and Release-asset rate limits), gives stable cacheable URLs, and keeps integrity guaranteed by the manifest sha256. LFS remains the in-repo source of truth; the www/CDN layer is the distribution front-end.
Catalog: manifest.json (per zackees/manifest.json)
Produce a Catalog document for tool: "ffmpeg":
kind: "Catalog", schema_version: 1, tool: "ffmpeg"
channels: { "latest-stable": "<version>" }
releases[]: newest-first, each with version, published_at, and platforms[] mapping platform tuples → Asset { filename, sha256, size_bytes, media_type, urls[] }.
- Platform tuple fields used:
{os, arch, libc} (libc = glibc/musl; optionally os_version for the 2.17 floor).
Client resolution contract: resolve("ffmpeg", <detected-platform>, "latest-stable") → Asset.
Client changes (static_ffmpeg/run.py)
- Platform detection must gain libc awareness on Linux (distinguish glibc vs musl — e.g.
platform.libc_ver() / probe for ld-musl-* / ctypes), producing the platform tuple {os, arch, libc}.
- Manifest resolver: fetch
ffmpeg-bins2/manifest.json, resolve the tuple + latest-stable channel to an Asset, download from Asset.urls, and verify sha256 before extracting.
- Version pinning: record the resolved version + sha256 in
installed.crumb. Existing installs stay on their pinned artifact and don't re-download; only fresh installs (or an explicit upgrade) pull the newest channel version.
- Fallback: if the manifest fetch/resolve fails, fall back to the current hardcoded legacy behavior so we never regress availability.
Backward compatibility & upgrade strategy (must-have)
- Freeze
zackees/ffmpeg_bins. Never migrate it to LFS, never move/rename v8.0/*.zip. Old clients hardcode ffmpeg_bins/raw/main/v8.0/<platform>.zip; those exact URLs must keep serving the exact bytes forever. (This is why the recent local LFS migration of ffmpeg_bins was reverted rather than pushed.)
- New installs use
ffmpeg-bins2 + manifest.
- Upgrading FFmpeg later = publish a new release into
ffmpeg-bins2 and repoint the latest-stable channel in manifest.json. No client release required. Already-installed users keep their pinned version (crumb) and are unaffected; new installs get the new version. This decouples FFmpeg version cadence from static-ffmpeg releases.
⚠️ Licensing risk (decision needed, not a blocker)
Enabling --enable-gpl --enable-nonfree produces binaries whose redistribution is legally restricted — nonfree output is, per FFmpeg's own docs, not redistributable. Publishing such binaries in a public repo/release may violate those terms. Options to decide:
- Keep nonfree artifacts in a private distribution channel, or
- Ship a public GPL (no-nonfree) default plus an opt-in nonfree channel, or
- Accept the risk explicitly (owner's call).
Flagging per repo policy; proceeding as the owner directs.
Acceptance criteria / task checklist
Open questions
- glibc 2.17: forge container job vs. separate manylinux2014 build?
Distribution transport Decided: LFS files served over a www site fronted by a CDN (no bandwidth limits); manifest urls[] point at the CDN. Remaining sub-question: which host/CDN for the www site?
- Nonfree licensing channel: public, private, or split?
- FFmpeg version to target for the first
ffmpeg-bins2 release?
Filed via Claude Code.
Summary
Move
static-ffmpegonto a freshly built, full-feature FFmpeg (all features enabled, including GPL/nonfree/licensed codecs), built reproducibly viazackees/forge, published to a newzackees/ffmpeg-bins2repo (content stored via Git LFS), and cataloged with azackees/manifest.json-format manifest so the Python client resolves the correct artifact per platform.Hard constraint: existing
static-ffmpeginstalls that download from the legacyzackees/ffmpeg_binsrepo must keep working forever, and we must be able to ship future FFmpeg upgrades without breaking already-deployed installs.Goals
--enable-nonfree(see Licensing risk below).zackees/ffmpeg-bins2, store binaries via Git LFS.manifest.jsoncatalog (per thezackees/manifest.jsonschema) describing every artifact withsha256,size_bytes, and downloadurls[].static-ffmpegclient to resolve + verify artifacts through the manifest.ffmpeg_binsor its raw URLs; old installs keep resolving their pinned binaries.Target build matrix
win_x64win_arm64windows_arm64darwin_x64darwin_arm64linux_x64_glibc217linux_arm64_glibc217linux_x64_musllinux_x64_musllinux_arm64_musllinux_arm64_musl8 targets. Each artifact packages
ffmpeg+ffprobe(+LICENSE/build-info).Build:
zackees/forgezackees/forge/.github/workflows/forge-conan.yml@main, toggling the platform inputs (windows_arm64,linux_x64_musl,linux_arm64_musl, etc.).ffmpeg-bins2.New repo:
zackees/ffmpeg-bins2ffmpeg-bins2)..gitattributes:* binarybinaries tracked by LFS (*.zip/*.tar.zst filter=lfs diff=lfs merge=lfs -text).ffmpeg/<version>/<platform-key>.<ext>plus a top-levelmanifest.json.github.com/.../raw/...will serve LFS content the way it serves plain blobs. The manifest must carry the canonical download URLs plussha256, and the client must verify the hash — so integrity never depends on raw-URL smudging behavior.Transport (decided): serve the LFS files behind a
wwwsite fronted by a CDN. The binaries are stored in-repo via LFS, but the manifest'sAsset.urls[]point at CDN-frontedhttps://<www-site>/...URLs (not GitHub raw/LFS-media or Release-asset endpoints). This puts delivery on a CDN with no bandwidth/quota limits (avoiding both GitHub LFS bandwidth caps and Release-asset rate limits), gives stable cacheable URLs, and keeps integrity guaranteed by the manifestsha256. LFS remains the in-repo source of truth; the www/CDN layer is the distribution front-end.Catalog:
manifest.json(perzackees/manifest.json)Produce a Catalog document for
tool: "ffmpeg":kind: "Catalog",schema_version: 1,tool: "ffmpeg"channels:{ "latest-stable": "<version>" }releases[]: newest-first, each withversion,published_at, andplatforms[]mapping platform tuples →Asset { filename, sha256, size_bytes, media_type, urls[] }.{os, arch, libc}(libc =glibc/musl; optionallyos_versionfor the 2.17 floor).Client resolution contract:
resolve("ffmpeg", <detected-platform>, "latest-stable") → Asset.Client changes (
static_ffmpeg/run.py)platform.libc_ver()/ probe forld-musl-*/ctypes), producing the platform tuple{os, arch, libc}.ffmpeg-bins2/manifest.json, resolve the tuple +latest-stablechannel to anAsset, download fromAsset.urls, and verifysha256before extracting.installed.crumb. Existing installs stay on their pinned artifact and don't re-download; only fresh installs (or an explicit upgrade) pull the newest channel version.Backward compatibility & upgrade strategy (must-have)
zackees/ffmpeg_bins. Never migrate it to LFS, never move/renamev8.0/*.zip. Old clients hardcodeffmpeg_bins/raw/main/v8.0/<platform>.zip; those exact URLs must keep serving the exact bytes forever. (This is why the recent local LFS migration offfmpeg_binswas reverted rather than pushed.)ffmpeg-bins2+ manifest.ffmpeg-bins2and repoint thelatest-stablechannel inmanifest.json. No client release required. Already-installed users keep their pinned version (crumb) and are unaffected; new installs get the new version. This decouples FFmpeg version cadence fromstatic-ffmpegreleases.Enabling
--enable-gpl --enable-nonfreeproduces binaries whose redistribution is legally restricted —nonfreeoutput is, per FFmpeg's own docs, not redistributable. Publishing such binaries in a public repo/release may violate those terms. Options to decide:Flagging per repo policy; proceeding as the owner directs.
Acceptance criteria / task checklist
zackees/ffmpeg-bins2created; binaries stored (LFS in-repo and/or Release assets).manifest.json(Catalog) validates against thezackees/manifest.jsonschema and resolves all 8 tuples.static-ffmpegclient resolves via manifest, verifies sha256, and falls back to legacy on failure.ffmpeg_bins/raw/main/v8.0/*.zipsuccessfully.Open questions
Distribution transportDecided: LFS files served over awwwsite fronted by a CDN (no bandwidth limits); manifesturls[]point at the CDN. Remaining sub-question: which host/CDN for thewwwsite?ffmpeg-bins2release?Filed via Claude Code.