Skip to content

tvtimes

Iain Smith edited this page Sep 3, 2026 · 5 revisions

tvtimes

tvtimes is the companion self-hosted TV-guide web app. It aggregates several IPTV/tuner sources (M3U, Xtream, Stalker, HDHomeRun) into one de-duplicated line-up and one XMLTV guide whose times are already corrected per channel, then publishes both behind a single rotatable token.

Point tvdinner at it and you get that whole merged line-up, guide included, as one source — the natural split being tvtimes is where you plan, tvdinner is where you watch.

The URL

tvtimes://host[:port]?token=...

Use tvtimess:// if the server is served over https — the usual case behind a reverse proxy. A base path is kept if tvtimes sits under a sub-path on your proxy:

tvtimess://example.com/tv?token=...

Getting the URL

In tvtimes: Settings → Export feeds → Generate feed links. Alongside the M3U and XMLTV URLs there's an Open in tvdinner row carrying the tvtimes:// URL ready-made.

  • Click it — after tvdinner default-handler (see the README), the browser hands tvtimes:/tvtimess: links straight to tvdinner, same as it already does for the tvdinner: Play links.
  • Or copy it and run tvdinner 'tvtimess://…', or save it as a bookmark so it's one keystroke next time.

The token is shown once. Rotating it in tvtimes invalidates the old one, so a saved bookmark needs updating after a rotate.

What it actually does

This is sugar, not a new protocol. The URL expands to that account's two export feeds:

<base>/api/exports/playlist.m3u?token=...
<base>/api/exports/epg.xml?token=...

and from there it's an ordinary M3U playlist with an XMLTV guide — so the program guide, favorites, recording and scheduling, bookmarks and the EPG cache all behave exactly as they do for any other M3U + XMLTV pair. There is no tvtimes-specific code past the URL expansion.

Two details worth knowing:

  • The guide URL comes from the host you typed, not from the url-tvg= header inside the playlist. tvtimes builds that header from its own configured public origin, which needn't be the address this machine reaches it on (a LAN IP vs the proxied hostname). An explicit --epg still overrides both.
  • Per-channel clock shifts are already applied server-side. tvtimes writes corrected times into its XMLTV, so you shouldn't need --time-shift/--epg-shifts on a tvtimes source — if a channel still looks off, fix it in tvtimes so every downstream player benefits.

Recording from the tvtimes watchlist

Add --record-watchlist and tvdinner polls the account's watchlist every 15 minutes, scheduling a recording for each upcoming airing anyone on it has flagged:

tvdinner 'tvtimess://tv.example.com?token=...' --record-watchlist

Press Remind me (or Watch this title) in the tvtimes web app — from your phone, on the bus — and the box at home records it. Its scheduled-recording engine does the actual work; this just feeds it.

  • The watchlist is per user but the export token is per account, so a shared household account records what anyone on it flagged, de-duplicated per broadcast.
  • Entries created this way are tagged in schedule.json and reconciled on every poll — un-watchlist something in tvtimes and its pending recording disappears too.
  • Recordings you scheduled by hand from the guide are never touched, and if one already covers an airing the watchlist also wants, no duplicate is added beside it.
  • Times arrive already clock-shift corrected, so they line up with the guide rather than with raw feed time.
  • The usual caveat: there's no background service, so tvdinner has to still be running when the recording's time comes.

A failed poll (server down, box off the network) is logged and retried on the next tick — it never drops recordings already scheduled.

Reporting what you watched back

--report-watch-state posts what you watch on this source back to the tvtimes account every 15 minutes, so its web guide dims and ticks the programmes you've already seen:

tvdinner 'tvtimess://tv.example.com?token=...' --report-watch-state \
    --device-name 'living room'
  • Only live-channel watches from this feed are sent. A local file, a YouTube video, a Plex episode or a channel from another playlist has no tvtimes channel to attribute to and is skipped.
  • What goes over the wire is plain start/stop intervals, never "programme X was watched". tvtimes derives which programmes those cover by overlapping them against its own guide — so a guide refresh, or correcting a channel's clock shift, updates the answer with nothing re-reported.
  • A programme counts as watched once half of it, or 30 minutes, is covered. Flicking past doesn't count; half a three-hour film isn't demanded either.
  • The last week of history is resent on every tick rather than tracked as "already sent". tvtimes de-duplicates, so a restart or a spell offline catches up by itself with no local bookkeeping to fall out of step.
  • --device-name labels the box, so a household with more than one player can tell them apart.

This is the only thing the export token can write, and it stays narrow — it appends intervals for channels already on the account and nothing else. Leave the flag off and nothing is ever written.

Sharing favourites

tvdinner 'tvtimess://tv.example.com?token=...' --sync-favourites

Stars set in the tvtimes web app appear in tvdinner's guide, read from that account's favourites feed at startup.

Additive and one-way. Un-starring in tvtimes never removes a favourite here. favorites.json records only channel names, with no note of where each came from, so a two-way reconcile couldn't tell "removed upstream" from "added locally" -- and quietly deleting a favourite you set yourself is the worse failure of the two. Press h to un-star it here.

Jumping back to the web guide

T (shift-t) opens the tvtimes web app for whatever's on the current channel -- the reverse of tvtimes' own Play button.

It lands on tvtimes' search page for the programme title rather than the exact guide cell. tvtimes' grid is virtualised, so pointing at one cell would need scroll-to-row support there; finding the thing by name is what you actually want from this end, and it needs nothing. With no EPG data for the channel it just opens tvtimes itself.

Everything at once

tvdinner 'tvtimess://tv.example.com?token=...' \
    --record-watchlist \
    --report-watch-state --device-name 'living room' \
    --sync-favourites

Every one of these rides the same export token -- there's no second credential and no per-feature pairing. The feeds behind them are ordinary HTTP and are documented as OpenAPI, if you want to build something else against the same account: tvtimes Export API. tvtimes' own Pairing with tvdinner page covers the other side of each.

Playing a single channel instead

tvtimes' Play button on a programme emits a tvdinner: link that carries just that one channel (plus its own guide). That's a different, lighter hand-off — see the README. Use tvtimes:// when you want the whole line-up; use Play when you just want to watch the thing you're looking at.

Credentials

Like the Xtream/Stalker/Plex cases, a tvtimes:// URL's token is stored as plain text wherever the source URL itself is stored (bookmarks.json, backup archives), and is redacted before it reaches the log file. Anyone holding it can read your line-up and stream through it — treat it as a password.

Clone this wiki locally