# Makov webkamera → Jellyfin (Live TV) Restreamuje YouTube live přenos ("Krmítko", https://www.youtube.com/watch?v=F0fsV1GGZ3M) do lokálního HLS, na který se Jellyfin dívá přes stabilní adresu. Když YouTube odkaz vyprší nebo spadne, systemd proces sám restartuje a natáhne novou URL — divák/Jellyfin si ničeho nevšimne. ## 0) Kam skript patří Toto **musí běžet na stejném stroji (resp. za stejnou veřejnou IP), jako Jellyfin** — YouTube vrací streamovací URL svázanou s IP adresou, ze které byla vyžádána (`yt-dlp -g`). Pokud poběží jinde než Jellyfin, `ffmpeg` dostane 403 a nikdy se nerozjede. ## 1) Závislosti ```bash sudo apt install -y ffmpeg python3 sudo pip install -U yt-dlp # nebo pipx install yt-dlp — apt verze bývá stará # a YouTube ji rád rozbije ``` Ověř, že `yt-dlp` a `ffmpeg` jedou z shellu bez chyby: ```bash yt-dlp --version ffmpeg -version ``` ## 2) Uživatel a oprávnění Vlastní, neprivilegovaný systémový účet — obě služby pod ním běží: ```bash sudo useradd --system --no-create-home --shell /usr/sbin/nologin makov-cam sudo mkdir -p /var/lib/jellyfin-iptv/hls sudo chown -R makov-cam:makov-cam /var/lib/jellyfin-iptv ``` Pokud je `yt-dlp` nainstalované přes **snap** (`which yt-dlp` ukazuje `/snap/bin/yt-dlp`), potřebuje při startu zapisovatelný domovský adresář — a to i přes `--no-create-home`, protože domov si zjišťuje přímo z `/etc/passwd`, ne z proměnné `$HOME` (tu přenastavenou v service souboru ignoruje). Bez toho skončí na `Permission denied: mkdir /home/makov-cam`. Vytvoř mu ho ručně: ```bash sudo mkdir -p /home/makov-cam sudo chown makov-cam:makov-cam /home/makov-cam sudo chmod 750 /home/makov-cam ``` (Pokud `yt-dlp` instaluješ přes `pip`/`pipx` dle kroku 1, tenhle problém nenastává a krok lze přeskočit.) ## 3) Nasazení souborů ```bash sudo install -m 755 makov-cam.sh /usr/local/bin/makov-cam.sh sudo install -m 644 makov-cam.service /etc/systemd/system/ sudo install -m 644 makov-cam-http.service /etc/systemd/system/ sudo systemctl daemon-reload ``` ## 4) Spuštění ```bash sudo systemctl enable --now makov-cam.service sudo systemctl enable --now makov-cam-http.service ``` Ověření: ```bash systemctl status makov-cam.service makov-cam-http.service journalctl -u makov-cam.service -f # živě sleduj log ffmpeg/yt-dlp # do pár vteřin by se měl objevit playlist a segmenty: ls -la /var/lib/jellyfin-iptv/hls/ # a mělo by jít stáhnout přes HTTP: curl -sI http://127.0.0.1:8888/hls/makov.m3u8 ``` `makov-cam-http.service` je svázaná (`BindsTo=`) na `makov-cam.service` — když restream spadne/restartuje, HTTP server padne/naskočí s ním, ať nikdy neservíruje mrtvý/prázdný playlist. Pozor: `BindsTo=` jen zajišťuje, že HTTP server *spadne*, když spadne hlavní služba — nezajišťuje, že se s ní i sám *nastartuje*. Po `enable --now` obou služeb to není problém (obě se spustí), ale pokud jen restartuješ `makov-cam.service` ručně, zkontroluj/restartuj i `makov-cam-http.service`. HTTP server poslouchá jen na jedné IP (`--bind`), ne na `0.0.0.0` — nemá cenu ho vystavovat celé síti, stačí odkud se na něj bude dívat Jellyfin: - **Jellyfin běží přímo na hostu** (ne v kontejneru): `--bind 127.0.0.1` stačí. - **Jellyfin běží v Docker kontejneru na tomtéž hostu** (běžný případ) — `127.0.0.1` uvnitř kontejneru je jeho *vlastní* loopback, ne hostitelův, takže na hostitelův HTTP server se tak nedostane. Zjisti gateway IP té docker sítě, na které kontejner běží, a naviaž se na ni: ```bash docker inspect --format '{{.HostConfig.NetworkMode}}' docker inspect --format '{{json .NetworkSettings.Networks}}' # z výstupu vezmi "Gateway", např. 172.19.0.1 — tam se z kontejneru dostaneš ``` a použij `--bind ` (viz `makov-cam-http.service`). Pokud by Jellyfin běžel v jiné VM, změň `--bind` na IP, na které je pro ni dosažitelný, a případně otevři firewall jen pro tenhle port a jen z její IP. ## 5) Playlist pro Jellyfin `WorkingDirectory` HTTP služby je `/var/lib/jellyfin-iptv` (rodič `hls/`), aby šel přes stejný server servírovat i samotný `.m3u` playlist — to se hodí, pokud Jellyfin (typicky v kontejneru) nevidí souborový systém hostitele a tuner tak musí načíst playlist přes HTTP URL, ne lokální cestou. Vytvoř `/var/lib/jellyfin-iptv/makov.m3u`: ``` #EXTM3U #EXTINF:-1 tvg-id="makov-krmitko" tvg-name="Makov - Krmítko",Makov - Krmítko http://:8888/hls/makov.m3u8 ``` kde `` je stejná adresa, na kterou je navázaný `makov-cam-http.service` (viz krok 4). ## 6) Nastavení v Jellyfinu 1. **Dashboard → Live TV → Tuner Devices → +** (Add) 2. Typ: **M3U Tuner** 3. File or URL: - Jellyfin na hostu s přístupem k souborovému systému: lokální cesta `/var/lib/jellyfin-iptv/makov.m3u`. - Jellyfin v kontejneru (nevidí hostitelův FS): HTTP URL `http://:8888/makov.m3u` (viz krok 5). 4. **Save** → Jellyfin načte kanál "Makov - Krmítko" do Live TV 5. Doporučeno: **Dashboard → Live TV → Guide Providers** — pro tenhle kanál žádného není třeba (žádný EPG), Jellyfin ho ukáže jako kanál bez programu, což nevadí. 6. Zkontroluj přehrávání v **Live TV** sekci hlavního rozhraní. Protože kanál pořád ukazuje na stejnou lokální adresu, odpadá starost o obnovování guide dat nebo expiraci URL na Jellyfin straně — o to se stará jen `makov-cam.service` na pozadí. ## 7) Autostart po restartu serveru `enable` v kroku 4 už zajistil start při bootu. Rychlá kontrola: ```bash systemctl is-enabled makov-cam.service makov-cam-http.service ``` ## Řešení problémů - **`ffmpeg` pořád padá / v logu 403 od googlevideo.com** — nejčastěji IP mismatch (viz bod 0): stroj, co spouští `yt-dlp -g`/`ffmpeg`, musí mít stejnou veřejnou IP jako to, odkud by se stream normálně přehrával. Zkontroluj `journalctl -u makov-cam.service -n 50`. - **`yt-dlp: command not found` v service logu** — systemd nepoužívá tvůj interaktivní `$PATH`; ověř `which yt-dlp` a případně uprav `ExecStart` v `makov-cam.sh`/skriptu na plnou cestu (`/usr/local/bin/yt-dlp` apod.), nebo nainstaluj přes `sudo pip install`, aby šel do `/usr/local/bin`. - **Playlist v Jellyfinu jede, ale sekundy/minuty výpadku při refreshi** — normální, `hls_list_size 6` × `hls_time 4s` drží ~24s okno; při restartu streamu je pár vteřin díra, než naběhnou nové segmenty. Jde zkrátit/prodloužit úpravou těch dvou hodnot ve skriptu. - **YouTube někdy dočasně rozbije `yt-dlp` novou verzí přehrávače** — drž `yt-dlp` aktuální (`sudo pip install -U yt-dlp`, klidně jako týdenní cron). - **`ERROR: Requested format is not available`** — stream v tu chvíli nenabízí žádný kombinovaný (progresivní) formát, jen oddělené video-only/audio-only HLS řady (ověř přes `yt-dlp -F `). Skript proto primárně používá `-f 'bestvideo+bestaudio/best'` a při dvou vrácených URL pustí `ffmpeg` se dvěma `-i` vstupy (`-map 0:v -map 1:a`); jedna URL = fallback na progresivní formát, pokud se zase objeví.