Delete nas_strm_proxy.py
This commit is contained in:
parent
a712d5da15
commit
47591c39d0
@ -1,193 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
nas_strm_proxy.py
|
|
||||||
=================
|
|
||||||
Lightweight redirect-only HTTP proxy for Plex .strm playback on Synology NAS.
|
|
||||||
Compatible with Python 3.5+.
|
|
||||||
|
|
||||||
How it works:
|
|
||||||
1. SQLite triggers (installed by nas_setup.py) store proxy URLs in Plex DB:
|
|
||||||
http://127.0.0.1:8086/Movies/Hindi/Film.mp4
|
|
||||||
|
|
||||||
2. When Plex plays that item it sends:
|
|
||||||
GET /Movies/Hindi/Film.mp4 to this server
|
|
||||||
|
|
||||||
3. This server:
|
|
||||||
a. Strips .mp4 -> adds .strm
|
|
||||||
b. Reads: /volume1/Plex/Library/Streams/Movies/Hindi/Film.strm
|
|
||||||
c. Gets the raw IPTV URL from inside the file
|
|
||||||
d. Returns HTTP 302 -> real stream URL
|
|
||||||
|
|
||||||
4. Plex server follows the 302 on the server side.
|
|
||||||
Browser only talks HTTPS to Plex -- no mixed content block.
|
|
||||||
|
|
||||||
USAGE (SSH into Synology):
|
|
||||||
python3 nas_strm_proxy.py
|
|
||||||
python3 nas_strm_proxy.py --port 8086 --strm-root /volume1/Plex/Library/Streams
|
|
||||||
|
|
||||||
Run permanently on boot via Synology Task Scheduler (triggered task at boot):
|
|
||||||
nohup python3 /volume1/Plex/scripts/nas_strm_proxy.py > /tmp/strm_proxy.log 2>&1 &
|
|
||||||
"""
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
||||||
try:
|
|
||||||
from urllib.parse import unquote
|
|
||||||
except ImportError:
|
|
||||||
from urllib import unquote # Python 2 fallback (not expected)
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
|
||||||
# DEFAULTS -- adjust to match your Synology NAS layout
|
|
||||||
# -----------------------------------------------------------------------
|
|
||||||
DEFAULT_STRM_ROOT = "/volume1/Plex/Library/Streams"
|
|
||||||
DEFAULT_BIND_HOST = "127.0.0.1" # loopback -- Plex is on the same machine
|
|
||||||
DEFAULT_PORT = 8086
|
|
||||||
|
|
||||||
|
|
||||||
def make_handler(strm_root):
|
|
||||||
"""Return a request handler class with strm_root baked in."""
|
|
||||||
|
|
||||||
class StrmRedirectHandler(BaseHTTPRequestHandler):
|
|
||||||
|
|
||||||
def log_message(self, fmt, *args):
|
|
||||||
print("[" + self.address_string() + "] " + (fmt % args), flush=True)
|
|
||||||
|
|
||||||
def do_HEAD(self):
|
|
||||||
self._handle(head_only=True)
|
|
||||||
|
|
||||||
def do_GET(self):
|
|
||||||
self._handle(head_only=False)
|
|
||||||
|
|
||||||
def _handle(self, head_only=False):
|
|
||||||
method = "HEAD" if head_only else "GET"
|
|
||||||
|
|
||||||
# 1. Decode the request path
|
|
||||||
raw_path = (self.path or "/").split("?")[0].split("#")[0]
|
|
||||||
try:
|
|
||||||
decoded_path = unquote(raw_path)
|
|
||||||
except Exception:
|
|
||||||
decoded_path = raw_path
|
|
||||||
|
|
||||||
print("\n[->] Request: " + method + " " + decoded_path, flush=True)
|
|
||||||
|
|
||||||
# 2. Security: prevent path traversal
|
|
||||||
full_path = os.path.normpath(os.path.join(strm_root, decoded_path.lstrip("/")))
|
|
||||||
if not full_path.startswith(strm_root):
|
|
||||||
print("[-] Path traversal attempt blocked: " + decoded_path, flush=True)
|
|
||||||
self._send(403, "Forbidden")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 3. Map .mp4 / .mkv / etc. -> .strm
|
|
||||||
strm_path = re.sub(r"\.(mp4|mkv|avi|mov|ts)$", ".strm", full_path, flags=re.IGNORECASE)
|
|
||||||
if not strm_path.endswith(".strm"):
|
|
||||||
strm_path = full_path + ".strm"
|
|
||||||
|
|
||||||
print("[*] Mapping to: " + strm_path, flush=True)
|
|
||||||
|
|
||||||
# 4. Check the .strm file exists
|
|
||||||
if not os.path.isfile(strm_path):
|
|
||||||
print("[-] .strm file not found: " + strm_path, flush=True)
|
|
||||||
self._send(404, "STRM file not found: " + os.path.basename(strm_path))
|
|
||||||
return
|
|
||||||
|
|
||||||
# 5. Read the URL from the .strm file
|
|
||||||
try:
|
|
||||||
with open(strm_path, "r") as f:
|
|
||||||
stream_url = f.read().strip()
|
|
||||||
except OSError as e:
|
|
||||||
print("[-] Cannot read .strm file: " + str(e), flush=True)
|
|
||||||
self._send(500, "Cannot read STRM file: " + str(e))
|
|
||||||
return
|
|
||||||
|
|
||||||
if not stream_url:
|
|
||||||
print("[-] .strm file is empty: " + strm_path, flush=True)
|
|
||||||
self._send(500, "STRM file is empty")
|
|
||||||
return
|
|
||||||
|
|
||||||
if not (stream_url.startswith("http://") or stream_url.startswith("https://")):
|
|
||||||
print("[-] Invalid URL in .strm file: " + stream_url[:80], flush=True)
|
|
||||||
self._send(500, "STRM file does not contain a valid HTTP/HTTPS URL")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 6. Return 302 redirect -- Plex server follows this, browser never sees it
|
|
||||||
print("[->] 302 Redirect -> " + stream_url[:100], flush=True)
|
|
||||||
self.send_response(302)
|
|
||||||
self.send_header("Location", stream_url)
|
|
||||||
self.send_header("Content-Length", "0")
|
|
||||||
self.send_header("Cache-Control", "no-cache")
|
|
||||||
self.end_headers()
|
|
||||||
|
|
||||||
def _send(self, code, message):
|
|
||||||
body = message.encode("utf-8")
|
|
||||||
self.send_response(code)
|
|
||||||
self.send_header("Content-Type", "text/plain; charset=utf-8")
|
|
||||||
self.send_header("Content-Length", str(len(body)))
|
|
||||||
self.end_headers()
|
|
||||||
self.wfile.write(body)
|
|
||||||
|
|
||||||
return StrmRedirectHandler
|
|
||||||
|
|
||||||
|
|
||||||
def run_server(host, port, strm_root):
|
|
||||||
strm_root = strm_root.rstrip("/")
|
|
||||||
|
|
||||||
print("=" * 65)
|
|
||||||
print(" PLEX STRM REDIRECT PROXY (NAS Edition)")
|
|
||||||
print("=" * 65)
|
|
||||||
print("[*] Listening on : http://" + host + ":" + str(port))
|
|
||||||
print("[*] STRM root : " + strm_root)
|
|
||||||
print("[*] Behaviour : GET /Movies/Film.mp4")
|
|
||||||
print(" reads " + strm_root + "/Movies/Film.strm")
|
|
||||||
print(" 302 redirect -> real stream URL")
|
|
||||||
print()
|
|
||||||
|
|
||||||
if not os.path.isdir(strm_root):
|
|
||||||
print("[!] WARNING: STRM root directory not found: " + strm_root)
|
|
||||||
print("[!] The server will start but all requests will return 404.")
|
|
||||||
print("[!] Check your --strm-root path.")
|
|
||||||
print()
|
|
||||||
|
|
||||||
handler = make_handler(strm_root)
|
|
||||||
server = HTTPServer((host, port), handler)
|
|
||||||
|
|
||||||
print("[+] Server started. Press CTRL+C to stop.\n", flush=True)
|
|
||||||
try:
|
|
||||||
server.serve_forever()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print("\n[*] Shutting down proxy server...")
|
|
||||||
server.server_close()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="Redirect-only HTTP proxy for Plex .strm file playback on Synology NAS."
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--strm-root",
|
|
||||||
default=DEFAULT_STRM_ROOT,
|
|
||||||
help="Root directory containing .strm files (default: " + DEFAULT_STRM_ROOT + ")",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--port",
|
|
||||||
type=int,
|
|
||||||
default=DEFAULT_PORT,
|
|
||||||
help="Port to listen on (default: " + str(DEFAULT_PORT) + ")",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--bind",
|
|
||||||
default=DEFAULT_BIND_HOST,
|
|
||||||
help="IP to bind to (default: " + DEFAULT_BIND_HOST + " -- loopback only)",
|
|
||||||
)
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
if not os.path.isdir(args.strm_root):
|
|
||||||
print("[!] WARNING: STRM root not found: " + args.strm_root)
|
|
||||||
|
|
||||||
run_server(args.bind, args.port, args.strm_root)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
Loading…
x
Reference in New Issue
Block a user