Skip to content

Instantly share code, notes, and snippets.

@brettpetch
Last active March 20, 2024 04:09
Show Gist options
  • Save brettpetch/23cdde62a21601ff283227a7aec342b8 to your computer and use it in GitHub Desktop.
Save brettpetch/23cdde62a21601ff283227a7aec342b8 to your computer and use it in GitHub Desktop.
A 4K Transcode Killing script with custom message for Jellyfin.

Jellyfin 4K Transcode Killer

  1. Ensure you have an operational Jellyfin install.
  2. Open Jellyfin settings, go to the API tab, generate an api credential named something like checker
  3. Run the commands below to create the neccesary files to run the software, changing the API_BASE_URL to the appropriate value and the API_KEY as well.

Installing the Script

Note: if you don't have root, you may install it in $HOME/.local/bin/

sudo nano /usr/local/bin/jellyfin-check.py

import requests
import time

API_BASE_URL = "http://127.0.0.1:8096"  # Replace with your server address
API_KEY = "CHANGEME" # Set to a valid api key

# Retrieve active sessions from the last 30 seconds
def get_active_sessions():
    url = f"{API_BASE_URL}/Sessions?nowPlaying=true&apikey={API_KEY}"
    response = requests.get(url)
    if response.status_code == 200:
        return response.json()
    return []

# Send a message to a specific session
def send_message(session_id):
    url = f"{API_BASE_URL}/Sessions/{session_id}/Message?apikey={API_KEY}"
    payload = {
        "Text": "4K Transcoding is not allowed, please try a lower quality version.",
        "TimeoutMs": 5000
    }
    response = requests.post(url, json=payload)
    return response.status_code == 200

# Stop transcoding for a specific session
def stop_transcoding(session_id):
    url = f"{API_BASE_URL}/Sessions/{session_id}/Playing/Stop?apikey={API_KEY}"
    response = requests.post(url)
    return response.status_code == 200

def main():
    while True:  # Infinite loop to keep checking every 30 seconds
        sessions = get_active_sessions()

        for session in sessions:
            # Check conditions
            if (
                not session.get("TranscodingInfo", {}).get("IsVideoDirect", True) and
                session.get("NowPlayingItem", {}).get("Width", 0) > 1921  # Assuming 4K resolution greater than 1920 pixels in width
            ):
                session_id = session.get("Id")

                if session_id:
                    send_message(session_id)
                    stop_transcoding(session_id)
                    print(f"Stopped transcoding for session ID: {session_id}")

        time.sleep(30)  # Wait for 30 seconds before checking again

if __name__ == "__main__":
    main()

Note: If you don't have root, this can be installed in $HOME/.config/systemd/user/. Be sure to change out the neccesary paths in commands / pastes below.

sudo chmod +x /usr/local/bin/jellyfin-check.py

Installing the Systemd Service

Note: If you don't have root, this can be installed in $HOME/.config/systemd/user/. Be sure to change out the neccesary paths in commands / pastes below.

sudo nano /etc/systemd/system/jellyfin-check.service

# /etc/systemd/system/jellyfin-check.service
[Unit]
Description=Jellyfin 4K Transcoding Checker
After=network.target

[Service]
Type=simple
User=nobody
Group=nogroup
ExecStart=/usr/local/bin/jellyfin-check.py
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Enabling the systemd service

Note: if you don't have root, you can use systemctl --user

systemctl enable --now jellyfin-check

Checking the logs

journalctl -fu jellyfin-check
@brettpetch
Copy link
Author

brettpetch commented Nov 14, 2023

The 4K checker code had a bug that only occured when true DirectPlays were happening. Thanks to one of my friends for making me check my work-- Issue: When TranscodingInfo was empty (in instances of a true direct play), it was setting IsVideoDirect to False instead of True. The fix in the code is written below for transparency.

-                not session.get("TranscodingInfo", {}).get("IsVideoDirect", False) and
+                not session.get("TranscodingInfo", {}).get("IsVideoDirect", True) and

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment