- Tags déclaratifs (max 10, 30 chars) via track_tags + tags - Genres normalisés (max 3) via track_genres + taxonomy - GET /api/v1/discover/genre/:genre, tag/:tag (browse chrono) - POST/DELETE follow genre/tag - Section feed "Nouvelles sorties dans vos genres" - Track update: SyncTrackTags, SyncTrackGenres via discover service - Frontend: discoverService, FeedPage by_genres, DiscoverPage - Migration 126_tags_genres_discover - MSW handlers for discover
71 lines
2 KiB
Go
71 lines
2 KiB
Go
package feed
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
apperrors "veza-backend-api/internal/errors"
|
|
"veza-backend-api/internal/handlers"
|
|
)
|
|
|
|
// Handler handles the chronological tracks feed (v0.10.0 F210)
|
|
type Handler struct {
|
|
service *Service
|
|
}
|
|
|
|
// NewHandler creates a new feed handler
|
|
func NewHandler(service *Service) *Handler {
|
|
return &Handler{service: service}
|
|
}
|
|
|
|
// GetTracksFeed returns tracks from followed users, chronological, cursor-paginated.
|
|
// v0.10.1 F355: Adds by_genres section "Nouvelles sorties dans vos genres" when user follows genres.
|
|
// GET /api/v1/feed?cursor=...&limit=20&by_genres_cursor=...&by_genres_limit=10
|
|
// Requires authentication.
|
|
func (h *Handler) GetTracksFeed(c *gin.Context) {
|
|
userID, ok := handlers.GetUserIDUUID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
limitStr := c.DefaultQuery("limit", "20")
|
|
limit, err := strconv.Atoi(limitStr)
|
|
if err != nil || limit < 1 || limit > 50 {
|
|
handlers.RespondWithAppError(c, apperrors.NewValidationError("limit must be between 1 and 50"))
|
|
return
|
|
}
|
|
|
|
cursor := c.Query("cursor")
|
|
byGenresCursor := c.Query("by_genres_cursor")
|
|
byGenresLimit := 10
|
|
if l, err := strconv.Atoi(c.DefaultQuery("by_genres_limit", "10")); err == nil && l > 0 && l <= 50 {
|
|
byGenresLimit = l
|
|
}
|
|
|
|
tracks, nextCursor, err := h.service.GetTracksFeed(c.Request.Context(), userID, limit, cursor)
|
|
if err != nil {
|
|
handlers.RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to get feed", err))
|
|
return
|
|
}
|
|
|
|
resp := gin.H{"items": tracks}
|
|
if nextCursor != "" {
|
|
resp["next_cursor"] = nextCursor
|
|
}
|
|
|
|
// v0.10.1 F355: Section "Nouvelles sorties dans vos genres"
|
|
if ds := h.service.GetDiscoverService(); ds != nil {
|
|
byGenresTracks, byGenresNext, err := ds.GetTracksFromFollowedGenres(c.Request.Context(), userID, byGenresLimit, byGenresCursor)
|
|
if err == nil {
|
|
section := gin.H{"items": byGenresTracks}
|
|
if byGenresNext != "" {
|
|
section["next_cursor"] = byGenresNext
|
|
}
|
|
resp["by_genres"] = section
|
|
}
|
|
}
|
|
|
|
handlers.RespondSuccess(c, http.StatusOK, resp)
|
|
}
|