summaryrefslogtreecommitdiffstats
path: root/yt_dlp/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'yt_dlp/__init__.py')
-rw-r--r--yt_dlp/__init__.py47
1 files changed, 28 insertions, 19 deletions
diff --git a/yt_dlp/__init__.py b/yt_dlp/__init__.py
index 3d606bc..f88f15d 100644
--- a/yt_dlp/__init__.py
+++ b/yt_dlp/__init__.py
@@ -14,7 +14,7 @@ import os
import re
import traceback
-from .compat import compat_os_name, compat_shlex_quote
+from .compat import compat_os_name
from .cookies import SUPPORTED_BROWSERS, SUPPORTED_KEYRINGS
from .downloader.external import get_external_downloader
from .extractor import list_extractor_classes
@@ -58,11 +58,13 @@ from .utils import (
read_stdin,
render_table,
setproctitle,
+ shell_quote,
traverse_obj,
variadic,
write_string,
)
from .utils.networking import std_headers
+from .utils._utils import _UnsafeExtensionError
from .YoutubeDL import YoutubeDL
_IN_CLI = False
@@ -115,9 +117,9 @@ def print_extractor_information(opts, urls):
ie.description(markdown=False, search_examples=_SEARCHES)
for ie in list_extractor_classes(opts.age_limit) if ie.working() and ie.IE_DESC is not False)
elif opts.ap_list_mso:
- out = 'Supported TV Providers:\n%s\n' % render_table(
+ out = 'Supported TV Providers:\n{}\n'.format(render_table(
['mso', 'mso name'],
- [[mso_id, mso_info['name']] for mso_id, mso_info in MSO_INFO.items()])
+ [[mso_id, mso_info['name']] for mso_id, mso_info in MSO_INFO.items()]))
else:
return False
write_string(out, out=sys.stdout)
@@ -129,7 +131,7 @@ def set_compat_opts(opts):
if name not in opts.compat_opts:
return False
opts.compat_opts.discard(name)
- opts.compat_opts.update(['*%s' % name])
+ opts.compat_opts.update([f'*{name}'])
return True
def set_default_compat(compat_name, opt_name, default=True, remove_compat=True):
@@ -222,7 +224,7 @@ def validate_options(opts):
validate_minmax(opts.sleep_interval, opts.max_sleep_interval, 'sleep interval')
if opts.wait_for_video is not None:
- min_wait, max_wait, *_ = map(parse_duration, opts.wait_for_video.split('-', 1) + [None])
+ min_wait, max_wait, *_ = map(parse_duration, [*opts.wait_for_video.split('-', 1), None])
validate(min_wait is not None and not (max_wait is None and '-' in opts.wait_for_video),
'time range to wait for video', opts.wait_for_video)
validate_minmax(min_wait, max_wait, 'time range to wait for video')
@@ -264,9 +266,9 @@ def validate_options(opts):
# Retry sleep function
def parse_sleep_func(expr):
NUMBER_RE = r'\d+(?:\.\d+)?'
- op, start, limit, step, *_ = tuple(re.fullmatch(
+ op, start, limit, step, *_ = (*tuple(re.fullmatch(
rf'(?:(linear|exp)=)?({NUMBER_RE})(?::({NUMBER_RE})?)?(?::({NUMBER_RE}))?',
- expr.strip()).groups()) + (None, None)
+ expr.strip()).groups()), None, None)
if op == 'exp':
return lambda n: min(float(start) * (float(step or 2) ** n), float(limit or 'inf'))
@@ -396,13 +398,13 @@ def validate_options(opts):
# MetadataParser
def metadataparser_actions(f):
if isinstance(f, str):
- cmd = '--parse-metadata %s' % compat_shlex_quote(f)
+ cmd = f'--parse-metadata {shell_quote(f)}'
try:
actions = [MetadataFromFieldPP.to_action(f)]
except Exception as err:
raise ValueError(f'{cmd} is invalid; {err}')
else:
- cmd = '--replace-in-metadata %s' % ' '.join(map(compat_shlex_quote, f))
+ cmd = f'--replace-in-metadata {shell_quote(f)}'
actions = ((MetadataParserPP.Actions.REPLACE, x, *f[1:]) for x in f[0].split(','))
for action in actions:
@@ -413,7 +415,7 @@ def validate_options(opts):
yield action
if opts.metafromtitle is not None:
- opts.parse_metadata.setdefault('pre_process', []).append('title:%s' % opts.metafromtitle)
+ opts.parse_metadata.setdefault('pre_process', []).append(f'title:{opts.metafromtitle}')
opts.parse_metadata = {
k: list(itertools.chain(*map(metadataparser_actions, v)))
for k, v in opts.parse_metadata.items()
@@ -592,6 +594,13 @@ def validate_options(opts):
if opts.ap_username is not None and opts.ap_password is None:
opts.ap_password = getpass.getpass('Type TV provider account password and press [Return]: ')
+ # compat option changes global state destructively; only allow from cli
+ if 'allow-unsafe-ext' in opts.compat_opts:
+ warnings.append(
+ 'Using allow-unsafe-ext opens you up to potential attacks. '
+ 'Use with great care!')
+ _UnsafeExtensionError.sanitize_extension = lambda x: x
+
return warnings, deprecation_warnings
@@ -602,7 +611,7 @@ def get_postprocessors(opts):
yield {
'key': 'MetadataParser',
'actions': actions,
- 'when': when
+ 'when': when,
}
sponsorblock_query = opts.sponsorblock_mark | opts.sponsorblock_remove
if sponsorblock_query:
@@ -610,19 +619,19 @@ def get_postprocessors(opts):
'key': 'SponsorBlock',
'categories': sponsorblock_query,
'api': opts.sponsorblock_api,
- 'when': 'after_filter'
+ 'when': 'after_filter',
}
if opts.convertsubtitles:
yield {
'key': 'FFmpegSubtitlesConvertor',
'format': opts.convertsubtitles,
- 'when': 'before_dl'
+ 'when': 'before_dl',
}
if opts.convertthumbnails:
yield {
'key': 'FFmpegThumbnailsConvertor',
'format': opts.convertthumbnails,
- 'when': 'before_dl'
+ 'when': 'before_dl',
}
if opts.extractaudio:
yield {
@@ -647,7 +656,7 @@ def get_postprocessors(opts):
yield {
'key': 'FFmpegEmbedSubtitle',
# already_have_subtitle = True prevents the file from being deleted after embedding
- 'already_have_subtitle': opts.writesubtitles and keep_subs
+ 'already_have_subtitle': opts.writesubtitles and keep_subs,
}
if not opts.writeautomaticsub and keep_subs:
opts.writesubtitles = True
@@ -660,7 +669,7 @@ def get_postprocessors(opts):
'remove_sponsor_segments': opts.sponsorblock_remove,
'remove_ranges': opts.remove_ranges,
'sponsorblock_chapter_title': opts.sponsorblock_chapter_title,
- 'force_keyframes': opts.force_keyframes_at_cuts
+ 'force_keyframes': opts.force_keyframes_at_cuts,
}
# FFmpegMetadataPP should be run after FFmpegVideoConvertorPP and
# FFmpegExtractAudioPP as containers before conversion may not support
@@ -694,7 +703,7 @@ def get_postprocessors(opts):
yield {
'key': 'EmbedThumbnail',
# already_have_thumbnail = True prevents the file from being deleted after embedding
- 'already_have_thumbnail': opts.writethumbnail
+ 'already_have_thumbnail': opts.writethumbnail,
}
if not opts.writethumbnail:
opts.writethumbnail = True
@@ -741,7 +750,7 @@ def parse_options(argv=None):
print_only = bool(opts.forceprint) and all(k not in opts.forceprint for k in POSTPROCESS_WHEN[3:])
any_getting = any(getattr(opts, k) for k in (
'dumpjson', 'dump_single_json', 'getdescription', 'getduration', 'getfilename',
- 'getformat', 'getid', 'getthumbnail', 'gettitle', 'geturl'
+ 'getformat', 'getid', 'getthumbnail', 'gettitle', 'geturl',
))
if opts.quiet is None:
opts.quiet = any_getting or opts.print_json or bool(opts.forceprint)
@@ -1002,7 +1011,7 @@ def _real_main(argv=None):
def make_row(target, handler):
return [
join_nonempty(target.client.title(), target.version, delim='-') or '-',
- join_nonempty((target.os or "").title(), target.os_version, delim='-') or '-',
+ join_nonempty((target.os or '').title(), target.os_version, delim='-') or '-',
handler,
]