Inspiration

Home media servers solve playback, but browsing their schedules from a phone can still feel like operating server software. ErsatzGuide makes that daily interaction feel like a native TV guide while preserving the privacy and control of a self-hosted setup.

What it does

ErsatzGuide combines an ErsatzTV M3U playlist and XMLTV schedule into a local Android guide. It provides a timeline, Now & Next, channel schedules, offline search, programme details, and reminders that survive reboots and guide refreshes. Imports are transactional, so a failed network refresh cannot erase the last usable guide. There are no accounts, ads, analytics, or cloud backend.

How we built it

The app is written in Kotlin with Jetpack Compose and Material 3. Navigation Compose drives the five main destinations and detail routes. Hilt supplies replaceable services; Room stores guide and reminder data; DataStore holds settings; OkHttp streams the feeds; WorkManager handles periodic refreshes; and AlarmManager schedules reminders. Parsing, channel matching, synchronization, and reminder reconciliation live outside composables and are covered by unit and instrumentation tests.

Challenges we ran into

XMLTV is deceptively irregular: timestamps include offsets, individual records can be malformed, playlists and schedules disagree about identifiers, and a refresh can arrive while reminders refer to older programme times. The app therefore parses defensively, matches channels conservatively, converts times to instants, and commits validated imports in one Room transaction. Android's background limits and responsive Compose navigation added a second set of constraints across phones, tablets, reboots, and notification deep links.

Accomplishments that we're proud of

  • A useful guide remains available offline and survives failed refreshes.
  • Reminders are reconciled rather than silently dropped when schedules change.
  • Local HTTP is limited to private-network destinations; normal TLS validation remains enabled for HTTPS.
  • The same shell adapts from a bottom bar to a navigation rail on large screens.
  • Unit tests cover parsing, matching, time calculations, URL validation, reminders, and view-model behavior; instrumentation tests cover Room, navigation, accessibility, and reminder reconciliation. ## What we learned For self-hosted tools, resilience and honest failure states matter more than a large feature count. Stable IDs, offset-aware times, conservative matching, and transactional imports make the interface feel simple because ambiguity is dealt with below the UI. The navigation bug also reinforced that selected visual state is not enough: each tab needs a real destination if the content is expected to change. ## What's next for ErsatzGuide Next steps are optional video playback, richer programme artwork and metadata, homescreen widgets, and signed release distribution. The existing stream URL, repository boundaries, and responsive navigation leave room for those additions without replacing the guide core.

Built With

  • android
  • codex
  • gpt-5.6
  • gradle
  • hilt
  • jetpack-compose
  • kotlin
  • material-3
  • room
  • workmanager
Share this project:

Updates

Submission history