fix: polish player controls and remove fake track stats
This commit is contained in:
@@ -171,11 +171,17 @@ func (a app) me(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (a app) home(w http.ResponseWriter, r *http.Request) {
|
||||
user := currentUserFromContext(r)
|
||||
home, err := a.library.Home(r.Context())
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to load home"})
|
||||
return
|
||||
}
|
||||
home.RecentTracks, err = a.library.PopulateTrackStats(r.Context(), user.ID, home.RecentTracks)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to load home"})
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, home)
|
||||
}
|
||||
|
||||
@@ -195,6 +201,11 @@ func (a app) recentlyPlayed(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to load recent tracks"})
|
||||
return
|
||||
}
|
||||
items, err = a.library.PopulateTrackStats(r.Context(), user.ID, items)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to load recent tracks"})
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]any{"items": items})
|
||||
}
|
||||
|
||||
@@ -221,6 +232,7 @@ func (a app) albums(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (a app) albumByID(w http.ResponseWriter, r *http.Request) {
|
||||
user := currentUserFromContext(r)
|
||||
item, err := a.library.AlbumByID(r.Context(), chi.URLParam(r, "id"))
|
||||
if err != nil {
|
||||
if errors.Is(err, library.ErrNotFound) {
|
||||
@@ -230,19 +242,31 @@ func (a app) albumByID(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to load album"})
|
||||
return
|
||||
}
|
||||
item.Tracks, err = a.library.PopulateTrackStats(r.Context(), user.ID, item.Tracks)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to load album"})
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, item)
|
||||
}
|
||||
|
||||
func (a app) tracks(w http.ResponseWriter, r *http.Request) {
|
||||
user := currentUserFromContext(r)
|
||||
items, err := a.library.Tracks(r.Context(), 200)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to load tracks"})
|
||||
return
|
||||
}
|
||||
items, err = a.library.PopulateTrackStats(r.Context(), user.ID, items)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to load tracks"})
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]any{"items": items})
|
||||
}
|
||||
|
||||
func (a app) trackByID(w http.ResponseWriter, r *http.Request) {
|
||||
user := currentUserFromContext(r)
|
||||
item, err := a.library.TrackByID(r.Context(), chi.URLParam(r, "id"))
|
||||
if err != nil {
|
||||
if errors.Is(err, library.ErrNotFound) {
|
||||
@@ -252,6 +276,14 @@ func (a app) trackByID(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to load track"})
|
||||
return
|
||||
}
|
||||
enriched, err := a.library.PopulateTrackStats(r.Context(), user.ID, []library.Track{item})
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to load track"})
|
||||
return
|
||||
}
|
||||
if len(enriched) > 0 {
|
||||
item = enriched[0]
|
||||
}
|
||||
writeJSON(w, http.StatusOK, item)
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ type Track struct {
|
||||
FilePath string `json:"filePath"`
|
||||
ContentType string `json:"contentType"`
|
||||
CoverArtID string `json:"coverArtId"`
|
||||
PlayCount int `json:"playCount"`
|
||||
LastPlayedAt string `json:"lastPlayedAt"`
|
||||
}
|
||||
|
||||
type HomePayload struct {
|
||||
@@ -491,6 +493,60 @@ func (s *Service) RecordPlayEvent(ctx context.Context, userID, trackID, eventTyp
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) PopulateTrackStats(ctx context.Context, userID string, tracks []Track) ([]Track, error) {
|
||||
if userID == "" || len(tracks) == 0 {
|
||||
return tracks, nil
|
||||
}
|
||||
|
||||
placeholders := make([]string, 0, len(tracks))
|
||||
args := make([]any, 0, len(tracks)+1)
|
||||
args = append(args, userID)
|
||||
for _, track := range tracks {
|
||||
placeholders = append(placeholders, "?")
|
||||
args = append(args, track.ID)
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(
|
||||
`SELECT track_id, COUNT(*) AS play_count, COALESCE(MAX(played_at), '')
|
||||
FROM play_history
|
||||
WHERE user_id = ? AND track_id IN (%s)
|
||||
GROUP BY track_id`,
|
||||
strings.Join(placeholders, ","),
|
||||
)
|
||||
|
||||
rows, err := s.db.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query track stats: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
type stats struct {
|
||||
playCount int
|
||||
lastPlayedAt string
|
||||
}
|
||||
byTrackID := map[string]stats{}
|
||||
for rows.Next() {
|
||||
var trackID string
|
||||
var item stats
|
||||
if err := rows.Scan(&trackID, &item.playCount, &item.lastPlayedAt); err != nil {
|
||||
return nil, fmt.Errorf("scan track stats: %w", err)
|
||||
}
|
||||
byTrackID[trackID] = item
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("iterate track stats: %w", err)
|
||||
}
|
||||
|
||||
for index := range tracks {
|
||||
if item, ok := byTrackID[tracks[index].ID]; ok {
|
||||
tracks[index].PlayCount = item.playCount
|
||||
tracks[index].LastPlayedAt = item.lastPlayedAt
|
||||
}
|
||||
}
|
||||
|
||||
return tracks, nil
|
||||
}
|
||||
|
||||
func (s *Service) albumsByArtistID(ctx context.Context, artistID string) ([]Album, error) {
|
||||
rows, err := s.db.QueryContext(
|
||||
ctx,
|
||||
|
||||
Reference in New Issue
Block a user