Personal project · Work in progress
Jellyfin station
What if a personal media library felt like a television station you could just switch on?
Jellyfin is a free, open-source media server for organising and streaming your own films, television and music. You run it on your own server and watch through a browser or a compatible app.
Why build a television station?
I built Jellyfin station around a familiar problem: having plenty to watch and still spending time deciding what to put on. A personal library makes films and television convenient to keep and find. It also leaves every evening starting with a catalogue.
There is something appealing about simply turning on the television, discovering what is showing and settling into it. A schedule makes room for things you might not have deliberately chosen. I wanted to see whether I could bring some of that serendipity to the media already in Jellyfin.
A television station without a broadcast
One way to recreate television is to generate a continuous stream for each channel. That can be useful, but a dedicated FFmpeg pipeline and another layer of streaming infrastructure felt like more machinery than this project needed. Jellyfin already had a library, a media server and players.
I chose to make the station a persistent schedule. Its clock advances whether anybody is watching or not. When someone tunes in, the application works out what should be showing and asks Jellyfin to start it at the corresponding position.
If an episode starts at 20:00 and I join at 20:17, the request is to begin roughly seventeen minutes into it. The station has been carrying on in my absence, without needing to generate a separate broadcast stream.
Jellyfin still delivers the media and handles any transcoding it needs. This keeps the extra application focused on scheduling and coordination, though it depends on the receiving client supporting remote playback.
Rules instead of playlists
The weekly template gives the station its character. I can put a series on particular weekdays, choose how many episodes should run together, or leave an evening to films from a chosen genre. Episodes follow their series order, with progression remembered between scheduled broadcasts. Film selection adds some randomness and prefers titles that have not been scheduled recently.
The times are guides rather than boxes that every programme has to fit inside. The scheduler uses the actual runtimes from Jellyfin. If a film beginning at 20:00 runs until 21:43, a programme preferred for 21:30 follows at 21:43. The guide accommodates the film’s length instead of shortening it to meet the next slot.
That flexibility lets the schedule take shape around the programmes. I can still choose a particular item or replace something in the published guide, with subsequent airtimes adjusted to follow it. The guide and episode progression live in the database, so closing the application does not mean starting the station’s programme history again.
A small layer around what already exists
Python holds the scheduling and coordination logic, while Flask provides the browser interface for programming the station and controlling playback. SQLite keeps the library metadata, recurring rules, published timeline, episode progression and relevant session state together. It suits a small, self-hosted application without adding a separate database service to look after.
Jellyfin’s APIs supply the catalogue, runtimes, artwork and connected playback sessions. A background controller follows programme boundaries and sends playback commands as the scheduled content changes. The application’s job is to connect those pieces; Jellyfin remains the media server.
- The libraryJellyfin’s API supplies the catalogue and programme metadata.
- The schedulePython and Flask build a published timeline in SQLite.
- The playerThe controller uses Jellyfin’s API to coordinate an existing playback client.
Media travels directly from Jellyfin to the player. It is not streamed through Flask.
Asking is not the same as playing
The interesting engineering problem is that the player sits outside the application’s control. A command can be accepted without the expected programme actually starting. The schedule says what ought to be happening; the client’s reported state is the evidence of what may be happening.
The controller keeps track of transitions and checks the playback state reported by Jellyfin. It needs to allow time for a change to take effect, make a bounded attempt to recover when it does not, and leave a programme alone once the transition has been confirmed. Repeatedly issuing commands would make the experience worse rather than more reliable.
That boundary is an important part of the design. The application can coordinate and verify against the information it receives, but it cannot assume every player behaves identically or that an API response tells the whole story.
Something to switch on
Jellyfin station is a personal, self-hosted project, with private source. It has demonstrated the core idea: generate a schedule from an existing library and use it to coordinate playback on compatible Jellyfin clients. It remains a work in progress, and client capabilities still shape what is possible.
What interests me is the change in experience that a relatively small piece of software can create. The aim is to make a familiar library feel less like another catalogue to browse and more like a television station I can simply switch on.