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.
239 lines
8.2 KiB
Markdown
239 lines
8.2 KiB
Markdown
# ━━━━━ MEDIA — Manual ━━━━━
|
|
|
|
Config reference, procedures, operational workflows for Media/ scripts.
|
|
For overview see README-Media.md. For per-script detail see script headers.
|
|
|
|
For arr stack config and procedures (cleanup, release fixer, sync, discovery) see
|
|
`Arrs_Stack/Manual-Arrs_Stack.md`.
|
|
|
|
---
|
|
|
|
## ━━━ PERMISSIONS MODEL ━━━
|
|
|
|
```
|
|
Directories: 755 nobody:users
|
|
Owner (nobody) — rwx enter, list, create files
|
|
Group (users) — r-x enter and list
|
|
Others — r-x Samba guests can browse
|
|
No world-write — prevents accidental deletion by unauthenticated access
|
|
|
|
Files: 664 nobody:users
|
|
Owner (nobody) — rw read + write
|
|
Group (users) — rw arrs can import, rename, delete
|
|
Others — r Samba guests can read
|
|
No execute bit — media files are never executable
|
|
```
|
|
|
|
**Two separate passes — not a single recursive chmod.** Directories need the execute bit
|
|
to enter. Files must never have the execute bit. A single `chmod -R 664` would break
|
|
directory entry. The script runs `find -type d` and `find -type f` separately.
|
|
|
|
**If this script corrects many files on every run**, a container has wrong PUID/PGID.
|
|
Correct values on unRAID: `PUID=99 (nobody)` `PGID=100 (users)`. Add to each container's
|
|
environment in its Docker template. Common culprits: SABnzbd, qBittorrent, slskd.
|
|
Once fixed, this script corrects 0 files per run — it becomes a pure daily failsafe.
|
|
|
|
---
|
|
|
|
## ━━━ CONFIGURATION — master.conf ━━━
|
|
|
|
### Permissions
|
|
|
|
```bash
|
|
PERMISSIONS_DIR_MODE="755"
|
|
PERMISSIONS_FILE_MODE="664"
|
|
PERMISSIONS_OWNER="nobody:users"
|
|
```
|
|
|
|
---
|
|
|
|
### Media Cleaner — File Patterns
|
|
|
|
```bash
|
|
ANIME_FILE_PATTERNS=(
|
|
"*.sfv" # checksum verification — useless after download verified
|
|
"*.md5" "*.sha1" # other checksum formats
|
|
"*.nfo" # scene info file — not library metadata
|
|
"*.url" "*.lnk" # website shortcuts
|
|
"*.rar" "*.zip" # source archives kept by some clients after extraction
|
|
"*.info" # tool output files
|
|
"*.torrent" # torrent descriptor left by some clients
|
|
"*.sample*" # scene preview clip
|
|
"*.proof*" # screenshot proving encode quality
|
|
"*sync-conflict*" # Syncthing conflict copies
|
|
"*.scr" "*.exe" # executables — should never be in a media folder
|
|
"*.srr" # scene recovery record
|
|
"*.log" # tool/client logs
|
|
"*.json" # metadata or tool output
|
|
)
|
|
|
|
MEDIA_FILE_PATTERNS=(
|
|
"${ANIME_FILE_PATTERNS[@]}" # all anime patterns plus:
|
|
"*.iso" # disc images after ripping
|
|
"*.lrc" # lyric files in media folders
|
|
)
|
|
```
|
|
|
|
> **DO NOT add patterns that match media you want to keep:**
|
|
> `*.mkv *.mp4 *.avi *.m4v` — video files
|
|
> `*.flac *.mp3 *.m4a` — audio files
|
|
> `*.srt *.sub *.ass` — subtitle files (Bazarr managed)
|
|
> `*.jpg *.png` — artwork
|
|
> Always use `--dry-run` when adding new patterns.
|
|
|
|
---
|
|
|
|
### Play State Sync
|
|
|
|
```bash
|
|
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
|
|
# host*.conf
|
|
HOST1_EMBY_URL="http://192.168.50.2:8096"
|
|
HOST1_EMBY_API_KEY="..."
|
|
HOST1_JELLYFIN_URL="" # empty = skip Jellyfin on this host
|
|
HOST1_JELLYFIN_API_KEY=""
|
|
```
|
|
|
|
---
|
|
|
|
## ━━━ CONFIGURATION — host*.conf ━━━
|
|
|
|
```bash
|
|
# Shares this server applies permissions to
|
|
HOST1_MEDIA_PERMISSION_SHARES=(
|
|
"/mnt/user/Movies"
|
|
"/mnt/user/Tv_Shows"
|
|
"/mnt/user/Music"
|
|
"/mnt/user/Kids_Movies"
|
|
"/mnt/user/Kids_Tv_Shows"
|
|
"/mnt/user/Sports"
|
|
"/mnt/user/stand-up_comedy"
|
|
)
|
|
|
|
# Folders cleaned by each profile
|
|
HOST1_ANIME_CLEAN_FOLDERS=(
|
|
"/mnt/user/Anime_Movies"
|
|
"/mnt/user/Anime_Movies-Old"
|
|
"/mnt/user/Anime_Shows"
|
|
"/mnt/user/Anime_Shows-Old"
|
|
)
|
|
HOST1_MEDIA_CLEAN_FOLDERS=(
|
|
"/mnt/user/Kids_Movies"
|
|
"/mnt/user/Kids_Tv_Shows"
|
|
"/mnt/user/Movies"
|
|
"/mnt/user/Music"
|
|
"/mnt/user/Sports"
|
|
"/mnt/user/stand-up_comedy"
|
|
"/mnt/user/Tv_Shows"
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## ━━━ OUTPUT TIERS ━━━
|
|
|
|
All scripts have two output levels controlled by `--log`.
|
|
|
|
Without `--log`, each script processes silently and concludes with a summary block.
|
|
Warnings and errors are always visible.
|
|
|
|
With `--log`, per-item detail appears — individual shares being processed, file counts,
|
|
per-server sync results.
|
|
|
|
---
|
|
|
|
## ━━━ FLAG REFERENCE ━━━
|
|
|
|
### media_shares_permissions.sh
|
|
|
|
`media_shares_permissions.sh`
|
|
Apply correct ownership and permissions to all configured media shares. Safe to run
|
|
manually at any time — idempotent, only changes what's wrong.
|
|
|
|
`media_shares_permissions.sh --dry-run`
|
|
Show how many files and directories would be corrected per share. If unexpectedly large,
|
|
check container PUID/PGID settings first (PUID=99 PGID=100).
|
|
|
|
`media_shares_permissions.sh --status`
|
|
Show configured share list and ownership of the share roots.
|
|
|
|
`media_shares_permissions.sh --log`
|
|
Show ownership correction count per share and per file (verbose).
|
|
|
|
> On large libraries this runs 20-30 minutes. This is expected — millions of files with
|
|
> recursive walk takes time. Designed to run overnight in the maintenance window.
|
|
|
|
---
|
|
|
|
### media_cleaner.sh
|
|
|
|
`media_cleaner.sh anime`
|
|
Remove junk files from anime share folders using ANIME_FILE_PATTERNS.
|
|
|
|
`media_cleaner.sh media`
|
|
Remove junk files from media share folders using MEDIA_FILE_PATTERNS.
|
|
|
|
`media_cleaner.sh [profile] --dry-run`
|
|
Show what would be deleted without removing anything. Always run first when adding new
|
|
patterns or folders.
|
|
|
|
`media_cleaner.sh [profile] --status`
|
|
Show folder list and file patterns for the profile.
|
|
|
|
`media_cleaner.sh [profile] --log`
|
|
Show every file examined, not just those removed.
|
|
|
|
---
|
|
|
|
### play_state_sync.sh
|
|
|
|
`play_state_sync.sh`
|
|
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`
|
|
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.
|
|
|
|
`play_state_sync.sh --status`
|
|
Show configured servers, reachability, and user counts.
|
|
|
|
`play_state_sync.sh --log`
|
|
Verbose output — show each item comparison.
|