]> jfr.im git - yt-dlp.git/blobdiff - yt_dlp/options.py
Release 2021.06.23
[yt-dlp.git] / yt_dlp / options.py
index 95ef27e26adbc9b60093582eb3da90db87313879..4ad5223fae9362ef7fdfdec9fca0cab527ec77a5 100644 (file)
@@ -5,7 +5,6 @@
 import re
 import sys
 
-from .downloader.external import list_external_downloaders
 from .compat import (
     compat_expanduser,
     compat_get_terminal_size,
     get_executable_path,
     OUTTMPL_TYPES,
     preferredencoding,
-    REMUX_EXTENSIONS,
     write_string,
 )
 from .version import __version__
 
+from .downloader.external import list_external_downloaders
+from .postprocessor.ffmpeg import (
+    FFmpegExtractAudioPP,
+    FFmpegSubtitlesConvertorPP,
+    FFmpegThumbnailsConvertorPP,
+    FFmpegVideoRemuxerPP,
+)
+
 
 def _hide_login_info(opts):
     PRIVATE_OPTS = set(['-p', '--password', '-u', '--username', '--video-password', '--ap-password', '--ap-username'])
@@ -107,22 +113,33 @@ def _format_option_string(option):
 
         return ''.join(opts)
 
-    def _comma_separated_values_options_callback(option, opt_str, value, parser):
-        setattr(parser.values, option.dest, value.split(','))
+    def _list_from_options_callback(option, opt_str, value, parser, append=True, delim=','):
+        # append can be True, False or -1 (prepend)
+        current = getattr(parser.values, option.dest) if append else []
+        value = [value] if delim is None else value.split(delim)
+        setattr(
+            parser.values, option.dest,
+            current + value if append is True else value + current)
 
-    def _dict_from_multiple_values_options_callback(
-            option, opt_str, value, parser, allowed_keys=r'[\w-]+', delimiter=':', default_key=None, process=None):
+    def _dict_from_options_callback(
+            option, opt_str, value, parser,
+            allowed_keys=r'[\w-]+', delimiter=':', default_key=None, process=None, multiple_keys=True):
 
         out_dict = getattr(parser.values, option.dest)
-        mobj = re.match(r'(?i)(?P<key>%s)%s(?P<val>.*)$' % (allowed_keys, delimiter), value)
+        if multiple_keys:
+            allowed_keys = r'(%s)(,(%s))*' % (allowed_keys, allowed_keys)
+        mobj = re.match(r'(?i)(?P<keys>%s)%s(?P<val>.*)$' % (allowed_keys, delimiter), value)
         if mobj is not None:
-            key, val = mobj.group('key').lower(), mobj.group('val')
+            keys = [k.strip() for k in mobj.group('keys').lower().split(',')]
+            val = mobj.group('val')
         elif default_key is not None:
-            key, val = default_key, value
+            keys, val = [default_key], value
         else:
             raise optparse.OptionValueError(
                 'wrong %s formatting; it should be %s, not "%s"' % (opt_str, option.metavar, value))
-        out_dict[key] = process(val) if callable(process) else val
+        val = process(val) if callable(process) else val
+        for key in keys:
+            out_dict[key] = val
 
     # No need to wrap help messages if we're on a wide console
     columns = compat_get_terminal_size().columns
@@ -156,7 +173,7 @@ def _dict_from_multiple_values_options_callback(
         help='Update this program to latest version. Make sure that you have sufficient permissions (run with sudo if needed)')
     general.add_option(
         '-i', '--ignore-errors', '--no-abort-on-error',
-        action='store_true', dest='ignoreerrors', default=True,
+        action='store_true', dest='ignoreerrors', default=None,
         help='Continue on download errors, for example to skip unavailable videos in a playlist (default) (Alias: --no-abort-on-error)')
     general.add_option(
         '--abort-on-error', '--no-ignore-errors',
@@ -220,6 +237,14 @@ def _dict_from_multiple_values_options_callback(
         '--no-colors',
         action='store_true', dest='no_color', default=False,
         help='Do not emit color codes in output')
+    general.add_option(
+        '--compat-options',
+        metavar='OPTS', dest='compat_opts', default=[],
+        action='callback', callback=_list_from_options_callback, type='str',
+        help=(
+            'Options that can help keep compatibility with youtube-dl and youtube-dlc '
+            'configurations by reverting some of the changes made in yt-dlp. '
+            'See "Differences in default behavior" for details'))
 
     network = optparse.OptionGroup(parser, 'Network Options')
     network.add_option(
@@ -250,7 +275,7 @@ def _dict_from_multiple_values_options_callback(
         help='Make all connections via IPv6',
     )
 
-    geo = optparse.OptionGroup(parser, 'Geo Restriction')
+    geo = optparse.OptionGroup(parser, 'Geo-restriction')
     geo.add_option(
         '--geo-verification-proxy',
         dest='geo_verification_proxy', default=None, metavar='URL',
@@ -386,6 +411,10 @@ def _dict_from_multiple_values_options_callback(
         '--break-on-reject',
         action='store_true', dest='break_on_reject', default=False,
         help='Stop the download process when encountering a file that has been filtered out')
+    selection.add_option(
+        '--skip-playlist-after-errors', metavar='N',
+        dest='skip_playlist_after_errors', default=None, type=int,
+        help='Number of allowed failures until the rest of the playlist is skipped')
     selection.add_option(
         '--no-download-archive',
         dest='download_archive', action="store_const", const=None,
@@ -393,11 +422,11 @@ def _dict_from_multiple_values_options_callback(
     selection.add_option(
         '--include-ads',
         dest='include_ads', action='store_true',
-        help='Download advertisements as well (experimental)')
+        help=optparse.SUPPRESS_HELP)
     selection.add_option(
         '--no-include-ads',
         dest='include_ads', action='store_false',
-        help='Do not download advertisements (default)')
+        help=optparse.SUPPRESS_HELP)
 
     authentication = optparse.OptionGroup(parser, 'Authentication Options')
     authentication.add_option(
@@ -420,21 +449,19 @@ def _dict_from_multiple_values_options_callback(
         '--video-password',
         dest='videopassword', metavar='PASSWORD',
         help='Video password (vimeo, youku)')
-
-    adobe_pass = optparse.OptionGroup(parser, 'Adobe Pass Options')
-    adobe_pass.add_option(
+    authentication.add_option(
         '--ap-mso',
         dest='ap_mso', metavar='MSO',
         help='Adobe Pass multiple-system operator (TV provider) identifier, use --ap-list-mso for a list of available MSOs')
-    adobe_pass.add_option(
+    authentication.add_option(
         '--ap-username',
         dest='ap_username', metavar='USERNAME',
         help='Multiple-system operator account login')
-    adobe_pass.add_option(
+    authentication.add_option(
         '--ap-password',
         dest='ap_password', metavar='PASSWORD',
         help='Multiple-system operator account password. If this option is left out, yt-dlp will ask interactively')
-    adobe_pass.add_option(
+    authentication.add_option(
         '--ap-list-mso',
         action='store_true', dest='ap_list_mso', default=False,
         help='List all supported multiple-system operators')
@@ -446,8 +473,8 @@ def _dict_from_multiple_values_options_callback(
         help='Video format code, see "FORMAT SELECTION" for more details')
     video_format.add_option(
         '-S', '--format-sort', metavar='SORTORDER',
-        dest='format_sort', default=[],
-        action='callback', callback=_comma_separated_values_options_callback, type='str',
+        dest='format_sort', default=[], type='str', action='callback',
+        callback=_list_from_options_callback, callback_kwargs={'append': -1},
         help='Sort the formats by the fields given, see "Sorting Formats" for more details')
     video_format.add_option(
         '--format-sort-force', '--S-force',
@@ -463,7 +490,7 @@ def _dict_from_multiple_values_options_callback(
             'see "Sorting Formats" for more details'))
     video_format.add_option(
         '--video-multistreams',
-        action='store_true', dest='allow_multiple_video_streams', default=False,
+        action='store_true', dest='allow_multiple_video_streams', default=None,
         help='Allow multiple video streams to be merged into a single file')
     video_format.add_option(
         '--no-video-multistreams',
@@ -471,7 +498,7 @@ def _dict_from_multiple_values_options_callback(
         help='Only one video stream is downloaded for each output file (default)')
     video_format.add_option(
         '--audio-multistreams',
-        action='store_true', dest='allow_multiple_audio_streams', default=False,
+        action='store_true', dest='allow_multiple_audio_streams', default=None,
         help='Allow multiple audio streams to be merged into a single file')
     video_format.add_option(
         '--no-audio-multistreams',
@@ -480,7 +507,7 @@ def _dict_from_multiple_values_options_callback(
     video_format.add_option(
         '--all-formats',
         action='store_const', dest='format', const='all',
-        help='Download all available video formats')
+        help=optparse.SUPPRESS_HELP)
     video_format.add_option(
         '--prefer-free-formats',
         action='store_true', dest='prefer_free_formats', default=False,
@@ -491,6 +518,10 @@ def _dict_from_multiple_values_options_callback(
         '--no-prefer-free-formats',
         action='store_true', dest='prefer_free_formats', default=False,
         help="Don't give any special preference to free containers (default)")
+    video_format.add_option(
+        '--check-formats',
+        action='store_true', dest='check_formats', default=False,
+        help="Check that the formats selected are actually downloadable (Experimental)")
     video_format.add_option(
         '-F', '--list-formats',
         action='store_true', dest='listformats',
@@ -498,11 +529,11 @@ def _dict_from_multiple_values_options_callback(
     video_format.add_option(
         '--list-formats-as-table',
         action='store_true', dest='listformats_table', default=True,
-        help='Present the output of -F in tabular form (default)')
+        help=optparse.SUPPRESS_HELP)
     video_format.add_option(
         '--list-formats-old', '--no-list-formats-as-table',
         action='store_false', dest='listformats_table',
-        help='Present the output of -F in the old form (Alias: --no-list-formats-as-table)')
+        help=optparse.SUPPRESS_HELP)
     video_format.add_option(
         '--merge-output-format',
         action='store', dest='merge_output_format', metavar='FORMAT', default=None,
@@ -515,7 +546,7 @@ def _dict_from_multiple_values_options_callback(
         action='store_true', dest='allow_unplayable_formats', default=False,
         help=(
             'Allow unplayable formats to be listed and downloaded. '
-            'All video postprocessing will also be turned off'))
+            'All video post-processing will also be turned off'))
     video_format.add_option(
         '--no-allow-unplayable-formats',
         action='store_false', dest='allow_unplayable_formats',
@@ -541,7 +572,7 @@ def _dict_from_multiple_values_options_callback(
     subtitles.add_option(
         '--all-subs',
         action='store_true', dest='allsubtitles', default=False,
-        help='Download all the available subtitles of the video')
+        help=optparse.SUPPRESS_HELP)
     subtitles.add_option(
         '--list-subs',
         action='store_true', dest='listsubtitles', default=False,
@@ -553,18 +584,25 @@ def _dict_from_multiple_values_options_callback(
     subtitles.add_option(
         '--sub-langs', '--srt-langs',
         action='callback', dest='subtitleslangs', metavar='LANGS', type='str',
-        default=[], callback=_comma_separated_values_options_callback,
-        help='Languages of the subtitles to download (optional) separated by commas, use --list-subs for available language tags')
+        default=[], callback=_list_from_options_callback,
+        help=(
+            'Languages of the subtitles to download (can be regex) or "all" separated by commas. (Eg: --sub-langs en.*,ja) '
+            'You can prefix the language code with a "-" to exempt it from the requested languages. (Eg: --sub-langs all,-live_chat) '
+            'Use --list-subs for a list of available language tags'))
 
     downloader = optparse.OptionGroup(parser, 'Download Options')
     downloader.add_option(
         '-N', '--concurrent-fragments',
         dest='concurrent_fragment_downloads', metavar='N', default=1, type=int,
-        help='Number of fragments to download concurrently (default is %default)')
+        help='Number of fragments of a dash/hlsnative video that should be download concurrently (default is %default)')
     downloader.add_option(
         '-r', '--limit-rate', '--rate-limit',
         dest='ratelimit', metavar='RATE',
         help='Maximum download rate in bytes per second (e.g. 50K or 4.2M)')
+    downloader.add_option(
+        '--throttled-rate',
+        dest='throttledratelimit', metavar='RATE',
+        help='Minimum download rate in bytes per second below which throttling is assumed and the video data is re-extracted (e.g. 100K)')
     downloader.add_option(
         '-R', '--retries',
         dest='retries', metavar='RETRIES', default=10,
@@ -630,11 +668,11 @@ def _dict_from_multiple_values_options_callback(
     downloader.add_option(
         '--hls-prefer-native',
         dest='hls_prefer_native', action='store_true', default=None,
-        help='Use the native HLS downloader instead of ffmpeg')
+        help=optparse.SUPPRESS_HELP)
     downloader.add_option(
         '--hls-prefer-ffmpeg',
         dest='hls_prefer_native', action='store_false', default=None,
-        help='Use ffmpeg instead of the native HLS downloader')
+        help=optparse.SUPPRESS_HELP)
     downloader.add_option(
         '--hls-use-mpegts',
         dest='hls_use_mpegts', action='store_true', default=None,
@@ -650,18 +688,31 @@ def _dict_from_multiple_values_options_callback(
             'Do not use the mpegts container for HLS videos. '
             'This is default when not downloading live streams'))
     downloader.add_option(
-        '--external-downloader',
-        dest='external_downloader', metavar='NAME',
+        '--downloader', '--external-downloader',
+        dest='external_downloader', metavar='[PROTO:]NAME', default={}, type='str',
+        action='callback', callback=_dict_from_options_callback,
+        callback_kwargs={
+            'allowed_keys': 'http|ftp|m3u8|dash|rtsp|rtmp|mms',
+            'default_key': 'default',
+            'process': lambda x: x.strip()
+        },
         help=(
-            'Name or path of the external downloader to use. '
-            'Currently supports %s (Recommended: aria2c)' % ', '.join(list_external_downloaders())))
+            'Name or path of the external downloader to use (optionally) prefixed by '
+            'the protocols (http, ftp, m3u8, dash, rstp, rtmp, mms) to use it for. '
+            'Currently supports native, %s (Recommended: aria2c). '
+            'You can use this option multiple times to set different downloaders for different protocols. '
+            'For example, --downloader aria2c --downloader "dash,m3u8:native" will use '
+            'aria2c for http/ftp downloads, and the native downloader for dash/m3u8 downloads '
+            '(Alias: --external-downloader)' % ', '.join(list_external_downloaders())))
     downloader.add_option(
         '--downloader-args', '--external-downloader-args',
         metavar='NAME:ARGS', dest='external_downloader_args', default={}, type='str',
-        action='callback', callback=_dict_from_multiple_values_options_callback,
+        action='callback', callback=_dict_from_options_callback,
         callback_kwargs={
             'allowed_keys': '|'.join(list_external_downloaders()),
-            'default_key': 'default', 'process': compat_shlex_split},
+            'default_key': 'default',
+            'process': compat_shlex_split
+        },
         help=(
             'Give these arguments to the external downloader. '
             'Specify the downloader name and the arguments separated by a colon ":". '
@@ -692,7 +743,8 @@ def _dict_from_multiple_values_options_callback(
     workarounds.add_option(
         '--add-header',
         metavar='FIELD:VALUE', dest='headers', default={}, type='str',
-        action='callback', callback=_dict_from_multiple_values_options_callback,
+        action='callback', callback=_dict_from_options_callback,
+        callback_kwargs={'multiple_keys': False},
         help='Specify a custom HTTP header and its value, separated by a colon ":". You can use this option multiple times',
     )
     workarounds.add_option(
@@ -732,42 +784,59 @@ def _dict_from_multiple_values_options_callback(
         '-s', '--simulate',
         action='store_true', dest='simulate', default=False,
         help='Do not download the video and do not write anything to disk')
+    verbosity.add_option(
+        '--ignore-no-formats-error',
+        action='store_true', dest='ignore_no_formats_error', default=False,
+        help=(
+            'Ignore "No video formats" error. Usefull for extracting metadata '
+            'even if the video is not actually available for download (experimental)'))
+    verbosity.add_option(
+        '--no-ignore-no-formats-error',
+        action='store_false', dest='ignore_no_formats_error',
+        help='Throw error when no downloadable video formats are found (default)')
     verbosity.add_option(
         '--skip-download', '--no-download',
         action='store_true', dest='skip_download', default=False,
         help='Do not download the video but write all related files (Alias: --no-download)')
+    verbosity.add_option(
+        '-O', '--print', metavar='TEMPLATE',
+        action='callback', dest='forceprint', type='str', default=[],
+        callback=_list_from_options_callback, callback_kwargs={'delim': None},
+        help=(
+            'Simulate, quiet but print the given fields. Either a field name '
+            'or similar formatting as the output template can be used'))
     verbosity.add_option(
         '-g', '--get-url',
         action='store_true', dest='geturl', default=False,
-        help='Simulate, quiet but print URL')
+        help=optparse.SUPPRESS_HELP)
     verbosity.add_option(
         '-e', '--get-title',
         action='store_true', dest='gettitle', default=False,
-        help='Simulate, quiet but print title')
+        help=optparse.SUPPRESS_HELP)
     verbosity.add_option(
         '--get-id',
         action='store_true', dest='getid', default=False,
-        help='Simulate, quiet but print id')
+        help=optparse.SUPPRESS_HELP)
     verbosity.add_option(
         '--get-thumbnail',
         action='store_true', dest='getthumbnail', default=False,
-        help='Simulate, quiet but print thumbnail URL')
+        help=optparse.SUPPRESS_HELP)
     verbosity.add_option(
         '--get-description',
         action='store_true', dest='getdescription', default=False,
-        help='Simulate, quiet but print video description')
+        help=optparse.SUPPRESS_HELP)
     verbosity.add_option(
         '--get-duration',
         action='store_true', dest='getduration', default=False,
-        help='Simulate, quiet but print video length')
+        help=optparse.SUPPRESS_HELP)
     verbosity.add_option(
         '--get-filename',
         action='store_true', dest='getfilename', default=False,
-        help='Simulate, quiet but print output filename')
+        help=optparse.SUPPRESS_HELP)
     verbosity.add_option(
         '--get-format',
         action='store_true', dest='getformat', default=False,
-        help='Simulate, quiet but print output format')
+        help=optparse.SUPPRESS_HELP)
     verbosity.add_option(
         '-j', '--dump-json',
         action='store_true', dest='dumpjson', default=False,
@@ -786,8 +855,8 @@ def _dict_from_multiple_values_options_callback(
         '--force-write-archive', '--force-write-download-archive', '--force-download-archive',
         action='store_true', dest='force_write_download_archive', default=False,
         help=(
-            'Force download archive entries to be written as far as no errors occur,'
-            'even if -s or another simulation switch is used (Alias: --force-download-archive)'))
+            'Force download archive entries to be written as far as no errors occur, '
+            'even if -s or another simulation option is used (Alias: --force-download-archive)'))
     verbosity.add_option(
         '--newline',
         action='store_true', dest='progress_with_newline', default=False,
@@ -842,11 +911,9 @@ def _dict_from_multiple_values_options_callback(
         action='store_true', dest='useid', help=optparse.SUPPRESS_HELP)
     filesystem.add_option(
         '-P', '--paths',
-        metavar='TYPE:PATH', dest='paths', default={}, type='str',
-        action='callback', callback=_dict_from_multiple_values_options_callback,
-        callback_kwargs={
-            'allowed_keys': 'home|temp|%s' % '|'.join(OUTTMPL_TYPES.keys()),
-            'process': lambda x: x.strip()},
+        metavar='TYPES:PATH', dest='paths', default={}, type='str',
+        action='callback', callback=_dict_from_options_callback,
+        callback_kwargs={'allowed_keys': 'home|temp|%s' % '|'.join(OUTTMPL_TYPES.keys())},
         help=(
             'The paths where the files should be downloaded. '
             'Specify the type of file and the path separated by a colon ":". '
@@ -857,12 +924,13 @@ def _dict_from_multiple_values_options_callback(
             'This option is ignored if --output is an absolute path'))
     filesystem.add_option(
         '-o', '--output',
-        metavar='[TYPE:]TEMPLATE', dest='outtmpl', default={}, type='str',
-        action='callback', callback=_dict_from_multiple_values_options_callback,
+        metavar='[TYPES:]TEMPLATE', dest='outtmpl', default={}, type='str',
+        action='callback', callback=_dict_from_options_callback,
         callback_kwargs={
             'allowed_keys': '|'.join(OUTTMPL_TYPES.keys()),
-            'default_key': 'default', 'process': lambda x: x.strip()},
-        help='Output filename template, see "OUTPUT TEMPLATE" for details')
+            'default_key': 'default'
+        },
+        help='Output filename template; see "OUTPUT TEMPLATE" for details')
     filesystem.add_option(
         '--output-na-placeholder',
         dest='outtmpl_na_placeholder', metavar='TEXT', default='NA',
@@ -874,7 +942,7 @@ def _dict_from_multiple_values_options_callback(
     filesystem.add_option(
         '--autonumber-start',
         dest='autonumber_start', metavar='NUMBER', default=1, type=int,
-        help='Specify the start value for %(autonumber)s (default is %default)')
+        help=optparse.SUPPRESS_HELP)
     filesystem.add_option(
         '--restrict-filenames',
         action='store_true', dest='restrictfilenames', default=False,
@@ -896,15 +964,15 @@ def _dict_from_multiple_values_options_callback(
         dest='trim_file_name', default=0, type=int,
         help='Limit the filename length (excluding extension) to the specified number of characters')
     filesystem.add_option(
-        '-A', '--auto-number',
+        '--auto-number',
         action='store_true', dest='autonumber', default=False,
         help=optparse.SUPPRESS_HELP)
     filesystem.add_option(
-        '-t', '--title',
+        '--title',
         action='store_true', dest='usetitle', default=False,
         help=optparse.SUPPRESS_HELP)
     filesystem.add_option(
-        '-l', '--literal', default=False,
+        '--literal', default=False,
         action='store_true', dest='usetitle',
         help=optparse.SUPPRESS_HELP)
     filesystem.add_option(
@@ -928,7 +996,7 @@ def _dict_from_multiple_values_options_callback(
         action='store_false', dest='continue_dl',
         help=(
             'Do not resume partially downloaded fragments. '
-            'If the file is unfragmented, restart download of the entire file'))
+            'If the file is not fragmented, restart download of the entire file'))
     filesystem.add_option(
         '--part',
         action='store_false', dest='nopart', default=False,
@@ -971,7 +1039,7 @@ def _dict_from_multiple_values_options_callback(
         help='Do not write video annotations (default)')
     filesystem.add_option(
         '--write-playlist-metafiles',
-        action='store_true', dest='allow_playlist_files', default=True,
+        action='store_true', dest='allow_playlist_files', default=None,
         help=(
             'Write playlist metadata in addition to the video metadata '
             'when using --write-info-json, --write-description etc. (default)'))
@@ -990,11 +1058,15 @@ def _dict_from_multiple_values_options_callback(
         action='store_false', dest='clean_infojson',
         help='Write all fields to the infojson')
     filesystem.add_option(
-        '--get-comments',
+        '--write-comments', '--get-comments',
         action='store_true', dest='getcomments', default=False,
         help=(
-            'Retrieve video comments to be placed in the .info.json file. '
-            'The comments are fetched even without this option if the extraction is known to be quick'))
+            'Retrieve video comments to be placed in the infojson. '
+            'The comments are fetched even without this option if the extraction is known to be quick (Alias: --get-comments)'))
+    filesystem.add_option(
+        '--no-write-comments', '--no-get-comments',
+        action='store_true', dest='getcomments', default=False,
+        help='Do not retrieve video comments unless the extraction is known to be quick (Alias: --no-get-comments)')
     filesystem.add_option(
         '--load-info-json', '--load-info',
         dest='load_info_filename', metavar='FILE',
@@ -1009,7 +1081,7 @@ def _dict_from_multiple_values_options_callback(
         help='Do not read/dump cookies (default)')
     filesystem.add_option(
         '--cache-dir', dest='cachedir', default=None, metavar='DIR',
-        help='Location in the filesystem where youtube-dl can store some downloaded information permanently. By default $XDG_CACHE_HOME/youtube-dl or ~/.cache/youtube-dl . At the moment, only YouTube player files (for videos with obfuscated signatures) are cached, but that may change')
+        help='Location in the filesystem where youtube-dl can store some downloaded information (such as client ids and signatures) permanently. By default $XDG_CACHE_HOME/youtube-dl or ~/.cache/youtube-dl')
     filesystem.add_option(
         '--no-cache-dir', action='store_false', dest='cachedir',
         help='Disable filesystem caching')
@@ -1018,7 +1090,7 @@ def _dict_from_multiple_values_options_callback(
         action='store_true', dest='rm_cachedir',
         help='Delete all filesystem cache files')
 
-    thumbnail = optparse.OptionGroup(parser, 'Thumbnail Images')
+    thumbnail = optparse.OptionGroup(parser, 'Thumbnail Options')
     thumbnail.add_option(
         '--write-thumbnail',
         action='store_true', dest='writethumbnail', default=False,
@@ -1061,7 +1133,9 @@ def _dict_from_multiple_values_options_callback(
         help='Convert video files to audio-only files (requires ffmpeg and ffprobe)')
     postproc.add_option(
         '--audio-format', metavar='FORMAT', dest='audioformat', default='best',
-        help='Specify audio format: "best", "aac", "flac", "mp3", "m4a", "opus", "vorbis", or "wav"; "%default" by default; No effect without -x')
+        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)))
     postproc.add_option(
         '--audio-quality', metavar='QUALITY',
         dest='audioquality', default='5',
@@ -1072,26 +1146,31 @@ def _dict_from_multiple_values_options_callback(
         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(REMUX_EXTENSIONS)))
+            '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)))
     postproc.add_option(
         '--recode-video',
         metavar='FORMAT', dest='recodevideo', default=None,
         help=(
             'Re-encode the video into another format if re-encoding is necessary. '
-            'The supported formats are the same as --remux-video'))
+            'The syntax and supported formats are the same as --remux-video'))
     postproc.add_option(
         '--postprocessor-args', '--ppa',
         metavar='NAME:ARGS', dest='postprocessor_args', default={}, type='str',
-        action='callback', callback=_dict_from_multiple_values_options_callback,
-        callback_kwargs={'default_key': 'default-compat', 'allowed_keys': r'\w+(?:\+\w+)?', 'process': compat_shlex_split},
+        action='callback', callback=_dict_from_options_callback,
+        callback_kwargs={
+            'allowed_keys': r'\w+(?:\+\w+)?', 'default_key': 'default-compat',
+            'process': compat_shlex_split,
+            'multiple_keys': False
+        },
         help=(
             'Give these arguments to the postprocessors. '
             'Specify the postprocessor/executable name and the arguments separated by a colon ":" '
-            'to give the argument to the specified postprocessor/executable. Supported postprocessors are: '
-            'SponSkrub, ExtractAudio, VideoRemuxer, VideoConvertor, EmbedSubtitle, Metadata, Merger, '
-            'FixupStretched, FixupM4a, FixupM3u8, SubtitlesConvertor, EmbedThumbnail and SplitChapters. '
-            'The supported executables are: SponSkrub, FFmpeg, FFprobe, and AtomicParsley. '
+            'to give the argument to the specified postprocessor/executable. Supported PP are: '
+            'Merger, ExtractAudio, SplitChapters, Metadata, EmbedSubtitle, EmbedThumbnail, '
+            'SubtitlesConvertor, ThumbnailsConvertor, VideoRemuxer, VideoConvertor, '
+            'SponSkrub, FixupStretched, FixupM4a, FixupM3u8, FixupTimestamp and FixupDuration. '
+            'The supported executables are: AtomicParsley, FFmpeg, FFprobe, and SponSkrub. '
             'You can also specify "PP+EXE:ARGS" to give the arguments to the specified executable '
             'only when being used by the specified postprocessor. Additionally, for ffmpeg/ffprobe, '
             '"_i"/"_o" can be appended to the prefix optionally followed by a number to pass the argument '
@@ -1125,41 +1204,29 @@ def _dict_from_multiple_values_options_callback(
     postproc.add_option(
         '--embed-thumbnail',
         action='store_true', dest='embedthumbnail', default=False,
-        help='Embed thumbnail in the audio as cover art')
+        help='Embed thumbnail in the video as cover art')
     postproc.add_option(
         '--no-embed-thumbnail',
         action='store_false', dest='embedthumbnail',
         help='Do not embed thumbnail (default)')
     postproc.add_option(
-        '--add-metadata',
+        '--embed-metadata', '--add-metadata',
         action='store_true', dest='addmetadata', default=False,
-        help='Write metadata to the video file')
+        help='Embed metadata including chapter markers (if supported by the format) to the video file (Alias: --add-metadata)')
     postproc.add_option(
-        '--no-add-metadata',
+        '--no-embed-metadata', '--no-add-metadata',
         action='store_false', dest='addmetadata',
-        help='Do not write metadata (default)')
+        help='Do not write metadata (default)  (Alias: --no-add-metadata)')
     postproc.add_option(
         '--metadata-from-title',
         metavar='FORMAT', dest='metafromtitle',
         help=optparse.SUPPRESS_HELP)
     postproc.add_option(
         '--parse-metadata',
-        metavar='FIELD:FORMAT', dest='metafromfield', action='append',
+        metavar='FROM:TO', dest='metafromfield', action='append',
         help=(
-            'Parse additional metadata like title/artist from other fields. '
-            'Give a template or field name to extract data from and the '
-            'format to interpret it as, seperated by a ":". '
-            'Either regular expression with named capture groups or a '
-            'similar syntax to the output template can be used for the FORMAT. '
-            'Similarly, the syntax for output template can be used for FIELD '
-            'to parse the data from multiple fields. '
-            'The parsed parameters replace any existing values and can be used in output templates. '
-            'This option can be used multiple times. '
-            'Example: --parse-metadata "title:%(artist)s - %(title)s" matches a title like '
-            '"Coldplay - Paradise". '
-            'Example: --parse-metadata "%(series)s %(episode_number)s:%(title)s" '
-            'sets the title using series and episode number. '
-            'Example (regex): --parse-metadata "description:Artist - (?P<artist>.+?)"'))
+            'Parse additional metadata like title/artist from other fields; '
+            'see "MODIFYING METADATA" for details'))
     postproc.add_option(
         '--xattrs',
         action='store_true', dest='xattrs', default=False,
@@ -1167,10 +1234,12 @@ def _dict_from_multiple_values_options_callback(
     postproc.add_option(
         '--fixup',
         metavar='POLICY', dest='fixup', default=None,
+        choices=('never', 'ignore', 'warn', 'detect_or_warn', 'force'),
         help=(
             'Automatically correct known faults of the file. '
             'One of never (do nothing), warn (only emit a warning), '
-            'detect_or_warn (the default; fix file if we can, warn otherwise)'))
+            'detect_or_warn (the default; fix file if we can, warn otherwise), '
+            'force (try fixing even if file already exists'))
     postproc.add_option(
         '--prefer-avconv', '--no-prefer-ffmpeg',
         action='store_false', dest='prefer_ffmpeg',
@@ -1186,11 +1255,23 @@ def _dict_from_multiple_values_options_callback(
     postproc.add_option(
         '--exec',
         metavar='CMD', dest='exec_cmd',
-        help='Execute a command on the file after downloading and post-processing, similar to find\'s -exec syntax. Example: --exec \'adb push {} /sdcard/Music/ && rm {}\'')
+        help=(
+            'Execute a command on the file after downloading and post-processing. '
+            'Similar syntax to the output template can be used to pass any field as arguments to the command. '
+            'An additional field "filepath" that contains the final path of the downloaded file is also available. '
+            'If no fields are passed, "%(filepath)s" is appended to the end of the command'))
     postproc.add_option(
         '--convert-subs', '--convert-sub', '--convert-subtitles',
         metavar='FORMAT', dest='convertsubtitles', default=None,
-        help='Convert the subtitles to another format (currently supported: srt|ass|vtt|lrc) (Alias: --convert-subtitles)')
+        help=(
+            'Convert the subtitles to another format (currently supported: %s) '
+            '(Alias: --convert-subtitles)' % '|'.join(FFmpegSubtitlesConvertorPP.SUPPORTED_EXTS)))
+    postproc.add_option(
+        '--convert-thumbnails',
+        metavar='FORMAT', dest='convertthumbnails', default=None,
+        help=(
+            'Convert the thumbnails to another format '
+            '(currently supported: %s) ' % '|'.join(FFmpegThumbnailsConvertorPP.SUPPORTED_EXTS)))
     postproc.add_option(
         '--split-chapters', '--split-tracks',
         dest='split_chapters', action='store_true', default=False,
@@ -1292,7 +1373,6 @@ def _dict_from_multiple_values_options_callback(
     parser.add_option_group(video_format)
     parser.add_option_group(subtitles)
     parser.add_option_group(authentication)
-    parser.add_option_group(adobe_pass)
     parser.add_option_group(postproc)
     parser.add_option_group(sponskrub)
     parser.add_option_group(extractor)