From ef030b3da0439cb0c061914f1b98b017b8722963 Mon Sep 17 00:00:00 2001 From: SergeantPanda Date: Sun, 12 Apr 2026 10:03:46 -0500 Subject: [PATCH] Bug Fix: Manual stream selection from the Stats page not enforcing M3U profile connection limits in multi-worker deployments --- CHANGELOG.md | 1 + apps/proxy/ts_proxy/server.py | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d91965c0..237b1e58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed manual stream selection from the Stats page not enforcing M3U profile connection limits in multi-worker deployments. When a non-owning worker handled the `change_stream` request it correctly packaged `stream_id` and `m3u_profile_id` into the Redis pubsub message, but the owning worker's pubsub handler only consumed `url` and `user_agent` silently dropping both IDs before calling `stream_manager.update_url()`. Because `update_url` only calls `update_stream_profile()` when a `stream_id` is provided, the `profile_connections` counter was never updated after the switch, causing subsequent capacity checks to see incorrect counts and bypass the full-profile guard. The handler now extracts `stream_id` and `m3u_profile_id` from the event and forwards them to `update_url()`. - Fixed uploading a local M3U file with no expiration date set sending the string `"null"` as the `exp_date` field in the `FormData` request, causing a 400 validation error from the API. Null/undefined values are now skipped when building the `FormData` body, matching the behaviour already present in the update path. - Fixed `PATCH /api/channels/channels/edit/bulk/` returning a 500 error when the request body included a `streams` list. The bulk edit handler was iterating `validated_data` directly and calling `setattr(channel, "streams", value)`, which Django prohibits on ManyToMany fields. Also added an `@extend_schema` decorator so the Swagger UI correctly documents the endpoint as accepting a JSON array and shows the `streams` field. (Fixes #883) - Fixed several incorrect or incomplete OpenAPI (`@extend_schema`) schemas across the API: diff --git a/apps/proxy/ts_proxy/server.py b/apps/proxy/ts_proxy/server.py index 5316ec58..1e87ea62 100644 --- a/apps/proxy/ts_proxy/server.py +++ b/apps/proxy/ts_proxy/server.py @@ -227,6 +227,8 @@ class ProxyServer: # Handle stream switch request new_url = data.get("url") user_agent = data.get("user_agent") + event_stream_id = data.get("stream_id") + event_m3u_profile_id = data.get("m3u_profile_id") if new_url and channel_id in self.stream_managers: # Update metadata in Redis @@ -240,9 +242,9 @@ class ProxyServer: status_key = RedisKeys.switch_status(channel_id) self.redis_client.set(status_key, "switching") - # Perform the stream switch + # Perform the stream switch, forwarding stream_id and m3u_profile_id stream_manager = self.stream_managers[channel_id] - success = stream_manager.update_url(new_url) + success = stream_manager.update_url(new_url, event_stream_id, event_m3u_profile_id) if success: logger.info(f"Stream switch initiated for channel {channel_id}")