package services import ( "context" "github.com/fujin/music-agregator/internal/database" "github.com/fujin/music-agregator/internal/torrent" "github.com/google/uuid" ) type QueueSyncResult struct { Synced int `json:"synced"` Updated int `json:"updated"` } func SyncDownloadQueue(ctx context.Context, db *database.DB, torrentService *TorrentService) (*QueueSyncResult, error) { if !torrentService.IsConfigured() { return &QueueSyncResult{}, nil } torrents, err := torrentService.ListTorrents(ctx) if err != nil { return nil, err } torrentMap := make(map[string]torrent.TorrentInfo) for _, t := range torrents { torrentMap[t.Hash] = t } queueItems, err := db.ListDownloadQueue(ctx, nil) if err != nil { return nil, err } var synced, updated int for _, item := range queueItems { if item.TorrentHash == nil { continue } synced++ t, exists := torrentMap[*item.TorrentHash] if !exists { continue } newStatus := mapTorrentState(t.State) sizeLeft := int64(float64(item.Size) * (1 - t.Progress)) if newStatus != item.Status || item.Progress != float32(t.Progress) { if err := db.UpdateDownloadQueueProgress(ctx, item.ID, float32(t.Progress), sizeLeft, newStatus); err != nil { continue } updated++ if newStatus == "completed" && item.AlbumID != nil { db.RemoveFromWantedAlbums(ctx, *item.AlbumID) } } } return &QueueSyncResult{Synced: synced, Updated: updated}, nil } func mapTorrentState(state torrent.TorrentState) string { switch state { case torrent.StateDownloading: return "downloading" case torrent.StateSeeding: return "completed" case torrent.StatePaused: return "paused" case torrent.StateQueued: return "queued" case torrent.StateChecking: return "checking" case torrent.StateError: return "failed" default: return "queued" } } func HandleFailedDownload(ctx context.Context, db *database.DB, queueID uuid.UUID, errorMessage string) error { item, err := db.GetDownloadQueueItem(ctx, queueID) if err != nil { return err } if err := db.UpdateDownloadQueueStatus(ctx, queueID, "failed", &errorMessage); err != nil { return err } if item.ArtistID != nil && item.AlbumID != nil { if err := db.AddToBlocklist(ctx, *item.ArtistID, *item.AlbumID, item.Title, item.TorrentHash, item.Indexer); err != nil { return err } } if item.AlbumID != nil { if err := db.AddToWantedAlbums(ctx, *item.AlbumID); err != nil { return err } } return nil } type BlocklistResult struct { Blocklisted bool `json:"blocklisted"` Removed bool `json:"removed"` } func BlocklistAndRemove(ctx context.Context, db *database.DB, torrentService *TorrentService, queueID uuid.UUID) (*BlocklistResult, error) { item, err := db.GetDownloadQueueItem(ctx, queueID) if err != nil { return nil, err } result := &BlocklistResult{} if item.ArtistID != nil { albumID := item.AlbumID if albumID == nil { albumID = &uuid.Nil } if err := db.AddToBlocklist(ctx, *item.ArtistID, *albumID, item.Title, item.TorrentHash, item.Indexer); err == nil { result.Blocklisted = true } } if item.TorrentHash != nil && torrentService.IsConfigured() { torrentService.RemoveTorrent(ctx, *item.TorrentHash, true) } if err := db.DeleteDownloadQueueItem(ctx, queueID); err != nil { return nil, err } result.Removed = true if item.AlbumID != nil { db.AddToWantedAlbums(ctx, *item.AlbumID) } return result, nil }