/* Copyright © 2023 Philipp Wolfer This file is part of Scotty. 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 Foundation, either version 3 of the License, or (at your option) any later version. Scotty is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Scotty. If not, see . */ package jspf import ( "os" "time" "github.com/spf13/viper" "go.uploadedlobster.com/scotty/internal/models" "go.uploadedlobster.com/scotty/pkg/jspf" ) type JSPFBackend struct { filePath string title string creator string identifier string tracks []jspf.Track } func (b *JSPFBackend) Name() string { return "jspf" } func (b *JSPFBackend) FromConfig(config *viper.Viper) 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) return b } func (b *JSPFBackend) StartImport() error { return nil } func (b *JSPFBackend) FinishImport() error { err := b.writeJSPF(b.tracks) return err } 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) importResult.ImportCount += 1 importResult.UpdateTimestamp(listen.ListenedAt) } progress <- models.Progress{}.FromImportResult(importResult) return importResult, nil } 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) importResult.ImportCount += 1 importResult.UpdateTimestamp(love.Created) } progress <- models.Progress{}.FromImportResult(importResult) return importResult, nil } func listenAsTrack(l models.Listen) jspf.Track { l.FillAdditionalInfo() track := trackAsTrack(l.Track) extension := makeMusicBrainzExtension(l.Track) extension.AddedAt = l.ListenedAt extension.AddedBy = l.UserName track.Extension[jspf.MusicBrainzTrackExtensionId] = extension if l.RecordingMbid != "" { track.Identifier = append(track.Identifier, "https://musicbrainz.org/recording/"+string(l.RecordingMbid)) } return track } func loveAsTrack(l models.Love) jspf.Track { l.FillAdditionalInfo() track := trackAsTrack(l.Track) extension := makeMusicBrainzExtension(l.Track) extension.AddedAt = l.Created extension.AddedBy = l.UserName track.Extension[jspf.MusicBrainzTrackExtensionId] = extension recordingMbid := l.Track.RecordingMbid if l.RecordingMbid != "" { recordingMbid = l.RecordingMbid } if recordingMbid != "" { track.Identifier = append(track.Identifier, "https://musicbrainz.org/recording/"+string(recordingMbid)) } return track } func trackAsTrack(t models.Track) jspf.Track { track := jspf.Track{ Title: t.TrackName, Album: t.ReleaseName, Creator: t.ArtistName(), TrackNum: t.TrackNumber, Extension: map[string]any{}, } return track } func makeMusicBrainzExtension(t models.Track) jspf.MusicBrainzTrackExtension { extension := jspf.MusicBrainzTrackExtension{ AdditionalMetadata: t.AdditionalInfo, ArtistIdentifiers: make([]string, len(t.ArtistMbids)), } for i, mbid := range t.ArtistMbids { extension.ArtistIdentifiers[i] = "https://musicbrainz.org/artist/" + string(mbid) } if t.ReleaseMbid != "" { extension.ReleaseIdentifier = "https://musicbrainz.org/release/" + string(t.ReleaseMbid) } // The tracknumber tag would be redundant delete(extension.AdditionalMetadata, "tracknumber") return extension } func (b JSPFBackend) writeJSPF(tracks []jspf.Track) error { playlist := jspf.JSPF{ Playlist: jspf.Playlist{ Title: b.title, Creator: b.creator, Identifier: b.identifier, Date: time.Now(), Tracks: tracks, }, } file, err := os.Create(b.filePath) if err != nil { return err } defer file.Close() return playlist.Write(file) }