diff --git a/engine/browser.go b/engine/browser.go index df551b1b..8b4d31fd 100644 --- a/engine/browser.go +++ b/engine/browser.go @@ -81,8 +81,7 @@ func (b *browser) Artist(ctx context.Context, id string) (*DirectoryInfo, error) for _, al := range albums { albumIds = append(albumIds, al.ID) } - annMap, err := b.ds.Annotation(ctx).GetMap(getUserID(ctx), model.AlbumItemType, albumIds) - return b.buildArtistDir(a, albums, annMap), nil + return b.buildArtistDir(a, albums), nil } func (b *browser) Album(ctx context.Context, id string) (*DirectoryInfo, error) { @@ -96,16 +95,7 @@ func (b *browser) Album(ctx context.Context, id string) (*DirectoryInfo, error) mfIds = append(mfIds, mf.ID) } - userID := getUserID(ctx) - trackAnnMap, err := b.ds.Annotation(ctx).GetMap(userID, model.MediaItemType, mfIds) - if err != nil { - return nil, err - } - ann, err := b.ds.Annotation(ctx).Get(userID, model.AlbumItemType, al.ID) - if err != nil { - return nil, err - } - return b.buildAlbumDir(al, ann, tracks, trackAnnMap), nil + return b.buildAlbumDir(al, tracks), nil } func (b *browser) Directory(ctx context.Context, id string) (*DirectoryInfo, error) { @@ -126,13 +116,7 @@ func (b *browser) GetSong(ctx context.Context, id string) (*Entry, error) { return nil, err } - userId := getUserID(ctx) - ann, err := b.ds.Annotation(ctx).Get(userId, model.MediaItemType, id) - if err != nil { - return nil, err - } - - entry := FromMediaFile(mf, ann) + entry := FromMediaFile(mf) return &entry, nil } @@ -149,7 +133,7 @@ func (b *browser) GetGenres(ctx context.Context) (model.Genres, error) { return genres, err } -func (b *browser) buildArtistDir(a *model.Artist, albums model.Albums, albumAnnMap model.AnnotationMap) *DirectoryInfo { +func (b *browser) buildArtistDir(a *model.Artist, albums model.Albums) *DirectoryInfo { dir := &DirectoryInfo{ Id: a.ID, Name: a.Name, @@ -158,39 +142,31 @@ func (b *browser) buildArtistDir(a *model.Artist, albums model.Albums, albumAnnM dir.Entries = make(Entries, len(albums)) for i, al := range albums { - ann := albumAnnMap[al.ID] - dir.Entries[i] = FromAlbum(&al, &ann) - dir.PlayCount += int32(ann.PlayCount) + dir.Entries[i] = FromAlbum(&al) + dir.PlayCount += int32(al.PlayCount) } return dir } -func (b *browser) buildAlbumDir(al *model.Album, albumAnn *model.Annotation, tracks model.MediaFiles, trackAnnMap model.AnnotationMap) *DirectoryInfo { +func (b *browser) buildAlbumDir(al *model.Album, tracks model.MediaFiles) *DirectoryInfo { dir := &DirectoryInfo{ - Id: al.ID, - Name: al.Name, - Parent: al.ArtistID, - Artist: al.Artist, - ArtistId: al.ArtistID, - SongCount: al.SongCount, - Duration: al.Duration, - Created: al.CreatedAt, - Year: al.Year, - Genre: al.Genre, - CoverArt: al.CoverArtId, - } - if albumAnn != nil { - dir.PlayCount = int32(albumAnn.PlayCount) - dir.Starred = albumAnn.StarredAt - dir.UserRating = albumAnn.Rating + Id: al.ID, + Name: al.Name, + Parent: al.ArtistID, + Artist: al.Artist, + ArtistId: al.ArtistID, + SongCount: al.SongCount, + Duration: al.Duration, + Created: al.CreatedAt, + Year: al.Year, + Genre: al.Genre, + CoverArt: al.CoverArtId, + PlayCount: int32(al.PlayCount), + Starred: al.StarredAt, + UserRating: al.Rating, } - dir.Entries = make(Entries, len(tracks)) - for i, mf := range tracks { - mfId := mf.ID - ann := trackAnnMap[mfId] - dir.Entries[i] = FromMediaFile(&mf, &ann) - } + dir.Entries = FromMediaFiles(tracks) return dir } diff --git a/engine/common.go b/engine/common.go index 8801dbf6..a988548e 100644 --- a/engine/common.go +++ b/engine/common.go @@ -1,7 +1,6 @@ package engine import ( - "context" "fmt" "time" @@ -46,19 +45,17 @@ type Entry struct { type Entries []Entry -func FromArtist(ar *model.Artist, ann *model.Annotation) Entry { +func FromArtist(ar *model.Artist) Entry { e := Entry{} e.Id = ar.ID e.Title = ar.Name e.AlbumCount = ar.AlbumCount e.IsDir = true - //if ann != nil { e.Starred = ar.StarredAt - //} return e } -func FromAlbum(al *model.Album, ann *model.Annotation) Entry { +func FromAlbum(al *model.Album) Entry { e := Entry{} e.Id = al.ID e.Title = al.Name @@ -74,15 +71,13 @@ func FromAlbum(al *model.Album, ann *model.Annotation) Entry { e.ArtistId = al.ArtistID e.Duration = al.Duration e.SongCount = al.SongCount - //if ann != nil { e.Starred = al.StarredAt e.PlayCount = int32(al.PlayCount) e.UserRating = al.Rating - //} return e } -func FromMediaFile(mf *model.MediaFile, ann *model.Annotation) Entry { +func FromMediaFile(mf *model.MediaFile) Entry { e := Entry{} e.Id = mf.ID e.Title = mf.Title @@ -111,11 +106,9 @@ func FromMediaFile(mf *model.MediaFile, ann *model.Annotation) Entry { e.AlbumId = mf.AlbumID e.ArtistId = mf.ArtistID e.Type = "music" // TODO Hardcoded for now - //if ann != nil { e.PlayCount = int32(mf.PlayCount) e.Starred = mf.StarredAt e.UserRating = mf.Rating - //} return e } @@ -130,37 +123,26 @@ func realArtistName(mf *model.MediaFile) string { return mf.Artist } -func FromAlbums(albums model.Albums, annMap model.AnnotationMap) Entries { +func FromAlbums(albums model.Albums) Entries { entries := make(Entries, len(albums)) for i, al := range albums { - ann := annMap[al.ID] - entries[i] = FromAlbum(&al, &ann) + entries[i] = FromAlbum(&al) } return entries } -func FromMediaFiles(mfs model.MediaFiles, annMap model.AnnotationMap) Entries { +func FromMediaFiles(mfs model.MediaFiles) Entries { entries := make(Entries, len(mfs)) for i, mf := range mfs { - ann := annMap[mf.ID] - entries[i] = FromMediaFile(&mf, &ann) + entries[i] = FromMediaFile(&mf) } return entries } -func FromArtists(ars model.Artists, annMap model.AnnotationMap) Entries { +func FromArtists(ars model.Artists) Entries { entries := make(Entries, len(ars)) for i, ar := range ars { - ann := annMap[ar.ID] - entries[i] = FromArtist(&ar, &ann) + entries[i] = FromArtist(&ar) } return entries } - -func getUserID(ctx context.Context) string { - user, ok := ctx.Value("user").(*model.User) - if ok { - return user.ID - } - return "" -} diff --git a/engine/list_generator.go b/engine/list_generator.go index 70d6d2e0..ab231a46 100644 --- a/engine/list_generator.go +++ b/engine/list_generator.go @@ -40,34 +40,7 @@ func (g *listGenerator) query(ctx context.Context, qo model.QueryOptions) (Entri for i, al := range albums { albumIds[i] = al.ID } - annMap, err := g.ds.Annotation(ctx).GetMap(getUserID(ctx), model.AlbumItemType, albumIds) - if err != nil { - return nil, err - } - return FromAlbums(albums, annMap), err -} - -func (g *listGenerator) queryByAnnotation(ctx context.Context, qo model.QueryOptions) (Entries, error) { - annotations, err := g.ds.Annotation(ctx).GetAll(getUserID(ctx), model.AlbumItemType, qo) - if err != nil { - return nil, err - } - albumIds := make([]string, len(annotations)) - for i, ann := range annotations { - albumIds[i] = ann.ItemID - } - - albumMap, err := g.ds.Album(ctx).GetMap(albumIds) - if err != nil { - return nil, err - } - - var albums Entries - for _, ann := range annotations { - album := albumMap[ann.ItemID] - albums = append(albums, FromAlbum(&album, &ann)) - } - return albums, nil + return FromAlbums(albums), err } func (g *listGenerator) GetNewest(ctx context.Context, offset int, size int) (Entries, error) { @@ -78,19 +51,19 @@ func (g *listGenerator) GetNewest(ctx context.Context, offset int, size int) (En func (g *listGenerator) GetRecent(ctx context.Context, offset int, size int) (Entries, error) { qo := model.QueryOptions{Sort: "PlayDate", Order: "desc", Offset: offset, Max: size, Filters: squirrel.Gt{"play_date": time.Time{}}} - return g.queryByAnnotation(ctx, qo) + return g.query(ctx, qo) } func (g *listGenerator) GetFrequent(ctx context.Context, offset int, size int) (Entries, error) { qo := model.QueryOptions{Sort: "PlayCount", Order: "desc", Offset: offset, Max: size, Filters: squirrel.Gt{"play_count": 0}} - return g.queryByAnnotation(ctx, qo) + return g.query(ctx, qo) } func (g *listGenerator) GetHighest(ctx context.Context, offset int, size int) (Entries, error) { qo := model.QueryOptions{Sort: "Rating", Order: "desc", Offset: offset, Max: size, - Filters: squirrel.Gt{"rating__gt": 0}} - return g.queryByAnnotation(ctx, qo) + Filters: squirrel.Gt{"rating": 0}} + return g.query(ctx, qo) } func (g *listGenerator) GetByName(ctx context.Context, offset int, size int) (Entries, error) { @@ -109,19 +82,7 @@ func (g *listGenerator) GetRandom(ctx context.Context, offset int, size int) (En return nil, err } - annMap, err := g.getAnnotationsForAlbums(ctx, albums) - if err != nil { - return nil, err - } - return FromAlbums(albums, annMap), nil -} - -func (g *listGenerator) getAnnotationsForAlbums(ctx context.Context, albums model.Albums) (model.AnnotationMap, error) { - albumIds := make([]string, len(albums)) - for i, al := range albums { - albumIds[i] = al.ID - } - return g.ds.Annotation(ctx).GetMap(getUserID(ctx), model.AlbumItemType, albumIds) + return FromAlbums(albums), nil } func (g *listGenerator) GetRandomSongs(ctx context.Context, size int, genre string) (Entries, error) { @@ -134,45 +95,33 @@ func (g *listGenerator) GetRandomSongs(ctx context.Context, size int, genre stri return nil, err } - r := make(Entries, len(mediaFiles)) - for i, mf := range mediaFiles { - ann, err := g.ds.Annotation(ctx).Get(getUserID(ctx), model.MediaItemType, mf.ID) - if err != nil { - return nil, err - } - r[i] = FromMediaFile(&mf, ann) - } - return r, nil + return FromMediaFiles(mediaFiles), nil } func (g *listGenerator) GetStarred(ctx context.Context, offset int, size int) (Entries, error) { qo := model.QueryOptions{Offset: offset, Max: size, Sort: "starred_at", Order: "desc"} - albums, err := g.ds.Album(ctx).GetStarred(getUserID(ctx), qo) + albums, err := g.ds.Album(ctx).GetStarred(qo) if err != nil { return nil, err } - annMap, err := g.getAnnotationsForAlbums(ctx, albums) - if err != nil { - return nil, err - } - return FromAlbums(albums, annMap), nil + return FromAlbums(albums), nil } func (g *listGenerator) GetAllStarred(ctx context.Context) (artists Entries, albums Entries, mediaFiles Entries, err error) { options := model.QueryOptions{Sort: "starred_at", Order: "desc"} - ars, err := g.ds.Artist(ctx).GetStarred(getUserID(ctx), options) + ars, err := g.ds.Artist(ctx).GetStarred(options) if err != nil { return nil, nil, nil, err } - als, err := g.ds.Album(ctx).GetStarred(getUserID(ctx), options) + als, err := g.ds.Album(ctx).GetStarred(options) if err != nil { return nil, nil, nil, err } - mfs, err := g.ds.MediaFile(ctx).GetStarred(getUserID(ctx), options) + mfs, err := g.ds.MediaFile(ctx).GetStarred(options) if err != nil { return nil, nil, nil, err } @@ -181,28 +130,15 @@ func (g *listGenerator) GetAllStarred(ctx context.Context) (artists Entries, alb for _, mf := range mfs { mfIds = append(mfIds, mf.ID) } - trackAnnMap, err := g.ds.Annotation(ctx).GetMap(getUserID(ctx), model.MediaItemType, mfIds) - if err != nil { - return nil, nil, nil, err - } - - albumAnnMap, err := g.getAnnotationsForAlbums(ctx, als) - if err != nil { - return nil, nil, nil, err - } var artistIds []string for _, ar := range ars { artistIds = append(artistIds, ar.ID) } - artistAnnMap, err := g.ds.Annotation(ctx).GetMap(getUserID(ctx), model.MediaItemType, artistIds) - if err != nil { - return nil, nil, nil, err - } - artists = FromArtists(ars, artistAnnMap) - albums = FromAlbums(als, albumAnnMap) - mediaFiles = FromMediaFiles(mfs, trackAnnMap) + artists = FromArtists(ars) + albums = FromAlbums(als) + mediaFiles = FromMediaFiles(mfs) return } @@ -218,8 +154,7 @@ func (g *listGenerator) GetNowPlaying(ctx context.Context) (Entries, error) { if err != nil { return nil, err } - ann, err := g.ds.Annotation(ctx).Get(getUserID(ctx), model.MediaItemType, mf.ID) - entries[i] = FromMediaFile(mf, ann) + entries[i] = FromMediaFile(mf) entries[i].UserName = np.Username entries[i].MinutesAgo = int(time.Now().Sub(np.Start).Minutes()) entries[i].PlayerId = np.PlayerId diff --git a/engine/playlists.go b/engine/playlists.go index 5033c434..4235467d 100644 --- a/engine/playlists.go +++ b/engine/playlists.go @@ -123,7 +123,7 @@ func (p *playlists) Get(ctx context.Context, id string) (*PlaylistInfo, error) { } // TODO Use model.Playlist when got rid of Entries - pinfo := &PlaylistInfo{ + plsInfo := &PlaylistInfo{ Id: pl.ID, Name: pl.Name, SongCount: len(pl.Tracks), @@ -132,19 +132,7 @@ func (p *playlists) Get(ctx context.Context, id string) (*PlaylistInfo, error) { Owner: pl.Owner, Comment: pl.Comment, } - pinfo.Entries = make(Entries, len(pl.Tracks)) - var mfIds []string - for _, mf := range pl.Tracks { - mfIds = append(mfIds, mf.ID) - } - - annMap, err := p.ds.Annotation(ctx).GetMap(getUserID(ctx), model.MediaItemType, mfIds) - - for i, mf := range pl.Tracks { - ann := annMap[mf.ID] - pinfo.Entries[i] = FromMediaFile(&mf, &ann) - } - - return pinfo, nil + plsInfo.Entries = FromMediaFiles(pl.Tracks) + return plsInfo, nil } diff --git a/engine/ratings.go b/engine/ratings.go index a2702bfa..717d1e22 100644 --- a/engine/ratings.go +++ b/engine/ratings.go @@ -26,9 +26,9 @@ func (r ratings) SetRating(ctx context.Context, id string, rating int) error { return err } if exist { - return r.ds.Annotation(ctx).SetRating(rating, getUserID(ctx), model.AlbumItemType, id) + return r.ds.Annotation(ctx).SetRating(rating, model.AlbumItemType, id) } - return r.ds.Annotation(ctx).SetRating(rating, getUserID(ctx), model.MediaItemType, id) + return r.ds.Annotation(ctx).SetRating(rating, model.MediaItemType, id) } func (r ratings) SetStar(ctx context.Context, star bool, ids ...string) error { @@ -36,7 +36,6 @@ func (r ratings) SetStar(ctx context.Context, star bool, ids ...string) error { log.Warn(ctx, "Cannot star/unstar an empty list of ids") return nil } - userId := getUserID(ctx) return r.ds.WithTx(func(tx model.DataStore) error { for _, id := range ids { @@ -45,7 +44,7 @@ func (r ratings) SetStar(ctx context.Context, star bool, ids ...string) error { return err } if exist { - err = tx.Annotation(ctx).SetStar(star, userId, model.AlbumItemType, ids...) + err = tx.Annotation(ctx).SetStar(star, model.AlbumItemType, ids...) if err != nil { return err } @@ -56,13 +55,13 @@ func (r ratings) SetStar(ctx context.Context, star bool, ids ...string) error { return err } if exist { - err = tx.Annotation(ctx).SetStar(star, userId, model.ArtistItemType, ids...) + err = tx.Annotation(ctx).SetStar(star, model.ArtistItemType, ids...) if err != nil { return err } continue } - err = tx.Annotation(ctx).SetStar(star, userId, model.MediaItemType, ids...) + err = tx.Annotation(ctx).SetStar(star, model.MediaItemType, ids...) if err != nil { return err } diff --git a/engine/scrobbler.go b/engine/scrobbler.go index d3d87436..1d73e7de 100644 --- a/engine/scrobbler.go +++ b/engine/scrobbler.go @@ -24,8 +24,6 @@ type scrobbler struct { } func (s *scrobbler) Register(ctx context.Context, playerId int, trackId string, playTime time.Time) (*model.MediaFile, error) { - userId := getUserID(ctx) - var mf *model.MediaFile var err error err = s.ds.WithTx(func(tx model.DataStore) error { @@ -33,11 +31,11 @@ func (s *scrobbler) Register(ctx context.Context, playerId int, trackId string, if err != nil { return err } - err = s.ds.Annotation(ctx).IncPlayCount(userId, model.MediaItemType, trackId, playTime) + err = s.ds.Annotation(ctx).IncPlayCount(model.MediaItemType, trackId, playTime) if err != nil { return err } - err = s.ds.Annotation(ctx).IncPlayCount(userId, model.AlbumItemType, mf.AlbumID, playTime) + err = s.ds.Annotation(ctx).IncPlayCount(model.AlbumItemType, mf.AlbumID, playTime) return err }) return mf, err diff --git a/engine/search.go b/engine/search.go index 8d7ae5e3..b84bf93b 100644 --- a/engine/search.go +++ b/engine/search.go @@ -34,12 +34,7 @@ func (s *search) SearchArtist(ctx context.Context, q string, offset int, size in for i, al := range artists { artistIds[i] = al.ID } - annMap, err := s.ds.Annotation(ctx).GetMap(getUserID(ctx), model.ArtistItemType, artistIds) - if err != nil { - return nil, nil - } - - return FromArtists(artists, annMap), nil + return FromArtists(artists), nil } func (s *search) SearchAlbum(ctx context.Context, q string, offset int, size int) (Entries, error) { @@ -53,12 +48,8 @@ func (s *search) SearchAlbum(ctx context.Context, q string, offset int, size int for i, al := range albums { albumIds[i] = al.ID } - annMap, err := s.ds.Annotation(ctx).GetMap(getUserID(ctx), model.AlbumItemType, albumIds) - if err != nil { - return nil, nil - } - return FromAlbums(albums, annMap), nil + return FromAlbums(albums), nil } func (s *search) SearchSong(ctx context.Context, q string, offset int, size int) (Entries, error) { @@ -72,10 +63,6 @@ func (s *search) SearchSong(ctx context.Context, q string, offset int, size int) for i, mf := range mediaFiles { trackIds[i] = mf.ID } - annMap, err := s.ds.Annotation(ctx).GetMap(getUserID(ctx), model.MediaItemType, trackIds) - if err != nil { - return nil, nil - } - return FromMediaFiles(mediaFiles, annMap), nil + return FromMediaFiles(mediaFiles), nil } diff --git a/model/album.go b/model/album.go index c081669a..b641a125 100644 --- a/model/album.go +++ b/model/album.go @@ -5,7 +5,7 @@ import "time" type Album struct { ID string `json:"id" orm:"column(id)"` Name string `json:"name"` - ArtistID string `json:"artistId"` + ArtistID string `json:"artistId" orm:"pk;column(artist_id)"` CoverArtPath string `json:"-"` CoverArtId string `json:"-"` Artist string `json:"artist"` @@ -37,7 +37,7 @@ type AlbumRepository interface { GetAll(...QueryOptions) (Albums, error) GetMap(ids []string) (map[string]Album, error) GetRandom(...QueryOptions) (Albums, error) - GetStarred(userId string, options ...QueryOptions) (Albums, error) + GetStarred(options ...QueryOptions) (Albums, error) Search(q string, offset int, size int) (Albums, error) Refresh(ids ...string) error PurgeEmpty() error diff --git a/model/annotation.go b/model/annotation.go index 2261fc6c..d76c0eef 100644 --- a/model/annotation.go +++ b/model/annotation.go @@ -9,25 +9,22 @@ const ( ) type Annotation struct { - AnnotationID string - UserID string - ItemID string - ItemType string - PlayCount int - PlayDate time.Time - Rating int - Starred bool - StarredAt time.Time + AnnID string `json:"annID" orm:"pk;column(ann_id)"` + UserID string `json:"userID" orm:"pk;column(user_id)"` + ItemID string `json:"itemID" orm:"pk;column(item_id)"` + ItemType string `json:"itemType"` + PlayCount int `json:"playCount"` + PlayDate time.Time `json:"playDate"` + Rating int `json:"rating"` + Starred bool `json:"starred"` + StarredAt time.Time `json:"starredAt"` } type AnnotationMap map[string]Annotation type AnnotationRepository interface { - Get(userID, itemType string, itemID string) (*Annotation, error) - GetAll(userID, itemType string, options ...QueryOptions) ([]Annotation, error) - GetMap(userID, itemType string, itemID []string) (AnnotationMap, error) - Delete(userID, itemType string, itemID ...string) error - IncPlayCount(userID, itemType string, itemID string, ts time.Time) error - SetStar(starred bool, userID, itemType string, ids ...string) error - SetRating(rating int, userID, itemType string, itemID string) error + Delete(itemType string, itemID ...string) error + IncPlayCount(itemType, itemID string, ts time.Time) error + SetStar(starred bool, itemType string, ids ...string) error + SetRating(rating int, itemType, itemID string) error } diff --git a/model/artist.go b/model/artist.go index 3a8c94fa..07877e09 100644 --- a/model/artist.go +++ b/model/artist.go @@ -28,7 +28,7 @@ type ArtistRepository interface { Exists(id string) (bool, error) Put(m *Artist) error Get(id string) (*Artist, error) - GetStarred(userId string, options ...QueryOptions) (Artists, error) + GetStarred(options ...QueryOptions) (Artists, error) Search(q string, offset int, size int) (Artists, error) Refresh(ids ...string) error GetIndex() (ArtistIndexes, error) diff --git a/model/mediafile.go b/model/mediafile.go index 9da98c72..1410d69b 100644 --- a/model/mediafile.go +++ b/model/mediafile.go @@ -11,9 +11,9 @@ type MediaFile struct { Title string `json:"title"` Album string `json:"album"` Artist string `json:"artist"` - ArtistID string `json:"artistId"` + ArtistID string `json:"artistId" orm:"pk;column(artist_id)"` AlbumArtist string `json:"albumArtist"` - AlbumID string `json:"albumId"` + AlbumID string `json:"albumId" orm:"pk;column(album_id)"` HasCoverArt bool `json:"hasCoverArt"` TrackNumber int `json:"trackNumber"` DiscNumber int `json:"discNumber"` @@ -48,8 +48,7 @@ type MediaFileRepository interface { Get(id string) (*MediaFile, error) FindByAlbum(albumId string) (MediaFiles, error) FindByPath(path string) (MediaFiles, error) - // TODO Remove userId - GetStarred(userId string, options ...QueryOptions) (MediaFiles, error) + GetStarred(options ...QueryOptions) (MediaFiles, error) GetRandom(options ...QueryOptions) (MediaFiles, error) Search(q string, offset int, size int) (MediaFiles, error) Delete(id string) error diff --git a/persistence/album_repository.go b/persistence/album_repository.go index 206d7eff..9148ce67 100644 --- a/persistence/album_repository.go +++ b/persistence/album_repository.go @@ -101,7 +101,7 @@ func (r *albumRepository) PurgeEmpty() error { return nil } -func (r *albumRepository) GetStarred(userId string, options ...model.QueryOptions) (model.Albums, error) { +func (r *albumRepository) GetStarred(options ...model.QueryOptions) (model.Albums, error) { sq := r.selectAlbum(options...).Where("starred = true") var starred model.Albums err := r.queryAll(sq, &starred) diff --git a/persistence/album_repository_test.go b/persistence/album_repository_test.go index 8e088181..f1202a38 100644 --- a/persistence/album_repository_test.go +++ b/persistence/album_repository_test.go @@ -57,7 +57,7 @@ var _ = Describe("AlbumRepository", func() { Describe("GetStarred", func() { It("returns all starred records", func() { - Expect(repo.GetStarred("userid", model.QueryOptions{})).To(Equal(model.Albums{ + Expect(repo.GetStarred(model.QueryOptions{})).To(Equal(model.Albums{ albumRadioactivity, })) }) diff --git a/persistence/annotation_repository.go b/persistence/annotation_repository.go index 84fdc66f..e60acc79 100644 --- a/persistence/annotation_repository.go +++ b/persistence/annotation_repository.go @@ -10,24 +10,6 @@ import ( "github.com/google/uuid" ) -type annotation struct { - AnnotationID string `orm:"pk;column(ann_id)"` - UserID string `orm:"column(user_id)"` - ItemID string `orm:"column(item_id)"` - ItemType string `orm:"column(item_type)"` - PlayCount int `orm:"column(play_count);index;null"` - PlayDate time.Time `orm:"column(play_date);index;null"` - Rating int `orm:"null"` - Starred bool `orm:"index"` - StarredAt time.Time `orm:"column(starred_at);null"` -} - -func (u *annotation) TableUnique() [][]string { - return [][]string{ - {"UserID", "ItemID", "ItemType"}, - } -} - type annotationRepository struct { sqlRepository } @@ -40,144 +22,70 @@ func NewAnnotationRepository(ctx context.Context, o orm.Ormer) model.AnnotationR return r } -func (r *annotationRepository) Get(userID, itemType string, itemID string) (*model.Annotation, error) { - q := Select("*").From(r.tableName).Where(And{ - Eq{"user_id": userId(r.ctx)}, - Eq{"item_type": itemType}, - Eq{"item_id": itemID}, - }) - var ann annotation - err := r.queryOne(q, &ann) - if err == model.ErrNotFound { - return nil, nil +func (r *annotationRepository) upsert(values map[string]interface{}, itemType string, itemIDs ...string) error { + upd := Update(r.tableName).Where(r.getId(itemType, itemIDs...)) + for f, v := range values { + upd = upd.Set(f, v) } - resp := model.Annotation(ann) - return &resp, nil -} - -func (r *annotationRepository) GetMap(userID, itemType string, itemIDs []string) (model.AnnotationMap, error) { - if len(itemIDs) == 0 { - return nil, nil - } - q := Select("*").From(r.tableName).Where(And{ - Eq{"user_id": userId(r.ctx)}, - Eq{"item_type": itemType}, - Eq{"item_id": itemIDs}, - }) - var res []annotation - err := r.queryAll(q, &res) - if err != nil { - return nil, err - } - - m := make(model.AnnotationMap) - for _, a := range res { - m[a.ItemID] = model.Annotation(a) - } - return m, nil -} - -func (r *annotationRepository) GetAll(userID, itemType string, options ...model.QueryOptions) ([]model.Annotation, error) { - q := Select("*").From(r.tableName).Where(And{ - Eq{"user_id": userId(r.ctx)}, - Eq{"item_type": itemType}, - }) - var res []annotation - err := r.queryAll(q, &res) - if err != nil { - return nil, err - } - - all := make([]model.Annotation, len(res)) - for i, a := range res { - all[i] = model.Annotation(a) - } - return all, err -} - -func (r *annotationRepository) new(userID, itemType string, itemID string) *annotation { - id, _ := uuid.NewRandom() - return &annotation{ - AnnotationID: id.String(), - UserID: userID, - ItemID: itemID, - ItemType: itemType, - } -} - -func (r *annotationRepository) IncPlayCount(userID, itemType string, itemID string, ts time.Time) error { - uid := userId(r.ctx) - q := Update(r.tableName). - Set("play_count", Expr("play_count + 1")). - Set("play_date", ts). - Where(And{ - Eq{"user_id": uid}, - Eq{"item_type": itemType}, - Eq{"item_id": itemID}, - }) - c, err := r.executeSQL(q) + c, err := r.executeSQL(upd) if c == 0 || err == orm.ErrNoRows { - ann := r.new(uid, itemType, itemID) - ann.PlayCount = 1 - ann.PlayDate = ts - _, err = r.ormer.Insert(ann) - } - return err -} - -func (r *annotationRepository) SetStar(starred bool, userID, itemType string, ids ...string) error { - uid := userId(r.ctx) - var starredAt time.Time - if starred { - starredAt = time.Now() - } - q := Update(r.tableName). - Set("starred", starred). - Set("starred_at", starredAt). - Where(And{ - Eq{"user_id": uid}, - Eq{"item_type": itemType}, - Eq{"item_id": ids}, - }) - c, err := r.executeSQL(q) - if c == 0 || err == orm.ErrNoRows { - for _, id := range ids { - ann := r.new(uid, itemType, id) - ann.Starred = starred - ann.StarredAt = starredAt - _, err = r.ormer.Insert(ann) + for _, itemID := range itemIDs { + id, _ := uuid.NewRandom() + values["ann_id"] = id.String() + values["user_id"] = userId(r.ctx) + values["item_type"] = itemType + values["item_id"] = itemID + ins := Insert(r.tableName).SetMap(values) + _, err = r.executeSQL(ins) if err != nil { - if err.Error() != "LastInsertId is not supported by this driver" { - return err - } + return err } } } - return nil + return err } -func (r *annotationRepository) SetRating(rating int, userID, itemType string, itemID string) error { - uid := userId(r.ctx) - q := Update(r.tableName). - Set("rating", rating). - Where(And{ - Eq{"user_id": uid}, - Eq{"item_type": itemType}, - Eq{"item_id": itemID}, - }) - c, err := r.executeSQL(q) +func (r *annotationRepository) IncPlayCount(itemType, itemID string, ts time.Time) error { + upd := Update(r.tableName).Where(r.getId(itemType, itemID)). + Set("play_count", Expr("play_count+1")). + Set("play_date", ts) + c, err := r.executeSQL(upd) + if c == 0 || err == orm.ErrNoRows { - ann := r.new(uid, itemType, itemID) - ann.Rating = rating - _, err = r.ormer.Insert(ann) + id, _ := uuid.NewRandom() + values := map[string]interface{}{} + values["ann_id"] = id.String() + values["user_id"] = userId(r.ctx) + values["item_type"] = itemType + values["item_id"] = itemID + values["play_count"] = 1 + values["play_date"] = ts + ins := Insert(r.tableName).SetMap(values) + _, err = r.executeSQL(ins) + if err != nil { + return err + } } return err } -func (r *annotationRepository) Delete(userID, itemType string, ids ...string) error { - return r.delete(And{ +func (r *annotationRepository) getId(itemType string, itemID ...string) And { + return And{ Eq{"user_id": userId(r.ctx)}, Eq{"item_type": itemType}, - Eq{"item_id": ids}, - }) + Eq{"item_id": itemID}, + } +} + +func (r *annotationRepository) SetStar(starred bool, itemType string, ids ...string) error { + starredAt := time.Now() + return r.upsert(map[string]interface{}{"starred": starred, "starred_at": starredAt}, itemType, ids...) +} + +func (r *annotationRepository) SetRating(rating int, itemType, itemID string) error { + return r.upsert(map[string]interface{}{"rating": rating}, itemType, itemID) +} + +func (r *annotationRepository) Delete(itemType string, itemIDs ...string) error { + return r.delete(r.getId(itemType, itemIDs...)) } diff --git a/persistence/artist_repository.go b/persistence/artist_repository.go index 90c01e7f..af757d38 100644 --- a/persistence/artist_repository.go +++ b/persistence/artist_repository.go @@ -108,7 +108,7 @@ func (r *artistRepository) Refresh(ids ...string) error { return nil } -func (r *artistRepository) GetStarred(userId string, options ...model.QueryOptions) (model.Artists, error) { +func (r *artistRepository) GetStarred(options ...model.QueryOptions) (model.Artists, error) { return nil, nil // TODO } @@ -121,7 +121,7 @@ func (r *artistRepository) Search(q string, offset int, size int) (model.Artists } func (r *artistRepository) Count(options ...rest.QueryOptions) (int64, error) { - return r.CountAll(r.parseRestOptions(options...)) + return r.CountAll(r.parseRestOptions(options...)) // TODO Don't apply pagination! } func (r *artistRepository) Read(id string) (interface{}, error) { diff --git a/persistence/mediafile_repository.go b/persistence/mediafile_repository.go index d772bbda..d1b92d6e 100644 --- a/persistence/mediafile_repository.go +++ b/persistence/mediafile_repository.go @@ -78,7 +78,7 @@ func (r mediaFileRepository) FindByPath(path string) (model.MediaFiles, error) { return res, err } -func (r mediaFileRepository) GetStarred(userId string, options ...model.QueryOptions) (model.MediaFiles, error) { +func (r mediaFileRepository) GetStarred(options ...model.QueryOptions) (model.MediaFiles, error) { sq := r.selectMediaFile(options...).Where("starred = true") var starred model.MediaFiles err := r.queryAll(sq, &starred) diff --git a/persistence/mediafile_repository_test.go b/persistence/mediafile_repository_test.go index 4861995d..922092da 100644 --- a/persistence/mediafile_repository_test.go +++ b/persistence/mediafile_repository_test.go @@ -44,7 +44,7 @@ var _ = Describe("MediaRepository", func() { }) It("returns empty array when no tracks are found", func() { - Expect(mr.FindByAlbum("67")).To(Equal(model.MediaFiles{})) + Expect(mr.FindByAlbum("67")).To(Equal(model.MediaFiles(nil))) }) It("finds tracks by path", func() { @@ -54,7 +54,7 @@ var _ = Describe("MediaRepository", func() { }) It("returns starred tracks", func() { - Expect(mr.GetStarred("userid")).To(Equal(model.MediaFiles{ + Expect(mr.GetStarred()).To(Equal(model.MediaFiles{ songComeTogether, })) }) diff --git a/persistence/persistence.go b/persistence/persistence.go index 009a204b..ce56a378 100644 --- a/persistence/persistence.go +++ b/persistence/persistence.go @@ -125,17 +125,8 @@ func (db *NewSQLStore) getOrmer() orm.Ormer { } func initORM(dbPath string) error { - //verbose := conf.Server.LogLevel == "trace" - //orm.Debug = verbose if strings.Contains(dbPath, "postgres") { driver = "postgres" } - err := orm.RegisterDataBase("default", driver, dbPath) - if err != nil { - return err - } - // TODO Remove all RegisterModels (i.e. don't use orm.Insert/Update) - orm.RegisterModel(new(annotation)) - - return nil + return orm.RegisterDataBase("default", driver, dbPath) } diff --git a/persistence/persistence_suite_test.go b/persistence/persistence_suite_test.go index 8c15d40f..52b38f65 100644 --- a/persistence/persistence_suite_test.go +++ b/persistence/persistence_suite_test.go @@ -5,6 +5,7 @@ import ( "strings" "testing" + "github.com/Masterminds/squirrel" "github.com/astaxie/beego/orm" "github.com/deluan/navidrome/conf" "github.com/deluan/navidrome/db" @@ -52,8 +53,8 @@ var testSongs = model.MediaFiles{ songAntenna, } -var annAlbumRadioactivity = model.Annotation{AnnotationID: "1", UserID: "userid", ItemType: model.AlbumItemType, ItemID: "3", Starred: true} -var annSongComeTogether = model.Annotation{AnnotationID: "2", UserID: "userid", ItemType: model.MediaItemType, ItemID: "2", Starred: true} +var annAlbumRadioactivity = model.Annotation{AnnID: "1", UserID: "userid", ItemType: model.AlbumItemType, ItemID: "3", Starred: true} +var annSongComeTogether = model.Annotation{AnnID: "2", UserID: "userid", ItemType: model.MediaItemType, ItemID: "2", Starred: true} var testAnnotations = []model.Annotation{ annAlbumRadioactivity, annSongComeTogether, @@ -81,7 +82,6 @@ var _ = Describe("Initialize test DB", func() { BeforeSuite(func() { o := orm.NewOrm() mr := NewMediaFileRepository(nil, o) - for _, s := range testSongs { err := mr.Put(&s) if err != nil { @@ -90,8 +90,13 @@ var _ = Describe("Initialize test DB", func() { } for _, a := range testAnnotations { - ann := annotation(a) - _, err := o.Insert(&ann) + values, _ := toSqlArgs(a) + ins := squirrel.Insert("annotation").SetMap(values) + query, args, err := ins.ToSql() + if err != nil { + panic(err) + } + _, err = o.Raw(query, args...).Exec() if err != nil { panic(err) } diff --git a/persistence/sql_repository.go b/persistence/sql_repository.go index 51ea6a4d..900f58c6 100644 --- a/persistence/sql_repository.go +++ b/persistence/sql_repository.go @@ -55,9 +55,9 @@ func (r *sqlRepository) applyOptions(sq SelectBuilder, options ...model.QueryOpt } if options[0].Sort != "" { if options[0].Order == "desc" { - sq = sq.OrderBy(options[0].Sort + " desc") + sq = sq.OrderBy(toSnakeCase(options[0].Sort + " desc")) } else { - sq = sq.OrderBy(options[0].Sort) + sq = sq.OrderBy(toSnakeCase(options[0].Sort)) } } if options[0].Filters != nil { @@ -151,7 +151,7 @@ func (r sqlRepository) toSql(sq Sqlizer) (string, []interface{}, error) { func (r sqlRepository) parseRestOptions(options ...rest.QueryOptions) model.QueryOptions { qo := model.QueryOptions{} if len(options) > 0 { - qo.Sort = toSnakeCase(options[0].Sort) + qo.Sort = options[0].Sort qo.Order = options[0].Order qo.Max = options[0].Max qo.Offset = options[0].Offset