Dispatcharr Timeshift Plugin
Timeshift/catch-up TV plugin for Dispatcharr. Watch past TV programs (up to 7 days) via Xtream Codes providers.
Version: 1.1.9
GitHub: https://github.com/cedric-marcoux/dispatcharr_timeshift
License: MIT
⚠️ IMPORTANT: Installation Methods
Method 1: Git Clone (Recommended)
The easiest and most reliable way to install:
cd /path/to/dispatcharr/data/plugins/
git clone https://github.com/cedric-marcoux/dispatcharr_timeshift.git
docker compose restart dispatcharr
Then enable the plugin in Dispatcharr Settings → Plugins.
Method 2: Download Release Asset
Download dispatcharr_timeshift.zip from the Releases page, then import via Settings → Plugins → Import.
Method 3: Manual ZIP (Advanced)
⚠️ WARNING: GitHub's default "Download ZIP" includes the branch/tag name in the folder (e.g., dispatcharr_timeshift-1.1.8/ or dispatcharr_timeshift-main/), which breaks Python imports. You must rename the folder:
cd /path/to/dispatcharr/data/plugins/
# If downloaded from a release tag (v1.1.8):
unzip dispatcharr_timeshift-1.1.8.zip
mv dispatcharr_timeshift-1.1.8 dispatcharr_timeshift
# If downloaded from main branch:
unzip dispatcharr_timeshift-main.zip
mv dispatcharr_timeshift-main dispatcharr_timeshift
# Fix permissions
chmod 644 dispatcharr_timeshift/*.py
chown 1000:1000 dispatcharr_timeshift/*
docker compose restart dispatcharr
Timeshift Not Working?
If timeshift features don't appear after installation, your provider may not support timeshift (tv_archive). Check if your Xtream Codes provider offers catch-up/replay functionality.
Changelog
v1.3.1
- [FIX] Catch-up icons missing in TiviMate / IPTVnator after EPG cache clear — the XMLTV stream served at
/xmltv.php did not include past programmes, so clients had nothing to attach has_archive icons to once their local cache was wiped
- Root cause: Dispatcharr core
generate_epg honors a prev_days parameter (URL query or user.custom_properties.epg_prev_days) that controls how many days of past EPG to include. Default is 0. The plugin's generate_epg patch only converted timestamps to local timezone; it never injected prev_days. Catch-up clients consume the XMLTV grid for the rewind UI, so when their cache was cleared they saw zero past programmes and lost all catch-up icons
- Fix: the patch now auto-injects
prev_days based on each provider's tv_archive_duration (max across all XC streams with tv_archive=1). The value is cached for 5 minutes per worker so XMLTV downloads stay fast. URL ?prev_days= and per-user epg_prev_days keep precedence
- New setting
xmltv_prev_days_override (default: 0 = auto from provider): set a positive integer (1-30) to force a fixed lookback regardless of provider metadata
- This bug pre-dated v1.3.0 but was masked by clients' persistent EPG cache; clearing TiviMate cache after upgrade exposed it
v1.3.0
- Major refactor for Dispatcharr v0.24 compatibility + idempotency
- [NEW] uWSGI worker warm-up (Dispatcharr v0.24+): hooks were not being installed in uWSGI workers
- Root cause: Dispatcharr v0.24 (commit ddb0328) introduced
should_skip_initialization() which skips plugin discovery in worker processes (parent.name in ['uwsgi', 'gunicorn']). With lazy-apps=true, each uWSGI worker initializes independently and never imports plugin.py → monkey-patches never applied → tv_archive=0 returned
- Fix: new
_warmup_workers() helper. When the plugin gets imported in any worker (triggered by a Connect event firing — e.g. a client playing any channel), the helper fires 20 sequential HTTP loopback requests to /live/...ts. Each request hits a different uWSGI worker (round-robin), which fires channel_start, which causes that worker to import the plugin and install hooks. A Redis NX lock prevents multiple workers from running warmup simultaneously
- Defense in depth: new
_keepalive action subscribed to channel_start, channel_stop, client_connect, client_disconnect, epg_refresh events. Any worker that handles a Connect event (even not a stream play) loads the plugin
- New setting
warmup_on_enable (default: true): disable to skip the auto-warmup HTTP loopback (~12 KB total bandwidth) and rely solely on natural event firing
- Cold-start behavior: immediately after Dispatcharr restart, no uWSGI worker has the plugin loaded (Dispatcharr v0.24 design). The first client action — playing any channel — triggers
channel_start in one worker, which discovers the plugin, installs hooks, and runs warmup to propagate to remaining workers. Within ~2 seconds all workers are patched
- Plugin remains 100% independent — no Dispatcharr core modifications required
- [FIX] Issue #14 — RecursionError on plugin re-discover: the
URLResolver.resolve patch was not idempotent. Calling _patch_url_resolver() twice (e.g. via pm.discover_plugins(force_reload=True) from another plugin's development cycle) captured the already-patched function as "original", recursing one level per re-discover until Python's recursion limit blew up every API request. Fixed using the same _is_timeshift_patch + _native_func marker pattern already used by the other four patches in this module
- [FIX] Issue #8 — Fallback chain not working in
timeshift_proxy: when a channel's first stream lacks tv_archive=1 but a later stream has it, the plugin returned "Timeshift not supported for this channel" instead of using the catch-up-enabled fallback stream. The fallback chain logic was originally added in v1.1.9 (community contribution) but was accidentally reverted in v1.2.0 during a refactor. Restored in v1.3.0
v1.1.9
- New feature: Catch-up Fallback Chain - Channels with multiple streams now support catch-up even when top-priority stream lacks it
- Previously: If top-priority stream (e.g., UHD) had no catch-up, entire channel was marked as unavailable
- Now: Plugin searches all streams in priority order to find one with
tv_archive=1
- IPTV clients now correctly see "Catch-up available" even when highest-quality stream lacks it
- Timeshift requests automatically use the catch-up-enabled stream (transparent to user)
- Maintains stream priority for live TV (top stream still preferred for live playback)
- Example: BBC One has Sky (no catch-up) and IPTV (with catch-up) → Users can now watch catch-up from IPTV stream
- Modified 3 functions across 2 files:
_patch_xc_get_live_streams(), _patch_xc_get_epg(), timeshift_proxy()
- Backward compatible: Single-stream channels work identically
v1.1.8
- Documentation: Improved installation instructions
- Method 1: Git clone (recommended) - most reliable method
- Method 2: Download release asset
dispatcharr_timeshift.zip (correct folder name)
- Method 3: Manual ZIP with folder rename warning
- Explained why GitHub's default ZIP breaks Python imports (folder name includes version/branch)
v1.1.7
- Bug fix: Export Plugin class in
__init__.py
- Dispatcharr requires the Plugin class to be exported from
__init__.py
- Without this, the plugin fails with "invalid plugin: missing plugin class"
- Added
from .plugin import Plugin and __all__ = ['Plugin']
v1.1.6
- New feature: Debug Mode - Toggle in plugin settings to enable ultra-verbose logging
- Normal mode: Minimal logging (1 line per timeshift request + errors only)
- Debug mode: Detailed logs for every step (config loading, channel lookup, timestamp conversion, URL building, provider response)
- New feature: URL Format Selection - Choose between timeshift URL formats:
- Auto-detect (default): Tries Format A, falls back to Format B if 400 error
- Format A: Query string (
/streaming/timeshift.php?username=X&...)
- Format B: Path-based (
/timeshift/user/pass/duration/time/id.ts)
- Custom: User-defined template with placeholders
- New feature: Custom URL Template - For exotic providers with non-standard URLs
- Placeholders:
{server_url}, {username}, {password}, {stream_id}, {timestamp}, {duration}
- Only used when "Custom template" is selected in URL Format
- New feature: Timezone Dropdown - Provider timezone now uses a dropdown with 120 IANA timezone options
- Organized by region: UTC, Europe, Americas, Asia, Africa, Australia/Pacific
- Prevents typos and invalid timezone entries
- Code cleanup: Added
.strip() to all config values to prevent whitespace issues
- Reduced log noise: Production logs now minimal, detailed info only in debug mode
- Bug fix: XMLTV EPG compatibility with Dispatcharr v0.14 (handles both HttpResponse and StreamingHttpResponse)
v1.1.5
- Bug fix: Re-enabled UTC→Local timezone conversion for timeshift timestamps
- v1.1.4 incorrectly removed the conversion, causing wrong content to play
- Root cause: IPTV clients (iPlayTV, TiviMate, Televizo) use
start_timestamp (UTC unix timestamp) from EPG to construct timeshift URLs
- The timestamp in the URL is therefore in UTC, but XC providers expect LOCAL time
- Now views.py correctly converts the timestamp from UTC to the configured timezone
- Example: User selects 18:00 Toronto show → Client sends 23:00 UTC → Plugin converts to 18:00 local → Provider plays correct content
v1.1.4 (BROKEN - DO NOT USE)
- ~~Bug fix: Removed double timezone conversion~~ - This was incorrect
- This version broke timeshift for all non-UTC timezones
- Users experienced wrong content playing (offset by their timezone difference)
- Fixed in v1.1.5
v1.1.3
- Bug fix: Timezone setting was not being read from database
- Plugin was using wrong attribute
config.config instead of config.settings
- Timezone always defaulted to "Europe/Brussels" regardless of user setting
- Now correctly reads from Dispatcharr's PluginConfig.settings field
- Affects both timeshift URL conversion and EPG timestamp conversion
v1.1.2
- Code cleanup: Removed dead code (
uninstall_hooks() and _restore_*() functions)
- Dispatcharr never calls
plugin.run("disable"), so these functions were never executed
- Optimized diagnostics: Expensive DB queries in 404 handler now only run in DEBUG mode
- Reduces overhead on production systems
- Basic warning still logged at INFO level
- Minor fix: Removed unnecessary
if chunk: check in stream generator
- Tested with Dispatcharr v0.14.0
v1.1.1
- Dynamic EPG-based duration: Timeshift requests now use the actual programme duration from EPG
- Calculates duration from programme's
end_time - start_time
- Adds 5-minute buffer for stream startup
- Falls back to 120 minutes if programme not found in EPG
- Caps at 8 hours maximum to prevent issues
- Prevents long movies from being cut off (was hardcoded to 2h)
- More efficient for short programmes (30-45 min)
v1.1.0
- URL format fallback: Automatic detection and fallback for providers using different timeshift URL formats
- Format A (default):
/streaming/timeshift.php?username=X&password=Y&stream=Z&start=T&duration=N
- Format B (fallback):
/timeshift/{username}/{password}/N/{timestamp}/{stream_id}.ts
- Automatically tries Format B if Format A returns HTTP 400
- Caches working format per M3U account for session (no restart needed)
- Fixes "Provider returned 400" error for providers using path-based timeshift URLs
v1.0.5
- Enhanced diagnostics: Improved "Channel not found" logging with detailed troubleshooting info
- Shows if stream exists but with wrong account type (need 'XC')
- Shows if stream has no channels assigned
- Shows total XC streams count (helps detect sync issues)
- Shows if channel exists but user lacks access level
v1.0.4
- Bug fix: Fixed
AssertionError: .accepted_renderer not set on Response error
- Replaced Django REST Framework
Response with Django JsonResponse in patched_stream_xc()
- This error occurred when channel lookup failed (404) or credentials were invalid (401)
- The issue was that DRF's Response requires a renderer, but the patched function is called from Django URL patterns, not through DRF's APIView
v1.0.3
- Enhanced logging: Improved error diagnostics with detailed context for troubleshooting
- API requests now log channel enhancement stats (e.g., "Enhanced 36/36 channels")
- EPG requests log channel lookups and program generation counts
- Authentication failures now specify the exact reason (missing xc_password, wrong password, unknown user)
- Provider errors include status code, content-type, and response body preview
- All errors include actionable diagnostic hints
v1.0.2
Based on fixes from Lesthat's fork - thanks for the contributions!
- Multi-client support: Added compatibility for Snappier iOS and IPTVX
- EPG 404 fix: Fixed "not found" errors when clients request EPG data using provider stream IDs
- Data type fixes: Corrected JSON types for strict validation (Snappier iOS compatibility)
- EPG timezone fix: Programs now display at correct times (fixed +2h offset issue)
- XMLTV timezone: Converted timestamps for IPTVX compatibility
- Language setting: Configurable EPG language (27 European languages)
- Unique program IDs: Each program now has a timestamp-based unique ID
v1.0.1
- User-Agent fix: Now uses the User-Agent configured in M3U account settings (TiviMate, VLC, etc.) instead of a hardcoded value
v1.0.0
IMPORTANT: After enabling or disabling this plugin, you must refresh your source in your IPTV player (e.g., iPlayTV) for it to detect the timeshift/replay availability on channels.
Features
- 100% Plugin Solution - No modification to Dispatcharr