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', 'only_download')
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
, f
'{k} output template')
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')
310 for k
, tmpl
in opts
.progress_template
.items():
311 k
= f
'{k[:-6]} console title' if '-title' in k
else f
'{k} progress'
312 validate_outtmpl(tmpl
, f
'{k} template')
314 if opts
.extractaudio
and not opts
.keepvideo
and opts
.format
is None:
315 opts
.format
= 'bestaudio/best'
317 if outtmpl_default
is not None and not os
.path
.splitext(outtmpl_default
)[1] and opts
.extractaudio
:
318 parser
.error('Cannot download a video and extract audio into the same'
319 ' file! Use "{0}.%(ext)s" instead of "{0}" as the output'
320 ' template'.format(outtmpl_default
))
322 for f
in opts
.format_sort
:
323 if re
.match(InfoExtractor
.FormatSort
.regex
, f
) is None:
324 parser
.error('invalid format sort string "%s" specified' % f
)
326 def metadataparser_actions(f
):
327 if isinstance(f
, str):
328 cmd
= '--parse-metadata %s' % compat_shlex_quote(f
)
330 actions
= [MetadataFromFieldPP
.to_action(f
)]
331 except Exception as err
:
332 parser
.error(f
'{cmd} is invalid; {err}')
334 cmd
= '--replace-in-metadata %s' % ' '.join(map(compat_shlex_quote
, f
))
335 actions
= ((MetadataParserPP
.Actions
.REPLACE
, x
, *f
[1:]) for x
in f
[0].split(','))
337 for action
in actions
:
339 MetadataParserPP
.validate_action(*action
)
340 except Exception as err
:
341 parser
.error(f
'{cmd} is invalid; {err}')
344 if opts
.parse_metadata
is None:
345 opts
.parse_metadata
= []
346 if opts
.metafromtitle
is not None:
347 opts
.parse_metadata
.append('title:%s' % opts
.metafromtitle
)
348 opts
.parse_metadata
= list(itertools
.chain(*map(metadataparser_actions
, opts
.parse_metadata
)))
350 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
351 any_printing
= opts
.print_json
352 download_archive_fn
= expand_path(opts
.download_archive
) if opts
.download_archive
is not None else opts
.download_archive
354 # If JSON is not printed anywhere, but comments are requested, save it to file
355 printing_json
= opts
.dumpjson
or opts
.print_json
or opts
.dump_single_json
356 if opts
.getcomments
and not printing_json
:
357 opts
.writeinfojson
= True
359 if opts
.no_sponsorblock
:
360 opts
.sponsorblock_mark
= set()
361 opts
.sponsorblock_remove
= set()
362 sponsorblock_query
= opts
.sponsorblock_mark | opts
.sponsorblock_remove
364 if (opts
.addmetadata
or opts
.sponsorblock_mark
) and opts
.addchapters
is None:
365 opts
.addchapters
= True
366 opts
.remove_chapters
= opts
.remove_chapters
or []
368 def report_conflict(arg1
, arg2
):
369 warnings
.append('%s is ignored since %s was given' % (arg2
, arg1
))
371 if (opts
.remove_chapters
or sponsorblock_query
) and opts
.sponskrub
is not False:
373 if opts
.remove_chapters
:
374 report_conflict('--remove-chapters', '--sponskrub')
375 if opts
.sponsorblock_mark
:
376 report_conflict('--sponsorblock-mark', '--sponskrub')
377 if opts
.sponsorblock_remove
:
378 report_conflict('--sponsorblock-remove', '--sponskrub')
379 opts
.sponskrub
= False
380 if opts
.sponskrub_cut
and opts
.split_chapters
and opts
.sponskrub
is not False:
381 report_conflict('--split-chapter', '--sponskrub-cut')
382 opts
.sponskrub_cut
= False
384 if opts
.remuxvideo
and opts
.recodevideo
:
385 report_conflict('--recode-video', '--remux-video')
386 opts
.remuxvideo
= False
388 if opts
.allow_unplayable_formats
:
389 if opts
.extractaudio
:
390 report_conflict('--allow-unplayable-formats', '--extract-audio')
391 opts
.extractaudio
= False
393 report_conflict('--allow-unplayable-formats', '--remux-video')
394 opts
.remuxvideo
= False
396 report_conflict('--allow-unplayable-formats', '--recode-video')
397 opts
.recodevideo
= False
399 report_conflict('--allow-unplayable-formats', '--add-metadata')
400 opts
.addmetadata
= False
401 if opts
.embedsubtitles
:
402 report_conflict('--allow-unplayable-formats', '--embed-subs')
403 opts
.embedsubtitles
= False
404 if opts
.embedthumbnail
:
405 report_conflict('--allow-unplayable-formats', '--embed-thumbnail')
406 opts
.embedthumbnail
= False
408 report_conflict('--allow-unplayable-formats', '--xattrs')
410 if opts
.fixup
and opts
.fixup
.lower() not in ('never', 'ignore'):
411 report_conflict('--allow-unplayable-formats', '--fixup')
413 if opts
.remove_chapters
:
414 report_conflict('--allow-unplayable-formats', '--remove-chapters')
415 opts
.remove_chapters
= []
416 if opts
.sponsorblock_remove
:
417 report_conflict('--allow-unplayable-formats', '--sponsorblock-remove')
418 opts
.sponsorblock_remove
= set()
420 report_conflict('--allow-unplayable-formats', '--sponskrub')
421 opts
.sponskrub
= False
424 postprocessors
= list(opts
.add_postprocessors
)
425 if sponsorblock_query
:
426 postprocessors
.append({
427 'key': 'SponsorBlock',
428 'categories': sponsorblock_query
,
429 'api': opts
.sponsorblock_api
,
430 # Run this immediately after extraction is complete
431 'when': 'pre_process'
433 if opts
.parse_metadata
:
434 postprocessors
.append({
435 'key': 'MetadataParser',
436 'actions': opts
.parse_metadata
,
437 # Run this immediately after extraction is complete
438 'when': 'pre_process'
440 if opts
.convertsubtitles
:
441 postprocessors
.append({
442 'key': 'FFmpegSubtitlesConvertor',
443 'format': opts
.convertsubtitles
,
444 # Run this before the actual video download
447 if opts
.convertthumbnails
:
448 postprocessors
.append({
449 'key': 'FFmpegThumbnailsConvertor',
450 'format': opts
.convertthumbnails
,
451 # Run this before the actual video download
454 # Must be after all other before_dl
455 if opts
.exec_before_dl_cmd
:
456 postprocessors
.append({
458 'exec_cmd': opts
.exec_before_dl_cmd
,
461 if opts
.extractaudio
:
462 postprocessors
.append({
463 'key': 'FFmpegExtractAudio',
464 'preferredcodec': opts
.audioformat
,
465 'preferredquality': opts
.audioquality
,
466 'nopostoverwrites': opts
.nopostoverwrites
,
469 postprocessors
.append({
470 'key': 'FFmpegVideoRemuxer',
471 'preferedformat': opts
.remuxvideo
,
474 postprocessors
.append({
475 'key': 'FFmpegVideoConvertor',
476 'preferedformat': opts
.recodevideo
,
478 # If ModifyChapters is going to remove chapters, subtitles must already be in the container.
479 if opts
.embedsubtitles
:
480 already_have_subtitle
= opts
.writesubtitles
and 'no-keep-subs' not in compat_opts
481 postprocessors
.append({
482 'key': 'FFmpegEmbedSubtitle',
483 # already_have_subtitle = True prevents the file from being deleted after embedding
484 'already_have_subtitle': already_have_subtitle
486 if not opts
.writeautomaticsub
and 'no-keep-subs' not in compat_opts
:
487 opts
.writesubtitles
= True
488 # --all-sub automatically sets --write-sub if --write-auto-sub is not given
489 # this was the old behaviour if only --all-sub was given.
490 if opts
.allsubtitles
and not opts
.writeautomaticsub
:
491 opts
.writesubtitles
= True
492 # ModifyChapters must run before FFmpegMetadataPP
493 remove_chapters_patterns
= []
494 for regex
in opts
.remove_chapters
:
496 remove_chapters_patterns
.append(re
.compile(regex
))
497 except re
.error
as err
:
498 parser
.error(f
'invalid --remove-chapters regex {regex!r} - {err}')
499 if opts
.remove_chapters
or sponsorblock_query
:
500 postprocessors
.append({
501 'key': 'ModifyChapters',
502 'remove_chapters_patterns': remove_chapters_patterns
,
503 'remove_sponsor_segments': opts
.sponsorblock_remove
,
504 'sponsorblock_chapter_title': opts
.sponsorblock_chapter_title
,
505 'force_keyframes': opts
.force_keyframes_at_cuts
507 # FFmpegMetadataPP should be run after FFmpegVideoConvertorPP and
508 # FFmpegExtractAudioPP as containers before conversion may not support
509 # metadata (3gp, webm, etc.)
510 # By default ffmpeg preserves metadata applicable for both
511 # source and target containers. From this point the container won't change,
512 # so metadata can be added here.
513 if opts
.addmetadata
or opts
.addchapters
:
514 postprocessors
.append({
515 'key': 'FFmpegMetadata',
516 'add_chapters': opts
.addchapters
,
517 'add_metadata': opts
.addmetadata
,
520 # This should be above EmbedThumbnail since sponskrub removes the thumbnail attachment
521 # but must be below EmbedSubtitle and FFmpegMetadata
522 # See https://github.com/yt-dlp/yt-dlp/issues/204 , https://github.com/faissaloo/SponSkrub/issues/29
523 # If opts.sponskrub is None, sponskrub is used, but it silently fails if the executable can't be found
524 if opts
.sponskrub
is not False:
525 postprocessors
.append({
527 'path': opts
.sponskrub_path
,
528 'args': opts
.sponskrub_args
,
529 'cut': opts
.sponskrub_cut
,
530 'force': opts
.sponskrub_force
,
531 'ignoreerror': opts
.sponskrub
is None,
533 if opts
.embedthumbnail
:
534 already_have_thumbnail
= opts
.writethumbnail
or opts
.write_all_thumbnails
535 postprocessors
.append({
536 'key': 'EmbedThumbnail',
537 # already_have_thumbnail = True prevents the file from being deleted after embedding
538 'already_have_thumbnail': already_have_thumbnail
540 if not already_have_thumbnail
:
541 opts
.writethumbnail
= True
542 opts
.outtmpl
['pl_thumbnail'] = ''
543 if opts
.split_chapters
:
544 postprocessors
.append({
545 'key': 'FFmpegSplitChapters',
546 'force_keyframes': opts
.force_keyframes_at_cuts
,
548 # XAttrMetadataPP should be run after post-processors that may change file contents
550 postprocessors
.append({'key': 'XAttrMetadata'}
)
551 # Exec must be the last PP
553 postprocessors
.append({
555 'exec_cmd': opts
.exec_cmd
,
556 # Run this only after the files have been moved to their final locations
560 def report_args_compat(arg
, name
):
561 warnings
.append('%s given without specifying name. The arguments will be given to all %s' % (arg
, name
))
563 if 'default' in opts
.external_downloader_args
:
564 report_args_compat('--downloader-args', 'external downloaders')
566 if 'default-compat' in opts
.postprocessor_args
and 'default' not in opts
.postprocessor_args
:
567 report_args_compat('--post-processor-args', 'post-processors')
568 opts
.postprocessor_args
.setdefault('sponskrub', [])
569 opts
.postprocessor_args
['default'] = opts
.postprocessor_args
['default-compat']
572 opts
.recodevideo
if opts
.recodevideo
in FFmpegVideoConvertorPP
.SUPPORTED_EXTS
573 else opts
.remuxvideo
if opts
.remuxvideo
in FFmpegVideoRemuxerPP
.SUPPORTED_EXTS
574 else opts
.audioformat
if (opts
.extractaudio
and opts
.audioformat
!= 'best')
578 None if opts
.match_filter
is None
579 else match_filter_func(opts
.match_filter
))
582 'usenetrc': opts
.usenetrc
,
583 'netrc_location': opts
.netrc_location
,
584 'username': opts
.username
,
585 'password': opts
.password
,
586 'twofactor': opts
.twofactor
,
587 'videopassword': opts
.videopassword
,
588 'ap_mso': opts
.ap_mso
,
589 'ap_username': opts
.ap_username
,
590 'ap_password': opts
.ap_password
,
591 'quiet': (opts
.quiet
or any_getting
or any_printing
),
592 'no_warnings': opts
.no_warnings
,
593 'forceurl': opts
.geturl
,
594 'forcetitle': opts
.gettitle
,
595 'forceid': opts
.getid
,
596 'forcethumbnail': opts
.getthumbnail
,
597 'forcedescription': opts
.getdescription
,
598 'forceduration': opts
.getduration
,
599 'forcefilename': opts
.getfilename
,
600 'forceformat': opts
.getformat
,
601 'forceprint': opts
.forceprint
,
602 'forcejson': opts
.dumpjson
or opts
.print_json
,
603 'dump_single_json': opts
.dump_single_json
,
604 'force_write_download_archive': opts
.force_write_download_archive
,
605 'simulate': (any_getting
or None) if opts
.simulate
is None else opts
.simulate
,
606 'skip_download': opts
.skip_download
,
607 'format': opts
.format
,
608 'allow_unplayable_formats': opts
.allow_unplayable_formats
,
609 'ignore_no_formats_error': opts
.ignore_no_formats_error
,
610 'format_sort': opts
.format_sort
,
611 'format_sort_force': opts
.format_sort_force
,
612 'allow_multiple_video_streams': opts
.allow_multiple_video_streams
,
613 'allow_multiple_audio_streams': opts
.allow_multiple_audio_streams
,
614 'check_formats': opts
.check_formats
,
615 'listformats': opts
.listformats
,
616 'listformats_table': opts
.listformats_table
,
617 'outtmpl': opts
.outtmpl
,
618 'outtmpl_na_placeholder': opts
.outtmpl_na_placeholder
,
620 'autonumber_size': opts
.autonumber_size
,
621 'autonumber_start': opts
.autonumber_start
,
622 'restrictfilenames': opts
.restrictfilenames
,
623 'windowsfilenames': opts
.windowsfilenames
,
624 'ignoreerrors': opts
.ignoreerrors
,
625 'force_generic_extractor': opts
.force_generic_extractor
,
626 'ratelimit': opts
.ratelimit
,
627 'throttledratelimit': opts
.throttledratelimit
,
628 'overwrites': opts
.overwrites
,
629 'retries': opts
.retries
,
630 'fragment_retries': opts
.fragment_retries
,
631 'extractor_retries': opts
.extractor_retries
,
632 'skip_unavailable_fragments': opts
.skip_unavailable_fragments
,
633 'keep_fragments': opts
.keep_fragments
,
634 'concurrent_fragment_downloads': opts
.concurrent_fragment_downloads
,
635 'buffersize': opts
.buffersize
,
636 'noresizebuffer': opts
.noresizebuffer
,
637 'http_chunk_size': opts
.http_chunk_size
,
638 'continuedl': opts
.continue_dl
,
639 'noprogress': opts
.quiet
if opts
.noprogress
is None else opts
.noprogress
,
640 'progress_with_newline': opts
.progress_with_newline
,
641 'progress_template': opts
.progress_template
,
642 'playliststart': opts
.playliststart
,
643 'playlistend': opts
.playlistend
,
644 'playlistreverse': opts
.playlist_reverse
,
645 'playlistrandom': opts
.playlist_random
,
646 'noplaylist': opts
.noplaylist
,
647 'logtostderr': outtmpl_default
== '-',
648 'consoletitle': opts
.consoletitle
,
649 'nopart': opts
.nopart
,
650 'updatetime': opts
.updatetime
,
651 'writedescription': opts
.writedescription
,
652 'writeannotations': opts
.writeannotations
,
653 'writeinfojson': opts
.writeinfojson
,
654 'allow_playlist_files': opts
.allow_playlist_files
,
655 'clean_infojson': opts
.clean_infojson
,
656 'getcomments': opts
.getcomments
,
657 'writethumbnail': opts
.writethumbnail
,
658 'write_all_thumbnails': opts
.write_all_thumbnails
,
659 'writelink': opts
.writelink
,
660 'writeurllink': opts
.writeurllink
,
661 'writewebloclink': opts
.writewebloclink
,
662 'writedesktoplink': opts
.writedesktoplink
,
663 'writesubtitles': opts
.writesubtitles
,
664 'writeautomaticsub': opts
.writeautomaticsub
,
665 'allsubtitles': opts
.allsubtitles
,
666 'listsubtitles': opts
.listsubtitles
,
667 'subtitlesformat': opts
.subtitlesformat
,
668 'subtitleslangs': opts
.subtitleslangs
,
669 'matchtitle': decodeOption(opts
.matchtitle
),
670 'rejecttitle': decodeOption(opts
.rejecttitle
),
671 'max_downloads': opts
.max_downloads
,
672 'prefer_free_formats': opts
.prefer_free_formats
,
673 'trim_file_name': opts
.trim_file_name
,
674 'verbose': opts
.verbose
,
675 'dump_intermediate_pages': opts
.dump_intermediate_pages
,
676 'write_pages': opts
.write_pages
,
678 'keepvideo': opts
.keepvideo
,
679 'min_filesize': opts
.min_filesize
,
680 'max_filesize': opts
.max_filesize
,
681 'min_views': opts
.min_views
,
682 'max_views': opts
.max_views
,
684 'cachedir': opts
.cachedir
,
685 'youtube_print_sig_code': opts
.youtube_print_sig_code
,
686 'age_limit': opts
.age_limit
,
687 'download_archive': download_archive_fn
,
688 'break_on_existing': opts
.break_on_existing
,
689 'break_on_reject': opts
.break_on_reject
,
690 'skip_playlist_after_errors': opts
.skip_playlist_after_errors
,
691 'cookiefile': opts
.cookiefile
,
692 'cookiesfrombrowser': opts
.cookiesfrombrowser
,
693 'nocheckcertificate': opts
.no_check_certificate
,
694 'prefer_insecure': opts
.prefer_insecure
,
696 'socket_timeout': opts
.socket_timeout
,
697 'bidi_workaround': opts
.bidi_workaround
,
698 'debug_printtraffic': opts
.debug_printtraffic
,
699 'prefer_ffmpeg': opts
.prefer_ffmpeg
,
700 'include_ads': opts
.include_ads
,
701 'default_search': opts
.default_search
,
702 'dynamic_mpd': opts
.dynamic_mpd
,
703 'extractor_args': opts
.extractor_args
,
704 'youtube_include_dash_manifest': opts
.youtube_include_dash_manifest
,
705 'youtube_include_hls_manifest': opts
.youtube_include_hls_manifest
,
706 'encoding': opts
.encoding
,
707 'extract_flat': opts
.extract_flat
,
708 'mark_watched': opts
.mark_watched
,
709 'merge_output_format': opts
.merge_output_format
,
710 'final_ext': final_ext
,
711 'postprocessors': postprocessors
,
713 'source_address': opts
.source_address
,
714 'call_home': opts
.call_home
,
715 'sleep_interval_requests': opts
.sleep_interval_requests
,
716 'sleep_interval': opts
.sleep_interval
,
717 'max_sleep_interval': opts
.max_sleep_interval
,
718 'sleep_interval_subtitles': opts
.sleep_interval_subtitles
,
719 'external_downloader': opts
.external_downloader
,
720 'list_thumbnails': opts
.list_thumbnails
,
721 'playlist_items': opts
.playlist_items
,
722 'xattr_set_filesize': opts
.xattr_set_filesize
,
723 'match_filter': match_filter
,
724 'no_color': opts
.no_color
,
725 'ffmpeg_location': opts
.ffmpeg_location
,
726 'hls_prefer_native': opts
.hls_prefer_native
,
727 'hls_use_mpegts': opts
.hls_use_mpegts
,
728 'hls_split_discontinuity': opts
.hls_split_discontinuity
,
729 'external_downloader_args': opts
.external_downloader_args
,
730 'postprocessor_args': opts
.postprocessor_args
,
731 'cn_verification_proxy': opts
.cn_verification_proxy
,
732 'geo_verification_proxy': opts
.geo_verification_proxy
,
733 'geo_bypass': opts
.geo_bypass
,
734 'geo_bypass_country': opts
.geo_bypass_country
,
735 'geo_bypass_ip_block': opts
.geo_bypass_ip_block
,
736 'warnings': warnings
,
737 'compat_opts': compat_opts
,
740 with YoutubeDL(ydl_opts
) as ydl
:
741 actual_use
= len(all_urls
) or opts
.load_info_filename
749 # If updater returns True, exit. Required for windows
752 sys
.exit('ERROR: The program must exit for the update to complete')
757 if opts
.update_self
or opts
.rm_cachedir
:
760 ydl
.warn_if_short_id(sys
.argv
[1:] if argv
is None else argv
)
762 'You must provide at least one URL.\n'
763 'Type yt-dlp --help to see a list of all options.')
766 if opts
.load_info_filename
is not None:
767 retcode
= ydl
.download_with_info_file(expand_path(opts
.load_info_filename
))
769 retcode
= ydl
.download(all_urls
)
770 except (MaxDownloadsReached
, ExistingVideoReached
, RejectedVideoReached
):
771 ydl
.to_screen('Aborting remaining downloads')
780 except DownloadError
:
782 except SameFileError
:
783 sys
.exit('ERROR: fixed output name but more than one file to download')
784 except KeyboardInterrupt:
785 sys
.exit('\nERROR: Interrupted by user')
786 except BrokenPipeError
:
787 # https://docs.python.org/3/library/signal.html#note-on-sigpipe
788 devnull
= os
.open(os
.devnull
, os
.O_WRONLY
)
789 os
.dup2(devnull
, sys
.stdout
.fileno())
790 sys
.exit(r
'\nERROR: {err}')
793 __all__
= ['main', 'YoutubeDL', 'gen_extractors', 'list_extractors']