4 from __future__
import unicode_literals
6 __license__
= 'Public Domain'
16 from .options
import (
21 workaround_optparse_bug9161
,
41 from .update
import update_self
42 from .downloader
import (
45 from .extractor
import gen_extractors
, list_extractors
46 from .extractor
.common
import InfoExtractor
47 from .extractor
.adobepass
import MSO_INFO
48 from .postprocessor
.metadatafromfield
import MetadataFromFieldPP
49 from .YoutubeDL
import YoutubeDL
52 def _real_main(argv
=None):
53 # Compatibility fixes for Windows
54 if sys
.platform
== 'win32':
55 # https://github.com/ytdl-org/youtube-dl/issues/820
56 codecs
.register(lambda name
: codecs
.lookup('utf-8') if name
== 'cp65001' else None)
58 workaround_optparse_bug9161()
60 setproctitle('yt-dlp')
62 parser
, opts
, args
= parseOpts(argv
)
66 if opts
.user_agent
is not None:
67 std_headers
['User-Agent'] = opts
.user_agent
70 if opts
.referer
is not None:
71 std_headers
['Referer'] = opts
.referer
74 std_headers
.update(opts
.headers
)
77 if opts
.dump_user_agent
:
78 write_string(std_headers
['User-Agent'] + '\n', out
=sys
.stdout
)
81 # Batch file verification
83 if opts
.batchfile
is not None:
85 if opts
.batchfile
== '-':
89 expand_path(opts
.batchfile
),
90 'r', encoding
='utf-8', errors
='ignore')
91 batch_urls
= read_batch_urls(batchfd
)
93 write_string('[debug] Batch file urls: ' + repr(batch_urls
) + '\n')
95 sys
.exit('ERROR: batch file %s could not be read' % opts
.batchfile
)
96 all_urls
= batch_urls
+ [url
.strip() for url
in args
] # batch_urls are already striped in read_batch_urls
97 _enc
= preferredencoding()
98 all_urls
= [url
.decode(_enc
, 'ignore') if isinstance(url
, bytes) else url
for url
in all_urls
]
100 if opts
.list_extractors
:
101 for ie
in list_extractors(opts
.age_limit
):
102 write_string(ie
.IE_NAME
+ (' (CURRENTLY BROKEN)' if not ie
._WORKING
else '') + '\n', out
=sys
.stdout
)
103 matchedUrls
= [url
for url
in all_urls
if ie
.suitable(url
)]
104 for mu
in matchedUrls
:
105 write_string(' ' + mu
+ '\n', out
=sys
.stdout
)
107 if opts
.list_extractor_descriptions
:
108 for ie
in list_extractors(opts
.age_limit
):
111 desc
= getattr(ie
, 'IE_DESC', ie
.IE_NAME
)
114 if hasattr(ie
, 'SEARCH_KEY'):
115 _SEARCHES
= ('cute kittens', 'slithering pythons', 'falling cat', 'angry poodle', 'purple fish', 'running tortoise', 'sleeping bunny', 'burping cow')
116 _COUNTS
= ('', '5', '10', 'all')
117 desc
+= ' (Example: "%s%s:%s" )' % (ie
.SEARCH_KEY
, random
.choice(_COUNTS
), random
.choice(_SEARCHES
))
118 write_string(desc
+ '\n', out
=sys
.stdout
)
121 table
= [[mso_id
, mso_info
['name']] for mso_id
, mso_info
in MSO_INFO
.items()]
122 write_string('Supported TV Providers:\n' + render_table(['mso', 'mso name'], table
) + '\n', out
=sys
.stdout
)
125 # Conflicting, missing and erroneous options
126 if opts
.usenetrc
and (opts
.username
is not None or opts
.password
is not None):
127 parser
.error('using .netrc conflicts with giving username/password')
128 if opts
.password
is not None and opts
.username
is None:
129 parser
.error('account username missing\n')
130 if opts
.ap_password
is not None and opts
.ap_username
is None:
131 parser
.error('TV Provider account username missing\n')
132 if opts
.autonumber_size
is not None:
133 if opts
.autonumber_size
<= 0:
134 parser
.error('auto number size must be positive')
135 if opts
.autonumber_start
is not None:
136 if opts
.autonumber_start
< 0:
137 parser
.error('auto number start must be positive or 0')
138 if opts
.username
is not None and opts
.password
is None:
139 opts
.password
= compat_getpass('Type account password and press [Return]: ')
140 if opts
.ap_username
is not None and opts
.ap_password
is None:
141 opts
.ap_password
= compat_getpass('Type TV provider account password and press [Return]: ')
142 if opts
.ratelimit
is not None:
143 numeric_limit
= FileDownloader
.parse_bytes(opts
.ratelimit
)
144 if numeric_limit
is None:
145 parser
.error('invalid rate limit specified')
146 opts
.ratelimit
= numeric_limit
147 if opts
.min_filesize
is not None:
148 numeric_limit
= FileDownloader
.parse_bytes(opts
.min_filesize
)
149 if numeric_limit
is None:
150 parser
.error('invalid min_filesize specified')
151 opts
.min_filesize
= numeric_limit
152 if opts
.max_filesize
is not None:
153 numeric_limit
= FileDownloader
.parse_bytes(opts
.max_filesize
)
154 if numeric_limit
is None:
155 parser
.error('invalid max_filesize specified')
156 opts
.max_filesize
= numeric_limit
157 if opts
.sleep_interval
is not None:
158 if opts
.sleep_interval
< 0:
159 parser
.error('sleep interval must be positive or 0')
160 if opts
.max_sleep_interval
is not None:
161 if opts
.max_sleep_interval
< 0:
162 parser
.error('max sleep interval must be positive or 0')
163 if opts
.sleep_interval
is None:
164 parser
.error('min sleep interval must be specified, use --min-sleep-interval')
165 if opts
.max_sleep_interval
< opts
.sleep_interval
:
166 parser
.error('max sleep interval must be greater than or equal to min sleep interval')
168 opts
.max_sleep_interval
= opts
.sleep_interval
169 if opts
.sleep_interval_subtitles
is not None:
170 if opts
.sleep_interval_subtitles
< 0:
171 parser
.error('subtitles sleep interval must be positive or 0')
172 if opts
.sleep_interval_requests
is not None:
173 if opts
.sleep_interval_requests
< 0:
174 parser
.error('requests sleep interval must be positive or 0')
175 if opts
.ap_mso
and opts
.ap_mso
not in MSO_INFO
:
176 parser
.error('Unsupported TV Provider, use --ap-list-mso to get a list of supported TV Providers')
177 if opts
.overwrites
: # --yes-overwrites implies --no-continue
178 opts
.continue_dl
= False
179 if opts
.concurrent_fragment_downloads
<= 0:
180 raise ValueError('Concurrent fragments must be positive')
182 def parse_retries(retries
, name
=''):
183 if retries
in ('inf', 'infinite'):
184 parsed_retries
= float('inf')
187 parsed_retries
= int(retries
)
188 except (TypeError, ValueError):
189 parser
.error('invalid %sretry count specified' % name
)
190 return parsed_retries
191 if opts
.retries
is not None:
192 opts
.retries
= parse_retries(opts
.retries
)
193 if opts
.fragment_retries
is not None:
194 opts
.fragment_retries
= parse_retries(opts
.fragment_retries
, 'fragment ')
195 if opts
.extractor_retries
is not None:
196 opts
.extractor_retries
= parse_retries(opts
.extractor_retries
, 'extractor ')
197 if opts
.buffersize
is not None:
198 numeric_buffersize
= FileDownloader
.parse_bytes(opts
.buffersize
)
199 if numeric_buffersize
is None:
200 parser
.error('invalid buffer size specified')
201 opts
.buffersize
= numeric_buffersize
202 if opts
.http_chunk_size
is not None:
203 numeric_chunksize
= FileDownloader
.parse_bytes(opts
.http_chunk_size
)
204 if not numeric_chunksize
:
205 parser
.error('invalid http chunk size specified')
206 opts
.http_chunk_size
= numeric_chunksize
207 if opts
.playliststart
<= 0:
208 raise ValueError('Playlist start must be positive')
209 if opts
.playlistend
not in (-1, None) and opts
.playlistend
< opts
.playliststart
:
210 raise ValueError('Playlist end must be greater than playlist start')
211 if opts
.extractaudio
:
212 if opts
.audioformat
not in ['best', 'aac', 'flac', 'mp3', 'm4a', 'opus', 'vorbis', 'wav']:
213 parser
.error('invalid audio format specified')
214 if opts
.audioquality
:
215 opts
.audioquality
= opts
.audioquality
.strip('k').strip('K')
216 if not opts
.audioquality
.isdigit():
217 parser
.error('invalid audio quality specified')
218 if opts
.recodevideo
is not None:
219 if opts
.recodevideo
not in REMUX_EXTENSIONS
:
220 parser
.error('invalid video recode format specified')
221 if opts
.remuxvideo
is not None:
222 opts
.remuxvideo
= opts
.remuxvideo
.replace(' ', '')
223 remux_regex
= r
'{0}(?:/{0})*$'.format(r
'(?:\w+>)?(?:%s)' % '|'.join(REMUX_EXTENSIONS
))
224 if not re
.match(remux_regex
, opts
.remuxvideo
):
225 parser
.error('invalid video remux format specified')
226 if opts
.convertsubtitles
is not None:
227 if opts
.convertsubtitles
not in ('srt', 'vtt', 'ass', 'lrc'):
228 parser
.error('invalid subtitle format specified')
229 if opts
.convertthumbnails
is not None:
230 if opts
.convertthumbnails
not in ('jpg', ):
231 parser
.error('invalid thumbnail format specified')
233 if opts
.date
is not None:
234 date
= DateRange
.day(opts
.date
)
236 date
= DateRange(opts
.dateafter
, opts
.datebefore
)
238 def parse_compat_opts():
239 parsed_compat_opts
, compat_opts
= set(), opts
.compat_opts
[::-1]
241 actual_opt
= opt
= compat_opts
.pop().lower()
242 if opt
== 'youtube-dl':
243 compat_opts
.extend(['-multistreams', 'all'])
244 elif opt
== 'youtube-dlc':
245 compat_opts
.extend(['-no-youtube-channel-redirect', '-no-live-chat', 'all'])
247 parsed_compat_opts
.update(all_compat_opts
)
249 parsed_compat_opts
= set()
253 parsed_compat_opts
.discard(opt
)
255 parsed_compat_opts
.update([opt
])
256 if opt
not in all_compat_opts
:
257 parser
.error('Invalid compatibility option %s' % actual_opt
)
258 return parsed_compat_opts
261 'filename', 'format-sort', 'abort-on-error', 'format-spec', 'multistreams',
262 'no-playlist-metafiles', 'no-live-chat', 'playlist-index', 'list-formats',
263 'no-youtube-channel-redirect', 'no-youtube-unavailable-videos', 'no-attach-info-json',
265 compat_opts
= parse_compat_opts()
267 def _unused_compat_opt(name
):
268 if name
not in compat_opts
:
270 compat_opts
.discard(name
)
271 compat_opts
.update(['*%s' % name
])
274 def set_default_compat(compat_name
, opt_name
, default
=True, remove_compat
=False):
275 attr
= getattr(opts
, opt_name
)
276 if compat_name
in compat_opts
:
278 setattr(opts
, opt_name
, not default
)
282 _unused_compat_opt(compat_name
)
285 setattr(opts
, opt_name
, default
)
288 set_default_compat('abort-on-error', 'ignoreerrors')
289 set_default_compat('no-playlist-metafiles', 'allow_playlist_files')
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')
297 if 'filename' in compat_opts
:
298 if outtmpl_default
is None:
299 outtmpl_default
= '%(title)s.%(id)s.%(ext)s'
300 opts
.outtmpl
.update({'default': outtmpl_default}
)
302 _unused_compat_opt('filename')
304 if opts
.extractaudio
and not opts
.keepvideo
and opts
.format
is None:
305 opts
.format
= 'bestaudio/best'
307 if outtmpl_default
is not None and not os
.path
.splitext(outtmpl_default
)[1] and opts
.extractaudio
:
308 parser
.error('Cannot download a video and extract audio into the same'
309 ' file! Use "{0}.%(ext)s" instead of "{0}" as the output'
310 ' template'.format(outtmpl_default
))
312 for f
in opts
.format_sort
:
313 if re
.match(InfoExtractor
.FormatSort
.regex
, f
) is None:
314 parser
.error('invalid format sort string "%s" specified' % f
)
316 if opts
.metafromfield
is None:
317 opts
.metafromfield
= []
318 if opts
.metafromtitle
is not None:
319 opts
.metafromfield
.append('title:%s' % opts
.metafromtitle
)
320 for f
in opts
.metafromfield
:
321 if re
.match(MetadataFromFieldPP
.regex
, f
) is None:
322 parser
.error('invalid format string "%s" specified for --parse-metadata' % f
)
324 any_getting
= opts
.print 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
325 any_printing
= opts
.print_json
326 download_archive_fn
= expand_path(opts
.download_archive
) if opts
.download_archive
is not None else opts
.download_archive
328 # If JSON is not printed anywhere, but comments are requested, save it to file
329 printing_json
= opts
.dumpjson
or opts
.print_json
or opts
.dump_single_json
330 if opts
.getcomments
and not printing_json
:
331 opts
.writeinfojson
= True
333 def report_conflict(arg1
, arg2
):
334 warnings
.append('%s is ignored since %s was given' % (arg2
, arg1
))
336 if opts
.remuxvideo
and opts
.recodevideo
:
337 report_conflict('--recode-video', '--remux-video')
338 opts
.remuxvideo
= False
339 if opts
.sponskrub_cut
and opts
.split_chapters
and opts
.sponskrub
is not False:
340 report_conflict('--split-chapter', '--sponskrub-cut')
341 opts
.sponskrub_cut
= False
343 if opts
.allow_unplayable_formats
:
344 if opts
.extractaudio
:
345 report_conflict('--allow-unplayable-formats', '--extract-audio')
346 opts
.extractaudio
= False
348 report_conflict('--allow-unplayable-formats', '--remux-video')
349 opts
.remuxvideo
= False
351 report_conflict('--allow-unplayable-formats', '--recode-video')
352 opts
.recodevideo
= False
354 report_conflict('--allow-unplayable-formats', '--add-metadata')
355 opts
.addmetadata
= False
356 if opts
.embedsubtitles
:
357 report_conflict('--allow-unplayable-formats', '--embed-subs')
358 opts
.embedsubtitles
= False
359 if opts
.embedthumbnail
:
360 report_conflict('--allow-unplayable-formats', '--embed-thumbnail')
361 opts
.embedthumbnail
= False
363 report_conflict('--allow-unplayable-formats', '--xattrs')
365 if opts
.fixup
and opts
.fixup
.lower() not in ('never', 'ignore'):
366 report_conflict('--allow-unplayable-formats', '--fixup')
369 report_conflict('--allow-unplayable-formats', '--sponskrub')
370 opts
.sponskrub
= False
374 if opts
.metafromfield
:
375 postprocessors
.append({
376 'key': 'MetadataFromField',
377 'formats': opts
.metafromfield
,
378 # Run this immediately after extraction is complete
379 'when': 'pre_process'
381 if opts
.convertsubtitles
:
382 postprocessors
.append({
383 'key': 'FFmpegSubtitlesConvertor',
384 'format': opts
.convertsubtitles
,
385 # Run this before the actual video download
388 if opts
.convertthumbnails
:
389 postprocessors
.append({
390 'key': 'FFmpegThumbnailsConvertor',
391 'format': opts
.convertthumbnails
,
392 # Run this before the actual video download
395 if opts
.extractaudio
:
396 postprocessors
.append({
397 'key': 'FFmpegExtractAudio',
398 'preferredcodec': opts
.audioformat
,
399 'preferredquality': opts
.audioquality
,
400 'nopostoverwrites': opts
.nopostoverwrites
,
403 postprocessors
.append({
404 'key': 'FFmpegVideoRemuxer',
405 'preferedformat': opts
.remuxvideo
,
408 postprocessors
.append({
409 'key': 'FFmpegVideoConvertor',
410 'preferedformat': opts
.recodevideo
,
412 # FFmpegMetadataPP should be run after FFmpegVideoConvertorPP and
413 # FFmpegExtractAudioPP as containers before conversion may not support
414 # metadata (3gp, webm, etc.)
415 # And this post-processor should be placed before other metadata
416 # manipulating post-processors (FFmpegEmbedSubtitle) to prevent loss of
417 # extra metadata. By default ffmpeg preserves metadata applicable for both
418 # source and target containers. From this point the container won't change,
419 # so metadata can be added here.
421 postprocessors
.append({'key': 'FFmpegMetadata'}
)
422 if opts
.embedsubtitles
:
423 already_have_subtitle
= opts
.writesubtitles
424 postprocessors
.append({
425 'key': 'FFmpegEmbedSubtitle',
426 # already_have_subtitle = True prevents the file from being deleted after embedding
427 'already_have_subtitle': already_have_subtitle
429 if not already_have_subtitle
:
430 opts
.writesubtitles
= True
431 # --all-sub automatically sets --write-sub if --write-auto-sub is not given
432 # this was the old behaviour if only --all-sub was given.
433 if opts
.allsubtitles
and not opts
.writeautomaticsub
:
434 opts
.writesubtitles
= True
435 # This should be above EmbedThumbnail since sponskrub removes the thumbnail attachment
436 # but must be below EmbedSubtitle and FFmpegMetadata
437 # See https://github.com/yt-dlp/yt-dlp/issues/204 , https://github.com/faissaloo/SponSkrub/issues/29
438 # If opts.sponskrub is None, sponskrub is used, but it silently fails if the executable can't be found
439 if opts
.sponskrub
is not False:
440 postprocessors
.append({
442 'path': opts
.sponskrub_path
,
443 'args': opts
.sponskrub_args
,
444 'cut': opts
.sponskrub_cut
,
445 'force': opts
.sponskrub_force
,
446 'ignoreerror': opts
.sponskrub
is None,
448 if opts
.embedthumbnail
:
449 already_have_thumbnail
= opts
.writethumbnail
or opts
.write_all_thumbnails
450 postprocessors
.append({
451 'key': 'EmbedThumbnail',
452 # already_have_thumbnail = True prevents the file from being deleted after embedding
453 'already_have_thumbnail': already_have_thumbnail
455 if not already_have_thumbnail
:
456 opts
.writethumbnail
= True
457 if opts
.split_chapters
:
458 postprocessors
.append({'key': 'FFmpegSplitChapters'}
)
459 # XAttrMetadataPP should be run after post-processors that may change file contents
461 postprocessors
.append({'key': 'XAttrMetadata'}
)
462 # ExecAfterDownload must be the last PP
464 postprocessors
.append({
465 'key': 'ExecAfterDownload',
466 'exec_cmd': opts
.exec_cmd
,
467 # Run this only after the files have been moved to their final locations
471 def report_args_compat(arg
, name
):
472 warnings
.append('%s given without specifying name. The arguments will be given to all %s' % (arg
, name
))
474 if 'default' in opts
.external_downloader_args
:
475 report_args_compat('--downloader-args', 'external downloaders')
477 if 'default-compat' in opts
.postprocessor_args
and 'default' not in opts
.postprocessor_args
:
478 report_args_compat('--post-processor-args', 'post-processors')
479 opts
.postprocessor_args
.setdefault('sponskrub', [])
480 opts
.postprocessor_args
['default'] = opts
.postprocessor_args
['default-compat']
484 or (opts
.remuxvideo
in REMUX_EXTENSIONS
) and opts
.remuxvideo
485 or (opts
.extractaudio
and opts
.audioformat
!= 'best') and opts
.audioformat
489 None if opts
.match_filter
is None
490 else match_filter_func(opts
.match_filter
))
493 'usenetrc': opts
.usenetrc
,
494 'username': opts
.username
,
495 'password': opts
.password
,
496 'twofactor': opts
.twofactor
,
497 'videopassword': opts
.videopassword
,
498 'ap_mso': opts
.ap_mso
,
499 'ap_username': opts
.ap_username
,
500 'ap_password': opts
.ap_password
,
501 'quiet': (opts
.quiet
or any_getting
or any_printing
),
502 'no_warnings': opts
.no_warnings
,
503 'forceurl': opts
.geturl
,
504 'forcetitle': opts
.gettitle
,
505 'forceid': opts
.getid
,
506 'forcethumbnail': opts
.getthumbnail
,
507 'forcedescription': opts
.getdescription
,
508 'forceduration': opts
.getduration
,
509 'forcefilename': opts
.getfilename
,
510 'forceformat': opts
.getformat
,
511 'forceprint': opts
.print,
512 'forcejson': opts
.dumpjson
or opts
.print_json
,
513 'dump_single_json': opts
.dump_single_json
,
514 'force_write_download_archive': opts
.force_write_download_archive
,
515 'simulate': opts
.simulate
or any_getting
,
516 'skip_download': opts
.skip_download
,
517 'format': opts
.format
,
518 'allow_unplayable_formats': opts
.allow_unplayable_formats
,
519 'ignore_no_formats_error': opts
.ignore_no_formats_error
,
520 'format_sort': opts
.format_sort
,
521 'format_sort_force': opts
.format_sort_force
,
522 'allow_multiple_video_streams': opts
.allow_multiple_video_streams
,
523 'allow_multiple_audio_streams': opts
.allow_multiple_audio_streams
,
524 'check_formats': opts
.check_formats
,
525 'listformats': opts
.listformats
,
526 'listformats_table': opts
.listformats_table
,
527 'outtmpl': opts
.outtmpl
,
528 'outtmpl_na_placeholder': opts
.outtmpl_na_placeholder
,
530 'autonumber_size': opts
.autonumber_size
,
531 'autonumber_start': opts
.autonumber_start
,
532 'restrictfilenames': opts
.restrictfilenames
,
533 'windowsfilenames': opts
.windowsfilenames
,
534 'ignoreerrors': opts
.ignoreerrors
,
535 'force_generic_extractor': opts
.force_generic_extractor
,
536 'ratelimit': opts
.ratelimit
,
537 'overwrites': opts
.overwrites
,
538 'retries': opts
.retries
,
539 'fragment_retries': opts
.fragment_retries
,
540 'extractor_retries': opts
.extractor_retries
,
541 'skip_unavailable_fragments': opts
.skip_unavailable_fragments
,
542 'keep_fragments': opts
.keep_fragments
,
543 'concurrent_fragment_downloads': opts
.concurrent_fragment_downloads
,
544 'buffersize': opts
.buffersize
,
545 'noresizebuffer': opts
.noresizebuffer
,
546 'http_chunk_size': opts
.http_chunk_size
,
547 'continuedl': opts
.continue_dl
,
548 'noprogress': opts
.noprogress
,
549 'progress_with_newline': opts
.progress_with_newline
,
550 'playliststart': opts
.playliststart
,
551 'playlistend': opts
.playlistend
,
552 'playlistreverse': opts
.playlist_reverse
,
553 'playlistrandom': opts
.playlist_random
,
554 'noplaylist': opts
.noplaylist
,
555 'logtostderr': outtmpl_default
== '-',
556 'consoletitle': opts
.consoletitle
,
557 'nopart': opts
.nopart
,
558 'updatetime': opts
.updatetime
,
559 'writedescription': opts
.writedescription
,
560 'writeannotations': opts
.writeannotations
,
561 'writeinfojson': opts
.writeinfojson
,
562 'allow_playlist_files': opts
.allow_playlist_files
,
563 'clean_infojson': opts
.clean_infojson
,
564 'getcomments': opts
.getcomments
,
565 'writethumbnail': opts
.writethumbnail
,
566 'write_all_thumbnails': opts
.write_all_thumbnails
,
567 'writelink': opts
.writelink
,
568 'writeurllink': opts
.writeurllink
,
569 'writewebloclink': opts
.writewebloclink
,
570 'writedesktoplink': opts
.writedesktoplink
,
571 'writesubtitles': opts
.writesubtitles
,
572 'writeautomaticsub': opts
.writeautomaticsub
,
573 'allsubtitles': opts
.allsubtitles
,
574 'listsubtitles': opts
.listsubtitles
,
575 'subtitlesformat': opts
.subtitlesformat
,
576 'subtitleslangs': opts
.subtitleslangs
,
577 'matchtitle': decodeOption(opts
.matchtitle
),
578 'rejecttitle': decodeOption(opts
.rejecttitle
),
579 'max_downloads': opts
.max_downloads
,
580 'prefer_free_formats': opts
.prefer_free_formats
,
581 'trim_file_name': opts
.trim_file_name
,
582 'verbose': opts
.verbose
,
583 'dump_intermediate_pages': opts
.dump_intermediate_pages
,
584 'write_pages': opts
.write_pages
,
586 'keepvideo': opts
.keepvideo
,
587 'min_filesize': opts
.min_filesize
,
588 'max_filesize': opts
.max_filesize
,
589 'min_views': opts
.min_views
,
590 'max_views': opts
.max_views
,
592 'cachedir': opts
.cachedir
,
593 'youtube_print_sig_code': opts
.youtube_print_sig_code
,
594 'age_limit': opts
.age_limit
,
595 'download_archive': download_archive_fn
,
596 'break_on_existing': opts
.break_on_existing
,
597 'break_on_reject': opts
.break_on_reject
,
598 'skip_playlist_after_errors': opts
.skip_playlist_after_errors
,
599 'cookiefile': opts
.cookiefile
,
600 'nocheckcertificate': opts
.no_check_certificate
,
601 'prefer_insecure': opts
.prefer_insecure
,
603 'socket_timeout': opts
.socket_timeout
,
604 'bidi_workaround': opts
.bidi_workaround
,
605 'debug_printtraffic': opts
.debug_printtraffic
,
606 'prefer_ffmpeg': opts
.prefer_ffmpeg
,
607 'include_ads': opts
.include_ads
,
608 'default_search': opts
.default_search
,
609 'dynamic_mpd': opts
.dynamic_mpd
,
610 'youtube_include_dash_manifest': opts
.youtube_include_dash_manifest
,
611 'youtube_include_hls_manifest': opts
.youtube_include_hls_manifest
,
612 'encoding': opts
.encoding
,
613 'extract_flat': opts
.extract_flat
,
614 'mark_watched': opts
.mark_watched
,
615 'merge_output_format': opts
.merge_output_format
,
616 'final_ext': final_ext
,
617 'postprocessors': postprocessors
,
619 'source_address': opts
.source_address
,
620 'call_home': opts
.call_home
,
621 'sleep_interval_requests': opts
.sleep_interval_requests
,
622 'sleep_interval': opts
.sleep_interval
,
623 'max_sleep_interval': opts
.max_sleep_interval
,
624 'sleep_interval_subtitles': opts
.sleep_interval_subtitles
,
625 'external_downloader': opts
.external_downloader
,
626 'list_thumbnails': opts
.list_thumbnails
,
627 'playlist_items': opts
.playlist_items
,
628 'xattr_set_filesize': opts
.xattr_set_filesize
,
629 'match_filter': match_filter
,
630 'no_color': opts
.no_color
,
631 'ffmpeg_location': opts
.ffmpeg_location
,
632 'hls_prefer_native': opts
.hls_prefer_native
,
633 'hls_use_mpegts': opts
.hls_use_mpegts
,
634 'hls_split_discontinuity': opts
.hls_split_discontinuity
,
635 'external_downloader_args': opts
.external_downloader_args
,
636 'postprocessor_args': opts
.postprocessor_args
,
637 'cn_verification_proxy': opts
.cn_verification_proxy
,
638 'geo_verification_proxy': opts
.geo_verification_proxy
,
639 'geo_bypass': opts
.geo_bypass
,
640 'geo_bypass_country': opts
.geo_bypass_country
,
641 'geo_bypass_ip_block': opts
.geo_bypass_ip_block
,
642 'warnings': warnings
,
643 'compat_opts': compat_opts
,
644 # just for deprecation check
645 'autonumber': opts
.autonumber
or None,
646 'usetitle': opts
.usetitle
or None,
647 'useid': opts
.useid
or None,
650 with YoutubeDL(ydl_opts
) as ydl
:
651 actual_use
= len(all_urls
) or opts
.load_info_filename
659 # If updater returns True, exit. Required for windows
660 if update_self(ydl
.to_screen
, opts
.verbose
, ydl
._opener
):
662 sys
.exit('ERROR: The program must exit for the update to complete')
667 if opts
.update_self
or opts
.rm_cachedir
:
670 ydl
.warn_if_short_id(sys
.argv
[1:] if argv
is None else argv
)
672 'You must provide at least one URL.\n'
673 'Type yt-dlp --help to see a list of all options.')
676 if opts
.load_info_filename
is not None:
677 retcode
= ydl
.download_with_info_file(expand_path(opts
.load_info_filename
))
679 retcode
= ydl
.download(all_urls
)
680 except (MaxDownloadsReached
, ExistingVideoReached
, RejectedVideoReached
):
681 ydl
.to_screen('Aborting remaining downloads')
690 except DownloadError
:
692 except SameFileError
:
693 sys
.exit('ERROR: fixed output name but more than one file to download')
694 except KeyboardInterrupt:
695 sys
.exit('\nERROR: Interrupted by user')
698 __all__
= ['main', 'YoutubeDL', 'gen_extractors', 'list_extractors']