# ━━━━━ 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.