Folio Red
March 11th, 2024

Alto

Date: 2020 โ€” present

Status: Rockin'

The year was 2020. The pandemic was just beginning and I was stuck at home, not being able to do much of anything. Worse, rumours came around that Google was shutting down Google Play Music, my music player of choice. They were going to force everyone onto their streaming service instead. Oh, they may have a place for all the music you've downloaded (or written) yourself, but not in the first version. Maybe they'll get to it later.

"Well, fuck that", I said to myself. "I've been battered around by Google shutting down things and forcing migrations onto other things one to many times. I'm going build my own music app."

And so I built my own music app: Alto.

Why "Alto"? Well, the name goes back to when I was in secondary school, when I was learning the viola, which uses the alto clef for it's written music. I said to myself at the time that if I were ever to build a music player, it'll have something referencing the viola. The alto clef seemed like the best thing to use. Plus, it stands out amongst the other music apps that tend to use other notation symbols like notes or the treble clef.

The Alto logo, taken mainly from the clef itself.
The Alto logo, taken mainly from the clef itself.

The idea for Alto was pretty straight forward: a music player and catalogue that'll manage and stream music from an S3 bucket. No tracking, no "promotions" or "recommendations", no bullshit UI that's impossible to navigate. Only the music I'm interested in, played the way I want, and a dead simple UI that puts the album front and centre. The music player that was meant for me.

The Web Catalogue

The ultimate version that would come to be would consist of two parts: an Android mobile app, and a web-app. I'll talk about the mobile app in the next post.

The web-app can be used as a player, but is ultimately be responsible for managing the collection.

The main album list.
The main album list.

The web-app was built using Buffalo, a rapid web development much like Rails for Go. It's basically a simple server-side rendered web-app. The frontend consists mainly of Bootstrap plus some Stimulus and vanilla JavaScript to handle the interactive elements.
It's also using Turbo to prevent unloading the current page when moving to a new one. This means clicking around the site will not stop playback of the current track, a very nice feature (and doubly so when you consider that this isn't a single-page app).

Much of the UI is dedicated to managing the catalogue but there is also an integrated player, which can be invoked by clicking the play button. The player itself can be bought up at any time by pressing "P". There's no scrubber but there are seek by 30 second buttons which do the job. Each of the controls in this player have associated shortcut keys that are always available, even with the player hidden.

Clicking the play button in a track list will start playing it.
Clicking the play button in a track list will start playing it.

The collection is managed within a PostgreSQL database and referenced files stored on an S3 bucket. S3 was chosen to ensure that if I were ever to stop work on this project, or the database were to be corrupted, I wouldn't loose my music. It does mean that I've got an ongoing cost for running this service, but based on the amount of music I'm keeping and my music listening patterns, the monthly bill for S3 is around 50ยข-60ยข AUD, plus $12.00 US for the web-app server.


The catalogue model was made as simple as possible. The main construct is the Album, which would consist of zero or more Tracks. Albums had things like title, artist, cover images, etc. but these are nothing more than just properties of an album.

Tracks could be added one at a time via the frontend, or uploaded from a Zip file pulled from a URL (useful for songs bought on Bandcamp). The catalogue tries it's best to avoid uploading media via the web-server, either opting to pull it in from the backend or upload it to S3 directly. This was a deliberate choice to reduce the amount of network it uses, but it does mean going through some strange loops. For example, when uploading a single track via the frontend, it would upload it directly to S3, then download it from S3 on the backend so that it can set catalogue metadata from the file itself (ID3 tags, MP3 length, etc). This is pretty convoluted and doesn't even work half the time, and if I were making this again, I'd probably just bite the bullet and allow large uploads via the frontend.

Media


The media model is a little more complicated. Media โ€” audio files, cover images, etc โ€” all belonged to a Repository, which is essentially a reference to an S3 bucket, although it could also reference things like a HTTP domain. A repository does have some other configuration such as how to name the uploaded media files. I could've used something like a UUID, but I wanted to keep the names human readable as much as I could, so that if this project were to shutdown, I would still be able to access the files from S3 myself.

Listing all the media managed by the catalogue.
Listing all the media managed by the catalogue.

An Album or Track is linked to a Media record through what's called a Media Reference. Each of these references has a "rel" property (short for "relevance") which describes what the media is for: an audio file for a track, a cover image for an album, etc. There also exists a classifier which was to allow an Album of Track to use multiple Media records of a particular relevance. For example, some albums, released in different regions, had different album covers, with one being slightly darker than the other. The classifier could be use to switch between the two, based on whether Dark Mode was enabled. At least that was the theory: it never really got used for that.

Governing the link between albums, tracks and media was a simple resolution algorithm that supported things like inheritance. For example, tracks could have their own album cover, but if one didn't exist, they would inherit it from the album. This went beyond just album covers: it would be possible, theoretically at least, to have the audio associated with the album and have the tracks reference a different cover image (I never tried this).

Finally, there are Playlists. These are pretty standard: just a collection of references to other tracks, plus some ordering information. Playlists and playlist items are essentially links and cannot have Media References themselves. There are some downsides to this: the biggest one being that Playlists do not have album cover art, which is something I'll need to fix. Playlists can also have metadata items.

Playlists are out of the way from albums, and the UI for them is a little bit underbaked.
Playlists are out of the way from albums, and the UI for them is a little bit underbaked.

Speaking of metadata items.

Metadata Items


A number of objects can also have metadata, which could be use to attach extra attributes. The goal was to make this generic enough for end users to use it for whatever, with Alto having a few predefined names it uses for it's own purpose.

There were actually two kinds of metadata record. The first was an arbitrary JSON structure that can be set for each Track.ย  Only one such name was reserved, called heading, which was used to define track groups within albums. I do have plans for adding more attributes, such as things dedicated to Eurovision tracks (year and country for example).