package subsonic import ( "git.noroutes.net/NoRoutes/vibetunes/internal/database" ) func init() { register("star", handleStar) register("unstar", handleUnstar) register("setRating", handleSetRating) register("scrobble", handleScrobble) } func handleStar(rc *Context) *apiError { return applyStarUnstar(rc, true) } func handleUnstar(rc *Context) *apiError { return applyStarUnstar(rc, false) } func applyStarUnstar(rc *Context, star bool) *apiError { db := rc.Server.DB ids := rc.Params.GetAll("id") albumIDs := rc.Params.GetAll("albumId") artistIDs := rc.Params.GetAll("artistId") if len(ids) == 0 && len(albumIDs) == 0 && len(artistIDs) == 0 { return errMissingParameter("id, albumId, or artistId") } apply := func(itemType, itemID string) error { if star { return database.Star(db, rc.User.ID, itemType, itemID) } return database.Unstar(db, rc.User.ID, itemType, itemID) } for _, id := range ids { if err := apply("song", id); err != nil { return newError(ErrGeneric, err.Error()) } } for _, id := range albumIDs { if err := apply("album", id); err != nil { return newError(ErrGeneric, err.Error()) } } for _, id := range artistIDs { if err := apply("artist", id); err != nil { return newError(ErrGeneric, err.Error()) } } rc.Response = newResponse() return nil } func handleSetRating(rc *Context) *apiError { id, aerr := rc.Params.Required("id") if aerr != nil { return aerr } rating, rerr := rc.Params.RequiredInt("rating") if rerr != nil { return rerr } if rating < 0 || rating > 5 { return newError(ErrGeneric, "rating must be between 0 and 5") } itemType := "song" db := rc.Server.DB if _, err := database.GetSong(db, id); err != nil { if _, err := database.GetAlbum(db, id); err == nil { itemType = "album" } else if _, err := database.GetArtist(db, id); err == nil { itemType = "artist" } else { return errNotFound("Item") } } if err := database.SetRating(db, rc.User.ID, itemType, id, rating); err != nil { return newError(ErrGeneric, err.Error()) } rc.Response = newResponse() return nil } // handleScrobble records playback history and, when submission=true (the // default), a scrobble record for potential Last.fm-style submission // later. VibeSonic doesn't itself talk to Last.fm in Phase 2; it stores // scrobbles so a future integration (or the user's own reporting) has // real data rather than inventing play counts elsewhere. func handleScrobble(rc *Context) *apiError { ids := rc.Params.GetAll("id") if len(ids) == 0 { return errMissingParameter("id") } submission := rc.Params.Bool("submission", true) client := rc.Params.Get("c") db := rc.Server.DB for _, id := range ids { song, err := database.GetSong(db, id) if err != nil { continue // unknown song id: skip rather than fail the whole batch } if submission { if err := database.RecordPlayback(db, rc.User.ID, song.ID, 0); err != nil { return newError(ErrGeneric, err.Error()) } if err := database.RecordScrobble(db, rc.User.ID, song.ID, client, false); err != nil { return newError(ErrGeneric, err.Error()) } } else { if err := database.SetNowPlaying(db, rc.User.ID, song.ID, client, 0); err != nil { return newError(ErrGeneric, err.Error()) } } } rc.Response = newResponse() return nil }