From f74980cbaebaf3c4ea89d1b257424a50545991d9 Mon Sep 17 00:00:00 2001 From: pukkandan Date: Sun, 24 Jan 2021 19:10:02 +0530 Subject: [PATCH] Plugin support Extractor plugins are loaded from /ytdlp_plugins/extractor/__init__.py Inspired by https://github.com/un-def/dl-plus :ci skip dl --- .gitignore | 10 +++++++++- README.md | 5 ++++- make_win.bat | 2 +- youtube_dlc/YoutubeDL.py | 11 +++++++---- youtube_dlc/extractor/__init__.py | 6 ++++++ youtube_dlc/options.py | 9 ++------- youtube_dlc/utils.py | 29 +++++++++++++++++++++++++++++ ytdlp_plugins/extractor/__init__.py | 2 ++ ytdlp_plugins/extractor/sample.py | 12 ++++++++++++ 9 files changed, 72 insertions(+), 14 deletions(-) create mode 100644 ytdlp_plugins/extractor/__init__.py create mode 100644 ytdlp_plugins/extractor/sample.py diff --git a/.gitignore b/.gitignore index 9ee6e91cf..189ada254 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 a2ddc3db5..59999245b 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ This is a fork of [youtube-dlc](https://github.com/blackjack4494/yt-dlc) which i * [Filtering Formats](#filtering-formats) * [Sorting Formats](#sorting-formats) * [Format Selection examples](#format-selection-examples) +* [PLUGINS](#plugins) * [MORE](#more) @@ -1082,9 +1083,11 @@ $ youtube-dlc -S 'res:720,fps' $ 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 c35d9937e..a3d98155b 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 b45b1bbba..02cc97625 100644 --- a/youtube_dlc/YoutubeDL.py +++ b/youtube_dlc/YoutubeDL.py @@ -105,7 +105,7 @@ from .utils import ( 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 @@ class YoutubeDL(object): 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 @@ class YoutubeDL(object): 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 18d8dbcd6..56251384d 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 7a18f0f84..97e8964d6 100644 --- a/youtube_dlc/options.py +++ b/youtube_dlc/options.py @@ -15,6 +15,7 @@ from .compat import ( ) from .utils import ( expand_path, + get_executable_path, preferredencoding, write_string, ) @@ -1226,13 +1227,7 @@ def parseOpts(overrideArguments=None): 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 6740f0cdb..34a14424a 100644 --- a/youtube_dlc/utils.py +++ b/youtube_dlc/utils.py @@ -16,6 +16,7 @@ import email.header 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 000000000..e1a83b909 --- /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 000000000..41954b6be --- /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)