From 4b60073ef06a76c87ed487cebd51f1e71e2190cb Mon Sep 17 00:00:00 2001 From: tcely Date: Mon, 2 Dec 2024 10:38:22 -0500 Subject: [PATCH 1/8] Add multiple key sorting --- tubesync/sync/utils.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tubesync/sync/utils.py b/tubesync/sync/utils.py index cf72462e..f49c9894 100644 --- a/tubesync/sync/utils.py +++ b/tubesync/sync/utils.py @@ -1,6 +1,7 @@ import os import re import math +from operator import itemgetter from pathlib import Path import requests from PIL import Image @@ -134,6 +135,15 @@ def seconds_to_timestr(seconds): return '{:02d}:{:02d}:{:02d}'.format(hour, minutes, seconds) +def multi_key_sort(sort_dict, specs, use_reversed=False): + result = list(sort_dict) + for key, reverse in reversed(specs): + result = sorted(result, key=itemgetter(key), reverse=reverse) + if use_reversed: + return list(reversed(result)) + return result + + def parse_media_format(format_dict): ''' This parser primarily adapts the format dict returned by youtube-dl into a From ede9dff4e6b2cbbf8ca81eb6681c744d61fcbf89 Mon Sep 17 00:00:00 2001 From: tcely Date: Mon, 2 Dec 2024 10:56:19 -0500 Subject: [PATCH 2/8] Use new multiple key sorting --- tubesync/sync/matching.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tubesync/sync/matching.py b/tubesync/sync/matching.py index c453ff96..5d5b5dbd 100644 --- a/tubesync/sync/matching.py +++ b/tubesync/sync/matching.py @@ -5,6 +5,7 @@ ''' +from .utils import multi_key_sort from django.conf import settings @@ -49,6 +50,7 @@ def get_best_audio_format(media): ''' # Order all audio-only formats by bitrate audio_formats = [] + sort_keys = [('abr', True)] # key, reverse for fmt in media.iter_formats(): # If the format has a video stream, skip it if fmt['vcodec'] is not None: @@ -56,7 +58,7 @@ def get_best_audio_format(media): if not fmt['acodec']: continue audio_formats.append(fmt) - audio_formats = list(reversed(sorted(audio_formats, key=lambda k: k['abr']))) + audio_formats = list(multi_key_sort(audio_formats, sort_keys)) if not audio_formats: # Media has no audio formats at all return False, False @@ -86,6 +88,7 @@ def get_best_video_format(media): return False, False # Filter video-only formats by resolution that matches the source video_formats = [] + sort_keys = [('height', True), ('id', True)] # key, reverse for fmt in media.iter_formats(): # If the format has an audio stream, skip it if fmt['acodec'] is not None: @@ -109,7 +112,7 @@ def get_best_video_format(media): else: # Can't fallback return False, False - video_formats = list(reversed(sorted(video_formats, key=lambda k: k['height']))) + video_formats = list(multi_key_sort(video_formats, sort_keys)) source_resolution = media.source.source_resolution.strip().upper() source_vcodec = media.source.source_vcodec if not video_formats: From 77247f7a7f01158252eb8415097338fc2bdbc6dc Mon Sep 17 00:00:00 2001 From: tcely Date: Mon, 2 Dec 2024 13:33:00 -0500 Subject: [PATCH 3/8] Use reversed argument for multiple key sorting --- tubesync/sync/matching.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tubesync/sync/matching.py b/tubesync/sync/matching.py index 5d5b5dbd..7fdc303a 100644 --- a/tubesync/sync/matching.py +++ b/tubesync/sync/matching.py @@ -50,7 +50,7 @@ def get_best_audio_format(media): ''' # Order all audio-only formats by bitrate audio_formats = [] - sort_keys = [('abr', True)] # key, reverse + sort_keys = [('abr', False)] # key, reverse for fmt in media.iter_formats(): # If the format has a video stream, skip it if fmt['vcodec'] is not None: @@ -58,7 +58,7 @@ def get_best_audio_format(media): if not fmt['acodec']: continue audio_formats.append(fmt) - audio_formats = list(multi_key_sort(audio_formats, sort_keys)) + audio_formats = multi_key_sort(audio_formats, sort_keys, True) if not audio_formats: # Media has no audio formats at all return False, False @@ -88,7 +88,7 @@ def get_best_video_format(media): return False, False # Filter video-only formats by resolution that matches the source video_formats = [] - sort_keys = [('height', True), ('id', True)] # key, reverse + sort_keys = [('height', False), ('id', False)] # key, reverse for fmt in media.iter_formats(): # If the format has an audio stream, skip it if fmt['acodec'] is not None: @@ -112,7 +112,7 @@ def get_best_video_format(media): else: # Can't fallback return False, False - video_formats = list(multi_key_sort(video_formats, sort_keys)) + video_formats = multi_key_sort(video_formats, sort_keys, True) source_resolution = media.source.source_resolution.strip().upper() source_vcodec = media.source.source_vcodec if not video_formats: From 526c6a97b4f6748f68563a4b4f7449125eb0ec92 Mon Sep 17 00:00:00 2001 From: tcely Date: Mon, 2 Dec 2024 23:09:10 -0500 Subject: [PATCH 4/8] Match against height instead of format --- tubesync/sync/matching.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tubesync/sync/matching.py b/tubesync/sync/matching.py index 7fdc303a..75009dff 100644 --- a/tubesync/sync/matching.py +++ b/tubesync/sync/matching.py @@ -22,7 +22,7 @@ def get_best_combined_format(media): ''' for fmt in media.iter_formats(): # Check height matches - if media.source.source_resolution.strip().upper() != fmt['format']: + if media.source.source_resolution_height != fmt['height']: continue # Check the video codec matches if media.source.source_vcodec != fmt['vcodec']: @@ -97,6 +97,8 @@ def get_best_video_format(media): continue if media.source.source_resolution.strip().upper() == fmt['format']: video_formats.append(fmt) + elif media.source.source_resolution_height == fmt['height']: + video_formats.append(fmt) # Check we matched some streams if not video_formats: # No streams match the requested resolution, see if we can fallback From ed98f56946301e6138691ede107c7d4234695736 Mon Sep 17 00:00:00 2001 From: tcely Date: Wed, 4 Dec 2024 16:36:52 -0500 Subject: [PATCH 5/8] Adjust VP09 vcodec to match VP09 source vcodec --- tubesync/sync/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tubesync/sync/utils.py b/tubesync/sync/utils.py index f49c9894..3dfcba2d 100644 --- a/tubesync/sync/utils.py +++ b/tubesync/sync/utils.py @@ -158,6 +158,8 @@ def parse_media_format(format_dict): vcodec = None if vcodec == 'NONE': vcodec = None + if vcodec == 'VP09': + vcodec = 'VP9' acodec_full = format_dict.get('acodec', '') acodec_parts = acodec_full.split('.') if len(acodec_parts) > 0: From 6d7d3483a43374d46ccb94a50967ab2415f22194 Mon Sep 17 00:00:00 2001 From: tcely Date: Wed, 4 Dec 2024 16:52:10 -0500 Subject: [PATCH 6/8] Use the audio format list from youtube-dl --- tubesync/sync/matching.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tubesync/sync/matching.py b/tubesync/sync/matching.py index 75009dff..6a72972d 100644 --- a/tubesync/sync/matching.py +++ b/tubesync/sync/matching.py @@ -48,9 +48,8 @@ def get_best_audio_format(media): Finds the best match for the source required audio format. If the source has a 'fallback' of fail this can return no match. ''' - # Order all audio-only formats by bitrate + # Reverse order all audio-only formats audio_formats = [] - sort_keys = [('abr', False)] # key, reverse for fmt in media.iter_formats(): # If the format has a video stream, skip it if fmt['vcodec'] is not None: @@ -58,18 +57,18 @@ def get_best_audio_format(media): if not fmt['acodec']: continue audio_formats.append(fmt) - audio_formats = multi_key_sort(audio_formats, sort_keys, True) + audio_formats = list(reversed(audio_formats)) if not audio_formats: # Media has no audio formats at all return False, False - # Find the highest bitrate audio format with a matching codec + # Find the first audio format with a matching codec for fmt in audio_formats: if media.source.source_acodec == fmt['acodec']: # Matched! return True, fmt['id'] # No codecs matched if media.source.can_fallback: - # Can fallback, find the next highest bitrate non-matching codec + # Can fallback, find the next non-matching codec return False, audio_formats[0]['id'] else: # Can't fallback From 09044aa95d4e8cf5c5c4926eb08da3de2e7fe8a4 Mon Sep 17 00:00:00 2001 From: tcely Date: Wed, 4 Dec 2024 17:03:18 -0500 Subject: [PATCH 7/8] Return for empty format lists first --- tubesync/sync/matching.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tubesync/sync/matching.py b/tubesync/sync/matching.py index 6a72972d..982af040 100644 --- a/tubesync/sync/matching.py +++ b/tubesync/sync/matching.py @@ -57,10 +57,10 @@ def get_best_audio_format(media): if not fmt['acodec']: continue audio_formats.append(fmt) - audio_formats = list(reversed(audio_formats)) if not audio_formats: # Media has no audio formats at all return False, False + audio_formats = list(reversed(audio_formats)) # Find the first audio format with a matching codec for fmt in audio_formats: if media.source.source_acodec == fmt['acodec']: @@ -113,12 +113,12 @@ def get_best_video_format(media): else: # Can't fallback return False, False - video_formats = multi_key_sort(video_formats, sort_keys, True) - source_resolution = media.source.source_resolution.strip().upper() - source_vcodec = media.source.source_vcodec if not video_formats: # Still no matches return False, False + video_formats = multi_key_sort(video_formats, sort_keys, True) + source_resolution = media.source.source_resolution.strip().upper() + source_vcodec = media.source.source_vcodec exact_match, best_match = None, None # Of our filtered video formats, check for resolution + codec + hdr + fps match if media.source.prefer_60fps and media.source.prefer_hdr: From b1a4c8bbaf244591e3b8af781fa61a13c5882bdf Mon Sep 17 00:00:00 2001 From: tcely Date: Wed, 4 Dec 2024 17:08:37 -0500 Subject: [PATCH 8/8] Include streams with a blank format_note and the proper height --- tubesync/sync/matching.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tubesync/sync/matching.py b/tubesync/sync/matching.py index 982af040..0b1f8d34 100644 --- a/tubesync/sync/matching.py +++ b/tubesync/sync/matching.py @@ -87,7 +87,7 @@ def get_best_video_format(media): return False, False # Filter video-only formats by resolution that matches the source video_formats = [] - sort_keys = [('height', False), ('id', False)] # key, reverse + sort_keys = [('height', False), ('vcodec', True), ('vbr', False)] # key, reverse for fmt in media.iter_formats(): # If the format has an audio stream, skip it if fmt['acodec'] is not None: @@ -120,6 +120,10 @@ def get_best_video_format(media): source_resolution = media.source.source_resolution.strip().upper() source_vcodec = media.source.source_vcodec exact_match, best_match = None, None + for fmt in video_formats: + # format_note was blank, match height instead + if '' == fmt['format'] and fmt['height'] == media.source.source_resolution_height: + fmt['format'] = source_resolution # Of our filtered video formats, check for resolution + codec + hdr + fps match if media.source.prefer_60fps and media.source.prefer_hdr: for fmt in video_formats: