Commit graph

12 commits

Author SHA1 Message Date
SergeantPanda
b24f39d9f8 fix(timestamps): enhance timestamp parsing to support mixed separators
This update modifies the timestamp parsing logic to accommodate various formats, including those using both dash and colon as separators for hours, minutes, and seconds. The changes ensure that timestamps like "HH-MM-SS" and mixed formats such as "HH-MM:SS" are correctly parsed and normalized. Additional tests have been added to validate these new parsing capabilities.
2026-07-17 22:53:21 +00:00
SergeantPanda
8bcc7c9ebe feat(timeshift): enhance catch-up programme handling and UI updates
Some checks are pending
Backend Tests / Plan test groups (push) Waiting to run
Backend Tests / (push) Blocked by required conditions
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
- Updated the catch-up stats UI to advance to the next EPG programme when playback continues past the original programme's end, improving user experience.
- Enhanced the `get_programme_info` function to resolve the guide entry based on the playhead position, ensuring accurate display of the current programme.
- Introduced new utility functions for computing catch-up playback position and checking if the playhead is outside the displayed programme window, optimizing session management.
- Added tests to validate the new functionality and ensure correct behaviour of the catch-up features.
2026-07-16 23:10:06 +00:00
SergeantPanda
14bfd25d9a feat(timeshift): enhance catch-up session handling with optional duration support
- 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.
2026-07-16 21:26:06 +00:00
SergeantPanda
388c4bd28d Enhancement: update catch-up features and enhance stream ordering logic
Some checks are pending
Backend Tests / Plan test groups (push) Waiting to run
Backend Tests / (push) Blocked by required conditions
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
- Improved multi-provider failover by preferring streams with sufficient catch-up days.
- Added new helper functions for calculating programme age and ordering catch-up streams.
- Removed deprecated XMLTV settings from the frontend and backend.
- Updated tests to validate new stream ordering logic and catch-up functionality.
2026-07-16 15:33:34 +00:00
SergeantPanda
33ada59dba fix(timeshift): refine catch-up timestamp handling and enhance EOF probe logic
Updated the catch-up timestamp normalization to reflect XC client specifications. Introduced a new mechanism to handle near-EOF duration probes, ensuring that playback statistics are preserved without reanchoring to the end of the file. Additionally, improved the handling of presentation lengths in the timeshift proxy to align with XC behavior. Updated tests to validate these changes and ensure robust playback functionality.
2026-07-15 21:26:25 +00:00
SergeantPanda
223dff33ed feat(timeshift): add catch-up playback, stats, and admin APIs
Add end-to-end catch-up support for XC clients and native apps: provider
proxy with failover and per-viewer session pooling, REST session minting
for tokenless playback URLs, catch-up admin stats, combined connection
stats, and Stats UI with dedicated cards plus websocket updates.

Includes Redis namespace consolidation under timeshift:* (dropping legacy
timeshift_ id prefixes), dedicated catch-up stop by session_id, and
cleaner channel/client metadata split for stats keys.

Closes #133
2026-07-11 17:14:31 +00:00
SergeantPanda
8e0a7b44f5 feat(timestamps): Introduce comprehensive timestamp normalization and parsing for catch-up functionality. Support various input formats including colon-dash, underscore, and Unix epoch. Enhance related tests to ensure robust handling of timestamp shapes in timeshift operations. 2026-06-26 13:33:50 -05:00
SergeantPanda
f2dfe6deb6 feat(channels): Add caching and resolution logic for provider archive days in XC catch-up streams. Implemented compute_provider_archive_days_capped and resolve_xc_epg_prev_days functions to enhance performance and flexibility in determining catch-up days. Updated docstrings for clarity. 2026-06-26 12:45:13 -05:00
Cédric Marcoux
c91f4f9a6e feat(timeshift): provider pool accounting, per-channel session takeover, rollup self-heal
Aligns catch-up with how live and VOD manage provider capacity:

- Reserve a provider profile slot (connection_pool) before every upstream
  connect, walking the account's active profiles default-first when the
  default is at capacity (profile_full/credential_full are transient and
  never mark the account decisive). Credentials for the reserved profile
  are resolved via get_transformed_credentials — the same credential
  extraction live playback uses — so pool accounting and real upstream
  usage always agree. All eligible streams blocked on capacity alone
  returns 503 (the VOD pool-exhausted precedent).

- Release exactly once via a one-shot Redis ownership token consumed with
  a transactional GET+DEL: the generator finally, the response-close
  wrapper, failed failover attempts and session takeover all share it, so
  no path can double-decrement and a client disconnecting before the
  first chunk still releases (Django registers the iterator's close() as
  a resource closer). Failures between reservation and the streaming
  response owning the slot release before propagating; an ownership-token
  write failure releases directly and reports transient unavailability.

- One catch-up session per user and channel: a new request (programme
  jump or seek) displaces the user's previous session on that channel —
  its slot is released synchronously, its stats are unregistered, and its
  generator is stopped through the standard stop-key mechanism — so rapid
  seeking cannot stack upstream provider connections.

- rollup_channel_catchup_fields self-heals channels left flagged
  is_catchup with no remaining catch-up stream (outside the
  account-scoped CTE), covering bulk removals on manual/multi-provider
  channels regardless of how the link rows disappeared. A regression test
  locks the ChannelStream post_delete signal firing on queryset bulk
  deletes.

Backend test suite grows to 94 (slot reservation/release on every
failover outcome, profile walk, mixed capacity-vs-upstream precedence,
exception-path release, token exactly-once semantics, takeover scoping
and ordering, never-started-generator release, rollup self-heal).
2026-06-12 16:36:02 +02:00
Cédric Marcoux
b6bbd39469 fix(timeshift): PATH-first catch-up + strict-UTC API surface with proxy-time provider timezone conversion
Two coupled fixes that make catch-up play the requested programme:

1. Try the PATH catch-up form (/timeshift/.../{start}/{id}.ts) BEFORE the
   timeshift.php query form. Empirical testing showed some XC providers
   return the LIVE stream on the query form (HTTP 200, silently ignoring
   `start`) — a valid MPEG-TS indistinguishable from a real archive, so the
   cascade accepted it and the user always got live content. The PATH form
   actually seeks. Candidate ordering now lives in
   build_timeshift_candidate_urls() with the full rationale.

2. Strict-UTC XC API surface + single proxy-time timezone conversion:
   - xc_get_epg start/end always UTC, server_info.timezone always "UTC",
     time_now UTC — the timezone triple is consistently UTC.
   - Removed the global default_timezone setting and the
     _convert_xmltv_to_local_timezone rewrite entirely.
   - The proxy converts the client's UTC timestamp to the SERVING provider's
     own zone (server_info.timezone captured on account refresh, read from
     the account's default profile) right before building the upstream URL —
     DST-correct via ZoneInfo, no-op for UTC/unknown zones. Verified against
     a real provider: it interprets the URL timestamp as its LOCAL wall
     clock, so pure UTC pass-through seeks 1-2h off.
   - EPG duration lookup keeps the ORIGINAL UTC timestamp (programmes are
     stored in UTC) — exactly one conversion in the whole chain.

Also per review feedback:
- Removed the debug_logging toggle — verbose timeshift logging now follows
  the standard logger DEBUG level (DISPATCHARR_LOG_LEVEL).
- Removed the dead default_language setting (never read anywhere; the EPG
  lang field is hardcoded). TIMESHIFT_DEFAULTS is down to
  xmltv_prev_days_override, and the Settings UI shows that single field
  with accurate help text.
- Validate the timestamp up front (400 on malformed input) instead of
  forwarding it verbatim into the upstream URL.
- Redact the upstream URL in the connection-error log (requests exceptions
  embed the full URL, which carries XC credentials).
- URL-encode XC credentials in both URL builders.
- Request identity encoding upstream (the TS-sync peek reads raw bytes).
- Poll the stop key on the 5-second heartbeat cadence instead of every 100
  chunks (~25 MB), so stream-limit terminations free the provider slot fast.
- New tests: proxy timestamp wiring (converted value reaches the URL
  builder, original UTC value reaches the duration lookup), _redact_url,
  decisive 3xx break (anti ban), invalid timestamp rejection; deflaked the
  format-cache promotion test (django cache persists across runs).
2026-06-10 22:04:44 +02:00
Cédric Marcoux
3b8717c4ce fix: timeshift stream management, channel resolution, and streaming throughput
- Channel resolution: resolve by Channel.id first (what the XC API emits
  to clients), fall back to provider stream_id for backward compatibility.
  Fixes 404 on all timeshift requests from XC clients.

- Stream management: timeshift is now a first-class citizen alongside live
  and VOD in the stream-limits system.  get_user_active_connections()
  detects type='timeshift', attempt_stream_termination() sets a Redis
  stop key (same pattern as VOD), and the timeshift view calls
  check_user_stream_limits() before connecting upstream.  The stream
  generator checks the stop key every 100 chunks.  Fixes infinite retry
  loops on max_connections=1 providers for live→timeshift and
  timeshift→timeshift transitions.

- Streaming: replaced HTTPStreamReader thread+pipe with direct
  iter_content+yield (same pattern as VOD proxy).  The pipe approach
  deadlocked under gevent because the reader thread's select() competed
  with the greenlet's pipe read for hub scheduling.  Throughput went
  from ~74 B/s to 13+ MB/s.

- TS preamble: peek now strips pre-sync bytes (PHP warnings, BOM) before
  prepending to the stream, preventing corrupt TS output.

- Credential safety: _redact_url() now truncates to scheme://host/...
  to avoid leaking XC path-based credentials in logs.

- Tests: updated 5 tests that referenced the removed HTTPStreamReader.
  21/21 timeshift tests pass.

- Migration: fixed SyntaxWarning for unescaped regex in 0038.
2026-05-27 23:32:57 +02:00
Cédric Marcoux
2781b21a89 feat: built-in XC catch-up (timeshift) support
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
2026-05-27 13:13:20 +02:00