Commit graph

1036 commits

Author SHA1 Message Date
SergeantPanda
e78c24aad7 Enhance Redis chunk management in StreamBuffer and StreamGenerator
- Register Lua scripts for optimized chunk retrieval in StreamBuffer.
- Implement atomic Lua binary search to find the oldest available chunk in Redis.
- Update StreamGenerator to handle expired chunks more efficiently, reducing TOCTOU race conditions.
2026-03-03 15:13:46 -06:00
SergeantPanda
cd9d8d5d2a Merge branch 'dev' of https://github.com/Dispatcharr/Dispatcharr into pr/PFalko/1035 2026-03-03 11:07:10 -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
SergeantPanda
c005a48162 Merge branch 'dev' of https://github.com/Dispatcharr/Dispatcharr into pr/JCBird1012/1027 2026-03-03 09:15:31 -06:00
SergeantPanda
b098deae76 Got rid of extra api call to query if any channels had no EPG assigned. Moved query to an EXISTS lookup during channel query. 2026-03-02 17:20:55 -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
SergeantPanda
565d335403 Bug Fix: The EPG filter dropdown was showing all EPG sources regardless of whether they had any channels assigned, and the "No EPG" option was never displayed. Fixed by annotating EPGSource records with a has_channels flag (via a lightweight EXISTS subquery) so only EPG sources actually in use appear as filter options. "No EPG" now appears only when at least one channel has no EPG assigned, determined via a page_size=1 query against the existing channel filter endpoint.
Some checks are pending
CI Pipeline / prepare (push) Waiting to run
CI Pipeline / docker (amd64, ubuntu-24.04) (push) Blocked by required conditions
CI Pipeline / docker (arm64, ubuntu-24.04-arm) (push) Blocked by required conditions
CI Pipeline / create-manifest (push) Blocked by required conditions
Build and Push Multi-Arch Docker Image / build-and-push (push) Waiting to run
Frontend Tests / test (push) Waiting to run
2026-03-01 16:54:05 -06:00
Jonathan Caicedo
a318d919d8 feat: filter channels that contain stale streams
closes: #1025
2026-02-28 12:30:21 -05: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
SergeantPanda
abba65816b Migrations: Add missing migrations. 2026-02-26 13:25:51 -06:00
SergeantPanda
216575a411
Merge pull request #1020 from Dispatcharr/enhanced-webhooks
enhanced webhooks to support custom headers and payload templates
2026-02-26 13:14:24 -06:00
SergeantPanda
60c165f422 Enhancement: Stats page "Now Playing" EPG lookup updated to use channel_uuids directly (the proxy stats already key active channels by UUID), removing the need for a UUID→integer ID conversion step introduced alongside the lazy channel-fetch refactor. Stream preview sessions (which use a content hash rather than a UUID as their channel ID) are now filtered out before any API call is made, preventing a backend ValidationError on both the current-programs and by-uuids endpoints when a stream preview is active on the Stats page. 2026-02-26 12:00:29 -06:00
dekzter
4d6351507f enhanced webhooks to support custom headers and payload templates 2026-02-25 10:59:45 -05:00
SergeantPanda
6f63b59e89
Merge pull request #999 from firestaerter3:fix/xtream-added-field-type
Fix: add tmdb_id and imdb_id to Xtream get_series response
2026-02-24 19:57:47 -06:00
SergeantPanda
8d6d99a6c5
Merge pull request #1008 from CodeBormen:Feature/954-1004-user-account-management
Fix #954: Superuser detection; Feature #1004: User account disable/enablement
2026-02-24 19:38:01 -06:00
SergeantPanda
273ba9b3b6
Merge pull request #1016 from znake-oil/fix/episode-uuid-stability
Remove clearing of existing episodes on refresh
2026-02-24 18:37:08 -06:00
SergeantPanda
1791a75f15 Bug Fix: Remove stale episode relations when series relations are deleted as well as remove episodes not seen in current series scan.
Enhancement: Added series_relation FK to M3UEpisodeRelation table to properly link episodes with the m3useriesrelation they came from.
2026-02-24 18:32:48 -06:00
Matt Grutza
3ec62a3d52
Merge branch 'dev' into Feature/954-1004-user-account-management 2026-02-24 14:49:31 -06:00
SergeantPanda
e9a2cb4dbb
Merge pull request #1014 from CodeBormen/fix/861-M3U-downloading-endlessly
Some checks are pending
CI Pipeline / prepare (push) Waiting to run
CI Pipeline / docker (amd64, ubuntu-24.04) (push) Blocked by required conditions
CI Pipeline / docker (arm64, ubuntu-24.04-arm) (push) Blocked by required conditions
CI Pipeline / create-manifest (push) Blocked by required conditions
Build and Push Multi-Arch Docker Image / build-and-push (push) Waiting to run
Frontend Tests / test (push) Waiting to run
Fix #861: Prevent M3U/EPG tasks from downloading endlessly
2026-02-24 14:28:36 -06:00
SergeantPanda
f861b01e8a
Merge pull request #1018 from Dispatcharr/api-keys
Api keys
2026-02-24 10:54:47 -06:00
SergeantPanda
74ce971b75 Bug Fix: Cron schedule got overwrote when changing/loading settings (signals issue) 2026-02-24 10:48:17 -06:00
znake-oil
dca72f330e Remove clearing of existing episodes on refresh
These 4 lines were wiping episodes before batch_process_episodes could update them in place, causing new UUIDs to be generated on every refresh.
# batch_process_episodes already handles create vs update correctly using (series, season_number, episode_number) as the stable natural key.
2026-02-24 12:11:19 +01:00
None
54f3ee2185 Remove is_Active reference to user accounts
Removed is_active references and checks for users accounts as the functionality does not align with project direction
2026-02-23 18:39:54 -06:00
None
e55ef41acb Fix uncovered lock_renewer.stop() paths in refresh_single_m3u_account
Five early-return paths released the Redis lock without stopping the
renewal thread, and the final release_task_lock was outside the finally block making it unreachable on exception. All exit paths now properly
stop the renewer before releasing the lock.
2026-02-23 00:48:07 -06:00
None
c841cfc8fc Fix #861: Prevent M3U endless downloading caused by Redis lock expiry
Large M3U downloads that exceed the 300s Redis lock TTL caused Celery Beat
to re-queue duplicate tasks, creating overlapping downloads that never complete.

Files changed:

- core/utils.py: Add TaskLockRenewer class — a daemon thread that periodically renews Redis lock TTL (every 120s) to prevent expiry during long-running tasks.

- apps/m3u/tasks.py: Apply TaskLockRenewer to refresh_single_m3u_account and refresh_m3u_groups; add HTTP timeout (30s connect, 60s read) to M3U download (the only download path missing one); stream M3U download to disk instead of accumulating in memory; add Celery task time limits (1 hour hard limit).

- apps/epg/tasks.py: Apply TaskLockRenewer to refresh_epg_data and parse_programs_for_tvg_id; add Celery task time limits (30 min / 1 hour).

Verified with a throttled test server: locks renewed at T+120s, T+240s, T+360s; 50,000 streams processed with no duplicate tasks.
2026-02-23 00:34:28 -06:00
None
f453380f5c Remove user account disable/enable UI (keep backend guards)
Removed the frontend UI exposure
while keeping backend guards as defensive measures.

Removed:
- frontend/src/components/forms/User.jsx — Account Enabled switch
- frontend/src/components/tables/UsersTable.jsx — Status column
- apps/accounts/api_views.py — is_active check in TokenRefreshView
- apps/accounts/tests.py — related tests
2026-02-22 20:02:08 -06:00
None
db318e4981 Fix #954: Superuser detection; Feature #1004: User account disable/enable
Bug #954: The initialize_superuser endpoint only checked is_superuser=True, missing admin accounts created via the API (user_level=10). This caused users to intermittently see the "Create Super User" page instead of login.

Feature #1004: Admins can now disable/enable user accounts. Disabled users are blocked from JWT login, token refresh, XC API access, and all
authenticated endpoints. The last active admin cannot be disabled.

Files changed:

Backend:
- apps/accounts/api_views.py — Superuser check uses user_level>=10; token refresh blocks disabled users
- apps/accounts/models.py — CustomUserManager ensures create_superuser() always sets user_level=10
- apps/accounts/serializers.py — Last-admin protection guard; respect is_active on user creation
- apps/accounts/tests.py — new tests covering superuser detection, token
 refresh blocking, last-admin protection, disabled user login/access
- apps/backups/api_views.py — Switched from DRF's IsAdminUser (checks is_staff) to app's IsAdmin (checks user_level) on all 8 endpoints
- apps/backups/tests.py — new tests verifying user_level-based admin permission on backup endpoints
- apps/output/views.py — is_active check on xc_get_user(), xc_movie_stream(), xc_series_stream()
- apps/proxy/ts_proxy/views.py — is_active check on stream_xc()
- core/api_views.py — Admin notification filter uses user_level>=10
- core/developer_notifications.py — Admin checks use user_level>=10

Frontend:
- frontend/src/App.jsx — Null-safe superuser existence check
- frontend/src/components/forms/User.jsx — Account Enabled switch with
  self-disable prevention and tooltip
- frontend/src/components/tables/UsersTable.jsx — Status column (Active/Disabled badge)
2026-02-22 18:05:02 -06:00
dekzter
aece3367ce backend work for api key authentication 2026-02-21 13:45:07 -05:00
SergeantPanda
a7b9c0ffe1
Merge branch 'dev' into optimize-channels-store 2026-02-20 09:42:36 -06:00
SergeantPanda
438aa526bb Bug Fix: get_live_streams and get_vod_info now return the added field as a string (e.g., "1708300800") instead of an integer, fixing compatibility with XC clients that have strict JSON serializers (such as Jellyfin's Xtream Library plugin). (Closes #978)
Some checks are pending
CI Pipeline / prepare (push) Waiting to run
CI Pipeline / docker (amd64, ubuntu-24.04) (push) Blocked by required conditions
CI Pipeline / docker (arm64, ubuntu-24.04-arm) (push) Blocked by required conditions
CI Pipeline / create-manifest (push) Blocked by required conditions
Build and Push Multi-Arch Docker Image / build-and-push (push) Waiting to run
Frontend Tests / test (push) Waiting to run
2026-02-19 17:48:32 -06:00
Roland
8999e832ff Fix: add tmdb_id and imdb_id to Xtream get_series response
The get_series endpoint was omitting tmdb_id and imdb_id fields that
are already present on the Series model and already included in the
get_vod_streams (movies) response.

Xtream clients such as Chillio use these IDs to fetch proper metadata
from TMDB, including sanitized series titles and poster images. Without
them, clients fall back to the raw provider name (which may include
provider prefixes like ┃NL┃) and the proxied cover URL instead of
TMDB's poster.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 23:26:44 +01:00
SergeantPanda
1503127f61
Merge pull request #983 from CodeBormen/fix/962-VOD-proxy-connection-counter-leak-on-client-disconnect
Fix: VOD proxy connection counter leak on client disconnect
2026-02-19 11:32:20 -06:00
SergeantPanda
b1abab6654 Bug Fix: Fix bug where attempting to schedule a one-time recording of a future program resulted in the recording starting immediately. 2026-02-18 16:25:33 -06:00
SergeantPanda
7db6aded22 add summary endpoint and lightweight channel summary retrieval for TV Guide 2026-02-18 11:51:41 -06:00
None
58ccf2d6e1 Fix: VOD proxy connection counter leaks on client disconnect and seeking
Fix multiple VOD proxy connection counter leak paths that caused
profile_connections to remain permanently elevated after client
disconnects, leaving profiles locked at capacity.

Changes:
- Fix get_stream() race condition: save state under lock with fresh active_streams to prevent overwriting concurrent decrements during scrubbing/seeking (root cause of counter stuck after timeshift)
- Add early active_streams increment in session reuse path to prevent cleanup race between GeneratorExit and new request
- Add rollback on get_stream() returning None (range not satisfiable)
- Add profile_id fallback to closure variable when Redis state is deleted before generator cleanup runs
- Change increment/decrement return types and add warning logs for lock acquisition and state failures
- Clean up debug traceback logging from increment/decrement methods
2026-02-17 20:24:00 -06:00
SergeantPanda
47853bdb5b Enhancement: Added optional subtitle template field to custom dummy EPG configuration. Users can now define subtitle patterns using extracted regex groups and time/date placeholders (e.g., {starttime} - {endtime}). (Closes #942) 2026-02-17 12:00:24 -06:00
SergeantPanda
ac1b43445e
Merge pull request #946 from CodeBormen/fix/945-XC-profile-refresh-extract-wrong-credentials
Fix XC URL sub-path handling in profile refresh and EPG creation
2026-02-16 16:04:53 -06:00
dekzter
cd6b3da8eb Merge remote-tracking branch 'origin/dev' into optimize-channels-store 2026-02-16 16:44:20 -05:00
SergeantPanda
b1cb70c5d2
Merge pull request #975 from Dispatcharr/connect
Feature: Connect
2026-02-16 15:27:56 -06:00
SergeantPanda
968551c1a6 Enhancement: M3U generation for Xtream Codes API endpoints now use proper XC-style stream URLs (/live/username/password/channel_id) instead of UUID-based stream endpoints, ensuring full compatibility with XC clients. (Fixes #839) 2026-02-16 13:45:57 -06:00
SergeantPanda
717cce92ec
Merge branch 'dev' into cron-scheduling 2026-02-16 10:55:33 -06:00
dekzter
55a5180ee1 hardening of script handling with configurable variables - making script execution more secure 2026-02-15 07:37:00 -05:00
dekzter
45d0f180fe added endpoint for testing connections with a dummy payload 2026-02-15 07:36:08 -05:00
None
4842bb87fa Fix: VOD proxy connection counter leak on client disconnect
Three fixes:
- TOCTOU: Replace GET-check-INCR with atomic INCR-first-then-check pattern in both connection managers to prevent concurrent requests exceeding max_streams
- Immediate DECR: Decrement profile counter directly in stream generator exit paths (normal completion, client disconnect, error) instead of deferring to daemon thread cleanup which may never execute
- Rollback: Decrement profile counter on create_connection() failure so the reserved slot is released

Fixes #962
2026-02-13 19:37:06 -06:00
SergeantPanda
fb2f156fd8 Bug Fix: Fixes a bug with auto channel sync where channel numbers could be duplicated if multiple groups overlapped. 2026-02-13 15:07:24 -06:00
SergeantPanda
2c9f6d7360 Enhancement: For auto channel sync, allow using provider number, fixed start number or next available. (Closes #956, #433) 2026-02-13 14:34:22 -06:00
dekzter
e030906cdd more supported events, better UI (columns) for events in connection editor 2026-02-13 12:20:40 -05:00
dekzter
1236d3968f forgot dangling import 2026-02-13 12:02:33 -05:00
dekzter
3b0a890e94 fixed circular import 2026-02-13 11:48:59 -05:00
dekzter
f1d6b86278 refactored frontend usage of 'all channels' to only pull down IDs, query for individual channels as necessary 2026-02-13 11:46:17 -05:00