diff --git a/internal/database/download_repository.go b/internal/database/download_repository.go index cc2bdaa..39c7d98 100644 --- a/internal/database/download_repository.go +++ b/internal/database/download_repository.go @@ -173,27 +173,28 @@ func (r *DownloadRepository) GetByID(ctx context.Context, id string) (*Download, return d, nil } -func (r *DownloadRepository) GetLatestByAlbumIDs(ctx context.Context, albumIDs []string) (map[string]*Download, error) { +func (r *DownloadRepository) GetByAlbumIDs(ctx context.Context, albumIDs []string) (map[string][]*Download, error) { if len(albumIDs) == 0 { return nil, nil } rows, err := r.pool.Query(ctx, - `SELECT DISTINCT ON (album_id) id, torrent_id, album_id, format, quality, state, qbit_hash, save_path, error_message, queued_at, started_at, completed_at, created_at, updated_at - FROM downloads WHERE album_id = ANY($1) ORDER BY album_id, created_at DESC`, albumIDs, + `SELECT id, torrent_id, album_id, format, quality, state, qbit_hash, save_path, error_message, queued_at, started_at, completed_at, created_at, updated_at + FROM downloads WHERE album_id = ANY($1) AND state NOT IN ('failed', 'cancelled') + ORDER BY album_id, created_at DESC`, albumIDs, ) if err != nil { return nil, fmt.Errorf("batch listing downloads: %w", err) } defer rows.Close() - result := make(map[string]*Download, len(albumIDs)) + result := make(map[string][]*Download, len(albumIDs)) for rows.Next() { d := &Download{} if err := rows.Scan(&d.ID, &d.TorrentID, &d.AlbumID, &d.Format, &d.Quality, &d.State, &d.QbitHash, &d.SavePath, &d.ErrorMessage, &d.QueuedAt, &d.StartedAt, &d.CompletedAt, &d.CreatedAt, &d.UpdatedAt); err != nil { return nil, fmt.Errorf("scanning download: %w", err) } - result[d.AlbumID] = d + result[d.AlbumID] = append(result[d.AlbumID], d) } return result, nil } diff --git a/internal/service.go b/internal/service.go index 4f186ae..bef36d8 100644 --- a/internal/service.go +++ b/internal/service.go @@ -251,7 +251,7 @@ func (service *MusicAgregatorService) buildAlbumsForArtist(ctx context.Context, albumIDs = append(albumIDs, a.ID) } - downloadsByAlbumID, _ := service.downloads.GetLatestByAlbumIDs(ctx, albumIDs) + downloadsByAlbumID, _ := service.downloads.GetByAlbumIDs(ctx, albumIDs) albums := make([]*pb.AlbumDetail, 0, len(metadataAlbums)) for _, ma := range metadataAlbums { @@ -276,12 +276,14 @@ func (service *MusicAgregatorService) buildAlbumsForArtist(ctx context.Context, detail.Id = dbAlbum.ID detail.MonitorState = toProtoMonitorState(dbAlbum.MonitorState) - if d, ok := downloadsByAlbumID[dbAlbum.ID]; ok { - detail.Download = &pb.DownloadInfo{ - State: d.State, - Format: d.Format, - Quality: d.Quality, - SavePath: derefStr(d.SavePath), + if downloads, ok := downloadsByAlbumID[dbAlbum.ID]; ok { + for _, d := range downloads { + detail.Downloads = append(detail.Downloads, &pb.DownloadInfo{ + State: d.State, + Format: d.Format, + Quality: d.Quality, + SavePath: derefStr(d.SavePath), + }) } } } else { @@ -343,18 +345,30 @@ func (service *MusicAgregatorService) buildAlbumInfo(ctx context.Context, dbAlbu downloads, err := service.downloads.GetByAlbumID(ctx, dbAlbum.ID) if err == nil && len(downloads) > 0 { - best := downloads[0] - album.Download = &pb.DownloadInfo{ - State: best.State, - Format: best.Format, - Quality: best.Quality, - SavePath: derefStr(best.SavePath), + for _, d := range downloads { + album.Downloads = append(album.Downloads, &pb.DownloadInfo{ + State: d.State, + Format: d.Format, + Quality: d.Quality, + SavePath: derefStr(d.SavePath), + }) } } + var bestDownload *database.Download + for _, d := range downloads { + if d.State == "completed" || d.State == "seeding" { + bestDownload = d + break + } + } + if bestDownload == nil && len(downloads) > 0 { + bestDownload = downloads[0] + } + var downloadFilesByTrackID map[string]*database.DownloadFile - if album.Download != nil { - files, err := service.downloadFiles.GetByDownloadID(ctx, downloads[0].ID) + if bestDownload != nil { + files, err := service.downloadFiles.GetByDownloadID(ctx, bestDownload.ID) if err == nil { downloadFilesByTrackID = make(map[string]*database.DownloadFile, len(files)) for _, f := range files { @@ -911,13 +925,14 @@ func (service *MusicAgregatorService) buildAlbumDetail(ctx context.Context, dbAl } downloads, err := service.downloads.GetByAlbumID(ctx, dbAlbum.ID) - if err == nil && len(downloads) > 0 { - best := downloads[0] - detail.Download = &pb.DownloadInfo{ - State: best.State, - Format: best.Format, - Quality: best.Quality, - SavePath: derefStr(best.SavePath), + if err == nil { + for _, d := range downloads { + detail.Downloads = append(detail.Downloads, &pb.DownloadInfo{ + State: d.State, + Format: d.Format, + Quality: d.Quality, + SavePath: derefStr(d.SavePath), + }) } } diff --git a/proto/music_agregator/v1/music_agregator.proto b/proto/music_agregator/v1/music_agregator.proto index ed03e9e..5bb9127 100644 --- a/proto/music_agregator/v1/music_agregator.proto +++ b/proto/music_agregator/v1/music_agregator.proto @@ -74,7 +74,7 @@ message AlbumDetail { repeated string genres = 9; string label = 10; MonitorState monitor_state = 11; - DownloadInfo download = 12; + repeated DownloadInfo downloads = 12; AlbumReleaseDetail release = 13; } diff --git a/test/component/monitor_album_test.go b/test/component/monitor_album_test.go index ad17633..e7d3462 100644 --- a/test/component/monitor_album_test.go +++ b/test/component/monitor_album_test.go @@ -261,8 +261,8 @@ func TestMonitorAlbum_AlreadyOwned(t *testing.T) { require.NoError(t, err) require.NotNil(t, resp.Album) assert.Equal(t, pb.MonitorState_MONITOR_STATE_MONITORED, resp.Album.MonitorState) - require.NotNil(t, resp.Album.Download) - assert.Equal(t, "completed", resp.Album.Download.State) + require.NotEmpty(t, resp.Album.Downloads) + assert.Equal(t, "completed", resp.Album.Downloads[0].State) assert.Nil(t, resp.Release) assert.False(t, indexerCalled)