4 f
'You are using an unsupported version of Python. Only Python versions 3.6 and above are supported by yt-dlp' # noqa: F541
6 __license__
= 'Public Domain'
16 from .options
import (
22 workaround_optparse_bug9161
,
24 from .cookies
import SUPPORTED_BROWSERS
46 from .update
import run_update
47 from .downloader
import (
50 from .extractor
import gen_extractors
, list_extractors
51 from .extractor
.common
import InfoExtractor
52 from .extractor
.adobepass
import MSO_INFO
53 from .postprocessor
import (
55 FFmpegSubtitlesConvertorPP
,
56 FFmpegThumbnailsConvertorPP
,
57 FFmpegVideoConvertorPP
,
62 from .YoutubeDL
import YoutubeDL
65 def _real_main(argv
=None):
66 # Compatibility fixes for Windows
67 if sys
.platform
== 'win32':
68 # https://github.com/ytdl-org/youtube-dl/issues/820
69 codecs
.register(lambda name
: codecs
.lookup('utf-8') if name
== 'cp65001' else None)
71 workaround_optparse_bug9161()
73 setproctitle('yt-dlp')
75 parser
, opts
, args
= parseOpts(argv
)
79 if opts
.user_agent
is not None:
80 std_headers
['User-Agent'] = opts
.user_agent
83 if opts
.referer
is not None:
84 std_headers
['Referer'] = opts
.referer
87 std_headers
.update(opts
.headers
)
90 if opts
.dump_user_agent
:
91 write_string(std_headers
['User-Agent'] + '\n', out
=sys
.stdout
)
94 # Batch file verification
96 if opts
.batchfile
is not None:
98 if opts
.batchfile
== '-':
102 expand_path(opts
.batchfile
),
103 'r', encoding
='utf-8', errors
='ignore')
104 batch_urls
= read_batch_urls(batchfd
)
106 write_string('[debug] Batch file urls: ' + repr(batch_urls
) + '\n')
108 sys
.exit('ERROR: batch file %s could not be read' % opts
.batchfile
)
109 all_urls
= batch_urls
+ [url
.strip() for url
in args
] # batch_urls are already striped in read_batch_urls
110 _enc
= preferredencoding()
111 all_urls
= [url
.decode(_enc
, 'ignore') if isinstance(url
, bytes) else url
for url
in all_urls
]
113 if opts
.list_extractors
:
114 for ie
in list_extractors(opts
.age_limit
):
115 write_string(ie
.IE_NAME
+ (' (CURRENTLY BROKEN)' if not ie
.working() else '') + '\n', out
=sys
.stdout
)
116 matchedUrls
= [url
for url
in all_urls
if ie
.suitable(url
)]
117 for mu
in matchedUrls
:
118 write_string(' ' + mu
+ '\n', out
=sys
.stdout
)
120 if opts
.list_extractor_descriptions
:
121 for ie
in list_extractors(opts
.age_limit
):
124 desc
= getattr(ie
, 'IE_DESC', ie
.IE_NAME
)
127 if getattr(ie
, 'SEARCH_KEY', None) is not None:
128 _SEARCHES
= ('cute kittens', 'slithering pythons', 'falling cat', 'angry poodle', 'purple fish', 'running tortoise', 'sleeping bunny', 'burping cow')
129 _COUNTS
= ('', '5', '10', 'all')
130 desc
+= f
'; "{ie.SEARCH_KEY}:" prefix (Example: "{ie.SEARCH_KEY}{random.choice(_COUNTS)}:{random.choice(_SEARCHES)}")'
131 write_string(desc
+ '\n', out
=sys
.stdout
)
134 table
= [[mso_id
, mso_info
['name']] for mso_id
, mso_info
in MSO_INFO
.items()]
135 write_string('Supported TV Providers:\n' + render_table(['mso', 'mso name'], table
) + '\n', out
=sys
.stdout
)
138 # Conflicting, missing and erroneous options
139 if opts
.usenetrc
and (opts
.username
is not None or opts
.password
is not None):
140 parser
.error('using .netrc conflicts with giving username/password')
141 if opts
.password
is not None and opts
.username
is None:
142 parser
.error('account username missing\n')
143 if opts
.ap_password
is not None and opts
.ap_username
is None:
144 parser
.error('TV Provider account username missing\n')
145 if opts
.autonumber_size
is not None:
146 if opts
.autonumber_size
<= 0:
147 parser
.error('auto number size must be positive')
148 if opts
.autonumber_start
is not None:
149 if opts
.autonumber_start
< 0:
150 parser
.error('auto number start must be positive or 0')
151 if opts
.username
is not None and opts
.password
is None:
152 opts
.password
= compat_getpass('Type account password and press [Return]: ')
153 if opts
.ap_username
is not None and opts
.ap_password
is None:
154 opts
.ap_password
= compat_getpass('Type TV provider account password and press [Return]: ')
155 if opts
.ratelimit
is not None:
156 numeric_limit
= FileDownloader
.parse_bytes(opts
.ratelimit
)
157 if numeric_limit
is None:
158 parser
.error('invalid rate limit specified')
159 opts
.ratelimit
= numeric_limit
160 if opts
.throttledratelimit
is not None:
161 numeric_limit
= FileDownloader
.parse_bytes(opts
.throttledratelimit
)
162 if numeric_limit
is None:
163 parser
.error('invalid rate limit specified')
164 opts
.throttledratelimit
= numeric_limit
165 if opts
.min_filesize
is not None:
166 numeric_limit
= FileDownloader
.parse_bytes(opts
.min_filesize
)
167 if numeric_limit
is None:
168 parser
.error('invalid min_filesize specified')
169 opts
.min_filesize
= numeric_limit
170 if opts
.max_filesize
is not None:
171 numeric_limit
= FileDownloader
.parse_bytes(opts
.max_filesize
)
172 if numeric_limit
is None:
173 parser
.error('invalid max_filesize specified')
174 opts
.max_filesize
= numeric_limit
175 if opts
.sleep_interval
is not None:
176 if opts
.sleep_interval
< 0:
177 parser
.error('sleep interval must be positive or 0')
178 if opts
.max_sleep_interval
is not None:
179 if opts
.max_sleep_interval
< 0:
180 parser
.error('max sleep interval must be positive or 0')
181 if opts
.sleep_interval
is None:
182 parser
.error('min sleep interval must be specified, use --min-sleep-interval')
183 if opts
.max_sleep_interval
< opts
.sleep_interval
:
184 parser
.error('max sleep interval must be greater than or equal to min sleep interval')
186 opts
.max_sleep_interval
= opts
.sleep_interval
187 if opts
.sleep_interval_subtitles
is not None:
188 if opts
.sleep_interval_subtitles
< 0:
189 parser
.error('subtitles sleep interval must be positive or 0')
190 if opts
.sleep_interval_requests
is not None:
191 if opts
.sleep_interval_requests
< 0:
192 parser
.error('requests sleep interval must be positive or 0')
193 if opts
.ap_mso
and opts
.ap_mso
not in MSO_INFO
:
194 parser
.error('Unsupported TV Provider, use --ap-list-mso to get a list of supported TV Providers')
195 if opts
.overwrites
: # --yes-overwrites implies --no-continue
196 opts
.continue_dl
= False
197 if opts
.concurrent_fragment_downloads
<= 0:
198 raise ValueError('Concurrent fragments must be positive')
200 def parse_retries(retries
, name
=''):
201 if retries
in ('inf', 'infinite'):
202 parsed_retries
= float('inf')
205 parsed_retries
= int(retries
)
206 except (TypeError, ValueError):
207 parser
.error('invalid %sretry count specified' % name
)
208 return parsed_retries
209 if opts
.retries
is not None:
210 opts
.retries
= parse_retries(opts
.retries
)
211 if opts
.fragment_retries
is not None:
212 opts
.fragment_retries
= parse_retries(opts
.fragment_retries
, 'fragment ')
213 if opts
.extractor_retries
is not None:
214 opts
.extractor_retries
= parse_retries(opts
.extractor_retries
, 'extractor ')
215 if opts
.buffersize
is not None:
216 numeric_buffersize
= FileDownloader
.parse_bytes(opts
.buffersize
)
217 if numeric_buffersize
is None:
218 parser
.error('invalid buffer size specified')
219 opts
.buffersize
= numeric_buffersize
220 if opts
.http_chunk_size
is not None:
221 numeric_chunksize
= FileDownloader
.parse_bytes(opts
.http_chunk_size
)
222 if not numeric_chunksize
:
223 parser
.error('invalid http chunk size specified')
224 opts
.http_chunk_size
= numeric_chunksize
225 if opts
.playliststart
<= 0:
226 raise ValueError('Playlist start must be positive')
227 if opts
.playlistend
not in (-1, None) and opts
.playlistend
< opts
.playliststart
:
228 raise ValueError('Playlist end must be greater than playlist start')
229 if opts
.extractaudio
:
230 opts
.audioformat
= opts
.audioformat
.lower()
231 if opts
.audioformat
not in ['best'] + list(FFmpegExtractAudioPP
.SUPPORTED_EXTS
):
232 parser
.error('invalid audio format specified')
233 if opts
.audioquality
:
234 opts
.audioquality
= opts
.audioquality
.strip('k').strip('K')
235 audioquality
= int_or_none(float_or_none(opts
.audioquality
)) # int_or_none prevents inf, nan
236 if audioquality
is None or audioquality
< 0:
237 parser
.error('invalid audio quality specified')
238 if opts
.recodevideo
is not None:
239 opts
.recodevideo
= opts
.recodevideo
.replace(' ', '')
240 if not re
.match(FFmpegVideoConvertorPP
.FORMAT_RE
, opts
.recodevideo
):
241 parser
.error('invalid video remux format specified')
242 if opts
.remuxvideo
is not None:
243 opts
.remuxvideo
= opts
.remuxvideo
.replace(' ', '')
244 if not re
.match(FFmpegVideoRemuxerPP
.FORMAT_RE
, opts
.remuxvideo
):
245 parser
.error('invalid video remux format specified')
246 if opts
.convertsubtitles
is not None:
247 if opts
.convertsubtitles
not in FFmpegSubtitlesConvertorPP
.SUPPORTED_EXTS
:
248 parser
.error('invalid subtitle format specified')
249 if opts
.convertthumbnails
is not None:
250 if opts
.convertthumbnails
not in FFmpegThumbnailsConvertorPP
.SUPPORTED_EXTS
:
251 parser
.error('invalid thumbnail format specified')
253 if opts
.cookiesfrombrowser
is not None:
254 opts
.cookiesfrombrowser
= [
255 part
.strip() or None for part
in opts
.cookiesfrombrowser
.split(':', 1)]
256 if opts
.cookiesfrombrowser
[0].lower() not in SUPPORTED_BROWSERS
:
257 parser
.error('unsupported browser specified for cookies')
259 if opts
.date
is not None:
260 date
= DateRange
.day(opts
.date
)
262 date
= DateRange(opts
.dateafter
, opts
.datebefore
)
264 compat_opts
= opts
.compat_opts
266 def report_conflict(arg1
, arg2
):
267 warnings
.append(f
'{arg2} is ignored since {arg1} was given')
269 def _unused_compat_opt(name
):
270 if name
not in compat_opts
:
272 compat_opts
.discard(name
)
273 compat_opts
.update(['*%s' % name
])
276 def set_default_compat(compat_name
, opt_name
, default
=True, remove_compat
=True):
277 attr
= getattr(opts
, opt_name
)
278 if compat_name
in compat_opts
:
280 setattr(opts
, opt_name
, not default
)
284 _unused_compat_opt(compat_name
)
287 setattr(opts
, opt_name
, default
)
290 set_default_compat('abort-on-error', 'ignoreerrors', 'only_download')
291 set_default_compat('no-playlist-metafiles', 'allow_playlist_files')
292 set_default_compat('no-clean-infojson', 'clean_infojson')
293 if 'format-sort' in compat_opts
:
294 opts
.format_sort
.extend(InfoExtractor
.FormatSort
.ytdl_default
)
295 _video_multistreams_set
= set_default_compat('multistreams', 'allow_multiple_video_streams', False, remove_compat
=False)
296 _audio_multistreams_set
= set_default_compat('multistreams', 'allow_multiple_audio_streams', False, remove_compat
=False)
297 if _video_multistreams_set
is False and _audio_multistreams_set
is False:
298 _unused_compat_opt('multistreams')
299 outtmpl_default
= opts
.outtmpl
.get('default')
301 if outtmpl_default
is None:
302 outtmpl_default
= opts
.outtmpl
['default'] = '%(id)s.%(ext)s'
304 report_conflict('--output', '--id')
305 if 'filename' in compat_opts
:
306 if outtmpl_default
is None:
307 outtmpl_default
= opts
.outtmpl
['default'] = '%(title)s-%(id)s.%(ext)s'
309 _unused_compat_opt('filename')
311 def validate_outtmpl(tmpl
, msg
):
312 err
= YoutubeDL
.validate_outtmpl(tmpl
)
314 parser
.error('invalid %s %r: %s' % (msg
, tmpl
, error_to_compat_str(err
)))
316 for k
, tmpl
in opts
.outtmpl
.items():
317 validate_outtmpl(tmpl
, f
'{k} output template')
318 opts
.forceprint
= opts
.forceprint
or []
319 for tmpl
in opts
.forceprint
or []:
320 validate_outtmpl(tmpl
, 'print template')
321 validate_outtmpl(opts
.sponsorblock_chapter_title
, 'SponsorBlock chapter title')
322 for k
, tmpl
in opts
.progress_template
.items():
323 k
= f
'{k[:-6]} console title' if '-title' in k
else f
'{k} progress'
324 validate_outtmpl(tmpl
, f
'{k} template')
326 if opts
.extractaudio
and not opts
.keepvideo
and opts
.format
is None:
327 opts
.format
= 'bestaudio/best'
329 if outtmpl_default
is not None and not os
.path
.splitext(outtmpl_default
)[1] and opts
.extractaudio
:
330 parser
.error('Cannot download a video and extract audio into the same'
331 ' file! Use "{0}.%(ext)s" instead of "{0}" as the output'
332 ' template'.format(outtmpl_default
))
334 for f
in opts
.format_sort
:
335 if re
.match(InfoExtractor
.FormatSort
.regex
, f
) is None:
336 parser
.error('invalid format sort string "%s" specified' % f
)
338 def metadataparser_actions(f
):
339 if isinstance(f
, str):
340 cmd
= '--parse-metadata %s' % compat_shlex_quote(f
)
342 actions
= [MetadataFromFieldPP
.to_action(f
)]
343 except Exception as err
:
344 parser
.error(f
'{cmd} is invalid; {err}')
346 cmd
= '--replace-in-metadata %s' % ' '.join(map(compat_shlex_quote
, f
))
347 actions
= ((MetadataParserPP
.Actions
.REPLACE
, x
, *f
[1:]) for x
in f
[0].split(','))
349 for action
in actions
:
351 MetadataParserPP
.validate_action(*action
)
352 except Exception as err
:
353 parser
.error(f
'{cmd} is invalid; {err}')
356 if opts
.parse_metadata
is None:
357 opts
.parse_metadata
= []
358 if opts
.metafromtitle
is not None:
359 opts
.parse_metadata
.append('title:%s' % opts
.metafromtitle
)
360 opts
.parse_metadata
= list(itertools
.chain(*map(metadataparser_actions
, opts
.parse_metadata
)))
362 any_getting
= opts
.forceprint
or opts
.geturl
or opts
.gettitle
or opts
.getid
or opts
.getthumbnail
or opts
.getdescription
or opts
.getfilename
or opts
.getformat
or opts
.getduration
or opts
.dumpjson
or opts
.dump_single_json
363 any_printing
= opts
.print_json
364 download_archive_fn
= expand_path(opts
.download_archive
) if opts
.download_archive
is not None else opts
.download_archive
366 # If JSON is not printed anywhere, but comments are requested, save it to file
367 printing_json
= opts
.dumpjson
or opts
.print_json
or opts
.dump_single_json
368 if opts
.getcomments
and not printing_json
:
369 opts
.writeinfojson
= True
371 if opts
.no_sponsorblock
:
372 opts
.sponsorblock_mark
= set()
373 opts
.sponsorblock_remove
= set()
374 sponsorblock_query
= opts
.sponsorblock_mark | opts
.sponsorblock_remove
376 if (opts
.addmetadata
or opts
.sponsorblock_mark
) and opts
.addchapters
is None:
377 opts
.addchapters
= True
378 opts
.remove_chapters
= opts
.remove_chapters
or []
380 if (opts
.remove_chapters
or sponsorblock_query
) and opts
.sponskrub
is not False:
382 if opts
.remove_chapters
:
383 report_conflict('--remove-chapters', '--sponskrub')
384 if opts
.sponsorblock_mark
:
385 report_conflict('--sponsorblock-mark', '--sponskrub')
386 if opts
.sponsorblock_remove
:
387 report_conflict('--sponsorblock-remove', '--sponskrub')
388 opts
.sponskrub
= False
389 if opts
.sponskrub_cut
and opts
.split_chapters
and opts
.sponskrub
is not False:
390 report_conflict('--split-chapter', '--sponskrub-cut')
391 opts
.sponskrub_cut
= False
393 if opts
.remuxvideo
and opts
.recodevideo
:
394 report_conflict('--recode-video', '--remux-video')
395 opts
.remuxvideo
= False
397 if opts
.allow_unplayable_formats
:
398 if opts
.extractaudio
:
399 report_conflict('--allow-unplayable-formats', '--extract-audio')
400 opts
.extractaudio
= False
402 report_conflict('--allow-unplayable-formats', '--remux-video')
403 opts
.remuxvideo
= False
405 report_conflict('--allow-unplayable-formats', '--recode-video')
406 opts
.recodevideo
= False
408 report_conflict('--allow-unplayable-formats', '--add-metadata')
409 opts
.addmetadata
= False
410 if opts
.embedsubtitles
:
411 report_conflict('--allow-unplayable-formats', '--embed-subs')
412 opts
.embedsubtitles
= False
413 if opts
.embedthumbnail
:
414 report_conflict('--allow-unplayable-formats', '--embed-thumbnail')
415 opts
.embedthumbnail
= False
417 report_conflict('--allow-unplayable-formats', '--xattrs')
419 if opts
.fixup
and opts
.fixup
.lower() not in ('never', 'ignore'):
420 report_conflict('--allow-unplayable-formats', '--fixup')
422 if opts
.remove_chapters
:
423 report_conflict('--allow-unplayable-formats', '--remove-chapters')
424 opts
.remove_chapters
= []
425 if opts
.sponsorblock_remove
:
426 report_conflict('--allow-unplayable-formats', '--sponsorblock-remove')
427 opts
.sponsorblock_remove
= set()
429 report_conflict('--allow-unplayable-formats', '--sponskrub')
430 opts
.sponskrub
= False
433 postprocessors
= list(opts
.add_postprocessors
)
434 if sponsorblock_query
:
435 postprocessors
.append({
436 'key': 'SponsorBlock',
437 'categories': sponsorblock_query
,
438 'api': opts
.sponsorblock_api
,
439 # Run this immediately after extraction is complete
440 'when': 'pre_process'
442 if opts
.parse_metadata
:
443 postprocessors
.append({
444 'key': 'MetadataParser',
445 'actions': opts
.parse_metadata
,
446 # Run this immediately after extraction is complete
447 'when': 'pre_process'
449 if opts
.convertsubtitles
:
450 postprocessors
.append({
451 'key': 'FFmpegSubtitlesConvertor',
452 'format': opts
.convertsubtitles
,
453 # Run this before the actual video download
456 if opts
.convertthumbnails
:
457 postprocessors
.append({
458 'key': 'FFmpegThumbnailsConvertor',
459 'format': opts
.convertthumbnails
,
460 # Run this before the actual video download
463 # Must be after all other before_dl
464 if opts
.exec_before_dl_cmd
:
465 postprocessors
.append({
467 'exec_cmd': opts
.exec_before_dl_cmd
,
470 if opts
.extractaudio
:
471 postprocessors
.append({
472 'key': 'FFmpegExtractAudio',
473 'preferredcodec': opts
.audioformat
,
474 'preferredquality': opts
.audioquality
,
475 'nopostoverwrites': opts
.nopostoverwrites
,
478 postprocessors
.append({
479 'key': 'FFmpegVideoRemuxer',
480 'preferedformat': opts
.remuxvideo
,
483 postprocessors
.append({
484 'key': 'FFmpegVideoConvertor',
485 'preferedformat': opts
.recodevideo
,
487 # If ModifyChapters is going to remove chapters, subtitles must already be in the container.
488 if opts
.embedsubtitles
:
489 already_have_subtitle
= opts
.writesubtitles
and 'no-keep-subs' not in compat_opts
490 postprocessors
.append({
491 'key': 'FFmpegEmbedSubtitle',
492 # already_have_subtitle = True prevents the file from being deleted after embedding
493 'already_have_subtitle': already_have_subtitle
495 if not opts
.writeautomaticsub
and 'no-keep-subs' not in compat_opts
:
496 opts
.writesubtitles
= True
497 # --all-sub automatically sets --write-sub if --write-auto-sub is not given
498 # this was the old behaviour if only --all-sub was given.
499 if opts
.allsubtitles
and not opts
.writeautomaticsub
:
500 opts
.writesubtitles
= True
501 # ModifyChapters must run before FFmpegMetadataPP
502 remove_chapters_patterns
, remove_ranges
= [], []
503 for regex
in opts
.remove_chapters
:
504 if regex
.startswith('*'):
505 dur
= list(map(parse_duration
, regex
[1:].split('-')))
506 if len(dur
) == 2 and all(t
is not None for t
in dur
):
507 remove_ranges
.append(tuple(dur
))
509 parser
.error(f
'invalid --remove-chapters time range {regex!r}. Must be of the form ?start-end')
511 remove_chapters_patterns
.append(re
.compile(regex
))
512 except re
.error
as err
:
513 parser
.error(f
'invalid --remove-chapters regex {regex!r} - {err}')
514 if opts
.remove_chapters
or sponsorblock_query
:
515 postprocessors
.append({
516 'key': 'ModifyChapters',
517 'remove_chapters_patterns': remove_chapters_patterns
,
518 'remove_sponsor_segments': opts
.sponsorblock_remove
,
519 'remove_ranges': remove_ranges
,
520 'sponsorblock_chapter_title': opts
.sponsorblock_chapter_title
,
521 'force_keyframes': opts
.force_keyframes_at_cuts
523 # FFmpegMetadataPP should be run after FFmpegVideoConvertorPP and
524 # FFmpegExtractAudioPP as containers before conversion may not support
525 # metadata (3gp, webm, etc.)
526 # By default ffmpeg preserves metadata applicable for both
527 # source and target containers. From this point the container won't change,
528 # so metadata can be added here.
529 if opts
.addmetadata
or opts
.addchapters
:
530 postprocessors
.append({
531 'key': 'FFmpegMetadata',
532 'add_chapters': opts
.addchapters
,
533 'add_metadata': opts
.addmetadata
,
536 # This should be above EmbedThumbnail since sponskrub removes the thumbnail attachment
537 # but must be below EmbedSubtitle and FFmpegMetadata
538 # See https://github.com/yt-dlp/yt-dlp/issues/204 , https://github.com/faissaloo/SponSkrub/issues/29
539 # If opts.sponskrub is None, sponskrub is used, but it silently fails if the executable can't be found
540 if opts
.sponskrub
is not False:
541 postprocessors
.append({
543 'path': opts
.sponskrub_path
,
544 'args': opts
.sponskrub_args
,
545 'cut': opts
.sponskrub_cut
,
546 'force': opts
.sponskrub_force
,
547 'ignoreerror': opts
.sponskrub
is None,
549 if opts
.embedthumbnail
:
550 already_have_thumbnail
= opts
.writethumbnail
or opts
.write_all_thumbnails
551 postprocessors
.append({
552 'key': 'EmbedThumbnail',
553 # already_have_thumbnail = True prevents the file from being deleted after embedding
554 'already_have_thumbnail': already_have_thumbnail
556 if not already_have_thumbnail
:
557 opts
.writethumbnail
= True
558 opts
.outtmpl
['pl_thumbnail'] = ''
559 if opts
.split_chapters
:
560 postprocessors
.append({
561 'key': 'FFmpegSplitChapters',
562 'force_keyframes': opts
.force_keyframes_at_cuts
,
564 # XAttrMetadataPP should be run after post-processors that may change file contents
566 postprocessors
.append({'key': 'XAttrMetadata'}
)
567 # Exec must be the last PP
569 postprocessors
.append({
571 'exec_cmd': opts
.exec_cmd
,
572 # Run this only after the files have been moved to their final locations
576 def report_args_compat(arg
, name
):
577 warnings
.append('%s given without specifying name. The arguments will be given to all %s' % (arg
, name
))
579 if 'default' in opts
.external_downloader_args
:
580 report_args_compat('--downloader-args', 'external downloaders')
582 if 'default-compat' in opts
.postprocessor_args
and 'default' not in opts
.postprocessor_args
:
583 report_args_compat('--post-processor-args', 'post-processors')
584 opts
.postprocessor_args
.setdefault('sponskrub', [])
585 opts
.postprocessor_args
['default'] = opts
.postprocessor_args
['default-compat']
588 opts
.recodevideo
if opts
.recodevideo
in FFmpegVideoConvertorPP
.SUPPORTED_EXTS
589 else opts
.remuxvideo
if opts
.remuxvideo
in FFmpegVideoRemuxerPP
.SUPPORTED_EXTS
590 else opts
.audioformat
if (opts
.extractaudio
and opts
.audioformat
!= 'best')
594 None if opts
.match_filter
is None
595 else match_filter_func(opts
.match_filter
))
598 'usenetrc': opts
.usenetrc
,
599 'netrc_location': opts
.netrc_location
,
600 'username': opts
.username
,
601 'password': opts
.password
,
602 'twofactor': opts
.twofactor
,
603 'videopassword': opts
.videopassword
,
604 'ap_mso': opts
.ap_mso
,
605 'ap_username': opts
.ap_username
,
606 'ap_password': opts
.ap_password
,
607 'quiet': (opts
.quiet
or any_getting
or any_printing
),
608 'no_warnings': opts
.no_warnings
,
609 'forceurl': opts
.geturl
,
610 'forcetitle': opts
.gettitle
,
611 'forceid': opts
.getid
,
612 'forcethumbnail': opts
.getthumbnail
,
613 'forcedescription': opts
.getdescription
,
614 'forceduration': opts
.getduration
,
615 'forcefilename': opts
.getfilename
,
616 'forceformat': opts
.getformat
,
617 'forceprint': opts
.forceprint
,
618 'forcejson': opts
.dumpjson
or opts
.print_json
,
619 'dump_single_json': opts
.dump_single_json
,
620 'force_write_download_archive': opts
.force_write_download_archive
,
621 'simulate': (any_getting
or None) if opts
.simulate
is None else opts
.simulate
,
622 'skip_download': opts
.skip_download
,
623 'format': opts
.format
,
624 'allow_unplayable_formats': opts
.allow_unplayable_formats
,
625 'ignore_no_formats_error': opts
.ignore_no_formats_error
,
626 'format_sort': opts
.format_sort
,
627 'format_sort_force': opts
.format_sort_force
,
628 'allow_multiple_video_streams': opts
.allow_multiple_video_streams
,
629 'allow_multiple_audio_streams': opts
.allow_multiple_audio_streams
,
630 'check_formats': opts
.check_formats
,
631 'listformats': opts
.listformats
,
632 'listformats_table': opts
.listformats_table
,
633 'outtmpl': opts
.outtmpl
,
634 'outtmpl_na_placeholder': opts
.outtmpl_na_placeholder
,
636 'autonumber_size': opts
.autonumber_size
,
637 'autonumber_start': opts
.autonumber_start
,
638 'restrictfilenames': opts
.restrictfilenames
,
639 'windowsfilenames': opts
.windowsfilenames
,
640 'ignoreerrors': opts
.ignoreerrors
,
641 'force_generic_extractor': opts
.force_generic_extractor
,
642 'ratelimit': opts
.ratelimit
,
643 'throttledratelimit': opts
.throttledratelimit
,
644 'overwrites': opts
.overwrites
,
645 'retries': opts
.retries
,
646 'fragment_retries': opts
.fragment_retries
,
647 'extractor_retries': opts
.extractor_retries
,
648 'skip_unavailable_fragments': opts
.skip_unavailable_fragments
,
649 'keep_fragments': opts
.keep_fragments
,
650 'concurrent_fragment_downloads': opts
.concurrent_fragment_downloads
,
651 'buffersize': opts
.buffersize
,
652 'noresizebuffer': opts
.noresizebuffer
,
653 'http_chunk_size': opts
.http_chunk_size
,
654 'continuedl': opts
.continue_dl
,
655 'noprogress': opts
.quiet
if opts
.noprogress
is None else opts
.noprogress
,
656 'progress_with_newline': opts
.progress_with_newline
,
657 'progress_template': opts
.progress_template
,
658 'playliststart': opts
.playliststart
,
659 'playlistend': opts
.playlistend
,
660 'playlistreverse': opts
.playlist_reverse
,
661 'playlistrandom': opts
.playlist_random
,
662 'noplaylist': opts
.noplaylist
,
663 'logtostderr': outtmpl_default
== '-',
664 'consoletitle': opts
.consoletitle
,
665 'nopart': opts
.nopart
,
666 'updatetime': opts
.updatetime
,
667 'writedescription': opts
.writedescription
,
668 'writeannotations': opts
.writeannotations
,
669 'writeinfojson': opts
.writeinfojson
,
670 'allow_playlist_files': opts
.allow_playlist_files
,
671 'clean_infojson': opts
.clean_infojson
,
672 'getcomments': opts
.getcomments
,
673 'writethumbnail': opts
.writethumbnail
,
674 'write_all_thumbnails': opts
.write_all_thumbnails
,
675 'writelink': opts
.writelink
,
676 'writeurllink': opts
.writeurllink
,
677 'writewebloclink': opts
.writewebloclink
,
678 'writedesktoplink': opts
.writedesktoplink
,
679 'writesubtitles': opts
.writesubtitles
,
680 'writeautomaticsub': opts
.writeautomaticsub
,
681 'allsubtitles': opts
.allsubtitles
,
682 'listsubtitles': opts
.listsubtitles
,
683 'subtitlesformat': opts
.subtitlesformat
,
684 'subtitleslangs': opts
.subtitleslangs
,
685 'matchtitle': decodeOption(opts
.matchtitle
),
686 'rejecttitle': decodeOption(opts
.rejecttitle
),
687 'max_downloads': opts
.max_downloads
,
688 'prefer_free_formats': opts
.prefer_free_formats
,
689 'trim_file_name': opts
.trim_file_name
,
690 'verbose': opts
.verbose
,
691 'dump_intermediate_pages': opts
.dump_intermediate_pages
,
692 'write_pages': opts
.write_pages
,
694 'keepvideo': opts
.keepvideo
,
695 'min_filesize': opts
.min_filesize
,
696 'max_filesize': opts
.max_filesize
,
697 'min_views': opts
.min_views
,
698 'max_views': opts
.max_views
,
700 'cachedir': opts
.cachedir
,
701 'youtube_print_sig_code': opts
.youtube_print_sig_code
,
702 'age_limit': opts
.age_limit
,
703 'download_archive': download_archive_fn
,
704 'break_on_existing': opts
.break_on_existing
,
705 'break_on_reject': opts
.break_on_reject
,
706 'skip_playlist_after_errors': opts
.skip_playlist_after_errors
,
707 'cookiefile': opts
.cookiefile
,
708 'cookiesfrombrowser': opts
.cookiesfrombrowser
,
709 'nocheckcertificate': opts
.no_check_certificate
,
710 'prefer_insecure': opts
.prefer_insecure
,
712 'socket_timeout': opts
.socket_timeout
,
713 'bidi_workaround': opts
.bidi_workaround
,
714 'debug_printtraffic': opts
.debug_printtraffic
,
715 'prefer_ffmpeg': opts
.prefer_ffmpeg
,
716 'include_ads': opts
.include_ads
,
717 'default_search': opts
.default_search
,
718 'dynamic_mpd': opts
.dynamic_mpd
,
719 'extractor_args': opts
.extractor_args
,
720 'youtube_include_dash_manifest': opts
.youtube_include_dash_manifest
,
721 'youtube_include_hls_manifest': opts
.youtube_include_hls_manifest
,
722 'encoding': opts
.encoding
,
723 'extract_flat': opts
.extract_flat
,
724 'mark_watched': opts
.mark_watched
,
725 'merge_output_format': opts
.merge_output_format
,
726 'final_ext': final_ext
,
727 'postprocessors': postprocessors
,
729 'source_address': opts
.source_address
,
730 'call_home': opts
.call_home
,
731 'sleep_interval_requests': opts
.sleep_interval_requests
,
732 'sleep_interval': opts
.sleep_interval
,
733 'max_sleep_interval': opts
.max_sleep_interval
,
734 'sleep_interval_subtitles': opts
.sleep_interval_subtitles
,
735 'external_downloader': opts
.external_downloader
,
736 'list_thumbnails': opts
.list_thumbnails
,
737 'playlist_items': opts
.playlist_items
,
738 'xattr_set_filesize': opts
.xattr_set_filesize
,
739 'match_filter': match_filter
,
740 'no_color': opts
.no_color
,
741 'ffmpeg_location': opts
.ffmpeg_location
,
742 'hls_prefer_native': opts
.hls_prefer_native
,
743 'hls_use_mpegts': opts
.hls_use_mpegts
,
744 'hls_split_discontinuity': opts
.hls_split_discontinuity
,
745 'external_downloader_args': opts
.external_downloader_args
,
746 'postprocessor_args': opts
.postprocessor_args
,
747 'cn_verification_proxy': opts
.cn_verification_proxy
,
748 'geo_verification_proxy': opts
.geo_verification_proxy
,
749 'geo_bypass': opts
.geo_bypass
,
750 'geo_bypass_country': opts
.geo_bypass_country
,
751 'geo_bypass_ip_block': opts
.geo_bypass_ip_block
,
752 '_warnings': warnings
,
753 'compat_opts': compat_opts
,
756 with YoutubeDL(ydl_opts
) as ydl
:
757 actual_use
= len(all_urls
) or opts
.load_info_filename
765 # If updater returns True, exit. Required for windows
768 sys
.exit('ERROR: The program must exit for the update to complete')
773 if opts
.update_self
or opts
.rm_cachedir
:
776 ydl
.warn_if_short_id(sys
.argv
[1:] if argv
is None else argv
)
778 'You must provide at least one URL.\n'
779 'Type yt-dlp --help to see a list of all options.')
782 if opts
.load_info_filename
is not None:
783 retcode
= ydl
.download_with_info_file(expand_path(opts
.load_info_filename
))
785 retcode
= ydl
.download(all_urls
)
786 except (MaxDownloadsReached
, ExistingVideoReached
, RejectedVideoReached
):
787 ydl
.to_screen('Aborting remaining downloads')
796 except DownloadError
:
798 except SameFileError
as e
:
799 sys
.exit(f
'ERROR: {e}')
800 except KeyboardInterrupt:
801 sys
.exit('\nERROR: Interrupted by user')
802 except BrokenPipeError
as e
:
803 # https://docs.python.org/3/library/signal.html#note-on-sigpipe
804 devnull
= os
.open(os
.devnull
, os
.O_WRONLY
)
805 os
.dup2(devnull
, sys
.stdout
.fileno())
806 sys
.exit(f
'\nERROR: {e}')
809 __all__
= ['main', 'YoutubeDL', 'gen_extractors', 'list_extractors']