Skip to content

feat: export as MP4 (karaoke video) for MP4 uploads and YouTube - #226

Merged
thcp merged 3 commits into
mainfrom
feat/export-mp4
Jun 25, 2026
Merged

feat: export as MP4 (karaoke video) for MP4 uploads and YouTube#226
thcp merged 3 commits into
mainfrom
feat/export-mp4

Conversation

@thcp

@thcp thcp commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Closes #219.

What

Adds an MP4 export that muxes the current mixer state (e.g. vocals muted) with the source video, producing a karaoke-style video the user can sing along to.

Source MP4 export Visual track
Imported .mp4 Original video, stream-copied (lossless)
YouTube Real video stream (H.264/avc1, ≤720p), stream-copied
SoundCloud Audio-only — no video stream exists
MP3 / WAV / FLAC upload No video available

Backend

  • Preserve video for .mp4 uploads — extract a silent video.mp4 (stream-copy, no re-encode) before the source is transcoded for Demucs.
  • YouTube video — best-effort, decoupled video-only download (bestvideo[vcodec^=avc1], ≤STEMDECK_VIDEO_MAX_HEIGHT/720p) to video.mp4. The audio source download is untouched; any video failure degrades gracefully to audio-only. H.264 is preferred so the export plays everywhere (avoids AV1, which Safari/iOS can't decode).
  • GET /api/jobs/{id}/video.mp4?stems=&gains= — streams a fragmented MP4: the same amix audio graph as the existing mixdown (encoded AAC) with the video stream-copied. 404 when the job has no video.
  • New has_video flag on Job, surfaced in state and persisted to metadata.json.

Frontend

  • MP4 added as a fourth export format (WAV/MP3/FLAC/MP4), shown only for jobs with a preserved video track.
  • In MP4 mode, Export Mix produces the karaoke video ("Export mix with video for karaoke"); the audio-only Export All Stems / Export Current Region rows are hidden. Reverts to WAV automatically on a non-video job.

Tests

  • Stems API: video mux happy path, 404 without video, requires-done, malformed id, param validation.
  • Pipeline: _extract_video_track preserves video from an .mp4 and skips audio-only .mp4.
  • Full suite: 107 passed, ruff clean. ffmpeg-dependent paths verified against the bundled ffmpeg (libx264/aac present).

Notes

  • Only new imports get the option — the video is captured at import time.
  • YouTube quality is capped at 720p H.264 (configurable); if a video only offers VP9/AV1, it stays audio-only rather than re-encoding.

Also in this PR — Linux FFmpeg

Linux no longer needs sudo apt install ffmpeg. The desktop shell downloads a static FFmpeg on first launch (like Windows/macOS), falling back to a system ffmpeg on PATH when present. Not bundled in the tarball, so we do not redistribute FFmpeg. Also fixes Demucs failing on compressed sources in source/dev runs.

  • Verified by compiling + clippy on the Linux target in WSL (PR CI does not build the Rust); rustfmt clean. Widened download_file/make_executable from macos to unix for reuse.

Add an MP4 export that muxes the current mixer state (e.g. vocals muted)
with the source video, producing a karaoke-style video.

Backend:
- Preserve a silent video.mp4 from .mp4 uploads (stream-copy, no re-encode).
- YouTube jobs do a best-effort video-only download (H.264/avc1, <=720p)
  to video.mp4, decoupled from the audio source so failures degrade to
  audio-only. New STEMDECK_VIDEO_MAX_HEIGHT config.
- GET /api/jobs/{id}/video.mp4 streams a fragmented MP4: the amix audio
  graph encoded as AAC, video stream-copied.
- has_video flag on Job, surfaced in state and persisted to metadata.

Frontend:
- MP4 added as a fourth export format (WAV/MP3/FLAC/MP4), shown only for
  jobs with a preserved video track. In MP4 mode, Export Mix produces the
  karaoke video and the audio-only Stems/Region rows are hidden.

SoundCloud and plain audio uploads are audio-only (no MP4 option).
@thcp thcp self-assigned this Jun 25, 2026
Thales added 2 commits June 25, 2026 22:38
Linux no longer requires `sudo apt install ffmpeg`. The desktop shell now
downloads a static FFmpeg build into the user data dir on first launch
(like Windows/macOS), falling back to a system ffmpeg on PATH when present.
This also fixes Demucs failing to decode compressed sources, since the
download lands in data_dir/ffmpeg which config.json already adds to PATH.

- ensure_ffmpeg: prefer a system ffmpeg, else download_linux_ffmpeg.
- download_linux_ffmpeg: fetch the .tar.xz, extract with system tar,
  copy ffmpeg + ffprobe into data_dir/ffmpeg. STEMDECK_FFMPEG_URL overrides.
- Widen download_file and make_executable from macos to unix so Linux
  reuses them.
- Not bundled in the tarball, so we don't redistribute FFmpeg.
- Update Linux README/notices/packaging comment to drop the ffmpeg apt step.
@thcp
thcp merged commit da93c5e into main Jun 25, 2026
8 checks passed
@thcp
thcp deleted the feat/export-mp4 branch June 25, 2026 21:46
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.

feat: export as mp4

1 participant