package musichandler import ( "strconv" "github.com/gin-gonic/gin" musiclogic "spark-server/internal/module/music/logic" "spark-server/internal/pkg/errs" "spark-server/internal/transport/http/respond" ) type Handler struct { svc *musiclogic.Service } func New(svc *musiclogic.Service) *Handler { return &Handler{svc: svc} } // Playlist GET /v1/music/playlist — public curated list + category tree. // Query categoryId>0 filters tracks belonging to that category or its descendants. // Query requireMember=true|false filters member-only / free tracks. func (h *Handler) Playlist(c *gin.Context) { if h == nil || h.svc == nil || !h.svc.Ready() { respond.JSONError(c, errs.ErrAvailable) return } q, err := parsePlaylistQuery(c) if err != nil { respond.JSONError(c, err) return } out, err := h.svc.Playlist(c.Request.Context(), q) if err != nil { respond.JSONError(c, err) return } respond.JSON(c, out) } // PlaylistGrouped GET /v1/music/playlist/grouped — leaf categories with nested items. func (h *Handler) PlaylistGrouped(c *gin.Context) { if h == nil || h.svc == nil || !h.svc.Ready() { respond.JSONError(c, errs.ErrAvailable) return } var parentID int64 if raw := c.Query("parentId"); raw != "" { n, err := strconv.ParseInt(raw, 10, 64) if err != nil || n < 0 { respond.JSONError(c, errs.ErrInvalidArgument.WithMsg("parentId 无效")) return } parentID = n } out, err := h.svc.PlaylistGrouped(c.Request.Context(), parentID) if err != nil { respond.JSONError(c, err) return } respond.JSON(c, out) } // GetTrack GET /v1/music/tracks/:id — single enabled track (public). func (h *Handler) GetTrack(c *gin.Context) { if h == nil || h.svc == nil || !h.svc.Ready() { respond.JSONError(c, errs.ErrAvailable) return } id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil || id <= 0 { respond.JSONError(c, errs.ErrInvalidArgument.WithMsg("id 无效")) return } out, err := h.svc.GetPublic(c.Request.Context(), id) if err != nil { respond.JSONError(c, err) return } respond.JSON(c, out) } // Categories GET /v1/music/categories — enabled category tree. func (h *Handler) Categories(c *gin.Context) { if h == nil || h.svc == nil || !h.svc.Ready() { respond.JSONError(c, errs.ErrAvailable) return } out, err := h.svc.CategoryTree(c.Request.Context(), true) if err != nil { respond.JSONError(c, err) return } respond.JSON(c, gin.H{"categories": out}) } // Settings GET /v1/music/settings — global member preview seconds only. func (h *Handler) Settings(c *gin.Context) { if h == nil || h.svc == nil || !h.svc.Ready() { respond.JSONError(c, errs.ErrAvailable) return } out, err := h.svc.Settings(c.Request.Context()) if err != nil { respond.JSONError(c, err) return } respond.JSON(c, out) } func parsePlaylistQuery(c *gin.Context) (musiclogic.PlaylistQuery, error) { var q musiclogic.PlaylistQuery if raw := c.Query("categoryId"); raw != "" { n, err := strconv.ParseInt(raw, 10, 64) if err != nil || n < 0 { return q, errs.ErrInvalidArgument.WithMsg("categoryId 无效") } q.CategoryID = n } if raw := c.Query("requireMember"); raw != "" { switch raw { case "true", "1": v := true q.RequireMember = &v case "false", "0": v := false q.RequireMember = &v default: return q, errs.ErrInvalidArgument.WithMsg("requireMember 须为 true|false") } } return q, nil }