Merge pull request #566 from tcely/patch-1
Some checks are pending
Run Django tests for TubeSync / test (3.7) (push) Waiting to run
Run Django tests for TubeSync / test (3.8) (push) Waiting to run
Run Django tests for TubeSync / test (3.9) (push) Waiting to run
Run Django tests for TubeSync / containerise (push) Waiting to run

Sort video formats by height and bitrate
This commit is contained in:
meeb 2024-12-11 01:03:24 +11:00 committed by GitHub
commit 1981301ed6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 26 additions and 8 deletions

View File

@ -5,6 +5,7 @@
''' '''
from .utils import multi_key_sort
from django.conf import settings from django.conf import settings
@ -21,7 +22,7 @@ def get_best_combined_format(media):
''' '''
for fmt in media.iter_formats(): for fmt in media.iter_formats():
# Check height matches # Check height matches
if media.source.source_resolution.strip().upper() != fmt['format']: if media.source.source_resolution_height != fmt['height']:
continue continue
# Check the video codec matches # Check the video codec matches
if media.source.source_vcodec != fmt['vcodec']: if media.source.source_vcodec != fmt['vcodec']:
@ -47,7 +48,7 @@ def get_best_audio_format(media):
Finds the best match for the source required audio format. If the source Finds the best match for the source required audio format. If the source
has a 'fallback' of fail this can return no match. 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 = [] audio_formats = []
for fmt in media.iter_formats(): for fmt in media.iter_formats():
# If the format has a video stream, skip it # If the format has a video stream, skip it
@ -56,18 +57,18 @@ def get_best_audio_format(media):
if not fmt['acodec']: if not fmt['acodec']:
continue continue
audio_formats.append(fmt) audio_formats.append(fmt)
audio_formats = list(reversed(sorted(audio_formats, key=lambda k: k['abr'])))
if not audio_formats: if not audio_formats:
# Media has no audio formats at all # Media has no audio formats at all
return False, False return False, False
# Find the highest bitrate audio format with a matching codec audio_formats = list(reversed(audio_formats))
# Find the first audio format with a matching codec
for fmt in audio_formats: for fmt in audio_formats:
if media.source.source_acodec == fmt['acodec']: if media.source.source_acodec == fmt['acodec']:
# Matched! # Matched!
return True, fmt['id'] return True, fmt['id']
# No codecs matched # No codecs matched
if media.source.can_fallback: 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'] return False, audio_formats[0]['id']
else: else:
# Can't fallback # Can't fallback
@ -86,6 +87,7 @@ def get_best_video_format(media):
return False, False return False, False
# Filter video-only formats by resolution that matches the source # Filter video-only formats by resolution that matches the source
video_formats = [] video_formats = []
sort_keys = [('height', False), ('vcodec', True), ('vbr', False)] # key, reverse
for fmt in media.iter_formats(): for fmt in media.iter_formats():
# If the format has an audio stream, skip it # If the format has an audio stream, skip it
if fmt['acodec'] is not None: if fmt['acodec'] is not None:
@ -94,6 +96,8 @@ def get_best_video_format(media):
continue continue
if media.source.source_resolution.strip().upper() == fmt['format']: if media.source.source_resolution.strip().upper() == fmt['format']:
video_formats.append(fmt) video_formats.append(fmt)
elif media.source.source_resolution_height == fmt['height']:
video_formats.append(fmt)
# Check we matched some streams # Check we matched some streams
if not video_formats: if not video_formats:
# No streams match the requested resolution, see if we can fallback # No streams match the requested resolution, see if we can fallback
@ -109,13 +113,17 @@ def get_best_video_format(media):
else: else:
# Can't fallback # Can't fallback
return False, False return False, False
video_formats = list(reversed(sorted(video_formats, key=lambda k: k['height'])))
source_resolution = media.source.source_resolution.strip().upper()
source_vcodec = media.source.source_vcodec
if not video_formats: if not video_formats:
# Still no matches # Still no matches
return False, False 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 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 # Of our filtered video formats, check for resolution + codec + hdr + fps match
if media.source.prefer_60fps and media.source.prefer_hdr: if media.source.prefer_60fps and media.source.prefer_hdr:
for fmt in video_formats: for fmt in video_formats:

View File

@ -1,6 +1,7 @@
import os import os
import re import re
import math import math
from operator import itemgetter
from pathlib import Path from pathlib import Path
import requests import requests
from PIL import Image from PIL import Image
@ -134,6 +135,15 @@ def seconds_to_timestr(seconds):
return '{:02d}:{:02d}:{:02d}'.format(hour, minutes, 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 normalize_codec(codec_str): def normalize_codec(codec_str):
result = str(codec_str).upper() result = str(codec_str).upper()
parts = result.split('.') parts = result.split('.')