From d25095267876e1a6fdb19d950a061047da8a2c1d Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Sat, 24 May 2025 11:33:10 +0200 Subject: [PATCH] Extend dump backend to be able to write to a file --- config.example.toml | 8 +++- internal/backends/dump/dump.go | 75 ++++++++++++++++++++++++++++++---- 2 files changed, 74 insertions(+), 9 deletions(-) diff --git a/config.example.toml b/config.example.toml index 40ffd18..ecbba9b 100644 --- a/config.example.toml +++ b/config.example.toml @@ -141,4 +141,10 @@ client-secret = "" [service.dump] # This backend allows writing listens and loves as console output. Useful for # debugging the export from other services. -backend = "dump" +backend = "dump" +# Path to a file where the listens and loves are written to. If not set, +# the output is written to stdout. +file-path = "" +# If true (default), new listens will be appended to the existing file. Set to +# false to overwrite the file on every run. +append = true diff --git a/internal/backends/dump/dump.go b/internal/backends/dump/dump.go index 1fcd864..4714bd6 100644 --- a/internal/backends/dump/dump.go +++ b/internal/backends/dump/dump.go @@ -17,25 +17,80 @@ Scotty. If not, see . package dump import ( + "bytes" "context" "fmt" + "io" + "os" + "strings" "go.uploadedlobster.com/scotty/internal/config" + "go.uploadedlobster.com/scotty/internal/i18n" "go.uploadedlobster.com/scotty/internal/models" ) -type DumpBackend struct{} +type DumpBackend struct { + buffer io.ReadWriter + print bool // Whether to print the output to stdout +} func (b *DumpBackend) Name() string { return "dump" } -func (b *DumpBackend) Options() []models.BackendOption { return nil } +func (b *DumpBackend) Options() []models.BackendOption { + return []models.BackendOption{{ + Name: "file-path", + Label: i18n.Tr("File path"), + Type: models.String, + }, { + Name: "append", + Label: i18n.Tr("Append to file"), + Type: models.Bool, + Default: "true", + }} +} func (b *DumpBackend) InitConfig(config *config.ServiceConfig) error { + filePath := config.GetString("file-path") + append := config.GetBool("append", true) + if strings.TrimSpace(filePath) != "" { + mode := os.O_WRONLY | os.O_CREATE + if !append { + mode |= os.O_TRUNC // Truncate the file if not appending + } + f, err := os.OpenFile(filePath, mode, 0644) + if err != nil { + return err + } + b.buffer = f + b.print = false // If a file path is specified, we don't print to stdout + } else { + // If no file path is specified, use a bytes.Buffer for in-memory dumping + b.buffer = new(bytes.Buffer) + b.print = true // Print to stdout + } return nil } -func (b *DumpBackend) StartImport() error { return nil } -func (b *DumpBackend) FinishImport() error { return nil } +func (b *DumpBackend) StartImport() error { return nil } + +func (b *DumpBackend) FinishImport() error { + if b.print { + out := new(strings.Builder) + _, err := io.Copy(out, b.buffer) + if err != nil { + return err + } + fmt.Println(out.String()) + } + + // Close the io writer if it is closable + if closer, ok := b.buffer.(io.Closer); ok { + if err := closer.Close(); err != nil { + return fmt.Errorf("failed to close output file: %w", err) + } + } + return nil +} func (b *DumpBackend) ImportListens(ctx context.Context, export models.ListensResult, importResult models.ImportResult, progress chan models.TransferProgress) (models.ImportResult, error) { for _, listen := range export.Items { @@ -45,9 +100,11 @@ func (b *DumpBackend) ImportListens(ctx context.Context, export models.ListensRe importResult.UpdateTimestamp(listen.ListenedAt) importResult.ImportCount += 1 - msg := fmt.Sprintf("🎶 %v: \"%v\" by %v (%v)", + _, err := fmt.Fprintf(b.buffer, "🎶 %v: \"%v\" by %v (%v)\n", listen.ListenedAt, listen.TrackName, listen.ArtistName(), listen.RecordingMBID) - importResult.Log(models.Info, msg) + if err != nil { + return importResult, err + } progress <- models.TransferProgress{}.FromImportResult(importResult, false) } @@ -62,9 +119,11 @@ func (b *DumpBackend) ImportLoves(ctx context.Context, export models.LovesResult importResult.UpdateTimestamp(love.Created) importResult.ImportCount += 1 - msg := fmt.Sprintf("❤️ %v: \"%v\" by %v (%v)", + _, err := fmt.Fprintf(b.buffer, "❤️ %v: \"%v\" by %v (%v)\n", love.Created, love.TrackName, love.ArtistName(), love.RecordingMBID) - importResult.Log(models.Info, msg) + if err != nil { + return importResult, err + } progress <- models.TransferProgress{}.FromImportResult(importResult, false) }