1
0
mirror of https://github.com/yt-dlp/yt-dlp.git synced 2025-07-12 16:28:31 +00:00

jsi override

This commit is contained in:
c-basalt 2025-05-17 10:18:59 -04:00
parent d74f921e37
commit f058874929
6 changed files with 69 additions and 8 deletions

View File

@ -124,6 +124,11 @@ def test_jsi_runtime_classes(self):
self.assertIn(f'{PACKAGE_NAME}.jsinterp.normal', sys.modules.keys())
self.assertIn('NormalPluginJSI', plugin_jsis.value)
self.assertNotIn('OverrideDenoJSI', plugins_jsi.keys())
self.assertNotIn('OverrideDenoJSI', plugin_jsis.value)
self.assertNotIn('_UnderscoreOverrideDenoJSI', plugins_jsi.keys())
self.assertNotIn('_UnderscoreOverrideDenoJSI', plugin_jsis.value)
def test_importing_zipped_module(self):
zip_path = TEST_DATA_DIR / 'zipped_plugins.zip'
shutil.make_archive(str(zip_path)[:-4], 'zip', str(zip_path)[:-4])
@ -209,6 +214,24 @@ def test_extractor_override_plugin(self):
from yt_dlp.extractor.generic import GenericIE
self.assertEqual(GenericIE.IE_NAME, 'generic+override+underscore-override')
def test_jsi_override_plugin(self):
load_plugins(JSI_PLUGIN_SPEC)
from yt_dlp.jsinterp._deno import DenoJSI
# test that jsi_runtimes is updated with override jsi
self.assertTrue(DenoJSI is jsi_runtimes.value['Deno'])
self.assertEqual(jsi_runtimes.value['Deno'].TEST_FIELD, 'override')
self.assertEqual(jsi_runtimes.value['Deno'].SECONDARY_TEST_FIELD, 'underscore-override')
self.assertEqual(jsi_runtimes.value['Deno'].JSI_NAME, 'Deno+override+underscore-override')
importlib.invalidate_caches()
# test that loading a second time doesn't wrap a second time
load_plugins(EXTRACTOR_PLUGIN_SPEC)
from yt_dlp.jsinterp._deno import DenoJSI
self.assertTrue(DenoJSI is jsi_runtimes.value['Deno'])
self.assertEqual(jsi_runtimes.value['Deno'].JSI_NAME, 'Deno+override+underscore-override')
def test_load_all_plugin_types(self):
# no plugin specs registered

View File

@ -0,0 +1,5 @@
from yt_dlp.jsinterp._deno import DenoJSI
class OverrideDenoJSI(DenoJSI, plugin_name='override'):
TEST_FIELD = 'override'

View File

@ -0,0 +1,5 @@
from yt_dlp.jsinterp._deno import DenoJSI
class _UnderscoreOverrideDenoJSI(DenoJSI, plugin_name='underscore-override'):
SECONDARY_TEST_FIELD = 'underscore-override'

View File

@ -39,6 +39,8 @@
plugin_ies,
plugin_ies_overrides,
plugin_pps,
plugin_jsis,
plugin_jsis_overrides,
all_plugins_loaded,
plugin_dirs,
)
@ -4090,13 +4092,17 @@ def get_encoding(stream):
write_debug(f'Proxy map: {self.proxies}')
write_debug(f'Request Handlers: {", ".join(rh.RH_NAME for rh in self._request_director.handlers.values())}')
for plugin_type, plugins in (('Extractor', plugin_ies), ('Post-Processor', plugin_pps)):
for plugin_type, plugins in (('Extractor', plugin_ies), ('Post-Processor', plugin_pps),
('JSI-Runtime', plugin_jsis)):
display_list = [
klass.__name__ if klass.__name__ == name else f'{klass.__name__} as {name}'
for name, klass in plugins.value.items()]
if plugin_type == 'Extractor':
display_list.extend(f'{plugins[-1].IE_NAME.partition("+")[2]} ({parent.__name__})'
for parent, plugins in plugin_ies_overrides.value.items())
elif plugin_type == 'JSI-Runtime':
display_list.extend(f'{plugins[-1].JSI_NAME.partition("+")[2]} ({parent.__name__})'
for parent, plugins in plugin_jsis_overrides.value.items())
if not display_list:
continue
write_debug(f'{plugin_type} Plugins: {", ".join(sorted(display_list))}')

View File

@ -26,6 +26,7 @@ def __repr__(self, /):
plugin_pps = Indirect({})
plugin_jsis = Indirect({})
plugin_ies_overrides = Indirect(defaultdict(list))
plugin_jsis_overrides = Indirect(defaultdict(list))
# Misc
IN_CLI = Indirect(False)

View File

@ -2,9 +2,10 @@
import abc
import inspect
import sys
import typing
from ..globals import jsi_runtimes
from ..globals import jsi_runtimes, plugin_jsis_overrides
from ..extractor.common import InfoExtractor
from ..utils import (
classproperty,
@ -214,24 +215,44 @@ def __init__(self, downloader: YoutubeDL, url: str, timeout: float | int, user_a
self.timeout = timeout
self.user_agent: str = user_agent or self._downloader.params['http_headers']['User-Agent']
@classmethod
def __init_subclass__(cls, *, plugin_name=None, **kwargs):
if plugin_name:
mro = inspect.getmro(cls)
next_mro_class = super_class = mro[mro.index(cls) + 1]
while getattr(super_class, '__wrapped__', None):
super_class = super_class.__wrapped__
if not any(override.PLUGIN_NAME == plugin_name for override in plugin_jsis_overrides.value[super_class]):
cls.__wrapped__ = next_mro_class
cls.PLUGIN_NAME, cls.JSI_KEY = plugin_name, next_mro_class.JSI_KEY
cls.JSI_NAME = f'{next_mro_class.JSI_NAME}+{plugin_name}'
setattr(sys.modules[super_class.__module__], super_class.__name__, cls)
# additional update jsi_runtime because jsis are not further loaded like extractors
jsi_runtimes.value[super_class.JSI_KEY] = cls
plugin_jsis_overrides.value[super_class].append(cls)
return super().__init_subclass__(**kwargs)
@abc.abstractmethod
def is_available(self) -> bool:
raise NotImplementedError
def write_debug(self, message, *args, **kwargs):
self._downloader.write_debug(f'[{self.JSI_KEY}] {message}', *args, **kwargs)
def write_debug(self, msg, *args, **kwargs):
self._downloader.write_debug(f'[{self.JSI_NAME}] {msg}', *args, **kwargs)
def report_warning(self, message, *args, **kwargs):
self._downloader.report_warning(f'[{self.JSI_KEY}] {message}', *args, **kwargs)
def report_warning(self, msg, *args, **kwargs):
self._downloader.report_warning(f'[{self.JSI_NAME}] {msg}', *args, **kwargs)
def to_screen(self, msg, *args, **kwargs):
self._downloader.to_screen(f'[{self.JSI_KEY}] {msg}', *args, **kwargs)
self._downloader.to_screen(f'[{self.JSI_NAME}] {msg}', *args, **kwargs)
def report_note(self, video_id, note):
self.to_screen(f'{format_field(video_id, None, "%s: ")}{note}')
def report_version(self):
raise NotImplementedError
pass
@classmethod
def supports_extractor(cls, ie_key: str):