playlist phantom track matching added, updated search queries for efficiency
This commit is contained in:
+183
-22
@@ -1,6 +1,7 @@
|
||||
package library
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
@@ -48,6 +49,39 @@ func splitGenres(concatenated string) []string {
|
||||
return strings.Split(concatenated, genreDelimiter)
|
||||
}
|
||||
|
||||
// mapTrackRow converts raw database column values into a Track.
|
||||
// This is shared by GetAllTracks, SearchTracks, and GetTracksByGenre
|
||||
// to avoid tripling the row-mapping code.
|
||||
func mapTrackRow(
|
||||
filePath string,
|
||||
lengthMs int64,
|
||||
title, artistName string,
|
||||
trackNumber, discNumber sql.NullInt64,
|
||||
album, genre string,
|
||||
year int64,
|
||||
composer, fileType string,
|
||||
sampleRate, bitDepth, channels, bitrate, fileSize int64,
|
||||
) Track {
|
||||
return Track{
|
||||
TrackName: title,
|
||||
ArtistName: artistName,
|
||||
TrackLength: strconv.FormatInt(lengthMs, 10),
|
||||
FilePath: filePath,
|
||||
TrackNumber: trackNumber.Int64,
|
||||
DiscNumber: discNumber.Int64,
|
||||
Album: album,
|
||||
Genre: splitGenres(genre),
|
||||
Year: year,
|
||||
Composer: composer,
|
||||
FileType: fileType,
|
||||
SampleRate: sampleRate,
|
||||
BitDepth: bitDepth,
|
||||
Channels: channels,
|
||||
Bitrate: bitrate,
|
||||
FileSize: fileSize,
|
||||
}
|
||||
}
|
||||
|
||||
// Artist represents an artist in the library.
|
||||
type Artist struct {
|
||||
ID int64
|
||||
@@ -91,28 +125,24 @@ func (l *Library) GetAllTracks() ([]Track, error) {
|
||||
tracks := make([]Track, 0, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
track := Track{
|
||||
TrackName: row.Title,
|
||||
ArtistName: row.ArtistName,
|
||||
TrackLength: strconv.FormatInt(
|
||||
row.LengthMilliseconds, 10,
|
||||
),
|
||||
FilePath: row.FilePath,
|
||||
TrackNumber: row.TrackNumber.Int64,
|
||||
DiscNumber: row.DiscNumber.Int64,
|
||||
Album: row.Album,
|
||||
Genre: splitGenres(row.Genre),
|
||||
Year: row.Year,
|
||||
Composer: row.Composer,
|
||||
FileType: row.FileType,
|
||||
SampleRate: row.SampleRate,
|
||||
BitDepth: row.BitDepth,
|
||||
Channels: row.Channels,
|
||||
Bitrate: row.Bitrate,
|
||||
FileSize: row.FileSize,
|
||||
}
|
||||
|
||||
tracks = append(tracks, track)
|
||||
tracks = append(tracks, mapTrackRow(
|
||||
row.FilePath,
|
||||
row.LengthMilliseconds,
|
||||
row.Title,
|
||||
row.ArtistName,
|
||||
row.TrackNumber,
|
||||
row.DiscNumber,
|
||||
row.Album,
|
||||
row.Genre,
|
||||
row.Year,
|
||||
row.Composer,
|
||||
row.FileType,
|
||||
row.SampleRate,
|
||||
row.BitDepth,
|
||||
row.Channels,
|
||||
row.Bitrate,
|
||||
row.FileSize,
|
||||
))
|
||||
}
|
||||
|
||||
l.logger.Info("formatted tracks", "count", len(tracks))
|
||||
@@ -120,6 +150,56 @@ func (l *Library) GetAllTracks() ([]Track, error) {
|
||||
return tracks, nil
|
||||
}
|
||||
|
||||
// searchTrackLimit is the maximum number of results returned by
|
||||
// a full-text search.
|
||||
const searchTrackLimit = 200
|
||||
|
||||
// SearchTracks performs an FTS5 full-text search and returns
|
||||
// matching tracks with full metadata.
|
||||
func (l *Library) SearchTracks(
|
||||
query string,
|
||||
) ([]Track, error) {
|
||||
rows, err := l.db.SearchFTSTracks(
|
||||
query, searchTrackLimit,
|
||||
)
|
||||
if err != nil {
|
||||
l.logger.Error(
|
||||
"FTS track search failed",
|
||||
"query", query,
|
||||
"error", err,
|
||||
)
|
||||
|
||||
return nil, fmt.Errorf(
|
||||
"search tracks failed: %w", err,
|
||||
)
|
||||
}
|
||||
|
||||
tracks := make([]Track, 0, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
tracks = append(tracks, mapTrackRow(
|
||||
row.FilePath,
|
||||
row.LengthMilliseconds,
|
||||
row.Title,
|
||||
row.ArtistName,
|
||||
row.TrackNumber,
|
||||
row.DiscNumber,
|
||||
row.Album,
|
||||
row.Genre,
|
||||
row.Year,
|
||||
row.Composer,
|
||||
row.FileType,
|
||||
row.SampleRate,
|
||||
row.BitDepth,
|
||||
row.Channels,
|
||||
row.Bitrate,
|
||||
row.FileSize,
|
||||
))
|
||||
}
|
||||
|
||||
return tracks, nil
|
||||
}
|
||||
|
||||
// GetAlbumTracks returns all tracks for a given album (release group), ordered by disc and track number.
|
||||
func (l *Library) GetAlbumTracks(albumID int64) ([]Track, error) {
|
||||
rows, err := l.db.Queries.GetAudioFilesByReleaseGroup(l.ctx, albumID)
|
||||
@@ -280,3 +360,84 @@ func (l *Library) GetAlbumsByArtist(
|
||||
|
||||
return albums, nil
|
||||
}
|
||||
|
||||
// GenreWithCount holds a genre name and its associated track count.
|
||||
type GenreWithCount struct {
|
||||
Name string `json:"Name"`
|
||||
TrackCount int64 `json:"TrackCount"`
|
||||
}
|
||||
|
||||
// GetTracksByGenre returns all tracks tagged with the given genre.
|
||||
func (l *Library) GetTracksByGenre(
|
||||
genreName string,
|
||||
) ([]Track, error) {
|
||||
rows, err := l.db.Queries.GetTracksByGenre(
|
||||
l.ctx, genreName,
|
||||
)
|
||||
if err != nil {
|
||||
l.logger.Error(
|
||||
"could not retrieve tracks for genre",
|
||||
"genre", genreName,
|
||||
"error", err,
|
||||
)
|
||||
|
||||
return nil, fmt.Errorf(
|
||||
"could not get tracks for genre: %w", err,
|
||||
)
|
||||
}
|
||||
|
||||
tracks := make([]Track, 0, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
tracks = append(tracks, mapTrackRow(
|
||||
row.FilePath,
|
||||
row.LengthMilliseconds,
|
||||
row.Title,
|
||||
row.ArtistName,
|
||||
row.TrackNumber,
|
||||
row.DiscNumber,
|
||||
row.Album,
|
||||
row.Genre,
|
||||
row.Year,
|
||||
row.Composer,
|
||||
row.FileType,
|
||||
row.SampleRate,
|
||||
row.BitDepth,
|
||||
row.Channels,
|
||||
row.Bitrate,
|
||||
row.FileSize,
|
||||
))
|
||||
}
|
||||
|
||||
return tracks, nil
|
||||
}
|
||||
|
||||
// GetAllGenresWithCounts returns all genres with their track counts.
|
||||
func (l *Library) GetAllGenresWithCounts() (
|
||||
[]GenreWithCount, error,
|
||||
) {
|
||||
rows, err := l.db.Queries.GetAllGenresWithCounts(
|
||||
l.ctx,
|
||||
)
|
||||
if err != nil {
|
||||
l.logger.Error(
|
||||
"could not retrieve genres with counts",
|
||||
"error", err,
|
||||
)
|
||||
|
||||
return nil, fmt.Errorf(
|
||||
"could not get genres: %w", err,
|
||||
)
|
||||
}
|
||||
|
||||
genres := make([]GenreWithCount, 0, len(rows))
|
||||
|
||||
for _, row := range rows {
|
||||
genres = append(genres, GenreWithCount{
|
||||
Name: row.Name,
|
||||
TrackCount: row.TrackCount,
|
||||
})
|
||||
}
|
||||
|
||||
return genres, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user