This commit is contained in:
Tom-Oliver Heidel 2020-09-14 13:04:24 +02:00
commit acdb1a4ec6
3 changed files with 77 additions and 53 deletions

View File

@ -1217,11 +1217,13 @@ def _parse_format_selection(tokens, inside_merge=False, inside_choice=False, ins
group = _parse_format_selection(tokens, inside_group=True) group = _parse_format_selection(tokens, inside_group=True)
current_selector = FormatSelector(GROUP, group, []) current_selector = FormatSelector(GROUP, group, [])
elif string == '+': elif string == '+':
video_selector = current_selector if not current_selector:
audio_selector = _parse_format_selection(tokens, inside_merge=True) raise syntax_error('Unexpected "+"', start)
if not video_selector or not audio_selector: selector_1 = current_selector
raise syntax_error('"+" must be between two format selectors', start) selector_2 = _parse_format_selection(tokens, inside_merge=True)
current_selector = FormatSelector(MERGE, (video_selector, audio_selector), []) if not selector_2:
raise syntax_error('Expected a selector', start)
current_selector = FormatSelector(MERGE, (selector_1, selector_2), [])
else: else:
raise syntax_error('Operator not recognized: "{0}"'.format(string), start) raise syntax_error('Operator not recognized: "{0}"'.format(string), start)
elif type == tokenize.ENDMARKER: elif type == tokenize.ENDMARKER:
@ -1306,47 +1308,59 @@ def selector_function(ctx):
if matches: if matches:
yield matches[-1] yield matches[-1]
elif selector.type == MERGE: elif selector.type == MERGE:
def _merge(formats_info): def _merge(formats_pair):
format_1, format_2 = [f['format_id'] for f in formats_info] format_1, format_2 = formats_pair
# The first format must contain the video and the
# second the audio formats_info = []
if formats_info[0].get('vcodec') == 'none': formats_info.extend(format_1.get('requested_formats', (format_1,)))
self.report_error('The first format must ' formats_info.extend(format_2.get('requested_formats', (format_2,)))
'contain the video, try using '
'"-f %s+%s"' % (format_2, format_1)) video_fmts = [fmt_info for fmt_info in formats_info if fmt_info.get('vcodec') != 'none']
return audio_fmts = [fmt_info for fmt_info in formats_info if fmt_info.get('acodec') != 'none']
# Formats must be opposite (video+audio)
if formats_info[0].get('acodec') == 'none' and formats_info[1].get('acodec') == 'none': the_only_video = video_fmts[0] if len(video_fmts) == 1 else None
self.report_error( the_only_audio = audio_fmts[0] if len(audio_fmts) == 1 else None
'Both formats %s and %s are video-only, you must specify "-f video+audio"'
% (format_1, format_2)) output_ext = self.params.get('merge_output_format')
return if not output_ext:
output_ext = ( if the_only_video:
formats_info[0]['ext'] output_ext = the_only_video['ext']
if self.params.get('merge_output_format') is None elif the_only_audio and not video_fmts:
else self.params['merge_output_format']) output_ext = the_only_audio['ext']
return { else:
output_ext = 'mkv'
new_dict = {
'requested_formats': formats_info, 'requested_formats': formats_info,
'format': '%s+%s' % (formats_info[0].get('format'), 'format': '+'.join(fmt_info.get('format') for fmt_info in formats_info),
formats_info[1].get('format')), 'format_id': '+'.join(fmt_info.get('format_id') for fmt_info in formats_info),
'format_id': '%s+%s' % (formats_info[0].get('format_id'),
formats_info[1].get('format_id')),
'width': formats_info[0].get('width'),
'height': formats_info[0].get('height'),
'resolution': formats_info[0].get('resolution'),
'fps': formats_info[0].get('fps'),
'vcodec': formats_info[0].get('vcodec'),
'vbr': formats_info[0].get('vbr'),
'stretched_ratio': formats_info[0].get('stretched_ratio'),
'acodec': formats_info[1].get('acodec'),
'abr': formats_info[1].get('abr'),
'ext': output_ext, 'ext': output_ext,
} }
video_selector, audio_selector = map(_build_selector_function, selector.selector)
if the_only_video:
new_dict.update({
'width': the_only_video.get('width'),
'height': the_only_video.get('height'),
'resolution': the_only_video.get('resolution'),
'fps': the_only_video.get('fps'),
'vcodec': the_only_video.get('vcodec'),
'vbr': the_only_video.get('vbr'),
'stretched_ratio': the_only_video.get('stretched_ratio'),
})
if the_only_audio:
new_dict.update({
'acodec': the_only_audio.get('acodec'),
'abr': the_only_audio.get('abr'),
})
return new_dict
selector_1, selector_2 = map(_build_selector_function, selector.selector)
def selector_function(ctx): def selector_function(ctx):
for pair in itertools.product( for pair in itertools.product(
video_selector(copy.deepcopy(ctx)), audio_selector(copy.deepcopy(ctx))): selector_1(copy.deepcopy(ctx)), selector_2(copy.deepcopy(ctx))):
yield _merge(pair) yield _merge(pair)
filters = [self._build_format_filter(f) for f in selector.filters] filters = [self._build_format_filter(f) for f in selector.filters]
@ -1899,17 +1913,21 @@ def dl(name, info):
postprocessors = [merger] postprocessors = [merger]
def compatible_formats(formats): def compatible_formats(formats):
video, audio = formats # TODO: some formats actually allow this (mkv, webm, ogg, mp4), but not all of them.
video_formats = [format for format in formats if format.get('vcodec') != 'none']
audio_formats = [format for format in formats if format.get('acodec') != 'none']
if len(video_formats) > 2 or len(audio_formats) > 2:
return False
# Check extension # Check extension
video_ext, audio_ext = video.get('ext'), audio.get('ext') exts = set(format.get('ext') for format in formats)
if video_ext and audio_ext: COMPATIBLE_EXTS = (
COMPATIBLE_EXTS = ( set(('mp3', 'mp4', 'm4a', 'm4p', 'm4b', 'm4r', 'm4v', 'ismv', 'isma')),
('mp3', 'mp4', 'm4a', 'm4p', 'm4b', 'm4r', 'm4v', 'ismv', 'isma'), set(('webm',)),
('webm') )
) for ext_sets in COMPATIBLE_EXTS:
for exts in COMPATIBLE_EXTS: if ext_sets.issuperset(exts):
if video_ext in exts and audio_ext in exts: return True
return True
# TODO: Check acodec/vcodec # TODO: Check acodec/vcodec
return False return False
@ -2088,7 +2106,7 @@ def post_process(self, filename, ie_info):
except PostProcessingError as e: except PostProcessingError as e:
self.report_error(e.msg) self.report_error(e.msg)
if files_to_delete and not self.params.get('keepvideo', False): if files_to_delete and not self.params.get('keepvideo', False):
for old_filename in files_to_delete: for old_filename in set(files_to_delete):
self.to_screen('Deleting original file %s (pass -k to keep)' % old_filename) self.to_screen('Deleting original file %s (pass -k to keep)' % old_filename)
try: try:
os.remove(encodeFilename(old_filename)) os.remove(encodeFilename(old_filename))

View File

@ -93,6 +93,7 @@ def _parse_flv(self, metadata):
'quality': -2, 'quality': -2,
'preference': -2, 'preference': -2,
'format_id': 'slides', 'format_id': 'slides',
'acodec': 'none',
}) })
speaker_video_path = xpath_text(metadata, './speakerVideo', fatal=True) speaker_video_path = xpath_text(metadata, './speakerVideo', fatal=True)
formats.append({ formats.append({

View File

@ -476,7 +476,7 @@ def add(meta_list, info_list=None):
filename = info['filepath'] filename = info['filepath']
temp_filename = prepend_extension(filename, 'temp') temp_filename = prepend_extension(filename, 'temp')
in_filenames = [filename] in_filenames = [filename]
options = [] options = ['-map', '0']
if info['ext'] == 'm4a': if info['ext'] == 'm4a':
options.extend(['-vn', '-acodec', 'copy']) options.extend(['-vn', '-acodec', 'copy'])
@ -518,7 +518,12 @@ class FFmpegMergerPP(FFmpegPostProcessor):
def run(self, info): def run(self, info):
filename = info['filepath'] filename = info['filepath']
temp_filename = prepend_extension(filename, 'temp') temp_filename = prepend_extension(filename, 'temp')
args = ['-c', 'copy', '-map', '0:v:0', '-map', '1:a:0'] args = ['-c', 'copy']
for (i, fmt) in enumerate(info['requested_formats']):
if fmt.get('acodec') != 'none':
args.extend(['-map', '%u:a:0' % (i)])
if fmt.get('vcodec') != 'none':
args.extend(['-map', '%u:v:0' % (i)])
self._downloader.to_screen('[ffmpeg] Merging formats into "%s"' % filename) self._downloader.to_screen('[ffmpeg] Merging formats into "%s"' % filename)
self.run_ffmpeg_multiple_files(info['__files_to_merge'], temp_filename, args) self.run_ffmpeg_multiple_files(info['__files_to_merge'], temp_filename, args)
os.rename(encodeFilename(temp_filename), encodeFilename(filename)) os.rename(encodeFilename(temp_filename), encodeFilename(filename))