Correct and extend folder docs for Docker Essentials, Watchdogs, Media and Arrs Stack

The docs had drifted from the scripts — a script that no longer exists, three wrong variable
names, a reversed run order, and seven scheduled scripts that were never documented at all.
This commit is contained in:
Gmer4Lfe
2026-08-01 22:59:07 -04:00
parent c377ddfcca
commit 8a2707ee37
6 changed files with 331 additions and 30 deletions
+33 -7
View File
@@ -86,10 +86,25 @@ MEDIA_FILE_PATTERNS=(
### Play State Sync
```bash
PLAY_STATE_SYNC_ENABLED=true # toggle entire sync
PLAY_STATE_SYNC_LOOKBACK_DAYS=30 # history window for played items
PLAY_SYNC_ENABLED=true # toggle entire sync
PLAY_SYNC_REMOTE=true # sync across hosts via Tailscale
# false = local servers only (this host's Emby + Jellyfin)
PLAY_SYNC_TYPES="Movie,Episode" # item types to sync — Audio excluded, music library too large
PLAY_SYNC_FAV_TYPES="MusicArtist,MusicAlbum,Movie,Series" # favourites, union sync, never unmarks
PLAY_SYNC_PROBE=true # skip per-item work when nothing changed since last run
PLAY_SYNC_PROBE_MAX_AGE_HOURS=24 # force a full comparison when the fingerprint is older
PLAY_SYNC_HANDBACK_RETRIES=5 # fallback handback: attempts before DNS cutover proceeds
PLAY_SYNC_HANDBACK_RETRY_DELAY=60 # seconds between those attempts
```
> **There is no date/lookback filter, and one must not be re-added.** An earlier version
> gated on played-date; it was removed once the real cost was measured — the 30-minute
> runtime was fork overhead per item, not API volume or item count. The fix was jq epoch
> parsing plus the response-hash probe below. Re-introducing a date window would reduce
> correctness (older items silently stop syncing) without meaningfully reducing runtime.
Emby and Jellyfin servers configured per-host:
```bash
@@ -194,13 +209,24 @@ Show every file examined, not just those removed.
### play_state_sync.sh
`play_state_sync.sh`
Sync played/unplayed state and resume positions from local Emby to the remote Emby.
Only items played within PLAY_STATE_SYNC_LOOKBACK_DAYS are synced.
Sync played/unplayed state, resume positions and favourites across **every** configured Emby
and Jellyfin server — not just local→remote. Servers are discovered from every
`HOST*_TRANSCODE_SERVERS` entry, with remote hosts' localhost URLs rewritten to their
Tailscale IP. Newest `LastPlayedDate` wins; state only ever moves forward, never clears.
No age filter — every matched item is considered on every run. The **change probe** is what
keeps that cheap: the raw API responses are hashed and compared against the fingerprint from
the last successful run, and per-item processing is skipped entirely when nothing moved.
Fetches still happen every run, so nothing can be missed by the probe.
`play_state_sync.sh --full`
Ignore PLAY_STATE_SYNC_LOOKBACK_DAYS — sync all played items regardless of age. May be
slow on large libraries. Use after a new Emby install or database restore to rebuild
full play history.
Bypass the change probe and force the full per-item comparison even when the fingerprint
matches. Use after a new Emby install or database restore, or when debugging a sync that
appears to be skipping work it should be doing.
`play_state_sync.sh --wait`
Wait for an in-progress run instead of exiting. For manual runs that would otherwise be
skipped by the scheduled every-30-minute pass. Used by `fallback.sh` during handback.
`play_state_sync.sh --dry-run`
Show what would be synced without writing any state.
+40
View File
@@ -100,3 +100,43 @@ syncs between them.
| `media_shares_permissions.sh` | Apply `nobody:users` ownership + correct permissions to all media shares | Daily — runs first |
| `media_cleaner.sh` | Remove junk files (two profiles: `anime` + `media`) | Daily — runs before arr cleanup |
| `play_state_sync.sh` | Sync watched/played state + resume positions across Emby + Jellyfin | Every 30 min |
---
## ━━━ THE ctime INVARIANT — READ BEFORE CHANGING PERMISSIONS ━━━
`media_shares_permissions.sh` applies every pass **conditionally** — it touches only entries
whose owner or mode is actually wrong. That is not an optimisation, and it must stay that way.
`chown` and `chmod` rewrite an inode's ctime **even when the value does not change**. A
blanket pass would therefore restamp every file in the library every night.
The arr cleanup scripts (`sonarr_cleanup.sh`, `radarr_cleanup.sh`, `lidarr_cleanup.sh`) gate
orphan deletion on ctime. mtime cannot substitute: an import preserves the release's original
timestamp, so mtime says nothing about when a file arrived here. Measured 2026-07-27 — of 400
files imported that week, **all 400 had mtimes over 7 days old, one of them 9613 days.**
So:
```
blanket chown/chmod → every ctime resets to today
→ no file ever appears older than *_ORPHAN_AGE
→ orphan collection silently stops
→ nothing errors, nothing warns, disk just fills
```
**The failure is invisible.** No script fails, no notification fires. The only symptom is
orphans quietly accumulating until a pool fills — which is exactly how the 755 GB / 89%-full
cache pool incident happened.
Two rules follow, and both are load-bearing:
1. **`media_shares_permissions.sh` passes stay conditional.** Making any of them unconditional
breaks orphan collection ecosystem-wide.
2. **`Tools/bulk_permissions_repair.sh` is unconditional by design** — it exists to repair
known-wrong paths where correctness beats preserving a clock. That is precisely why it is a
manual, targeted tool and not scheduled. Pointing it at a whole media root pauses orphan
collection there for `*_ORPHAN_AGE` days.
Both scripts' headers carry this warning too. If you are reading this because you are about to
"simplify" the permissions job, this is the thing that breaks.