mirror of
https://github.com/yt-dlp/yt-dlp
synced 2024-12-26 21:59:08 +01:00
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:
parent
c571435f9c
commit
f74980cbae
9 changed files with 72 additions and 14 deletions
10
.gitignore
vendored
10
.gitignore
vendored
|
@ -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
|
|
@ -40,6 +40,7 @@ This is a fork of [youtube-dlc](https://github.com/blackjack4494/yt-dlc) which i
|
||||||
* [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 @@ $ youtube-dlc -S 'res:720,fps'
|
||||||
$ 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)
|
||||||
|
|
|
@ -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
|
|
@ -105,7 +105,7 @@ from .utils import (
|
||||||
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 @@ class YoutubeDL(object):
|
||||||
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 @@ class YoutubeDL(object):
|
||||||
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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -15,6 +15,7 @@ from .compat import (
|
||||||
)
|
)
|
||||||
from .utils import (
|
from .utils import (
|
||||||
expand_path,
|
expand_path,
|
||||||
|
get_executable_path,
|
||||||
preferredencoding,
|
preferredencoding,
|
||||||
write_string,
|
write_string,
|
||||||
)
|
)
|
||||||
|
@ -1226,13 +1227,7 @@ def parseOpts(overrideArguments=None):
|
||||||
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
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import email.header
|
||||||
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
|
||||||
|
|
2
ytdlp_plugins/extractor/__init__.py
Normal file
2
ytdlp_plugins/extractor/__init__.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# flake8: noqa
|
||||||
|
from .sample import SamplePluginIE
|
12
ytdlp_plugins/extractor/sample.py
Normal file
12
ytdlp_plugins/extractor/sample.py
Normal 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)
|
Loading…
Reference in a new issue