refactor: remove annotation handling from engine

This commit is contained in:
Deluan 2020-01-30 22:07:02 -05:00 committed by Deluan Quintão
parent 67ed830a68
commit 72d9ddf532
20 changed files with 151 additions and 386 deletions

View File

@ -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
}

View File

@ -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 ""
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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,
}))
})

View File

@ -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...))
}

View File

@ -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) {

View File

@ -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)

View File

@ -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,
}))
})

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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