[BE-API-020] be-api: Implement HLS stream info endpoint

- Added GetStreamInfo method to HLSService
- Added GetStreamInfo handler in HLSHandler
- Standardized GetStreamStatus handler to use RespondSuccess/RespondWithAppError
- Added routes: GET /tracks/:id/hls/info and GET /tracks/:id/hls/status
- GetStreamInfo returns general stream information
- GetStreamStatus returns status with processing info if applicable
- Handlers use standard API response format

Phase: PHASE-2
Priority: P1
Progress: 29/267 (10.9%)
This commit is contained in:
senke 2025-12-24 11:32:50 +01:00
parent 99edce5029
commit f75ebb5e20
4 changed files with 85 additions and 8 deletions

View file

@ -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": [
{

View file

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

View file

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

View file

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