fix: polish player controls and remove fake track stats

This commit is contained in:
2026-04-03 02:27:24 +03:00
parent d7e21956db
commit db6e2818c1
8 changed files with 253 additions and 39 deletions

View File

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

View File

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