§01 context
We carried a projector to a friend's place for movie night and nobody had the remote. No IR codes existed for it anywhere, so we plugged in a laptop and drove the menus over ADB, one input keyevent at a time. We never watched the movie. I did get an idea I couldn't drop.
It's now a Go binary that boots with the projector, serves a PWA remote, injects input straight into the kernel, and plays movies, shows and anime in its own player app. I reverse engineered the netmirror app for the catalog and wrote adapters for anime providers. Tailscale means the remote works from any of my devices.
§02 shape
How it’s put together
phone / laptop (PWA)
│ HTTP + WS over LAN / Tailscale
▼
Go server, running ON the projector
├─ serves the embedded React build (go:embed)
├─ /ws JSON message protocol for the remote
├─ /api/catalog NetMirror + anime catalog, normalized
├─ deviceio local command execution
└─ playback sessions, resume, next episode
│ │ WS /player (localhost)
▼ ▼
kernel input (uinput) player app (ExoPlayer)
+ input text / am HLS / MP4 + subtitles§03 hard parts
The hard parts
- 01
Making it survive a reboot
ADB over TCP turned itself off on every boot because of a vendor init script. Fixing it meant understanding the boot chain and dynamic partitions, then patching one file inside a mounted, read-only partition without bricking the thing.
- 02
The backdoor I wasn't looking for
Digging through the boot scripts turned up a live OEM backdoor. I shut it down before I trusted the device on my home network again. That part is in the write-up, minus anything that identifies the unit.
- 03
Input that doesn't lag
Shelling out to input keyevent per press is slow. The server writes to uinput and the pointer node directly, and only falls back to input and am for text entry and app launches.
- 04
A catalog that wasn't meant to be used like this
Movies and shows come from the NetMirror API, which I worked out by reverse engineering their app. Anime uses AniList for metadata and an ordered list of provider adapters, so when one source dies the next one takes over.
stack
- Go
- WebSockets
- React PWA
- Android
- Media3 ExoPlayer
- Tailscale
next →
WhatsApp service
A plug-and-play WhatsApp Cloud API service any multi-tenant CRM can sit in front of.