'ts': 'mpegts',
'wma': 'asf',
'wmv': 'asf',
+ 'weba': 'webm',
'vtt': 'webvtt',
}
ACODECS = {
None)
return num, len(streams)
+ def _fixup_chapters(self, info):
+ last_chapter = traverse_obj(info, ('chapters', -1))
+ if last_chapter and not last_chapter.get('end_time'):
+ last_chapter['end_time'] = self._get_real_video_duration(info['filepath'])
+
def _get_real_video_duration(self, filepath, fatal=True):
try:
duration = float_or_none(
if acodec != 'copy':
more_opts = self._quality_args(acodec)
- # not os.path.splitext, since the latter does not work on unicode in all setups
- temp_path = new_path = f'{path.rpartition(".")[0]}.{extension}'
+ temp_path = new_path = replace_extension(path, extension, information['ext'])
if new_path == path:
if acodec == 'copy':
@PostProcessor._restrict_to(images=False)
def run(self, info):
+ self._fixup_chapters(info)
filename, metadata_filename = info['filepath'], None
files_to_delete, options = [], []
if self._add_chapters and info.get('chapters'):
def add(meta_list, info_list=None):
value = next((
- str(info[key]) for key in [f'{meta_prefix}_'] + list(variadic(info_list or meta_list))
+ info[key] for key in [f'{meta_prefix}_'] + list(variadic(info_list or meta_list))
if info.get(key) is not None), None)
if value not in ('', None):
+ value = ', '.join(map(str, variadic(value)))
value = value.replace('\0', '') # nul character cannot be passed in command line
metadata['common'].update({meta_f: value for meta_f in variadic(meta_list)})
add(('description', 'synopsis'), 'description')
add(('purl', 'comment'), 'webpage_url')
add('track', 'track_number')
- add('artist', ('artist', 'creator', 'uploader', 'uploader_id'))
- add('genre')
+ add('artist', ('artist', 'artists', 'creator', 'creators', 'uploader', 'uploader_id'))
+ add('composer', ('composer', 'composers'))
+ add('genre', ('genre', 'genres'))
add('album')
- add('album_artist')
+ add('album_artist', ('album_artist', 'album_artists'))
add('disc', 'disc_number')
add('show', 'series')
add('season_number')
yield ('-metadata', f'{name}={value}')
stream_idx = 0
- for fmt in info.get('requested_formats') or []:
+ for fmt in info.get('requested_formats') or [info]:
stream_count = 2 if 'none' not in (fmt.get('vcodec'), fmt.get('acodec')) else 1
lang = ISO639Utils.short2long(fmt.get('language') or '') or fmt.get('language')
for i in range(stream_idx, stream_idx + stream_count):
new_stream -= 1
yield (
- '-attach', infofn,
+ '-attach', self._ffmpeg_filename_argument(infofn),
f'-metadata:s:{new_stream}', 'mimetype=application/json',
f'-metadata:s:{new_stream}', 'filename=info.json',
)
@PostProcessor._restrict_to(images=False)
def run(self, info):
if all(self._needs_fixup(info)):
+ args = ['-f', 'mp4']
+ if self.get_audio_codec(info['filepath']) == 'aac':
+ args.extend(['-bsf:a', 'aac_adtstoasc'])
self._fixup('Fixing MPEG-TS in MP4 container', info['filepath'], [
- *self.stream_copy_opts(), '-f', 'mp4', '-bsf:a', 'aac_adtstoasc'])
+ *self.stream_copy_opts(), *args])
return [], info
@PostProcessor._restrict_to(images=False)
def run(self, info):
+ self._fixup_chapters(info)
chapters = info.get('chapters') or []
if not chapters:
self.to_screen('Chapter information is unavailable')