Commit graph

77 commits

Author SHA1 Message Date
None
c96603f923 Merge remote-tracking branch 'upstream/dev' into fix/947-Connection-capacity-leak-failed-stream-initialization-permanently-consumes-connection-slot 2026-03-03 14:02:52 -06:00
SergeantPanda
6d7130d71e Bug Fix: fix get_instance deadlock and non-atomic ownership acquisition
If ProxyServer() raises during singleton construction, _instance was left
as _INITIALIZING permanently, causing all subsequent get_instance() callers
to spin in an infinite gevent.sleep() loop. Wrap construction in try/except
and reset _instance to None on failure so callers can retry.

Also replace the non-atomic setnx() + expire() pair in try_acquire_ownership()
with a single atomic SET NX EX call, consistent with the approach already used
in extend_ownership() and eliminating the race window where a crash between the
two calls could leave a key with no TTL (permanent ownership lock).
2026-03-03 11:06:52 -06:00
Claude Code
7fed49a334 Address review feedback from CodeBormen
- Use atomic SET NX EX instead of separate SETNX + EXPIRE to prevent
  zombie locks if the process crashes between the two calls
- Replace time.sleep() with gevent.sleep() in get_instance() spin-wait
  to avoid blocking the greenlet hub
- Defer cleanup when ownership is lost but clients are still connected,
  giving the new owner time to establish its stream before we tear down

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 13:23:45 +00:00
PFalko
8bd38ad71c Fix stream ownership bugs causing streams to die after 30-200s
Three interrelated bugs cause TS proxy streams to terminate prematurely
in multi-worker uWSGI deployments:

1. Double ProxyServer instantiation: ProxyConfig.ready() in apps/proxy/apps.py
   calls TSProxyServer() directly, bypassing get_instance(). The subsequent
   TSProxyConfig.ready() call to get_instance() creates a second instance.
   Each instance starts its own cleanup thread, but only one holds channel
   data — the orphaned cleanup thread cannot extend ownership.

2. Redis flushdb() on every client init: RedisClient.get_client() in
   core/utils.py calls flushdb() whenever a new connection is created.
   Celery autoscale workers spawning mid-stream nuke all Redis keys
   including ownership, client records, and channel metadata.

3. No recovery from expired ownership: get_channel_owner() has a TOCTOU
   bug (two separate GET calls in a lambda). extend_ownership() silently
   fails when keys expire. Non-owner cleanup unconditionally kills streams
   even when the worker holds the stream_manager.

Fixes:
- Use TSProxyServer.get_instance() in ProxyConfig.ready()
- Remove flushdb() from Redis client initialization
- Use sentinel pattern for gevent-safe singleton (threading.Lock does not
  work with gevent greenlets)
- Single GET in get_channel_owner() to avoid TOCTOU race
- Re-acquire expired ownership keys in extend_ownership()
- Attempt re-acquisition before cleanup in non-owner path

Relates to #992, #980
2026-02-27 16:40:28 +01:00
None
51b5824737 Fix cleanup paths for stream preview
Fix exception handling in _clean_redis_keys() and stop_channel()
   where `except Channel.DoesNotExist` didn't catch ValidationError from invalid UUID format, bypassing the Stream fallback path.
2026-02-12 00:09:29 -06:00
None
6360271e28 Fix unprotected _clean_redis_keys() call in check_if_channel_exists
Wrap _clean_redis_keys() call in check_if_channel_exists() with
try/except to prevent exception propagation when Channel/Stream
lookup or release_stream() encounters DB or Redis errors.
2026-02-11 20:09:17 -06:00
None
fe17d0d585 Check release_stream() boolean return at all call sites
All 12 call sites for Channel.release_stream() and Stream.release_stream() now check the boolean return value and log warnings when release fails.

Files updated:
- server.py: 4 call sites (zombie cleanup + _clean_redis_keys)
- channel_service.py: 2 call sites (stop_channel)
- stream_generator.py: 1 call site (last client disconnect)
- views.py: 4 call sites (failed init, redirect, init failure, exception)
- url_utils.py: 1 call site (URL generation error)

Also fixed bare except clause in _clean_redis_keys and uncaught
Stream.DoesNotExist in channel_service.stop_channel.

Based on PR #838 by patchy8736
2026-02-11 19:49:11 -06:00
None
e217960500 feat: Add Redis authentication support for modular deployment
Add support for authentication when connecting to external Redis instances in modular deployment mode. This enables secure Redis deployments using either password-only authentication (Redis <6) or
username + password authentication (Redis 6+ ACL).

Changes:
- Add REDIS_PASSWORD and REDIS_USER environment variables
- Implement URL encoding for special characters in passwords
- Update all Redis connection points to support auth
- Add comprehensive documentation and examples to docker-compose.yml
- Maintains full backward compatibility (empty defaults = no auth)

All authentication mechanisms have been fully tested including pasword-only authentication, Redis 6+ ACL authentication with username + password, volume-mounted configuration files, and special character handling.

Existing deployments are not effected, authentication support is entirely opt-in using docker-compose.

Also, acknowledging the fact that the docker-compose file for modular deployments has been getting out of hand with auth support, the docker-compose.yml file was re-formatted for better visibility in configuration. This seemed like a better way to go than mandating a .env file.
2026-02-03 21:42:06 -06:00
SergeantPanda
89a23164ff Enhancement: Add system event logging and viewer with M3U/EPG endpoint caching
System Event Logging:
- Add SystemEvent model with 15 event types tracking channel operations, client connections, M3U/EPG activities, and buffering events
- Log detailed metrics for M3U/EPG refresh operations (streams/programs created/updated/deleted)
- Track M3U/EPG downloads with client information (IP address, user agent, profile, channel count)
- Record channel lifecycle events (start, stop, reconnect) with stream and client details
- Monitor client connections/disconnections and buffering events with stream metadata

Event Viewer UI:
- Add SystemEvents component with real-time updates via WebSocket
- Implement pagination, filtering by event type, and configurable auto-refresh
- Display events with color-coded badges and type-specific icons
- Integrate event viewer into Stats page with modal display
- Add event management settings (retention period, refresh rate)

M3U/EPG Endpoint Optimizations:
- Implement content caching with 5-minute TTL to reduce duplicate processing
- Add client-based event deduplication (2-second window) using IP and user agent hashing
- Support HEAD requests for efficient preflight checks
- Cache streamed EPG responses while maintaining streaming behavior for first request
2025-11-20 17:41:06 -06:00
SergeantPanda
b6c3234e96 Enhancement: Improve zombie channel handling in ProxyServer by checking client connections and cleaning up orphaned metadata, ensuring better resource management and stability. 2025-11-18 10:02:35 -06:00
SergeantPanda
6bd5958c3c Enhancement: Improve channel shutdown logic in ProxyServer to handle connection timeouts and grace periods more effectively, ensuring proper channel management based on client connections. 2025-11-15 14:22:26 -06:00
SergeantPanda
2514528337 Enhancement: Update channel state handling in ProxyServer and views to include 'STOPPING' state, ensuring proper cleanup and preventing reinitialization during shutdown. 2025-11-14 19:57:59 -06:00
SergeantPanda
fa08216600 Enhancement: Add chunk timeout configuration in ConfigHelper. Improve StreamManager timeout handling for consistency. Only 1 heartbeat thread per worker should be started now. Timeout on proxy reduced from 60 seconds to 5. 2025-10-11 18:08:20 -05:00
SergeantPanda
fefab4c4c6 Enhancement: Improve resource cleanup in ProxyServer and StreamManager classes to avoid "SystemError: (libev) error creating signal/async pipe: Too many open files" errors 2025-10-10 15:26:02 -05:00
SergeantPanda
8e2c6c7780 Check for any state to detmine if channel is running. 2025-07-01 10:57:07 -05:00
SergeantPanda
c04ca0a804 Add buffering as an active state. 2025-06-25 17:01:21 -05:00
SergeantPanda
8eec41cfbb Fixes a bug where heartbeat thread will exit if channel is in shutdown delay.
This may also fix #129
2025-06-13 10:27:51 -05:00
SergeantPanda
1e9ab54609 Use new methods for getting settings. 2025-06-12 16:11:43 -05:00
SergeantPanda
50048518a9 Fixes bug where mulitiple channel initializations can occur which leads to choppy streams and zombie channels. 2025-05-27 19:05:26 -05:00
SergeantPanda
423020861c Replace time.sleep with gevent.sleep for improved concurrency 2025-04-30 13:32:16 -05:00
dekzter
7351264e8a centralized and lazy-loaded redis client singleton, check for manage.py commands so we don't init proxyservers (redis connection), put manage commmands before starting uwsgi 2025-04-04 16:18:12 -04:00
SergeantPanda
cf059045c1 Decoupled from global redis client. 2025-04-04 14:21:27 -05:00
SergeantPanda
3f440d855e Replaced invalid character in logs. 2025-03-27 18:50:48 -05:00
dekzter
91a85020c3 modifications to allow previewing of a raw stream 2025-03-27 09:26:04 -04:00
SergeantPanda
d622c96aba Improved connection handling for redis pubsub. 2025-03-22 08:48:39 -05:00
SergeantPanda
77002beaac Improved logging to include current component name. 2025-03-22 07:42:46 -05:00
SergeantPanda
efaa7f7195 Singular redis-client. 2025-03-21 10:55:13 -05:00
SergeantPanda
4738d301d1 Fixed regression with buffer checks when clients should be disconnecting due to failure. 2025-03-20 21:03:01 -05:00
SergeantPanda
6d84821942 Adds better checks for a channel that is partially initialized and clears it. 2025-03-20 15:56:34 -05:00
SergeantPanda
d6a452548c Automatic stream switching is working. 2025-03-20 15:28:12 -05:00
SergeantPanda
86e6c942d7 Final refactoring and minor bug fix for transcode stream switching. 2025-03-19 20:22:13 -05:00
SergeantPanda
569846a687 Lots more refactoring. 2025-03-19 19:45:29 -05:00
SergeantPanda
f03b885d71 Lots more refactoring. 2025-03-19 19:03:33 -05:00
SergeantPanda
8e3c3cb7e8 Fixes timers not releasing on reconnect attempts 2025-03-18 12:10:27 -05:00
SergeantPanda
71f7357d0b Fixed bug: RuntimeError: dictionary changed size during iteration 2025-03-18 10:22:41 -05:00
dekzter
7a7cd0711d live stats dashboard 2025-03-17 12:42:58 -04:00
SergeantPanda
8bb9317f92 Individual client stopping is fully working now. 2025-03-16 18:11:35 -05:00
SergeantPanda
03bfaeb24f Adding stop client abilities. Still needs some work. 2025-03-16 17:16:10 -05:00
SergeantPanda
921a63c730 Working on resource cleanup. 2025-03-16 16:45:00 -05:00
SergeantPanda
9c6f31e014 Channel stop is working correclty. 2025-03-16 12:35:06 -05:00
dekzter
4cef55a9c2 updated channel with uuid 2025-03-16 09:46:07 -04:00
SergeantPanda
5da288b15b Added channel profile to metadata. 2025-03-15 18:18:22 -05:00
SergeantPanda
a4c69710b7 Stream switching is working for both proxy and transcode 2025-03-15 17:23:06 -05:00
SergeantPanda
8ed1a36535 Lots of refractoring. 2025-03-15 09:44:38 -05:00
SergeantPanda
b5383bcc19 Updated TTL logic for channel metadata 2025-03-14 16:18:02 -05:00
dekzter
a58993ba7d merged in sergeantpanda upstream 2025-03-14 10:19:22 -04:00
dekzter
8b6b16232b re-added in socket streaming for transcode processes 2025-03-14 10:08:01 -04:00
SergeantPanda
387c2491b7 Continuing cleanup. 2025-03-13 18:34:49 -05:00
dekzter
0607957f67 integrating proxy with rest of the application, added transcoding to ts proxy 2025-03-13 19:22:35 -04:00
SergeantPanda
d953386c57 Code cleanup. 2025-03-13 18:06:32 -05:00