Skip to content

fix: handle transcode for videos without audio track#78

Closed
rubenxyz wants to merge 1 commit into
Techiebutler:mainfrom
rubenxyz:feature/fix-transcode-no-audio
Closed

fix: handle transcode for videos without audio track#78
rubenxyz wants to merge 1 commit into
Techiebutler:mainfrom
rubenxyz:feature/fix-transcode-no-audio

Conversation

@rubenxyz

Copy link
Copy Markdown

Description

Videos without an audio track (e.g. screen recordings, animations, silent clips) cause the transcoder to fail with Stream map 'a:0' matches no streams because -map a:0 was applied unconditionally.

Fix

  • Probe for audio streams using ffprobe -select_streams a before building the ffmpeg command
  • Only include -map a:0 and var_stream_map audio entries (v:{i},a:{i}) when has_audio=True
  • Videos without audio now produce video-only HLS output

@ravirajsinh45 ravirajsinh45 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conditional -map a:0 / var_stream_map logic for audio-less inputs is correct and a genuinely useful fix. A few things before merge:

  • Coordinate with #79 (overlapping change). This PR and #79 both edit the same region of packages/transcoder/ffmpeg_transcoder.py and neither is merged yet. GitHub reports each as individually mergeable because it only checks against main, not against each other — whichever lands second will need a rebase.
  • The probe should fail safe. The new audio probe uses subprocess.run(...) + json.loads(stdout) unchecked. On a flaky/partial probe (truncated upload, S3 stall, expired presigned URL), ffprobe exits non-zero but still prints {}, so has_audio becomes False and audio is silently dropped from a file that has it while the job reports success. Recommend check=True (a genuine no-audio file still exits 0 with {"streams": []}, so the happy path is unaffected). If #79 lands first, route this through its _run helper instead.
  • Tests. packages/transcoder/ has no tests, so the new has_audio=False branch is unverified. A unit test asserting the constructed argv (no -map a:0, var_stream_map = v:0 v:1 v:2) for both audio/no-audio inputs would lock it in.
  • Minor. This adds a second full ffprobe round-trip over the presigned URL; a single -show_streams call can yield both metadata and audio presence.

PR Techiebutler#76 (HLS Playback):
- Prefix relative stream URLs with API_URL before hls.loadSource()
- Add streamLoading to useEffect dependency array
- Lazy-import hls.js (no top-level import)
- Add Hls.Events.ERROR handler + video onError for fatal-error state
- Add canPlayType guard for native .m3u8 Safari fallback

PR Techiebutler#77 (Comment Crash):
- Replace guest_name/guest_email with guest_author.name as primary
- Fallback chain: guest_author?.name || author?.name || 'Unknown'
- Add referrerPolicy=no-referrer on avatar <img>
- Add onError fallback to initials on failed avatar load
- Use || instead of ?? for empty-string fallthrough

PR Techiebutler#78 (No-Audio Transcode):
- Add tests asserting constructed argv for has_audio=True/False
- ffprobe audio probe uses _run() with fail-fast error handling

PR Techiebutler#79 (Stderr in Error Messages):
- Add errors='replace' to subprocess.run() for Latin-1/Shift-JIS stderr
- Change ffprobe -v quiet to -v error to preserve stderr content

PR Techiebutler#80 (Clipboard Fallback):
- Extract copyToClipboard() helper into lib/utils.ts
- iOS Safari: contentEditable + setSelectionRange for execCommand
- Add aria-live region for success/failure screen reader announcements
- Add 'Failed' visual state with XCircle icon
- Use copyToClipboard in both share-dialog and share-create-dialog
@rubenxyz rubenxyz force-pushed the feature/fix-transcode-no-audio branch from 0ec29c1 to 7a1ae5f Compare June 23, 2026 08:03
@rubenxyz

Copy link
Copy Markdown
Author

Thanks for the review @ravirajsinh45. Feedback addressed:

Probe fail-safe:

Tests:

  • Added 5 unit tests in apps/api/tests/test_ffmpeg_transcoder.py:
    • test_transcode_with_audio_includes_audio_map — verifies -map a:0 and v:{i},a:{i} in var_stream_map when audio detected
    • test_transcode_without_audio_excludes_audio_map — verifies NO -map a:0 and video-only var_stream_map when no audio
    • test_run_raises_on_nonzero — RuntimeError with stderr
    • test_run_uses_errors_replaceerrors='replace' passed to subprocess
    • test_run_returns_stdout — stdout returned on success

All 5 tests pass (python -m pytest apps/api/tests/test_ffmpeg_transcoder.py -v).

@ravirajsinh45

Copy link
Copy Markdown
Contributor

Thanks for splitting this up, @rubenxyz — I can see you took the "separate PRs" suggestion to heart. 🙏 One thing to flag: #76, #77, #78, #79, and #80 currently contain the exact same diff (identical +474/-67 across the same 6 files). Each branch was created from the same combined working tree, so every PR has the whole bundle rather than just its own slice.

To keep the review in one place, I'm consolidating on #76 (which already has the full review) and closing this one as a duplicate — nothing is lost, all the changes live on #76.

If you'd like to genuinely split them later, the key is that each branch should contain only its own change. A clean way from main:

git checkout main && git pull
git checkout -b fix/one-thing
# bring over just the file(s)/hunks for that one change:
git checkout feature/your-bundle-branch -- path/to/relevant-file
git commit -m "fix: one thing"

…repeated per change, so each PR is independently reviewable. But that's optional — happy to take it all as one PR on #76 for now. Let's continue there!

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.

2 participants