Plex Transcode Monitor

Helps monitor Plex transcodes and stops the user's stream if they are either using a web browser and transcoding, or transcoding using the native Plex app.
 avatar
unknown
python
6 months ago
4.3 kB
163
No Index
import time
import requests
import logging
from flask import Flask, request

TAUTULLI_API_URL = "http://<ENDPOINT>:8181/api/v2"
TAUTULLI_API_KEY = "<API KEY>"
RETRY_WINDOW = 3600 # 1 hour
MAX_RETRIES = 1
BROWSER_CLIENTS = set(["plex web", "chrome", "firefox", "edge", "safari"])

user_retry_tracker = {} # { "user_id": {"retries": int, "last_attempt": float}}

app = Flask(__name__)

logging.basicConfig(
    filename='Plex Transcode Monitor/plex_transcode_monitor.log',
    level=logging.DEBUG,
    format='%(asctime)s - %(levelname)s - %(message)s',
)

def stop_transcoding_session(session_id, user, message):
    """Send a command to stop the transcoding session."""
    try:
        response = requests.get(
            TAUTULLI_API_URL,
            params={
                "apikey": TAUTULLI_API_KEY,
                "cmd": "terminate_session",
                "session_id": session_id,
                "message": message,
            },
        )
        response.raise_for_status()
        logging.info(f"Stream stopped: {session_id}, User: {user} Message: {message}")
    except requests.RequestException as e:
        logging.error(f"Error stopping stream: {e}")

def should_allow_retry(user_id):
    """Check if the user is within the retry window and track retries."""
    current_time = time.time()

    # Initialize and block the first attempt
    if user_id not in user_retry_tracker:    
        user_retry_tracker[user_id] = {"last_attempt": current_time, "retries": 0}
        return False

    user_data = user_retry_tracker[user_id]
    last_attempt_time = user_data["last_attempt"]
    retries = user_data["retries"]

    # Check if we're within the retry window and within max allowed retries,
    # or reset retries if outside the retry window.
    if current_time - last_attempt_time <= RETRY_WINDOW:
        if retries < MAX_RETRIES:
            user_retry_tracker[user_id]["retries"] += 1
            user_retry_tracker[user_id]["last_attempt"] = current_time
            return False
        else:
            return True
    else:
        user_retry_tracker[user_id] = {"last_attempt": current_time, "retries": 1}
        return False  # Block since it's the first attempt after the retry window

@app.route("/webhook", methods=["POST"])
def webhook():
    """Handle Tautulli webhook."""
    try:
        data = request.json

        user = data.get("user")
        session_id = data.get("session_id")
        transcode_decision = data.get("transcode_decision")
        platform = data.get("platform")

        if not user or not session_id or not transcode_decision or not platform:
            logging.error("Invalid data received")
            return "Invalid data", 400

        if transcode_decision.lower() == "transcode":
            if platform.lower() in BROWSER_CLIENTS:
                message = (
                    "You are using a web browser to watch this video. This slows down your video stream "
                    "and the server as it has to convert the video to a compatible format. Please download "
                    "the native Plex application for your device and update the settings to stream videos in 'Original' "
                    "or 'Maximum' quality. If you really need to watch in a web browser, you can retry playing this video."
                )
            else:
                message = (
                    "You are not playing the current video in full quality. This slows down your video stream "
                    "and the server as it has to convert the original video to a different size. Please update your "
                    "Plex application settings to stream videos in 'Original' or 'Maximum' quality. If you really need to "
                    "watch in lower quality than the original file, you can retry playing this video."
                )

            if should_allow_retry(user):
                logging.info(f"User {user} allowed to play transcoding video after {user_retry_tracker[user]['retries']} retries.")
            else:
                stop_transcoding_session(session_id, user, message)

        return "OK", 200

    except Exception as e:
        logging.error(f"Error processing webhook: {e}")
        return "Internal Server Error", 500

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=2000)
Editor is loading...