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:
@@ -95,7 +95,9 @@ SELECT
|
|||||||
af.bit_depth,
|
af.bit_depth,
|
||||||
af.channels,
|
af.channels,
|
||||||
af.bitrate,
|
af.bitrate,
|
||||||
af.file_size
|
af.file_size,
|
||||||
|
af.play_count,
|
||||||
|
COALESCE(af.last_played, '') AS last_played
|
||||||
FROM audio_files af
|
FROM audio_files af
|
||||||
JOIN recordings r ON af.recording_id = r.id
|
JOIN recordings r ON af.recording_id = r.id
|
||||||
JOIN artist_credit ac ON r.artist_credit_id = ac.id
|
JOIN artist_credit ac ON r.artist_credit_id = ac.id
|
||||||
@@ -159,7 +161,9 @@ SELECT
|
|||||||
af.bit_depth,
|
af.bit_depth,
|
||||||
af.channels,
|
af.channels,
|
||||||
af.bitrate,
|
af.bitrate,
|
||||||
af.file_size
|
af.file_size,
|
||||||
|
af.play_count,
|
||||||
|
COALESCE(af.last_played, '') AS last_played
|
||||||
FROM audio_files af
|
FROM audio_files af
|
||||||
JOIN recordings r ON af.recording_id = r.id
|
JOIN recordings r ON af.recording_id = r.id
|
||||||
JOIN artist_credit ac ON r.artist_credit_id = ac.id
|
JOIN artist_credit ac ON r.artist_credit_id = ac.id
|
||||||
|
|||||||
@@ -253,7 +253,9 @@ SELECT
|
|||||||
af.bit_depth,
|
af.bit_depth,
|
||||||
af.channels,
|
af.channels,
|
||||||
af.bitrate,
|
af.bitrate,
|
||||||
af.file_size
|
af.file_size,
|
||||||
|
af.play_count,
|
||||||
|
COALESCE(af.last_played, '') AS last_played
|
||||||
FROM audio_files af
|
FROM audio_files af
|
||||||
JOIN recordings r ON af.recording_id = r.id
|
JOIN recordings r ON af.recording_id = r.id
|
||||||
JOIN artist_credit ac ON r.artist_credit_id = ac.id
|
JOIN artist_credit ac ON r.artist_credit_id = ac.id
|
||||||
@@ -279,6 +281,8 @@ type GetAllTracksWithFullMetadataRow struct {
|
|||||||
Channels int64
|
Channels int64
|
||||||
Bitrate int64
|
Bitrate int64
|
||||||
FileSize int64
|
FileSize int64
|
||||||
|
PlayCount int64
|
||||||
|
LastPlayed string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) GetAllTracksWithFullMetadata(ctx context.Context) ([]GetAllTracksWithFullMetadataRow, error) {
|
func (q *Queries) GetAllTracksWithFullMetadata(ctx context.Context) ([]GetAllTracksWithFullMetadataRow, error) {
|
||||||
@@ -307,6 +311,8 @@ func (q *Queries) GetAllTracksWithFullMetadata(ctx context.Context) ([]GetAllTra
|
|||||||
&i.Channels,
|
&i.Channels,
|
||||||
&i.Bitrate,
|
&i.Bitrate,
|
||||||
&i.FileSize,
|
&i.FileSize,
|
||||||
|
&i.PlayCount,
|
||||||
|
&i.LastPlayed,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -344,7 +350,9 @@ SELECT
|
|||||||
af.bit_depth,
|
af.bit_depth,
|
||||||
af.channels,
|
af.channels,
|
||||||
af.bitrate,
|
af.bitrate,
|
||||||
af.file_size
|
af.file_size,
|
||||||
|
af.play_count,
|
||||||
|
COALESCE(af.last_played, '') AS last_played
|
||||||
FROM audio_files af
|
FROM audio_files af
|
||||||
JOIN recordings r ON af.recording_id = r.id
|
JOIN recordings r ON af.recording_id = r.id
|
||||||
JOIN artist_credit ac ON r.artist_credit_id = ac.id
|
JOIN artist_credit ac ON r.artist_credit_id = ac.id
|
||||||
@@ -371,6 +379,8 @@ type GetAllTracksWithFullMetadataByLibraryRow struct {
|
|||||||
Channels int64
|
Channels int64
|
||||||
Bitrate int64
|
Bitrate int64
|
||||||
FileSize int64
|
FileSize int64
|
||||||
|
PlayCount int64
|
||||||
|
LastPlayed string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) GetAllTracksWithFullMetadataByLibrary(ctx context.Context, libraryID int64) ([]GetAllTracksWithFullMetadataByLibraryRow, error) {
|
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.Channels,
|
||||||
&i.Bitrate,
|
&i.Bitrate,
|
||||||
&i.FileSize,
|
&i.FileSize,
|
||||||
|
&i.PlayCount,
|
||||||
|
&i.LastPlayed,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ func mapTrackRow(
|
|||||||
year int64,
|
year int64,
|
||||||
composer, fileType string,
|
composer, fileType string,
|
||||||
sampleRate, bitDepth, channels, bitrate, fileSize int64,
|
sampleRate, bitDepth, channels, bitrate, fileSize int64,
|
||||||
|
playCount int64,
|
||||||
|
lastPlayed string,
|
||||||
) Track {
|
) Track {
|
||||||
return Track{
|
return Track{
|
||||||
TrackName: title,
|
TrackName: title,
|
||||||
@@ -83,6 +85,8 @@ func mapTrackRow(
|
|||||||
Channels: channels,
|
Channels: channels,
|
||||||
Bitrate: bitrate,
|
Bitrate: bitrate,
|
||||||
FileSize: fileSize,
|
FileSize: fileSize,
|
||||||
|
PlayCount: playCount,
|
||||||
|
LastPlayed: lastPlayed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,6 +150,8 @@ func (l *Library) GetAllTracks() ([]Track, error) {
|
|||||||
row.Channels,
|
row.Channels,
|
||||||
row.Bitrate,
|
row.Bitrate,
|
||||||
row.FileSize,
|
row.FileSize,
|
||||||
|
row.PlayCount,
|
||||||
|
row.LastPlayed,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,6 +204,7 @@ func (l *Library) SearchTracks(
|
|||||||
row.Channels,
|
row.Channels,
|
||||||
row.Bitrate,
|
row.Bitrate,
|
||||||
row.FileSize,
|
row.FileSize,
|
||||||
|
0, "",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,6 +244,7 @@ func (l *Library) GetAlbumTracks(albumID int64) ([]Track, error) {
|
|||||||
row.Channels,
|
row.Channels,
|
||||||
row.Bitrate,
|
row.Bitrate,
|
||||||
row.FileSize,
|
row.FileSize,
|
||||||
|
0, "",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,6 +419,7 @@ func (l *Library) GetTracksByGenre(
|
|||||||
row.Channels,
|
row.Channels,
|
||||||
row.Bitrate,
|
row.Bitrate,
|
||||||
row.FileSize,
|
row.FileSize,
|
||||||
|
0, "",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,6 +501,8 @@ func (l *Library) GetAllTracksByLibrary(
|
|||||||
row.Channels,
|
row.Channels,
|
||||||
row.Bitrate,
|
row.Bitrate,
|
||||||
row.FileSize,
|
row.FileSize,
|
||||||
|
row.PlayCount,
|
||||||
|
row.LastPlayed,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -724,6 +735,7 @@ func (l *Library) GetTracksByGenreByLibrary(
|
|||||||
row.Channels,
|
row.Channels,
|
||||||
row.Bitrate,
|
row.Bitrate,
|
||||||
row.FileSize,
|
row.FileSize,
|
||||||
|
0, "",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -775,6 +787,7 @@ func (l *Library) GetAlbumTracksByLibrary(
|
|||||||
row.Channels,
|
row.Channels,
|
||||||
row.Bitrate,
|
row.Bitrate,
|
||||||
row.FileSize,
|
row.FileSize,
|
||||||
|
0, "",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -822,6 +835,7 @@ func (l *Library) SearchTracksByLibrary(
|
|||||||
row.Channels,
|
row.Channels,
|
||||||
row.Bitrate,
|
row.Bitrate,
|
||||||
row.FileSize,
|
row.FileSize,
|
||||||
|
0, "",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -186,6 +186,16 @@ export const COLUMN_DEFS: Record<string, ColumnDef> = {
|
|||||||
comparator: (a, b) =>
|
comparator: (a, b) =>
|
||||||
compareNum(a.FileSize, b.FileSize),
|
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),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -266,6 +266,8 @@ export namespace library {
|
|||||||
Channels: number;
|
Channels: number;
|
||||||
Bitrate: number;
|
Bitrate: number;
|
||||||
FileSize: number;
|
FileSize: number;
|
||||||
|
PlayCount: number;
|
||||||
|
LastPlayed: string;
|
||||||
|
|
||||||
static createFrom(source: any = {}) {
|
static createFrom(source: any = {}) {
|
||||||
return new Track(source);
|
return new Track(source);
|
||||||
@@ -289,6 +291,8 @@ export namespace library {
|
|||||||
this.Channels = source["Channels"];
|
this.Channels = source["Channels"];
|
||||||
this.Bitrate = source["Bitrate"];
|
this.Bitrate = source["Bitrate"];
|
||||||
this.FileSize = source["FileSize"];
|
this.FileSize = source["FileSize"];
|
||||||
|
this.PlayCount = source["PlayCount"];
|
||||||
|
this.LastPlayed = source["LastPlayed"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user