diff --git a/README.md b/README.md
index dcd870decb..dcb433810e 100644
--- a/README.md
+++ b/README.md
@@ -273,7 +273,7 @@ Then simply run `make`. You can also run `make yt-dlp` instead to compile only t
     --no-mark-watched                Do not mark videos watched (default)
     --no-colors                      Do not emit color codes in output
     --compat-options OPTS            Options that can help keep compatibility
-                                     with youtube-dl and youtube-dlc
+                                     with youtube-dl or youtube-dlc
                                      configurations by reverting some of the
                                      changes made in yt-dlp. See "Differences in
                                      default behavior" for details
diff --git a/yt_dlp/__init__.py b/yt_dlp/__init__.py
index d6479b3ffe..36b2eddbab 100644
--- a/yt_dlp/__init__.py
+++ b/yt_dlp/__init__.py
@@ -257,35 +257,7 @@ def _real_main(argv=None):
     else:
         date = DateRange(opts.dateafter, opts.datebefore)
 
-    def parse_compat_opts():
-        parsed_compat_opts, compat_opts = set(), opts.compat_opts[::-1]
-        while compat_opts:
-            actual_opt = opt = compat_opts.pop().lower()
-            if opt == 'youtube-dl':
-                compat_opts.extend(['-multistreams', 'all'])
-            elif opt == 'youtube-dlc':
-                compat_opts.extend(['-no-youtube-channel-redirect', '-no-live-chat', 'all'])
-            elif opt == 'all':
-                parsed_compat_opts.update(all_compat_opts)
-            elif opt == '-all':
-                parsed_compat_opts = set()
-            else:
-                if opt[0] == '-':
-                    opt = opt[1:]
-                    parsed_compat_opts.discard(opt)
-                else:
-                    parsed_compat_opts.update([opt])
-                if opt not in all_compat_opts:
-                    parser.error('Invalid compatibility option %s' % actual_opt)
-        return parsed_compat_opts
-
-    all_compat_opts = [
-        'filename', 'format-sort', 'abort-on-error', 'format-spec', 'no-playlist-metafiles',
-        'multistreams', 'no-live-chat', 'playlist-index', 'list-formats', 'no-direct-merge',
-        'no-youtube-channel-redirect', 'no-youtube-unavailable-videos', 'no-attach-info-json',
-        'embed-thumbnail-atomicparsley', 'seperate-video-versions', 'no-clean-infojson', 'no-keep-subs',
-    ]
-    compat_opts = parse_compat_opts()
+    compat_opts = opts.compat_opts
 
     def _unused_compat_opt(name):
         if name not in compat_opts:
diff --git a/yt_dlp/options.py b/yt_dlp/options.py
index f2d5deb683..1499991a11 100644
--- a/yt_dlp/options.py
+++ b/yt_dlp/options.py
@@ -122,6 +122,30 @@ def parseOpts(overrideArguments=None):
             parser.values, option.dest,
             current + value if append is True else value + current)
 
+    def _set_from_options_callback(
+            option, opt_str, value, parser,
+            delim=',', allowed_values=None, process=str.lower, aliases={}):
+        current = getattr(parser.values, option.dest)
+        values = [process(value)] if delim is None else process(value).split(delim)[::-1]
+        while values:
+            actual_val = val = values.pop()
+            if val == 'all':
+                current.update(allowed_values)
+            elif val == '-all':
+                current = set()
+            elif val in aliases:
+                values.extend(aliases[val])
+            else:
+                if val[0] == '-':
+                    val = val[1:]
+                    current.discard(val)
+                else:
+                    current.update([val])
+                if allowed_values is not None and val not in allowed_values:
+                    raise optparse.OptionValueError(f'wrong {option.metavar} for {opt_str}: {actual_val}')
+
+        setattr(parser.values, option.dest, current)
+
     def _dict_from_options_callback(
             option, opt_str, value, parser,
             allowed_keys=r'[\w-]+', delimiter=':', default_key=None, process=None, multiple_keys=True):
@@ -241,10 +265,21 @@ def parseOpts(overrideArguments=None):
         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 '
+        metavar='OPTS', dest='compat_opts', default=set(), type='str',
+        action='callback', callback=_set_from_options_callback,
+        callback_kwargs={
+            'allowed_values': {
+                'filename', 'format-sort', 'abort-on-error', 'format-spec', 'no-playlist-metafiles',
+                'multistreams', 'no-live-chat', 'playlist-index', 'list-formats', 'no-direct-merge',
+                'no-youtube-channel-redirect', 'no-youtube-unavailable-videos', 'no-attach-info-json',
+                'embed-thumbnail-atomicparsley', 'seperate-video-versions', 'no-clean-infojson', 'no-keep-subs',
+            },
+            'aliases': {
+                'youtube-dl': ['-multistreams', 'all'],
+                'youtube-dlc': ['-no-youtube-channel-redirect', '-no-live-chat', 'all'],
+            }
+        }, help=(
+            'Options that can help keep compatibility with youtube-dl or youtube-dlc '
             'configurations by reverting some of the changes made in yt-dlp. '
             'See "Differences in default behavior" for details'))