mirror of
https://github.com/gabehf/Koito.git
synced 2026-03-07 13:38:15 -08:00
fix: associate tracks with release when scrobbling (#118)
This commit is contained in:
parent
d327729bff
commit
c346c7cb31
12 changed files with 58 additions and 1794 deletions
|
|
@ -39,7 +39,7 @@ func AssociateTrack(ctx context.Context, d db.DB, opts AssociateTrackOpts) (*mod
|
|||
return matchTrackByMbzID(ctx, d, opts)
|
||||
} else {
|
||||
l.Debug().Msgf("Associating track '%s' by title and artist", opts.TrackName)
|
||||
return matchTrackByTitleAndArtist(ctx, d, opts)
|
||||
return matchTrackByTrackInfo(ctx, d, opts)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ func matchTrackByMbzID(ctx context.Context, d db.DB, opts AssociateTrackOpts) (*
|
|||
return nil, fmt.Errorf("matchTrackByMbzID: %w", err)
|
||||
} else {
|
||||
l.Debug().Msgf("Track '%s' could not be found by MusicBrainz ID", opts.TrackName)
|
||||
track, err := matchTrackByTitleAndArtist(ctx, d, opts)
|
||||
track, err := matchTrackByTrackInfo(ctx, d, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("matchTrackByMbzID: %w", err)
|
||||
}
|
||||
|
|
@ -73,28 +73,30 @@ func matchTrackByMbzID(ctx context.Context, d db.DB, opts AssociateTrackOpts) (*
|
|||
}
|
||||
}
|
||||
|
||||
func matchTrackByTitleAndArtist(ctx context.Context, d db.DB, opts AssociateTrackOpts) (*models.Track, error) {
|
||||
func matchTrackByTrackInfo(ctx context.Context, d db.DB, opts AssociateTrackOpts) (*models.Track, error) {
|
||||
l := logger.FromContext(ctx)
|
||||
// try provided track title
|
||||
track, err := d.GetTrack(ctx, db.GetTrackOpts{
|
||||
Title: opts.TrackName,
|
||||
ReleaseID: opts.AlbumID,
|
||||
ArtistIDs: opts.ArtistIDs,
|
||||
})
|
||||
if err == nil {
|
||||
l.Debug().Msgf("Track '%s' found by title and artist match", track.Title)
|
||||
l.Debug().Msgf("Track '%s' found by title, release and artist match", track.Title)
|
||||
return track, nil
|
||||
} else if !errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, fmt.Errorf("matchTrackByTitleAndArtist: %w", err)
|
||||
return nil, fmt.Errorf("matchTrackByTrackInfo: %w", err)
|
||||
} else {
|
||||
if opts.TrackMbzID != uuid.Nil {
|
||||
mbzTrack, err := opts.Mbzc.GetTrack(ctx, opts.TrackMbzID)
|
||||
if err == nil {
|
||||
track, err := d.GetTrack(ctx, db.GetTrackOpts{
|
||||
Title: mbzTrack.Title,
|
||||
ReleaseID: opts.AlbumID,
|
||||
ArtistIDs: opts.ArtistIDs,
|
||||
})
|
||||
if err == nil {
|
||||
l.Debug().Msgf("Track '%s' found by MusicBrainz title and artist match", opts.TrackName)
|
||||
l.Debug().Msgf("Track '%s' found by MusicBrainz title, release and artist match", opts.TrackName)
|
||||
return track, nil
|
||||
}
|
||||
}
|
||||
|
|
@ -108,7 +110,7 @@ func matchTrackByTitleAndArtist(ctx context.Context, d db.DB, opts AssociateTrac
|
|||
Duration: opts.Duration,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("matchTrackByTitleAndArtist: %w", err)
|
||||
return nil, fmt.Errorf("matchTrackByTrackInfo: %w", err)
|
||||
}
|
||||
if opts.TrackMbzID == uuid.Nil {
|
||||
l.Info().Msgf("Created track '%s' with title and artist", opts.TrackName)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ type GetTrackOpts struct {
|
|||
ID int32
|
||||
MusicBrainzID uuid.UUID
|
||||
Title string
|
||||
ReleaseID int32
|
||||
ArtistIDs []int32
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,14 +52,15 @@ func (d *Psql) GetTrack(ctx context.Context, opts db.GetTrackOpts) (*models.Trac
|
|||
AlbumID: t.ReleaseID,
|
||||
Duration: t.Duration,
|
||||
}
|
||||
} else if len(opts.ArtistIDs) > 0 {
|
||||
l.Debug().Msgf("Fetching track from DB with title '%s' and artist id(s) '%v'", opts.Title, opts.ArtistIDs)
|
||||
t, err := d.q.GetTrackByTitleAndArtists(ctx, repository.GetTrackByTitleAndArtistsParams{
|
||||
Title: opts.Title,
|
||||
Column2: opts.ArtistIDs,
|
||||
} else if len(opts.ArtistIDs) > 0 && opts.ReleaseID != 0 {
|
||||
l.Debug().Msgf("Fetching track from DB from release id %d with title '%s' and artist id(s) '%v'", opts.ReleaseID, opts.Title, opts.ArtistIDs)
|
||||
t, err := d.q.GetTrackByTrackInfo(ctx, repository.GetTrackByTrackInfoParams{
|
||||
Title: opts.Title,
|
||||
ReleaseID: opts.ReleaseID,
|
||||
Column3: opts.ArtistIDs,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetTrack: GetTrackByTitleAndArtists: %w", err)
|
||||
return nil, fmt.Errorf("GetTrack: GetTrackByTrackInfo: %w", err)
|
||||
}
|
||||
track = models.Track{
|
||||
ID: t.ID,
|
||||
|
|
|
|||
|
|
@ -16,55 +16,55 @@ func testDataForTracks(t *testing.T) {
|
|||
|
||||
// Insert artists
|
||||
err := store.Exec(context.Background(),
|
||||
`INSERT INTO artists (musicbrainz_id)
|
||||
`INSERT INTO artists (musicbrainz_id)
|
||||
VALUES ('00000000-0000-0000-0000-000000000001'),
|
||||
('00000000-0000-0000-0000-000000000002')`)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Insert artist aliases
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO artist_aliases (artist_id, alias, source, is_primary)
|
||||
`INSERT INTO artist_aliases (artist_id, alias, source, is_primary)
|
||||
VALUES (1, 'Artist One', 'Testing', true),
|
||||
(2, 'Artist Two', 'Testing', true)`)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Insert release groups
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO releases (musicbrainz_id)
|
||||
`INSERT INTO releases (musicbrainz_id)
|
||||
VALUES ('00000000-0000-0000-0000-000000000011'),
|
||||
('00000000-0000-0000-0000-000000000022')`)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Insert release aliases
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO release_aliases (release_id, alias, source, is_primary)
|
||||
`INSERT INTO release_aliases (release_id, alias, source, is_primary)
|
||||
VALUES (1, 'Release Group One', 'Testing', true),
|
||||
(2, 'Release Group Two', 'Testing', true)`)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Insert tracks
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO tracks (musicbrainz_id, release_id, duration)
|
||||
`INSERT INTO tracks (musicbrainz_id, release_id, duration)
|
||||
VALUES ('11111111-1111-1111-1111-111111111111', 1, 100),
|
||||
('22222222-2222-2222-2222-222222222222', 2, 100)`)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Insert track aliases
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO track_aliases (track_id, alias, source, is_primary)
|
||||
`INSERT INTO track_aliases (track_id, alias, source, is_primary)
|
||||
VALUES (1, 'Track One', 'Testing', true),
|
||||
(2, 'Track Two', 'Testing', true)`)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Associate tracks with artists
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO artist_tracks (artist_id, track_id)
|
||||
`INSERT INTO artist_tracks (artist_id, track_id)
|
||||
VALUES (1, 1), (2, 2)`)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Associate tracks with artists
|
||||
err = store.Exec(context.Background(),
|
||||
`INSERT INTO listens (user_id, track_id, listened_at)
|
||||
`INSERT INTO listens (user_id, track_id, listened_at)
|
||||
VALUES (1, 1, NOW()), (1, 2, NOW())`)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
|
@ -88,9 +88,10 @@ func TestGetTrack(t *testing.T) {
|
|||
assert.Equal(t, "Track Two", track.Title)
|
||||
assert.EqualValues(t, 100, track.TimeListened)
|
||||
|
||||
// Test GetTrack by Title and ArtistIDs
|
||||
// Test GetTrack by Title, Release and ArtistIDs
|
||||
track, err = store.GetTrack(ctx, db.GetTrackOpts{
|
||||
Title: "Track One",
|
||||
ReleaseID: 1,
|
||||
ArtistIDs: []int32{1},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
|
@ -99,7 +100,7 @@ func TestGetTrack(t *testing.T) {
|
|||
assert.EqualValues(t, 100, track.TimeListened)
|
||||
|
||||
// Test GetTrack with insufficient information
|
||||
_, err = store.GetTrack(ctx, db.GetTrackOpts{})
|
||||
_, err = store.GetTrack(ctx, db.GetTrackOpts{Title: "Track One"})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
func TestSaveTrack(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ func ImportKoitoFile(ctx context.Context, store db.DB, filename string) error {
|
|||
track, err := store.GetTrack(ctx, db.GetTrackOpts{
|
||||
MusicBrainzID: mbid,
|
||||
Title: getPrimaryAliasFromAliasSlice(data.Listens[i].Track.Aliases),
|
||||
ReleaseID: albumId,
|
||||
ArtistIDs: artistIds,
|
||||
})
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
|
|
|
|||
|
|
@ -417,23 +417,25 @@ func (q *Queries) GetTrackByMbzID(ctx context.Context, musicbrainzID *uuid.UUID)
|
|||
return i, err
|
||||
}
|
||||
|
||||
const getTrackByTitleAndArtists = `-- name: GetTrackByTitleAndArtists :one
|
||||
const getTrackByTrackInfo = `-- name: GetTrackByTrackInfo :one
|
||||
SELECT t.id, t.musicbrainz_id, t.duration, t.release_id, t.title
|
||||
FROM tracks_with_title t
|
||||
JOIN artist_tracks at ON at.track_id = t.id
|
||||
WHERE t.title = $1
|
||||
AND at.artist_id = ANY($2::int[])
|
||||
AND at.artist_id = ANY($3::int[])
|
||||
AND t.release_id = $2
|
||||
GROUP BY t.id, t.title, t.musicbrainz_id, t.duration, t.release_id
|
||||
HAVING COUNT(DISTINCT at.artist_id) = cardinality($2::int[])
|
||||
HAVING COUNT(DISTINCT at.artist_id) = cardinality($3::int[])
|
||||
`
|
||||
|
||||
type GetTrackByTitleAndArtistsParams struct {
|
||||
Title string
|
||||
Column2 []int32
|
||||
type GetTrackByTrackInfoParams struct {
|
||||
Title string
|
||||
ReleaseID int32
|
||||
Column3 []int32
|
||||
}
|
||||
|
||||
func (q *Queries) GetTrackByTitleAndArtists(ctx context.Context, arg GetTrackByTitleAndArtistsParams) (TracksWithTitle, error) {
|
||||
row := q.db.QueryRow(ctx, getTrackByTitleAndArtists, arg.Title, arg.Column2)
|
||||
func (q *Queries) GetTrackByTrackInfo(ctx context.Context, arg GetTrackByTrackInfoParams) (TracksWithTitle, error) {
|
||||
row := q.db.QueryRow(ctx, getTrackByTrackInfo, arg.Title, arg.ReleaseID, arg.Column3)
|
||||
var i TracksWithTitle
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue