package api import ( "encoding/json" "net/http" "github.com/fujin/music-agregator/internal/services" "github.com/go-chi/chi/v5" "github.com/google/uuid" ) func (h *Handlers) ListQueue(w http.ResponseWriter, r *http.Request) { if h.DB == nil { writeError(w, http.StatusServiceUnavailable, "database not connected") return } var status *string if s := r.URL.Query().Get("status"); s != "" { status = &s } items, err := h.DB.ListDownloadQueue(r.Context(), status) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusOK, map[string]any{ "items": items, "total": len(items), }) } func (h *Handlers) GetQueueItem(w http.ResponseWriter, r *http.Request) { if h.DB == nil { writeError(w, http.StatusServiceUnavailable, "database not connected") return } idStr := chi.URLParam(r, "id") id, err := parseUUID(idStr) if err != nil { writeError(w, http.StatusBadRequest, "invalid ID") return } item, err := h.DB.GetDownloadQueueItem(r.Context(), id) if err != nil { writeError(w, http.StatusNotFound, "queue item not found") return } writeJSON(w, http.StatusOK, item) } func (h *Handlers) AddToQueue(w http.ResponseWriter, r *http.Request) { if h.DB == nil { writeError(w, http.StatusServiceUnavailable, "database not connected") return } var req struct { Title string `json:"title"` TorrentHash *string `json:"torrent_hash"` Size int64 `json:"size"` Indexer *string `json:"indexer"` AlbumID *string `json:"album_id"` ArtistID *string `json:"artist_id"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request body") return } var albumID, artistID *uuid.UUID if req.AlbumID != nil { if id, err := parseUUID(*req.AlbumID); err == nil { albumID = &id } } if req.ArtistID != nil { if id, err := parseUUID(*req.ArtistID); err == nil { artistID = &id } } id, err := h.DB.AddToDownloadQueue(r.Context(), req.Title, req.Size, req.TorrentHash, req.Indexer, albumID, artistID) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } item, _ := h.DB.GetDownloadQueueItem(r.Context(), id) writeJSON(w, http.StatusOK, item) } func (h *Handlers) UpdateQueueItem(w http.ResponseWriter, r *http.Request) { if h.DB == nil { writeError(w, http.StatusServiceUnavailable, "database not connected") return } idStr := chi.URLParam(r, "id") id, err := parseUUID(idStr) if err != nil { writeError(w, http.StatusBadRequest, "invalid ID") return } var req struct { Status *string `json:"status"` ErrorMessage *string `json:"error_message"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request body") return } if req.Status != nil { if *req.Status == "failed" && req.ErrorMessage != nil { services.HandleFailedDownload(r.Context(), h.DB, id, *req.ErrorMessage) } else { if err := h.DB.UpdateDownloadQueueStatus(r.Context(), id, *req.Status, req.ErrorMessage); err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } if *req.Status == "completed" { item, _ := h.DB.GetDownloadQueueItem(r.Context(), id) if item != nil && item.AlbumID != nil { h.DB.RemoveFromWantedAlbums(r.Context(), *item.AlbumID) } } } } item, err := h.DB.GetDownloadQueueItem(r.Context(), id) if err != nil { writeError(w, http.StatusNotFound, "queue item not found") return } writeJSON(w, http.StatusOK, item) } func (h *Handlers) DeleteQueueItem(w http.ResponseWriter, r *http.Request) { if h.DB == nil { writeError(w, http.StatusServiceUnavailable, "database not connected") return } idStr := chi.URLParam(r, "id") id, err := parseUUID(idStr) if err != nil { writeError(w, http.StatusBadRequest, "invalid ID") return } item, err := h.DB.GetDownloadQueueItem(r.Context(), id) if err != nil { writeError(w, http.StatusNotFound, "queue item not found") return } if item.TorrentHash != nil && h.TorrentService.IsConfigured() { h.TorrentService.RemoveTorrent(r.Context(), *item.TorrentHash, false) } if err := h.DB.DeleteDownloadQueueItem(r.Context(), id); err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusOK, map[string]any{"deleted": true}) } func (h *Handlers) SyncQueue(w http.ResponseWriter, r *http.Request) { if h.DB == nil { writeError(w, http.StatusServiceUnavailable, "database not connected") return } result, err := services.SyncDownloadQueue(r.Context(), h.DB, h.TorrentService) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusOK, result) } func (h *Handlers) BlocklistQueueItem(w http.ResponseWriter, r *http.Request) { if h.DB == nil { writeError(w, http.StatusServiceUnavailable, "database not connected") return } idStr := chi.URLParam(r, "id") id, err := parseUUID(idStr) if err != nil { writeError(w, http.StatusBadRequest, "invalid ID") return } result, err := services.BlocklistAndRemove(r.Context(), h.DB, h.TorrentService, id) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusOK, result) } func (h *Handlers) QueueStats(w http.ResponseWriter, r *http.Request) { if h.DB == nil { writeError(w, http.StatusServiceUnavailable, "database not connected") return } stats, err := h.DB.GetDownloadQueueStats(r.Context()) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusOK, stats) } func (h *Handlers) GetJobStatus(w http.ResponseWriter, r *http.Request) { if h.DB == nil { writeError(w, http.StatusServiceUnavailable, "database not connected") return } idStr := chi.URLParam(r, "id") id, err := parseUUID(idStr) if err != nil { writeError(w, http.StatusBadRequest, "invalid job ID") return } status, err := services.GetJobStatus(r.Context(), h.DB, h.TorrentService, id) if err != nil { writeError(w, http.StatusNotFound, "job not found") return } writeJSON(w, http.StatusOK, status) } func (h *Handlers) ImportQueueItem(w http.ResponseWriter, r *http.Request) { if h.DB == nil { writeError(w, http.StatusServiceUnavailable, "database not connected") return } if h.StorageBasePath == "" { writeError(w, http.StatusServiceUnavailable, "storage not configured") return } idStr := chi.URLParam(r, "id") id, err := parseUUID(idStr) if err != nil { writeError(w, http.StatusBadRequest, "invalid ID") return } result, err := services.ImportCompletedDownload(r.Context(), id, h.StorageBasePath, h.DB, h.TorrentService) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } writeJSON(w, http.StatusOK, result) }