Extractor plugins are loaded from <root-dir>/ytdlp_plugins/extractor/__init__.py
Inspired by https://github.com/un-def/dl-plus
:ci skip dl
# VS Code related files
.vscode
+# SublimeText files
+*.sublime-workspace
+
+# Cookies
+cookies
cookies.txt
-*.sublime-workspace
\ No newline at end of file
+# Plugins
+ytdlp_plugins/extractor/*
+!ytdlp_plugins/extractor/__init__.py
+!ytdlp_plugins/extractor/sample.py
\ No newline at end of file
* [Filtering Formats](#filtering-formats)
* [Sorting Formats](#sorting-formats)
* [Format Selection examples](#format-selection-examples)
+* [PLUGINS](#plugins)
* [MORE](#more)
$ youtube-dlc -S '+res:480,codec,br'
```
+# PLUGINS
+Plugins are loaded from `<root-dir>/ytdlp_plugins/<type>/__init__.py`. Currently only `extractor` plugins are supported. Support for `downloader` and `postprocessor` plugins may be added in the future. See [ytdlp_plugins](ytdlp_plugins) for example.
-
+**Note**: `<root-dir>` is the directory of the binary (`<root-dir>/youtube-dlc`), or the root directory of the module if you are running directly from source-code ((`<root dir>/youtube_dlc/__main__.py`)
# MORE
For FAQ, Developer Instructions etc., see the [original README](https://github.com/ytdl-org/youtube-dl)
-py -m PyInstaller youtube_dlc\__main__.py --onefile --name youtube-dlc --version-file win\ver.txt --icon win\icon\cloud.ico --upx-exclude=vcruntime140.dll
\ No newline at end of file
+py -m PyInstaller youtube_dlc\__main__.py --onefile --name youtube-dlc --version-file win\ver.txt --icon win\icon\cloud.ico --upx-exclude=vcruntime140.dll --exclude-module ytdlp_plugins
\ No newline at end of file
process_communicate_or_kill,
)
from .cache import Cache
-from .extractor import get_info_extractor, gen_extractor_classes, _LAZY_LOADER
+from .extractor import get_info_extractor, gen_extractor_classes, _LAZY_LOADER, _PLUGIN_CLASSES
from .extractor.openload import PhantomJSwrapper
from .downloader import get_suitable_downloader
from .downloader.rtmp import rtmpdump_version
self.get_encoding()))
write_string(encoding_str, encoding=None)
- self._write_string('[debug] yt-dlp version ' + __version__ + '\n')
+ self._write_string('[debug] yt-dlp version %s\n' % __version__)
if _LAZY_LOADER:
- self._write_string('[debug] Lazy loading extractors enabled' + '\n')
+ self._write_string('[debug] Lazy loading extractors enabled\n')
+ if _PLUGIN_CLASSES:
+ self._write_string(
+ '[debug] Plugin Extractors: %s\n' % [ie.ie_key() for ie in _PLUGIN_CLASSES])
try:
sp = subprocess.Popen(
['git', 'rev-parse', '--short', 'HEAD'],
out, err = process_communicate_or_kill(sp)
out = out.decode().strip()
if re.match('[0-9a-f]+', out):
- self._write_string('[debug] Git HEAD: ' + out + '\n')
+ self._write_string('[debug] Git HEAD: %s\n' % out)
except Exception:
try:
sys.exc_clear()
from __future__ import unicode_literals
+from ..utils import load_plugins
+
try:
from .lazy_extractors import *
from .lazy_extractors import _ALL_CLASSES
_LAZY_LOADER = True
+ _PLUGIN_CLASSES = []
+
except ImportError:
_LAZY_LOADER = False
from .extractors import *
+ _PLUGIN_CLASSES = load_plugins('extractor', 'IE', globals())
+
_ALL_CLASSES = [
klass
for name, klass in globals().items()
)
from .utils import (
expand_path,
+ get_executable_path,
preferredencoding,
write_string,
)
return [], None
return config, current_path
- def get_portable_path():
- path = os.path.dirname(sys.argv[0])
- if os.path.abspath(sys.argv[0]) != os.path.abspath(sys.executable): # Not packaged
- path = os.path.join(path, '..')
- return os.path.abspath(path)
-
- configs['portable'], paths['portable'] = read_options(get_portable_path())
+ configs['portable'], paths['portable'] = read_options(get_executable_path())
if '--ignore-config' in configs['portable']:
return
import errno
import functools
import gzip
+import imp
import io
import itertools
import json
if callable(to_screen) is not None:
to_screen('unable to create directory ' + error_to_compat_str(err))
return False
+
+
+def get_executable_path():
+ path = os.path.dirname(sys.argv[0])
+ if os.path.abspath(sys.argv[0]) != os.path.abspath(sys.executable): # Not packaged
+ path = os.path.join(path, '..')
+ return os.path.abspath(path)
+
+
+def load_plugins(name, type, namespace):
+ plugin_info = [None]
+ classes = []
+ try:
+ plugin_info = imp.find_module(
+ name, [os.path.join(get_executable_path(), 'ytdlp_plugins')])
+ plugins = imp.load_module(name, *plugin_info)
+ for name in dir(plugins):
+ if not name.endswith(type):
+ continue
+ klass = getattr(plugins, name)
+ classes.append(klass)
+ namespace[name] = klass
+ except ImportError:
+ pass
+ finally:
+ if plugin_info[0] is not None:
+ plugin_info[0].close()
+ return classes
--- /dev/null
+# flake8: noqa
+from .sample import SamplePluginIE
--- /dev/null
+from __future__ import unicode_literals
+
+from youtube_dlc.extractor.common import InfoExtractor
+
+
+class SamplePluginIE(InfoExtractor):
+ _WORKING = False
+ IE_DESC = False
+ _VALID_URL = r'^sampleplugin:'
+
+ def _real_extract(self, url):
+ self.to_screen('URL "%s" sucessfully captured' % url)