Plugin support

Extractor plugins are loaded from <root-dir>/ytdlp_plugins/extractor/__init__.py

Inspired by https://github.com/un-def/dl-plus

:ci skip dl
This commit is contained in:
pukkandan 2021-01-24 19:10:02 +05:30
parent c571435f9c
commit f74980cbae
9 changed files with 72 additions and 14 deletions

10
.gitignore vendored
View File

@ -65,6 +65,14 @@ venv/
# VS Code related files # VS Code related files
.vscode .vscode
# SublimeText files
*.sublime-workspace
# Cookies
cookies
cookies.txt cookies.txt
*.sublime-workspace # Plugins
ytdlp_plugins/extractor/*
!ytdlp_plugins/extractor/__init__.py
!ytdlp_plugins/extractor/sample.py

View File

@ -40,6 +40,7 @@ # YT-DLP
* [Filtering Formats](#filtering-formats) * [Filtering Formats](#filtering-formats)
* [Sorting Formats](#sorting-formats) * [Sorting Formats](#sorting-formats)
* [Format Selection examples](#format-selection-examples) * [Format Selection examples](#format-selection-examples)
* [PLUGINS](#plugins)
* [MORE](#more) * [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' $ 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 # MORE
For FAQ, Developer Instructions etc., see the [original README](https://github.com/ytdl-org/youtube-dl) For FAQ, Developer Instructions etc., see the [original README](https://github.com/ytdl-org/youtube-dl)

View File

@ -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 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

View File

@ -105,7 +105,7 @@
process_communicate_or_kill, process_communicate_or_kill,
) )
from .cache import Cache 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 .extractor.openload import PhantomJSwrapper
from .downloader import get_suitable_downloader from .downloader import get_suitable_downloader
from .downloader.rtmp import rtmpdump_version from .downloader.rtmp import rtmpdump_version
@ -2652,9 +2652,12 @@ def print_debug_header(self):
self.get_encoding())) self.get_encoding()))
write_string(encoding_str, encoding=None) 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: 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: try:
sp = subprocess.Popen( sp = subprocess.Popen(
['git', 'rev-parse', '--short', 'HEAD'], ['git', 'rev-parse', '--short', 'HEAD'],
@ -2663,7 +2666,7 @@ def print_debug_header(self):
out, err = process_communicate_or_kill(sp) out, err = process_communicate_or_kill(sp)
out = out.decode().strip() out = out.decode().strip()
if re.match('[0-9a-f]+', out): 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: except Exception:
try: try:
sys.exc_clear() sys.exc_clear()

View File

@ -1,13 +1,19 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from ..utils import load_plugins
try: try:
from .lazy_extractors import * from .lazy_extractors import *
from .lazy_extractors import _ALL_CLASSES from .lazy_extractors import _ALL_CLASSES
_LAZY_LOADER = True _LAZY_LOADER = True
_PLUGIN_CLASSES = []
except ImportError: except ImportError:
_LAZY_LOADER = False _LAZY_LOADER = False
from .extractors import * from .extractors import *
_PLUGIN_CLASSES = load_plugins('extractor', 'IE', globals())
_ALL_CLASSES = [ _ALL_CLASSES = [
klass klass
for name, klass in globals().items() for name, klass in globals().items()

View File

@ -15,6 +15,7 @@
) )
from .utils import ( from .utils import (
expand_path, expand_path,
get_executable_path,
preferredencoding, preferredencoding,
write_string, write_string,
) )
@ -1226,13 +1227,7 @@ def read_options(path, user=False):
return [], None return [], None
return config, current_path return config, current_path
def get_portable_path(): configs['portable'], paths['portable'] = read_options(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)
configs['portable'], paths['portable'] = read_options(get_portable_path())
if '--ignore-config' in configs['portable']: if '--ignore-config' in configs['portable']:
return return

View File

@ -16,6 +16,7 @@
import errno import errno
import functools import functools
import gzip import gzip
import imp
import io import io
import itertools import itertools
import json import json
@ -5905,3 +5906,31 @@ def make_dir(path, to_screen=None):
if callable(to_screen) is not None: if callable(to_screen) is not None:
to_screen('unable to create directory ' + error_to_compat_str(err)) to_screen('unable to create directory ' + error_to_compat_str(err))
return False 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

View File

@ -0,0 +1,2 @@
# flake8: noqa
from .sample import SamplePluginIE

View File

@ -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)