-
Notifications
You must be signed in to change notification settings - Fork 1.1k
yt dlp plugins
yt-dlp can be extended with plugins: extra extractors (support for a site, or a different way of handling one) and postprocessors. MeTube runs yt-dlp inside its own process, so a plugin that yt-dlp can find is used by every download, playlist scan and subscription check without any change to MeTube.
yt-dlp only looks for plugins inside a package named yt_dlp_plugins, so the folder layout matters. A plugin file placed anywhere else is silently ignored. Create a folder like this next to your compose file:
plugins/
└── yt_dlp_plugins/
├── extractor/
│ └── my_extractor.py
└── postprocessor/
└── my_postprocessor.py
Only the subfolders you need have to exist, and no __init__.py files are required. Then mount the folder and add it to Python's module path:
services:
metube:
image: ghcr.io/alexta69/metube
environment:
- PYTHONPATH=/plugins
volumes:
- ./downloads:/downloads
- ./plugins:/plugins:roRestart the container after adding or changing a plugin.
Contributed by @marcelmanz in #1084, based on @coletdjnz's example in yt-dlp/yt-dlp#7403.
yt-dlp recognises links from a fixed list of public Invidious instances as YouTube. A link copied from your own instance, like https://yt.example.com/watch?v=..., is treated as an ordinary web page, and fails. If the instance is behind a login, yt-dlp ends up on the login page instead of the video.
Front-ends like these use the same link paths as YouTube (/watch?v=, /playlist?list=, /channel/...), so this extractor just swaps the host for www.youtube.com before yt-dlp handles the link. Save it as plugins/yt_dlp_plugins/extractor/youtube_frontend.py, and change the pattern to match your instance's hostname:
from yt_dlp.extractor.common import InfoExtractor
class YoutubeInvidiousRedirectIE(InfoExtractor):
INVIDIOUS_URLS = (r'(?:www\.)?yt\.example\.com',) # your instance's hostname, as a regex
_VALID_URL = r'https?://(?P<invidious_base>{})'.format('|'.join(INVIDIOUS_URLS))
def _real_extract(self, url):
invidious_base = self._match_valid_url(url).group('invidious_base')
return self.url_result(url.replace(invidious_base, 'www.youtube.com'))To list more than one instance, add more patterns to INVIDIOUS_URLS.
With the plugin loaded, a pasted front-end link appears in the queue as the matching www.youtube.com link, and this works for links sent from bookmarklets or other apps, and for subscriptions, too. If the add instead fails with an error starting [generic], yt-dlp did not load the plugin: check the folder layout above.
Front-ends on a private address. Before yt-dlp sees a link, MeTube checks the link's host and refuses hosts that resolve to private, loopback or other internal addresses (see ALLOW_PRIVATE_ADDRESSES in the README). That check runs before the plugin can rewrite the link, so if your front-end is only reachable on your local network or over a VPN such as Tailscale, MeTube refuses its links (Refusing to fetch internal host). Setting ALLOW_PRIVATE_ADDRESSES=true gets past this but turns the protection off entirely. The alternative is to send the YouTube link in the first place: see Sending links from a YouTube front-end for a bookmarklet that does this.
Using MeTube
Configuring downloads
- yt-dlp options
- YTDL_OPTIONS Cookbook
- Output templates
- OUTPUT_TEMPLATE Cookbook
- GPU transcoding
- yt-dlp plugins
Deployment
Help