feat(M003/S03): play count column + data pipeline

Backend:
- GetAllTracksWithFullMetadata queries now select play_count and last_played
- mapTrackRow accepts and passes through PlayCount/LastPlayed
- PlayCount + LastPlayed added to library.Track struct
- sqlcgen Row types updated with new fields

Frontend:
- PlayCount + LastPlayed added to library.Track TypeScript model
- 'Plays' column added to track-list column definitions (60px, right-aligned, sortable)

Queries without play data (search, genre, album) pass 0/empty defaults.
This commit is contained in:
2026-03-21 15:33:08 -04:00
parent 9c85cfcc7b
commit 2ee157a376
5 changed files with 48 additions and 4 deletions
+6 -2
View File
@@ -95,7 +95,9 @@ SELECT
af.bit_depth,
af.channels,
af.bitrate,
af.file_size
af.file_size,
af.play_count,
COALESCE(af.last_played, '') AS last_played
FROM audio_files af
JOIN recordings r ON af.recording_id = r.id
JOIN artist_credit ac ON r.artist_credit_id = ac.id
@@ -159,7 +161,9 @@ SELECT
af.bit_depth,
af.channels,
af.bitrate,
af.file_size
af.file_size,
af.play_count,
COALESCE(af.last_played, '') AS last_played
FROM audio_files af
JOIN recordings r ON af.recording_id = r.id
JOIN artist_credit ac ON r.artist_credit_id = ac.id
@@ -253,7 +253,9 @@ SELECT
af.bit_depth,
af.channels,
af.bitrate,
af.file_size
af.file_size,
af.play_count,
COALESCE(af.last_played, '') AS last_played
FROM audio_files af
JOIN recordings r ON af.recording_id = r.id
JOIN artist_credit ac ON r.artist_credit_id = ac.id
@@ -279,6 +281,8 @@ type GetAllTracksWithFullMetadataRow struct {
Channels int64
Bitrate int64
FileSize int64
PlayCount int64
LastPlayed string
}
func (q *Queries) GetAllTracksWithFullMetadata(ctx context.Context) ([]GetAllTracksWithFullMetadataRow, error) {
@@ -307,6 +311,8 @@ func (q *Queries) GetAllTracksWithFullMetadata(ctx context.Context) ([]GetAllTra
&i.Channels,
&i.Bitrate,
&i.FileSize,
&i.PlayCount,
&i.LastPlayed,
); err != nil {
return nil, err
}
@@ -344,7 +350,9 @@ SELECT
af.bit_depth,
af.channels,
af.bitrate,
af.file_size
af.file_size,
af.play_count,
COALESCE(af.last_played, '') AS last_played
FROM audio_files af
JOIN recordings r ON af.recording_id = r.id
JOIN artist_credit ac ON r.artist_credit_id = ac.id
@@ -371,6 +379,8 @@ type GetAllTracksWithFullMetadataByLibraryRow struct {
Channels int64
Bitrate int64
FileSize int64
PlayCount int64
LastPlayed string
}
func (q *Queries) GetAllTracksWithFullMetadataByLibrary(ctx context.Context, libraryID int64) ([]GetAllTracksWithFullMetadataByLibraryRow, error) {
@@ -399,6 +409,8 @@ func (q *Queries) GetAllTracksWithFullMetadataByLibrary(ctx context.Context, lib
&i.Channels,
&i.Bitrate,
&i.FileSize,
&i.PlayCount,
&i.LastPlayed,
); err != nil {
return nil, err
}
+14
View File
@@ -65,6 +65,8 @@ func mapTrackRow(
year int64,
composer, fileType string,
sampleRate, bitDepth, channels, bitrate, fileSize int64,
playCount int64,
lastPlayed string,
) Track {
return Track{
TrackName: title,
@@ -83,6 +85,8 @@ func mapTrackRow(
Channels: channels,
Bitrate: bitrate,
FileSize: fileSize,
PlayCount: playCount,
LastPlayed: lastPlayed,
}
}
@@ -146,6 +150,8 @@ func (l *Library) GetAllTracks() ([]Track, error) {
row.Channels,
row.Bitrate,
row.FileSize,
row.PlayCount,
row.LastPlayed,
))
}
@@ -198,6 +204,7 @@ func (l *Library) SearchTracks(
row.Channels,
row.Bitrate,
row.FileSize,
0, "",
))
}
@@ -237,6 +244,7 @@ func (l *Library) GetAlbumTracks(albumID int64) ([]Track, error) {
row.Channels,
row.Bitrate,
row.FileSize,
0, "",
))
}
@@ -411,6 +419,7 @@ func (l *Library) GetTracksByGenre(
row.Channels,
row.Bitrate,
row.FileSize,
0, "",
))
}
@@ -492,6 +501,8 @@ func (l *Library) GetAllTracksByLibrary(
row.Channels,
row.Bitrate,
row.FileSize,
row.PlayCount,
row.LastPlayed,
))
}
@@ -724,6 +735,7 @@ func (l *Library) GetTracksByGenreByLibrary(
row.Channels,
row.Bitrate,
row.FileSize,
0, "",
))
}
@@ -775,6 +787,7 @@ func (l *Library) GetAlbumTracksByLibrary(
row.Channels,
row.Bitrate,
row.FileSize,
0, "",
))
}
@@ -822,6 +835,7 @@ func (l *Library) SearchTracksByLibrary(
row.Channels,
row.Bitrate,
row.FileSize,
0, "",
))
}
@@ -186,6 +186,16 @@ export const COLUMN_DEFS: Record<string, ColumnDef> = {
comparator: (a, b) =>
compareNum(a.FileSize, b.FileSize),
},
playCount: {
id: 'playCount',
label: 'Plays',
accessor: (t) =>
t.PlayCount ? String(t.PlayCount) : '',
defaultWidth: '60px',
align: 'right',
comparator: (a, b) =>
compareNum(a.PlayCount, b.PlayCount),
},
};
/**
+4
View File
@@ -266,6 +266,8 @@ export namespace library {
Channels: number;
Bitrate: number;
FileSize: number;
PlayCount: number;
LastPlayed: string;
static createFrom(source: any = {}) {
return new Track(source);
@@ -289,6 +291,8 @@ export namespace library {
this.Channels = source["Channels"];
this.Bitrate = source["Bitrate"];
this.FileSize = source["FileSize"];
this.PlayCount = source["PlayCount"];
this.LastPlayed = source["LastPlayed"];
}
}