← Writing

20 Oct 2026 · 4 min read

Anime, novels and addons on one TV remote

How I stitched anime, web novels, music and community addons into one Android TV app, and what integrating sources that break every week taught me about identity, failure and honest UI.

I watch a lot of anime and read a lot of web novels, and for years that meant a different app for each, none of them made for a TV remote. So I forked an Android TV media app and spent my evenings turning it into the one place I go for all of it: films and series, anime, web novels, music, audiobooks and live TV, all driven by a D-pad.

The features were the fun part. What made them hard was everything behind them: content comes from sources I don’t control, and those sources change, rate-limit, or disappear without notice. Most of the interesting engineering turned out to be about three things: identity, failure, and being honest in the UI.

Stop shipping the scrapers

The original app had fifteen site scrapers compiled into the APK. Every time a site rotated its domain or changed a page, that source died until the next release. Users saw it as “most sources don’t work”, and they were right.

The fix was structural. The app now speaks the community addon protocol (a manifest, catalogues, metadata and stream endpoints), where the source logic lives on a server that someone maintains and updates without touching my app. Built-in sources still exist, but where streams come from is now a setting with plain words on it: built-in sources, addons, or one before the other. The settings screen says what you trade, for example that addons without a debrid key mean peer-to-peer on a box that may struggle with it.

Identity is the real integration problem

Addons namespace their ids. A catalogue might call a show somecatalog:1234, while the most reliable stream addons only understand IMDb ids. The protocol’s prefix filtering meant that anything opened from such a catalogue skipped the very addons most likely to work.

That’s not a permissions problem, it’s an identity problem. So the app now resolves the IMDb id (from the addon’s own metadata, or failing that a title lookup) and also asks the addons that couldn’t understand the namespaced id. The original addon is still asked first and its streams stay on top.

Anime got the same treatment. Matching shows by title across sites is a swamp of romanisations and season naming, so every anime source is keyed on its AniList id instead. No title matching, no guessing which “Season 2” is which. Addon-provided anime uses the same id with a prefix, so switching between built-in and addon sources doesn’t change what you’re looking at.

Ranking what comes back

Ask several addons for a film and you get dozens of rows. Sorting by popularity alone put a 263-film collection at the top of a single film, because it had the most peers. The ranking now:

  1. drops things a TV can’t play directly (disc images, archives);
  2. ranks by how awkward startup will be: a direct link first, then a single-file torrent, then a film buried inside a pack;
  3. then sorts by peers and language coverage, and caps the list.

The detail that mattered: for a film, a file index inside a torrent means it’s buried in a collection, so it gets pushed down. For a series that’s normal (episodes live in season packs), so it must not be.

Web novels: measure before you build

When the comics section’s only provider vanished from DNS, I replaced it with web novels. Instead of guessing which sites to support, I took the 147 English sources from a well-maintained open-source reader’s plugin list and probed every one:

  • 38 answered;
  • 37 sat behind a JavaScript challenge a plain HTTP client can’t pass;
  • 22 had no DNS at all.

That’s why sources are plural. When one host starts challenging, another still has to answer. Three ship today, each verified end to end (search → novel → chapter list → chapter text). A source in the code is an engine for a family of sites sharing the same CMS, so adding a sibling site later is configuration, not code.

The library tracks new chapters itself: each saved novel remembers the chapter count from when you last opened it, and the badge is the difference. The obvious release tracker sits behind a challenge too, so counting ourselves was both simpler and the only thing that worked. Refreshes are batched four at a time, so a forty-novel library doesn’t fire forty requests at three small sites. A session cache keeps going back to the chapter list instant. Without it, one 3,000-chapter series cost 32 requests every time.

Music that never dead-ends

The music section is radio-first: one button starts a mix seeded from artists I like (Japanese R&B and rap, plus anime songs), metadata from a music catalogue API, audio found and streamed separately. When the queue runs out, it refills from the same seeds and then from related artists, so autoplay never stops at the end of a short radio.

A remote is not a keyboard

The TV shaped everything. A keyboard that pops up whenever focus lands on a text field is maddening with a D-pad, so the keyboard now opens only when you press OK on the field. Dialogs let direction keys move between their own buttons but keep focus from escaping. Intro and recap segments come from two community databases, so you can skip them with one press. And when a stream fails, the player says which source failed and moves to the next one, instead of spinning forever.

What I took away

  • With sources you don’t control, design for failure first: several sources, clear fallbacks, nothing compiled in that will rot.
  • Most “integration” bugs are identity bugs. Pick a stable id (IMDb, AniList) and translate at the edges.
  • Measure the landscape before choosing what to support. Probing 147 sites took an afternoon and saved weeks of building on ones that would never work.
  • Say the trade-offs in the UI, in plain words. Users can make good choices when you tell them what they’re choosing.