fix disabling alternate limits

This commit is contained in:
Simon Gardling 2025-09-12 01:27:17 -04:00
parent 43317044f2
commit 4729bd2cc4
Signed by: titaniumtown
GPG Key ID: 9AB28AC10ECE533D

View File

@ -32,8 +32,8 @@ class JellyfinQBittorrentMonitor:
self.session = requests.Session() # Use session for cookies self.session = requests.Session() # Use session for cookies
# Hysteresis settings to prevent rapid switching # Hysteresis settings to prevent rapid switching
self.streaming_start_delay = 10 # seconds to wait before throttling self.streaming_start_delay = 10
self.streaming_stop_delay = 60 # seconds to wait before removing throttle self.streaming_stop_delay = 60
self.last_state_change = 0 self.last_state_change = 0
# Try to authenticate with qBittorrent # Try to authenticate with qBittorrent
@ -50,18 +50,11 @@ class JellyfinQBittorrentMonitor:
logger.info("Attempting to authenticate with qBittorrent...") logger.info("Attempting to authenticate with qBittorrent...")
try: try:
# First, try to access a simple endpoint to see if auth is needed
test_response = self.session.get( test_response = self.session.get(
f"{self.qbittorrent_url}/api/v2/app/version", timeout=5 f"{self.qbittorrent_url}/api/v2/app/version", timeout=5
) )
logger.info(
f"Version endpoint test: HTTP {test_response.status_code}, Response: {test_response.text}"
)
if test_response.status_code == 200: if test_response.status_code == 200:
logger.info( logger.info("qBittorrent accessible without explicit login - subnet whitelist working")
"qBittorrent accessible without explicit login - subnet whitelist working"
)
return True return True
except Exception as e: except Exception as e:
@ -75,7 +68,6 @@ class JellyfinQBittorrentMonitor:
"Content-Type": "application/x-www-form-urlencoded", "Content-Type": "application/x-www-form-urlencoded",
} }
logger.info(f"Attempting login to {self.qbittorrent_url}/login")
response = self.session.post( response = self.session.post(
f"{self.qbittorrent_url}/login", f"{self.qbittorrent_url}/login",
data=login_data, data=login_data,
@ -83,22 +75,13 @@ class JellyfinQBittorrentMonitor:
timeout=10, timeout=10,
) )
logger.info( if response.status_code == 200 and ("Ok." in response.text or response.text.strip() == "Ok."):
f"Login response: HTTP {response.status_code}, Response: '{response.text}'"
)
if response.status_code == 200:
if "Ok." in response.text or response.text.strip() == "Ok.":
logger.info("Successfully authenticated with qBittorrent") logger.info("Successfully authenticated with qBittorrent")
return True return True
elif "Fails." in response.text: elif "Fails." in response.text:
logger.warning( logger.warning("qBittorrent login failed - authentication may be required")
"qBittorrent login failed - authentication may be required"
)
else: else:
logger.info(f"Unexpected login response: '{response.text}'") logger.warning(f"Login failed: HTTP {response.status_code}")
else:
logger.warning(f"Login request failed with HTTP {response.status_code}")
except Exception as e: except Exception as e:
logger.error(f"Could not authenticate with qBittorrent: {e}") logger.error(f"Could not authenticate with qBittorrent: {e}")
@ -140,97 +123,65 @@ class JellyfinQBittorrentMonitor:
def check_qbittorrent_alternate_limits(self): def check_qbittorrent_alternate_limits(self):
"""Check if alternate speed limits are currently enabled""" """Check if alternate speed limits are currently enabled"""
# For qBittorrent v5.1.0, use API v2 with GET requests
try: try:
# Try the transfer info endpoint first (more reliable) response = self.session.get(
f"{self.qbittorrent_url}/api/v2/transfer/speedLimitsMode", timeout=10
)
if response.status_code == 200:
return response.text.strip() == "1"
else:
logger.warning(f"SpeedLimitsMode endpoint returned HTTP {response.status_code}")
except requests.exceptions.RequestException as e:
logger.error(f"SpeedLimitsMode endpoint failed: {e}")
except Exception as e:
logger.error(f"Failed to parse speedLimitsMode response: {e}")
# Fallback: try transfer info endpoint
try:
response = self.session.get( response = self.session.get(
f"{self.qbittorrent_url}/api/v2/transfer/info", timeout=10 f"{self.qbittorrent_url}/api/v2/transfer/info", timeout=10
) )
logger.info(f"Transfer info endpoint: HTTP {response.status_code}")
if response.status_code == 200: if response.status_code == 200:
data = response.json() data = response.json()
logger.info(f"Transfer info keys: {list(data.keys())}")
# Check for alternative speed limit status in the response
if "use_alt_speed_limits" in data: if "use_alt_speed_limits" in data:
is_enabled = data["use_alt_speed_limits"] return data["use_alt_speed_limits"]
logger.info(f"Alternative speed limits enabled: {is_enabled}")
return is_enabled
response.raise_for_status()
except requests.exceptions.RequestException as e:
logger.error(f"Transfer info endpoint failed: {e}")
except json.JSONDecodeError as e:
logger.error(f"Failed to parse transfer info JSON: {e}")
# Fallback: try app preferences endpoint
try:
response = self.session.get(
f"{self.qbittorrent_url}/api/v2/app/preferences", timeout=10
)
logger.info(f"Preferences endpoint: HTTP {response.status_code}")
if response.status_code == 200:
data = response.json()
# Look for alternative speed settings
if "alt_up_limit" in data or "scheduler_enabled" in data:
# Check if alternative speeds are currently active
# This is a bit indirect but should work
logger.info(
"Found preferences data, assuming alt speeds not active by default"
)
return False
except Exception as e: except Exception as e:
logger.error(f"Preferences endpoint failed: {e}") logger.error(f"Transfer info fallback failed: {e}")
logger.error( logger.warning("Could not determine qBittorrent alternate limits status, using tracked state")
"Failed to check qBittorrent alternate limits status: all endpoints failed" return self.throttle_active
)
return False
def toggle_qbittorrent_limits(self, enable_throttle): def toggle_qbittorrent_limits(self, enable_throttle):
"""Toggle qBittorrent alternate speed limits""" """Toggle qBittorrent alternate speed limits"""
try: try:
# Check current state
current_throttle = self.check_qbittorrent_alternate_limits() current_throttle = self.check_qbittorrent_alternate_limits()
if enable_throttle and not current_throttle: if current_throttle == enable_throttle:
try: action = "enabled" if enable_throttle else "disabled"
# Use API v2 POST endpoint to toggle alternative speed limits logger.info(f"Alternate speed limits already {action}, no action needed")
return
response = self.session.post( response = self.session.post(
f"{self.qbittorrent_url}/api/v2/transfer/toggleSpeedLimitsMode", f"{self.qbittorrent_url}/api/v2/transfer/toggleSpeedLimitsMode",
timeout=10, timeout=10,
) )
logger.info(
f"Toggle enable response: HTTP {response.status_code}, {response.text[:100]}"
)
response.raise_for_status() response.raise_for_status()
self.throttle_active = True
logger.info("✓ Enabled alternate speed limits (throttling)")
return
except requests.exceptions.RequestException as e:
logger.error(f"Failed to enable alternate speed limits: {e}")
elif not enable_throttle and current_throttle: self.throttle_active = enable_throttle
try:
# Use API v2 POST endpoint to toggle alternative speed limits
response = self.session.post(
f"{self.qbittorrent_url}/api/v2/transfer/toggleSpeedLimitsMode",
timeout=10,
)
logger.info(
f"Toggle disable response: HTTP {response.status_code}, {response.text[:100]}"
)
response.raise_for_status()
self.throttle_active = False
logger.info("✓ Disabled alternate speed limits (normal)")
return
except requests.exceptions.RequestException as e:
logger.error(f"Failed to disable alternate speed limits: {e}")
# Verify the change took effect
new_state = self.check_qbittorrent_alternate_limits()
if new_state == enable_throttle:
action = "enabled" if enable_throttle else "disabled"
logger.info(f"✓ Successfully {action} alternate speed limits")
else:
logger.warning(f"Toggle may have failed: expected {enable_throttle}, got {new_state}")
except requests.exceptions.RequestException as e:
action = "enable" if enable_throttle else "disable"
logger.error(f"Failed to {action} alternate speed limits: {e}")
except Exception as e: except Exception as e:
logger.error(f"Failed to toggle qBittorrent limits: {e}") logger.error(f"Failed to toggle qBittorrent limits: {e}")
@ -244,24 +195,28 @@ class JellyfinQBittorrentMonitor:
"""Apply hysteresis to prevent rapid state changes""" """Apply hysteresis to prevent rapid state changes"""
now = time.time() now = time.time()
# If state hasn't changed, no action needed
if new_streaming_state == self.last_streaming_state: if new_streaming_state == self.last_streaming_state:
return False return False
# Calculate time since last state change
time_since_change = now - self.last_state_change time_since_change = now - self.last_state_change
# If we want to start throttling (streaming started) # Start throttling (streaming started)
if new_streaming_state and not self.last_streaming_state: if new_streaming_state and not self.last_streaming_state:
if time_since_change >= self.streaming_start_delay: if time_since_change >= self.streaming_start_delay:
self.last_state_change = now self.last_state_change = now
return True return True
else:
remaining = self.streaming_start_delay - time_since_change
logger.info(f"Streaming started - waiting {remaining:.1f}s before enabling throttling")
# If we want to stop throttling (streaming stopped) # Stop throttling (streaming stopped)
elif not new_streaming_state and self.last_streaming_state: elif not new_streaming_state and self.last_streaming_state:
if time_since_change >= self.streaming_stop_delay: if time_since_change >= self.streaming_stop_delay:
self.last_state_change = now self.last_state_change = now
return True return True
else:
remaining = self.streaming_stop_delay - time_since_change
logger.info(f"Streaming stopped - waiting {remaining:.1f}s before disabling throttling")
return False return False
@ -287,8 +242,8 @@ class JellyfinQBittorrentMonitor:
logger.info( logger.info(
f"Active streams ({len(active_streams)}): {', '.join(active_streams)}" f"Active streams ({len(active_streams)}): {', '.join(active_streams)}"
) )
else: elif len(active_streams) == 0 and self.last_streaming_state:
logger.debug("No active streaming sessions") logger.info("No active streaming sessions")
# Apply hysteresis and change state if needed # Apply hysteresis and change state if needed
if self.should_change_state(streaming_active): if self.should_change_state(streaming_active):