package handlers import ( "bytes" "encoding/csv" "encoding/json" "github.com/google/uuid" "net/http" "strconv" "time" "github.com/gin-gonic/gin" "veza-backend-api/internal/models" "veza-backend-api/internal/services" ) // PlaylistExportHandler gère les exports de playlists // T0493: Create Playlist Export Feature type PlaylistExportHandler struct { playlistService *services.PlaylistService } // NewPlaylistExportHandler crée un nouveau handler d'export de playlists func NewPlaylistExportHandler(playlistService *services.PlaylistService) *PlaylistExportHandler { return &PlaylistExportHandler{ playlistService: playlistService, } } // ExportPlaylistJSON exporte une playlist au format JSON // T0493: Create Playlist Export Feature func (h *PlaylistExportHandler) ExportPlaylistJSON(c *gin.Context) { playlistID, err := uuid.Parse(c.Param("id")) // Changed to uuid.Parse if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid playlist id"}) return } // Vérifier que la playlist existe et que l'utilisateur a accès var userID *uuid.UUID if uidInterface, exists := c.Get("user_id"); exists { if uid, ok := uidInterface.(uuid.UUID); ok { userID = &uid } } playlist, err := h.playlistService.GetPlaylist(c.Request.Context(), playlistID, userID) if err != nil { if err.Error() == "playlist not found" { c.JSON(http.StatusNotFound, gin.H{"error": "playlist not found"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Vérifier que l'utilisateur a accès (propriétaire, collaborateur ou playlist publique) currentUserID := uuid.Nil if userID != nil { currentUserID = *userID } if playlist.UserID != currentUserID && !playlist.IsPublic { // Vérifier si l'utilisateur est collaborateur if userID != nil { hasAccess, err := h.playlistService.CheckPermission(c.Request.Context(), playlistID, *userID, models.PlaylistPermissionRead) if err != nil || !hasAccess { c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"}) return } } else { c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"}) return } } // Préparer les données d'export exportData := map[string]interface{}{ "playlist": map[string]interface{}{ "id": playlist.ID, "title": playlist.Title, "description": playlist.Description, "is_public": playlist.IsPublic, "cover_url": playlist.CoverURL, "track_count": playlist.TrackCount, "created_at": playlist.CreatedAt, "updated_at": playlist.UpdatedAt, }, "tracks": make([]map[string]interface{}, 0), "exported_at": time.Now().Format(time.RFC3339), } // Ajouter les tracks avec leurs informations if playlist.Tracks != nil { for _, playlistTrack := range playlist.Tracks { // Track est un struct (non-pointer), toujours valide { trackData := map[string]interface{}{ "position": playlistTrack.Position, "id": playlistTrack.Track.ID, "title": playlistTrack.Track.Title, "artist": playlistTrack.Track.Artist, "album": playlistTrack.Track.Album, "duration": playlistTrack.Track.Duration, "genre": playlistTrack.Track.Genre, "year": playlistTrack.Track.Year, "added_at": playlistTrack.AddedAt, } exportData["tracks"] = append(exportData["tracks"].([]map[string]interface{}), trackData) } } } // Convertir en JSON jsonData, err := json.MarshalIndent(exportData, "", " ") if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate JSON export"}) return } // Définir les headers pour le téléchargement filename := "playlist_" + playlistID.String() + "_" + time.Now().Format("20060102") + ".json" // Changed to playlistID.String() c.Header("Content-Type", "application/json") c.Header("Content-Disposition", "attachment; filename="+filename) c.Data(http.StatusOK, "application/json", jsonData) } // ExportPlaylistCSV exporte une playlist au format CSV // T0493: Create Playlist Export Feature func (h *PlaylistExportHandler) ExportPlaylistCSV(c *gin.Context) { playlistID, err := uuid.Parse(c.Param("id")) // Changed to uuid.Parse if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid playlist id"}) return } // Vérifier que la playlist existe et que l'utilisateur a accès var userID *uuid.UUID if uidInterface, exists := c.Get("user_id"); exists { if uid, ok := uidInterface.(uuid.UUID); ok { userID = &uid } } playlist, err := h.playlistService.GetPlaylist(c.Request.Context(), playlistID, userID) if err != nil { if err.Error() == "playlist not found" { c.JSON(http.StatusNotFound, gin.H{"error": "playlist not found"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Vérifier que l'utilisateur a accès (propriétaire, collaborateur ou playlist publique) currentUserID := uuid.Nil if userID != nil { currentUserID = *userID } if playlist.UserID != currentUserID && !playlist.IsPublic { // Vérifier si l'utilisateur est collaborateur if userID != nil { hasAccess, err := h.playlistService.CheckPermission(c.Request.Context(), playlistID, *userID, models.PlaylistPermissionRead) if err != nil || !hasAccess { c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"}) return } } else { c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"}) return } } // Créer le buffer CSV var csvData [][]string // En-têtes csvData = append(csvData, []string{ "Position", "Track ID", "Title", "Artist", "Album", "Duration (seconds)", "Genre", "Year", "Added At", }) // Ajouter les tracks if playlist.Tracks != nil { for _, playlistTrack := range playlist.Tracks { // Track est un struct (non-pointer), toujours valide { row := []string{ strconv.Itoa(playlistTrack.Position), playlistTrack.Track.ID.String(), // Changed to playlistTrack.Track.ID.String() playlistTrack.Track.Title, playlistTrack.Track.Artist, playlistTrack.Track.Album, strconv.Itoa(playlistTrack.Track.Duration), playlistTrack.Track.Genre, strconv.Itoa(playlistTrack.Track.Year), playlistTrack.AddedAt.Format(time.RFC3339), } csvData = append(csvData, row) } } } // Générer le CSV var csvBuffer bytes.Buffer writer := csv.NewWriter(&csvBuffer) // Écrire toutes les lignes for _, row := range csvData { if err := writer.Write(row); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate CSV export"}) return } } writer.Flush() if err := writer.Error(); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate CSV export"}) return } // Définir les headers pour le téléchargement filename := "playlist_" + playlistID.String() + "_" + time.Now().Format("20060102") + ".csv" // Changed to playlistID.String() c.Header("Content-Type", "text/csv") c.Header("Content-Disposition", "attachment; filename="+filename) c.Data(http.StatusOK, "text/csv", csvBuffer.Bytes()) }