Redo av codec settings & selections to accomodate webm
Allows for ranked preferences for h264, av1, and vp9 codecs in settings, along with equal preferences which are tiebroken using smaller file size. For each quality, gives av-merge a list of video sources and audio sources sorted based on preference & file size. It will pick the first one that the browser supports. Closes #84 Signed-off-by: Jesús <heckyel@hyperbola.info>
This commit is contained in:
parent
854ab81b91
commit
9c7e93ecf8
56
settings.py
56
settings.py
@ -168,14 +168,34 @@ For security reasons, enabling this is not recommended.''',
|
|||||||
'category': 'playback',
|
'category': 'playback',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
('preferred_video_codec', {
|
('codec_rank_h264', {
|
||||||
'type': int,
|
'type': int,
|
||||||
'default': 0,
|
'default': 1,
|
||||||
|
'label': 'H.264 Codec Ranking',
|
||||||
'comment': '',
|
'comment': '',
|
||||||
'options': [
|
'options': [(1, '#1'), (2, '#2'), (3, '#3')],
|
||||||
(0, 'h.264'),
|
'category': 'playback',
|
||||||
(1, 'AV1'),
|
'description': (
|
||||||
],
|
'Which video codecs to prefer. Codecs given the same '
|
||||||
|
'ranking will use smaller file size as a tiebreaker.'
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
('codec_rank_vp', {
|
||||||
|
'type': int,
|
||||||
|
'default': 2,
|
||||||
|
'label': 'VP8/VP9 Codec Ranking',
|
||||||
|
'comment': '',
|
||||||
|
'options': [(1, '#1'), (2, '#2'), (3, '#3')],
|
||||||
|
'category': 'playback',
|
||||||
|
}),
|
||||||
|
|
||||||
|
('codec_rank_av1', {
|
||||||
|
'type': int,
|
||||||
|
'default': 3,
|
||||||
|
'label': 'AV1 Codec Ranking',
|
||||||
|
'comment': '',
|
||||||
|
'options': [(1, '#1'), (2, '#2'), (3, '#3')],
|
||||||
'category': 'playback',
|
'category': 'playback',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@ -280,14 +300,16 @@ For security reasons, enabling this is not recommended.''',
|
|||||||
|
|
||||||
('settings_version', {
|
('settings_version', {
|
||||||
'type': int,
|
'type': int,
|
||||||
'default': 3,
|
'default': 4,
|
||||||
'comment': '''Do not change, remove, or comment out this value, or else your settings may be lost or corrupted''',
|
'comment': '''Do not change, remove, or comment out this value, or else your settings may be lost or corrupted''',
|
||||||
'hidden': True,
|
'hidden': True,
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
|
|
||||||
program_directory = os.path.dirname(os.path.realpath(__file__))
|
program_directory = os.path.dirname(os.path.realpath(__file__))
|
||||||
acceptable_targets = SETTINGS_INFO.keys() | {'enable_comments', 'enable_related_videos'}
|
acceptable_targets = SETTINGS_INFO.keys() | {
|
||||||
|
'enable_comments', 'enable_related_videos', 'preferred_video_codec'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def comment_string(comment):
|
def comment_string(comment):
|
||||||
@ -334,9 +356,27 @@ def upgrade_to_3(settings_dict):
|
|||||||
return new_settings
|
return new_settings
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade_to_4(settings_dict):
|
||||||
|
new_settings = settings_dict.copy()
|
||||||
|
if 'preferred_video_codec' in settings_dict:
|
||||||
|
pref = settings_dict['preferred_video_codec']
|
||||||
|
if pref == 0:
|
||||||
|
new_settings['codec_rank_h264'] = 1
|
||||||
|
new_settings['codec_rank_vp'] = 2
|
||||||
|
new_settings['codec_rank_av1'] = 3
|
||||||
|
else:
|
||||||
|
new_settings['codec_rank_h264'] = 3
|
||||||
|
new_settings['codec_rank_vp'] = 2
|
||||||
|
new_settings['codec_rank_av1'] = 1
|
||||||
|
del new_settings['preferred_video_codec']
|
||||||
|
new_settings['settings_version'] = 4
|
||||||
|
return new_settings
|
||||||
|
|
||||||
|
|
||||||
upgrade_functions = {
|
upgrade_functions = {
|
||||||
1: upgrade_to_2,
|
1: upgrade_to_2,
|
||||||
2: upgrade_to_3,
|
2: upgrade_to_3,
|
||||||
|
3: upgrade_to_4,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,13 +19,12 @@
|
|||||||
|
|
||||||
// TODO: Call abort to cancel in-progress appends?
|
// TODO: Call abort to cancel in-progress appends?
|
||||||
|
|
||||||
function AVMerge(video, srcPair, startTime){
|
|
||||||
this.videoSource = srcPair[0];
|
|
||||||
this.audioSource = srcPair[1];
|
function AVMerge(video, srcInfo, startTime){
|
||||||
if (this.videoSource.bitrate && this.audioSource.bitrate)
|
this.audioSource = null;
|
||||||
this.avRatio = this.audioSource.bitrate/this.videoSource.bitrate;
|
this.videoSource = null;
|
||||||
else
|
this.avRatio = null;
|
||||||
this.avRatio = 1/10;
|
|
||||||
this.videoStream = null;
|
this.videoStream = null;
|
||||||
this.audioStream = null;
|
this.audioStream = null;
|
||||||
this.seeking = false;
|
this.seeking = false;
|
||||||
@ -36,31 +35,49 @@ function AVMerge(video, srcPair, startTime){
|
|||||||
this.opened = false;
|
this.opened = false;
|
||||||
this.audioEndOfStreamCalled = false;
|
this.audioEndOfStreamCalled = false;
|
||||||
this.videoEndOfStreamCalled = false;
|
this.videoEndOfStreamCalled = false;
|
||||||
this.setup();
|
|
||||||
}
|
|
||||||
AVMerge.prototype.setup = function() {
|
|
||||||
if (!('MediaSource' in window)) {
|
if (!('MediaSource' in window)) {
|
||||||
reportError('MediaSource not supported.');
|
reportError('MediaSource not supported.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var audioSupported = MediaSource.isTypeSupported(
|
|
||||||
this.audioSource['mime_codec']
|
// Find supported video and audio sources
|
||||||
)
|
for (var src of srcInfo['videos']) {
|
||||||
var videoSupported = MediaSource.isTypeSupported(
|
if (MediaSource.isTypeSupported(src['mime_codec'])) {
|
||||||
this.videoSource['mime_codec']
|
reportDebug('Using video source', src['mime_codec'],
|
||||||
)
|
src['quality_string'], 'itag', src['itag']);
|
||||||
if (!audioSupported)
|
this.videoSource = src;
|
||||||
reportError('Unsupported MIME type or codec: ',
|
break;
|
||||||
this.audioSource['mime_codec']);
|
}
|
||||||
if (!videoSupported)
|
}
|
||||||
reportError('Unsupported MIME type or codec: ',
|
for (var src of srcInfo['audios']) {
|
||||||
this.videoSource['mime_codec']);
|
if (MediaSource.isTypeSupported(src['mime_codec'])) {
|
||||||
if (audioSupported && videoSupported) {
|
reportDebug('Using audio source', src['mime_codec'],
|
||||||
|
src['quality_string'], 'itag', src['itag']);
|
||||||
|
this.audioSource = src;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.videoSource === null)
|
||||||
|
reportError('No supported video MIME type or codec found: ',
|
||||||
|
srcInfo['videos'].map(s => s.mime_codec).join(', '));
|
||||||
|
if (this.audioSource === null)
|
||||||
|
reportError('No supported audio MIME type or codec found: ',
|
||||||
|
srcInfo['audios'].map(s => s.mime_codec).join(', '));
|
||||||
|
if (this.videoSource === null || this.audioSource === null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this.videoSource.bitrate && this.audioSource.bitrate)
|
||||||
|
this.avRatio = this.audioSource.bitrate/this.videoSource.bitrate;
|
||||||
|
else
|
||||||
|
this.avRatio = 1/10;
|
||||||
|
|
||||||
|
this.setup();
|
||||||
|
}
|
||||||
|
AVMerge.prototype.setup = function() {
|
||||||
this.mediaSource = new MediaSource();
|
this.mediaSource = new MediaSource();
|
||||||
this.video.src = URL.createObjectURL(this.mediaSource);
|
this.video.src = URL.createObjectURL(this.mediaSource);
|
||||||
this.mediaSource.onsourceopen = this.sourceOpen.bind(this);
|
this.mediaSource.onsourceopen = this.sourceOpen.bind(this);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
AVMerge.prototype.sourceOpen = function(_) {
|
AVMerge.prototype.sourceOpen = function(_) {
|
||||||
// If after calling mediaSource.endOfStream, the user seeks back
|
// If after calling mediaSource.endOfStream, the user seeks back
|
||||||
|
@ -20,10 +20,10 @@
|
|||||||
qualityOptions.push(src.quality_string)
|
qualityOptions.push(src.quality_string)
|
||||||
}
|
}
|
||||||
for (var src of data['pair_sources']) {
|
for (var src of data['pair_sources']) {
|
||||||
qualityOptions.push(src[0].quality_string)
|
qualityOptions.push(src.quality_string)
|
||||||
}
|
}
|
||||||
if (data['using_pair_sources'])
|
if (data['using_pair_sources'])
|
||||||
qualityDefault = data['pair_sources'][data['pair_idx']][0].quality_string;
|
qualityDefault = data['pair_sources'][data['pair_idx']].quality_string;
|
||||||
else if (data['uni_sources'].length != 0)
|
else if (data['uni_sources'].length != 0)
|
||||||
qualityDefault = data['uni_sources'][data['uni_idx']].quality_string;
|
qualityDefault = data['uni_sources'][data['uni_idx']].quality_string;
|
||||||
else
|
else
|
||||||
@ -108,7 +108,7 @@
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (var i=0; i < data['pair_sources'].length; i++) {
|
for (var i=0; i < data['pair_sources'].length; i++) {
|
||||||
if (data['pair_sources'][i][0].quality_string == quality) {
|
if (data['pair_sources'][i].quality_string == quality) {
|
||||||
changeQuality({'type': 'pair', 'index': i});
|
changeQuality({'type': 'pair', 'index': i});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -4,16 +4,15 @@ function changeQuality(selection) {
|
|||||||
var currentVideoTime = video.currentTime;
|
var currentVideoTime = video.currentTime;
|
||||||
var videoPaused = video.paused;
|
var videoPaused = video.paused;
|
||||||
var videoSpeed = video.playbackRate;
|
var videoSpeed = video.playbackRate;
|
||||||
var videoSource;
|
var srcInfo;
|
||||||
if (avMerge)
|
if (avMerge)
|
||||||
avMerge.close();
|
avMerge.close();
|
||||||
if (selection.type == 'uni'){
|
if (selection.type == 'uni'){
|
||||||
videoSource = data['uni_sources'][selection.index];
|
srcInfo = data['uni_sources'][selection.index];
|
||||||
video.src = videoSource.url;
|
video.src = srcInfo.url;
|
||||||
} else {
|
} else {
|
||||||
let srcPair = data['pair_sources'][selection.index];
|
srcInfo = data['pair_sources'][selection.index];
|
||||||
videoSource = srcPair[0];
|
avMerge = new AVMerge(video, srcInfo, currentVideoTime);
|
||||||
avMerge = new AVMerge(video, srcPair, currentVideoTime);
|
|
||||||
}
|
}
|
||||||
video.currentTime = currentVideoTime;
|
video.currentTime = currentVideoTime;
|
||||||
if (!videoPaused){
|
if (!videoPaused){
|
||||||
@ -26,7 +25,6 @@ function changeQuality(selection) {
|
|||||||
var avMerge;
|
var avMerge;
|
||||||
if (data.using_pair_sources) {
|
if (data.using_pair_sources) {
|
||||||
var srcPair = data['pair_sources'][data['pair_idx']];
|
var srcPair = data['pair_sources'][data['pair_idx']];
|
||||||
var videoSource = srcPair[0];
|
|
||||||
// Do it dynamically rather than as the default in jinja
|
// Do it dynamically rather than as the default in jinja
|
||||||
// in case javascript is disabled
|
// in case javascript is disabled
|
||||||
avMerge = new AVMerge(video, srcPair, 0);
|
avMerge = new AVMerge(video, srcPair, 0);
|
||||||
|
@ -87,7 +87,7 @@
|
|||||||
<option value='{"type": "uni", "index": {{ loop.index0 }}}' {{ 'selected' if loop.index0 == uni_idx and not using_pair_sources else '' }} >{{ src['quality_string'] }}</option>
|
<option value='{"type": "uni", "index": {{ loop.index0 }}}' {{ 'selected' if loop.index0 == uni_idx and not using_pair_sources else '' }} >{{ src['quality_string'] }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for src_pair in pair_sources %}
|
{% for src_pair in pair_sources %}
|
||||||
<option value='{"type": "pair", "index": {{ loop.index0}}}' {{ 'selected' if loop.index0 == pair_idx and using_pair_sources else '' }} >{{ src_pair[0]['quality_string'] }}, {{ src_pair[1]['quality_string'] }}</option>
|
<option value='{"type": "pair", "index": {{ loop.index0}}}' {{ 'selected' if loop.index0 == pair_idx and using_pair_sources else '' }} >{{ src_pair['quality_string'] }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
128
youtube/watch.py
128
youtube/watch.py
@ -23,22 +23,31 @@ except FileNotFoundError:
|
|||||||
decrypt_cache = {}
|
decrypt_cache = {}
|
||||||
|
|
||||||
|
|
||||||
|
def codec_name(vcodec):
|
||||||
|
if vcodec.startswith('avc'):
|
||||||
|
return 'h264'
|
||||||
|
elif vcodec.startswith('av01'):
|
||||||
|
return 'av1'
|
||||||
|
elif vcodec.startswith('vp'):
|
||||||
|
return 'vp'
|
||||||
|
else:
|
||||||
|
return 'unknown'
|
||||||
|
|
||||||
|
|
||||||
def get_video_sources(info, target_resolution):
|
def get_video_sources(info, target_resolution):
|
||||||
'''return dict with organized sources: {
|
'''return dict with organized sources: {
|
||||||
'uni_sources': [{}, ...], # video and audio in one file
|
'uni_sources': [{}, ...], # video and audio in one file
|
||||||
'uni_idx': int, # default unified source index
|
'uni_idx': int, # default unified source index
|
||||||
'pair_sources': [({video}, {audio}), ...],
|
'pair_sources': [{video: {}, audio: {}, quality: ..., ...}, ...],
|
||||||
'pair_idx': int, # default pair source index
|
'pair_idx': int, # default pair source index
|
||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
audio_sources = []
|
audio_sources = []
|
||||||
video_only_sources = []
|
video_only_sources = {}
|
||||||
uni_sources = []
|
uni_sources = []
|
||||||
pair_sources = []
|
pair_sources = []
|
||||||
for fmt in info['formats']:
|
for fmt in info['formats']:
|
||||||
if not all(fmt[attr] for attr in ('ext', 'url')):
|
if not all(fmt[attr] for attr in ('ext', 'url', 'itag')):
|
||||||
continue
|
|
||||||
if fmt['ext'] != 'mp4': # temporary until webm support
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# unified source
|
# unified source
|
||||||
@ -69,8 +78,11 @@ def get_video_sources(info, target_resolution):
|
|||||||
source['mime_codec'] = (source['type'] + '; codecs="'
|
source['mime_codec'] = (source['type'] + '; codecs="'
|
||||||
+ source['acodec'] + '"')
|
+ source['acodec'] + '"')
|
||||||
audio_sources.append(source)
|
audio_sources.append(source)
|
||||||
# video-only source, include audio source
|
# video-only source
|
||||||
elif all(fmt[attr] for attr in ('vcodec', 'quality', 'width')):
|
elif all(fmt[attr] for attr in ('vcodec', 'quality', 'width', 'fps',
|
||||||
|
'file_size')):
|
||||||
|
if codec_name(fmt['vcodec']) == 'unknown':
|
||||||
|
continue
|
||||||
source = {
|
source = {
|
||||||
'type': 'video/' + fmt['ext'],
|
'type': 'video/' + fmt['ext'],
|
||||||
'quality_string': short_video_quality_string(fmt),
|
'quality_string': short_video_quality_string(fmt),
|
||||||
@ -78,66 +90,61 @@ def get_video_sources(info, target_resolution):
|
|||||||
source.update(fmt)
|
source.update(fmt)
|
||||||
source['mime_codec'] = (source['type'] + '; codecs="'
|
source['mime_codec'] = (source['type'] + '; codecs="'
|
||||||
+ source['vcodec'] + '"')
|
+ source['vcodec'] + '"')
|
||||||
video_only_sources.append(source)
|
quality = str(fmt['quality']) + 'p' + str(fmt['fps'])
|
||||||
|
if quality in video_only_sources:
|
||||||
# Remove alternative mp4 codecs from video sources
|
video_only_sources[quality].append(source)
|
||||||
def codec_name(vcodec):
|
|
||||||
if vcodec.startswith('avc'):
|
|
||||||
return 'h.264'
|
|
||||||
elif vcodec.startswith('av01'):
|
|
||||||
return 'av1'
|
|
||||||
else:
|
else:
|
||||||
return 'unknown'
|
video_only_sources[quality] = [source]
|
||||||
quality_to_codecs = {}
|
|
||||||
for src in video_only_sources:
|
|
||||||
if src['quality'] in quality_to_codecs:
|
|
||||||
quality_to_codecs[src['quality']].add(codec_name(src['vcodec']))
|
|
||||||
else:
|
|
||||||
quality_to_codecs[src['quality']] = {codec_name(src['vcodec'])}
|
|
||||||
i = 0
|
|
||||||
while i < len(video_only_sources):
|
|
||||||
src = video_only_sources[i]
|
|
||||||
codecs_for_quality = quality_to_codecs[src['quality']]
|
|
||||||
have_both = ('h.264' in codecs_for_quality
|
|
||||||
and 'av1' in codecs_for_quality)
|
|
||||||
have_one = ('h.264' in codecs_for_quality
|
|
||||||
or 'av1' in codecs_for_quality)
|
|
||||||
name = codec_name(src['vcodec'])
|
|
||||||
if name == 'unknown' and have_one:
|
|
||||||
del video_only_sources[i]
|
|
||||||
continue
|
|
||||||
if not have_both:
|
|
||||||
i += 1
|
|
||||||
continue
|
|
||||||
if name == 'av1' and settings.preferred_video_codec == 0:
|
|
||||||
del video_only_sources[i]
|
|
||||||
elif name == 'h.264' and settings.preferred_video_codec == 1:
|
|
||||||
del video_only_sources[i]
|
|
||||||
else:
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
audio_sources.sort(key=lambda source: source['audio_bitrate'])
|
audio_sources.sort(key=lambda source: source['audio_bitrate'])
|
||||||
video_only_sources.sort(key=lambda src: src['quality'])
|
|
||||||
uni_sources.sort(key=lambda src: src['quality'])
|
uni_sources.sort(key=lambda src: src['quality'])
|
||||||
|
|
||||||
for source in video_only_sources:
|
webm_audios = [a for a in audio_sources if a['ext'] == 'webm']
|
||||||
|
mp4_audios = [a for a in audio_sources if a['ext'] == 'mp4']
|
||||||
|
|
||||||
|
for quality_string, sources in video_only_sources.items():
|
||||||
# choose an audio source to go with it
|
# choose an audio source to go with it
|
||||||
# 0.5 is semiarbitrary empirical constant to spread audio sources
|
# 0.5 is semiarbitrary empirical constant to spread audio sources
|
||||||
# between 144p and 1080p. Use something better eventually.
|
# between 144p and 1080p. Use something better eventually.
|
||||||
target_audio_bitrate = source['quality']*source.get('fps', 30)/30*0.5
|
quality, fps = map(int, quality_string.split('p'))
|
||||||
compat_audios = [a for a in audio_sources if a['ext'] == source['ext']]
|
target_audio_bitrate = quality*fps/30*0.5
|
||||||
if compat_audios:
|
pair_info = {
|
||||||
closest_audio_source = compat_audios[0]
|
'quality_string': quality_string,
|
||||||
best_err = target_audio_bitrate - compat_audios[0]['audio_bitrate']
|
'quality': quality,
|
||||||
|
'height': sources[0]['height'],
|
||||||
|
'width': sources[0]['width'],
|
||||||
|
'fps': fps,
|
||||||
|
'videos': sources,
|
||||||
|
'audios': [],
|
||||||
|
}
|
||||||
|
for audio_choices in (webm_audios, mp4_audios):
|
||||||
|
if not audio_choices:
|
||||||
|
continue
|
||||||
|
closest_audio_source = audio_choices[0]
|
||||||
|
best_err = target_audio_bitrate - audio_choices[0]['audio_bitrate']
|
||||||
best_err = abs(best_err)
|
best_err = abs(best_err)
|
||||||
for audio_source in compat_audios[1:]:
|
for audio_source in audio_choices[1:]:
|
||||||
err = abs(audio_source['audio_bitrate'] - target_audio_bitrate)
|
err = abs(audio_source['audio_bitrate'] - target_audio_bitrate)
|
||||||
# once err gets worse we have passed the closest one
|
# once err gets worse we have passed the closest one
|
||||||
if err > best_err:
|
if err > best_err:
|
||||||
break
|
break
|
||||||
best_err = err
|
best_err = err
|
||||||
closest_audio_source = audio_source
|
closest_audio_source = audio_source
|
||||||
pair_sources.append((source, closest_audio_source))
|
pair_info['audios'].append(closest_audio_source)
|
||||||
|
|
||||||
|
if not pair_info['audios']:
|
||||||
|
continue
|
||||||
|
|
||||||
|
def video_rank(src):
|
||||||
|
''' Sort by settings preference. Use file size as tiebreaker '''
|
||||||
|
setting_name = 'codec_rank_' + codec_name(src['vcodec'])
|
||||||
|
return (settings.current_settings_dict[setting_name],
|
||||||
|
src['file_size'])
|
||||||
|
pair_info['videos'].sort(key=video_rank)
|
||||||
|
|
||||||
|
pair_sources.append(pair_info)
|
||||||
|
|
||||||
|
pair_sources.sort(key=lambda src: src['quality'])
|
||||||
|
|
||||||
uni_idx = 0 if uni_sources else None
|
uni_idx = 0 if uni_sources else None
|
||||||
for i, source in enumerate(uni_sources):
|
for i, source in enumerate(uni_sources):
|
||||||
@ -146,8 +153,8 @@ def get_video_sources(info, target_resolution):
|
|||||||
uni_idx = i
|
uni_idx = i
|
||||||
|
|
||||||
pair_idx = 0 if pair_sources else None
|
pair_idx = 0 if pair_sources else None
|
||||||
for i, source_pair in enumerate(pair_sources):
|
for i, pair_info in enumerate(pair_sources):
|
||||||
if source_pair[0]['quality'] > target_resolution:
|
if pair_info['quality'] > target_resolution:
|
||||||
break
|
break
|
||||||
pair_idx = i
|
pair_idx = i
|
||||||
|
|
||||||
@ -619,8 +626,7 @@ def get_watch_page(video_id=None):
|
|||||||
uni_idx, 'width',
|
uni_idx, 'width',
|
||||||
default=640)
|
default=640)
|
||||||
|
|
||||||
pair_quality = yt_data_extract.deep_get(pair_sources, pair_idx, 0,
|
pair_quality = yt_data_extract.deep_get(pair_sources, pair_idx, 'quality')
|
||||||
'quality')
|
|
||||||
uni_quality = yt_data_extract.deep_get(uni_sources, uni_idx, 'quality')
|
uni_quality = yt_data_extract.deep_get(uni_sources, uni_idx, 'quality')
|
||||||
pair_error = abs((pair_quality or 360) - target_resolution)
|
pair_error = abs((pair_quality or 360) - target_resolution)
|
||||||
uni_error = abs((uni_quality or 360) - target_resolution)
|
uni_error = abs((uni_quality or 360) - target_resolution)
|
||||||
@ -634,6 +640,16 @@ def get_watch_page(video_id=None):
|
|||||||
using_pair_sources = (
|
using_pair_sources = (
|
||||||
bool(pair_sources) and (not uni_sources or closer_to_target == 'pair')
|
bool(pair_sources) and (not uni_sources or closer_to_target == 'pair')
|
||||||
)
|
)
|
||||||
|
if using_pair_sources:
|
||||||
|
video_height = pair_sources[pair_idx]['height']
|
||||||
|
video_width = pair_sources[pair_idx]['width']
|
||||||
|
else:
|
||||||
|
video_height = yt_data_extract.deep_get(
|
||||||
|
uni_sources, uni_idx, 'height', default=360
|
||||||
|
)
|
||||||
|
video_width = yt_data_extract.deep_get(
|
||||||
|
uni_sources, uni_idx, 'width', default=640
|
||||||
|
)
|
||||||
|
|
||||||
# 1 second per pixel, or the actual video width
|
# 1 second per pixel, or the actual video width
|
||||||
theater_video_target_width = max(640, info['duration'] or 0, video_width)
|
theater_video_target_width = max(640, info['duration'] or 0, video_width)
|
||||||
|
@ -420,7 +420,7 @@ def _extract_formats(info, player_response):
|
|||||||
fmt['vcodec'] = None
|
fmt['vcodec'] = None
|
||||||
fmt['width'] = yt_fmt.get('width')
|
fmt['width'] = yt_fmt.get('width')
|
||||||
fmt['height'] = yt_fmt.get('height')
|
fmt['height'] = yt_fmt.get('height')
|
||||||
fmt['file_size'] = yt_fmt.get('contentLength')
|
fmt['file_size'] = extract_int(yt_fmt.get('contentLength'))
|
||||||
fmt['audio_sample_rate'] = extract_int(yt_fmt.get('audioSampleRate'))
|
fmt['audio_sample_rate'] = extract_int(yt_fmt.get('audioSampleRate'))
|
||||||
fmt['duration_ms'] = yt_fmt.get('approxDurationMs')
|
fmt['duration_ms'] = yt_fmt.get('approxDurationMs')
|
||||||
fmt['fps'] = yt_fmt.get('fps')
|
fmt['fps'] = yt_fmt.get('fps')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user