Andromeda: a public TV channel with a schedule and a chat
A livestream page for my media library. Anyone with the link can see what's on, what's next, and chat about it. The whole thing runs in one container.
published
length1 min read
ErsatzTV takes a media library and turns it into a TV channel. It picks what plays next and just keeps going, so there's always something on. I liked that a lot, and I wanted other people to be able to watch it with me. But a raw stream is missing two things you need for that: a schedule, so you know if it's worth sticking around, and a chat, so you can talk about what's on.
Andromeda adds both. When you open the page, the channel is already playing. Under the player you can see what's on now and what's coming up, and the chat sits right next to it.
Everything runs in one process
The whole app is one Node process on one origin:
/ the page
/api/chat/* chat, with a server-sent-events stream for live messages
/api/schedule the schedule, normalized
/iptv/* a reverse proxy to ErsatzTVHonestly, that layout is most of why I built this. The player, schedule, chat, and stream proxy all fit in one container, so deploying it is a single compose service, and if you want to know how requests get routed, you can just read one file.
The schedule comes from the Jellyfin library that feeds ErsatzTV, and the server cleans it up before sending it to the browser. The IPTV proxy rewrites the playlist as it passes through, so the stream still works no matter what public hostname it's behind.
The chat doesn't stick around
Accounts are just a nickname and a password. Messages max out at 500 characters, and the server only keeps the last 100. That's intentional. TV doesn't have scrollback, so I didn't think the chat under it should either.
The frontend is React, TypeScript, Vite, and Tailwind. The backend is a small Express app with SQLite and its own tests. I use Bun for development and Node 24 in production. It's been running for months now, and people I know drop in when something good is on the schedule.
