diff --git a/CHANGES.md b/CHANGES.md index 5dfd892..a0a60f2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,16 @@ # Scotty Changelog ## 0.6.0 - WIP -- JSPF: Implemented export as loves and listens +- Fix program hanging endlessly if import fails (#11) +- If import fails still store the last successfully imported timestamp +- Show progress bars as aborted on export / import error +- JSPF: implemented export as loves and listens +- JSPF: write track duration +- JSPF: read username and recording MSID +- JSPF: add MusicBrainz playlist extension in append mode, if it does not exist + in the existing JSPF file +- scrobblerlog: fix timezone not being set from config (#6) +- scrobblerlog: fix listen export not considering latest timestamp ## 0.5.2 - 2025-05-01 @@ -20,9 +29,9 @@ - ListenBrainz: log missing recording MBID on love import - Subsonic: support OpenSubsonic fields for recording MBID and genres (#5) - Subsonic: fixed progress for loves export -- scrobblerlog: add "time-zone" config option (#6). +- scrobblerlog: add "time-zone" config option (#6) - scrobblerlog: fixed progress for listen export -- scrobblerlog: renamed setting `include-skipped` to `ignore-skipped`. +- scrobblerlog: renamed setting `include-skipped` to `ignore-skipped` Note: 386 builds for Linux are not available with this release due to an incompatibility with latest version of gorm. diff --git a/internal/backends/deezer/deezer.go b/internal/backends/deezer/deezer.go index 756e271..4ba367d 100644 --- a/internal/backends/deezer/deezer.go +++ b/internal/backends/deezer/deezer.go @@ -88,15 +88,13 @@ func (b *DeezerApiBackend) ExportListens(oldestTimestamp time.Time, results chan totalDuration := startTime.Sub(oldestTimestamp) - defer close(results) - p := models.Progress{Total: int64(totalDuration.Seconds())} out: for { result, err := b.client.UserHistory(offset, perPage) if err != nil { - progress <- p.Complete() + progress <- p.Abort() results <- models.ListensResult{Error: err} return } @@ -155,8 +153,6 @@ func (b *DeezerApiBackend) ExportLoves(oldestTimestamp time.Time, results chan m offset := math.MaxInt32 perPage := MaxItemsPerGet - defer close(results) - p := models.Progress{Total: int64(perPage)} var totalCount int @@ -164,7 +160,7 @@ out: for { result, err := b.client.UserTracks(offset, perPage) if err != nil { - progress <- p.Complete() + progress <- p.Abort() results <- models.LovesResult{Error: err} return } diff --git a/internal/backends/export.go b/internal/backends/export.go index 44b8757..0346af2 100644 --- a/internal/backends/export.go +++ b/internal/backends/export.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Philipp Wolfer +Copyright © 2023-2025 Philipp Wolfer Scotty is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -35,8 +35,9 @@ func (p ListensExportProcessor) ExportBackend() models.Backend { } func (p ListensExportProcessor) Process(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.Progress) { + defer close(results) + defer close(progress) p.Backend.ExportListens(oldestTimestamp, results, progress) - close(progress) } type LovesExportProcessor struct { @@ -48,6 +49,7 @@ func (p LovesExportProcessor) ExportBackend() models.Backend { } func (p LovesExportProcessor) Process(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.Progress) { + defer close(results) + defer close(progress) p.Backend.ExportLoves(oldestTimestamp, results, progress) - close(progress) } diff --git a/internal/backends/funkwhale/funkwhale.go b/internal/backends/funkwhale/funkwhale.go index 3e296c1..e32a952 100644 --- a/internal/backends/funkwhale/funkwhale.go +++ b/internal/backends/funkwhale/funkwhale.go @@ -64,8 +64,6 @@ func (b *FunkwhaleApiBackend) ExportListens(oldestTimestamp time.Time, results c page := 1 perPage := MaxItemsPerGet - defer close(results) - // We need to gather the full list of listens in order to sort them listens := make(models.ListensList, 0, 2*perPage) p := models.Progress{Total: int64(perPage)} @@ -113,8 +111,6 @@ func (b *FunkwhaleApiBackend) ExportLoves(oldestTimestamp time.Time, results cha page := 1 perPage := MaxItemsPerGet - defer close(results) - // We need to gather the full list of listens in order to sort them loves := make(models.LovesList, 0, 2*perPage) p := models.Progress{Total: int64(perPage)} @@ -123,7 +119,7 @@ out: for { result, err := b.client.GetFavoriteTracks(page, perPage) if err != nil { - progress <- p.Complete() + progress <- p.Abort() results <- models.LovesResult{Error: err} return } diff --git a/internal/backends/import.go b/internal/backends/import.go index 6173a53..407082c 100644 --- a/internal/backends/import.go +++ b/internal/backends/import.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Philipp Wolfer +Copyright © 2023-2025 Philipp Wolfer This file is part of Scotty. @@ -41,7 +41,7 @@ func (p ListensImportProcessor) Process(results chan models.ListensResult, out c func (p ListensImportProcessor) Import(export models.ListensResult, result models.ImportResult, out chan models.ImportResult, progress chan models.Progress) (models.ImportResult, error) { if export.Error != nil { - return handleError(result, export.Error, progress), export.Error + return result, export.Error } if export.Total > 0 { @@ -51,7 +51,7 @@ func (p ListensImportProcessor) Import(export models.ListensResult, result model } importResult, err := p.Backend.ImportListens(export, result, progress) if err != nil { - return handleError(result, err, progress), err + return importResult, err } return importResult, nil } @@ -70,7 +70,7 @@ func (p LovesImportProcessor) Process(results chan models.LovesResult, out chan func (p LovesImportProcessor) Import(export models.LovesResult, result models.ImportResult, out chan models.ImportResult, progress chan models.Progress) (models.ImportResult, error) { if export.Error != nil { - return handleError(result, export.Error, progress), export.Error + return result, export.Error } if export.Total > 0 { @@ -80,7 +80,7 @@ func (p LovesImportProcessor) Import(export models.LovesResult, result models.Im } importResult, err := p.Backend.ImportLoves(export, result, progress) if err != nil { - return handleError(importResult, err, progress), err + return importResult, err } return importResult, nil } @@ -89,35 +89,35 @@ func process[R models.LovesResult | models.ListensResult, P ImportProcessor[R]]( defer close(out) defer close(progress) result := models.ImportResult{} + p := models.Progress{} - err := processor.ImportBackend().StartImport() - if err != nil { + if err := processor.ImportBackend().StartImport(); err != nil { out <- handleError(result, err, progress) return } for exportResult := range results { importResult, err := processor.Import(exportResult, result, out, progress) + result.Update(importResult) if err != nil { + processor.ImportBackend().FinishImport() out <- handleError(result, err, progress) return } - result.Update(importResult) - progress <- models.Progress{}.FromImportResult(result) + progress <- p.FromImportResult(result) } - err = processor.ImportBackend().FinishImport() - if err != nil { + if err := processor.ImportBackend().FinishImport(); err != nil { out <- handleError(result, err, progress) return } - progress <- models.Progress{}.FromImportResult(result).Complete() + progress <- p.FromImportResult(result).Complete() out <- result } func handleError(result models.ImportResult, err error, progress chan models.Progress) models.ImportResult { result.Error = err - progress <- models.Progress{}.FromImportResult(result).Complete() + progress <- models.Progress{}.FromImportResult(result).Abort() return result } diff --git a/internal/backends/jspf/jspf.go b/internal/backends/jspf/jspf.go index 6551f15..e981741 100644 --- a/internal/backends/jspf/jspf.go +++ b/internal/backends/jspf/jspf.go @@ -77,14 +77,11 @@ func (b *JSPFBackend) InitConfig(config *config.ServiceConfig) error { Title: config.GetString("title"), Creator: config.GetString("username"), Identifier: config.GetString("identifier"), + Date: time.Now(), Tracks: make([]jspf.Track, 0), - Extension: jspf.ExtensionMap{ - jspf.MusicBrainzPlaylistExtensionID: jspf.MusicBrainzPlaylistExtension{ - LastModifiedAt: time.Now(), - Public: true, - }, - }, } + + b.addMusicBrainzPlaylistExtension() return nil } @@ -97,11 +94,9 @@ func (b *JSPFBackend) FinishImport() error { } func (b *JSPFBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.Progress) { - defer close(results) - err := b.readJSPF() if err != nil { - progress <- models.Progress{}.Complete() + progress <- models.Progress{}.Abort() results <- models.ListensResult{Error: err} return } @@ -131,11 +126,9 @@ func (b *JSPFBackend) ImportListens(export models.ListensResult, importResult mo } func (b *JSPFBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.Progress) { - defer close(results) - err := b.readJSPF() if err != nil { - progress <- models.Progress{}.Complete() + progress <- models.Progress{}.Abort() results <- models.LovesResult{Error: err} return } @@ -331,6 +324,7 @@ func (b *JSPFBackend) readJSPF() error { return err } b.playlist = playlist.Playlist + b.addMusicBrainzPlaylistExtension() } } @@ -350,3 +344,13 @@ func (b *JSPFBackend) writeJSPF() error { defer file.Close() return playlist.Write(file) } + +func (b *JSPFBackend) addMusicBrainzPlaylistExtension() { + if b.playlist.Extension == nil { + b.playlist.Extension = make(jspf.ExtensionMap, 1) + } + extension := jspf.MusicBrainzPlaylistExtension{Public: true} + b.playlist.Extension.Get(jspf.MusicBrainzPlaylistExtensionID, &extension) + extension.LastModifiedAt = time.Now() + b.playlist.Extension[jspf.MusicBrainzPlaylistExtensionID] = extension +} diff --git a/internal/backends/lastfm/lastfm.go b/internal/backends/lastfm/lastfm.go index 76fe9c7..d45f793 100644 --- a/internal/backends/lastfm/lastfm.go +++ b/internal/backends/lastfm/lastfm.go @@ -93,8 +93,6 @@ func (b *LastfmApiBackend) ExportListens(oldestTimestamp time.Time, results chan minTime := oldestTimestamp perPage := MaxItemsPerGet - defer close(results) - // We need to gather the full list of listens in order to sort them p := models.Progress{Total: int64(page)} @@ -110,7 +108,7 @@ out: result, err := b.client.User.GetRecentTracks(args) if err != nil { results <- models.ListensResult{Error: err} - progress <- p.Complete() + progress <- p.Abort() return } @@ -129,7 +127,7 @@ out: timestamp, err := strconv.ParseInt(scrobble.Date.Uts, 10, 64) if err != nil { results <- models.ListensResult{Error: err} - progress <- p.Complete() + progress <- p.Abort() break out } if timestamp > oldestTimestamp.Unix() { @@ -258,8 +256,6 @@ func (b *LastfmApiBackend) ExportLoves(oldestTimestamp time.Time, results chan m page := 1 perPage := MaxItemsPerGet - defer close(results) - loves := make(models.LovesList, 0, 2*MaxItemsPerGet) p := models.Progress{Total: int64(perPage)} var totalCount int @@ -272,7 +268,7 @@ out: "page": page, }) if err != nil { - progress <- p.Complete() + progress <- p.Abort() results <- models.LovesResult{Error: err} return } @@ -286,7 +282,7 @@ out: for _, track := range result.Tracks { timestamp, err := strconv.ParseInt(track.Date.Uts, 10, 64) if err != nil { - progress <- p.Complete() + progress <- p.Abort() results <- models.LovesResult{Error: err} return } diff --git a/internal/backends/listenbrainz/listenbrainz.go b/internal/backends/listenbrainz/listenbrainz.go index 6c7b747..fffe0f0 100644 --- a/internal/backends/listenbrainz/listenbrainz.go +++ b/internal/backends/listenbrainz/listenbrainz.go @@ -81,14 +81,12 @@ func (b *ListenBrainzApiBackend) ExportListens(oldestTimestamp time.Time, result totalDuration := startTime.Sub(minTime) - defer close(results) - p := models.Progress{Total: int64(totalDuration.Seconds())} for { result, err := b.client.GetListens(b.username, time.Now(), minTime) if err != nil { - progress <- p.Complete() + progress <- p.Abort() results <- models.ListensResult{Error: err} return } @@ -195,15 +193,15 @@ func (b *ListenBrainzApiBackend) ImportListens(export models.ListensResult, impo } func (b *ListenBrainzApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.Progress) { - defer close(results) exportChan := make(chan models.LovesResult) p := models.Progress{} go b.exportLoves(oldestTimestamp, exportChan) for existingLoves := range exportChan { if existingLoves.Error != nil { - progress <- p.Complete() + progress <- p.Abort() results <- models.LovesResult{Error: existingLoves.Error} + return } p.Total = int64(existingLoves.Total) diff --git a/internal/backends/maloja/maloja.go b/internal/backends/maloja/maloja.go index e9e3348..a22393b 100644 --- a/internal/backends/maloja/maloja.go +++ b/internal/backends/maloja/maloja.go @@ -67,8 +67,6 @@ func (b *MalojaApiBackend) ExportListens(oldestTimestamp time.Time, results chan page := 0 perPage := MaxItemsPerGet - defer close(results) - // We need to gather the full list of listens in order to sort them listens := make(models.ListensList, 0, 2*perPage) p := models.Progress{Total: int64(perPage)} @@ -107,6 +105,7 @@ out: } func (b *MalojaApiBackend) ImportListens(export models.ListensResult, importResult models.ImportResult, progress chan models.Progress) (models.ImportResult, error) { + p := models.Progress{}.FromImportResult(importResult) for _, listen := range export.Items { scrobble := NewScrobble{ Title: listen.TrackName, @@ -127,7 +126,7 @@ func (b *MalojaApiBackend) ImportListens(export models.ListensResult, importResu importResult.UpdateTimestamp(listen.ListenedAt) importResult.ImportCount += 1 - progress <- models.Progress{}.FromImportResult(importResult) + progress <- p.FromImportResult(importResult) } return importResult, nil diff --git a/internal/backends/scrobblerlog/scrobblerlog.go b/internal/backends/scrobblerlog/scrobblerlog.go index 14ee24f..7890971 100644 --- a/internal/backends/scrobblerlog/scrobblerlog.go +++ b/internal/backends/scrobblerlog/scrobblerlog.go @@ -132,10 +132,9 @@ func (b *ScrobblerLogBackend) FinishImport() error { } func (b *ScrobblerLogBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.Progress) { - defer close(results) file, err := os.Open(b.filePath) if err != nil { - progress <- models.Progress{}.Complete() + progress <- models.Progress{}.Abort() results <- models.ListensResult{Error: err} return } diff --git a/internal/backends/spotify/spotify.go b/internal/backends/spotify/spotify.go index 8c17903..be48dfe 100644 --- a/internal/backends/spotify/spotify.go +++ b/internal/backends/spotify/spotify.go @@ -101,14 +101,12 @@ func (b *SpotifyApiBackend) ExportListens(oldestTimestamp time.Time, results cha totalDuration := startTime.Sub(oldestTimestamp) - defer close(results) - p := models.Progress{Total: int64(totalDuration.Seconds())} for { result, err := b.client.RecentlyPlayedAfter(minTime, MaxItemsPerGet) if err != nil { - progress <- p.Complete() + progress <- p.Abort() results <- models.ListensResult{Error: err} return } @@ -120,7 +118,7 @@ func (b *SpotifyApiBackend) ExportListens(oldestTimestamp time.Time, results cha // Set minTime to the newest returned listen after, err := strconv.ParseInt(result.Cursors.After, 10, 64) if err != nil { - progress <- p.Complete() + progress <- p.Abort() results <- models.ListensResult{Error: err} return } else if after <= minTime.Unix() { @@ -163,8 +161,6 @@ func (b *SpotifyApiBackend) ExportLoves(oldestTimestamp time.Time, results chan offset := math.MaxInt32 perPage := MaxItemsPerGet - defer close(results) - p := models.Progress{Total: int64(perPage)} totalCount := 0 exportCount := 0 @@ -173,7 +169,7 @@ out: for { result, err := b.client.UserTracks(offset, perPage) if err != nil { - progress <- p.Complete() + progress <- p.Abort() results <- models.LovesResult{Error: err} return } diff --git a/internal/backends/spotifyhistory/spotifyhistory.go b/internal/backends/spotifyhistory/spotifyhistory.go index 1c986be..23309f3 100644 --- a/internal/backends/spotifyhistory/spotifyhistory.go +++ b/internal/backends/spotifyhistory/spotifyhistory.go @@ -73,11 +73,9 @@ func (b *SpotifyHistoryBackend) InitConfig(config *config.ServiceConfig) error { } func (b *SpotifyHistoryBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.Progress) { - defer close(results) - files, err := filepath.Glob(path.Join(b.dirPath, historyFileGlob)) if err != nil { - progress <- models.Progress{}.Complete() + progress <- models.Progress{}.Abort() results <- models.ListensResult{Error: err} return } @@ -88,7 +86,7 @@ func (b *SpotifyHistoryBackend) ExportListens(oldestTimestamp time.Time, results for i, filePath := range files { history, err := readHistoryFile(filePath) if err != nil { - progress <- models.Progress{}.Complete() + progress <- models.Progress{}.Abort() results <- models.ListensResult{Error: err} return } diff --git a/internal/backends/subsonic/subsonic.go b/internal/backends/subsonic/subsonic.go index d605324..370765e 100644 --- a/internal/backends/subsonic/subsonic.go +++ b/internal/backends/subsonic/subsonic.go @@ -64,17 +64,16 @@ func (b *SubsonicApiBackend) InitConfig(config *config.ServiceConfig) error { } func (b *SubsonicApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.Progress) { - defer close(results) err := b.client.Authenticate(b.password) if err != nil { - progress <- models.Progress{}.Complete() + progress <- models.Progress{}.Abort() results <- models.LovesResult{Error: err} return } starred, err := b.client.GetStarred2(map[string]string{}) if err != nil { - progress <- models.Progress{}.Complete() + progress <- models.Progress{}.Abort() results <- models.LovesResult{Error: err} return } diff --git a/internal/cli/progress.go b/internal/cli/progress.go index 6d4421d..88339ef 100644 --- a/internal/cli/progress.go +++ b/internal/cli/progress.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Philipp Wolfer +Copyright © 2023-2025 Philipp Wolfer This file is part of Scotty. @@ -28,7 +28,8 @@ import ( "go.uploadedlobster.com/scotty/internal/models" ) -func progressBar(wg *sync.WaitGroup, exportProgress chan models.Progress, importProgress chan models.Progress) *mpb.Progress { +func progressBar(exportProgress chan models.Progress, importProgress chan models.Progress) *mpb.Progress { + wg := &sync.WaitGroup{} p := mpb.New( mpb.WithWaitGroup(wg), mpb.WithOutput(color.Output), @@ -58,10 +59,12 @@ func setupProgressBar(p *mpb.Progress, name string) *mpb.Bar { ), mpb.AppendDecorators( decor.OnComplete( - decor.EwmaETA(decor.ET_STYLE_GO, 0, decor.WC{C: decor.DSyncWidth}), + decor.OnAbort( + decor.EwmaETA(decor.ET_STYLE_GO, 0, decor.WC{C: decor.DSyncWidth}), + i18n.Tr("aborted"), + ), i18n.Tr("done"), ), - // decor.OnComplete(decor.Percentage(decor.WC{W: 5, C: decor.DSyncWidthR}), "done"), decor.Name(" "), ), ) @@ -72,6 +75,10 @@ func updateProgressBar(bar *mpb.Bar, wg *sync.WaitGroup, progressChan chan model defer wg.Done() lastIterTime := time.Now() for progress := range progressChan { + if progress.Aborted { + bar.Abort(false) + return + } oldIterTime := lastIterTime lastIterTime = time.Now() bar.EwmaSetCurrent(progress.Elapsed, lastIterTime.Sub(oldIterTime)) diff --git a/internal/cli/transfer.go b/internal/cli/transfer.go index 0ba04b9..0391d0d 100644 --- a/internal/cli/transfer.go +++ b/internal/cli/transfer.go @@ -1,5 +1,5 @@ /* -Copyright © 2023 Philipp Wolfer +Copyright © 2023-2025 Philipp Wolfer Scotty is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -19,7 +19,6 @@ import ( "errors" "fmt" "strconv" - "sync" "time" "github.com/spf13/cobra" @@ -112,8 +111,7 @@ func (c *TransferCmd[E, I, R]) Transfer(exp backends.ExportProcessor[R], imp bac // Prepare progress bars exportProgress := make(chan models.Progress) importProgress := make(chan models.Progress) - var wg sync.WaitGroup - progress := progressBar(&wg, exportProgress, importProgress) + progress := progressBar(exportProgress, importProgress) // Export from source exportChan := make(chan R, 1000) @@ -123,23 +121,20 @@ func (c *TransferCmd[E, I, R]) Transfer(exp backends.ExportProcessor[R], imp bac resultChan := make(chan models.ImportResult) go imp.Process(exportChan, resultChan, importProgress) result := <-resultChan - if timestamp.After(result.LastTimestamp) { - result.LastTimestamp = timestamp - } - wg.Wait() progress.Wait() + + // Update timestamp + err = c.updateTimestamp(&result, timestamp) + if err != nil { + return err + } + + fmt.Println(i18n.Tr("Imported %v of %v %s into %v.", + result.ImportCount, result.TotalCount, c.entity, c.targetName)) if result.Error != nil { printTimestamp("Import failed, last reported timestamp was %v (%s)", result.LastTimestamp) return result.Error } - fmt.Println(i18n.Tr("Imported %v of %v %s into %v.", - result.ImportCount, result.TotalCount, c.entity, c.targetName)) - - // Update timestamp - err = c.updateTimestamp(result, timestamp) - if err != nil { - return err - } // Print errors if len(result.ImportLog) > 0 { @@ -179,7 +174,7 @@ func (c *TransferCmd[E, I, R]) timestamp() (time.Time, error) { return time.Time{}, errors.New(i18n.Tr("invalid timestamp string \"%v\"", flagValue)) } -func (c *TransferCmd[E, I, R]) updateTimestamp(result models.ImportResult, oldTimestamp time.Time) error { +func (c *TransferCmd[E, I, R]) updateTimestamp(result *models.ImportResult, oldTimestamp time.Time) error { if oldTimestamp.After(result.LastTimestamp) { result.LastTimestamp = oldTimestamp } diff --git a/internal/models/models.go b/internal/models/models.go index f2dd71d..b8f9121 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -213,6 +213,7 @@ type Progress struct { Total int64 Elapsed int64 Completed bool + Aborted bool } func (p Progress) FromImportResult(result ImportResult) Progress { @@ -226,3 +227,8 @@ func (p Progress) Complete() Progress { p.Completed = true return p } + +func (p Progress) Abort() Progress { + p.Aborted = true + return p +} diff --git a/internal/translations/catalog.go b/internal/translations/catalog.go index 3eb2f7e..f0aaaae 100644 --- a/internal/translations/catalog.go +++ b/internal/translations/catalog.go @@ -42,12 +42,12 @@ var messageKeyToIndex = map[string]int{ "\tbackend: %v": 11, "\texport: %s": 0, "\timport: %s\n": 1, - "%v: %v": 48, + "%v: %v": 49, "Aborted": 8, "Access token": 19, "Access token received, you can use %v now.\n": 34, "Append to file": 21, - "Backend": 42, + "Backend": 43, "Check for duplicate listens on import (slower)": 24, "Client ID": 15, "Client secret": 16, @@ -57,45 +57,46 @@ var messageKeyToIndex = map[string]int{ "Error: OAuth state mismatch": 33, "Failed reading config: %v": 2, "File path": 20, - "From timestamp: %v (%v)": 44, + "From timestamp: %v (%v)": 45, "Ignore listens in incognito mode": 30, "Ignore skipped listens": 27, "Ignored duplicate listen %v: \"%v\" by %v (%v)": 25, - "Import failed, last reported timestamp was %v (%s)": 45, - "Import log:": 47, + "Import failed, last reported timestamp was %v (%s)": 47, + "Import log:": 48, "Imported %v of %v %s into %v.": 46, - "Latest timestamp: %v (%v)": 50, + "Latest timestamp: %v (%v)": 51, "Minimum playback duration for skipped tracks (seconds)": 31, - "No": 39, + "No": 40, "Playlist title": 22, "Saved service %v using backend %v": 5, "Server URL": 17, - "Service": 41, + "Service": 42, "Service \"%v\" deleted\n": 9, "Service name": 3, "Specify a time zone for the listen timestamps": 28, "The backend %v requires authentication. Authenticate now?": 6, "Token received, you can close this window now.": 12, - "Transferring %s from %s to %s…": 43, + "Transferring %s from %s to %s…": 44, "Unique playlist identifier": 23, "Updated service %v using backend %v\n": 10, "User name": 18, "Visit the URL for authorization: %v": 32, - "Yes": 38, + "Yes": 39, "a service with this name already exists": 4, - "backend %s does not implement %s": 13, - "done": 37, - "exporting": 35, - "importing": 36, - "invalid timestamp string \"%v\"": 49, - "key must only consist of A-Za-z0-9_-": 52, - "no configuration file defined, cannot write config": 51, - "no existing service configurations": 40, - "no service configuration \"%v\"": 53, - "unknown backend \"%s\"": 14, + "aborted": 37, + "backend %s does not implement %s": 13, + "done": 38, + "exporting": 35, + "importing": 36, + "invalid timestamp string \"%v\"": 50, + "key must only consist of A-Za-z0-9_-": 53, + "no configuration file defined, cannot write config": 52, + "no existing service configurations": 41, + "no service configuration \"%v\"": 54, + "unknown backend \"%s\"": 14, } -var deIndex = []uint32{ // 55 elements +var deIndex = []uint32{ // 56 elements // Entry 0 - 1F 0x00000000, 0x00000013, 0x00000027, 0x00000052, 0x0000005e, 0x0000008d, 0x000000bd, 0x00000104, @@ -107,14 +108,14 @@ var deIndex = []uint32{ // 55 elements 0x0000037e, 0x000003a4, 0x000003b4, 0x000003da, // Entry 20 - 3F 0x00000418, 0x00000443, 0x0000046d, 0x000004ad, - 0x000004b8, 0x000004c3, 0x000004ca, 0x000004cd, - 0x000004d2, 0x000004fb, 0x00000503, 0x0000050b, - 0x00000534, 0x00000552, 0x0000058f, 0x000005ba, - 0x000005c5, 0x000005d2, 0x000005f6, 0x00000619, - 0x0000066a, 0x000006a1, 0x000006c8, -} // Size: 244 bytes + 0x000004b8, 0x000004c3, 0x000004cf, 0x000004d6, + 0x000004d9, 0x000004de, 0x00000507, 0x0000050f, + 0x00000517, 0x00000540, 0x0000055e, 0x00000589, + 0x000005c6, 0x000005d1, 0x000005de, 0x00000602, + 0x00000625, 0x00000676, 0x000006ad, 0x000006d4, +} // Size: 248 bytes -const deData string = "" + // Size: 1736 bytes +const deData string = "" + // Size: 1748 bytes "\x04\x01\x09\x00\x0e\x02Export: %[1]s\x04\x01\x09\x01\x0a\x0e\x02Import:" + " %[1]s\x02Fehler beim Lesen der Konfiguration: %[1]v\x02Servicename\x02e" + "in Service mit diesem Namen existiert bereits\x02Service %[1]v mit dem B" + @@ -134,17 +135,17 @@ const deData string = "" + // Size: 1736 bytes "inimale Wiedergabedauer für übersprungene Titel (Sekunden)\x02Zur Anmeld" + "ung folgende URL aufrufen: %[1]v\x02Fehler: OAuth-State stimmt nicht übe" + "rein\x04\x00\x01\x0a;\x02Zugriffstoken erhalten, %[1]v kann jetzt verwen" + - "det werden.\x02exportiere\x02importiere\x02fertig\x02Ja\x02Nein\x02keine" + - " bestehenden Servicekonfigurationen\x02Service\x02Backend\x02Übertrage %" + - "[1]s von %[2]s nach %[3]s…\x02Ab Zeitstempel: %[1]v (%[2]v)\x02Import fe" + - "hlgeschlagen, letzter Zeitstempel war %[1]v (%[2]s)\x02%[1]v von %[2]v %" + - "[3]s in %[4]v importiert.\x02Importlog:\x02%[1]v: %[2]v\x02ungültiger Ze" + - "itstempel „%[1]v“\x02Letzter Zeitstempel: %[1]v (%[2]v)\x02keine Konfigu" + - "rationsdatei definiert, Konfiguration kann nicht geschrieben werden\x02S" + - "chlüssel darf nur die Zeichen A-Za-z0-9_- beinhalten\x02keine Servicekon" + - "figuration „%[1]v“" + "det werden.\x02exportiere\x02importiere\x02abgebrochen\x02fertig\x02Ja" + + "\x02Nein\x02keine bestehenden Servicekonfigurationen\x02Service\x02Backe" + + "nd\x02Übertrage %[1]s von %[2]s nach %[3]s…\x02Ab Zeitstempel: %[1]v (%[" + + "2]v)\x02%[1]v von %[2]v %[3]s in %[4]v importiert.\x02Import fehlgeschla" + + "gen, letzter Zeitstempel war %[1]v (%[2]s)\x02Importlog:\x02%[1]v: %[2]v" + + "\x02ungültiger Zeitstempel „%[1]v“\x02Letzter Zeitstempel: %[1]v (%[2]v)" + + "\x02keine Konfigurationsdatei definiert, Konfiguration kann nicht geschr" + + "ieben werden\x02Schlüssel darf nur die Zeichen A-Za-z0-9_- beinhalten" + + "\x02keine Servicekonfiguration „%[1]v“" -var enIndex = []uint32{ // 55 elements +var enIndex = []uint32{ // 56 elements // Entry 0 - 1F 0x00000000, 0x00000013, 0x00000027, 0x00000044, 0x00000051, 0x00000079, 0x000000a1, 0x000000de, @@ -156,14 +157,14 @@ var enIndex = []uint32{ // 55 elements 0x00000307, 0x00000335, 0x00000344, 0x00000365, // Entry 20 - 3F 0x0000039c, 0x000003c3, 0x000003df, 0x00000412, - 0x0000041c, 0x00000426, 0x0000042b, 0x0000042f, - 0x00000432, 0x00000455, 0x0000045d, 0x00000465, - 0x0000048f, 0x000004ad, 0x000004e6, 0x00000510, - 0x0000051c, 0x00000529, 0x0000054a, 0x0000056a, - 0x0000059d, 0x000005c2, 0x000005e3, -} // Size: 244 bytes + 0x0000041c, 0x00000426, 0x0000042e, 0x00000433, + 0x00000437, 0x0000043a, 0x0000045d, 0x00000465, + 0x0000046d, 0x00000497, 0x000004b5, 0x000004df, + 0x00000518, 0x00000524, 0x00000531, 0x00000552, + 0x00000572, 0x000005a5, 0x000005ca, 0x000005eb, +} // Size: 248 bytes -const enData string = "" + // Size: 1507 bytes +const enData string = "" + // Size: 1515 bytes "\x04\x01\x09\x00\x0e\x02export: %[1]s\x04\x01\x09\x01\x0a\x0e\x02import:" + " %[1]s\x02Failed reading config: %[1]v\x02Service name\x02a service with" + " this name already exists\x02Saved service %[1]v using backend %[2]v\x02" + @@ -181,13 +182,14 @@ const enData string = "" + // Size: 1507 bytes "mps\x02Directory path\x02Ignore listens in incognito mode\x02Minimum pla" + "yback duration for skipped tracks (seconds)\x02Visit the URL for authori" + "zation: %[1]v\x02Error: OAuth state mismatch\x04\x00\x01\x0a.\x02Access " + - "token received, you can use %[1]v now.\x02exporting\x02importing\x02done" + - "\x02Yes\x02No\x02no existing service configurations\x02Service\x02Backen" + - "d\x02Transferring %[1]s from %[2]s to %[3]s…\x02From timestamp: %[1]v (%" + - "[2]v)\x02Import failed, last reported timestamp was %[1]v (%[2]s)\x02Imp" + - "orted %[1]v of %[2]v %[3]s into %[4]v.\x02Import log:\x02%[1]v: %[2]v" + - "\x02invalid timestamp string \x22%[1]v\x22\x02Latest timestamp: %[1]v (%" + - "[2]v)\x02no configuration file defined, cannot write config\x02key must " + - "only consist of A-Za-z0-9_-\x02no service configuration \x22%[1]v\x22" + "token received, you can use %[1]v now.\x02exporting\x02importing\x02abor" + + "ted\x02done\x02Yes\x02No\x02no existing service configurations\x02Servic" + + "e\x02Backend\x02Transferring %[1]s from %[2]s to %[3]s…\x02From timestam" + + "p: %[1]v (%[2]v)\x02Imported %[1]v of %[2]v %[3]s into %[4]v.\x02Import " + + "failed, last reported timestamp was %[1]v (%[2]s)\x02Import log:\x02%[1]" + + "v: %[2]v\x02invalid timestamp string \x22%[1]v\x22\x02Latest timestamp: " + + "%[1]v (%[2]v)\x02no configuration file defined, cannot write config\x02k" + + "ey must only consist of A-Za-z0-9_-\x02no service configuration \x22%[1]" + + "v\x22" - // Total table size 3731 bytes (3KiB); checksum: F7951710 + // Total table size 3759 bytes (3KiB); checksum: 7B4CF967 diff --git a/internal/translations/locales/de/messages.gotext.json b/internal/translations/locales/de/messages.gotext.json index 8cbe44a..b44b7af 100644 --- a/internal/translations/locales/de/messages.gotext.json +++ b/internal/translations/locales/de/messages.gotext.json @@ -368,21 +368,23 @@ "id": "exporting", "message": "exporting", "translatorComment": "Copied from source.", - "fuzzy": true, "translation": "exportiere" }, { "id": "importing", "message": "importing", "translatorComment": "Copied from source.", - "fuzzy": true, "translation": "importiere" }, + { + "id": "aborted", + "message": "aborted", + "translation": "abgebrochen" + }, { "id": "done", "message": "done", "translatorComment": "Copied from source.", - "fuzzy": true, "translation": "fertig" }, { @@ -462,27 +464,6 @@ } ] }, - { - "id": "Import failed, last reported timestamp was {Arg_1} ({Arg_2})", - "message": "Import failed, last reported timestamp was {Arg_1} ({Arg_2})", - "translation": "Import fehlgeschlagen, letzter Zeitstempel war {Arg_1} ({Arg_2})", - "placeholders": [ - { - "id": "Arg_1", - "string": "%[1]v", - "type": "", - "underlyingType": "interface{}", - "argNum": 1 - }, - { - "id": "Arg_2", - "string": "%[2]s", - "type": "", - "underlyingType": "string", - "argNum": 2 - } - ] - }, { "id": "Imported {ImportCount} of {TotalCount} {Entity} into {TargetName}.", "message": "Imported {ImportCount} of {TotalCount} {Entity} into {TargetName}.", @@ -522,6 +503,27 @@ } ] }, + { + "id": "Import failed, last reported timestamp was {Arg_1} ({Arg_2})", + "message": "Import failed, last reported timestamp was {Arg_1} ({Arg_2})", + "translation": "Import fehlgeschlagen, letzter Zeitstempel war {Arg_1} ({Arg_2})", + "placeholders": [ + { + "id": "Arg_1", + "string": "%[1]v", + "type": "", + "underlyingType": "interface{}", + "argNum": 1 + }, + { + "id": "Arg_2", + "string": "%[2]s", + "type": "", + "underlyingType": "string", + "argNum": 2 + } + ] + }, { "id": "Import log:", "message": "Import log:", diff --git a/internal/translations/locales/de/out.gotext.json b/internal/translations/locales/de/out.gotext.json index 680505e..863d9c8 100644 --- a/internal/translations/locales/de/out.gotext.json +++ b/internal/translations/locales/de/out.gotext.json @@ -368,22 +368,24 @@ "id": "exporting", "message": "exporting", "translation": "exportiere", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "importing", "message": "importing", "translation": "importiere", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." + }, + { + "id": "aborted", + "message": "aborted", + "translation": "abgebrochen" }, { "id": "done", "message": "done", "translation": "fertig", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Yes", @@ -462,27 +464,6 @@ } ] }, - { - "id": "Import failed, last reported timestamp was {Arg_1} ({Arg_2})", - "message": "Import failed, last reported timestamp was {Arg_1} ({Arg_2})", - "translation": "Import fehlgeschlagen, letzter Zeitstempel war {Arg_1} ({Arg_2})", - "placeholders": [ - { - "id": "Arg_1", - "string": "%[1]v", - "type": "", - "underlyingType": "interface{}", - "argNum": 1 - }, - { - "id": "Arg_2", - "string": "%[2]s", - "type": "", - "underlyingType": "string", - "argNum": 2 - } - ] - }, { "id": "Imported {ImportCount} of {TotalCount} {Entity} into {TargetName}.", "message": "Imported {ImportCount} of {TotalCount} {Entity} into {TargetName}.", @@ -522,6 +503,27 @@ } ] }, + { + "id": "Import failed, last reported timestamp was {Arg_1} ({Arg_2})", + "message": "Import failed, last reported timestamp was {Arg_1} ({Arg_2})", + "translation": "Import fehlgeschlagen, letzter Zeitstempel war {Arg_1} ({Arg_2})", + "placeholders": [ + { + "id": "Arg_1", + "string": "%[1]v", + "type": "", + "underlyingType": "interface{}", + "argNum": 1 + }, + { + "id": "Arg_2", + "string": "%[2]s", + "type": "", + "underlyingType": "string", + "argNum": 2 + } + ] + }, { "id": "Import log:", "message": "Import log:", diff --git a/internal/translations/locales/en/messages.gotext.json b/internal/translations/locales/en/messages.gotext.json index ed62636..878db22 100644 --- a/internal/translations/locales/en/messages.gotext.json +++ b/internal/translations/locales/en/messages.gotext.json @@ -15,8 +15,7 @@ "argNum": 1, "expr": "strings.Join(info.ExportCapabilities, \", \")" } - ], - "fuzzy": true + ] }, { "id": "import: {ImportCapabilities__}", @@ -32,8 +31,7 @@ "argNum": 1, "expr": "strings.Join(info.ImportCapabilities, \", \")" } - ], - "fuzzy": true + ] }, { "id": "Failed reading config: {Err}", @@ -49,22 +47,19 @@ "argNum": 1, "expr": "err" } - ], - "fuzzy": true + ] }, { "id": "Service name", "message": "Service name", "translation": "Service name", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "a service with this name already exists", "message": "a service with this name already exists", "translation": "a service with this name already exists", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Saved service {Name} using backend {Backend}", @@ -88,8 +83,7 @@ "argNum": 2, "expr": "service.Backend" } - ], - "fuzzy": true + ] }, { "id": "The backend {Backend} requires authentication. Authenticate now?", @@ -105,8 +99,7 @@ "argNum": 1, "expr": "service.Backend" } - ], - "fuzzy": true + ] }, { "id": "Delete the service configuration \"{Service}\"?", @@ -122,15 +115,13 @@ "argNum": 1, "expr": "service" } - ], - "fuzzy": true + ] }, { "id": "Aborted", "message": "Aborted", "translation": "Aborted", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Service \"{Name}\" deleted", @@ -146,8 +137,7 @@ "argNum": 1, "expr": "service.Name" } - ], - "fuzzy": true + ] }, { "id": "Updated service {Name} using backend {Backend}", @@ -171,8 +161,7 @@ "argNum": 2, "expr": "service.Backend" } - ], - "fuzzy": true + ] }, { "id": "backend: {Backend}", @@ -188,15 +177,13 @@ "argNum": 1, "expr": "s.Backend" } - ], - "fuzzy": true + ] }, { "id": "Token received, you can close this window now.", "message": "Token received, you can close this window now.", "translation": "Token received, you can close this window now.", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "backend {Backend} does not implement {InterfaceName}", @@ -220,8 +207,7 @@ "argNum": 2, "expr": "interfaceName" } - ], - "fuzzy": true + ] }, { "id": "unknown backend \"{BackendName}\"", @@ -237,78 +223,67 @@ "argNum": 1, "expr": "backendName" } - ], - "fuzzy": true + ] }, { "id": "Client ID", "message": "Client ID", "translation": "Client ID", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Client secret", "message": "Client secret", "translation": "Client secret", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Server URL", "message": "Server URL", "translation": "Server URL", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "User name", "message": "User name", "translation": "User name", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Access token", "message": "Access token", "translation": "Access token", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "File path", "message": "File path", "translation": "File path", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Append to file", "message": "Append to file", "translation": "Append to file", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Playlist title", "message": "Playlist title", "translation": "Playlist title", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Unique playlist identifier", "message": "Unique playlist identifier", "translation": "Unique playlist identifier", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Check for duplicate listens on import (slower)", "message": "Check for duplicate listens on import (slower)", "translation": "Check for duplicate listens on import (slower)", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Ignored duplicate listen {ListenedAt}: \"{TrackName}\" by {ArtistName} ({RecordingMBID})", @@ -348,50 +323,43 @@ "argNum": 4, "expr": "l.RecordingMBID" } - ], - "fuzzy": true + ] }, { "id": "Disable auto correction of submitted listens", "message": "Disable auto correction of submitted listens", "translation": "Disable auto correction of submitted listens", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Ignore skipped listens", "message": "Ignore skipped listens", "translation": "Ignore skipped listens", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Specify a time zone for the listen timestamps", "message": "Specify a time zone for the listen timestamps", "translation": "Specify a time zone for the listen timestamps", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Directory path", "message": "Directory path", "translation": "Directory path", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Ignore listens in incognito mode", "message": "Ignore listens in incognito mode", "translation": "Ignore listens in incognito mode", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Minimum playback duration for skipped tracks (seconds)", "message": "Minimum playback duration for skipped tracks (seconds)", "translation": "Minimum playback duration for skipped tracks (seconds)", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Visit the URL for authorization: {URL}", @@ -407,15 +375,13 @@ "argNum": 1, "expr": "authURL.URL" } - ], - "fuzzy": true + ] }, { "id": "Error: OAuth state mismatch", "message": "Error: OAuth state mismatch", "translation": "Error: OAuth state mismatch", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Access token received, you can use {Name} now.", @@ -431,64 +397,55 @@ "argNum": 1, "expr": "service.Name" } - ], - "fuzzy": true + ] }, { "id": "exporting", "message": "exporting", "translation": "exporting", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "importing", "message": "importing", "translation": "importing", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "done", "message": "done", "translation": "done", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Yes", "message": "Yes", "translation": "Yes", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "No", "message": "No", "translation": "No", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "no existing service configurations", "message": "no existing service configurations", "translation": "no existing service configurations", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Service", "message": "Service", "translation": "Service", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Backend", "message": "Backend", "translation": "Backend", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Transferring {Entity} from {SourceName} to {TargetName}…", @@ -520,8 +477,7 @@ "argNum": 3, "expr": "c.targetName" } - ], - "fuzzy": true + ] }, { "id": "From timestamp: {Arg_1} ({Arg_2})", @@ -543,8 +499,7 @@ "underlyingType": "interface{}", "argNum": 2 } - ], - "fuzzy": true + ] }, { "id": "Import failed, last reported timestamp was {Arg_1} ({Arg_2})", @@ -566,8 +521,7 @@ "underlyingType": "string", "argNum": 2 } - ], - "fuzzy": true + ] }, { "id": "Imported {ImportCount} of {TotalCount} {Entity} into {TargetName}.", @@ -607,15 +561,13 @@ "argNum": 4, "expr": "c.targetName" } - ], - "fuzzy": true + ] }, { "id": "Import log:", "message": "Import log:", "translation": "Import log:", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "{Type}: {Message}", @@ -639,8 +591,7 @@ "argNum": 2, "expr": "entry.Message" } - ], - "fuzzy": true + ] }, { "id": "invalid timestamp string \"{FlagValue}\"", @@ -656,8 +607,7 @@ "argNum": 1, "expr": "flagValue" } - ], - "fuzzy": true + ] }, { "id": "Latest timestamp: {Arg_1} ({Arg_2})", @@ -679,22 +629,19 @@ "underlyingType": "interface{}", "argNum": 2 } - ], - "fuzzy": true + ] }, { "id": "no configuration file defined, cannot write config", "message": "no configuration file defined, cannot write config", "translation": "no configuration file defined, cannot write config", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "key must only consist of A-Za-z0-9_-", "message": "key must only consist of A-Za-z0-9_-", "translation": "key must only consist of A-Za-z0-9_-", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "no service configuration \"{Name}\"", @@ -710,8 +657,7 @@ "argNum": 1, "expr": "name" } - ], - "fuzzy": true + ] } ] } diff --git a/internal/translations/locales/en/out.gotext.json b/internal/translations/locales/en/out.gotext.json index eecf359..c2e0e84 100644 --- a/internal/translations/locales/en/out.gotext.json +++ b/internal/translations/locales/en/out.gotext.json @@ -15,8 +15,7 @@ "argNum": 1, "expr": "strings.Join(info.ExportCapabilities, \", \")" } - ], - "fuzzy": true + ] }, { "id": "import: {ImportCapabilities__}", @@ -32,8 +31,7 @@ "argNum": 1, "expr": "strings.Join(info.ImportCapabilities, \", \")" } - ], - "fuzzy": true + ] }, { "id": "Failed reading config: {Err}", @@ -49,22 +47,19 @@ "argNum": 1, "expr": "err" } - ], - "fuzzy": true + ] }, { "id": "Service name", "message": "Service name", "translation": "Service name", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "a service with this name already exists", "message": "a service with this name already exists", "translation": "a service with this name already exists", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Saved service {Name} using backend {Backend}", @@ -88,8 +83,7 @@ "argNum": 2, "expr": "service.Backend" } - ], - "fuzzy": true + ] }, { "id": "The backend {Backend} requires authentication. Authenticate now?", @@ -105,8 +99,7 @@ "argNum": 1, "expr": "service.Backend" } - ], - "fuzzy": true + ] }, { "id": "Delete the service configuration \"{Service}\"?", @@ -122,15 +115,13 @@ "argNum": 1, "expr": "service" } - ], - "fuzzy": true + ] }, { "id": "Aborted", "message": "Aborted", "translation": "Aborted", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Service \"{Name}\" deleted", @@ -146,8 +137,7 @@ "argNum": 1, "expr": "service.Name" } - ], - "fuzzy": true + ] }, { "id": "Updated service {Name} using backend {Backend}", @@ -171,8 +161,7 @@ "argNum": 2, "expr": "service.Backend" } - ], - "fuzzy": true + ] }, { "id": "backend: {Backend}", @@ -188,15 +177,13 @@ "argNum": 1, "expr": "s.Backend" } - ], - "fuzzy": true + ] }, { "id": "Token received, you can close this window now.", "message": "Token received, you can close this window now.", "translation": "Token received, you can close this window now.", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "backend {Backend} does not implement {InterfaceName}", @@ -220,8 +207,7 @@ "argNum": 2, "expr": "interfaceName" } - ], - "fuzzy": true + ] }, { "id": "unknown backend \"{BackendName}\"", @@ -237,78 +223,67 @@ "argNum": 1, "expr": "backendName" } - ], - "fuzzy": true + ] }, { "id": "Client ID", "message": "Client ID", "translation": "Client ID", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Client secret", "message": "Client secret", "translation": "Client secret", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Server URL", "message": "Server URL", "translation": "Server URL", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "User name", "message": "User name", "translation": "User name", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Access token", "message": "Access token", "translation": "Access token", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "File path", "message": "File path", "translation": "File path", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Append to file", "message": "Append to file", "translation": "Append to file", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Playlist title", "message": "Playlist title", "translation": "Playlist title", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Unique playlist identifier", "message": "Unique playlist identifier", "translation": "Unique playlist identifier", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Check for duplicate listens on import (slower)", "message": "Check for duplicate listens on import (slower)", "translation": "Check for duplicate listens on import (slower)", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Ignored duplicate listen {ListenedAt}: \"{TrackName}\" by {ArtistName} ({RecordingMBID})", @@ -348,50 +323,43 @@ "argNum": 4, "expr": "l.RecordingMBID" } - ], - "fuzzy": true + ] }, { "id": "Disable auto correction of submitted listens", "message": "Disable auto correction of submitted listens", "translation": "Disable auto correction of submitted listens", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Ignore skipped listens", "message": "Ignore skipped listens", "translation": "Ignore skipped listens", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Specify a time zone for the listen timestamps", "message": "Specify a time zone for the listen timestamps", "translation": "Specify a time zone for the listen timestamps", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Directory path", "message": "Directory path", "translation": "Directory path", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Ignore listens in incognito mode", "message": "Ignore listens in incognito mode", "translation": "Ignore listens in incognito mode", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Minimum playback duration for skipped tracks (seconds)", "message": "Minimum playback duration for skipped tracks (seconds)", "translation": "Minimum playback duration for skipped tracks (seconds)", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Visit the URL for authorization: {URL}", @@ -407,15 +375,13 @@ "argNum": 1, "expr": "authURL.URL" } - ], - "fuzzy": true + ] }, { "id": "Error: OAuth state mismatch", "message": "Error: OAuth state mismatch", "translation": "Error: OAuth state mismatch", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Access token received, you can use {Name} now.", @@ -431,20 +397,24 @@ "argNum": 1, "expr": "service.Name" } - ], - "fuzzy": true + ] }, { "id": "exporting", "message": "exporting", "translation": "exporting", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "importing", "message": "importing", "translation": "importing", + "translatorComment": "Copied from source." + }, + { + "id": "aborted", + "message": "aborted", + "translation": "aborted", "translatorComment": "Copied from source.", "fuzzy": true }, @@ -452,43 +422,37 @@ "id": "done", "message": "done", "translation": "done", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Yes", "message": "Yes", "translation": "Yes", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "No", "message": "No", "translation": "No", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "no existing service configurations", "message": "no existing service configurations", "translation": "no existing service configurations", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Service", "message": "Service", "translation": "Service", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Backend", "message": "Backend", "translation": "Backend", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "Transferring {Entity} from {SourceName} to {TargetName}…", @@ -520,8 +484,7 @@ "argNum": 3, "expr": "c.targetName" } - ], - "fuzzy": true + ] }, { "id": "From timestamp: {Arg_1} ({Arg_2})", @@ -543,31 +506,7 @@ "underlyingType": "interface{}", "argNum": 2 } - ], - "fuzzy": true - }, - { - "id": "Import failed, last reported timestamp was {Arg_1} ({Arg_2})", - "message": "Import failed, last reported timestamp was {Arg_1} ({Arg_2})", - "translation": "Import failed, last reported timestamp was {Arg_1} ({Arg_2})", - "translatorComment": "Copied from source.", - "placeholders": [ - { - "id": "Arg_1", - "string": "%[1]v", - "type": "", - "underlyingType": "interface{}", - "argNum": 1 - }, - { - "id": "Arg_2", - "string": "%[2]s", - "type": "", - "underlyingType": "string", - "argNum": 2 - } - ], - "fuzzy": true + ] }, { "id": "Imported {ImportCount} of {TotalCount} {Entity} into {TargetName}.", @@ -607,15 +546,35 @@ "argNum": 4, "expr": "c.targetName" } - ], - "fuzzy": true + ] + }, + { + "id": "Import failed, last reported timestamp was {Arg_1} ({Arg_2})", + "message": "Import failed, last reported timestamp was {Arg_1} ({Arg_2})", + "translation": "Import failed, last reported timestamp was {Arg_1} ({Arg_2})", + "translatorComment": "Copied from source.", + "placeholders": [ + { + "id": "Arg_1", + "string": "%[1]v", + "type": "", + "underlyingType": "interface{}", + "argNum": 1 + }, + { + "id": "Arg_2", + "string": "%[2]s", + "type": "", + "underlyingType": "string", + "argNum": 2 + } + ] }, { "id": "Import log:", "message": "Import log:", "translation": "Import log:", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "{Type}: {Message}", @@ -639,8 +598,7 @@ "argNum": 2, "expr": "entry.Message" } - ], - "fuzzy": true + ] }, { "id": "invalid timestamp string \"{FlagValue}\"", @@ -656,8 +614,7 @@ "argNum": 1, "expr": "flagValue" } - ], - "fuzzy": true + ] }, { "id": "Latest timestamp: {Arg_1} ({Arg_2})", @@ -679,22 +636,19 @@ "underlyingType": "interface{}", "argNum": 2 } - ], - "fuzzy": true + ] }, { "id": "no configuration file defined, cannot write config", "message": "no configuration file defined, cannot write config", "translation": "no configuration file defined, cannot write config", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "key must only consist of A-Za-z0-9_-", "message": "key must only consist of A-Za-z0-9_-", "translation": "key must only consist of A-Za-z0-9_-", - "translatorComment": "Copied from source.", - "fuzzy": true + "translatorComment": "Copied from source." }, { "id": "no service configuration \"{Name}\"", @@ -710,8 +664,7 @@ "argNum": 1, "expr": "name" } - ], - "fuzzy": true + ] } ] } \ No newline at end of file