/* 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 subsonic import ( "net/http" "sort" "time" "github.com/delucks/go-subsonic" "github.com/spf13/viper" "go.uploadedlobster.com/scotty/internal/models" "go.uploadedlobster.com/scotty/internal/version" ) type SubsonicApiBackend struct { client subsonic.Client password string } func (b *SubsonicApiBackend) Name() string { return "subsonic" } func (b *SubsonicApiBackend) Options() *[]models.BackendOption { return &[]models.BackendOption{{ Name: "server-url", Label: "Server URL", Type: models.String, }, { Name: "username", Label: "User name", Type: models.String, }, { Name: "token", Label: "Access token", Type: models.Secret, }} } func (b *SubsonicApiBackend) FromConfig(config *viper.Viper) models.Backend { b.client = subsonic.Client{ Client: &http.Client{}, BaseUrl: config.GetString("server-url"), User: config.GetString("username"), ClientName: version.AppName, } b.password = config.GetString("token") return b } 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() results <- models.LovesResult{Error: err} return } starred, err := b.client.GetStarred2(map[string]string{}) if err != nil { progress <- models.Progress{}.Complete() results <- models.LovesResult{Error: err} return } progress <- models.Progress{Elapsed: int64(len(starred.Song))}.Complete() results <- models.LovesResult{Items: b.filterSongs(starred.Song, oldestTimestamp)} } func (b *SubsonicApiBackend) filterSongs(songs []*subsonic.Child, oldestTimestamp time.Time) models.LovesList { loves := make(models.LovesList, 0, len(songs)) for _, song := range songs { love := SongAsLove(*song, b.client.User) if love.Created.Unix() > oldestTimestamp.Unix() { loves = append(loves, love) } } sort.Sort(loves) return loves } func SongAsLove(song subsonic.Child, username string) models.Love { love := models.Love{ UserName: username, Created: song.Starred, Track: models.Track{ TrackName: song.Title, ReleaseName: song.Album, ArtistNames: []string{song.Artist}, TrackNumber: song.Track, DiscNumber: song.DiscNumber, Tags: []string{song.Genre}, AdditionalInfo: map[string]any{}, Duration: time.Duration(song.Duration * int(time.Second)), }, } return love }