package api import ( "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/redis/go-redis/v9" "go.uber.org/zap" discovercore "veza-backend-api/internal/core/discover" trackcore "veza-backend-api/internal/core/track" "veza-backend-api/internal/handlers" "veza-backend-api/internal/services" ) // setupTrackRoutes configure les routes de gestion des tracks func (r *APIRouter) setupTrackRoutes(router *gin.RouterGroup) { uploadDir := r.config.UploadDir if uploadDir == "" { uploadDir = "uploads/tracks" } chunksDir := uploadDir + "/chunks" trackService := trackcore.NewTrackServiceWithDB(r.db, r.logger, uploadDir) if r.config.CacheService != nil { trackService.SetCacheService(r.config.CacheService) } streamService := services.NewStreamServiceWithAPIKey(r.config.StreamServerURL, r.config.StreamServerInternalAPIKey, r.logger) trackService.SetStreamService(streamService) // INT-02: Enable HLS pipeline for regular uploads discoverService := discovercore.NewService(r.db.GormDB, r.logger) trackService.SetDiscoverService(discoverService) // v0.10.1: tags/genres sync trackUploadService := services.NewTrackUploadService(r.db.GormDB, r.logger) var redisClient *redis.Client if r.config != nil { redisClient = r.config.RedisClient } chunkService := services.NewTrackChunkService(chunksDir, redisClient, r.logger) likeService := services.NewTrackLikeService(r.db.GormDB, r.logger) trackHandler := trackcore.NewTrackHandler( trackService, trackUploadService, chunkService, likeService, streamService, ) if r.config != nil { if r.config.PermissionService != nil { trackHandler.SetPermissionService(r.config.PermissionService) } if r.config.JobWorker != nil { trackHandler.SetJobEnqueuer(r.config.JobWorker) } } uploadConfig := getUploadConfigWithEnv() uploadValidator, err := services.NewUploadValidator(uploadConfig, r.logger) if err != nil { r.logger.Warn("Upload validator created with ClamAV unavailable - uploads will be rejected", zap.Error(err)) uploadConfig.ClamAVEnabled = false uploadValidator, _ = services.NewUploadValidator(uploadConfig, r.logger) } trackHandler.SetUploadValidator(uploadValidator) trackSearchService := services.NewTrackSearchServiceWithDB(r.db) trackHandler.SetSearchService(trackSearchService) trackVersionService := services.NewTrackVersionService(r.db.GormDB, r.logger, uploadDir) trackHandler.SetVersionService(trackVersionService) playbackAnalyticsService := services.NewPlaybackAnalyticsService(r.db.GormDB, r.logger) trackHandler.SetPlaybackAnalyticsService(playbackAnalyticsService) trackHistoryService := services.NewTrackHistoryService(r.db.GormDB, r.logger) trackHandler.SetHistoryService(trackHistoryService) licenseChecker := services.NewDBTrackDownloadLicenseChecker(r.db.GormDB, r.logger) trackHandler.SetLicenseChecker(licenseChecker) if r.notificationService == nil { r.notificationService = services.NewNotificationService(r.db, r.logger) if r.pushService != nil { r.notificationService.SetPushService(r.pushService) } } trackHandler.SetNotificationService(r.notificationService) repostService := services.NewTrackRepostService(r.db.GormDB) trackHandler.SetRepostService(repostService) // v0.10.3 F203 trackRecommendationService := services.NewTrackRecommendationService(r.db.GormDB, r.logger) trackHandler.SetTrackRecommendationService(trackRecommendationService) waveformService := services.NewWaveformService(r.db.GormDB, r.logger, r.config.S3StorageService) if r.config.CacheService != nil { waveformService.SetCacheService(r.config.CacheService) } trackHandler.SetWaveformService(waveformService) tracks := router.Group("/tracks") { if r.config.AuthMiddleware != nil { tracks.GET("", r.config.AuthMiddleware.OptionalAuth(), trackHandler.ListTracks) } else { tracks.GET("", trackHandler.ListTracks) } tracks.GET("/search", trackHandler.SearchTracks) tracks.GET("/suggested-tags", trackHandler.GetSuggestedTags) if r.config.AuthMiddleware != nil { tracks.GET("/:id", r.config.AuthMiddleware.OptionalAuth(), trackHandler.GetTrack) } else { tracks.GET("/:id", trackHandler.GetTrack) } tracks.GET("/:id/lyrics", trackHandler.GetLyrics) tracks.GET("/:id/stats", trackHandler.GetTrackStats) tracks.GET("/:id/waveform", trackHandler.GetWaveform) tracks.GET("/:id/history", trackHandler.GetTrackHistory) tracks.GET("/:id/download", trackHandler.DownloadTrack) tracks.GET("/shared/:token", trackHandler.GetSharedTrack) if r.config.AuthMiddleware != nil { tracks.GET("/:id/repost", r.config.AuthMiddleware.OptionalAuth(), trackHandler.GetRepostStatus) // v0.10.3 F203 } else { tracks.GET("/:id/repost", trackHandler.GetRepostStatus) } if r.config.AuthMiddleware != nil { protected := tracks.Group("") protected.Use(r.config.AuthMiddleware.RequireAuth()) r.applyCSRFProtection(protected) protected.GET("/recommendations", trackHandler.GetRecommendations) uploadGroup := protected.Group("") uploadGroup.Use(r.config.AuthMiddleware.RequireContentCreatorRole()) uploadGroup.POST("", trackHandler.UploadTrack) trackOwnerResolver := func(c *gin.Context) (uuid.UUID, error) { trackIDStr := c.Param("id") trackID, err := uuid.Parse(trackIDStr) if err != nil { return uuid.Nil, err } track, err := trackService.GetTrackByID(c.Request.Context(), trackID) if err != nil { return uuid.Nil, err } return track.UserID, nil } protected.PUT("/:id", r.config.AuthMiddleware.RequireOwnershipOrAdmin("track", trackOwnerResolver), trackHandler.UpdateTrack) protected.PUT("/:id/lyrics", r.config.AuthMiddleware.RequireOwnershipOrAdmin("track", trackOwnerResolver), trackHandler.UpdateLyrics) protected.DELETE("/:id", r.config.AuthMiddleware.RequireOwnershipOrAdmin("track", trackOwnerResolver), trackHandler.DeleteTrack) protected.GET("/:id/status", trackHandler.GetUploadStatus) protected.POST("/initiate", trackHandler.InitiateChunkedUpload) protected.POST("/chunk", trackHandler.UploadChunk) protected.POST("/complete", trackHandler.CompleteChunkedUpload) protected.GET("/quota/:id", trackHandler.GetUploadQuota) protected.GET("/resume/:uploadId", trackHandler.ResumeUpload) protected.POST("/batch/delete", trackHandler.BatchDeleteTracks) protected.POST("/batch/update", trackHandler.BatchUpdateTracks) protected.POST("/:id/like", trackHandler.LikeTrack) protected.DELETE("/:id/like", trackHandler.UnlikeTrack) protected.GET("/:id/likes", trackHandler.GetTrackLikes) protected.POST("/:id/repost", trackHandler.RepostTrack) protected.DELETE("/:id/repost", trackHandler.UnrepostTrack) protected.POST("/:id/share", trackHandler.CreateShare) protected.DELETE("/share/:id", trackHandler.RevokeShare) protected.POST("/:id/versions/:versionId/restore", trackHandler.RestoreVersion) protected.POST("/:id/play", trackHandler.RecordPlay) // v0.10.7 F482: Stem sharing stemUploadDir := uploadDir if stemUploadDir == "" { stemUploadDir = "uploads/tracks" } stemService := services.NewTrackStemService(r.db.GormDB, stemUploadDir, r.logger) stemHandler := handlers.NewTrackStemHandler(stemService, trackService, r.logger) protected.GET("/:id/stems/:name/download", stemHandler.DownloadStem) protected.POST("/:id/stems", r.config.AuthMiddleware.RequireOwnershipOrAdmin("track", trackOwnerResolver), stemHandler.UploadStem) protected.GET("/:id/stems", stemHandler.ListStems) 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) tracks.GET("/:id/hls/status", hlsHandler.GetStreamStatus) if r.config.HLSEnabled { hlsStreaming := tracks.Group("/:id/hls") { hlsStreaming.GET("/master.m3u8", hlsHandler.ServeMasterPlaylist) hlsStreaming.GET("/:bitrate/playlist.m3u8", hlsHandler.ServeQualityPlaylist) hlsStreaming.GET("/:bitrate/:segment", hlsHandler.ServeSegment) } } } } commentService := services.NewCommentService(r.db.GormDB, r.logger) commentService.SetModerationService(services.NewCommentModerationService(r.db.GormDB)) // v0.10.3 F201 commentHandler := handlers.NewCommentHandler(commentService, r.logger) commentHandler.SetNotificationService(r.notificationService) comments := router.Group("/tracks") { comments.GET("/:id/comments", commentHandler.GetComments) if r.config.AuthMiddleware != nil { protected := comments.Group("") protected.Use(r.config.AuthMiddleware.RequireAuth()) r.applyCSRFProtection(protected) { protected.POST("/:id/comments", commentHandler.CreateComment) } } } commentsProtected := router.Group("/comments") { if r.config.AuthMiddleware != nil { commentsProtected.Use(r.config.AuthMiddleware.RequireAuth()) r.applyCSRFProtection(commentsProtected) { commentsProtected.DELETE("/:id", commentHandler.DeleteComment) } } } }