feat: genre mixes pick specific genres that don't share songs (#6933)
release / go (push) Successful in 2m0s
release / web (push) Successful in 1m33s
release / govulncheck (push) Successful in 18s
release / integration (push) Successful in 5m11s
release / android (push) Successful in 4m14s
release / Build signed APK (push) Successful in 4m38s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 29s
release / Verify release artifacts (tag releases only) (push) Skipped
release / go (push) Successful in 2m0s
release / web (push) Successful in 1m33s
release / govulncheck (push) Successful in 18s
release / integration (push) Successful in 5m11s
release / android (push) Successful in 4m14s
release / Build signed APK (push) Successful in 4m38s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 29s
release / Verify release artifacts (tag releases only) (push) Skipped
The shelf had four mixes built largely from the same lo-fi beats: about 1,250 tracks carry Hip Hop, Lo-Fi, Instrumental, Downtempo and four more tags at once, and the near-duplicate guard compared artists, not songs, so each tag passed as its own channel. Genre selection now works on songs: - ListGenreMixCandidates returns each genre's track ids instead of its artist ids. - A broad genre, one whose songs mostly also carry clearly narrower candidates (Rock over Pop Punk and Ska Punk), is considered after the specific genres of its tier. - A genre is kept only when at least half its songs are not already in a kept genre. - Each mix leaves out songs tagged with a genre kept before it (ListGenreMixTracks exclude_genres), so a song is in one mix at most. The tiers, the minimum shelf and the minimum mix length are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,14 +14,16 @@
|
||||
-- has decayed past its window
|
||||
-- tier 3: any other library genre, scored by its size
|
||||
--
|
||||
-- artist_ids lets the caller skip a genre whose artists mostly repeat one
|
||||
-- already picked (Indie Rock after Alternative Rock).
|
||||
-- track_ids lets the caller tell how much music two genres share: a genre
|
||||
-- whose songs mostly sit in genres already picked adds no new channel, and a
|
||||
-- broad genre ("Rock") whose songs mostly carry narrower ones ("Pop Punk",
|
||||
-- "Ska Punk") gives way to them.
|
||||
-- $1 user_id.
|
||||
WITH library_genres AS (
|
||||
SELECT lower(trim(g.g)) AS genre_key,
|
||||
min(trim(g.g))::text AS label,
|
||||
COUNT(DISTINCT t.id) AS track_count,
|
||||
array_agg(DISTINCT t.artist_id)::uuid[] AS artist_ids
|
||||
array_agg(DISTINCT t.id)::uuid[] AS track_ids
|
||||
FROM tracks t
|
||||
CROSS JOIN LATERAL regexp_split_to_table(coalesce(t.genre, ''), '[;,]') AS g(g)
|
||||
WHERE t.missing_since IS NULL
|
||||
@@ -63,7 +65,7 @@ SELECT lg.genre_key::text AS genre_key,
|
||||
(CASE WHEN ta.genre_key IS NOT NULL THEN ta.weight
|
||||
WHEN h.genre_key IS NOT NULL THEN h.plays::float8
|
||||
ELSE lg.track_count::float8 END)::float8 AS score,
|
||||
lg.artist_ids
|
||||
lg.track_ids
|
||||
FROM library_genres lg
|
||||
LEFT JOIN taste ta ON ta.genre_key = lg.genre_key
|
||||
LEFT JOIN history h ON h.genre_key = lg.genre_key
|
||||
@@ -96,6 +98,9 @@ SELECT lg.genre_key::text AS genre_key,
|
||||
-- A track the user keeps skipping stays out: twice-skipped and skipped more
|
||||
-- often than played through. The second half of that test keeps a favourite
|
||||
-- with a couple of stray skips in.
|
||||
--
|
||||
-- exclude_genres keeps each song in one mix: a track carrying a genre that
|
||||
-- already has a mix today is left to that mix.
|
||||
WITH play_counts AS (
|
||||
SELECT pe.track_id, COUNT(*) AS c
|
||||
FROM play_events pe
|
||||
@@ -166,6 +171,11 @@ eligible AS (
|
||||
FROM regexp_split_to_table(coalesce(t.genre, ''), '[;,]') AS g(g)
|
||||
WHERE lower(trim(g.g)) = sqlc.arg(genre_key)::text
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM regexp_split_to_table(coalesce(t.genre, ''), '[;,]') AS g(g)
|
||||
WHERE lower(trim(g.g)) = ANY(sqlc.arg(exclude_genres)::text[])
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM quarantine q
|
||||
WHERE q.user_id = sqlc.arg(user_id) AND q.track_id = t.id
|
||||
|
||||
Reference in New Issue
Block a user