A yt-dlp format file is not the primary file when merged media is present (#817)

`get_primary_file` returned the first video/audio file in whatever order it
was given, so a group holding both `<stem>.mp4` and a leftover video-only
`<stem>.f313.webm` could make the leftover the primary file.  The Video then
pointed at a file without audio, and a download of it was deferred with
"Download did not contain audio stream" on every retry.

Skip yt-dlp per-format files (`.fNNN` stems) when a merged media file is in
the group.  A group of only format files still gets one as its primary.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
lrnselfreliance 2026-10-11 16:20:44 -06:00 • committed by GitHub
parent fc7db34864
commit a95b187edc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 27 additions and 3 deletions

View file

@ -694,6 +694,10 @@ def _paths_to_files_dict(group: List[pathlib.Path]) -> List[dict]:
return files
# The stem of a yt-dlp per-format file, which is merged into the final media file. Example: The Title.f313.webm
YTDLP_FORMAT_STEM = re.compile(r'\.f\d{2,4}$')
def get_primary_file(files: Union[Tuple[pathlib.Path], Iterable[pathlib.Path]]) -> pathlib.Path:
"""Given a list of files, return the file that we can model or index.
@ -720,13 +724,16 @@ def get_primary_file(files: Union[Tuple[pathlib.Path], Iterable[pathlib.Path]])
# EPUB has high priority.
mimetypes = {i[1] for i in file_mimetypes}
has_epub = any(i.startswith('application/epub') for i in mimetypes)
# A yt-dlp per-format file (video-only or audio-only) is primary only when no merged media file is present.
has_merged_media = any(m.startswith(('video/', 'audio/')) and not YTDLP_FORMAT_STEM.search(f.stem)
for f, m in file_mimetypes)
for file, mimetype in file_mimetypes:
# These are the files that can be modeled.
if is_singlefile_file(file):
return file
if mimetype.startswith('video/'):
return file
if mimetype.startswith('audio/'):
if mimetype.startswith(('video/', 'audio/')):
if has_merged_media and YTDLP_FORMAT_STEM.search(file.stem):
continue
return file
if mimetype.startswith('application/epub'):
return file

View file

@ -1,3 +1,4 @@
import itertools
import json
import pathlib
import shutil
@ -995,6 +996,22 @@ def test_get_primary_file(test_directory, video_file, srt_file3, example_epub, e
assert lib.get_primary_file([foo, bar])
def test_get_primary_file_skips_ytdlp_format_files(test_directory):
"""A yt-dlp per-format file (`<stem>.f313.webm`) is never the primary file when the merged media is present."""
merged = test_directory / 'a video.mp4'
video_only = test_directory / 'a video.f313.webm'
audio_only = test_directory / 'a video.f251.webm'
for path in (merged, video_only):
shutil.copy(PROJECT_DIR / 'test/big_buck_bunny_720p_1mb.mp4', path)
shutil.copy(PROJECT_DIR / 'test/big_buck_bunny.mp3', audio_only)
for files in itertools.permutations([merged, video_only, audio_only]):
assert lib.get_primary_file(list(files)) == merged
# Only format files remain (an unmerged download); one must still be the primary.
assert lib.get_primary_file([video_only, audio_only]) in (video_only, audio_only)
@pytest.mark.asyncio
async def test_refresh_files_no_groups(async_client, test_session, test_directory, make_files_structure,
zip_file_factory, refresh_files):