feat: rebuild the moods from real tags, add exclusions, fix playlist ownership #12

Merged
lyrathorpe merged 3 commits from feat/moods-from-real-tags into main 2026-08-24 18:48:44 +01:00
Owner

Measured, then written

The first set of moods was guessed at. A tag census of the real library shows
how badly.

Dead weight I shipped. edm, big room, hardstyle and electronic dance music carry nothing at all — they do not appear in the top 150 tags. Meanwhile
techno (1,828), electro (1,891), big beat (1,379), rave (1,334) and
progressive house (1,207) carry real weight and were all absent from my list.

Spellings missed. The drum and bass mood had four of the eight spellings the
taggers actually use, and missed techstep, darkstep, drumstep and
hospital records entirely.

The eighties mood was the worst of it. It leaned on the bare 80s tag —
eleventh most-played here at 8,188 plays — which in this library sits on Def
Leppard, Bon Jovi and AC/DC, not on synthesisers. Dropping it and adding
hair-metal (glam rock, hair metal, glam metal, arena rock, AOR, windowed
1975-1994) puts ~8,500 plays somewhere sensible and stops the synth playlist
being a stadium rock playlist.

Whole clusters had no mood. Metal is ~25,000 plays across heavy, thrash,
speed, power, death and NWOBHM. Pop punk and emo is similar. Neither existed.

The set now

Mood Selected on
drum-and-bass all six spellings, liquid funk, neurofunk, jungle, techstep, hospital records
bass dubstep, brostep, grime, trip-hop, big beat
dance house variants, trance, techno, electro, rave
pop-punk pop punk, punk, emo, emocore, easycore, power pop
screamo screamo, post-hardcore, metalcore, melodic hardcore, trancecore
heavy-metal heavy metal, thrash, speed, power, death, prog, NWOBHM
hair-metal hair metal, glam metal, glam rock, arena rock, AOR — 1975-1994
nu-metal nu metal, alternative metal, rapcore, industrial
classic-rock classic rock, prog, psychedelic, blues rock, 70s, 60s
80s-synths synthpop, electropop, new wave, post-punk — 1975-1992
indie indie, indie rock, indie pop, britpop, singer-songwriter

Three tags never used, with a test enforcing it

Each produces a playlist that looks reasonable and is not.

  • Nationality. american spans 241 artists, british 202. A passport is
    not a sound.
  • rock and electronic. 340 and 275 artists — most of the library. A
    mood that matches everything is not a mood.
  • Artist names. Last.fm's most popular tag for an artist is frequently the
    artist's own name: green day at 1,711 plays, paramore at 711, queen at
    523. Selecting on those yields a single-artist playlist wearing a genre's
    clothes.

Testing

101 tests, green locally and in docker build --target test. Three new ones
pin the exclusions, the uniqueness of mood names, and that 80s-synths does
not lean on the bare decade tag.


Also folded in: playlist ownership

Playlists were being written owned by root. The image runs as root by
default, deliberately, so a bind-mounted dataset of any ownership stays
writable -- but everything it writes then comes out root-owned, and a root-owned
playlist inside a mirror owned by the apps account is unreadable to whatever
serves it. The group-read bit does not help when the group is also root.

Each playlist now takes the owner and group of the mirror it is written
into
. No configuration, cannot drift from what the mirror actually is, and a
no-op when the two already agree -- which is the case whenever the container is
run with an explicit user:.

Applied to the temporary file before the rename, so a playlist is never briefly
visible owned by the wrong account, and to _playlists/ on the run that creates
it. A refused chown is ignored rather than fatal: that only happens when not
running as root, which is exactly when the ownership was already correct.

Kept in this PR rather than raised separately, to save a container rebuild.

Testing

107 tests, green locally and in docker build --target test.

## Measured, then written The first set of moods was guessed at. A tag census of the real library shows how badly. **Dead weight I shipped.** `edm`, `big room`, `hardstyle` and `electronic dance music` carry *nothing at all* — they do not appear in the top 150 tags. Meanwhile `techno` (1,828), `electro` (1,891), `big beat` (1,379), `rave` (1,334) and `progressive house` (1,207) carry real weight and were all absent from my list. **Spellings missed.** The drum and bass mood had four of the eight spellings the taggers actually use, and missed `techstep`, `darkstep`, `drumstep` and `hospital records` entirely. **The eighties mood was the worst of it.** It leaned on the bare `80s` tag — eleventh most-played here at 8,188 plays — which in this library sits on Def Leppard, Bon Jovi and AC/DC, not on synthesisers. Dropping it and adding `hair-metal` (glam rock, hair metal, glam metal, arena rock, AOR, windowed 1975-1994) puts ~8,500 plays somewhere sensible and stops the synth playlist being a stadium rock playlist. **Whole clusters had no mood.** Metal is ~25,000 plays across heavy, thrash, speed, power, death and NWOBHM. Pop punk and emo is similar. Neither existed. ## The set now | Mood | Selected on | | --------------- | ------------------------------------------------------- | | `drum-and-bass` | all six spellings, liquid funk, neurofunk, jungle, techstep, hospital records | | `bass` | dubstep, brostep, grime, trip-hop, big beat | | `dance` | house variants, trance, techno, electro, rave | | `pop-punk` | pop punk, punk, emo, emocore, easycore, power pop | | `screamo` | screamo, post-hardcore, metalcore, melodic hardcore, trancecore | | `heavy-metal` | heavy metal, thrash, speed, power, death, prog, NWOBHM | | `hair-metal` | hair metal, glam metal, glam rock, arena rock, AOR — 1975-1994 | | `nu-metal` | nu metal, alternative metal, rapcore, industrial | | `classic-rock` | classic rock, prog, psychedelic, blues rock, 70s, 60s | | `80s-synths` | synthpop, electropop, new wave, post-punk — 1975-1992 | | `indie` | indie, indie rock, indie pop, britpop, singer-songwriter | ## Three tags never used, with a test enforcing it Each produces a playlist that looks reasonable and is not. - **Nationality.** `american` spans 241 artists, `british` 202. A passport is not a sound. - **`rock` and `electronic`.** 340 and 275 artists — most of the library. A mood that matches everything is not a mood. - **Artist names.** Last.fm's most popular tag for an artist is frequently the artist's own name: `green day` at 1,711 plays, `paramore` at 711, `queen` at 523. Selecting on those yields a single-artist playlist wearing a genre's clothes. ## Testing 101 tests, green locally and in `docker build --target test`. Three new ones pin the exclusions, the uniqueness of mood names, and that `80s-synths` does not lean on the bare decade tag. --- ## Also folded in: playlist ownership Playlists were being written **owned by root**. The image runs as root by default, deliberately, so a bind-mounted dataset of any ownership stays writable -- but everything it writes then comes out root-owned, and a root-owned playlist inside a mirror owned by the apps account is unreadable to whatever serves it. The group-read bit does not help when the group is also root. Each playlist now takes the **owner and group of the mirror it is written into**. No configuration, cannot drift from what the mirror actually is, and a no-op when the two already agree -- which is the case whenever the container is run with an explicit `user:`. Applied to the temporary file before the rename, so a playlist is never briefly visible owned by the wrong account, and to `_playlists/` on the run that creates it. A refused chown is ignored rather than fatal: that only happens when not running as root, which is exactly when the ownership was already correct. Kept in this PR rather than raised separately, to save a container rebuild. ## Testing 107 tests, green locally and in `docker build --target test`.
lyrathorpe added 1 commit 2026-08-24 18:35:16 +01:00
feat: rebuild the moods from the library's measured tag distribution
Build and publish container / build (pull_request) Successful in 2m40s
41f6b290d8
The first set of moods was guessed at, and a tag census of the real library
shows how badly. Four tags in the dance mood -- edm, big room, hardstyle,
electronic dance music -- carry nothing whatsoever, while techno, electro, big
beat, rave and progressive house together carry over seven thousand plays and
were all absent. The drum and bass mood missed four of the six spellings the
taggers actually use, plus techstep, darkstep and hospital records.

The eighties mood was the worst of it. It leaned on the bare `80s` tag, which is
the eleventh most-played tag here and sits on Def Leppard, Bon Jovi and AC/DC
rather than on anything with a synthesiser in it. Removing it and adding a
hair-metal mood -- glam rock, hair metal, glam metal, arena rock, AOR, windowed
to 1975-1994 -- puts roughly eight thousand plays somewhere sensible and stops
the synth playlist being a stadium rock playlist.

Whole clusters had no mood at all: metal at around twenty-five thousand plays
across heavy, thrash, speed, power, death and NWOBHM, and pop punk and emo at a
similar figure. Both now exist, along with bass, nu-metal and indie.

Three kinds of tag are excluded on principle, with a test enforcing it, because
each produces a playlist that looks reasonable and is not. Nationality describes
a passport rather than a sound, and `american` alone spans 241 artists. `rock`
and `electronic` span 340 and 275, which is most of the collection. And
Last.fm's most popular tag for an artist is frequently the artist's own name, so
selecting on `green day` yields a Green Day playlist wearing a genre's clothes.
lyrathorpe added 1 commit 2026-08-24 18:40:22 +01:00
feat: let a mood exclude tags, and widen the eighties one to the canon
Build and publish container / build (pull_request) Successful in 2m52s
8c4da6e14e
The eighties mood selected on synth-specific tags only, on the grounds that the
bare `80s` tag drags in Def Leppard and Bon Jovi. Checking the canon against
live Last.fm pages shows what that costs. Eurythmics is tagged 80s, new wave,
pop, female vocalists, synth pop -- four of those five are tags no mood may use.
Frankie Goes to Hollywood is tagged 80s, new wave, pop, british, dance, and
survives only on new wave. An act tagged purely 80s, pop and dance, which is a
great deal of eighties pop, was missed outright.

Two spellings also matter more than the census suggested. Of Depeche Mode, Duran
Duran, Eurythmics and Frankie Goes to Hollywood, three carry "synth pop" with a
space and only one carries "synthpop" without. The census could not show this
because it only sees artists already in the library; the spelling that was kept
was the rarer one.

Add an `exclude` list to a mood. An excluded tag drops the artist outright
rather than docking their score, which is the only construction that expresses
"the eighties, but not the stadium rock" -- weighting cannot do it, because the
tag it would weight is the one both share. The synth acts do not carry hard rock
or hair metal, and that is the whole of the difference.

A mood may not both select on and exclude the same tag; that is rejected when
the definitions are read rather than silently producing nothing.
lyrathorpe added 1 commit 2026-08-24 18:45:49 +01:00
fix: give playlists the mirror's ownership rather than root's
Build and publish container / build (pull_request) Successful in 2m40s
a7d16ca0b2
Playlists were being written owned by root. The image runs as root by default,
deliberately, so that a bind-mounted dataset of any ownership stays writable --
but everything it writes then comes out root-owned, and a root-owned playlist
inside a mirror owned by the apps account is unreadable to whatever serves it.
The group-read bit does not help when the group is also root.

Copy the ownership of the mirror the playlist is being written into. That needs
no configuration, cannot drift from whatever the mirror actually is, and does
nothing at all when the two already agree -- which is the case whenever the
container is run with an explicit user.

Applied to the temporary file before the rename, so a playlist is never briefly
visible owned by the wrong account, and to the _playlists directory when this is
the run that creates it. A chown that is refused is ignored rather than fatal:
that only happens when not running as root, which is precisely the case where
the ownership was already right.
lyrathorpe changed title from feat: rebuild the moods from the library's measured tag distribution to feat: rebuild the moods from real tags, add exclusions, fix playlist ownership 2026-08-24 18:46:06 +01:00
lyrathorpe merged commit 0ae2630a79 into main 2026-08-24 18:48:44 +01:00
lyrathorpe deleted branch feat/moods-from-real-tags 2026-08-24 18:48:44 +01:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: lyrathorpe/music-curator#12