Updated yt_dlp version; added extremly basic dumb cache setup in thumbnailer; moved build and script as well as deb folder to build
This commit is contained in:
145
plugins/youtube_download/yt_dlp/utils/_jsruntime.py
Normal file
145
plugins/youtube_download/yt_dlp/utils/_jsruntime.py
Normal file
@@ -0,0 +1,145 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import abc
|
||||
import dataclasses
|
||||
import functools
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
from ._utils import _get_exe_version_output, detect_exe_version, version_tuple
|
||||
|
||||
|
||||
_FALLBACK_PATHEXT = ('.COM', '.EXE', '.BAT', '.CMD')
|
||||
|
||||
|
||||
def _find_exe(basename: str) -> str:
|
||||
if os.name != 'nt':
|
||||
return basename
|
||||
|
||||
paths: list[str] = []
|
||||
|
||||
# binary dir
|
||||
if getattr(sys, 'frozen', False):
|
||||
paths.append(os.path.dirname(sys.executable))
|
||||
# cwd
|
||||
paths.append(os.getcwd())
|
||||
# PATH items
|
||||
if path := os.environ.get('PATH'):
|
||||
paths.extend(filter(None, path.split(os.path.pathsep)))
|
||||
|
||||
pathext = os.environ.get('PATHEXT')
|
||||
if pathext is None:
|
||||
exts = _FALLBACK_PATHEXT
|
||||
else:
|
||||
exts = tuple(ext for ext in pathext.split(os.pathsep) if ext)
|
||||
|
||||
visited = []
|
||||
for path in map(os.path.realpath, paths):
|
||||
normed = os.path.normcase(path)
|
||||
if normed in visited:
|
||||
continue
|
||||
visited.append(normed)
|
||||
|
||||
for ext in exts:
|
||||
binary = os.path.join(path, f'{basename}{ext}')
|
||||
if os.access(binary, os.F_OK | os.X_OK) and not os.path.isdir(binary):
|
||||
return binary
|
||||
|
||||
return basename
|
||||
|
||||
|
||||
def _determine_runtime_path(path, basename):
|
||||
if not path:
|
||||
return _find_exe(basename)
|
||||
if os.path.isdir(path):
|
||||
return os.path.join(path, basename)
|
||||
return path
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class JsRuntimeInfo:
|
||||
name: str
|
||||
path: str
|
||||
version: str
|
||||
version_tuple: tuple[int, ...]
|
||||
supported: bool = True
|
||||
|
||||
|
||||
class JsRuntime(abc.ABC):
|
||||
def __init__(self, path=None):
|
||||
self._path = path
|
||||
|
||||
@functools.cached_property
|
||||
def info(self) -> JsRuntimeInfo | None:
|
||||
return self._info()
|
||||
|
||||
@abc.abstractmethod
|
||||
def _info(self) -> JsRuntimeInfo | None:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class DenoJsRuntime(JsRuntime):
|
||||
MIN_SUPPORTED_VERSION = (2, 0, 0)
|
||||
|
||||
def _info(self):
|
||||
path = _determine_runtime_path(self._path, 'deno')
|
||||
out = _get_exe_version_output(path, ['--version'])
|
||||
if not out:
|
||||
return None
|
||||
version = detect_exe_version(out, r'^deno (\S+)', 'unknown')
|
||||
vt = version_tuple(version, lenient=True)
|
||||
return JsRuntimeInfo(
|
||||
name='deno', path=path, version=version, version_tuple=vt,
|
||||
supported=vt >= self.MIN_SUPPORTED_VERSION)
|
||||
|
||||
|
||||
class BunJsRuntime(JsRuntime):
|
||||
MIN_SUPPORTED_VERSION = (1, 0, 31)
|
||||
|
||||
def _info(self):
|
||||
path = _determine_runtime_path(self._path, 'bun')
|
||||
out = _get_exe_version_output(path, ['--version'])
|
||||
if not out:
|
||||
return None
|
||||
version = detect_exe_version(out, r'^(\S+)', 'unknown')
|
||||
vt = version_tuple(version, lenient=True)
|
||||
return JsRuntimeInfo(
|
||||
name='bun', path=path, version=version, version_tuple=vt,
|
||||
supported=vt >= self.MIN_SUPPORTED_VERSION)
|
||||
|
||||
|
||||
class NodeJsRuntime(JsRuntime):
|
||||
MIN_SUPPORTED_VERSION = (20, 0, 0)
|
||||
|
||||
def _info(self):
|
||||
path = _determine_runtime_path(self._path, 'node')
|
||||
out = _get_exe_version_output(path, ['--version'])
|
||||
if not out:
|
||||
return None
|
||||
version = detect_exe_version(out, r'^v(\S+)', 'unknown')
|
||||
vt = version_tuple(version, lenient=True)
|
||||
return JsRuntimeInfo(
|
||||
name='node', path=path, version=version, version_tuple=vt,
|
||||
supported=vt >= self.MIN_SUPPORTED_VERSION)
|
||||
|
||||
|
||||
class QuickJsRuntime(JsRuntime):
|
||||
MIN_SUPPORTED_VERSION = (2023, 12, 9)
|
||||
|
||||
def _info(self):
|
||||
path = _determine_runtime_path(self._path, 'qjs')
|
||||
# quickjs does not have --version and --help returns a status code of 1
|
||||
out = _get_exe_version_output(path, ['--help'], ignore_return_code=True)
|
||||
if not out:
|
||||
return None
|
||||
is_ng = 'QuickJS-ng' in out
|
||||
|
||||
version = detect_exe_version(out, r'^QuickJS(?:-ng)?\s+version\s+(\S+)', 'unknown')
|
||||
vt = version_tuple(version, lenient=True)
|
||||
if is_ng:
|
||||
return JsRuntimeInfo(
|
||||
name='quickjs-ng', path=path, version=version, version_tuple=vt,
|
||||
supported=vt > (0,))
|
||||
return JsRuntimeInfo(
|
||||
name='quickjs', path=path, version=version, version_tuple=vt,
|
||||
supported=vt >= self.MIN_SUPPORTED_VERSION)
|
||||
@@ -876,13 +876,19 @@ class Popen(subprocess.Popen):
|
||||
kwargs.setdefault('encoding', 'utf-8')
|
||||
kwargs.setdefault('errors', 'replace')
|
||||
|
||||
if shell and os.name == 'nt' and kwargs.get('executable') is None:
|
||||
if not isinstance(args, str):
|
||||
args = shell_quote(args, shell=True)
|
||||
shell = False
|
||||
# Set variable for `cmd.exe` newline escaping (see `utils.shell_quote`)
|
||||
env['='] = '"^\n\n"'
|
||||
args = f'{self.__comspec()} /Q /S /D /V:OFF /E:ON /C "{args}"'
|
||||
if os.name == 'nt' and kwargs.get('executable') is None:
|
||||
# Must apply shell escaping if we are trying to run a batch file
|
||||
# These conditions should be very specific to limit impact
|
||||
if not shell and isinstance(args, list) and args and args[0].lower().endswith(('.bat', '.cmd')):
|
||||
shell = True
|
||||
|
||||
if shell:
|
||||
if not isinstance(args, str):
|
||||
args = shell_quote(args, shell=True)
|
||||
shell = False
|
||||
# Set variable for `cmd.exe` newline escaping (see `utils.shell_quote`)
|
||||
env['='] = '"^\n\n"'
|
||||
args = f'{self.__comspec()} /Q /S /D /V:OFF /E:ON /C "{args}"'
|
||||
|
||||
super().__init__(args, *remaining, env=env, shell=shell, **kwargs, startupinfo=self._startupinfo)
|
||||
|
||||
@@ -1256,7 +1262,8 @@ def unified_strdate(date_str, day_first=True):
|
||||
return str(upload_date)
|
||||
|
||||
|
||||
def unified_timestamp(date_str, day_first=True):
|
||||
@partial_application
|
||||
def unified_timestamp(date_str, day_first=True, tz_offset=0):
|
||||
if not isinstance(date_str, str):
|
||||
return None
|
||||
|
||||
@@ -1264,7 +1271,8 @@ def unified_timestamp(date_str, day_first=True):
|
||||
r'(?i)[,|]|(mon|tues?|wed(nes)?|thu(rs)?|fri|sat(ur)?|sun)(day)?', '', date_str))
|
||||
|
||||
pm_delta = 12 if re.search(r'(?i)PM', date_str) else 0
|
||||
timezone, date_str = extract_timezone(date_str)
|
||||
timezone, date_str = extract_timezone(
|
||||
date_str, default=dt.timedelta(hours=tz_offset) if tz_offset else None)
|
||||
|
||||
# Remove AM/PM + timezone
|
||||
date_str = re.sub(r'(?i)\s*(?:AM|PM)(?:\s+[A-Z]+)?', '', date_str)
|
||||
@@ -2150,14 +2158,14 @@ def check_executable(exe, args=[]):
|
||||
return exe
|
||||
|
||||
|
||||
def _get_exe_version_output(exe, args):
|
||||
def _get_exe_version_output(exe, args, ignore_return_code=False):
|
||||
try:
|
||||
# STDIN should be redirected too. On UNIX-like systems, ffmpeg triggers
|
||||
# SIGTTOU if yt-dlp is run in the background.
|
||||
# See https://github.com/ytdl-org/youtube-dl/issues/955#issuecomment-209789656
|
||||
stdout, _, ret = Popen.run([encodeArgument(exe), *args], text=True,
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
if ret:
|
||||
if not ignore_return_code and ret:
|
||||
return None
|
||||
except OSError:
|
||||
return False
|
||||
@@ -2822,7 +2830,7 @@ def js_to_json(code, vars={}, *, strict=False):
|
||||
{STRING_RE}|
|
||||
{COMMENT_RE}|,(?={SKIP_RE}[\]}}])|
|
||||
void\s0|(?:(?<![0-9])[eE]|[a-df-zA-DF-Z_$])[.a-zA-Z_$0-9]*|
|
||||
\b(?:0[xX][0-9a-fA-F]+|0+[0-7]+)(?:{SKIP_RE}:)?|
|
||||
\b(?:0[xX][0-9a-fA-F]+|(?<!\.)0+[0-7]+)(?:{SKIP_RE}:)?|
|
||||
[0-9]+(?={SKIP_RE}:)|
|
||||
!+
|
||||
''', fix_kv, code)
|
||||
@@ -2889,8 +2897,9 @@ def limit_length(s, length):
|
||||
return s
|
||||
|
||||
|
||||
def version_tuple(v):
|
||||
return tuple(int(e) for e in re.split(r'[-.]', v))
|
||||
def version_tuple(v, *, lenient=False):
|
||||
parse = int_or_none(default=-1) if lenient else int
|
||||
return tuple(parse(e) for e in re.split(r'[-.]', v))
|
||||
|
||||
|
||||
def is_outdated_version(version, limit, assume_new=True):
|
||||
@@ -2995,6 +3004,8 @@ def mimetype2ext(mt, default=NO_DEFAULT):
|
||||
'ttaf+xml': 'dfxp',
|
||||
'ttml+xml': 'ttml',
|
||||
'x-ms-sami': 'sami',
|
||||
'x-subrip': 'srt',
|
||||
'x-srt': 'srt',
|
||||
|
||||
# misc
|
||||
'gzip': 'gz',
|
||||
@@ -4467,7 +4478,7 @@ def decode_packed_codes(code):
|
||||
symbol_table[base_n_count] = symbols[count] or base_n_count
|
||||
|
||||
return re.sub(
|
||||
r'\b(\w+)\b', lambda mobj: symbol_table[mobj.group(0)],
|
||||
r'\b(\w+)\b', lambda m: symbol_table.get(m.group(0), m.group(0)),
|
||||
obfuscated_code)
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ from .traversal import traverse_obj
|
||||
def random_user_agent():
|
||||
USER_AGENT_TMPL = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{} Safari/537.36'
|
||||
# Target versions released within the last ~6 months
|
||||
CHROME_MAJOR_VERSION_RANGE = (134, 140)
|
||||
CHROME_MAJOR_VERSION_RANGE = (137, 143)
|
||||
return USER_AGENT_TMPL.format(f'{random.randint(*CHROME_MAJOR_VERSION_RANGE)}.0.0.0')
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user