feat: add download tracking endpoints (sections 4.1, 4.2, 4.3, 4.4)

This commit is contained in:
Alexander
2026-04-29 13:44:01 +02:00
parent c307c68d88
commit 3ecc6aee62
6 changed files with 999 additions and 0 deletions
+218
View File
@@ -577,6 +577,224 @@ func parseUUID(s string) (uuid.UUID, error) {
return uuid.Parse(s)
}
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 writeJSON(w http.ResponseWriter, status int, v any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)