From f58fd08e43d5ea61728cabcf5c21151ae6d74d4f Mon Sep 17 00:00:00 2001 From: Simon Gardling Date: Tue, 21 Oct 2025 23:39:44 -0400 Subject: [PATCH] jellyfin-qbittorrent-monitor: only count external networks --- services/jellyfin-qbittorrent-monitor.py | 33 +++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/services/jellyfin-qbittorrent-monitor.py b/services/jellyfin-qbittorrent-monitor.py index 6a0cec5..27bc167 100644 --- a/services/jellyfin-qbittorrent-monitor.py +++ b/services/jellyfin-qbittorrent-monitor.py @@ -6,6 +6,7 @@ import logging import sys import signal import json +import ipaddress logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" @@ -36,6 +37,25 @@ class JellyfinQBittorrentMonitor: self.streaming_stop_delay = 60 self.last_state_change = 0 + # Local network ranges (RFC 1918 private networks + localhost) + self.local_networks = [ + ipaddress.ip_network("10.0.0.0/8"), + ipaddress.ip_network("172.16.0.0/12"), + ipaddress.ip_network("192.168.0.0/16"), + ipaddress.ip_network("127.0.0.0/8"), + ipaddress.ip_network("::1/128"), # IPv6 localhost + ipaddress.ip_network("fe80::/10"), # IPv6 link-local + ] + + def is_local_ip(self, ip_address: str) -> bool: + """Check if an IP address is from a local network""" + try: + ip = ipaddress.ip_address(ip_address) + return any(ip in network for network in self.local_networks) + except ValueError: + logger.warning(f"Invalid IP address format: {ip_address}") + return True # Treat invalid IPs as local for safety + def signal_handler(self, signum, frame): logger.info("Received shutdown signal, cleaning up...") self.running = False @@ -43,7 +63,7 @@ class JellyfinQBittorrentMonitor: sys.exit(0) def check_jellyfin_sessions(self) -> list[str]: - """Check if anyone is actively streaming from Jellyfin""" + """Check if anyone is actively streaming from Jellyfin (external networks only)""" try: headers = {} if self.jellyfin_api_key: @@ -55,19 +75,26 @@ class JellyfinQBittorrentMonitor: response.raise_for_status() sessions = response.json() - # Count active streaming sessions (video only) + # Count active streaming sessions (video only, external networks only) active_streams = [] for session in sessions: if ( "NowPlayingItem" in session and session.get("PlayState", {}).get("IsPaused", True) == False ): + # Check if session is from external network + remote_endpoint = session.get("RemoteEndPoint", "") + if remote_endpoint and self.is_local_ip(remote_endpoint): + logger.debug(f"Skipping local session from {remote_endpoint}") + continue + item = session["NowPlayingItem"] # Only count video streams (Movies, Episodes, etc.) item_type = item.get("Type", "").lower() if item_type in ["movie", "episode", "video"]: user = session.get("UserName", "Unknown") - active_streams.append(f"{user}: {item.get('Name', 'Unknown')}") + client_info = f" (from {remote_endpoint})" if remote_endpoint else "" + active_streams.append(f"{user}: {item.get('Name', 'Unknown')}{client_info}") return active_streams