mirror of
https://github.com/navidrome/navidrome.git
synced 2026-08-31 07:30:32 +00:00
feat: add scrobble retrieval api
This commit is contained in:
parent
43e9ade8a3
commit
1ad8b59dbc
@ -3,12 +3,10 @@ package model
|
||||
import "time"
|
||||
|
||||
type Scrobble struct {
|
||||
MediaFileID string `json:"-"`
|
||||
UserID string `json:"-"`
|
||||
|
||||
ID int64 `structs:"id" json:"id"`
|
||||
MediaFileID string `structs:"media_file_id" json:"mediaFileId"`
|
||||
UserID string `json:"-"`
|
||||
SubmissionTime time.Time `structs:"submission_time" json:"submissionTime"`
|
||||
RowId int64 `structs:"row_id" json:"rowId"`
|
||||
MediaFile
|
||||
}
|
||||
|
||||
type ScrobbleRepository interface {
|
||||
|
||||
@ -16,15 +16,15 @@ type scrobbleRepository struct {
|
||||
}
|
||||
|
||||
type dbScrobble struct {
|
||||
dbMediaFile
|
||||
RowId int64 `structs:"row_id" json:"rowId"`
|
||||
SubmissionTime int64 `structs:"submission_time" json:"submissionTime"`
|
||||
MediaFileID string `structs:"media_file_id" json:"mediaFileId"`
|
||||
RowId int64 `structs:"row_id" json:"rowId"`
|
||||
SubmissionTime int64 `structs:"submission_time" json:"submissionTime"`
|
||||
}
|
||||
|
||||
func (m dbScrobble) toScrobble() model.Scrobble {
|
||||
return model.Scrobble{
|
||||
MediaFile: *m.MediaFile,
|
||||
RowId: m.RowId,
|
||||
MediaFileID: m.MediaFileID,
|
||||
ID: m.RowId,
|
||||
SubmissionTime: time.Unix(m.SubmissionTime, 0),
|
||||
}
|
||||
}
|
||||
@ -49,20 +49,7 @@ func (r *scrobbleRepository) baseQuery(options ...model.QueryOptions) SelectBuil
|
||||
user := loggedUser(r.ctx)
|
||||
|
||||
return r.newSelect(options...).
|
||||
Columns("scrobbles.ROWID row_id", "submission_time", "media_file.*", "library.path as library_path", "library.name as library_name").
|
||||
Join("media_file on media_file.id = media_file_id").
|
||||
LeftJoin("library on media_file.library_id = library.id").
|
||||
LeftJoin("annotation on ("+
|
||||
"annotation.item_id = media_file.id"+
|
||||
" AND annotation.item_type = 'media_file'"+
|
||||
" AND annotation.user_id = '"+user.ID+"')").
|
||||
Columns(
|
||||
"coalesce(starred, 0) as starred",
|
||||
"coalesce(rating, 0) as rating",
|
||||
"starred_at",
|
||||
"play_date",
|
||||
"coalesce(play_count, 0) as play_count",
|
||||
).
|
||||
Columns("scrobbles.ROWID row_id", "media_file_id", "submission_time").
|
||||
Where(Eq{"scrobbles.user_id": user.ID})
|
||||
}
|
||||
|
||||
@ -72,9 +59,8 @@ func NewScrobbleRepository(ctx context.Context, db dbx.Builder) model.ScrobbleRe
|
||||
r.db = db
|
||||
r.tableName = "scrobbles"
|
||||
r.registerModel(&model.Scrobble{}, map[string]filterFunc{
|
||||
"from": fromTs,
|
||||
"to": toTs,
|
||||
"title": fullTextFilter("media_file"),
|
||||
"from": fromTs,
|
||||
"to": toTs,
|
||||
})
|
||||
r.setSortMappings(map[string]string{
|
||||
"submission_time": "submission_time",
|
||||
@ -95,17 +81,9 @@ func (r *scrobbleRepository) RecordScrobble(mediaFileID string, submissionTime t
|
||||
}
|
||||
|
||||
func (r *scrobbleRepository) CountAll(options ...model.QueryOptions) (int64, error) {
|
||||
user := loggedUser(r.ctx)
|
||||
|
||||
sel := r.newSelect().
|
||||
Columns("count(*) count").
|
||||
Join("media_file on media_file.id = media_file_id").
|
||||
Where(Eq{"user_id": user.ID})
|
||||
|
||||
sel = r.applyFilters(sel, options...)
|
||||
|
||||
count := r.baseQuery(options...).RemoveColumns().Column("COUNT() as count").RemoveOffset().RemoveLimit().OrderBy("scrobbles.ROWID")
|
||||
var res struct{ Count int64 }
|
||||
err := r.queryOne(sel, &res)
|
||||
err := r.queryOne(count, &res)
|
||||
return res.Count, err
|
||||
}
|
||||
|
||||
@ -115,13 +93,13 @@ func (r *scrobbleRepository) Count(options ...rest.QueryOptions) (int64, error)
|
||||
|
||||
func (r *scrobbleRepository) Get(id string) (*model.Scrobble, error) {
|
||||
sel := r.baseQuery().Where(Eq{"row_id": id})
|
||||
res := dbScrobble{}
|
||||
var res dbScrobble
|
||||
err := r.queryOne(sel, &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
model := res.toScrobble()
|
||||
return &model, nil
|
||||
return &model, err
|
||||
}
|
||||
|
||||
func (r *scrobbleRepository) GetAll(options ...model.QueryOptions) (model.Scrobbles, error) {
|
||||
|
||||
@ -104,9 +104,10 @@ var _ = Describe("ScrobbleRepository", func() {
|
||||
It("returns an existing scrobble for the user", func() {
|
||||
scrobble, err := repo.Get("1")
|
||||
Expect(err).To(BeNil())
|
||||
scrobble.MediaFile.CreatedAt = time.Time{}
|
||||
Expect(scrobble.MediaFile).To(Equal(songDayInALife))
|
||||
Expect(scrobble.SubmissionTime).To(BeTemporally("==", time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)))
|
||||
Expect(scrobble.ID).To(Equal(int64(1)))
|
||||
Expect(scrobble.MediaFileID).To(Equal("1001"))
|
||||
Expect(scrobble.SubmissionTime).To(BeTemporally("==", firstScrobble.SubmissionTime))
|
||||
|
||||
})
|
||||
|
||||
It("does not return a scrobble that exists for another user", func() {
|
||||
@ -131,12 +132,13 @@ var _ = Describe("ScrobbleRepository", func() {
|
||||
Expect(err).To(BeNil())
|
||||
Expect(scrobbles).To(HaveLen(2))
|
||||
|
||||
for idx := range scrobbles {
|
||||
scrobbles[idx].MediaFile.CreatedAt = time.Time{}
|
||||
}
|
||||
Expect(scrobbles[0].ID).To(Equal(int64(3)))
|
||||
Expect(scrobbles[0].MediaFileID).To(Equal("1002"))
|
||||
Expect(scrobbles[0].SubmissionTime).To(BeTemporally("==", thirdScrobble.SubmissionTime))
|
||||
|
||||
Expect(scrobbles[1].MediaFile).To(Equal(songDayInALife))
|
||||
Expect(scrobbles[0].MediaFile).To(Equal(songComeTogether))
|
||||
Expect(scrobbles[1].ID).To(Equal(int64(1)))
|
||||
Expect(scrobbles[1].MediaFileID).To(Equal("1001"))
|
||||
Expect(scrobbles[1].SubmissionTime).To(BeTemporally("==", firstScrobble.SubmissionTime))
|
||||
})
|
||||
|
||||
It("returns scrobbles in a range", func() {
|
||||
@ -146,9 +148,9 @@ var _ = Describe("ScrobbleRepository", func() {
|
||||
Expect(err).To(BeNil())
|
||||
Expect(scrobbles).To(HaveLen(1))
|
||||
|
||||
scrobbles[0].MediaFile.CreatedAt = time.Time{}
|
||||
Expect(scrobbles[0].MediaFile).To(Equal(songComeTogether))
|
||||
|
||||
Expect(scrobbles[0].ID).To(Equal(int64(3)))
|
||||
Expect(scrobbles[0].MediaFileID).To(Equal("1002"))
|
||||
Expect(scrobbles[0].SubmissionTime).To(BeTemporally("==", thirdScrobble.SubmissionTime))
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -173,9 +175,9 @@ var _ = Describe("ScrobbleRepository", func() {
|
||||
It("returns an existing scrobble for the user", func() {
|
||||
scrobble, err := repo.Get("2")
|
||||
Expect(err).To(BeNil())
|
||||
scrobble.MediaFile.CreatedAt = time.Time{}
|
||||
Expect(scrobble.MediaFile).To(Equal(songRadioactivity))
|
||||
Expect(scrobble.SubmissionTime).To(BeTemporally("==", time.Date(1970, 2, 1, 0, 0, 0, 0, time.UTC)))
|
||||
Expect(scrobble.ID).To(Equal(int64(2)))
|
||||
Expect(scrobble.MediaFileID).To(Equal("1003"))
|
||||
Expect(scrobble.SubmissionTime).To(BeTemporally("==", secondScrobble.SubmissionTime))
|
||||
})
|
||||
|
||||
It("does not return a scrobble that exists for another user", func() {
|
||||
@ -200,8 +202,9 @@ var _ = Describe("ScrobbleRepository", func() {
|
||||
Expect(err).To(BeNil())
|
||||
Expect(scrobbles).To(HaveLen(1))
|
||||
|
||||
scrobbles[0].MediaFile.CreatedAt = time.Time{}
|
||||
Expect(scrobbles[0].MediaFile).To(Equal(songRadioactivity))
|
||||
Expect(scrobbles[0].ID).To(Equal(int64(2)))
|
||||
Expect(scrobbles[0].MediaFileID).To(Equal("1003"))
|
||||
Expect(scrobbles[0].SubmissionTime).To(BeTemporally("==", secondScrobble.SubmissionTime))
|
||||
})
|
||||
|
||||
It("returns scrobbles in a range", func() {
|
||||
@ -211,8 +214,9 @@ var _ = Describe("ScrobbleRepository", func() {
|
||||
Expect(err).To(BeNil())
|
||||
Expect(scrobbles).To(HaveLen(1))
|
||||
|
||||
scrobbles[0].MediaFile.CreatedAt = time.Time{}
|
||||
Expect(scrobbles[0].MediaFile).To(Equal(songRadioactivity))
|
||||
Expect(scrobbles[0].ID).To(Equal(int64(2)))
|
||||
Expect(scrobbles[0].MediaFileID).To(Equal("1003"))
|
||||
Expect(scrobbles[0].SubmissionTime).To(BeTemporally("==", secondScrobble.SubmissionTime))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -2,6 +2,7 @@ package tests
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/navidrome/navidrome/model"
|
||||
@ -15,7 +16,7 @@ type MockScrobbleRepo struct {
|
||||
|
||||
func (m *MockScrobbleRepo) Get(id string) (*model.Scrobble, error) {
|
||||
for _, scrobble := range m.RecordedScrobbles {
|
||||
if scrobble.ID == id {
|
||||
if strconv.FormatInt(scrobble.ID, 10) == id {
|
||||
return &scrobble, nil
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user