mirror of https://github.com/yt-dlp/yt-dlp.git
Add option `--plugin-dirs` (#11277)
Closes #3260 Authored by: imranh2, coletdjnz Co-authored-by: coletdjnz <coletdjnz@protonmail.com>
This commit is contained in:
parent
8de431ec97
commit
0f593dca9f
|
@ -348,6 +348,13 @@ If you fork the project on GitHub, you can run your fork's [build workflow](.git
|
||||||
containing directory ("-" for stdin). Can be
|
containing directory ("-" for stdin). Can be
|
||||||
used multiple times and inside other
|
used multiple times and inside other
|
||||||
configuration files
|
configuration files
|
||||||
|
--plugin-dirs PATH Path to an additional directory to search
|
||||||
|
for plugins. This option can be used
|
||||||
|
multiple times to add multiple directories.
|
||||||
|
Note that this currently only works for
|
||||||
|
extractor plugins; postprocessor plugins can
|
||||||
|
only be loaded from the default plugin
|
||||||
|
directories
|
||||||
--flat-playlist Do not extract the videos of a playlist,
|
--flat-playlist Do not extract the videos of a playlist,
|
||||||
only list them
|
only list them
|
||||||
--no-flat-playlist Fully extract the videos of a playlist
|
--no-flat-playlist Fully extract the videos of a playlist
|
||||||
|
|
|
@ -10,6 +10,7 @@ TEST_DATA_DIR = Path(os.path.dirname(os.path.abspath(__file__)), 'testdata')
|
||||||
sys.path.append(str(TEST_DATA_DIR))
|
sys.path.append(str(TEST_DATA_DIR))
|
||||||
importlib.invalidate_caches()
|
importlib.invalidate_caches()
|
||||||
|
|
||||||
|
from yt_dlp.utils import Config
|
||||||
from yt_dlp.plugins import PACKAGE_NAME, directories, load_plugins
|
from yt_dlp.plugins import PACKAGE_NAME, directories, load_plugins
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,6 +69,24 @@ class TestPlugins(unittest.TestCase):
|
||||||
os.remove(zip_path)
|
os.remove(zip_path)
|
||||||
importlib.invalidate_caches() # reset the import caches
|
importlib.invalidate_caches() # reset the import caches
|
||||||
|
|
||||||
|
def test_plugin_dirs(self):
|
||||||
|
# Internal plugin dirs hack for CLI --plugin-dirs
|
||||||
|
# To be replaced with proper system later
|
||||||
|
custom_plugin_dir = TEST_DATA_DIR / 'plugin_packages'
|
||||||
|
Config._plugin_dirs = [str(custom_plugin_dir)]
|
||||||
|
importlib.invalidate_caches() # reset the import caches
|
||||||
|
|
||||||
|
try:
|
||||||
|
package = importlib.import_module(f'{PACKAGE_NAME}.extractor')
|
||||||
|
self.assertIn(custom_plugin_dir / 'testpackage' / PACKAGE_NAME / 'extractor', map(Path, package.__path__))
|
||||||
|
|
||||||
|
plugins_ie = load_plugins('extractor', 'IE')
|
||||||
|
self.assertIn('PackagePluginIE', plugins_ie.keys())
|
||||||
|
|
||||||
|
finally:
|
||||||
|
Config._plugin_dirs = []
|
||||||
|
importlib.invalidate_caches() # reset the import caches
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
5
test/testdata/plugin_packages/testpackage/yt_dlp_plugins/extractor/package.py
vendored
Normal file
5
test/testdata/plugin_packages/testpackage/yt_dlp_plugins/extractor/package.py
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from yt_dlp.extractor.common import InfoExtractor
|
||||||
|
|
||||||
|
|
||||||
|
class PackagePluginIE(InfoExtractor):
|
||||||
|
pass
|
|
@ -34,6 +34,7 @@ from .postprocessor import (
|
||||||
)
|
)
|
||||||
from .update import Updater
|
from .update import Updater
|
||||||
from .utils import (
|
from .utils import (
|
||||||
|
Config,
|
||||||
NO_DEFAULT,
|
NO_DEFAULT,
|
||||||
POSTPROCESS_WHEN,
|
POSTPROCESS_WHEN,
|
||||||
DateRange,
|
DateRange,
|
||||||
|
@ -967,6 +968,10 @@ def _real_main(argv=None):
|
||||||
|
|
||||||
parser, opts, all_urls, ydl_opts = parse_options(argv)
|
parser, opts, all_urls, ydl_opts = parse_options(argv)
|
||||||
|
|
||||||
|
# HACK: Set the plugin dirs early on
|
||||||
|
# TODO(coletdjnz): remove when plugin globals system is implemented
|
||||||
|
Config._plugin_dirs = opts.plugin_dirs
|
||||||
|
|
||||||
# Dump user agent
|
# Dump user agent
|
||||||
if opts.dump_user_agent:
|
if opts.dump_user_agent:
|
||||||
ua = traverse_obj(opts.headers, 'User-Agent', casesense=False, default=std_headers['User-Agent'])
|
ua = traverse_obj(opts.headers, 'User-Agent', casesense=False, default=std_headers['User-Agent'])
|
||||||
|
|
|
@ -408,6 +408,14 @@ def create_parser():
|
||||||
help=(
|
help=(
|
||||||
'Location of the main configuration file; either the path to the config or its containing directory '
|
'Location of the main configuration file; either the path to the config or its containing directory '
|
||||||
'("-" for stdin). Can be used multiple times and inside other configuration files'))
|
'("-" for stdin). Can be used multiple times and inside other configuration files'))
|
||||||
|
general.add_option(
|
||||||
|
'--plugin-dirs',
|
||||||
|
dest='plugin_dirs', metavar='PATH', action='append',
|
||||||
|
help=(
|
||||||
|
'Path to an additional directory to search for plugins. '
|
||||||
|
'This option can be used multiple times to add multiple directories. '
|
||||||
|
'Note that this currently only works for extractor plugins; '
|
||||||
|
'postprocessor plugins can only be loaded from the default plugin directories'))
|
||||||
general.add_option(
|
general.add_option(
|
||||||
'--flat-playlist',
|
'--flat-playlist',
|
||||||
action='store_const', dest='extract_flat', const='in_playlist', default=False,
|
action='store_const', dest='extract_flat', const='in_playlist', default=False,
|
||||||
|
|
|
@ -15,6 +15,7 @@ from zipfile import ZipFile
|
||||||
|
|
||||||
from .compat import functools # isort: split
|
from .compat import functools # isort: split
|
||||||
from .utils import (
|
from .utils import (
|
||||||
|
Config,
|
||||||
get_executable_path,
|
get_executable_path,
|
||||||
get_system_config_dirs,
|
get_system_config_dirs,
|
||||||
get_user_config_dirs,
|
get_user_config_dirs,
|
||||||
|
@ -84,6 +85,12 @@ class PluginFinder(importlib.abc.MetaPathFinder):
|
||||||
with contextlib.suppress(ValueError): # Added when running __main__.py directly
|
with contextlib.suppress(ValueError): # Added when running __main__.py directly
|
||||||
candidate_locations.remove(Path(__file__).parent)
|
candidate_locations.remove(Path(__file__).parent)
|
||||||
|
|
||||||
|
# TODO(coletdjnz): remove when plugin globals system is implemented
|
||||||
|
if Config._plugin_dirs:
|
||||||
|
candidate_locations.extend(_get_package_paths(
|
||||||
|
*Config._plugin_dirs,
|
||||||
|
containing_folder=''))
|
||||||
|
|
||||||
parts = Path(*fullname.split('.'))
|
parts = Path(*fullname.split('.'))
|
||||||
for path in orderedSet(candidate_locations, lazy=True):
|
for path in orderedSet(candidate_locations, lazy=True):
|
||||||
candidate = path / parts
|
candidate = path / parts
|
||||||
|
|
|
@ -4897,6 +4897,10 @@ class Config:
|
||||||
filename = None
|
filename = None
|
||||||
__initialized = False
|
__initialized = False
|
||||||
|
|
||||||
|
# Internal only, do not use! Hack to enable --plugin-dirs
|
||||||
|
# TODO(coletdjnz): remove when plugin globals system is implemented
|
||||||
|
_plugin_dirs = None
|
||||||
|
|
||||||
def __init__(self, parser, label=None):
|
def __init__(self, parser, label=None):
|
||||||
self.parser, self.label = parser, label
|
self.parser, self.label = parser, label
|
||||||
self._loaded_paths, self.configs = set(), []
|
self._loaded_paths, self.configs = set(), []
|
||||||
|
|
Loading…
Reference in New Issue