diff --git a/VEZA_COMPLETE_MVP_TODOLIST.json b/VEZA_COMPLETE_MVP_TODOLIST.json index 282322a90..8df1f8f06 100644 --- a/VEZA_COMPLETE_MVP_TODOLIST.json +++ b/VEZA_COMPLETE_MVP_TODOLIST.json @@ -1997,7 +1997,19 @@ "description": "GET /api/v1/tracks/:id/hls/info and GET /api/v1/tracks/:id/hls/status", "owner": "backend", "estimated_hours": 4, - "status": "todo", + "status": "completed", + "completion": { + "completed_at": "2025-12-23T10:00:30Z", + "actual_hours": 1.0, + "commits": [], + "files_changed": [ + "veza-backend-api/internal/services/hls_service.go", + "veza-backend-api/internal/handlers/hls_handler.go", + "veza-backend-api/internal/api/router.go" + ], + "notes": "Added GetStreamInfo method to HLSService to return general stream information. Added GetStreamInfo handler in HLSHandler. Standardized GetStreamStatus handler to use RespondSuccess and RespondWithAppError. Added routes: GET /tracks/:id/hls/info and GET /tracks/:id/hls/status (public). GetStreamInfo returns track_id, playlist_url, bitrates, segments_count, created_at, updated_at. GetStreamStatus returns status with additional processing info if applicable. Handlers use standard API response format.", + "issues_encountered": [] + }, "files_involved": [], "implementation_steps": [ { diff --git a/veza-backend-api/internal/api/router.go b/veza-backend-api/internal/api/router.go index b0aced0a0..2786cb131 100644 --- a/veza-backend-api/internal/api/router.go +++ b/veza-backend-api/internal/api/router.go @@ -547,6 +547,17 @@ func (r *APIRouter) setupTrackRoutes(router *gin.RouterGroup) { // Analytics protected.POST("/:id/play", trackHandler.RecordPlay) // BE-API-019: Record track play event endpoint + + // HLS Streaming + // BE-API-020: HLS stream info and status endpoints + hlsOutputDir := r.config.UploadDir + if hlsOutputDir == "" { + hlsOutputDir = "uploads/tracks" + } + hlsService := services.NewHLSService(r.db.GormDB, hlsOutputDir, r.logger) + hlsHandler := handlers.NewHLSHandler(hlsService) + tracks.GET("/:id/hls/info", hlsHandler.GetStreamInfo) // BE-API-020: Get HLS stream info + tracks.GET("/:id/hls/status", hlsHandler.GetStreamStatus) // BE-API-020: Get HLS stream status } } diff --git a/veza-backend-api/internal/handlers/hls_handler.go b/veza-backend-api/internal/handlers/hls_handler.go index 20ca9f873..8e13ffd7b 100644 --- a/veza-backend-api/internal/handlers/hls_handler.go +++ b/veza-backend-api/internal/handlers/hls_handler.go @@ -7,6 +7,7 @@ import ( // "strconv" // Removed this import + apperrors "veza-backend-api/internal/errors" "veza-backend-api/internal/services" "github.com/gin-gonic/gin" @@ -83,21 +84,50 @@ func (h *HLSHandler) ServeSegment(c *gin.Context) { c.File(segmentPath) } -// GetStreamStatus retourne le statut d'un stream HLS pour un track -func (h *HLSHandler) GetStreamStatus(c *gin.Context) { - trackID, err := uuid.Parse(c.Param("id")) // Changed to uuid.Parse +// GetStreamInfo retourne les informations générales d'un stream HLS pour un track +// GET /api/v1/tracks/:id/hls/info +// BE-API-020: Implement HLS stream info endpoint +func (h *HLSHandler) GetStreamInfo(c *gin.Context) { + trackID, err := uuid.Parse(c.Param("id")) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "invalid track id"}) + RespondWithAppError(c, apperrors.NewValidationError("invalid track id")) + return + } + + info, err := h.hlsService.GetStreamInfo(c.Request.Context(), trackID) + if err != nil { + if err.Error()[:20] == "HLS stream not found" { + RespondWithAppError(c, apperrors.NewNotFoundError("HLS stream")) + return + } + RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "failed to get stream info", err)) + return + } + + RespondSuccess(c, http.StatusOK, info) +} + +// GetStreamStatus retourne le statut d'un stream HLS pour un track +// GET /api/v1/tracks/:id/hls/status +// BE-API-020: Implement HLS stream info endpoint +func (h *HLSHandler) GetStreamStatus(c *gin.Context) { + trackID, err := uuid.Parse(c.Param("id")) + if err != nil { + RespondWithAppError(c, apperrors.NewValidationError("invalid track id")) return } status, err := h.hlsService.GetStreamStatus(c.Request.Context(), trackID) if err != nil { - c.JSON(http.StatusNotFound, gin.H{"error": "stream not found"}) + if err.Error()[:20] == "HLS stream not found" { + RespondWithAppError(c, apperrors.NewNotFoundError("HLS stream")) + return + } + RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "failed to get stream status", err)) return } - c.JSON(http.StatusOK, status) + RespondSuccess(c, http.StatusOK, status) } // TriggerTranscode déclenche le transcodage HLS d'un track via la queue (T0343) diff --git a/veza-backend-api/internal/services/hls_service.go b/veza-backend-api/internal/services/hls_service.go index cabd48b28..c9d489a0e 100644 --- a/veza-backend-api/internal/services/hls_service.go +++ b/veza-backend-api/internal/services/hls_service.go @@ -7,9 +7,10 @@ import ( "path/filepath" "strings" + "veza-backend-api/internal/models" + "github.com/google/uuid" "gorm.io/gorm" - "veza-backend-api/internal/models" "go.uber.org/zap" ) @@ -293,3 +294,26 @@ func (s *HLSService) GetStreamStatus(ctx context.Context, trackID uuid.UUID) (ma return status, nil } + +// GetStreamInfo récupère les informations générales d'un stream HLS pour un track +// BE-API-020: Implement HLS stream info endpoint +func (s *HLSService) GetStreamInfo(ctx context.Context, trackID uuid.UUID) (map[string]interface{}, error) { + var stream models.HLSStream + if err := s.db.WithContext(ctx).Where("track_id = ?", trackID).First(&stream).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, fmt.Errorf("HLS stream not found for track %s", trackID) + } + return nil, fmt.Errorf("failed to query HLS stream: %w", err) + } + + info := map[string]interface{}{ + "track_id": stream.TrackID, + "playlist_url": stream.PlaylistURL, + "bitrates": stream.Bitrates, + "segments_count": stream.SegmentsCount, + "created_at": stream.CreatedAt, + "updated_at": stream.UpdatedAt, + } + + return info, nil +}