import re
import sys
-
from .options import (
parseOpts,
)
if opts.list_extractors:
for ie in list_extractors(opts.age_limit):
- write_string(ie.IE_NAME + (' (CURRENTLY BROKEN)' if not ie._WORKING else '') + '\n', out=sys.stdout)
+ write_string(ie.IE_NAME + (' (CURRENTLY BROKEN)' if not ie.working() else '') + '\n', out=sys.stdout)
matchedUrls = [url for url in all_urls if ie.suitable(url)]
for mu in matchedUrls:
write_string(' ' + mu + '\n', out=sys.stdout)
sys.exit(0)
if opts.list_extractor_descriptions:
for ie in list_extractors(opts.age_limit):
- if not ie._WORKING:
+ if not ie.working():
continue
desc = getattr(ie, 'IE_DESC', ie.IE_NAME)
if desc is False:
else:
date = DateRange(opts.dateafter, opts.datebefore)
- def parse_compat_opts():
- parsed_compat_opts, compat_opts = set(), opts.compat_opts[::-1]
- while compat_opts:
- actual_opt = opt = compat_opts.pop().lower()
- if opt == 'youtube-dl':
- compat_opts.extend(['-multistreams', 'all'])
- elif opt == 'youtube-dlc':
- compat_opts.extend(['-no-youtube-channel-redirect', '-no-live-chat', 'all'])
- elif opt == 'all':
- parsed_compat_opts.update(all_compat_opts)
- elif opt == '-all':
- parsed_compat_opts = set()
- else:
- if opt[0] == '-':
- opt = opt[1:]
- parsed_compat_opts.discard(opt)
- else:
- parsed_compat_opts.update([opt])
- if opt not in all_compat_opts:
- parser.error('Invalid compatibility option %s' % actual_opt)
- return parsed_compat_opts
-
- all_compat_opts = [
- 'filename', 'format-sort', 'abort-on-error', 'format-spec', 'no-playlist-metafiles',
- 'multistreams', 'no-live-chat', 'playlist-index', 'list-formats', 'no-direct-merge',
- 'no-youtube-channel-redirect', 'no-youtube-unavailable-videos', 'no-attach-info-json',
- 'embed-thumbnail-atomicparsley', 'seperate-video-versions', 'no-clean-infojson', 'no-keep-subs',
- ]
- compat_opts = parse_compat_opts()
+ compat_opts = opts.compat_opts
def _unused_compat_opt(name):
if name not in compat_opts:
for k, tmpl in opts.outtmpl.items():
validate_outtmpl(tmpl, '%s output template' % k)
- for tmpl in opts.forceprint:
+ opts.forceprint = opts.forceprint or []
+ for tmpl in opts.forceprint or []:
validate_outtmpl(tmpl, 'print template')
+ validate_outtmpl(opts.sponsorblock_chapter_title, 'SponsorBlock chapter title')
if opts.extractaudio and not opts.keepvideo and opts.format is None:
opts.format = 'bestaudio/best'
if opts.getcomments and not printing_json:
opts.writeinfojson = True
+ if opts.no_sponsorblock:
+ opts.sponsorblock_mark = set()
+ opts.sponsorblock_remove = set()
+ sponsorblock_query = opts.sponsorblock_mark | opts.sponsorblock_remove
+
+ if (opts.addmetadata or opts.sponsorblock_mark) and opts.addchapters is None:
+ opts.addchapters = True
+ opts.remove_chapters = opts.remove_chapters or []
+
def report_conflict(arg1, arg2):
warnings.append('%s is ignored since %s was given' % (arg2, arg1))
- if opts.remuxvideo and opts.recodevideo:
- report_conflict('--recode-video', '--remux-video')
- opts.remuxvideo = False
+ if (opts.remove_chapters or sponsorblock_query) and opts.sponskrub is not False:
+ if opts.sponskrub:
+ if opts.remove_chapters:
+ report_conflict('--remove-chapters', '--sponskrub')
+ if opts.sponsorblock_mark:
+ report_conflict('--sponsorblock-mark', '--sponskrub')
+ if opts.sponsorblock_remove:
+ report_conflict('--sponsorblock-remove', '--sponskrub')
+ opts.sponskrub = False
if opts.sponskrub_cut and opts.split_chapters and opts.sponskrub is not False:
report_conflict('--split-chapter', '--sponskrub-cut')
opts.sponskrub_cut = False
+ if opts.remuxvideo and opts.recodevideo:
+ report_conflict('--recode-video', '--remux-video')
+ opts.remuxvideo = False
+
if opts.allow_unplayable_formats:
if opts.extractaudio:
report_conflict('--allow-unplayable-formats', '--extract-audio')
if opts.fixup and opts.fixup.lower() not in ('never', 'ignore'):
report_conflict('--allow-unplayable-formats', '--fixup')
opts.fixup = 'never'
+ if opts.remove_chapters:
+ report_conflict('--allow-unplayable-formats', '--remove-chapters')
+ opts.remove_chapters = []
+ if opts.sponsorblock_remove:
+ report_conflict('--allow-unplayable-formats', '--sponsorblock-remove')
+ opts.sponsorblock_remove = set()
if opts.sponskrub:
report_conflict('--allow-unplayable-formats', '--sponskrub')
opts.sponskrub = False
# PostProcessors
postprocessors = []
+ if sponsorblock_query:
+ postprocessors.append({
+ 'key': 'SponsorBlock',
+ 'categories': sponsorblock_query,
+ 'api': opts.sponsorblock_api,
+ # Run this immediately after extraction is complete
+ 'when': 'pre_process'
+ })
if opts.parse_metadata:
postprocessors.append({
'key': 'MetadataParser',
# Must be after all other before_dl
if opts.exec_before_dl_cmd:
postprocessors.append({
- 'key': 'ExecAfterDownload',
+ 'key': 'Exec',
'exec_cmd': opts.exec_before_dl_cmd,
'when': 'before_dl'
})
'key': 'FFmpegVideoConvertor',
'preferedformat': opts.recodevideo,
})
- # FFmpegMetadataPP should be run after FFmpegVideoConvertorPP and
- # FFmpegExtractAudioPP as containers before conversion may not support
- # metadata (3gp, webm, etc.)
- # And this post-processor should be placed before other metadata
- # manipulating post-processors (FFmpegEmbedSubtitle) to prevent loss of
- # extra metadata. By default ffmpeg preserves metadata applicable for both
- # source and target containers. From this point the container won't change,
- # so metadata can be added here.
- if opts.addmetadata:
- postprocessors.append({'key': 'FFmpegMetadata'})
+ # If ModifyChapters is going to remove chapters, subtitles must already be in the container.
if opts.embedsubtitles:
already_have_subtitle = opts.writesubtitles and 'no-keep-subs' not in compat_opts
postprocessors.append({
# this was the old behaviour if only --all-sub was given.
if opts.allsubtitles and not opts.writeautomaticsub:
opts.writesubtitles = True
+ # ModifyChapters must run before FFmpegMetadataPP
+ remove_chapters_patterns = []
+ for regex in opts.remove_chapters:
+ try:
+ remove_chapters_patterns.append(re.compile(regex))
+ except re.error as err:
+ parser.error(f'invalid --remove-chapters regex {regex!r} - {err}')
+ if opts.remove_chapters or sponsorblock_query:
+ postprocessors.append({
+ 'key': 'ModifyChapters',
+ 'remove_chapters_patterns': remove_chapters_patterns,
+ 'remove_sponsor_segments': opts.sponsorblock_remove,
+ 'sponsorblock_chapter_title': opts.sponsorblock_chapter_title,
+ 'force_keyframes': opts.force_keyframes_at_cuts
+ })
+ # FFmpegMetadataPP should be run after FFmpegVideoConvertorPP and
+ # FFmpegExtractAudioPP as containers before conversion may not support
+ # metadata (3gp, webm, etc.)
+ # By default ffmpeg preserves metadata applicable for both
+ # source and target containers. From this point the container won't change,
+ # so metadata can be added here.
+ if opts.addmetadata or opts.addchapters:
+ postprocessors.append({
+ 'key': 'FFmpegMetadata',
+ 'add_chapters': opts.addchapters,
+ 'add_metadata': opts.addmetadata,
+ })
# This should be above EmbedThumbnail since sponskrub removes the thumbnail attachment
# but must be below EmbedSubtitle and FFmpegMetadata
# See https://github.com/yt-dlp/yt-dlp/issues/204 , https://github.com/faissaloo/SponSkrub/issues/29
if not already_have_thumbnail:
opts.writethumbnail = True
if opts.split_chapters:
- postprocessors.append({'key': 'FFmpegSplitChapters'})
+ postprocessors.append({
+ 'key': 'FFmpegSplitChapters',
+ 'force_keyframes': opts.force_keyframes_at_cuts,
+ })
# XAttrMetadataPP should be run after post-processors that may change file contents
if opts.xattrs:
postprocessors.append({'key': 'XAttrMetadata'})
- # ExecAfterDownload must be the last PP
+ # Exec must be the last PP
if opts.exec_cmd:
postprocessors.append({
- 'key': 'ExecAfterDownload',
+ 'key': 'Exec',
'exec_cmd': opts.exec_cmd,
# Run this only after the files have been moved to their final locations
'when': 'after_move'