fix: honor HD minimum when falling back formats
Some checks failed
Build TubeSync image / container (push) Failing after 2m48s

This commit is contained in:
wimby
2026-09-29 09:57:02 +02:00
parent a48a7cb4f6
commit 541ab6409e
2 changed files with 39 additions and 2 deletions

View File

@@ -202,7 +202,13 @@ def get_best_video_format(media):
if not media.source.can_fallback:
# Can't fallback
return False, False
# Find the next-best format matches by height
# Find the next-best format matches by height. If the source requires
# an HD fallback, exclude sub-HD candidates before preference ranking;
# otherwise a preferred-codec 360p stream can be selected and rejected
# later even when a valid 720p stream is available.
fallback_min_height = min_height
if media.source.fallback == Val(Fallback.REQUIRE_HD):
fallback_min_height = max(fallback_min_height, fallback_hd_cutoff)
for fmt in media.iter_formats():
# If the format has an audio stream, skip it
if fmt['acodec'] is not None:
@@ -212,7 +218,7 @@ def get_best_video_format(media):
if '-sr' in fmt['id']:
continue
accept_height = (
fmt['height'] >= min_height and
fmt['height'] >= fallback_min_height and
fmt['height'] <= source_resolution_height
)
if accept_height:

View File

@@ -1,3 +1,4 @@
import json
import logging
from django.test import TestCase
from sync.models import Source, Media
@@ -384,6 +385,36 @@ class FormatMatchingTestCase(TestCase):
self.source.audio_track = Val(AudioTrack.DEFAULT)
self.assertEqual(self.media.get_format_str(), '248+251-es')
def test_require_hd_fallback_ignores_sub_hd_preferred_codec(self):
self.source.fallback = Val(Fallback.REQUIRE_HD)
self.source.source_resolution = Val(SourceResolution.VIDEO_1080P)
self.source.source_vcodec = Val(YouTube_VideoCodec.VP9)
self.source.source_acodec = Val(YouTube_AudioCodec.OPUS)
self.source.prefer_60fps = True
self.source.prefer_hdr = False
self.media.metadata = json.dumps({
'formats': [
{
'format_id': '136', 'format_note': '720p', 'height': 720,
'fps': 24, 'vcodec': 'avc1.4d401f', 'acodec': 'none',
'tbr': 1000,
},
{
'format_id': '243', 'format_note': '360p', 'height': 360,
'fps': 24, 'vcodec': 'vp9', 'acodec': 'none',
'tbr': 500,
},
{
'format_id': '251', 'format_note': 'medium',
'vcodec': 'none', 'acodec': 'opus', 'tbr': 120,
},
],
})
self.media.save()
self.assertEqual(self.media.get_best_video_format(), (False, '136'))
self.assertEqual(self.media.get_format_str(), '136+251')
def test_audio_track_preference_no_marker_is_noop(self):
# 'boring' metadata carries no (original)/(default) markers, so the
# preference must not change what gets matched