Delete plex/#!/usr/nas_setup.py
This commit is contained in:
parent
84f2ce7d6f
commit
8951f2fc24
@ -1,254 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
nas_setup.py
|
|
||||||
============
|
|
||||||
One-time setup script to run on your Synology NAS via SSH.
|
|
||||||
Installs SQLite triggers into the Plex database and starts the redirect proxy.
|
|
||||||
|
|
||||||
This script reads automation_config.json for all paths — no arguments needed.
|
|
||||||
|
|
||||||
USAGE:
|
|
||||||
python /volume1/Plex/scripts/nas_setup.py # Install triggers + patch existing
|
|
||||||
python /volume1/Plex/scripts/nas_setup.py --dry-run # Preview only, no changes
|
|
||||||
python /volume1/Plex/scripts/nas_setup.py --remove # Remove triggers
|
|
||||||
|
|
||||||
Run order:
|
|
||||||
1. Stop Plex Media Server
|
|
||||||
2. Run this script (installs triggers, patches existing .strm rows)
|
|
||||||
3. Start nas_strm_proxy.py in the background
|
|
||||||
4. Start Plex Media Server
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
import sqlite3
|
|
||||||
import argparse
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
CONFIG_FILE = os.path.join(SCRIPT_DIR, "automation_config.json")
|
|
||||||
|
|
||||||
TRIGGER_NAMES = ["strm_proxy_insert", "strm_proxy_update"]
|
|
||||||
|
|
||||||
|
|
||||||
def load_config():
|
|
||||||
if not os.path.exists(CONFIG_FILE):
|
|
||||||
print(f"[-] Cannot find {CONFIG_FILE}")
|
|
||||||
print(f" Make sure this script is in the same folder as automation_config.json")
|
|
||||||
sys.exit(1)
|
|
||||||
with open(CONFIG_FILE, "r") as f:
|
|
||||||
return json.load(f)
|
|
||||||
|
|
||||||
|
|
||||||
def build_triggers(strm_root: str, proxy_base: str) -> tuple:
|
|
||||||
root_len = len(strm_root.rstrip("/"))
|
|
||||||
like_pattern = f"{strm_root.rstrip('/')}/%.strm"
|
|
||||||
|
|
||||||
# Convert stored NAS path → proxy URL:
|
|
||||||
# /volume1/Plex/Library/Streams/Movies/Hindi/Film.strm
|
|
||||||
# → http://127.0.0.1:8086/Movies/Hindi/Film.mp4
|
|
||||||
url_expr = f"'{proxy_base}' || REPLACE(substr(NEW.file, {root_len + 1}), '.strm', '.mp4')"
|
|
||||||
|
|
||||||
insert_sql = f"""
|
|
||||||
CREATE TRIGGER IF NOT EXISTS strm_proxy_insert
|
|
||||||
AFTER INSERT ON media_parts
|
|
||||||
WHEN NEW.file LIKE '{like_pattern}'
|
|
||||||
BEGIN
|
|
||||||
UPDATE media_parts SET file = {url_expr} WHERE id = NEW.id;
|
|
||||||
END;
|
|
||||||
""".strip()
|
|
||||||
|
|
||||||
update_sql = f"""
|
|
||||||
CREATE TRIGGER IF NOT EXISTS strm_proxy_update
|
|
||||||
AFTER UPDATE OF file ON media_parts
|
|
||||||
WHEN NEW.file LIKE '{like_pattern}'
|
|
||||||
BEGIN
|
|
||||||
UPDATE media_parts SET file = {url_expr} WHERE id = NEW.id;
|
|
||||||
END;
|
|
||||||
""".strip()
|
|
||||||
|
|
||||||
return insert_sql, update_sql
|
|
||||||
|
|
||||||
|
|
||||||
def check_plex_running():
|
|
||||||
"""Warn if Plex appears to be running — writing to a live DB risks corruption."""
|
|
||||||
try:
|
|
||||||
out = subprocess.check_output(["pgrep", "-f", "Plex Media Server"], text=True).strip()
|
|
||||||
if out:
|
|
||||||
print("=" * 65)
|
|
||||||
print("[!] WARNING: Plex Media Server appears to be RUNNING!")
|
|
||||||
print("[!] Writing to the Plex database while Plex is open can")
|
|
||||||
print("[!] corrupt it. Stop Plex first, then run this script.")
|
|
||||||
print("[!] On Synology: synoservice --stop 'pkgctl-Plex Media Server'")
|
|
||||||
print("=" * 65)
|
|
||||||
ans = input("Continue anyway? (yes/no): ").strip().lower()
|
|
||||||
if ans != "yes":
|
|
||||||
print("[*] Aborted.")
|
|
||||||
sys.exit(0)
|
|
||||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
||||||
pass # pgrep not found or no matching process — assume safe
|
|
||||||
|
|
||||||
|
|
||||||
def install_triggers(db_path: str, strm_root: str, proxy_base: str, dry_run: bool):
|
|
||||||
print(f"\n[*] Database : {db_path}")
|
|
||||||
print(f"[*] STRM root : {strm_root}")
|
|
||||||
print(f"[*] Proxy base : {proxy_base}")
|
|
||||||
|
|
||||||
if not os.path.exists(db_path):
|
|
||||||
print(f"\n[-] ERROR: Plex database not found at:\n {db_path}")
|
|
||||||
print(" Has Plex been started at least once to create its database?")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
insert_sql, update_sql = build_triggers(strm_root, proxy_base)
|
|
||||||
|
|
||||||
if dry_run:
|
|
||||||
print("\n[DRY RUN] SQL that would be installed:\n")
|
|
||||||
print("-- INSERT TRIGGER:\n" + insert_sql)
|
|
||||||
print("\n-- UPDATE TRIGGER:\n" + update_sql)
|
|
||||||
print("\n[DRY RUN] No changes made.")
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
conn = sqlite3.connect(db_path)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Drop old triggers if they exist
|
|
||||||
cursor.execute(
|
|
||||||
"SELECT name FROM sqlite_master WHERE type='trigger' AND name IN (?,?)",
|
|
||||||
TRIGGER_NAMES,
|
|
||||||
)
|
|
||||||
existing = [r[0] for r in cursor.fetchall()]
|
|
||||||
if existing:
|
|
||||||
print(f"[!] Removing old triggers: {existing}")
|
|
||||||
for name in existing:
|
|
||||||
cursor.execute(f"DROP TRIGGER IF EXISTS {name}")
|
|
||||||
conn.commit()
|
|
||||||
|
|
||||||
print("[*] Installing INSERT trigger...")
|
|
||||||
cursor.executescript(insert_sql)
|
|
||||||
print("[*] Installing UPDATE trigger...")
|
|
||||||
cursor.executescript(update_sql)
|
|
||||||
conn.commit()
|
|
||||||
|
|
||||||
cursor.execute(
|
|
||||||
"SELECT name FROM sqlite_master WHERE type='trigger' AND name IN (?,?)",
|
|
||||||
TRIGGER_NAMES,
|
|
||||||
)
|
|
||||||
installed = [r[0] for r in cursor.fetchall()]
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
print(f"[+] Triggers installed: {installed}")
|
|
||||||
|
|
||||||
except sqlite3.Error as e:
|
|
||||||
print(f"[-] SQLite error: {e}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def patch_existing(db_path: str, strm_root: str, proxy_base: str, dry_run: bool):
|
|
||||||
"""Directly UPDATE existing media_parts rows that still have raw .strm paths."""
|
|
||||||
print("\n[*] Patching already-scanned .strm rows in media_parts...")
|
|
||||||
strm_root = strm_root.rstrip("/")
|
|
||||||
root_len = len(strm_root)
|
|
||||||
like = f"{strm_root}/%.strm"
|
|
||||||
|
|
||||||
try:
|
|
||||||
conn = sqlite3.connect(db_path)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
cursor.execute(
|
|
||||||
"SELECT id, file FROM media_parts WHERE file LIKE ? AND deleted_at IS NULL",
|
|
||||||
(like,),
|
|
||||||
)
|
|
||||||
rows = cursor.fetchall()
|
|
||||||
|
|
||||||
if not rows:
|
|
||||||
print("[*] No unpatched .strm rows found — nothing to do.")
|
|
||||||
conn.close()
|
|
||||||
return
|
|
||||||
|
|
||||||
print(f"[*] Found {len(rows)} row(s) to patch:")
|
|
||||||
for part_id, file_path in rows:
|
|
||||||
rel = file_path[root_len:]
|
|
||||||
proxy_url = proxy_base + rel.replace(".strm", ".mp4")
|
|
||||||
print(f" {file_path}")
|
|
||||||
print(f" → {proxy_url}")
|
|
||||||
if not dry_run:
|
|
||||||
cursor.execute("UPDATE media_parts SET file = ? WHERE id = ?", (proxy_url, part_id))
|
|
||||||
|
|
||||||
if not dry_run:
|
|
||||||
conn.commit()
|
|
||||||
print(f"\n[+] Patched {len(rows)} row(s).")
|
|
||||||
else:
|
|
||||||
print(f"\n[DRY RUN] Would patch {len(rows)} row(s) — no changes made.")
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
except sqlite3.Error as e:
|
|
||||||
print(f"[-] SQLite error during patch: {e}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def remove_triggers(db_path: str):
|
|
||||||
print("[*] Removing STRM proxy triggers...")
|
|
||||||
try:
|
|
||||||
conn = sqlite3.connect(db_path)
|
|
||||||
for name in TRIGGER_NAMES:
|
|
||||||
conn.execute(f"DROP TRIGGER IF EXISTS {name}")
|
|
||||||
print(f"[+] Dropped: {name}")
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
print("[+] Done. Plex will now store raw .strm paths again on next scan.")
|
|
||||||
except sqlite3.Error as e:
|
|
||||||
print(f"[-] SQLite error: {e}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="One-time Plex DB trigger installer for NAS-native STRM playback."
|
|
||||||
)
|
|
||||||
parser.add_argument("--dry-run", action="store_true", help="Preview changes, write nothing")
|
|
||||||
parser.add_argument("--remove", action="store_true", help="Remove installed triggers")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
print("=" * 65)
|
|
||||||
print(" PLEX STRM TRIGGER SETUP")
|
|
||||||
print("=" * 65)
|
|
||||||
|
|
||||||
cfg = load_config()
|
|
||||||
|
|
||||||
db_path = cfg.get("plex_db_path", (
|
|
||||||
"/volume1/Plex/Library/Application Support/"
|
|
||||||
"Plex Media Server/Plug-in Support/Databases/"
|
|
||||||
"com.plexapp.plugins.library.db"
|
|
||||||
))
|
|
||||||
strm_root = cfg.get("output_dir", "/volume1/Plex/Library/Streams").rstrip("/")
|
|
||||||
proxy_port = cfg.get("nas_proxy_port", 8086)
|
|
||||||
proxy_base = f"http://127.0.0.1:{proxy_port}"
|
|
||||||
|
|
||||||
if args.remove:
|
|
||||||
remove_triggers(db_path)
|
|
||||||
return
|
|
||||||
|
|
||||||
check_plex_running()
|
|
||||||
install_triggers(db_path, strm_root, proxy_base, args.dry_run)
|
|
||||||
patch_existing(db_path, strm_root, proxy_base, args.dry_run)
|
|
||||||
|
|
||||||
if not args.dry_run:
|
|
||||||
print()
|
|
||||||
print("=" * 65)
|
|
||||||
print("[+] Setup complete!")
|
|
||||||
print()
|
|
||||||
print("Next steps:")
|
|
||||||
print(f" 1. Start the redirect proxy (keep it running permanently):")
|
|
||||||
print(f" nohup python {SCRIPT_DIR}/nas_strm_proxy.py > /tmp/strm_proxy.log 2>&1 &")
|
|
||||||
print()
|
|
||||||
print(f" 2. Start Plex Media Server")
|
|
||||||
print(f" synoservice --start 'pkgctl-Plex Media Server'")
|
|
||||||
print()
|
|
||||||
print(f" 3. Trigger a library scan in Plex, or run run_automation.py")
|
|
||||||
print("=" * 65)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
Loading…
x
Reference in New Issue
Block a user