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