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