Archive & Catch-up: M3U and Xtream specification
The source-of-truth spec for IPTV providers — how to declare catch-up/archive in M3U and Xtream Codes playlists so it plays correctly in UniPlayer.
IPTV catch-up (also called archive, timeshift or DVR) lets a player rebuild a past-broadcast URL from a live channel URL plus a UTC start time. In an M3U playlist it is declared with the catchup, catchup-source and catchup-days attributes on each #EXTINF line; UniPlayer supports the default, append, shift and Flussonic modes, plus Xtream Codes tv_archive auto-detection. UniPlayer is a player like VLC — the provider supplies the playlist and the archive window; UniPlayer only builds the playback URL from the EPG programme's start time.
What this document is
This is the provider-facing reference for declaring catch-up TV (a.k.a. archive, timeshift, DVR, рестарт/архив) in the playlists your subscribers load into UniPlayer.
UniPlayer is a player, not a content service — the same neutral-tool model as VLC. Your server holds the streams and the recorded archive; UniPlayer reads your playlist, matches it to an EPG, and — when a user scrubs back in the guide — rebuilds the archive playback URL from the rules below. If your playlist declares catch-up the way this spec describes, every UniPlayer client (iOS, iPadOS, Apple TV) plays your archive without any per-provider integration on our side.
In short. Put the right
catchupmode and (where needed) acatchup-sourcetemplate on each#EXTINFline, advertise how many days you keep withcatchup-days, and make sure your origin accepts a UTC Unix start time. That’s the whole contract.
1. The catch-up model
Catch-up playback is always the same three-part operation:
| Part | Who supplies it | Notes |
|---|---|---|
| Live URL | Your playlist (#EXTINF → URL line) | The normal live channel address. |
| Start time | UniPlayer, from the EPG | The selected programme’s start, as a UTC Unix timestamp (seconds). |
| Duration / window | Your playlist (catchup-days) + EPG | How far back the archive reaches; programme length. |
UniPlayer derives the start offset as programme_start − now (a negative number of seconds,
“into the past”), converts it to an absolute UTC Unix timestamp, and injects it into your live
URL according to the channel’s catchup mode. There is no separate “archive playlist” — the
archive URL is generated from the live URL.
Terminology (all interchangeable in the wild): catch-up = archive = timeshift = DVR = catchup TV = rewind. UniPlayer treats them as one feature.
2. M3U attributes UniPlayer reads
Declare catch-up with attributes on the #EXTINF line. UniPlayer parses any key="value"
pair; the catch-up–relevant keys are:
| Attribute | Required | Meaning |
|---|---|---|
catchup | Yes¹ | The catch-up mode (see §3). Also accepted as catchup-type. |
catchup-source | Mode-dependent | URL template or query string with placeholders (see §4). Required for default/append when the URL can’t be derived automatically. |
catchup-days | Recommended | Integer — how many past days of archive you keep. Drives how far back the guide lets users scrub. |
timeshift | Optional | Legacy SIPTV signal. Its presence makes UniPlayer treat the channel as shift mode; its value is also read as a days fallback. |
tvg-rec | Optional | Legacy “recording days” — read as a catchup-days fallback (>0 ⇒ archive available). |
catchup-time | Optional | Window expressed in seconds (converted to days internally). Lowest-priority days fallback. |
¹ If you omit catchup but the stream URL shape clearly indicates an archive-capable origin
(e.g. it contains /archive-, /rewind-, or timeshift), UniPlayer infers the mode. Explicit
is always better — don’t rely on inference.
Days resolution order. UniPlayer reads the window from the first present of:
catchup-days → timeshift → tvg-rec → catchup-time÷86400. If none is present but the
channel is otherwise detected as catch-up–capable, UniPlayer assumes a 30-day window.
Always set catchup-days so users see the real depth.
3. Catch-up modes (the catchup value)
UniPlayer normalises the catchup / catchup-type value to one of the modes below. Aliases in
the right column are all accepted and folded to the canonical mode.
| Canonical mode | Accepted aliases | Use when… |
|---|---|---|
default | default | You give a full archive URL template in catchup-source. |
append | append | You give a query-string fragment in catchup-source to append to the live URL. |
shift | shift, timeshift | Your origin accepts the SIPTV utc/lutc query parameters. |
flussonic | flussonic, fs, flussonic-hls, flussonic-ts | Your origin is Flussonic (or Flussonic-compatible). UniPlayer builds the URL — no catchup-source needed. |
xstream-1 | xstream, xtream, xc | Xtream Codes archive (usually auto-detected — see §6). |
vod | vod | Video-on-demand–style catch-up entries. |
3.1 default — full URL template
catchup-source is the complete archive URL, with placeholders UniPlayer substitutes:
#EXTINF:-1 catchup="default" catchup-days="7" catchup-source="https://origin.example/ch12/video-${start}-${duration}.m3u8",Channel 12
https://origin.example/ch12/index.m3u8
3.2 append — query fragment
catchup-source is just the part to append to the live URL. Start it with ? (or & if
your live URL already has a query string):
#EXTINF:-1 catchup="append" catchup-days="7" catchup-source="?utc=${start}&lutc=now",News 24
https://origin.example/news/index.m3u8?token=abc
3.3 shift — SIPTV utc/lutc
No catchup-source needed. UniPlayer appends utc=<start>&lutc=<now> to the live URL
(using & when the URL already contains ?):
#EXTINF:-1 catchup="shift" catchup-days="5",Sports 1
https://origin.example/sports1/index.m3u8
→ archive request becomes …/sports1/index.m3u8?utc=1717400000&lutc=1717420000.
If the live URL’s last path segment is mpegts, UniPlayer instead rewrites it to
timeshift_abs-<start>.ts (see §5).
3.4 flussonic — auto-built from the live URL
No catchup-source needed — set the mode and UniPlayer rewrites the live URL using Flussonic’s
own timeshift convention (see §5 for the exact transform):
#EXTINF:-1 catchup="flussonic" catchup-days="14",Movie Channel
https://origin.example/movie/index.m3u8
3.5 xstream-1 / xc — Xtream Codes
Normally you don’t hand-write this — when UniPlayer ingests an Xtream Codes playlist it sets
this automatically from tv_archive/tv_archive_duration (§6). The aliases exist so M3U
exports from Xtream panels are recognised too.
4. URL template placeholders
When you supply a catchup-source (modes default / append), UniPlayer substitutes these
tokens. All times are UTC Unix epoch seconds.
| Token | Substituted with | Notes |
|---|---|---|
${start} | Programme start, absolute UTC Unix seconds | The primary token. Use this for the archive start time. |
${duration} | Programme duration in seconds | Recognised inside the Flussonic-style literal video-${start}-${duration} (see below). |
utc=<n> / lutc=<n> | Injected by shift mode | utc = start, lutc = now. Not template tokens — emitted automatically in shift. |
Flussonic literal shortcut. If your catchup-source (or live URL) contains the literal
segment video-${start}-${duration}, UniPlayer replaces that whole segment with Flussonic’s
relative form mono-timeshift_rel<seconds> (negative seconds into the past). This lets a single
template serve both HLS and Flussonic origins.
Compatibility note (Kodi /
{utc}convention). The wider IPTV ecosystem (Kodi’s PVR IPTV Simple Client) also defines{utc},{lutc},{utcend},{duration},{offset}, the strftime tokens{Y}{m}{d}{H}{M}{S}, and{catchup-id}. UniPlayer’s reliably-supported set is the${start}/${duration}tokens plus theshift(utc/lutc) andflussonicauto-modes. For maximum compatibility across players and guaranteed playback in UniPlayer, prefercatchup="flussonic"for Flussonic origins andcatchup="shift"for utc/lutc origins — these need no template and can’t be mistyped.
5. Flussonic origins (exact transform)
For catchup="flussonic" (and fs / flussonic-hls / flussonic-ts), UniPlayer rewrites the
live URL into Flussonic’s timeshift URL. Two cases:
| Live URL ends with | Archive URL UniPlayer builds | Form |
|---|---|---|
…/mpegts | …/timeshift_abs-<startUnixUTC>.ts | Absolute HTTP-MPEG-TS timeshift |
…/index.m3u8, …/mono.m3u8, …/video.m3u8 (HLS) | the index / mono / video segment → timeshift_rel<seconds> | Relative HLS timeshift (negative seconds) |
Examples:
Live: https://origin.example/ch/mpegts
Archive: https://origin.example/ch/timeshift_abs-1717400000.ts
Live: https://origin.example/ch/index.m3u8
Archive: https://origin.example/ch/timeshift_rel-3600.m3u8 (1 hour back)
Your Flussonic DVR must therefore accept both the absolute (timeshift_abs-<utc>.ts) and
relative (timeshift_rel<-seconds>.m3u8) forms. These are Flussonic’s native DVR endpoints;
no custom configuration on your side beyond enabling DVR for the stream.
6. Xtream Codes archive
When a user adds an Xtream Codes account, UniPlayer reads the live streams via the panel API
(player_api.php?action=get_live_streams). Two fields drive catch-up per stream:
| Field | Meaning |
|---|---|
tv_archive | 1 ⇒ catch-up is enabled for this channel; 0 ⇒ none. |
tv_archive_duration | Integer days of archive retained (e.g. 7). |
UniPlayer auto-maps these into the catch-up model: tv_archive=1 marks the channel as archive
(internal mode xstream-1) and tv_archive_duration becomes the catchup-days window. The
playlist as a whole is tagged with an archive_type so the UI advertises catch-up.
Xtream timeshift URL. The Xtream-native archive endpoint format is:
http://host:port/timeshift/USERNAME/PASSWORD/DURATION/START/STREAM_ID.EXT
# e.g.
http://host:port/timeshift/user/pass/60/2026-06-03:14-30/1234.ts
DURATION— minutes of the requested segment.START—YYYY-MM-DD:HH-MMin the server’s archive timezone.EXT—tsorm3u8.
Provider recommendation. Xtream catch-up is signalled through
tv_archive/tv_archive_duration, and that’s what UniPlayer reads for the window. For the most robust archive playback today, panels that can also emit M3U exports should include explicitcatchup="shift"(utc/lutc) orcatchup="flussonic"attributes on archive channels, since those modes are unambiguous and origin-agnostic. If your panel only exposes the Xtream API, ensuretv_archive_durationis accurate so the guide depth is correct.
7. Time, timestamps & timezones
These rules are non-negotiable for archive to line up with the guide:
- Start time is UTC Unix epoch seconds. UniPlayer substitutes
${start}/utc/timeshift_abs-…with an integer UTC timestamp. Your origin must interpret it as UTC. - EPG must be UTC-correct. UniPlayer derives the archive start from the EPG programme’s start time. If your XMLTV programme times drift from the actual broadcast, the archive will start at the wrong moment. Publish EPG times with correct timezone offsets (UniPlayer normalises everything to UTC on ingest).
- Relative offsets are negative. Flussonic
timeshift_relvalues are negative seconds (e.g.-3600= one hour ago). UniPlayer computes them fromnow. - No client-side correction offset. UniPlayer does not apply a
catchup-correctionshift. If your archive needs a fixed hour correction, bake it into your origin or EPG — don’t rely on a per-channel correction attribute. - Minimum offset. Requests less than ~1 second into the past fall back to the live URL.
8. Worked examples
A complete, mixed playlist a provider can model theirs on:
#EXTM3U url-tvg="https://origin.example/epg.xml.gz"
# Flussonic origin — simplest, no template needed
#EXTINF:-1 tvg-id="ch.movie" tvg-logo="https://origin.example/logo/movie.png" catchup="flussonic" catchup-days="14",Movie Channel
https://origin.example/movie/index.m3u8
# SIPTV utc/lutc origin
#EXTINF:-1 tvg-id="ch.sports1" catchup="shift" catchup-days="7",Sports 1
https://origin.example/sports1/index.m3u8?token=abc
# Full template (default mode)
#EXTINF:-1 tvg-id="ch.news" catchup="default" catchup-days="5" catchup-source="https://origin.example/news/video-${start}-${duration}.m3u8",News 24
https://origin.example/news/index.m3u8
# Append a query fragment
#EXTINF:-1 tvg-id="ch.kids" catchup="append" catchup-days="3" catchup-source="?utc=${start}",Kids TV
https://origin.example/kids/index.m3u8
# HTTP-MPEG-TS Flussonic (absolute timeshift)
#EXTINF:-1 tvg-id="ch.doc" catchup="flussonic" catchup-days="30",Docs HD
https://origin.example/doc/mpegts
9. Provider checklist
Before you ship a playlist, verify:
- Every archive channel has a
catchup(orcatchup-type) value from §3. -
catchup-daysis set and matches your real retention. -
default/appendchannels include acatchup-sourcewith${start}(and${duration}if needed). - Flussonic channels expose both
timeshift_abs-<utc>.tsandtimeshift_rel<-sec>.m3u8. - Your origin accepts a UTC Unix start time.
-
tvg-idis present and maps to an EPG channel (catch-up is selected from the guide — no EPG, no archive UI). - EPG programme times are timezone-correct.
- Xtream panels return accurate
tv_archive/tv_archive_duration.
10. Common mistakes
| Symptom | Likely cause |
|---|---|
| Archive button never appears | No tvg-id / no EPG match, or no catchup/catchup-days and URL shape isn’t inferable. |
| Guide only scrolls back a few days | catchup-days missing → 30-day assumption, or set lower than real retention. |
| Archive plays the wrong moment | EPG programme times in the wrong timezone, or origin treats ${start} as local time, not UTC. |
| Template not substituted | Used a Kodi-only token ({utc}, {Y}{m}{d}…) instead of ${start} / shift / flussonic — switch to a supported mode (§4). |
| Flussonic archive 404s | DVR not enabled for the stream, or origin doesn’t accept timeshift_rel / timeshift_abs. |
| Xtream archive depth wrong | tv_archive_duration inaccurate in the panel. |
Related
- M3U / M3U8 playlist format — base attributes, encoding, and structure.
- XMLTV / EPG — how programme times feed the archive start.
- UniPlayer is a neutral player; this spec describes how your playlist must be structured, not what content it carries.