235 lines
7 KiB
Go
235 lines
7 KiB
Go
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())
|
|
}
|