WeLC

The Media Library interface

During the summer of 2026, I wrote a simple music player to allow us to play music from our home server anywhere. On our home network this is trivial; the home server has a Samba share that lets us mount it and play files directly. But on other networks, it's more restricted. My app WeLC (a reference to VLC, the well-known media player) exposes music via a web interface, providing both a playlist that can be sorted and shuffled and a media library that displays a directory of music as a tree, allowing tracks or whole albums / folders to be enqueued. (This presupposes your music is sorted, which ours partly is, into directories by artist and album, with the filename containing the track number. We use MusicBrainz Picard to identify, sort, and rename our files.)

The app architecture is straightforward: a Flask backend provides an API for the media library, retrieving matching records from the database (Postgresql) and inserting / deleting records upon request. Queries are written using SQLAlchemy, a popular but complex ORM. The frontend is writted in typed React, using react-aria-components for accessible, easy-to-style components in the tabs, media library search bar, and media library results display. RAC is more suited to an in-house design library than this sort of one-off usage, but I wanted the complexity of the table/tree display for the media library, and this was an option I was familiar with from a past job. Otherwise it is vanilla HTML and styles via CSS modules. Vite is used as a build tool, as it is apparently the modern successor to create-react-app.

The Playlist tab

Tests for this project have grown in stages. First came some basic e2e tests using Cypress, a browser automation tool. This required setting up a test database and .env file, and running the backend and frontend so that they pick up those environment variables. Currently there are tests for each tab in isolation and then one checking cross-tab interactions. When I got more serious about error handling in the API, that needed some tests as well. Most recently I migrated some tree-building code from Python to TypeScript to allow client-side filtering in the media library, which required also migrating the unit tests.

A brief note on security: because this project works extensively with filepaths, including user-input paths, I tried to pay some attention to making sure malicious input is foiled. Specifically, I require the app to have a root music directory specified as an environment variable, throwing errors from the API if the user tries to input a path that resolves outside of that directory. The pathlib module is very helpful here. I also followed advice for serving files securely, using Flask's send_from_directory function. Access to the web interface is gated via a Tailscale network, instead of having a traditional login system.

The audio player itself

Currently the app is running on our home server, using a setup detailed in this blog post. Next steps for this project include:

  • adding a PWA manifest, so the page can be installed as a shortcut;
  • potentially including routing so the user can jump straight to the media library tab (where they typically want to start);
  • maybe adding a History tab to show what's been played and easily re-enqueue files;
  • changing the file fetch mechanism to eliminate any delays between tracks (currently there is some latency as each track is fetched over the network), either to a streaming or a pre-fetch model. This might make it possible to crossfade tracks.

My goal is not to re-write VLC, but practice designing an API for a well-defined problem, so I'm not anticipating going much further on this.