🗂️ How I Manage My Music Library
In my previous post I mentioned that I had been sorting out my library. This post is about how I actually do that.
It is less a piece of software than a set of rules I decided to be strict about, and then wrote some code to enforce so I could not quietly stop following them. If you would rather poke at the finished thing than read about it, it lives on its own page.
The Problem
A folder full of tracks is not a library.
I noticed this the first time I sat down to actually plan a set rather than just play whatever came up. I had the music. What I did not have was any way to ask a useful question of it. “What do I open with” is not a question a folder can answer. Neither is “what have I got that bridges 128 BPM house into 140 BPM trance”, or “which of these has a vocal I should not put next to another vocal”.
The realization that made the whole thing click is that a track has two halves.
The first half is what the file says it is - title, artist, remixer, label, key, tempo, release year. All of it factual, all of it something a shop or a metadata provider can tell me. This half is a lookup problem.
The second half is what the track does in a set. Whether it opens or peaks. Whether it is euphoric or aggressive. Whether the vocal runs through the whole thing or just carries the breakdown. How hard it hits on a scale of one to five. None of that can be looked up. No provider knows it, no standard tag carries it, and it is the half that actually decides whether I play the track.
Almost everything interesting about this project comes from taking that second half seriously.
The Taxonomy
So every track gets described with the same small vocabulary. Six roles, seven moods, six vocal types, ten features, and an energy level from one to five.
The roles are the ones I reach for first when planning: opener, groover, builder, peaker, closer, transitioner. A track can have more than one - plenty of things work as both a groover and a builder depending on what is around them.
The moods are for pulling a selection together by feel rather than by genre or tempo: happy, euphoric, melancholic, dreamy, hypnotic, aggressive, nostalgic.
The vocal types exist because vocals change how a track lands, and because two full vocals back to back is usually a mistake. There is a real difference between a track with a one-off shouted sample, one with a repeated hook, one where the vocal only carries the breakdown, and one that is structurally a pop song with verses and a chorus.
The features are the single standout thing about a track - acid, piano, orchestra, a massive drop, a chart hit everyone in the room already knows.
And energy is one number from one to five, which sounds crude and turns out to be the most useful field in the whole system, because it is the one that lets me see the shape of a set before I play it.
The Storage
Here is the decision I am most happy with: none of this lives in a database.
It all goes into the audio files themselves, in ID3 frames. The standard stuff goes where it belongs - the label in TPUB, the key in TKEY, the tempo in TBPM. The classification, which has no standard home, goes into custom TXXX frames: ROLES, MOODS, VOCAL_TYPE, FEATURES, ENERGY.
The reason is simple. If the description lives in a database, it lives on one laptop, and it is one corrupted file or one migration away from being gone. If it lives in the file, it travels with the audio. I can copy a track onto a USB stick, hand it to somebody, import it somewhere else in five years, and everything I ever figured out about that track is still attached to it. The local files stay authoritative and everything else is advisory.
There is a related trick for genres. Rather than maintaining a list of every genre folder, the folder name derives its own parent: a folder called Electro House gets tagged House > Electro House, Hard Trance becomes Trance > Hard Trance. Adding a new folder needs no configuration anywhere. Only the names that do not say what family they belong to need an entry - Handsup, Complextro, and a legacy folder just called Progressive.
That was not the original design. It replaced an explicit list, and the reason it replaced it is that six real folders had quietly fallen through the gaps in that list and were sitting there wearing an inherited tag that had nothing to do with their contents. Deriving it meant the gap could not exist.
The Playlists
With everything described consistently, the playlists stop being something I maintain and become something the library computes.
Every generated playlist is a set of rules over the fields above, not a hand-filed list. There is a group per source folder, a group per genre, and one playlist per role, per mood, per vocal type, per feature, and per energy level. Nothing gets filed twice, and a grouping that turns out to be a bad idea can just be switched off rather than unpicked.
The interesting ones are the context playlists, because they are the only generated ones that combine two fields:
- Warm-Up - anything tagged as an opener, or sitting at energy 2 to 3
- Party - anything tagged as a peaker, or at energy 4 and up
- After-Party - anything tagged as a closer, or carrying a dreamy or melancholic mood
- Background - LoFi, or energy 1
“Warm-up” is not a tag. It is a consequence of role and energy, and expressing it as a rule rather than as a label means I never have to remember to apply it.
The decade playlists have their own small subtlety: they are built from the original release year rather than the release year of the file I happen to own, so a 2014 remaster of a 1994 record still lands in the 90s where it belongs.
On top of all of that sit the playlists I build and order by hand, for a specific night or a specific idea. Those are the only ones the rules do not touch.
The Rekordbox Compromise
This is my favourite part, because it is such a perfect example of a clean design meeting reality.
I did all of this work to describe tracks properly. Then I went to get it into Rekordbox, and discovered that Rekordbox has no field for energy, role, mood, vocal type or feature, and does not read the custom TXXX frames they live in. All that structure, and the software I actually perform with cannot see any of it.
There is exactly one column that Rekordbox both displays and searches: Comments.
So the export flattens the whole classification into that one field:
E4 · peaker · euphoric · full vocal · piano
It is not elegant. It is a structured system being squashed into a free-text box because that is the only door available. But it works: I can search it, sort by it, and see at a glance what a track is while I am mid-set. The lesson I keep relearning is that the last mile decides the data model, and no amount of correctness upstream helps if the thing you actually use cannot read it.
The export writes a full Rekordbox XML with the tags, the tempo, the key and the entire playlist tree, plus Serato crates as a secondary target. A flag in the interface tells me when the exports are stale, because changing a tag does not touch them.
The Providers
For the first half of a track - the factual half - I lean on external services. The thing that took me longest to understand is that providers are not competing answers, they are partial ones.
Deezer knows the ISRC. iTunes knows the original release year. MusicBrainz knows the canonical release. Bandcamp carries the bootleg nobody else has. Discogs is the only one that knows the record label and catalogue number a shop is actually searched by, and the only one that has heard of white labels and the old Hardbass compilations.
So a search result is not “the first provider that answered”. It is all of them merged into one recording, with a link for each service rather than only the winner. Two rules turned out to matter: versions stay apart, because “Adagio For Strings” and “Adagio For Strings (Blasterjaxx Remix)” are genuinely different records; and spelling does not, because iTunes writes the featuring credit into the title and Deezer does not.
The other half of that story is what you cannot connect, and being honest about it. When I last checked their documentation, in August 2026: MusicBrainz, Deezer and iTunes need no account at all. Spotify works with a free app registration, though a development-mode app requires the owner to have Premium. Discogs needs a personal access token pasted in. And Beatport’s current API is partner-approval only, Deezer closed library-sync registration to individuals in October 2025, YouTube Music has no official API, and Apple Music’s library needs a paid developer membership. Those four simply cannot be connected, so I name them and say why rather than pretending otherwise.
The Hot Cues
One rule that involves no software at all, but matters just as much, is a consistent hot-cue mapping. This one lives entirely in Rekordbox, and it is pure habit.
The whole point is muscle memory. Mid-set I do not want to be reading a waveform, I want to already know what each pad does:
- A - Intro - where the intro starts, the very first beat of the track
- B - Theme - where the main idea or the vocal comes in
- C - Breakdown - where the energy pulls back
- D - Drop - the moment it hits
- E - Reserved - anything worth coming back to, a signature sound or a re-intro
- F - Last Breakdown - the last pull-back before the final push
- G - Last Drop - the peak of the track
- H - Outro - where the actual outro starts
Every track is cued the same way, so loading anything means I already know its structure before it plays. E is the only one that floats, because it is whatever that particular track has that is worth reusing.
The Enforcement
None of the above needs software. It is a set of rules, and for a while I followed them by hand.
The problem with following rules by hand is that you stop. Not deliberately - you just import forty tracks at two in the morning and tell yourself you will tag them tomorrow, and eventually a third of the library is undescribed and the whole thing stops being trustworthy. A system that only works when I am disciplined is not a system.
So I wrote something to enforce it. Deliberately small: Bun and TypeScript compiled to a single standalone executable, a React interface served by that same binary over the loopback address, and a desktop window around it. No hosted service, no external database, nothing leaving the machine except the metadata lookups I explicitly ask for.
The rules it protects are the ones I would otherwise break:
- Every destructive operation is a dry run by default, and shows me what it would change before it changes it.
- Deduplication compares decoded audio rather than filenames, and parks suspected duplicates in a holding folder. It never deletes audio.
- Audio conversion re-applies every tag itself rather than trusting the encoder, because encoders only carry the fields they model generically - which would have silently dropped exactly the custom tags this whole system depends on.
- A flag tells me when Rekordbox and Serato are still reading a previous state, because changing a tag does not update an export.
That last one is a small thing that took me embarrassingly long to add, and it is probably the single feature that saves me the most confusion.
The Current Status
It works, I use it every time I sit down, and it is not finished.
The biggest gap is that nothing in it ever listens to the audio. Key and tempo come from file tags or from a provider, and energy is a number I type in myself. Real audio analysis - proper beatgrids, detected key, a computed energy curve - is the obvious next step and easily the largest piece of work left.
I do intend to put the code out publicly at some point. Before that I want the analysis in and a few rough edges filed down, so there is nothing to point you at yet. When there is, it will show up here.
The End
What I did not expect going in was how much of this would turn out to be about the second half of a track - the part no database has, that you only get by listening and deciding - and how much of the work would be in getting that part to survive the trip into software that was never designed to hold it.
The full system is on its own page if you want to browse it, and the genre map it sits alongside is here.
I hope you enjoyed this blog post and I will see you in the next one!