X-Git-Url: https://jfr.im/git/yt-dlp.git/blobdiff_plain/2dd5a2e3a1c0fad8441f8e9c7eb77315afcb075b..ac668111128b5f124b4271b3aa4c35f6e71a4749:/yt_dlp/options.py diff --git a/yt_dlp/options.py b/yt_dlp/options.py index c0718e007..a4f8e0959 100644 --- a/yt_dlp/options.py +++ b/yt_dlp/options.py @@ -4,10 +4,11 @@ import os.path import re import shlex +import shutil import string import sys -from .compat import compat_expanduser, compat_get_terminal_size, compat_getenv +from .compat import compat_expanduser from .cookies import SUPPORTED_BROWSERS, SUPPORTED_KEYRINGS from .downloader.external import list_external_downloaders from .postprocessor import ( @@ -39,7 +40,7 @@ def parseOpts(overrideArguments=None, ignore_config_files='if_override'): def _readUserConf(package_name, default=[]): # .config - xdg_config_home = compat_getenv('XDG_CONFIG_HOME') or compat_expanduser('~/.config') + xdg_config_home = os.getenv('XDG_CONFIG_HOME') or compat_expanduser('~/.config') userConfFile = os.path.join(xdg_config_home, package_name, 'config') if not os.path.isfile(userConfFile): userConfFile = os.path.join(xdg_config_home, '%s.conf' % package_name) @@ -48,7 +49,7 @@ def _readUserConf(package_name, default=[]): return userConf, userConfFile # appdata - appdata_dir = compat_getenv('appdata') + appdata_dir = os.getenv('appdata') if appdata_dir: userConfFile = os.path.join(appdata_dir, package_name, 'config') userConf = Config.read_file(userConfFile, default=None) @@ -96,12 +97,16 @@ def load_configs(): opts = optparse.Values({'verbose': True, 'print_help': False}) try: - if overrideArguments: - root.append_config(overrideArguments, label='Override') - else: - root.append_config(sys.argv[1:], label='Command-line') + try: + if overrideArguments: + root.append_config(overrideArguments, label='Override') + else: + root.append_config(sys.argv[1:], label='Command-line') + loaded_all_configs = all(load_configs()) + except ValueError as err: + raise root.parser.error(err) - if all(load_configs()): + if loaded_all_configs: # If ignoreconfig is found inside the system configuration file, # the user configuration is removed if root.parse_known_args()[0].ignoreconfig: @@ -133,7 +138,7 @@ def load_configs(): class _YoutubeDLHelpFormatter(optparse.IndentedHelpFormatter): def __init__(self): # No need to wrap help messages if we're on a wide console - max_width = compat_get_terminal_size().columns or 80 + max_width = shutil.get_terminal_size().columns or 80 # The % is chosen to get a pretty output in README.md super().__init__(width=max_width, max_help_position=int(0.45 * max_width)) @@ -183,7 +188,7 @@ def parse_known_args(self, args=None, values=None, strict=True): return self.check_values(self.values, self.largs) def error(self, msg): - msg = f'{self.get_prog_name()}: error: {msg.strip()}\n' + msg = f'{self.get_prog_name()}: error: {str(msg).strip()}\n' raise optparse.OptParseError(f'{self.get_usage()}\n{msg}' if self.usage else msg) def _get_args(self, args): @@ -312,6 +317,10 @@ def _alias_callback(option, opt_str, value, parser, opts, nargs): '-U', '--update', action='store_true', dest='update_self', help='Update this program to latest version') + general.add_option( + '--no-update', + action='store_false', dest='update_self', + help='Do not update (default)') general.add_option( '-i', '--ignore-errors', action='store_true', dest='ignoreerrors', @@ -366,8 +375,8 @@ def _alias_callback(option, opt_str, value, parser, opts, nargs): '--config-locations', dest='config_locations', metavar='PATH', action='append', help=( - 'Location of the main configuration file; either the path to the config or its containing directory. ' - 'Can be used multiple times and inside other configuration files')) + 'Location of the main configuration file; either the path to the config or its containing directory ' + '("-" for stdin). Can be used multiple times and inside other configuration files')) general.add_option( '--flat-playlist', action='store_const', dest='extract_flat', const='in_playlist', default=False, @@ -496,15 +505,19 @@ def _alias_callback(option, opt_str, value, parser, opts, nargs): selection.add_option( '--playlist-start', dest='playliststart', metavar='NUMBER', default=1, type=int, - help='Playlist video to start at (default is %default)') + help=optparse.SUPPRESS_HELP) selection.add_option( '--playlist-end', dest='playlistend', metavar='NUMBER', default=None, type=int, - help='Playlist video to end at (default is last)') + help=optparse.SUPPRESS_HELP) selection.add_option( - '--playlist-items', + '-I', '--playlist-items', dest='playlist_items', metavar='ITEM_SPEC', default=None, - help='Playlist video items to download. Specify indices of the videos in the playlist separated by commas like: "--playlist-items 1,2,5,8" if you want to download videos indexed 1, 2, 5, 8 in the playlist. You can specify range: "--playlist-items 1-3,7,10-13", it will download the videos at index 1, 2, 3, 7, 10, 11, 12 and 13') + help=( + 'Comma seperated playlist_index of the videos to download. ' + 'You can specify a range using "[START]:[STOP][:STEP]". For backward compatibility, START-STOP is also supported. ' + 'Use negative indices to count from the right and negative STEP to download in reverse order. ' + 'Eg: "-I 1:3,7,-5::2" used on a playlist of size 15 will download the videos at index 1,2,3,7,11,13,15')) selection.add_option( '--match-title', dest='matchtitle', metavar='REGEX', @@ -880,16 +893,24 @@ def _alias_callback(option, opt_str, value, parser, opts, nargs): help=optparse.SUPPRESS_HELP) downloader.add_option( '--playlist-reverse', - action='store_true', - help='Download playlist videos in reverse order') + action='store_true', dest='playlist_reverse', + help=optparse.SUPPRESS_HELP) downloader.add_option( '--no-playlist-reverse', action='store_false', dest='playlist_reverse', - help='Download playlist videos in default order (default)') + help=optparse.SUPPRESS_HELP) downloader.add_option( '--playlist-random', - action='store_true', + action='store_true', dest='playlist_random', help='Download playlist videos in random order') + downloader.add_option( + '--lazy-playlist', + action='store_true', dest='lazy_playlist', + help='Process entries in the playlist as they are received. This disables n_entries, --playlist-random and --playlist-reverse') + downloader.add_option( + '--no-lazy-playlist', + action='store_false', dest='lazy_playlist', + help='Process videos in the playlist only after the entire playlist is parsed (default)') downloader.add_option( '--xattr-set-filesize', dest='xattr_set_filesize', action='store_true', @@ -916,6 +937,14 @@ def _alias_callback(option, opt_str, value, parser, opts, nargs): help=( 'Do not use the mpegts container for HLS videos. ' 'This is default when not downloading live streams')) + downloader.add_option( + '--download-sections', + metavar='REGEX', dest='download_ranges', action='append', + help=( + 'Download only chapters whose title matches the given regular expression. ' + 'Time ranges prefixed by a "*" can also be used in place of chapters to download the specified range. ' + 'Eg: --download-sections "*10:15-15:00" --download-sections "intro". ' + 'Needs ffmpeg. This option can be used multiple times to download multiple sections')) downloader.add_option( '--downloader', '--external-downloader', dest='external_downloader', metavar='[PROTO:]NAME', default={}, type='str', @@ -1154,6 +1183,10 @@ def _alias_callback(option, opt_str, value, parser, opts, nargs): '--write-pages', action='store_true', dest='write_pages', default=False, help='Write downloaded intermediary pages to files in the current directory to debug problems') + verbosity.add_option( + '--load-pages', + action='store_true', dest='load_pages', default=False, + help=optparse.SUPPRESS_HELP) verbosity.add_option( '--youtube-print-sig-code', action='store_true', dest='youtube_print_sig_code', default=False, @@ -1419,20 +1452,23 @@ def _alias_callback(option, opt_str, value, parser, opts, nargs): postproc.add_option( '--audio-format', metavar='FORMAT', dest='audioformat', default='best', help=( - 'Specify audio format to convert the audio to when -x is used. Currently supported formats are: ' - 'best (default) or one of %s' % ', '.join(FFmpegExtractAudioPP.SUPPORTED_EXTS))) + 'Format to convert the audio to when -x is used. ' + f'(currently supported: best (default), {", ".join(FFmpegExtractAudioPP.SUPPORTED_EXTS)}). ' + 'You can specify multiple rules using similar syntax as --remux-video')) postproc.add_option( '--audio-quality', metavar='QUALITY', dest='audioquality', default='5', - help='Specify ffmpeg audio quality to use when converting the audio with -x. Insert a value between 0 (best) and 10 (worst) for VBR or a specific bitrate like 128K (default %default)') + help=( + 'Specify ffmpeg audio quality to use when converting the audio with -x. ' + 'Insert a value between 0 (best) and 10 (worst) for VBR or a specific bitrate like 128K (default %default)')) postproc.add_option( '--remux-video', metavar='FORMAT', dest='remuxvideo', default=None, help=( - 'Remux the video into another container if necessary (currently supported: %s). ' - 'If target container does not support the video/audio codec, remuxing will fail. ' - 'You can specify multiple rules; Eg. "aac>m4a/mov>mp4/mkv" will remux aac to m4a, mov to mp4 ' - 'and anything else to mkv.' % ', '.join(FFmpegVideoRemuxerPP.SUPPORTED_EXTS))) + 'Remux the video into another container if necessary ' + f'(currently supported: {", ".join(FFmpegVideoRemuxerPP.SUPPORTED_EXTS)}). ' + 'If target container does not support the video/audio codec, remuxing will fail. You can specify multiple rules; ' + 'Eg. "aac>m4a/mov>mp4/mkv" will remux aac to m4a, mov to mp4 and anything else to mkv')) postproc.add_option( '--recode-video', metavar='FORMAT', dest='recodevideo', default=None, @@ -1606,7 +1642,8 @@ def _alias_callback(option, opt_str, value, parser, opts, nargs): metavar='FORMAT', dest='convertthumbnails', default=None, help=( 'Convert the thumbnails to another format ' - '(currently supported: %s) ' % ', '.join(FFmpegThumbnailsConvertorPP.SUPPORTED_EXTS))) + f'(currently supported: {", ".join(FFmpegThumbnailsConvertorPP.SUPPORTED_EXTS)}). ' + 'You can specify multiple rules using similar syntax as --remux-video')) postproc.add_option( '--split-chapters', '--split-tracks', dest='split_chapters', action='store_true', default=False, @@ -1623,9 +1660,7 @@ def _alias_callback(option, opt_str, value, parser, opts, nargs): metavar='REGEX', dest='remove_chapters', action='append', help=( 'Remove chapters whose title matches the given regular expression. ' - 'Time ranges prefixed by a "*" can also be used in place of chapters to remove the specified range. ' - 'Eg: --remove-chapters "*10:15-15:00" --remove-chapters "intro". ' - 'This option can be used multiple times')) + 'The syntax is the same as --download-sections. This option can be used multiple times')) postproc.add_option( '--no-remove-chapters', dest='remove_chapters', action='store_const', const=None, help='Do not remove any chapters from the file (default)') @@ -1633,9 +1668,8 @@ def _alias_callback(option, opt_str, value, parser, opts, nargs): '--force-keyframes-at-cuts', action='store_true', dest='force_keyframes_at_cuts', default=False, help=( - 'Force keyframes around chapters when removing/splitting them. ' - 'The resulting video may have fewer artifacts around the cuts, ' - 'but is very slow due to needing a re-encode')) + 'Force keyframes at cuts when downloading/splitting/removing sections. ' + 'This is slow due to needing a re-encode, but the resulting video may have fewer artifacts around the cuts')) postproc.add_option( '--no-force-keyframes-at-cuts', action='store_false', dest='force_keyframes_at_cuts',