LibreTrakt generates live, subscribable iCalendar feeds for TV shows.
It is intentionally small: Cloudflare Pages Functions, TypeScript, TMDb, no database, no Trakt dependency, and no paid API. Calendar clients subscribe to normal .ics URLs and handle local timezone conversion themselves.
Starter catalog:
- Euphoria:
/api/cal/euphoria.ics - Your Friends & Neighbors:
/api/cal/your-friends-neighbors.ics - Margo's Got Money Troubles:
/api/cal/margos-got-money-troubles.ics - All starter shows:
/api/cal/all.ics
The homepage exposes raw ICS, Apple Calendar, and Google Calendar links for each starter show and for the all-starter feed.
Any TMDb TV show can be used by ID:
/api/cal/tmdb-241609.icsThe homepage also includes a TMDb TV search box. Search results generate feed URLs on demand using the same tmdb-<id> format.
Custom bundles are stateless and deterministic:
/api/cal/all.ics?shows=euphoria,tmdb-241609,tmdb-245318LibreTrakt fetches TV metadata, seasons, and episodes from TMDb. It extracts season number, episode number, title, air date, and network, then emits UTC calendar events with a 30-minute alarm.
TMDb provides air_date, but not a release time. LibreTrakt resolves timestamps in this order:
- Exact per-episode timestamp, when configured.
- Per-show release-time override.
- Platform strategy.
- UTC midnight.
Built-in strategies:
US_PRIMETIME = 21:00 America/New_York
GLOBAL_MIDNIGHT_PT = 00:00 America/Los_Angeles
GLOBAL_MIDNIGHT_ET = 00:00 America/New_York
DEFAULT = 00:00:00ZLocal-time strategies are DST-aware: LibreTrakt resolves the IANA timezone offset for each episode's air date, so winter and summer episodes land on the right UTC moment.
Built-in platform mapping:
HBO -> US_PRIMETIME
Apple TV+ -> GLOBAL_MIDNIGHT_PT
Netflix -> GLOBAL_MIDNIGHT_PTAll event timestamps are UTC in YYYY-MM-DDTHH:mm:ssZ form. LibreTrakt does not use local timezones.
Generated feeds and search responses are cached at the Cloudflare edge with:
Cache-Control: public, max-age=1800For a friendly URL, add a catalog entry in src/shows.ts:
{
slug: "slow-horses",
title: "Slow Horses",
tmdbId: 95480,
network: "Apple TV+",
}For exact release overrides:
{
slug: "example",
title: "Example",
tmdbId: 123,
exactReleases: {
"1x1": "2026-04-26T07:00:00Z"
}
}Without a catalog entry, use /api/cal/tmdb-<id>.ics.
Apple Calendar accepts webcal:// URLs. For example:
webcal://libretrakt.pages.dev/api/cal/euphoria.icsGoogle Calendar accepts URL imports:
https://libretrakt.pages.dev/api/cal/euphoria.icsThe homepage's Google links use Google's cid=webcal://... subscribe flow and copy the HTTPS ICS URL to the clipboard. If Google opens an empty "From URL" form, paste the copied URL into the field.
Install dependencies with pnpm:
pnpm installRun checks:
pnpm test
pnpm run typecheck
pnpm run buildRun locally:
pnpm run devCreate .dev.vars for local TMDb access:
TMDB_API_TOKEN=your_tmdb_read_access_tokenAn example file is included at .dev.vars.example. For local convenience, LibreTrakt also accepts API_TOKEN, TMDB_API_KEY, or API_KEY; production should use TMDB_API_TOKEN.
Create the Pages project:
pnpm run pages:createStore the TMDb API Read Access Token as a Pages secret:
pnpm run secret:tmdbDeploy:
pnpm run deployThe production URL is:
https://libretrakt.pages.devGitHub Actions runs tests, typechecking, and the production build on pull requests and pushes to main.
Pushes to main deploy to Cloudflare Pages after verification passes. The project is a Cloudflare Pages Direct Upload project, so deployment is handled by Wrangler from GitHub Actions rather than by Cloudflare's Git integration.
Required GitHub repository secrets:
CLOUDFLARE_ACCOUNT_ID: Cloudflare account ID for the Pages project.CLOUDFLARE_API_TOKEN: Cloudflare API token withAccount > Cloudflare Pages > Edit.
The runtime TMDB_API_TOKEN remains configured as a Cloudflare Pages secret with pnpm run secret:tmdb; it is not needed by GitHub Actions.
- Minimal
- Deterministic
- Location-agnostic
- Hackable
- Zero-cost to run