Hogyan mentettem meg a YouTube videóimat a feledéstől

Évek óta bosszantott, hogy a YouTube videók eltűnnek mire megnézném. Négy shell script, egy playlist és egy bookmarklet – ennyi kellett a megoldáshoz. Egy kattintás, és a videó a tiéd – még ha holnap törlik is.

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"

A teljes rendszer négy shell scriptből, egy YouTube playlistből és egy bookmarkletből áll. Nincs benne semmi bonyolult, semmi varázslat. Mégis megoldja azt a problémát, ami évekig bosszantott: a videó az enyém, és az is marad. Meglátod, egy mozdulat, és a tiéd – örökre.