Ismered azt az érzést? Böngészel YouTube-on, szembe jön egy videó ami nagyon érdekel, elmented “megnézem később”-re, aztán pár nap múlva mikor végre lenne időd rá… törölték. DMCA, a feltöltő meggondolta magát, regionális blokk – mindegy mi az ok, a videó eltűnt. Mintha sosem létezett volna.
Évek óta motoszkált bennem ez a probléma, mígnem egy nap leültem és megoldottam. Az eredmény egy apró, négy scriptből álló rendszer, ami azóta csendben dolgozik a háttérben. Ebben a posztban megmutatom hogyan működik.
Az ötlet
A megoldás meglepően egyszerű: ne “majd megnézem később”-ezz, hanem töltsd le azonnal. A letöltött fájl a te szervereden van, a te Jellyfin könyvtáradban – senki nem tudja törölni alólad.
Két bemeneti pont van: asztali böngészőből egy bookmarklet egy kattintással küldi a videót a MeTube-nak, telefonról pedig elég a YouTube saját “mentés playlistbe” funkcióját használni, és egy cron job időnként végigmegy rajta és lehúzza ami új.
De a letöltés csak a fél történet. A másik fele az, hogy ez a rendszer megbízhatóan, csendben, magától működjön – ne hagyjon szemetet, ne zavarjon, és ha belekukkantasz, lásd mi történik.
Fontos: ez a rendszer kizárólag személyes használatra, saját tartalom archiválásra készült. A letöltött videók továbbadása, újrafeltöltése vagy bármilyen kereskedelmi felhasználása nem megengedett.
A négy script
tube-sync.sh – a főnök
Ez a rendszer szíve, amit a cron futtat néhány óránként. Amikor elindul, először megnézi, van-e lockfile – ez gyakorlatilag egy jelzőtábla: “dolgozom, ne zavarj”. Ha megtalálja és a mögötte lévő folyamat tényleg fut, szépen kilép és nem csinál semmit. Ha a lockfile ott maradt de a folyamat már nem él – mondjuk volt egy áramszünet – akkor feltakarítja és folytatja.
Ha szabad a pálya, leteszi a saját lockját és elindul. Beolvassa a konfigurációt – itt van a playlist URL, a Jellyfin cím, az API kulcs, minden. Aztán elindítja a yt-dlp-t a háttérben és figyeli.
A fontos rész a trap. Ez azt mondja meg a scriptnek, hogy ha kap egy leállító jelet – akár te állítod le, akár a rendszer – akkor ne csak úgy meghaljon, hanem először szépen leállítsa a yt-dlp-t, kitörölje a félkész .part fájlokat, és felszabadítsa a lockot. Soha nem marad szemét a lemezen.
Amikor a letöltés végez, a script szól a Jellyfinnek egy API híváson keresztül, hogy frissítse a könyvtárat. Végül leveszi a lockot.
tube-stop.sh – a fék
A kézi leállító. Megnézi a PID fájlból melyik folyamatot kell megállítani, és küld neki egy TERM signalt – ez az udvarias kérés: “állj le szépen”. Utána vár maximum 10 másodpercet. Ha ennyi idő alatt nem áll le, erőszakkal kilövi. Végül törli a félkész fájlokat és a lockot. Akár normálisan, akár erőszakkal áll le, a végeredmény mindig tiszta.
tube-status.sh – a műszerfal
Ez nem csinál semmit, csak nézelődik és riportol. Megnézi van-e lockfile és fut-e a mögötte lévő folyamat – ebből tudja, aktív-e a sync. Keres .part fájlokat a letöltési mappában – ha talál, azok éppen töltődnek, kiírja a nevüket és a méretüket. Megszámolja az archive.txt sorait, ami megmondja hány videó van összesen letöltve. Megnézi a mappa méretét, kiírja az utolsó tíz sort a logból, és megmutatja a cron beállítást. Egy pillanat alatt átlátod az egész rendszer állapotát.
install.sh – az építőmester
Ez fut egyszer, a legelején. Végigkérdezi a beállításokat: mi a playlist URL, hol a Jellyfin, mi az API kulcs, melyik user futtassa, milyen gyakran. Megmutatja az összefoglalót és rábólintás után telepíti a csomagokat, elmenti a konfigurációt, helyükre másolja a scripteket, beállítja a jogosultságokat, a log rotációt és a cront. A végén kiírja hogy kész.
Négy script, mindegyiknek egy feladata, és együtt kerek az egész.
A Jellyfin API
A rendszer utolsó eleme a Jellyfin integráció. A letöltés után a tube-sync szól a Jellyfinnek, hogy frissítse a könyvtárát – így nem kell megvárni az időzített scant, a videó perceken belül megjelenik.
Ehhez egy API kulcs kell, amit a Jellyfin dashboardján lehet generálni. Hogy működik-e, egyszerűen ellenőrizhető:
curl -s "https://JELLYFIN_URL/System/Info" \
-H "X-Emby-Token: API_KULCS" | jq .
Ha a jq telepítve van, szépen formázott, színezett JSON-t kapsz vissza a szerver nevével, verziójával. Ha 401 Unauthorized jön, az API kulcs rossz.
A library scan triggerelése:
curl -s -X POST "https://JELLYFIN_URL/Library/Refresh" \
-H "X-Emby-Token: API_KULCS"
Üres válasz = sikeres, a scan elindult. Ennyi az egész integráció – egy POST kérés.
A scriptek
tube-sync.sh
#!/bin/sh
CONFIG="/etc/tube-sync/config"
if [ ! -f "$CONFIG" ]; then
echo "HIBA: Nincs konfiguráció: $CONFIG"
exit 1
fi
. "$CONFIG"
LOCKFILE="/tmp/tube-sync.lock"
PID_FILE="/tmp/tube-sync.pid"
# Ha már fut, kilépés
if [ -f "$LOCKFILE" ]; then
OLD_PID=$(cat "$PID_FILE" 2>/dev/null)
if [ -n "$OLD_PID" ] && kill -0 "$OLD_PID" 2>/dev/null; then
echo "$(date): Már fut (PID: $OLD_PID), kihagyás."
exit 0
else
echo "$(date): Régi lock találva, de a folyamat nem fut. Takarítás..."
rm -f "$LOCKFILE" "$PID_FILE"
fi
fi
touch "$LOCKFILE"
# Tiszta leállítás kezelése
cleanup() {
echo "$(date): Leállítás..."
if [ -n "$YTDLP_PID" ] && kill -0 "$YTDLP_PID" 2>/dev/null; then
kill "$YTDLP_PID" 2>/dev/null
wait "$YTDLP_PID" 2>/dev/null
fi
find "$DOWNLOAD_DIR" -name "*.part" -delete 2>/dev/null
find "$DOWNLOAD_DIR" -name "*.ytdl" -delete 2>/dev/null
rm -f "$LOCKFILE" "$PID_FILE"
echo "$(date): Leállítva, tisztán."
exit 0
}
trap cleanup INT TERM HUP
echo $$ > "$PID_FILE"
echo "$(date): Sync indítása..."
yt-dlp --download-archive "$DOWNLOAD_DIR/archive.txt" \
--remote-components ejs:github \
-f "bestvideo[height<=1080]+bestaudio/best" \
--merge-output-format mkv \
--no-overwrites \
--retries 3 \
--fragment-retries 3 \
-o "$DOWNLOAD_DIR/%(title)s.%(ext)s" \
"$PLAYLIST_URL" &
YTDLP_PID=$!
echo "$YTDLP_PID" > "$PID_FILE"
wait "$YTDLP_PID"
EXIT_CODE=$?
find "$DOWNLOAD_DIR" -name "*.part" -delete 2>/dev/null
find "$DOWNLOAD_DIR" -name "*.ytdl" -delete 2>/dev/null
# Jellyfin library scan
if [ -n "$JELLYFIN_URL" ] && [ -n "$JELLYFIN_API_KEY" ]; then
echo "$(date): Jellyfin library scan..."
curl -s -X POST "$JELLYFIN_URL/Library/Refresh" \
-H "X-Emby-Token: $JELLYFIN_API_KEY" && \
echo "$(date): Jellyfin scan elindítva." || \
echo "$(date): FIGYELEM: Jellyfin scan sikertelen."
fi
rm -f "$LOCKFILE" "$PID_FILE"
echo "$(date): Sync kész (exit: $EXIT_CODE)."
tube-stop.sh
#!/bin/sh
CONFIG="/etc/tube-sync/config"
[ -f "$CONFIG" ] && . "$CONFIG"
PID_FILE="/tmp/tube-sync.pid"
LOCKFILE="/tmp/tube-sync.lock"
DOWNLOAD_DIR="${DOWNLOAD_DIR:-/path/to/downloads}"
if [ ! -f "$LOCKFILE" ]; then
echo "Nem fut jelenleg."
exit 0
fi
PID=$(cat "$PID_FILE" 2>/dev/null)
if [ -z "$PID" ]; then
echo "Lock fájl van, de PID nem található. Takarítás..."
rm -f "$LOCKFILE" "$PID_FILE"
exit 0
fi
echo "Tube sync leállítása (PID: $PID)..."
kill "$PID" 2>/dev/null
WAIT=0
while [ $WAIT -lt 10 ] && kill -0 "$PID" 2>/dev/null; do
sleep 1
WAIT=$((WAIT + 1))
echo " Várakozás... ($WAIT mp)"
done
if kill -0 "$PID" 2>/dev/null; then
echo " Erőszakos leállítás..."
kill -9 "$PID" 2>/dev/null
pkill -9 yt-dlp 2>/dev/null
fi
find "$DOWNLOAD_DIR" -name "*.part" -delete 2>/dev/null
find "$DOWNLOAD_DIR" -name "*.ytdl" -delete 2>/dev/null
rm -f "$LOCKFILE" "$PID_FILE"
echo "Leállítva, tisztán."
tube-status.sh
#!/bin/sh
CONFIG="/etc/tube-sync/config"
[ -f "$CONFIG" ] && . "$CONFIG"
LOCKFILE="/tmp/tube-sync.lock"
PID_FILE="/tmp/tube-sync.pid"
LOG_FILE="/var/log/tube-sync.log"
DOWNLOAD_DIR="${DOWNLOAD_DIR:-/path/to/downloads}"
echo "============================="
echo " TUBE SYNC ÁLLAPOT"
echo "============================="
echo ""
if [ -f "$LOCKFILE" ]; then
PID=$(cat "$PID_FILE" 2>/dev/null)
if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then
echo " Státusz: ▶ FUT (PID: $PID)"
else
echo " Státusz: ⚠ Lock fájl maradt, de nem fut"
fi
else
echo " Státusz: ⏹ Nem fut"
fi
echo ""
PARTS=$(find "$DOWNLOAD_DIR" -name "*.part" 2>/dev/null)
if [ -n "$PARTS" ]; then
echo " Éppen töltődik:"
echo "$PARTS" | while read -r f; do
BASENAME=$(basename "$f" .part)
SIZE=$(du -h "$f" 2>/dev/null | cut -f1)
echo " 📥 $BASENAME ($SIZE)"
done
else
echo " Éppen töltődik: semmi"
fi
echo ""
if [ -f "$DOWNLOAD_DIR/archive.txt" ]; then
TOTAL=$(wc -l < "$DOWNLOAD_DIR/archive.txt")
echo " Letöltött videók összesen: $TOTAL"
else
echo " Letöltött videók összesen: 0"
fi
DISK=$(du -sh "$DOWNLOAD_DIR" 2>/dev/null | cut -f1)
echo " Tube mappa mérete: $DISK"
echo ""
if [ -f "$LOG_FILE" ]; then
echo " Utolsó 10 log sor:"
echo " ---"
tail -10 "$LOG_FILE" | sed 's/^/ /'
fi
echo ""
echo "============================="
Konfiguráció (/etc/tube-sync/config)
PLAYLIST_URL="https://www.youtube.com/playlist?list=PLAYLIST_ID"
DOWNLOAD_DIR="/path/to/downloads"
JELLYFIN_URL="https://JELLYFIN_URL"
JELLYFIN_API_KEY="JELLYFIN_API_KEY"