Loading repository data…
Loading repository data…
fe80Grau / repository
A little script to serve Youtube / Twitch / Crunchyroll videos without storage it. Uses yt-dlp HTTP data throught Flask and dynamic URLs. We can use this dynamic URLs to set STRM files.
A transparent discovery signal based on current public GitHub metadata.
This score does not audit code, security, maintainers, documentation quality, or suitability. Verify the repository and its current documentation before adoption.
PATH (ffmpeg -version from a terminal should work).PATH as a binary (yt-dlp --version must work from a terminal), because ytdlp2STRM calls it as an external process.
pip install -r requierments.txt installs the Python package, but on some systems the entry-point binary is not picked up on PATH. If that happens, install it as a binary instead:
winget install yt-dlp.yt-dlpsudo apt install yt-dlp or grab the release binary from the yt-dlp repo.brew install yt-dlp.cd /opt && git clone https://github.com/fe80Grau/ytdlp2STRM.git
cd /opt/ytdlp2STRM && pip install -r requierments.txt
sudo cp config/ytdlp2strm.service /etc/systemd/system/ytdlp2strm.service
sudo systemctl enable ytdlp2strm.service
sudo systemctl start ytdlp2strm.service
sudo systemctl status ytdlp2strm.service
http://localhost:5000/
cd C:\ProgramData; git clone https://github.com/fe80Grau/ytdlp2STRM.git;
cd C:\ProgramData\ytdlp2STRM; pip install -r requierments.txt
schtasks.exe /create /tn "ytdlp2STRM" /xml C:\ProgramData\ytdlp2STRM\config\MS-TASK-ytdlp2STRM.xml
schtasks.exe /run /tn "ytdlp2STRM"
(Get-ScheduledTask | Where TaskName -eq ytdlp2STRM ).State
http://localhost:5000/
To deploy this as a Docker container, follow these steps in the ytdlp2STRM root folder.
docker build . -t "ytdlp2strm"
docker create --name ytdlp2strm-data ytdlp2strm
docker run -p 5005:5000 --restart=always -d -v D:\media:/media --volumes-from ytdlp2strm-data --name ytdlp2STRM ytdlp2strm
http://localhost:5005/
Check the GUI in the browser
http://localhost:5001/
---
services:
ytdlp2strm:
image: fe80grau/ytdlp2strm
container_name: ytdlp2STRM
environment:
- AM_I_IN_A_DOCKER_CONTAINER=Yes
- DOCKER_PORT=5005
volumes:
- /local/path/to/media:/media
- /local/path/to/channel_list.json:/opt/ytdlp2STRM/plugins/youtube/channel_list.json
- /local/path/to/yt_config.json:/opt/ytdlp2STRM/plugins/youtube/config.json
- /local/path/to/config.json:/opt/ytdlp2STRM/config/config.json
- /local/path/to/crons.json:/opt/ytdlp2STRM/config/crons.json
- ytdlp2strm-data:/opt/ytdlp2STRM
ports:
- 5005:5000
restart: always
volumes:
ytdlp2strm-data:
Where:
/local/path/to/media is the local folder where the .strm file will be created/local/path/to/config.json is optional and will set your ytdlp2STRM general settings; formatted as config.example.json/local/path/to/crons.json is optional and will set your ytdlp2STRM cronjob settings; formatted as crons.example.json/local/path/to/channel_list.json is optional and will set your Youtube channel list; formatted as channel_list.example.json/local/path/to/yt_config.json is optional and will set your Youtube plugin settings; formatted as config.example.jsonjellyfin_preload was "False": the Crunchyroll plugin used bool(config['jellyfin_preload']), but bool("False") returns True in Python (any non-empty string is truthy). This caused the Jellyfin preload daemon to run and spam HTTP errors every minute even with the feature disabled. The check now uses str(value).lower() == 'true', matching the rest of the codebase./youtube/direct/<id>, /twitch/bridge/<id>, etc.) now validate the media ID against an allowlist ([A-Za-z0-9_.-], no leading -) before passing it to yt-dlp, returning HTTP 400 on invalid input.eval() in cli.py: plugin method resolution now uses getattr instead of eval() on user-supplied strings, preventing code injection.ytdlp2strm_host: main.py reads ytdlp2strm_host from config.json instead of hardcoding 0.0.0.0. The default config now ships with 127.0.0.1, reducing accidental exposure of the admin UI to the network.ytdlp2strm_web_username and ytdlp2strm_web_password in config.json protects the dashboard (/, /general, /plugin, /crons, /log, /restart_service) while leaving streaming endpoints open for Jellyfin/Emby/Kodi. Streaming routes (/youtube/*, /twitch/*, etc.) remain unauthenticated so players keep working..nfo, .png, .vtt and .srt files under a new episode number with no .strm next to them, while the real .strm lived at the original number. The existence check now runs before the new title/episode number is computed and reuses the path of the existing .strm for subtitle and metadata refreshes. The lookup also tolerates unreadable/locked files.video_quality change ignored until cache TTL expired: the direct-mode HLS cache (in memory and in <strm_output_folder>/.direct_cache) was keyed only by youtube_id + lang, so a previously-resolved low-quality variant kept being served after changing video_quality in plugins/youtube/config.json. The quality is now part of the cache key and filename, so changing the setting invalidates the previous caches immediately.scripts/clean_youtube_orphans.py removes companion files (.nfo, .png/.jpg/.webp, .vtt, .srt, .ass) that no longer have a matching .strm in the same folder. Runs in dry-run by default; pass --delete to actually remove files.
python scripts/clean_youtube_orphans.py "/media/Youtube" # dry-run
python scripts/clean_youtube_orphans.py "/media/Youtube" --delete # actually delete
version.py (__version__) and injected into every UI template as app_version, instead of being hardcoded in each HTML page and in cli.py. To cut a release just bump version.py.iframe mode: new generation method for the YouTube plugin. The .strm file is written with the public YouTube watch URL (https://www.youtube.com/watch?v=<id>) instead of a ytdlp2STRM endpoint, so players/apps that can resolve YouTube URLs natively don't need to go through this service. Use it from cron or CLI as --params iframe.Channel/Season {year}/S{year}E{XX} - video.strmChannel [ID]/Season 2025/S2025E01 - Video Title.strmSeason {year} folders (e.g., Season 2025, Season 2026)S{year}E{XX} format, resetting to E01 each yearlang parameter in config.jsonvideo_quality can limit playback quality. Use best for the current behavior or values like 1080, 720, 480 to avoid selecting higher resolutions.download_subtitles is enabled, YouTube .vtt subtitles are downloaded next to each .strm file. WebVTT files are post-processed to improve Jellyfin/Emby rendering: