/* 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 maloja import ( "errors" "sort" "strings" "time" "go.uploadedlobster.com/scotty/internal/config" "go.uploadedlobster.com/scotty/internal/models" ) type MalojaApiBackend struct { client Client nofix bool } func (b *MalojaApiBackend) Name() string { return "maloja" } func (b *MalojaApiBackend) Options() *[]models.BackendOption { return &[]models.BackendOption{{ Name: "server-url", Label: "Server URL", Type: models.String, }, { Name: "token", Label: "Access token", Type: models.Secret, }, { Name: "nofix", Label: "Disable auto correction of submitted listens", Type: models.Bool, }} } func (b *MalojaApiBackend) FromConfig(config *config.ServiceConfig) models.Backend { b.client = NewClient( config.GetString("server-url"), config.GetString("token"), ) b.nofix = config.GetBool("nofix") return b } func (b *MalojaApiBackend) StartImport() error { return nil } func (b *MalojaApiBackend) FinishImport() error { return nil } func (b *MalojaApiBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult, progress chan models.Progress) { 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)} out: for { result, err := b.client.GetScrobbles(page, perPage) if err != nil { progress <- p.Complete() results <- models.ListensResult{Error: err} return } count := len(result.List) if count == 0 { break } for _, scrobble := range result.List { if scrobble.ListenedAt > oldestTimestamp.Unix() { p.Elapsed += 1 listens = append(listens, scrobble.AsListen()) } else { break out } } p.Total += int64(perPage) progress <- p page += 1 } sort.Sort(listens) progress <- p.Complete() results <- models.ListensResult{Items: listens} } func (b *MalojaApiBackend) ImportListens(export models.ListensResult, importResult models.ImportResult, progress chan models.Progress) (models.ImportResult, error) { for _, listen := range export.Items { scrobble := NewScrobble{ Title: listen.TrackName, Artists: listen.ArtistNames, Album: listen.ReleaseName, Duration: int64(listen.PlaybackDuration.Seconds()), Length: int64(listen.Duration.Seconds()), Time: listen.ListenedAt.Unix(), Nofix: b.nofix, } resp, err := b.client.NewScrobble(scrobble) if err != nil { return importResult, err } else if resp.Status != "success" { return importResult, errors.New(resp.Error.Description) } importResult.UpdateTimestamp(listen.ListenedAt) importResult.ImportCount += 1 progress <- models.Progress{}.FromImportResult(importResult) } return importResult, nil } func (s Scrobble) AsListen() models.Listen { track := s.Track listen := models.Listen{ ListenedAt: time.Unix(s.ListenedAt, 0), PlaybackDuration: time.Duration(s.Duration * int64(time.Second)), Track: models.Track{ TrackName: track.Title, ReleaseName: track.Album.Title, ArtistNames: track.Artists, Duration: time.Duration(track.Length * int64(time.Second)), AdditionalInfo: map[string]any{}, }, } client, found := strings.CutPrefix(s.Origin, "client:") if found { listen.AdditionalInfo["media_player"] = client } return listen }