Commit graph

286 commits

Author SHA1 Message Date
SergeantPanda
2373044adb Merge branch 'dev' of https://github.com/Dispatcharr/Dispatcharr into pr/CodeBormen/1052 2026-03-06 16:12:52 -06:00
SergeantPanda
9805732582 Merge branch 'dev' of https://github.com/Dispatcharr/Dispatcharr into pr/CodeBormen/1023 2026-03-05 11:16:20 -06:00
None
68de1b0723 fix(dvr): add DVR search/filters, fix in-progress scheduling, prevent duplicate recordings
- Add search, channel, and status filter controls to DVR page (matching Guide/VODs pattern)
- Fix post_save signal to schedule recordings for currently-playing programs immediately instead of skipping them
- Remove duplicate evaluate_series_rules.delay() call from series rule creation endpoint to prevent race condition that created two recordings for the same program
2026-03-04 10:21:03 -06:00
None
9d08305dcb fix(dvr): fix recording modal crash, harden recovery pipeline, improve tests
Frontend:
- RecordingDetailsModal: fix ReferenceError crash (editing state used before declaration in useMemo — TDZ in production bundles broke all tile clicks)
- RecordingDetailsModal: fix focus-stealing on description edit by calling Movie()/Series() as plain functions instead of <Movie />/<Series />(inline component refs change every render, causing unmount/remount)
- RecordingDetailsModal: suppress sub_title in modal title after custom save
- ErrorBoundary: log caught errors and show message for easier debugging
- guideUtils: filter terminal recordings from mapRecordingsByProgramId so Guide red dots clear after recording finishes or rule is deleted
- RecordingDetailsModalUtils.test: add missing end_time to test fixtures (filterByUpcoming now uses end_time instead of start_time)

Backend:
- tasks.py: output file collision avoidance (check .mkv/.ts before writing)
- tasks.py: pre-restart TS segment concatenation (direct concat→MKV remux)
- tasks.py: recovery pipeline — recover crashed "recording" status, remux expired recordings, revoke stale tasks, deterministic task IDs, early lock release, 10-min lock TTL for large remux operations
- tasks.py: poster resolver skips external API when title matches channel name
- celery.py: trigger recover_recordings_on_startup via worker_ready signal

Tests:
- Renamed test_dvr_fixes.py → test_recording_pipeline.py (clarity)
- Renamed test_dvr_retry.py → test_db_retry.py (clarity), fixed mock paths for close_old_connections (resolved 3 test failures), replaced synthetic test
- test_recording_stop_cancel: replaced brittle time.sleep(0.5) with deterministic thread-capture pattern
2026-03-04 10:21:03 -06:00
None
bd14e48f8b fix(dvr): harden recording pipeline — idempotency guard, remux sanity checks, timezone fix
- Idempotency guard: fail closed on DB errors instead of proceeding, preventing duplicate tasks from overwriting valid recordings (#641)
- Remux sanity checks: reject MKV output that is <50% of a previous MKV (duplicate-task overwrite) or <10% of the source TS (corrupt first attempt); preserve .ts for manual recovery on all failure paths (#619)
- Recurring rule timezone fix: use user's configured timezone for date window calculation instead of UTC, fixing silent recording loss for users in UTC-negative timezones after 4pm local time (#1042)
- Include .ts path in final remux failure log for easier recovery
2026-03-04 10:21:03 -06:00
None
759688390b fix(dvr): fix series episode list, card layout, rule cleanup, and recording resilience
Backend:
- Add terminal status guard to stop() endpoint -prevents overwriting completed/interrupted/failed with "stopped" (returns 409 Conflict)
- Include currently-airing programs in series rule evaluation by filtering on end_time instead of start_time
- Clean up future recordings when deleting a series rule while preserving previously recorded episodes
- Add logo proxy negative cache to prevent repeated requests to unreachable hosts from blocking Daphne workers
- Add URL validation with HEAD-then-GET fallback and per-worker cache
- Add EPG time-slot matching (80% overlap threshold) to enrich manual recordings with program metadata
- Add multi-source poster resolution pipeline (EPG, VOD, TMDB, OMDb, TVMaze, iTunes) with URL validation at each stage
- Add editable recording metadata endpoint with user_edited flag to prevent EPG auto-overwrite
- Add refresh-artwork endpoint to re-run poster resolution on demand
- Batch-fetch PeriodicTask names in schedule_upcoming to avoid N+1

Frontend:
- Fix series modal showing only one episode — preserve _group_count from categorized prop when merging with store version so the episode list renders correctly
- Fix recording card button positioning — use flex column layout with marginTop auto so buttons align at the bottom regardless of content
- Add delete fallback in RecurringRuleModal for orphaned recordings from deleted rules
- Use end_time filter in upcoming episodes list to include currently-airing episodes
- Add inline metadata editing (title/description) in details modal
- Add refresh artwork button in details modal
- Replace hardcoded /logo.png fallbacks with imported default logo
- Add React.memo with custom comparator to RecordingCard

Tests:
- Add 21 EPG time-slot matching tests (overlap threshold, edge cases)
- Add 16 recording metadata tests (validation, artwork, logo cache)
- Add 17 URL validation tests (HEAD/GET fallback, caching, eviction)
2026-03-04 10:21:03 -06:00
None
cf3165ea88 fix(dvr): add DB retry resilience, fix recording card logos, prevent incorrect artwork
Add exponential backoff retry (_db_retry helper) for transient DB errors during final metadata save, startup recovery, and initial TS proxy
connection. Add FFmpeg remux retry for transient I/O errors. Retry same proxy base before falling back to next candidate on initial connection failure.

Fix DVR card logos not displaying: channel summary API returns logo_id (integer) but frontend expected a nested logo object with cache_url.

Add getChannelLogoUrl() helper that handles both data shapes. Backend artwork prefetch and run_recording now fall back to the channel's own logo when no show-specific artwork is found, ensuring poster_logo_id is always populated.

Prevent incorrect artwork by skipping external API searches (TVMaze, iTunes) when no program title is available - channel names like
"USA A&E SD*" returned unrelated results from fuzzy matching.

Add 13 tests for retry logic (test_dvr_retry.py)
2026-03-04 10:21:03 -06:00
None
e5206545dd fix(dvr): add stream reconnection resilience, fix comskip exit code handling
- Add reconnection loop to DVR streaming: on transient connectivity loss (ReadTimeout, ConnectionError, ChunkedEncodingError), the recording task retries the same TS proxy base up to 5 times with a 2-second delay, appending to the existing file. Counter resets on successful data receipt. TS container format tolerates brief discontinuities; MKV remux normalises timestamps.
- Extract _check_recording_cancelled() helper to consolidate repeated stop/delete DB checks across the streaming and reconnection paths.
- Fix comskip treating exit code 1 (no commercials detected) as a fatal error.
  Use check=False and handle return codes explicitly: 0 = commercials found,
  1 = no commercials (skipped), negative = signal, other = real error.
- Add human-readable reason messages for comskip 'skipped' notifications in
  WebSocket handler.
- Remove redundant conditional Channel import in stream_generator._cleanup() that shadowed the module-level import, causing UnboundLocalError when the conditional branch did not execute.
2026-03-04 10:21:02 -06:00
None
f28530a47b fix(dvr): add recording extend, fix cross-recording interference, artwork race, and keepalive gaps
File changes:

Backend

apps/channels/api_views.py — Added extend() action that uses queryset .update() to bypass signals, letting the running task pick up the new end_time via polling. Moved WS recording_stopped notification to synchronous (before HTTP response). Refactored destroy() to delete DB row first, send WS event immediately, defer cleanup to background thread.

apps/channels/signals.py — Added pre_save guard to skip task revocation when status is "recording". Added post_save reentrancy guard to skip artwork prefetch for internal field-only saves (custom_properties, task_id, end_time).

apps/channels/tasks.py — Added 2-second polling loop in streaming that checks for stop, deletion, and extended end_time. Added refresh_from_db() + merge before metadata save to prevent overwriting concurrent artwork prefetch. Replaced inline PeriodicTask cleanup with revoke_task()/_dvr_task_name(). Consolidated redundant DB queries in error path. Fixed TOCTOU os.path.exists + os.remove. Preserved "stopped" status in final-status logic instead of overwriting with "completed".

core/utils.py — Made send_websocket_update gevent-safe by detecting monkey-patching and offloading async_to_sync to gevent's native threadpool, fixing the event loop deadlock that caused cross-recording interference.

apps/proxy/ts_proxy/client_manager.py — Moved _trigger_stats_update to a background thread so remove_client() returns immediately.

apps/proxy/ts_proxy/stream_generator.py — Added Redis-based health check for non-owner workers in _should_send_keepalive(), so keepalives fire correctly even when stream_manager is None.

Frontend

frontend/src/WebSocket.jsx — Added 400ms debounced scheduleRecordingFetch() replacing all inline fetchRecordings() calls. Added recording_extended and recording_updated event handlers. recording_cancelled now does surgical removeRecording() by ID when available.

frontend/src/api.js — Added extendRecording(id, extraMinutes).

frontend/src/components/cards/RecordingCard.jsx — Added Extend menu (+15m/+30m/+1h) for in-progress recordings. Widened Comskip button to stopped/interrupted statuses.

frontend/src/components/forms/ProgramRecordingModal.jsx — Removed manual fetchRecordings() calls, relying on WS-driven debounced refresh.

frontend/src/components/forms/RecordingDetailsModal.jsx — Added stopped to playable statuses. Widened Comskip button. Removed manual fetchRecordings().

frontend/src/components/forms/RecurringRuleModal.jsx — Removed all manual fetchRecordings() calls, relying on WS events.

frontend/src/pages/Guide.jsx — Removed isLoading subscription that caused loading flash on debounced refresh. Removed manual fetchRecordings() after saving a series rule.

frontend/src/store/channels.jsx — Stripped isLoading/error state from fetchRecordings to prevent full-page loading indicators. Added no-op guards in removeRecording.

frontend/src/utils/cards/RecordingCardUtils.js — Added extendRecordingById wrapper.

Tests

apps/channels/tests/test_recording_extend.py (new) — 14 tests: extend endpoint validation, stacked extensions, signal bypass, pre_save guard behavior.

apps/channels/tests/test_recording_stop_cancel.py (new) — 18 tests: stop endpoint, DVR client teardown, cancel was_in_progress flag, run_recording race guards, signal reentrancy guards.

apps/channels/tests/test_ts_proxy_keepalive.py (new) — 21 tests: keepalive owner/non-owner logic, stats update error handling, client removal non-blocking, timing invariants.

apps/channels/tests/test_recording_scheduling.py — Updated mock assertion to match new _stop_dvr_clients call signature.

Summary:

Adds an Extend action for in-progress recordings that bypasses Django signals and lets the running task dynamically pick up the new end_time via its 2-second DB polling loop. Fixes cross-recording interference caused by send_websocket_update deadlocking the gevent event loop. Fixes an artwork race condition where run_recording overwrote concurrent prefetch data. Adds Redis-based keepalive health checks for non-owner workers to prevent DVR read timeouts during source transitions. Replaces manual fetchRecordings() calls throughout the frontend with a debounced WS-driven refresh. 53 new tests.
2026-03-04 10:21:02 -06:00
None
bd1e060bd2 fix(dvr): fix duplicate recording execution and add manual stop
Fixes #940: Duplicate DVR task executions were caused by ClockedSchedule objects being shared across PeriodicTasks, triggering run_recording multiple
times for the same recording. Added an idempotency guard in run_recording that exits early if status is already 'recording', 'completed', or 'stopped'.

revoke_task() now deletes the PeriodicTask and orphaned ClockedSchedule on execution rather than relying on Celery revocation alone.

Fixes #454: Added POST /api/channels/recordings/{id}/stop/ action that marks the recording as stopped and terminates only the specific DVR proxy client (identified via User-Agent: Dispatcharr-DVR/recording-{id}) without disrupting
simultaneous recordings on the same or other channels. The API returns
immediately; DVR client teardown and task revocation happen in a background thread to avoid 504 timeouts on the recordings list endpoint.

Additional changes:
- destroy() now only calls _stop_dvr_clients() for in-progress recordings, preventing accidental stream termination when deleting completed recordings
- WebSocket events differentiated: recording_stopped, recording_cancelled (in-progress cancel), and recording_cancelled (delete) with was_in_progress flag so the frontend shows distinct "Recording deleted" vs "Recording cancelled" notifications

- Frontend: Stop and Cancel split into separate buttons with confirmation
  modals; stopped recordings move immediately to Previously Recorded via
  optimistic status bucketing; removed dead 'Stopped' badge state

- 27 new unit tests covering scheduling, idempotency, revocation, and DVR client isolation
2026-03-04 10:21:02 -06:00
None
6ff81e6287 Fix modular mode deployment issues (#1045)
- Fix Postgres version check failing with restricted DB users (use $POSTGRES_DB instead of hardcoded 'postgres')
- Fix DVR recording broken in modular mode (respect DISPATCHARR_PORT instead of hardcoding 9191)
- Remove flushdb() from wait_for_redis.py to prevent Redis data loss on container restart
- Add DISPATCHARR_PORT to celery environment in docker-compose.yml
- Add depends_on health conditions for proper service startup ordering
- Add extra_hosts for host.docker.internal resolution on Linux
- Harden celery entrypoint with timeouts for JWT wait (120s) and migration wait (300s)
- Replace fragile showmigrations grep with migrate --check
- Add unit tests for DVR port resolution and flushdb removal regression
2026-03-03 17:18:16 -06:00
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
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
Jonathan Caicedo
a318d919d8 feat: filter channels that contain stale streams
closes: #1025
2026-02-28 12:30:21 -05:00
None
0e7eb916c2 Merge remote-tracking branch 'upstream/dev' into fix/947-Connection-capacity-leak-failed-stream-initialization-permanently-consumes-connection-slot 2026-02-27 20:59:30 -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
c69718b559 Fix: Make release_stream() idempotent to prevent double-DECR on rapid channel switching
Multiple code paths call release_stream() during channel shutdown (stream generator cleanup, _clean_redis_keys, ChannelService.stop_channel), causing
the profile_connections counter to be decremented multiple times for the same stream. The metadata hash fallback persisted longer than the primary keys, allowing subsequent calls to find stale stream_id/m3u_profile and DECR again.

Changes:
- Clear STREAM_ID and M3U_PROFILE metadata fields after decrementing in both the primary and fallback paths, making release_stream() safe to call multiple times
- Add metadata fallback when stream_profile:{id} key is missing but channel_stream:{id} exists, preventing a counter leak in that edge case
- Replace redundant get_stream() call in views.py with direct Redis reads to avoid side-effect INCR from a method that can allocate connection slots
2026-02-17 21:18:20 -06: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
None
837ba84768 fix(ts-proxy): Restore Stream.get_stream() with atomic slot reservation
Stream.get_stream() was incorrectly removed as dead code in commit
33f68a98. It is actually called get_stream_object()
in views.py when a stream hash (not channel UUID) is used, such as
during stream preview. The variable is named channel but holds a
Stream instance, which masked the dependency during analysis.

Restored with the INCR-first-then-check pattern applied, fixing the
same TOCTOU vulnerability the original method had.
2026-02-11 23:57:10 -06:00
None
6826e94921 Add boolean return to Stream.release_stream()
Stream.release_stream() now returns True on success and False when no
profile info could be found in stream_profile:{stream_id}. This allows
callers to detect and handle failed releases.

Enhanced logging to include stream ID for better traceability.

Based on PR #838 by patchy8736
2026-02-11 19:27:45 -06:00
None
56719d93fe Add boolean return and metadata fallback to Channel.release_stream()
Channel.release_stream() now returns True on success and False when no
stream/profile info could be found for cleanup. This allows callers to
detect and handle failed releases.

When the primary Redis keys (channel_stream/stream_profile) are already cleaned up by the proxy, the method falls back to the channel metadata hash (ts_proxy:channel:{uuid}:metadata) to recover stream_id and profile_id for proper connection counter cleanup.

Uses str(self.uuid) for metadata key lookup - fixes a bug in PR #838
which used self.id (integer PK), but metadata is keyed by UUID.

Enhanced logging now includes channel UUID for better traceability.

Based on PR #838 by patchy8736
2026-02-11 19:16:01 -06:00
None
33f68a9808 Remove unused Stream.get_stream() with TOCTOU vulnerability
Remove the Stream.get_stream() method which had the same TOCTOU race condition as Channel.get_stream()

All stream acquisition goes through
Channel.get_stream(), making this dead code. Removing it eliminates
a potential source of connection capacity leaks

Based on PR #838 by patchy8736
2026-02-11 19:11:28 -06:00
None
5a4b51b60a Fix TOCTOU race condition in Channel.get_stream() with atomic slot reservation
Add _check_and_reserve_profile_slot() method that uses an INCR-first-then-check pattern to atomically reserve connection slots. This eliminates the TOCTOU race condition where separate GET > check > INCR operations allowed concurrent requests to both pass the capacity check, exceeding max_streams.

For profiles with max_streams=0 (unlimited), INCR is skipped entirely. If over capacity, the increment is rolled back with DECR.

Based on PR #838 by patchy8736, adapted from Lua script to native Redis commands for consistency with project style and easier debugging.
2026-02-11 18:08:24 -06:00
None
15d67c7f65 Atomize profile counter swap in update_stream_profile to prevent counter drift
DECR/SET/INCR during stream profile switches are now executed in a Redis
pipeline, preventing partial updates if an exception occurs mid-operation
2026-02-10 18:30:43 -06:00
SergeantPanda
6fb4d42022
Merge pull request #894 from CodeBormen/Feature/771-Auto-Matching-Improvments
Feature/771 auto matching improvements
2026-02-03 10:20:45 -06:00
None
d7b98fef8d Add default/advanced mode toggle and test coverage for EPG matching
Enhanced EPG matching UX by adding radio button toggle to more clearly define optional advanced options. Also added test coverage for stability.

Frontend Changes:
- Added radio button UI to EPGMatchModal for default/advanced mode selection
- Default mode: Uses built-in normalization
- Advanced mode: Shows TagsInput fields for custom prefixes/suffixes/strings
- Mode persists across sessions and settings are preserved when switching modes

Test Coverage:
- Created EPGMatchModal.test.jsx with test cases covering:
  - Component rendering and mode switching
  - Form submission and settings persistence
  - Error handling and UI text variations

- Added test cases to SettingsUtils.test.js for EPG mode handling:
  - epg_match_mode routing to epg_settings group
  - Settings creation and preservation

Since advanced settings are saved persistently but ignored when default settings are used, this adds an easy way in the future to add additional advanced settings to EPG matching like  region influence, match aggression, and match preview, since users can easily switch between modes.
2026-01-31 23:11:20 -06:00
SergeantPanda
da598d3b33 Missed migration for previous feature. 2026-01-31 21:26:34 -06:00
SergeantPanda
0172a7cc00 Merge branch 'dev' of https://github.com/Dispatcharr/Dispatcharr into pr/CodeBormen/894 2026-01-31 19:47:57 -06:00
SergeantPanda
efe915a9b3
Merge pull request #904 from CodeBormen/Feature/866-Search-Filter-Sort-streams-by-TVG-ID
Feature/866 Add TVG-ID column with filter and sort to Streams table
2026-01-31 19:31:48 -06:00
SergeantPanda
9ffd595c96 Enhancement: Added stream_id (provider stream identifier) and stream_chno (provider channel number) fields to Stream model. For XC accounts, the stream hash now uses the stable stream_id instead of the URL when hashing, ensuring XC streams maintain their identity and channel associations even when account credentials or server URLs change. Supports both XC num and M3U tvg-chno/channel-number attributes. 2026-01-31 17:47:46 -06:00
None
f8ec970561 Add TVG-ID column with filter and sort to Streams table
Changes:

- frontend/src/components/tables/StreamsTable.jsx

Added TVG-ID column to streams table
Added search filter input in column header
Added sort button for ascending/descending order
Column follows same pattern as Name, Group, and M3U columns

- apps/channels/api_views.py

Added TVG-ID to filterable fields
Added TVG-ID to sortable fields
Enables backend support for search and sorting

Users can now view, search, and sort streams by their TVG-ID directly in the Streams table. Fulfills #866
2026-01-30 23:37:39 -06:00
None
325f8f4257 Merge remote-tracking branch 'upstream/dev' into Feature/771-Auto-Matching-Improvments 2026-01-29 19:24:44 -06:00
Matt Grutza
52b0c7ca26
Merge branch 'Dispatcharr:main' into Feature/771-Auto-Matching-Improvments 2026-01-29 18:22:09 -06:00
SergeantPanda
5364123745 - Swagger/OpenAPI Migration: Migrated from drf-yasg (OpenAPI 2.0) to drf-spectacular (OpenAPI 3.0) for API documentation. This provides:
- Native Bearer token authentication support in Swagger UI - users can now enter just the JWT token and the "Bearer " prefix is automatically added
  - Modern OpenAPI 3.0 specification compliance
  - Better auto-generation of request/response schemas
  - Improved documentation accuracy with serializer introspection
2026-01-27 13:33:33 -06:00
SergeantPanda
2abe07fe1c Update Series Rules API Swagger Documentation to include required items parameter for TYPE_ARRAY schemas 2026-01-27 10:56:04 -06:00
SergeantPanda
497bb819b9 Docs: Added comprehensive Swagger/OpenAPI documentation for all series-rules endpoints 2026-01-27 09:50:27 -06:00
Matt Grutza
a1fa737ccd Update verbiage 2026-01-25 22:08:32 -06:00
Matt Grutza
8d69fd9374 Add configurable EPG matching normalization settings (Feature #771)
Implements user-configurable string removal for EPG matching to improve channel-to-EPG
association accuracy. Settings are non-destructive and only affect EPG matching
normalization, never modifying actual channel display names.
2026-01-25 22:05:28 -06:00
SergeantPanda
655760f652 When moving a channel down (higher number) don't increase channel numbers, decrease.
Some checks failed
CI Pipeline / prepare (push) Has been cancelled
Build and Push Multi-Arch Docker Image / build-and-push (push) Has been cancelled
Frontend Tests / test (push) Has been cancelled
CI Pipeline / docker (amd64, ubuntu-24.04) (push) Has been cancelled
CI Pipeline / docker (arm64, ubuntu-24.04-arm) (push) Has been cancelled
CI Pipeline / create-manifest (push) Has been cancelled
2026-01-23 18:10:16 -06:00
SergeantPanda
e3122b1a1b Bug Fix: Fix reordering during drag and drop. 2026-01-23 17:31:46 -06:00
SergeantPanda
5348a4a564 Feature: Editable Channel Table Mode:
- Added a robust inline editing mode for the channels table, allowing users to quickly edit channel fields (name, number, group, EPG, logo) directly in the table without opening a modal.
  - EPG and logo columns now support searchable dropdowns with instant filtering and keyboard navigation for fast assignment.
  - Drag-and-drop reordering of channels enabled when unlocked, with persistent order updates.
  - Group column now uses a searchable dropdown for quick group assignment, matching the UX of EPG and logo selectors.
  - All editable cells provide clear focus, hover, and disabled states for improved accessibility and usability.
  - Table unlock/edit mode toggle with clear visual cues and safe-guarded save logic to prevent accidental edits.
  - All changes are saved via API with optimistic UI updates and error handling.
2026-01-23 16:03:26 -06:00
SergeantPanda
2fc2486c34 Enhancement: Added "Hide Stale" filter to quickly hide streams marked as stale.
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-01-20 18:51:13 -06:00
SergeantPanda
0b83137ac6 Enhancement: DVR recording remux fallback strategy: Implemented two-stage TS→MP4→MKV fallback when direct TS→MKV conversion fails due to timestamp issues. On remux failure, system now attempts TS→MP4 conversion (MP4 container handles broken timestamps better) followed by MP4→MKV conversion, automatically recovering from provider timestamp corruption. Failed conversions now properly clean up partial files and preserve source TS for manual recovery.
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
2026-01-19 18:56:23 -06:00
SergeantPanda
8b15e6e52f
Merge pull request #871 from Dispatcharr/main
Merge main into dev
2026-01-18 17:29:24 -06:00
SergeantPanda
620d65d1de Bug Fix: Fixed XtreamCodes API crash when channels have NULL channel_group: The player_api.php endpoint (xc_get_live_streams) now gracefully handles channels without an assigned channel_group by dynamically looking up and assigning them to "Default Group" instead of crashing with AttributeError. Additionally, the Channel serializer now auto-assigns new channels to "Default Group" when channel_group_id is omitted during creation, preventing future NULL channel_group issues. 2026-01-18 15:52:50 -06:00
DawtCom
c970cfcf9a Move caching to client to remove burden on dispatch server 2026-01-18 00:49:17 -06:00
SergeantPanda
a37659eb82
Merge pull request #846 from JeffreyBytes:feature/667
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
feat: Add unassociated streams filter; fix: StreamsTable Group column header overflow
2026-01-17 18:07:50 -06:00
SergeantPanda
f810142493 Enhancement: Optimize channel filtering in StreamViewSet using Count annotation for improved performance on large datasets. 2026-01-17 17:55:12 -06:00