music: Last.fm-style stats with 7d/30d/1y/all ranges and genres
All checks were successful
Deploy production / deploy (push) Successful in 1m14s
All checks were successful
Deploy production / deploy (push) Successful in 1m14s
- Range picker (?range=7d|30d|1y|all) drives every section; the hours chart covers the whole range (daily for 7d/30d, weekly for 1y, monthly for all). - Genres from Last.fm artist top tags (music.artist_tags, normalised and filtered), split by artist listening time, with a per-bucket stacked chart. - Listening clock, new artists, comparison with the previous period and top movers, streaks, milestones, top albums with art, rank changes. - music.play_facts materialized view (refreshed by the app when plays arrive) plus a per-range cache keep the page fast. - scripts/music-fetch-tags.ts backfills tags, most-played artists first.
This commit is contained in:
@@ -190,3 +190,28 @@ export const musicImportState = musicSchema.table('import_state', {
|
||||
data: jsonb('data').notNull(),
|
||||
updatedAt: timestamptz('updated_at').notNull().defaultNow()
|
||||
});
|
||||
|
||||
/**
|
||||
* Genres: Last.fm artist.getTopTags per artist, normalised and filtered for junk tags
|
||||
* (scripts/music-fetch-tags.ts). Weight is Last.fm's tag count (100 = the artist's top tag).
|
||||
*/
|
||||
export const musicArtistTags = musicSchema.table(
|
||||
'artist_tags',
|
||||
{
|
||||
artistId: integer('artist_id')
|
||||
.notNull()
|
||||
.references(() => musicArtists.id, { onDelete: 'cascade' }),
|
||||
tag: text('tag').notNull(),
|
||||
weight: smallint('weight').notNull()
|
||||
},
|
||||
(t) => [primaryKey({ columns: [t.artistId, t.tag] }), index('artist_tags_tag_idx').on(t.tag)]
|
||||
);
|
||||
|
||||
/** When tags were last fetched per artist: 'ok', 'no_tags' or 'not_found' (retry later). */
|
||||
export const musicArtistTagFetch = musicSchema.table('artist_tag_fetch', {
|
||||
artistId: integer('artist_id')
|
||||
.primaryKey()
|
||||
.references(() => musicArtists.id, { onDelete: 'cascade' }),
|
||||
status: text('status').notNull(),
|
||||
fetchedAt: timestamptz('fetched_at').notNull().defaultNow()
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user