- Introduced an optional `duration` parameter for catch-up sessions, allowing clients to specify the programme length in minutes. This value is preferred over EPG-derived durations and includes a buffer for provider lag.
- Updated the API views and serializers to accept and process the new `duration` field.
- Enhanced the catch-up proxy implementation to utilize the client-supplied duration, improving playback accuracy.
- Added tests to validate the new duration handling and ensure proper integration with existing functionality.
Clients that build catch-up requests in QUERY layout (e.g. Open-TV /
Fred TV: /streaming/timeshift.php?username=...&stream=...&start=...)
had no matching urlpattern, so the request silently fell through to
the frontend's <path:unused_path> catch-all and got served index.html
(200 OK, wrong content) instead of reaching the proxy — no error, no
log line, the client just fails to play.
The PATH-style layout (timeshift/<user>/<pass>/<stream_id>/<ts>/<dur>)
already worked; QUERY-style autodetection already existed for outgoing
provider requests (helpers.build_timeshift_url_format_a/_b) but was
never mirrored to the incoming route.
Split timeshift_proxy into a shared _timeshift_proxy_impl plus two thin
entry points (PATH-style timeshift_proxy, new QUERY-style
timeshift_proxy_query) so both incoming layouts are recognized.
Reported against the predecessor plugin as dispatcharr_timeshift#10;
reproduced identically against dispatcharr:dev (confirmed via nginx
access log: a QUERY-style request returned 200 with a response body
exactly matching the size of frontend/dist/index.html).
- timeshift_proxy(): drop the resolve_channel_by_provider_stream_id
fallback. The client only ever sends Dispatcharr's internal Channel.id,
so resolve by id and return 404 on miss. Remove the dead `channel = None`
sentinel and the now-unused import.
- Delete the now-orphaned resolve_channel_by_provider_stream_id helper and
its local Stream import from apps/channels/utils.py (zero remaining refs).
- Import django.core.cache.cache at module top-level instead of inside
_get_cached_format_index / _set_cached_format_index.
- Fix the stale dispatcharr/urls.py comment: the "duration" slot carries
Channel.id, not the provider stream_id.
- Genericize provider host references in code comments.
Adds native catch-up/timeshift replay for Xtream Codes providers through
the same HTTPStreamReader transport pipeline as live TV.
Timeshift proxy (apps/timeshift/):
- URL cascade: 3 candidate timestamp formats per provider, per-account
format cache for fast-forward seek performance
- MPEG-TS preamble stripping (shared with HTTPStreamReader)
- Stats integration: timeshift viewers appear on /stats with TIMESHIFT badge
- Auth via hmac.compare_digest on XC password
Catchup detection — denormalized for zero-cost output queries:
- Stream.is_catchup + Stream.catchup_days populated at XC import time
- Channel.has_catchup + Channel.catchup_days + Channel.catchup_provider_stream_id
rolled up via ChannelStream post_save signal (UI path) and explicit SQL
after bulk_create (import path)
- _xc_channel_entry() reads denormalized fields instead of per-channel
custom_properties JSON introspection (eliminates N+1 queries)
- Migration 0038 backfills existing data via raw SQL
XC API enhancements:
- server_info.timezone + start/end + time_now use configured timezone
(triple consistency rule — fixes wrong-programme-plays bug)
- Dynamic has_archive flag + auto prev_days for catch-up channels
- XMLTV timestamps rewritten to local timezone for catch-up clients
HTTPStreamReader extended (apps/proxy/live_proxy/input/http_streamer.py):
- 1 MB pipe buffer via fcntl F_SETPIPE_SZ (eliminates producer/consumer
ping-pong that halved throughput)
- Pre-opened response= for URL cascade workflows
- strip_ts_preamble= for XC servers emitting PHP warnings before TS
- find_ts_sync() as shared utility
- Builds on upstream O_NONBLOCK + select() write loop
Provider stream_id lookup order:
- stream_xc() and xc_get_epg() try internal Channel.id first, fall back
to provider stream_id only when needed (avoids unconditional query on
every request)
Also includes:
- VOD provider cascade in stream_vod() — iterates all M3U relations by
priority when first provider is at capacity
- Defensive null-safety: custom_sid: None → "" in get_live_streams,
get_vod_streams, get_vod_info, get_series_info (fixes iPlayTV crash on
JSON null for string fields)
- Timeshift settings UI (timezone selector, debug toggle)
- StreamConnectionCard violet TIMESHIFT badge
- Orphan cleanup skips timeshift_* virtual channels
- 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
Added login page
Added login/logout buttons
Added m3u settings
Added stream profiles to channels and streams
Added ability to add individual stream outside of m3u file
Added ability to add youtube live links as a stream source
Deleted migrations
Deleted pycache folders
Changed HDHR to get ip dynamically