From 4d07a39b64ce9d2a9926e46a78652a78a2dd9fd2 Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Sun, 10 Dec 2023 00:24:39 +0100 Subject: [PATCH] jspf: implement append mode --- config.example.toml | 3 ++ internal/backends/jspf/jspf.go | 90 +++++++++++++++++++++++----------- 2 files changed, 65 insertions(+), 28 deletions(-) diff --git a/config.example.toml b/config.example.toml index b7b82fc..11930a2 100644 --- a/config.example.toml +++ b/config.example.toml @@ -59,6 +59,9 @@ append = true backend = "jspf" # The file path to the XSPF file file-path = "data/playlist.jspf" +# If true (default), new listens will be appended to the existing file. Set to +# false to overwrite the file and create a new JSPF playlist on every run. +append = true # Title of the playlist title = "My Playlist" # Creator of the playlist (only informational) diff --git a/internal/backends/jspf/jspf.go b/internal/backends/jspf/jspf.go index 944c6e6..b87b86a 100644 --- a/internal/backends/jspf/jspf.go +++ b/internal/backends/jspf/jspf.go @@ -28,11 +28,9 @@ import ( ) type JSPFBackend struct { - filePath string - title string - creator string - identifier string - tracks []jspf.Track + filePath string + playlist jspf.Playlist + append bool } func (b *JSPFBackend) Name() string { return "jspf" } @@ -42,6 +40,11 @@ func (b *JSPFBackend) Options() []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", }, { Name: "title", Label: i18n.Tr("Playlist title"), @@ -59,23 +62,37 @@ func (b *JSPFBackend) Options() []models.BackendOption { func (b *JSPFBackend) FromConfig(config *config.ServiceConfig) models.Backend { b.filePath = config.GetString("file-path") - b.title = config.GetString("title") - b.creator = config.GetString("username") - b.identifier = config.GetString("identifier") - b.tracks = make([]jspf.Track, 0) + b.append = true + if config.IsSet("append") { + b.append = config.GetBool("append") + } + b.playlist = jspf.Playlist{ + Title: config.GetString("title"), + Creator: config.GetString("username"), + Identifier: config.GetString("identifier"), + Tracks: make([]jspf.Track, 0), + Extension: map[string]any{ + jspf.MusicBrainzPlaylistExtensionId: jspf.MusicBrainzPlaylistExtension{ + LastModifiedAt: time.Now(), + Public: true, + }, + }, + } return b } -func (b *JSPFBackend) StartImport() error { return nil } +func (b *JSPFBackend) StartImport() error { + return b.readJSPF() +} + func (b *JSPFBackend) FinishImport() error { - err := b.writeJSPF(b.tracks) - return err + return b.writeJSPF() } func (b *JSPFBackend) ImportListens(export models.ListensResult, importResult models.ImportResult, progress chan models.Progress) (models.ImportResult, error) { for _, listen := range export.Items { track := listenAsTrack(listen) - b.tracks = append(b.tracks, track) + b.playlist.Tracks = append(b.playlist.Tracks, track) importResult.ImportCount += 1 importResult.UpdateTimestamp(listen.ListenedAt) } @@ -87,7 +104,7 @@ func (b *JSPFBackend) ImportListens(export models.ListensResult, importResult mo func (b *JSPFBackend) ImportLoves(export models.LovesResult, importResult models.ImportResult, progress chan models.Progress) (models.ImportResult, error) { for _, love := range export.Items { track := loveAsTrack(love) - b.tracks = append(b.tracks, track) + b.playlist.Tracks = append(b.playlist.Tracks, track) importResult.ImportCount += 1 importResult.UpdateTimestamp(love.Created) } @@ -162,21 +179,38 @@ func makeMusicBrainzExtension(t models.Track) jspf.MusicBrainzTrackExtension { return extension } -func (b JSPFBackend) writeJSPF(tracks []jspf.Track) error { +func (b *JSPFBackend) readJSPF() error { + if b.append { + file, err := os.Open(b.filePath) + if err != nil { + return err + } + + defer file.Close() + stat, err := file.Stat() + if err != nil { + return err + } + + if stat.Size() == 0 { + // Zero length file, treat as a new file + return nil + } else { + playlist := jspf.JSPF{} + err := playlist.Read(file) + if err != nil { + return err + } + b.playlist = playlist.Playlist + } + } + + return nil +} + +func (b *JSPFBackend) writeJSPF() error { playlist := jspf.JSPF{ - Playlist: jspf.Playlist{ - Title: b.title, - Creator: b.creator, - Identifier: b.identifier, - Date: time.Now(), - Tracks: tracks, - Extension: map[string]any{ - jspf.MusicBrainzPlaylistExtensionId: jspf.MusicBrainzPlaylistExtension{ - LastModifiedAt: time.Now(), - Public: true, - }, - }, - }, + Playlist: b.playlist, } file, err := os.Create(b.filePath)