Add fast audio-only transcode mode; fixes no-sound from AC3/DTS/EAC3

Root cause of "movies have no volume control / can't hear anything":
0 of 200 movies had finished transcoding, so every movie streamed its
raw source file — and BDRip/AVI sources very commonly carry AC3/DTS/EAC3
audio, which no browser can decode natively. Browsers respond by hiding
the volume control entirely when they can't find a playable audio track,
which is exactly the symptom reported.

New "audio" transcode quality: video is stream-copied untouched, only
the audio track is re-encoded to AAC — dramatically faster than a full
ABR pass since no video re-encoding happens. Added:
- transcode_audio_fix() in transcode.py
- "audio" as a valid quality value everywhere quick/abr were validated
- POST /transcode/queue/fix-audio-all: cancels every still-pending job
  and replaces it with the fast audio fix for the same content (running
  jobs are left alone), so a library-wide backlog of slow ABR jobs can
  be swapped for something that actually restores sound soon
- Per-movie "Audio" button in Admin, "Fix Audio (Fast, All)" button on
  the Transcode Queue page

Note: this doesn't help HEVC/x265 sources, whose video (not just audio)
isn't natively browser-playable either — those still need a full ABR
re-encode to H.264.
This commit is contained in:
Myron Blair
2026-07-26 23:31:23 -05:00
parent cf8c7890be
commit b74d8a2652
4 changed files with 73 additions and 7 deletions
+22
View File
@@ -56,6 +56,28 @@ def variants_for_source(src_height: int) -> List[Tuple[int, str, str, str, str]]
return chosen
async def transcode_audio_fix(source: Path, out_dir: Path, on_status: Callable[..., Awaitable]) -> None:
"""Video stream-copied untouched, audio re-encoded to AAC — fixes browsers' inability to
play AC3/DTS/EAC3 (common on BDRips) without paying for a full video re-encode. Same
H.264-source assumption as transcode_quick; HEVC/x265 sources still need the full ABR path
since the video itself isn't browser-playable either in that case."""
if not source.is_file():
await on_status("failed", error=f"Source missing: {source}")
return
out_dir.mkdir(parents=True, exist_ok=True)
cmd = [
"nice", "-n", str(TRANSCODE_NICE),
"ffmpeg", "-y", "-i", str(source),
"-c:v", "copy", "-c:a", "aac", "-b:a", "192k", "-ac", "2",
"-bsf:v", "h264_mp4toannexb",
"-f", "hls", "-hls_time", "6",
"-hls_list_size", "0", "-hls_playlist_type", "vod",
"-hls_segment_filename", str(out_dir / "seg_%04d.ts"),
str(out_dir / "playlist.m3u8"),
]
await _run(cmd, on_status, out_dir, "playlist.m3u8")
async def transcode_quick(source: Path, out_dir: Path, on_status: Callable[..., Awaitable]) -> None:
"""Stream-copy to single-rate HLS. Output filename: playlist.m3u8."""
if not source.is_file():