50-plex: drop claim flow, gate on migrated DB and preserved identity
The forward-only decision was reversed: the server identity is now migrated, not minted. Claiming would destroy the exact thing the migration exists to preserve. - remove every PLEX_CLAIM path - check the six real mount points by exact share name - refuse to start against an un-remapped database - refuse to start without ProcessedMachineIdentifier in Preferences.xml - assert machineIdentifier after start
This commit is contained in:
+111
-88
@@ -1,135 +1,158 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 50-plex — deploy Plex. HUMAN-IN-THE-LOOP, run by hand, not from bootstrap.
|
# 50-plex — start Plex against the MIGRATED data directory.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage: sudo /srv/mediabox-bootstrap/scripts/50-plex.sh
|
||||||
# # 1. open https://plex.tv/claim and copy the token
|
|
||||||
# # 2. within four minutes:
|
|
||||||
# sudo PLEX_CLAIM=claim-xxxxxxxxxxxx /srv/mediabox-bootstrap/scripts/50-plex.sh
|
|
||||||
#
|
#
|
||||||
# A claim token expires four minutes after it is issued. That is the entire
|
# NO CLAIM TOKEN. This is deliberate and important.
|
||||||
# reason this stage is not automated: there is no way to bake one into a USB,
|
|
||||||
# a repo, or a first-boot script and have it still be valid.
|
|
||||||
#
|
#
|
||||||
# Claiming is only required on the FIRST start. Re-running later without
|
# The migration carries MachineIdentifier, ProcessedMachineIdentifier and the
|
||||||
# PLEX_CLAIM is fine and will not re-claim.
|
# online token across in Preferences.xml, so this server IS the existing
|
||||||
|
# server: already claimed, shared users still invited, clients still pointed
|
||||||
|
# at it. Claiming would mint a NEW identity and throw all of that away — the
|
||||||
|
# exact thing the migration exists to preserve.
|
||||||
|
#
|
||||||
|
# If you find yourself reaching for https://plex.tv/claim, stop. Something has
|
||||||
|
# gone wrong with the Preferences.xml step in migration/README.md § M3.
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
|
||||||
|
PLEXDIR="/srv/plex"
|
||||||
|
CFG="$PLEXDIR/config/Library/Application Support/Plex Media Server"
|
||||||
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
DEST="/srv/plex"
|
|
||||||
MASTER="/srv/secrets/stacks.env"
|
MASTER="/srv/secrets/stacks.env"
|
||||||
|
|
||||||
# --- gate 1: mounts must be live BEFORE Plex ever scans ----------------------
|
# The six shares, named exactly as the database stores them.
|
||||||
# If Plex scans a library whose mount is missing, it sees zero files. With a
|
MOUNTS=(
|
||||||
# read-write NAS account, "empty trash after scan" would then delete the
|
"/mnt/nas/media"
|
||||||
# library's records — and Plex has the permission to act on that. Never let
|
"/mnt/nas/Radio Shows"
|
||||||
# Plex start against absent mounts.
|
"/mnt/nas/Education Videos"
|
||||||
echo " checking NAS mounts before starting Plex"
|
"/mnt/nas/Health"
|
||||||
|
"/mnt/nas/Home Movies"
|
||||||
|
"/mnt/nas/Pictures"
|
||||||
|
)
|
||||||
|
|
||||||
|
ok(){ printf ' \033[1;32m[ok]\033[0m %s\n' "$*"; }
|
||||||
|
bad(){ printf ' \033[1;31m[FAIL]\033[0m %s\n' "$*"; }
|
||||||
|
note(){ printf ' %s\n' "$*"; }
|
||||||
|
|
||||||
|
[ "$(id -u)" -eq 0 ] || { bad "must run as root"; exit 1; }
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Gate 1 — every mount live BEFORE Plex ever opens the database.
|
||||||
|
#
|
||||||
|
# If Plex scans a library whose mount is absent it sees zero files. The NAS
|
||||||
|
# account is read-write, so with autoEmptyTrash on that becomes deletion it
|
||||||
|
# has permission to perform. gen-preferences.ps1 forces autoEmptyTrash=0, but
|
||||||
|
# this gate is the belt to that braces.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
echo " checking the six NAS mounts"
|
||||||
missing=0
|
missing=0
|
||||||
for mp in /mnt/nas/audiobooks /mnt/nas/education-videos /mnt/nas/health \
|
for mp in "${MOUNTS[@]}"; do
|
||||||
/mnt/nas/home-movies /mnt/nas/media /mnt/nas/music-organized \
|
if mountpoint -q "$mp" && ls "$mp" >/dev/null 2>&1; then
|
||||||
/mnt/nas/pictures /mnt/nas/radio-shows; do
|
ok "$mp"
|
||||||
if ls "$mp" >/dev/null 2>&1 && mountpoint -q "$mp"; then
|
|
||||||
printf ' [ok] %s\n' "$mp"
|
|
||||||
else
|
else
|
||||||
printf ' [FAIL] %s not mounted\n' "$mp"
|
bad "$mp not mounted"
|
||||||
missing=1
|
missing=1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if [ "$missing" -ne 0 ]; then
|
if [ "$missing" -ne 0 ]; then
|
||||||
echo
|
echo
|
||||||
echo " REFUSING to start Plex with missing mounts."
|
bad "REFUSING to start Plex with missing mounts."
|
||||||
echo " Fix with: sudo /srv/mediabox-bootstrap/scripts/20-cifs.sh --verify"
|
note "fix: sudo /srv/mediabox-bootstrap/scripts/20-cifs.sh --verify"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- gate 2: Quick Sync -----------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
if [ ! -e /dev/dri/renderD128 ]; then
|
# Gate 2 — the database must actually be migrated.
|
||||||
echo " [WARN] /dev/dri/renderD128 missing — iGPU not enabled in BIOS."
|
#
|
||||||
echo " Plex will still run, but every transcode will be software."
|
# Starting Plex against an un-remapped database means every item shows as
|
||||||
RENDER_GID=993
|
# unavailable, and the recovery is messier than simply not starting.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
DB="$CFG/Plug-in Support/Databases/com.plexapp.plugins.library.db"
|
||||||
|
if [ ! -f "$DB" ]; then
|
||||||
|
bad "no database at $DB"
|
||||||
|
note "restore the data directory first — migration/README.md § M3"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if grep -qac 'korval' "$DB" 2>/dev/null && grep -qac '\\\\korval\\' "$DB" 2>/dev/null; then
|
||||||
|
bad "database still contains Windows UNC paths — remap has not been run"
|
||||||
|
note "run: sudo /srv/mediabox-bootstrap/migration/migrate-db.sh \"$DB\""
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
ok "database present and remapped"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Gate 3 — identity preserved?
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
PREFS="$CFG/Preferences.xml"
|
||||||
|
if [ -f "$PREFS" ] && grep -q 'ProcessedMachineIdentifier=' "$PREFS"; then
|
||||||
|
ok "server identity present in Preferences.xml (no claim needed)"
|
||||||
else
|
else
|
||||||
|
bad "Preferences.xml missing or has no ProcessedMachineIdentifier"
|
||||||
|
note "generate it on the Windows box: migration/gen-preferences.ps1"
|
||||||
|
note "without it this becomes a NEW server and shared users are lost"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Quick Sync
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
if [ -e /dev/dri/renderD128 ]; then
|
||||||
RENDER_GID="$(stat -c '%g' /dev/dri/renderD128)"
|
RENDER_GID="$(stat -c '%g' /dev/dri/renderD128)"
|
||||||
echo " render node gid: $RENDER_GID"
|
ok "render node gid $RENDER_GID"
|
||||||
|
else
|
||||||
|
RENDER_GID=993
|
||||||
|
note "[warn] /dev/dri/renderD128 missing — iGPU not enabled in BIOS."
|
||||||
|
note " Plex will run, but every transcode will be software."
|
||||||
fi
|
fi
|
||||||
export RENDER_GID
|
export RENDER_GID
|
||||||
|
|
||||||
install -d -m 0755 "$DEST"
|
# ---------------------------------------------------------------------------
|
||||||
install -d -m 0755 "$DEST/config"
|
# Deploy
|
||||||
chown -R 3000:3000 "$DEST"
|
# ---------------------------------------------------------------------------
|
||||||
install -m 0644 "$REPO_DIR/plex/docker-compose.yml" "$DEST/docker-compose.yml"
|
install -d -m 0755 "$PLEXDIR"
|
||||||
|
install -m 0644 "$REPO_DIR/plex/docker-compose.yml" "$PLEXDIR/docker-compose.yml"
|
||||||
|
chown -R 3000:3000 "$PLEXDIR/config"
|
||||||
|
|
||||||
# --- env --------------------------------------------------------------------
|
|
||||||
set -a
|
set -a
|
||||||
TZ="$(grep -E '^TZ=' "$MASTER" 2>/dev/null | head -1 | cut -d= -f2- || echo America/New_York)"
|
TZ="$(grep -E '^TZ=' "$MASTER" 2>/dev/null | head -1 | cut -d= -f2- || echo America/New_York)"
|
||||||
PLEX_ADVERTISE_URL="$(grep -E '^PLEX_ADVERTISE_URL=' "$MASTER" 2>/dev/null | head -1 | cut -d= -f2- || echo http://10.0.1.20:32400)"
|
|
||||||
PLEX_CLAIM="${PLEX_CLAIM:-}"
|
|
||||||
set +a
|
set +a
|
||||||
|
|
||||||
if [ -n "$PLEX_CLAIM" ]; then
|
cd "$PLEXDIR" || exit 1
|
||||||
echo " claim token supplied (expires 4 min from issue — moving now)"
|
docker compose up -d || { bad "compose up failed"; exit 1; }
|
||||||
else
|
|
||||||
echo " no PLEX_CLAIM given; assuming this server is already claimed"
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd "$DEST" || exit 1
|
echo " waiting for Plex"
|
||||||
docker compose up -d || { echo " [FAIL] compose up failed"; exit 1; }
|
|
||||||
|
|
||||||
# The claim token must not linger anywhere on disk.
|
|
||||||
unset PLEX_CLAIM
|
|
||||||
|
|
||||||
echo " waiting for Plex to answer on 32400"
|
|
||||||
for i in $(seq 1 60); do
|
for i in $(seq 1 60); do
|
||||||
if curl -fsS -m 3 "http://127.0.0.1:32400/identity" >/dev/null 2>&1; then
|
if curl -fsS -m 3 "http://127.0.0.1:32400/identity" >/dev/null 2>&1; then
|
||||||
echo " [ok] Plex is up"
|
ok "Plex is up"
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
sleep 3
|
sleep 3
|
||||||
done
|
done
|
||||||
|
|
||||||
# --- gate 3: disable auto-empty-trash, permanently ---------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# This is the single most dangerous default on this box. If a CIFS mount is
|
# Verify the migration actually landed
|
||||||
# missing at scan time Plex sees an empty library; with autoEmptyTrash on it
|
# ---------------------------------------------------------------------------
|
||||||
# begins trimming, and the NAS account is read-write. Turn it off in the
|
ID="$(curl -fsS -m 5 http://127.0.0.1:32400/identity 2>/dev/null | grep -o 'machineIdentifier="[^"]*"' | cut -d'"' -f2)"
|
||||||
# config rather than trusting a UI checkbox to stay ticked.
|
if [ "$ID" = "eb69ec8a9f38b64eeafe911e26d89baeaa78f0e3" ]; then
|
||||||
PREFS="$DEST/config/Library/Application Support/Plex Media Server/Preferences.xml"
|
ok "machineIdentifier matches the original server — identity preserved"
|
||||||
if [ -f "$PREFS" ]; then
|
|
||||||
if grep -q 'autoEmptyTrash="0"' "$PREFS"; then
|
|
||||||
echo " [ok] autoEmptyTrash already disabled"
|
|
||||||
else
|
|
||||||
echo " disabling autoEmptyTrash (requires a Plex restart)"
|
|
||||||
docker compose stop plex >/dev/null 2>&1
|
|
||||||
cp -a "$PREFS" "${PREFS}.bak.$(date +%Y%m%d%H%M%S)"
|
|
||||||
if grep -q 'autoEmptyTrash=' "$PREFS"; then
|
|
||||||
sed -i 's/autoEmptyTrash="[^"]*"/autoEmptyTrash="0"/' "$PREFS"
|
|
||||||
else
|
|
||||||
sed -i 's/<Preferences /<Preferences autoEmptyTrash="0" /' "$PREFS"
|
|
||||||
fi
|
|
||||||
chown 3000:3000 "$PREFS"
|
|
||||||
docker compose start plex >/dev/null 2>&1
|
|
||||||
echo " [ok] autoEmptyTrash=0 written"
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
cat <<'MSG'
|
bad "machineIdentifier is $ID, expected eb69ec8a9f38b64eeafe911e26d89baeaa78f0e3"
|
||||||
[WARN] Preferences.xml not written yet (first start is still initialising).
|
note "this is a DIFFERENT server. Shared users and client registrations are lost."
|
||||||
Re-run this script once Plex has fully started to disable
|
note "stop, and check the Preferences.xml step before adding anything."
|
||||||
autoEmptyTrash, OR untick it by hand:
|
|
||||||
Settings > Library > "Empty trash automatically after every scan"
|
|
||||||
Do this BEFORE adding any library. With read-write NAS credentials a
|
|
||||||
missing mount plus this setting deletes media records.
|
|
||||||
MSG
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cat <<'NEXT'
|
cat <<'NEXT'
|
||||||
|
|
||||||
Next:
|
Verify before you walk away:
|
||||||
* Open http://10.0.1.20:32400/web and confirm the server is claimed.
|
* https://plex.tv/claim was NOT used and must not be.
|
||||||
* Add libraries pointing at /media/... (the container paths), not /mnt/nas.
|
* Movies should show 1,360 titles; TV Shows 23,493 episodes.
|
||||||
* Verify a transcode is using Quick Sync:
|
* Continue Watching should be populated — that is the whole point.
|
||||||
docker exec plex ls -l /dev/dri
|
* A remote client that already had this server should still see it
|
||||||
# start a transcode, then:
|
without re-adding.
|
||||||
intel_gpu_top # Video/VideoEnhance rows should be busy
|
* Force a transcode, then: sudo intel_gpu_top
|
||||||
|
Video / VideoEnhance rows should be busy.
|
||||||
NEXT
|
NEXT
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
Reference in New Issue
Block a user