diff --git a/go.mod b/go.mod index db7ecc3..47c7e88 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/supersonic-app/go-subsonic v0.0.0-20241224013245-9b2841f3711d github.com/vbauerster/mpb/v8 v8.10.0 go.uploadedlobster.com/mbtypes v0.4.0 - go.uploadedlobster.com/musicbrainzws2 v0.14.1-0.20250522060150-50bf4bea5400 + go.uploadedlobster.com/musicbrainzws2 v0.14.0 golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 golang.org/x/oauth2 v0.30.0 golang.org/x/text v0.25.0 diff --git a/go.sum b/go.sum index ef278f9..426e8c0 100644 --- a/go.sum +++ b/go.sum @@ -132,8 +132,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uploadedlobster.com/mbtypes v0.4.0 h1:D5asCgHsRWufj4Yn5u0IuH2J9z1UuYImYkYIp1Z1Q7s= go.uploadedlobster.com/mbtypes v0.4.0/go.mod h1:Bu1K1Hl77QTAE2Z7QKiW/JAp9KqYWQebkRRfG02dlZM= -go.uploadedlobster.com/musicbrainzws2 v0.14.1-0.20250522060150-50bf4bea5400 h1:wMJloSsyWjfXznQNjvsrqAeL61BGoil7t4H9hPt18fc= -go.uploadedlobster.com/musicbrainzws2 v0.14.1-0.20250522060150-50bf4bea5400/go.mod h1:T6sYE7ZHRH3mJWT3g9jdSUPKJLZubnBjKyjMPNdkgao= +go.uploadedlobster.com/musicbrainzws2 v0.14.0 h1:YaEtxNwLSNT1gzFipQ4XlaThNfXjBpzzb4I6WhIeUwg= +go.uploadedlobster.com/musicbrainzws2 v0.14.0/go.mod h1:T6sYE7ZHRH3mJWT3g9jdSUPKJLZubnBjKyjMPNdkgao= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= diff --git a/internal/backends/deezer/client.go b/internal/backends/deezer/client.go index 3ab2b6c..05264ae 100644 --- a/internal/backends/deezer/client.go +++ b/internal/backends/deezer/client.go @@ -1,5 +1,5 @@ /* -Copyright © 2023-2025 Philipp Wolfer +Copyright © 2023 Philipp Wolfer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,6 @@ THE SOFTWARE. package deezer import ( - "context" "errors" "strconv" @@ -53,14 +52,14 @@ func NewClient(token oauth2.TokenSource) Client { } } -func (c Client) UserHistory(ctx context.Context, offset int, limit int) (result HistoryResult, err error) { +func (c Client) UserHistory(offset int, limit int) (result HistoryResult, err error) { const path = "/user/me/history" - return listRequest[HistoryResult](ctx, c, path, offset, limit) + return listRequest[HistoryResult](c, path, offset, limit) } -func (c Client) UserTracks(ctx context.Context, offset int, limit int) (TracksResult, error) { +func (c Client) UserTracks(offset int, limit int) (TracksResult, error) { const path = "/user/me/tracks" - return listRequest[TracksResult](ctx, c, path, offset, limit) + return listRequest[TracksResult](c, path, offset, limit) } func (c Client) setToken(req *resty.Request) error { @@ -73,9 +72,8 @@ func (c Client) setToken(req *resty.Request) error { return nil } -func listRequest[T Result](ctx context.Context, c Client, path string, offset int, limit int) (result T, err error) { +func listRequest[T Result](c Client, path string, offset int, limit int) (result T, err error) { request := c.HTTPClient.R(). - SetContext(ctx). SetQueryParams(map[string]string{ "index": strconv.Itoa(offset), "limit": strconv.Itoa(limit), diff --git a/internal/backends/deezer/client_test.go b/internal/backends/deezer/client_test.go index 8b61804..c90b01a 100644 --- a/internal/backends/deezer/client_test.go +++ b/internal/backends/deezer/client_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2023-2025 Philipp Wolfer +Copyright © 2023 Philipp Wolfer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,6 @@ THE SOFTWARE. package deezer_test import ( - "context" "net/http" "testing" @@ -49,8 +48,7 @@ func TestGetUserHistory(t *testing.T) { "https://api.deezer.com/user/me/history", "testdata/user-history.json") - ctx := context.Background() - result, err := client.UserHistory(ctx, 0, 2) + result, err := client.UserHistory(0, 2) require.NoError(t, err) assert := assert.New(t) @@ -71,8 +69,7 @@ func TestGetUserTracks(t *testing.T) { "https://api.deezer.com/user/me/tracks", "testdata/user-tracks.json") - ctx := context.Background() - result, err := client.UserTracks(ctx, 0, 2) + result, err := client.UserTracks(0, 2) require.NoError(t, err) assert := assert.New(t) diff --git a/internal/backends/deezer/deezer.go b/internal/backends/deezer/deezer.go index c38f4e7..2209769 100644 --- a/internal/backends/deezer/deezer.go +++ b/internal/backends/deezer/deezer.go @@ -16,7 +16,6 @@ Scotty. If not, see . package deezer import ( - "context" "fmt" "math" "net/url" @@ -78,7 +77,7 @@ func (b *DeezerApiBackend) OAuth2Setup(token oauth2.TokenSource) error { return nil } -func (b *DeezerApiBackend) ExportListens(ctx context.Context, oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { +func (b *DeezerApiBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { // Choose a high offset, we attempt to search the loves backwards starting // at the oldest one. offset := math.MaxInt32 @@ -97,7 +96,7 @@ func (b *DeezerApiBackend) ExportListens(ctx context.Context, oldestTimestamp ti out: for { - result, err := b.client.UserHistory(ctx, offset, perPage) + result, err := b.client.UserHistory(offset, perPage) if err != nil { p.Export.Abort() progress <- p @@ -154,7 +153,7 @@ out: progress <- p } -func (b *DeezerApiBackend) ExportLoves(ctx context.Context, oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.TransferProgress) { +func (b *DeezerApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.TransferProgress) { // Choose a high offset, we attempt to search the loves backwards starting // at the oldest one. offset := math.MaxInt32 @@ -169,7 +168,7 @@ func (b *DeezerApiBackend) ExportLoves(ctx context.Context, oldestTimestamp time out: for { - result, err := b.client.UserTracks(ctx, offset, perPage) + result, err := b.client.UserTracks(offset, perPage) if err != nil { p.Export.Abort() progress <- p diff --git a/internal/backends/dump/dump.go b/internal/backends/dump/dump.go index 1fcd864..add8711 100644 --- a/internal/backends/dump/dump.go +++ b/internal/backends/dump/dump.go @@ -17,7 +17,6 @@ Scotty. If not, see . package dump import ( - "context" "fmt" "go.uploadedlobster.com/scotty/internal/config" @@ -37,12 +36,8 @@ func (b *DumpBackend) InitConfig(config *config.ServiceConfig) error { func (b *DumpBackend) StartImport() error { return nil } func (b *DumpBackend) FinishImport() error { return nil } -func (b *DumpBackend) ImportListens(ctx context.Context, export models.ListensResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { +func (b *DumpBackend) ImportListens(export models.ListensResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { for _, listen := range export.Items { - if err := ctx.Err(); err != nil { - return importResult, err - } - importResult.UpdateTimestamp(listen.ListenedAt) importResult.ImportCount += 1 msg := fmt.Sprintf("🎶 %v: \"%v\" by %v (%v)", @@ -54,12 +49,8 @@ func (b *DumpBackend) ImportListens(ctx context.Context, export models.ListensRe return importResult, nil } -func (b *DumpBackend) ImportLoves(ctx context.Context, export models.LovesResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { +func (b *DumpBackend) ImportLoves(export models.LovesResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { for _, love := range export.Items { - if err := ctx.Err(); err != nil { - return importResult, err - } - importResult.UpdateTimestamp(love.Created) importResult.ImportCount += 1 msg := fmt.Sprintf("❤️ %v: \"%v\" by %v (%v)", diff --git a/internal/backends/export.go b/internal/backends/export.go index 29ae595..54daafb 100644 --- a/internal/backends/export.go +++ b/internal/backends/export.go @@ -16,7 +16,6 @@ Scotty. If not, see . package backends import ( - "context" "sync" "time" @@ -25,7 +24,7 @@ import ( type ExportProcessor[T models.ListensResult | models.LovesResult] interface { ExportBackend() models.Backend - Process(ctx context.Context, wg *sync.WaitGroup, oldestTimestamp time.Time, results chan T, progress chan models.TransferProgress) + Process(wg *sync.WaitGroup, oldestTimestamp time.Time, results chan T, progress chan models.TransferProgress) } type ListensExportProcessor struct { @@ -36,11 +35,11 @@ func (p ListensExportProcessor) ExportBackend() models.Backend { return p.Backend } -func (p ListensExportProcessor) Process(ctx context.Context, wg *sync.WaitGroup, oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { +func (p ListensExportProcessor) Process(wg *sync.WaitGroup, oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { wg.Add(1) defer wg.Done() defer close(results) - p.Backend.ExportListens(ctx, oldestTimestamp, results, progress) + p.Backend.ExportListens(oldestTimestamp, results, progress) } type LovesExportProcessor struct { @@ -51,9 +50,9 @@ func (p LovesExportProcessor) ExportBackend() models.Backend { return p.Backend } -func (p LovesExportProcessor) Process(ctx context.Context, wg *sync.WaitGroup, oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.TransferProgress) { +func (p LovesExportProcessor) Process(wg *sync.WaitGroup, oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.TransferProgress) { wg.Add(1) defer wg.Done() defer close(results) - p.Backend.ExportLoves(ctx, oldestTimestamp, results, progress) + p.Backend.ExportLoves(oldestTimestamp, results, progress) } diff --git a/internal/backends/funkwhale/client.go b/internal/backends/funkwhale/client.go index 3471612..c231c94 100644 --- a/internal/backends/funkwhale/client.go +++ b/internal/backends/funkwhale/client.go @@ -1,5 +1,5 @@ /* -Copyright © 2023-2025 Philipp Wolfer +Copyright © 2023 Philipp Wolfer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -22,7 +22,6 @@ THE SOFTWARE. package funkwhale import ( - "context" "errors" "strconv" @@ -55,10 +54,15 @@ func NewClient(serverURL string, token string) Client { } } -func (c Client) GetHistoryListenings(ctx context.Context, user string, page int, perPage int) (result ListeningsResult, err error) { +func (c Client) GetHistoryListenings(user string, page int, perPage int) (result ListeningsResult, err error) { const path = "/api/v1/history/listenings" - response, err := c.buildListRequest(ctx, page, perPage). - SetQueryParam("username", user). + response, err := c.HTTPClient.R(). + SetQueryParams(map[string]string{ + "username": user, + "page": strconv.Itoa(page), + "page_size": strconv.Itoa(perPage), + "ordering": "-creation_date", + }). SetResult(&result). Get(path) @@ -69,25 +73,20 @@ func (c Client) GetHistoryListenings(ctx context.Context, user string, page int, return } -func (c Client) GetFavoriteTracks(ctx context.Context, page int, perPage int) (result FavoriteTracksResult, err error) { +func (c Client) GetFavoriteTracks(page int, perPage int) (result FavoriteTracksResult, err error) { const path = "/api/v1/favorites/tracks" - response, err := c.buildListRequest(ctx, page, perPage). - SetResult(&result). - Get(path) - - if !response.IsSuccess() { - err = errors.New(response.String()) - return - } - return -} - -func (c Client) buildListRequest(ctx context.Context, page int, perPage int) *resty.Request { - return c.HTTPClient.R(). - SetContext(ctx). + response, err := c.HTTPClient.R(). SetQueryParams(map[string]string{ "page": strconv.Itoa(page), "page_size": strconv.Itoa(perPage), "ordering": "-creation_date", - }) + }). + SetResult(&result). + Get(path) + + if !response.IsSuccess() { + err = errors.New(response.String()) + return + } + return } diff --git a/internal/backends/funkwhale/client_test.go b/internal/backends/funkwhale/client_test.go index d6b04e0..e850a4d 100644 --- a/internal/backends/funkwhale/client_test.go +++ b/internal/backends/funkwhale/client_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2023-2025 Philipp Wolfer +Copyright © 2023 Philipp Wolfer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -22,7 +22,6 @@ THE SOFTWARE. package funkwhale_test import ( - "context" "net/http" "testing" @@ -50,8 +49,7 @@ func TestGetHistoryListenings(t *testing.T) { "https://funkwhale.example.com/api/v1/history/listenings", "testdata/listenings.json") - ctx := context.Background() - result, err := client.GetHistoryListenings(ctx, "outsidecontext", 0, 2) + result, err := client.GetHistoryListenings("outsidecontext", 0, 2) require.NoError(t, err) assert := assert.New(t) @@ -75,8 +73,7 @@ func TestGetFavoriteTracks(t *testing.T) { "https://funkwhale.example.com/api/v1/favorites/tracks", "testdata/favorite-tracks.json") - ctx := context.Background() - result, err := client.GetFavoriteTracks(ctx, 0, 2) + result, err := client.GetFavoriteTracks(0, 2) require.NoError(t, err) assert := assert.New(t) diff --git a/internal/backends/funkwhale/funkwhale.go b/internal/backends/funkwhale/funkwhale.go index d9632a6..cd2f28e 100644 --- a/internal/backends/funkwhale/funkwhale.go +++ b/internal/backends/funkwhale/funkwhale.go @@ -17,7 +17,6 @@ Scotty. If not, see . package funkwhale import ( - "context" "sort" "time" @@ -61,7 +60,7 @@ func (b *FunkwhaleApiBackend) InitConfig(config *config.ServiceConfig) error { return nil } -func (b *FunkwhaleApiBackend) ExportListens(ctx context.Context, oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { +func (b *FunkwhaleApiBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { page := 1 perPage := MaxItemsPerGet @@ -75,7 +74,7 @@ func (b *FunkwhaleApiBackend) ExportListens(ctx context.Context, oldestTimestamp out: for { - result, err := b.client.GetHistoryListenings(ctx, b.username, page, perPage) + result, err := b.client.GetHistoryListenings(b.username, page, perPage) if err != nil { p.Export.Abort() progress <- p @@ -118,7 +117,7 @@ out: results <- models.ListensResult{Items: listens} } -func (b *FunkwhaleApiBackend) ExportLoves(ctx context.Context, oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.TransferProgress) { +func (b *FunkwhaleApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.TransferProgress) { page := 1 perPage := MaxItemsPerGet @@ -132,7 +131,7 @@ func (b *FunkwhaleApiBackend) ExportLoves(ctx context.Context, oldestTimestamp t out: for { - result, err := b.client.GetFavoriteTracks(ctx, page, perPage) + result, err := b.client.GetFavoriteTracks(page, perPage) if err != nil { p.Export.Abort() progress <- p diff --git a/internal/backends/import.go b/internal/backends/import.go index e7a6add..0a2e341 100644 --- a/internal/backends/import.go +++ b/internal/backends/import.go @@ -18,7 +18,6 @@ Scotty. If not, see . package backends import ( - "context" "sync" "go.uploadedlobster.com/scotty/internal/models" @@ -26,8 +25,8 @@ import ( type ImportProcessor[T models.ListensResult | models.LovesResult] interface { ImportBackend() models.ImportBackend - Process(ctx context.Context, wg *sync.WaitGroup, results chan T, out chan models.ImportResult, progress chan models.TransferProgress) - Import(ctx context.Context, export T, result models.ImportResult, out chan models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) + Process(wg *sync.WaitGroup, results chan T, out chan models.ImportResult, progress chan models.TransferProgress) + Import(export T, result models.ImportResult, out chan models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) } type ListensImportProcessor struct { @@ -38,11 +37,11 @@ func (p ListensImportProcessor) ImportBackend() models.ImportBackend { return p.Backend } -func (p ListensImportProcessor) Process(ctx context.Context, wg *sync.WaitGroup, results chan models.ListensResult, out chan models.ImportResult, progress chan models.TransferProgress) { - process(ctx, wg, p, results, out, progress) +func (p ListensImportProcessor) Process(wg *sync.WaitGroup, results chan models.ListensResult, out chan models.ImportResult, progress chan models.TransferProgress) { + process(wg, p, results, out, progress) } -func (p ListensImportProcessor) Import(ctx context.Context, export models.ListensResult, result models.ImportResult, out chan models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { +func (p ListensImportProcessor) Import(export models.ListensResult, result models.ImportResult, out chan models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { if export.Error != nil { return result, export.Error } @@ -52,7 +51,7 @@ func (p ListensImportProcessor) Import(ctx context.Context, export models.Listen } else { result.TotalCount += len(export.Items) } - importResult, err := p.Backend.ImportListens(ctx, export, result, progress) + importResult, err := p.Backend.ImportListens(export, result, progress) if err != nil { return importResult, err } @@ -67,11 +66,11 @@ func (p LovesImportProcessor) ImportBackend() models.ImportBackend { return p.Backend } -func (p LovesImportProcessor) Process(ctx context.Context, wg *sync.WaitGroup, results chan models.LovesResult, out chan models.ImportResult, progress chan models.TransferProgress) { - process(ctx, wg, p, results, out, progress) +func (p LovesImportProcessor) Process(wg *sync.WaitGroup, results chan models.LovesResult, out chan models.ImportResult, progress chan models.TransferProgress) { + process(wg, p, results, out, progress) } -func (p LovesImportProcessor) Import(ctx context.Context, export models.LovesResult, result models.ImportResult, out chan models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { +func (p LovesImportProcessor) Import(export models.LovesResult, result models.ImportResult, out chan models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { if export.Error != nil { return result, export.Error } @@ -81,19 +80,14 @@ func (p LovesImportProcessor) Import(ctx context.Context, export models.LovesRes } else { result.TotalCount += len(export.Items) } - importResult, err := p.Backend.ImportLoves(ctx, export, result, progress) + importResult, err := p.Backend.ImportLoves(export, result, progress) if err != nil { return importResult, err } return importResult, nil } -func process[R models.LovesResult | models.ListensResult, P ImportProcessor[R]]( - ctx context.Context, wg *sync.WaitGroup, - processor P, results chan R, - out chan models.ImportResult, - progress chan models.TransferProgress, -) { +func process[R models.LovesResult | models.ListensResult, P ImportProcessor[R]](wg *sync.WaitGroup, processor P, results chan R, out chan models.ImportResult, progress chan models.TransferProgress) { wg.Add(1) defer wg.Done() defer close(out) @@ -106,13 +100,7 @@ func process[R models.LovesResult | models.ListensResult, P ImportProcessor[R]]( } for exportResult := range results { - if err := ctx.Err(); err != nil { - processor.ImportBackend().FinishImport() - out <- handleError(result, err, progress) - return - } - - importResult, err := processor.Import(ctx, exportResult, result, out, progress) + importResult, err := processor.Import(exportResult, result, out, progress) result.Update(importResult) if err != nil { processor.ImportBackend().FinishImport() diff --git a/internal/backends/jspf/jspf.go b/internal/backends/jspf/jspf.go index 354640e..0e200f2 100644 --- a/internal/backends/jspf/jspf.go +++ b/internal/backends/jspf/jspf.go @@ -18,7 +18,6 @@ Scotty. If not, see . package jspf import ( - "context" "errors" "os" "sort" @@ -94,7 +93,7 @@ func (b *JSPFBackend) FinishImport() error { return b.writeJSPF() } -func (b *JSPFBackend) ExportListens(ctx context.Context, oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { +func (b *JSPFBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { err := b.readJSPF() p := models.TransferProgress{ Export: &models.Progress{}, @@ -121,12 +120,8 @@ func (b *JSPFBackend) ExportListens(ctx context.Context, oldestTimestamp time.Ti results <- models.ListensResult{Items: listens} } -func (b *JSPFBackend) ImportListens(ctx context.Context, export models.ListensResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { +func (b *JSPFBackend) ImportListens(export models.ListensResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { for _, listen := range export.Items { - if err := ctx.Err(); err != nil { - return importResult, err - } - track := listenAsTrack(listen) b.playlist.Tracks = append(b.playlist.Tracks, track) importResult.ImportCount += 1 @@ -137,7 +132,7 @@ func (b *JSPFBackend) ImportListens(ctx context.Context, export models.ListensRe return importResult, nil } -func (b *JSPFBackend) ExportLoves(ctx context.Context, oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.TransferProgress) { +func (b *JSPFBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.TransferProgress) { err := b.readJSPF() p := models.TransferProgress{ Export: &models.Progress{}, @@ -164,12 +159,8 @@ func (b *JSPFBackend) ExportLoves(ctx context.Context, oldestTimestamp time.Time results <- models.LovesResult{Items: loves} } -func (b *JSPFBackend) ImportLoves(ctx context.Context, export models.LovesResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { +func (b *JSPFBackend) ImportLoves(export models.LovesResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { for _, love := range export.Items { - if err := ctx.Err(); err != nil { - return importResult, err - } - track := loveAsTrack(love) b.playlist.Tracks = append(b.playlist.Tracks, track) importResult.ImportCount += 1 diff --git a/internal/backends/lastfm/lastfm.go b/internal/backends/lastfm/lastfm.go index b34452e..d262ada 100644 --- a/internal/backends/lastfm/lastfm.go +++ b/internal/backends/lastfm/lastfm.go @@ -16,7 +16,6 @@ Scotty. If not, see . package lastfm import ( - "context" "fmt" "net/url" "sort" @@ -89,7 +88,7 @@ func (b *LastfmApiBackend) OAuth2Setup(token oauth2.TokenSource) error { return nil } -func (b *LastfmApiBackend) ExportListens(ctx context.Context, oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { +func (b *LastfmApiBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { page := MaxPage minTime := oldestTimestamp perPage := MaxItemsPerGet @@ -103,13 +102,6 @@ func (b *LastfmApiBackend) ExportListens(ctx context.Context, oldestTimestamp ti out: for page > 0 { - if err := ctx.Err(); err != nil { - results <- models.ListensResult{Error: err} - p.Export.Abort() - progress <- p - return - } - args := lastfm.P{ "user": b.username, "limit": MaxListensPerGet, @@ -190,13 +182,9 @@ out: progress <- p } -func (b *LastfmApiBackend) ImportListens(ctx context.Context, export models.ListensResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { +func (b *LastfmApiBackend) ImportListens(export models.ListensResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { total := len(export.Items) for i := 0; i < total; i += MaxListensPerSubmission { - if err := ctx.Err(); err != nil { - return importResult, err - } - listens := export.Items[i:min(i+MaxListensPerSubmission, total)] count := len(listens) if count == 0 { @@ -270,7 +258,7 @@ func (b *LastfmApiBackend) ImportListens(ctx context.Context, export models.List return importResult, nil } -func (b *LastfmApiBackend) ExportLoves(ctx context.Context, oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.TransferProgress) { +func (b *LastfmApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.TransferProgress) { // Choose a high offset, we attempt to search the loves backwards starting // at the oldest one. page := 1 @@ -286,13 +274,6 @@ func (b *LastfmApiBackend) ExportLoves(ctx context.Context, oldestTimestamp time out: for { - if err := ctx.Err(); err != nil { - results <- models.LovesResult{Error: err} - p.Export.Abort() - progress <- p - return - } - result, err := b.client.User.GetLovedTracks(lastfm.P{ "user": b.username, "limit": MaxItemsPerGet, @@ -354,12 +335,8 @@ out: results <- models.LovesResult{Items: loves, Total: totalCount} } -func (b *LastfmApiBackend) ImportLoves(ctx context.Context, export models.LovesResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { +func (b *LastfmApiBackend) ImportLoves(export models.LovesResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { for _, love := range export.Items { - if err := ctx.Err(); err != nil { - return importResult, err - } - err := b.client.Track.Love(lastfm.P{ "track": love.TrackName, "artist": love.ArtistName(), diff --git a/internal/backends/listenbrainz/client.go b/internal/backends/listenbrainz/client.go index d1a1fa6..fff476c 100644 --- a/internal/backends/listenbrainz/client.go +++ b/internal/backends/listenbrainz/client.go @@ -1,5 +1,5 @@ /* -Copyright © 2023-2025 Philipp Wolfer +Copyright © 2023 Philipp Wolfer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -22,7 +22,6 @@ THE SOFTWARE. package listenbrainz import ( - "context" "errors" "strconv" "time" @@ -61,11 +60,10 @@ func NewClient(token string) Client { } } -func (c Client) GetListens(ctx context.Context, user string, maxTime time.Time, minTime time.Time) (result GetListensResult, err error) { +func (c Client) GetListens(user string, maxTime time.Time, minTime time.Time) (result GetListensResult, err error) { const path = "/user/{username}/listens" errorResult := ErrorResult{} response, err := c.HTTPClient.R(). - SetContext(ctx). SetPathParam("username", user). SetQueryParams(map[string]string{ "max_ts": strconv.FormatInt(maxTime.Unix(), 10), @@ -83,11 +81,10 @@ func (c Client) GetListens(ctx context.Context, user string, maxTime time.Time, return } -func (c Client) SubmitListens(ctx context.Context, listens ListenSubmission) (result StatusResult, err error) { +func (c Client) SubmitListens(listens ListenSubmission) (result StatusResult, err error) { const path = "/submit-listens" errorResult := ErrorResult{} response, err := c.HTTPClient.R(). - SetContext(ctx). SetBody(listens). SetResult(&result). SetError(&errorResult). @@ -100,11 +97,10 @@ func (c Client) SubmitListens(ctx context.Context, listens ListenSubmission) (re return } -func (c Client) GetFeedback(ctx context.Context, user string, status int, offset int) (result GetFeedbackResult, err error) { +func (c Client) GetFeedback(user string, status int, offset int) (result GetFeedbackResult, err error) { const path = "/feedback/user/{username}/get-feedback" errorResult := ErrorResult{} response, err := c.HTTPClient.R(). - SetContext(ctx). SetPathParam("username", user). SetQueryParams(map[string]string{ "status": strconv.Itoa(status), @@ -123,11 +119,10 @@ func (c Client) GetFeedback(ctx context.Context, user string, status int, offset return } -func (c Client) SendFeedback(ctx context.Context, feedback Feedback) (result StatusResult, err error) { +func (c Client) SendFeedback(feedback Feedback) (result StatusResult, err error) { const path = "/feedback/recording-feedback" errorResult := ErrorResult{} response, err := c.HTTPClient.R(). - SetContext(ctx). SetBody(feedback). SetResult(&result). SetError(&errorResult). @@ -140,11 +135,10 @@ func (c Client) SendFeedback(ctx context.Context, feedback Feedback) (result Sta return } -func (c Client) Lookup(ctx context.Context, recordingName string, artistName string) (result LookupResult, err error) { +func (c Client) Lookup(recordingName string, artistName string) (result LookupResult, err error) { const path = "/metadata/lookup" errorResult := ErrorResult{} response, err := c.HTTPClient.R(). - SetContext(ctx). SetQueryParams(map[string]string{ "recording_name": recordingName, "artist_name": artistName, diff --git a/internal/backends/listenbrainz/client_test.go b/internal/backends/listenbrainz/client_test.go index 45bb0de..2e841ae 100644 --- a/internal/backends/listenbrainz/client_test.go +++ b/internal/backends/listenbrainz/client_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2023-2025 Philipp Wolfer +Copyright © 2023 Philipp Wolfer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -22,7 +22,6 @@ THE SOFTWARE. package listenbrainz_test import ( - "context" "net/http" "testing" "time" @@ -50,9 +49,7 @@ func TestGetListens(t *testing.T) { "https://api.listenbrainz.org/1/user/outsidecontext/listens", "testdata/listens.json") - ctx := context.Background() - result, err := client.GetListens(ctx, "outsidecontext", - time.Now(), time.Now().Add(-2*time.Hour)) + result, err := client.GetListens("outsidecontext", time.Now(), time.Now().Add(-2*time.Hour)) require.NoError(t, err) assert := assert.New(t) @@ -95,8 +92,8 @@ func TestSubmitListens(t *testing.T) { }, }, } - ctx := context.Background() - result, err := client.SubmitListens(ctx, listens) + result, err := client.SubmitListens(listens) + require.NoError(t, err) assert.Equal(t, "ok", result.Status) } @@ -110,8 +107,7 @@ func TestGetFeedback(t *testing.T) { "https://api.listenbrainz.org/1/feedback/user/outsidecontext/get-feedback", "testdata/feedback.json") - ctx := context.Background() - result, err := client.GetFeedback(ctx, "outsidecontext", 1, 0) + result, err := client.GetFeedback("outsidecontext", 1, 3) require.NoError(t, err) assert := assert.New(t) @@ -139,8 +135,7 @@ func TestSendFeedback(t *testing.T) { RecordingMBID: "c0a1fc94-5f04-4a5f-bc09-e5de0c49cd12", Score: 1, } - ctx := context.Background() - result, err := client.SendFeedback(ctx, feedback) + result, err := client.SendFeedback(feedback) require.NoError(t, err) assert.Equal(t, "ok", result.Status) @@ -154,8 +149,7 @@ func TestLookup(t *testing.T) { "https://api.listenbrainz.org/1/metadata/lookup", "testdata/lookup.json") - ctx := context.Background() - result, err := client.Lookup(ctx, "Paradise Lost", "Say Just Words") + result, err := client.Lookup("Paradise Lost", "Say Just Words") require.NoError(t, err) assert := assert.New(t) diff --git a/internal/backends/listenbrainz/listenbrainz.go b/internal/backends/listenbrainz/listenbrainz.go index bf46c22..61597d1 100644 --- a/internal/backends/listenbrainz/listenbrainz.go +++ b/internal/backends/listenbrainz/listenbrainz.go @@ -17,7 +17,6 @@ Scotty. If not, see . package listenbrainz import ( - "context" "fmt" "sort" "time" @@ -73,7 +72,7 @@ func (b *ListenBrainzApiBackend) InitConfig(config *config.ServiceConfig) error func (b *ListenBrainzApiBackend) StartImport() error { return nil } func (b *ListenBrainzApiBackend) FinishImport() error { return nil } -func (b *ListenBrainzApiBackend) ExportListens(ctx context.Context, oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { +func (b *ListenBrainzApiBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { startTime := time.Now() minTime := oldestTimestamp if minTime.Unix() < 1 { @@ -88,7 +87,7 @@ func (b *ListenBrainzApiBackend) ExportListens(ctx context.Context, oldestTimest } for { - result, err := b.client.GetListens(ctx, b.username, time.Now(), minTime) + result, err := b.client.GetListens(b.username, time.Now(), minTime) if err != nil { p.Export.Abort() progress <- p @@ -135,7 +134,7 @@ func (b *ListenBrainzApiBackend) ExportListens(ctx context.Context, oldestTimest progress <- p } -func (b *ListenBrainzApiBackend) ImportListens(ctx context.Context, export models.ListensResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { +func (b *ListenBrainzApiBackend) ImportListens(export models.ListensResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { total := len(export.Items) p := models.TransferProgress{}.FromImportResult(importResult, false) for i := 0; i < total; i += MaxListensPerRequest { @@ -152,7 +151,7 @@ func (b *ListenBrainzApiBackend) ImportListens(ctx context.Context, export model for _, l := range listens { if b.checkDuplicates { - isDupe, err := b.checkDuplicateListen(ctx, l) + isDupe, err := b.checkDuplicateListen(l) p.Import.Elapsed += 1 progress <- p if err != nil { @@ -183,7 +182,7 @@ func (b *ListenBrainzApiBackend) ImportListens(ctx context.Context, export model } if len(submission.Payload) > 0 { - _, err := b.client.SubmitListens(ctx, submission) + _, err := b.client.SubmitListens(submission) if err != nil { return importResult, err } @@ -199,13 +198,13 @@ func (b *ListenBrainzApiBackend) ImportListens(ctx context.Context, export model return importResult, nil } -func (b *ListenBrainzApiBackend) ExportLoves(ctx context.Context, oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.TransferProgress) { +func (b *ListenBrainzApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.TransferProgress) { exportChan := make(chan models.LovesResult) p := models.TransferProgress{ Export: &models.Progress{}, } - go b.exportLoves(ctx, oldestTimestamp, exportChan) + go b.exportLoves(oldestTimestamp, exportChan) for existingLoves := range exportChan { if existingLoves.Error != nil { p.Export.Abort() @@ -225,14 +224,14 @@ func (b *ListenBrainzApiBackend) ExportLoves(ctx context.Context, oldestTimestam progress <- p } -func (b *ListenBrainzApiBackend) exportLoves(ctx context.Context, oldestTimestamp time.Time, results chan models.LovesResult) { +func (b *ListenBrainzApiBackend) exportLoves(oldestTimestamp time.Time, results chan models.LovesResult) { offset := 0 defer close(results) loves := make(models.LovesList, 0, 2*MaxItemsPerGet) out: for { - result, err := b.client.GetFeedback(ctx, b.username, 1, offset) + result, err := b.client.GetFeedback(b.username, 1, offset) if err != nil { results <- models.LovesResult{Error: err} return @@ -248,7 +247,7 @@ out: // longer available and might have been merged. Try fetching details // from MusicBrainz. if feedback.TrackMetadata == nil { - track, err := b.lookupRecording(ctx, feedback.RecordingMBID) + track, err := b.lookupRecording(feedback.RecordingMBID) if err == nil { feedback.TrackMetadata = track } @@ -272,10 +271,10 @@ out: } } -func (b *ListenBrainzApiBackend) ImportLoves(ctx context.Context, export models.LovesResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { +func (b *ListenBrainzApiBackend) ImportLoves(export models.LovesResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { if len(b.existingMBIDs) == 0 { existingLovesChan := make(chan models.LovesResult) - go b.exportLoves(ctx, time.Unix(0, 0), existingLovesChan) + go b.exportLoves(time.Unix(0, 0), existingLovesChan) // TODO: Store MBIDs directly b.existingMBIDs = make(map[mbtypes.MBID]bool, MaxItemsPerGet) @@ -304,7 +303,7 @@ func (b *ListenBrainzApiBackend) ImportLoves(ctx context.Context, export models. } if recordingMBID == "" { - lookup, err := b.client.Lookup(ctx, love.TrackName, love.ArtistName()) + lookup, err := b.client.Lookup(love.TrackName, love.ArtistName()) if err == nil { recordingMBID = lookup.RecordingMBID } @@ -316,7 +315,7 @@ func (b *ListenBrainzApiBackend) ImportLoves(ctx context.Context, export models. if b.existingMBIDs[recordingMBID] { ok = true } else { - resp, err := b.client.SendFeedback(ctx, Feedback{ + resp, err := b.client.SendFeedback(Feedback{ RecordingMBID: recordingMBID, Score: 1, }) @@ -352,7 +351,7 @@ var defaultDuration = time.Duration(3 * time.Minute) const trackSimilarityThreshold = 0.9 -func (b *ListenBrainzApiBackend) checkDuplicateListen(ctx context.Context, listen models.Listen) (bool, error) { +func (b *ListenBrainzApiBackend) checkDuplicateListen(listen models.Listen) (bool, error) { // Find listens duration := listen.Duration if duration == 0 { @@ -360,7 +359,7 @@ func (b *ListenBrainzApiBackend) checkDuplicateListen(ctx context.Context, liste } minTime := listen.ListenedAt.Add(-duration) maxTime := listen.ListenedAt.Add(duration) - candidates, err := b.client.GetListens(ctx, b.username, maxTime, minTime) + candidates, err := b.client.GetListens(b.username, maxTime, minTime) if err != nil { return false, err } @@ -375,11 +374,11 @@ func (b *ListenBrainzApiBackend) checkDuplicateListen(ctx context.Context, liste return false, nil } -func (b *ListenBrainzApiBackend) lookupRecording(ctx context.Context, mbid mbtypes.MBID) (*Track, error) { +func (b *ListenBrainzApiBackend) lookupRecording(mbid mbtypes.MBID) (*Track, error) { filter := musicbrainzws2.IncludesFilter{ Includes: []string{"artist-credits"}, } - recording, err := b.mbClient.LookupRecording(ctx, mbid, filter) + recording, err := b.mbClient.LookupRecording(mbid, filter) if err != nil { return nil, err } diff --git a/internal/backends/maloja/client.go b/internal/backends/maloja/client.go index b80cb56..249819a 100644 --- a/internal/backends/maloja/client.go +++ b/internal/backends/maloja/client.go @@ -1,5 +1,5 @@ /* -Copyright © 2023-2025 Philipp Wolfer +Copyright © 2023 Philipp Wolfer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -22,7 +22,6 @@ THE SOFTWARE. package maloja import ( - "context" "errors" "strconv" @@ -49,10 +48,9 @@ func NewClient(serverURL string, token string) Client { } } -func (c Client) GetScrobbles(ctx context.Context, page int, perPage int) (result GetScrobblesResult, err error) { +func (c Client) GetScrobbles(page int, perPage int) (result GetScrobblesResult, err error) { const path = "/apis/mlj_1/scrobbles" response, err := c.HTTPClient.R(). - SetContext(ctx). SetQueryParams(map[string]string{ "page": strconv.Itoa(page), "perpage": strconv.Itoa(perPage), @@ -67,11 +65,10 @@ func (c Client) GetScrobbles(ctx context.Context, page int, perPage int) (result return } -func (c Client) NewScrobble(ctx context.Context, scrobble NewScrobble) (result NewScrobbleResult, err error) { +func (c Client) NewScrobble(scrobble NewScrobble) (result NewScrobbleResult, err error) { const path = "/apis/mlj_1/newscrobble" scrobble.Key = c.token response, err := c.HTTPClient.R(). - SetContext(ctx). SetBody(scrobble). SetResult(&result). Post(path) diff --git a/internal/backends/maloja/client_test.go b/internal/backends/maloja/client_test.go index 415f911..54316a8 100644 --- a/internal/backends/maloja/client_test.go +++ b/internal/backends/maloja/client_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2023-2025 Philipp Wolfer +Copyright © 2023 Philipp Wolfer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -22,7 +22,6 @@ THE SOFTWARE. package maloja_test import ( - "context" "net/http" "testing" @@ -49,8 +48,7 @@ func TestGetScrobbles(t *testing.T) { "https://maloja.example.com/apis/mlj_1/scrobbles", "testdata/scrobbles.json") - ctx := context.Background() - result, err := client.GetScrobbles(ctx, 0, 2) + result, err := client.GetScrobbles(0, 2) require.NoError(t, err) assert := assert.New(t) @@ -71,13 +69,12 @@ func TestNewScrobble(t *testing.T) { url := server + "/apis/mlj_1/newscrobble" httpmock.RegisterResponder("POST", url, responder) - ctx := context.Background() scrobble := maloja.NewScrobble{ Title: "Oweynagat", Artist: "Dool", Time: 1699574369, } - result, err := client.NewScrobble(ctx, scrobble) + result, err := client.NewScrobble(scrobble) require.NoError(t, err) assert.Equal(t, "success", result.Status) diff --git a/internal/backends/maloja/maloja.go b/internal/backends/maloja/maloja.go index f082d9b..8968942 100644 --- a/internal/backends/maloja/maloja.go +++ b/internal/backends/maloja/maloja.go @@ -17,7 +17,6 @@ Scotty. If not, see . package maloja import ( - "context" "errors" "sort" "strings" @@ -64,7 +63,7 @@ func (b *MalojaApiBackend) InitConfig(config *config.ServiceConfig) error { func (b *MalojaApiBackend) StartImport() error { return nil } func (b *MalojaApiBackend) FinishImport() error { return nil } -func (b *MalojaApiBackend) ExportListens(ctx context.Context, oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { +func (b *MalojaApiBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { page := 0 perPage := MaxItemsPerGet @@ -78,7 +77,7 @@ func (b *MalojaApiBackend) ExportListens(ctx context.Context, oldestTimestamp ti out: for { - result, err := b.client.GetScrobbles(ctx, page, perPage) + result, err := b.client.GetScrobbles(page, perPage) if err != nil { p.Export.Abort() progress <- p @@ -112,7 +111,7 @@ out: results <- models.ListensResult{Items: listens} } -func (b *MalojaApiBackend) ImportListens(ctx context.Context, export models.ListensResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { +func (b *MalojaApiBackend) ImportListens(export models.ListensResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { p := models.TransferProgress{}.FromImportResult(importResult, false) for _, listen := range export.Items { scrobble := NewScrobble{ @@ -125,7 +124,7 @@ func (b *MalojaApiBackend) ImportListens(ctx context.Context, export models.List Nofix: b.nofix, } - resp, err := b.client.NewScrobble(ctx, scrobble) + resp, err := b.client.NewScrobble(scrobble) if err != nil { return importResult, err } else if resp.Status != "success" { diff --git a/internal/backends/scrobblerlog/scrobblerlog.go b/internal/backends/scrobblerlog/scrobblerlog.go index 6d331ce..db4e349 100644 --- a/internal/backends/scrobblerlog/scrobblerlog.go +++ b/internal/backends/scrobblerlog/scrobblerlog.go @@ -17,7 +17,7 @@ Scotty. If not, see . package scrobblerlog import ( - "context" + "bufio" "fmt" "os" "sort" @@ -105,7 +105,8 @@ func (b *ScrobblerLogBackend) StartImport() error { b.append = false } else { // Verify existing file is a scrobbler log - if err = b.log.ReadHeader(file); err != nil { + reader := bufio.NewReader(file) + if err = b.log.ReadHeader(reader); err != nil { file.Close() return err } @@ -130,7 +131,7 @@ func (b *ScrobblerLogBackend) FinishImport() error { return b.file.Close() } -func (b *ScrobblerLogBackend) ExportListens(ctx context.Context, oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { +func (b *ScrobblerLogBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { file, err := os.Open(b.filePath) p := models.TransferProgress{ Export: &models.Progress{}, @@ -167,7 +168,7 @@ func (b *ScrobblerLogBackend) ExportListens(ctx context.Context, oldestTimestamp results <- models.ListensResult{Items: listens} } -func (b *ScrobblerLogBackend) ImportListens(ctx context.Context, export models.ListensResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { +func (b *ScrobblerLogBackend) ImportListens(export models.ListensResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { records := make([]scrobblerlog.Record, len(export.Items)) for i, listen := range export.Items { records[i] = listenToRecord(listen) diff --git a/internal/backends/spotify/client.go b/internal/backends/spotify/client.go index 94d50ac..ff2b0a3 100644 --- a/internal/backends/spotify/client.go +++ b/internal/backends/spotify/client.go @@ -1,5 +1,5 @@ /* -Copyright © 2023-2025 Philipp Wolfer +Copyright © 2023 Philipp Wolfer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -59,18 +59,17 @@ func NewClient(token oauth2.TokenSource) Client { } } -func (c Client) RecentlyPlayedAfter(ctx context.Context, after time.Time, limit int) (RecentlyPlayedResult, error) { - return c.recentlyPlayed(ctx, &after, nil, limit) +func (c Client) RecentlyPlayedAfter(after time.Time, limit int) (RecentlyPlayedResult, error) { + return c.recentlyPlayed(&after, nil, limit) } -func (c Client) RecentlyPlayedBefore(ctx context.Context, before time.Time, limit int) (RecentlyPlayedResult, error) { - return c.recentlyPlayed(ctx, nil, &before, limit) +func (c Client) RecentlyPlayedBefore(before time.Time, limit int) (RecentlyPlayedResult, error) { + return c.recentlyPlayed(nil, &before, limit) } -func (c Client) recentlyPlayed(ctx context.Context, after *time.Time, before *time.Time, limit int) (result RecentlyPlayedResult, err error) { +func (c Client) recentlyPlayed(after *time.Time, before *time.Time, limit int) (result RecentlyPlayedResult, err error) { const path = "/me/player/recently-played" request := c.HTTPClient.R(). - SetContext(ctx). SetQueryParam("limit", strconv.Itoa(limit)). SetResult(&result) if after != nil { @@ -86,10 +85,9 @@ func (c Client) recentlyPlayed(ctx context.Context, after *time.Time, before *ti return } -func (c Client) UserTracks(ctx context.Context, offset int, limit int) (result TracksResult, err error) { +func (c Client) UserTracks(offset int, limit int) (result TracksResult, err error) { const path = "/me/tracks" response, err := c.HTTPClient.R(). - SetContext(ctx). SetQueryParams(map[string]string{ "offset": strconv.Itoa(offset), "limit": strconv.Itoa(limit), diff --git a/internal/backends/spotify/client_test.go b/internal/backends/spotify/client_test.go index 8135e1d..78ff063 100644 --- a/internal/backends/spotify/client_test.go +++ b/internal/backends/spotify/client_test.go @@ -1,5 +1,5 @@ /* -Copyright © 2023-2025 Philipp Wolfer +Copyright © 2023 Philipp Wolfer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -22,7 +22,6 @@ THE SOFTWARE. package spotify_test import ( - "context" "net/http" "testing" "time" @@ -48,8 +47,7 @@ func TestRecentlyPlayedAfter(t *testing.T) { "https://api.spotify.com/v1/me/player/recently-played", "testdata/recently-played.json") - ctx := context.Background() - result, err := client.RecentlyPlayedAfter(ctx, time.Now(), 3) + result, err := client.RecentlyPlayedAfter(time.Now(), 3) require.NoError(t, err) assert := assert.New(t) @@ -69,8 +67,7 @@ func TestGetUserTracks(t *testing.T) { "https://api.spotify.com/v1/me/tracks", "testdata/user-tracks.json") - ctx := context.Background() - result, err := client.UserTracks(ctx, 0, 2) + result, err := client.UserTracks(0, 2) require.NoError(t, err) assert := assert.New(t) diff --git a/internal/backends/spotify/spotify.go b/internal/backends/spotify/spotify.go index b00ebba..5d45087 100644 --- a/internal/backends/spotify/spotify.go +++ b/internal/backends/spotify/spotify.go @@ -18,7 +18,6 @@ Scotty. If not, see . package spotify import ( - "context" "math" "net/url" "sort" @@ -96,7 +95,7 @@ func (b *SpotifyApiBackend) OAuth2Setup(token oauth2.TokenSource) error { return nil } -func (b *SpotifyApiBackend) ExportListens(ctx context.Context, oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { +func (b *SpotifyApiBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { startTime := time.Now() minTime := oldestTimestamp @@ -108,7 +107,7 @@ func (b *SpotifyApiBackend) ExportListens(ctx context.Context, oldestTimestamp t } for { - result, err := b.client.RecentlyPlayedAfter(ctx, minTime, MaxItemsPerGet) + result, err := b.client.RecentlyPlayedAfter(minTime, MaxItemsPerGet) if err != nil { p.Export.Abort() progress <- p @@ -163,7 +162,7 @@ func (b *SpotifyApiBackend) ExportListens(ctx context.Context, oldestTimestamp t progress <- p } -func (b *SpotifyApiBackend) ExportLoves(ctx context.Context, oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.TransferProgress) { +func (b *SpotifyApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.TransferProgress) { // Choose a high offset, we attempt to search the loves backwards starting // at the oldest one. offset := math.MaxInt32 @@ -179,7 +178,7 @@ func (b *SpotifyApiBackend) ExportLoves(ctx context.Context, oldestTimestamp tim out: for { - result, err := b.client.UserTracks(ctx, offset, perPage) + result, err := b.client.UserTracks(offset, perPage) if err != nil { p.Export.Abort() progress <- p diff --git a/internal/backends/spotifyhistory/spotifyhistory.go b/internal/backends/spotifyhistory/spotifyhistory.go index 76d0c9e..d5c87bb 100644 --- a/internal/backends/spotifyhistory/spotifyhistory.go +++ b/internal/backends/spotifyhistory/spotifyhistory.go @@ -18,7 +18,6 @@ Scotty. If not, see . package spotifyhistory import ( - "context" "os" "path" "path/filepath" @@ -73,7 +72,7 @@ func (b *SpotifyHistoryBackend) InitConfig(config *config.ServiceConfig) error { return nil } -func (b *SpotifyHistoryBackend) ExportListens(ctx context.Context, oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { +func (b *SpotifyHistoryBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.TransferProgress) { files, err := filepath.Glob(path.Join(b.dirPath, historyFileGlob)) p := models.TransferProgress{ Export: &models.Progress{}, @@ -90,18 +89,11 @@ func (b *SpotifyHistoryBackend) ExportListens(ctx context.Context, oldestTimesta fileCount := int64(len(files)) p.Export.Total = fileCount for i, filePath := range files { - if err := ctx.Err(); err != nil { - results <- models.ListensResult{Error: err} - p.Export.Abort() - progress <- p - return - } - history, err := readHistoryFile(filePath) if err != nil { - results <- models.ListensResult{Error: err} p.Export.Abort() progress <- p + results <- models.ListensResult{Error: err} return } listens := history.AsListenList(ListenListOptions{ diff --git a/internal/backends/subsonic/subsonic.go b/internal/backends/subsonic/subsonic.go index aa1b1e3..2098688 100644 --- a/internal/backends/subsonic/subsonic.go +++ b/internal/backends/subsonic/subsonic.go @@ -17,7 +17,6 @@ Scotty. If not, see . package subsonic import ( - "context" "net/http" "sort" "time" @@ -64,7 +63,7 @@ func (b *SubsonicApiBackend) InitConfig(config *config.ServiceConfig) error { return nil } -func (b *SubsonicApiBackend) ExportLoves(ctx context.Context, oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.TransferProgress) { +func (b *SubsonicApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.TransferProgress) { err := b.client.Authenticate(b.password) p := models.TransferProgress{ Export: &models.Progress{}, diff --git a/internal/cli/progress.go b/internal/cli/progress.go index db862a1..e93ec18 100644 --- a/internal/cli/progress.go +++ b/internal/cli/progress.go @@ -18,7 +18,6 @@ Scotty. If not, see . package cli import ( - "context" "sync" "time" @@ -40,10 +39,9 @@ type progressBarUpdater struct { importedItems int } -func setupProgressBars(ctx context.Context, updateChan chan models.TransferProgress) progressBarUpdater { +func setupProgressBars(updateChan chan models.TransferProgress) progressBarUpdater { wg := &sync.WaitGroup{} - p := mpb.NewWithContext( - ctx, + p := mpb.New( mpb.WithWaitGroup(wg), mpb.WithOutput(color.Output), // mpb.WithWidth(64), diff --git a/internal/cli/transfer.go b/internal/cli/transfer.go index 3aabb4b..62dd079 100644 --- a/internal/cli/transfer.go +++ b/internal/cli/transfer.go @@ -16,7 +16,6 @@ Scotty. If not, see . package cli import ( - "context" "errors" "fmt" "strconv" @@ -110,32 +109,20 @@ func (c *TransferCmd[E, I, R]) Transfer(exp backends.ExportProcessor[R], imp bac } printTimestamp("From timestamp: %v (%v)", timestamp) - // Use a context with cancel to abort the transfer - ctx, cancel := context.WithCancel(context.Background()) - // Prepare progress bars progressChan := make(chan models.TransferProgress) - progress := setupProgressBars(ctx, progressChan) + progress := setupProgressBars(progressChan) wg := &sync.WaitGroup{} // Export from source exportChan := make(chan R, 1000) - go exp.Process(ctx, wg, timestamp, exportChan, progressChan) + go exp.Process(wg, timestamp, exportChan, progressChan) // Import into target resultChan := make(chan models.ImportResult) - go imp.Process(ctx, wg, exportChan, resultChan, progressChan) + go imp.Process(wg, exportChan, resultChan, progressChan) result := <-resultChan - - // If the import has errored, the context can be cancelled immediately - if result.Error != nil { - cancel() - } else { - defer cancel() - } - - // Wait for all goroutines to finish wg.Wait() progress.close() diff --git a/internal/models/interfaces.go b/internal/models/interfaces.go index 2f4beaf..bb97dac 100644 --- a/internal/models/interfaces.go +++ b/internal/models/interfaces.go @@ -17,7 +17,6 @@ Scotty. If not, see . package models import ( - "context" "time" // "go.uploadedlobster.com/scotty/internal/auth" @@ -56,7 +55,7 @@ type ListensExport interface { // Returns a list of all listens newer then oldestTimestamp. // The returned list of listens is supposed to be ordered by the // Listen.ListenedAt timestamp, with the oldest entry first. - ExportListens(ctx context.Context, oldestTimestamp time.Time, results chan ListensResult, progress chan TransferProgress) + ExportListens(oldestTimestamp time.Time, results chan ListensResult, progress chan TransferProgress) } // Must be implemented by services supporting the import of listens. @@ -64,7 +63,7 @@ type ListensImport interface { ImportBackend // Imports the given list of listens. - ImportListens(ctx context.Context, export ListensResult, importResult ImportResult, progress chan TransferProgress) (ImportResult, error) + ImportListens(export ListensResult, importResult ImportResult, progress chan TransferProgress) (ImportResult, error) } // Must be implemented by services supporting the export of loves. @@ -74,7 +73,7 @@ type LovesExport interface { // Returns a list of all loves newer then oldestTimestamp. // The returned list of listens is supposed to be ordered by the // Love.Created timestamp, with the oldest entry first. - ExportLoves(ctx context.Context, oldestTimestamp time.Time, results chan LovesResult, progress chan TransferProgress) + ExportLoves(oldestTimestamp time.Time, results chan LovesResult, progress chan TransferProgress) } // Must be implemented by services supporting the import of loves. @@ -82,5 +81,5 @@ type LovesImport interface { ImportBackend // Imports the given list of loves. - ImportLoves(ctx context.Context, export LovesResult, importResult ImportResult, progress chan TransferProgress) (ImportResult, error) + ImportLoves(export LovesResult, importResult ImportResult, progress chan TransferProgress) (ImportResult, error) } diff --git a/pkg/scrobblerlog/parser.go b/pkg/scrobblerlog/parser.go index 8bad56d..6b9d1ba 100644 --- a/pkg/scrobblerlog/parser.go +++ b/pkg/scrobblerlog/parser.go @@ -94,7 +94,7 @@ func (l *ScrobblerLog) Parse(data io.Reader, ignoreSkipped bool) error { l.Records = make([]Record, 0) reader := bufio.NewReader(data) - err := l.readHeader(reader) + err := l.ReadHeader(reader) if err != nil { return err } @@ -173,11 +173,7 @@ func (l *ScrobblerLog) Append(data io.Writer, records []Record) (lastTimestamp t // Parses just the header of a scrobbler log file from the given reader. // // This function sets [ScrobblerLog.TZ] and [ScrobblerLog.Client]. -func (l *ScrobblerLog) ReadHeader(reader io.Reader) error { - return l.readHeader(bufio.NewReader(reader)) -} - -func (l *ScrobblerLog) readHeader(reader *bufio.Reader) error { +func (l *ScrobblerLog) ReadHeader(reader *bufio.Reader) error { // Skip header for i := 0; i < 3; i++ { line, _, err := reader.ReadLine()