diff --git a/nas/transmission-bt.md b/nas/transmission-bt.md index e3fe580..33e4b4f 100644 --- a/nas/transmission-bt.md +++ b/nas/transmission-bt.md @@ -199,3 +199,70 @@ incus exec transmission-bt -- bash -c "ping -c1 -W2 8.8.8.8 || echo kill-switch - The image server check can make `incus launch` hang on slow WAN — launching from the cached image fingerprint (`incus image list`) bypasses it. + +## Jellyfin library scan on completion (2026-08-31) + +Jellyfin cannot notice finished downloads by itself any more. It watches +libraries with **inotify**, but since the media moved to nas the writer +(transmission, here) and the reader (`jellyfin-server` on nuc, over NFS) +are on different machines — an inotify event never crosses that. Before +the move both shared one local dataset on nuc, so it just worked. + +So transmission tells Jellyfin explicitly, via +`script-torrent-done`: + +```json +"script-torrent-done-enabled": true, +"script-torrent-done-filename": "/usr/local/bin/jellyfin-scan.sh" +``` + +The hook POSTs to Jellyfin's `/Library/Refresh`: + +```sh +#!/bin/sh +KEY_FILE=/etc/jellyfin-scan.key +JF=http://192.168.0.5:8096 +NAME="${TR_TORRENT_NAME:-unknown}" +[ -r "$KEY_FILE" ] || { logger -t jellyfin-scan "no readable key file; skipped ($NAME)"; exit 0; } +KEY=$(tr -d " \t\r\n" < "$KEY_FILE") +if curl -fsS -m 15 -X POST -H "X-Emby-Token: $KEY" "$JF/Library/Refresh" >/dev/null 2>&1; then + logger -t jellyfin-scan "library scan requested after: $NAME" +else + logger -t jellyfin-scan "library scan request FAILED (nuc off?) after: $NAME" +fi +exit 0 +``` + +Design points, each of which matters: + +- **Always `exit 0`, never block.** transmission runs the hook + synchronously; a hanging or failing hook stalls the daemon. Both paths + are tested — success and unreadable-key both exit 0. +- **nuc is usually powered off.** The request then fails, logs + `FAILED (nuc off?)`, and Jellyfin picks the file up on its next + scheduled scan. Not an error worth alerting on. +- **The key file is `640 root:debian-transmission`** — the hook runs as + `debian-transmission`, so it must be group-readable, and nothing wider. +- Reachable despite the kill switch: `192.168.0.5` is on the directly + connected LAN, so it needs no default route. + +Verify: + +```sh +incus exec transmission-bt -- su -s /bin/sh debian-transmission \ + -c 'TR_TORRENT_NAME=selftest /usr/local/bin/jellyfin-scan.sh' +incus exec transmission-bt -- journalctl -t jellyfin-scan -n 3 +``` + +**Rotating the key**: create a new one in Jellyfin (Dashboard → API Keys), +then + +```sh +printf %s '' | incus exec transmission-bt -- sh -c \ + 'umask 027; cat > /etc/jellyfin-scan.key; chown root:debian-transmission /etc/jellyfin-scan.key' +``` + +⚠️ Edit `settings.json` only while the daemon is **stopped** — +transmission rewrites the whole file on shutdown and will silently +discard changes made underneath it. + diff --git a/nuc/jellyfin-server.md b/nuc/jellyfin-server.md index c2f6ba2..7bb4026 100644 --- a/nuc/jellyfin-server.md +++ b/nuc/jellyfin-server.md @@ -116,6 +116,46 @@ incus start jellyfin-server the dataset locally ([nas/transmission-bt.md](../nas/transmission-bt.md)), so nuc's mount is read-only and there is exactly one writer. +## ⚠️ Real-time monitoring does not work over NFS (2026-08-31) + +Libraries have `EnableRealtimeMonitor=true` and Jellyfin reports +`SupportsLibraryMonitor: true`, but **new files never appear on their +own**. Jellyfin watches with inotify, which only reports changes made +through the local mount; transmission writes them on **nas**, so nuc's +NFS client sees nothing. Jellyfin looks healthy and silently misses +everything until a scan. + +This is a regression from the 2026-08-30 storage move — before it, +transmission and jellyfin-server shared one local dataset on nuc and +inotify fired normally. + +**Fix in place:** transmission calls Jellyfin's `/Library/Refresh` when a +download completes — see +[nas/transmission-bt.md](../nas/transmission-bt.md). Downloads appear +within seconds; if nuc is powered off the request fails harmlessly and +the scheduled scan catches up. + +Manual scan (UI): Dashboard → Scheduled Tasks → **Scan Media Library**. +By API: + +```sh +curl -X POST -H "X-Emby-Token: " http://192.168.0.5:8096/Library/Refresh # expect 204 +``` + +Diagnosing "my download is not in Jellyfin", in order — the first three +were all fine when this was hit, which is what made it confusing: + +```sh +ls /export/media/downloads/ # on nas: file there? +incus exec jellyfin-server -- ls /media/downloads/ # visible through NFS? +incus exec jellyfin-server -- find /var/lib/jellyfin/root -name '*.mblink' -exec cat {} + # in a library path? +incus exec jellyfin-server -- cat /var/lib/jellyfin/data/ScheduledTasks/*.js | grep -o '"Name":"Scan Media Library".*' # when did it last scan? +``` + +⚠️ **nuc's mount is read-only.** Reorganising finished downloads into +`/media/movies` or `/media/tv-shows` can no longer be done from nuc — do +it on nas under `/export/media/`. + ## First-run configuration 1. Run the setup wizard; add libraries pointing at `/media/...`.