diff --git a/.gitignore b/.gitignore index 9ee6e91cf7..189ada254b 100644 --- a/.gitignore +++ b/.gitignore @@ -65,6 +65,14 @@ venv/ # 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 diff --git a/README.md b/README.md index a2ddc3db5f..59999245bf 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ # YT-DLP * [Filtering Formats](#filtering-formats) * [Sorting Formats](#sorting-formats) * [Format Selection examples](#format-selection-examples) +* [PLUGINS](#plugins) * [MORE](#more) @@ -1082,9 +1083,11 @@ # prefering better codec and then larger total bitrate for the same resolution $ youtube-dlc -S '+res:480,codec,br' ``` +# PLUGINS +Plugins are loaded from `/ytdlp_plugins//__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**: `` is the directory of the binary (`/youtube-dlc`), or the root directory of the module if you are running directly from source-code ((`/youtube_dlc/__main__.py`) # MORE For FAQ, Developer Instructions etc., see the [original README](https://github.com/ytdl-org/youtube-dl) diff --git a/make_win.bat b/make_win.bat index c35d9937e4..a3d98155ba 100644 --- a/make_win.bat +++ b/make_win.bat @@ -1 +1 @@ -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 diff --git a/youtube_dlc/YoutubeDL.py b/youtube_dlc/YoutubeDL.py index b45b1bbbad..02cc976259 100644 --- a/youtube_dlc/YoutubeDL.py +++ b/youtube_dlc/YoutubeDL.py @@ -105,7 +105,7 @@ 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 @@ -2652,9 +2652,12 @@ def print_debug_header(self): 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'], @@ -2663,7 +2666,7 @@ def print_debug_header(self): 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() diff --git a/youtube_dlc/extractor/__init__.py b/youtube_dlc/extractor/__init__.py index 18d8dbcd66..56251384d8 100644 --- a/youtube_dlc/extractor/__init__.py +++ b/youtube_dlc/extractor/__init__.py @@ -1,13 +1,19 @@ 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() diff --git a/youtube_dlc/options.py b/youtube_dlc/options.py index 7a18f0f847..97e8964d61 100644 --- a/youtube_dlc/options.py +++ b/youtube_dlc/options.py @@ -15,6 +15,7 @@ ) from .utils import ( expand_path, + get_executable_path, preferredencoding, write_string, ) @@ -1226,13 +1227,7 @@ def read_options(path, user=False): 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 diff --git a/youtube_dlc/utils.py b/youtube_dlc/utils.py index 6740f0cdb4..34a14424ac 100644 --- a/youtube_dlc/utils.py +++ b/youtube_dlc/utils.py @@ -16,6 +16,7 @@ import errno import functools import gzip +import imp import io import itertools import json @@ -5905,3 +5906,31 @@ def make_dir(path, to_screen=None): 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 diff --git a/ytdlp_plugins/extractor/__init__.py b/ytdlp_plugins/extractor/__init__.py new file mode 100644 index 0000000000..e1a83b909d --- /dev/null +++ b/ytdlp_plugins/extractor/__init__.py @@ -0,0 +1,2 @@ +# flake8: noqa +from .sample import SamplePluginIE diff --git a/ytdlp_plugins/extractor/sample.py b/ytdlp_plugins/extractor/sample.py new file mode 100644 index 0000000000..41954b6be3 --- /dev/null +++ b/ytdlp_plugins/extractor/sample.py @@ -0,0 +1,12 @@ +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)