scotty/internal/backends/subsonic/subsonic.go
2023-12-10 01:33:14 +01:00

118 lines
3.3 KiB
Go

/*
Copyright © 2023 Philipp Wolfer <phw@uploadedlobster.com>
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 <https://www.gnu.org/licenses/>.
*/
package subsonic
import (
"net/http"
"sort"
"time"
"github.com/delucks/go-subsonic"
"go.uploadedlobster.com/scotty/internal/config"
"go.uploadedlobster.com/scotty/internal/i18n"
"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: i18n.Tr("Server URL"),
Type: models.String,
}, {
Name: "username",
Label: i18n.Tr("User name"),
Type: models.String,
}, {
Name: "token",
Label: i18n.Tr("Access token"),
Type: models.Secret,
}}
}
func (b *SubsonicApiBackend) FromConfig(config *config.ServiceConfig) 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,
AdditionalInfo: map[string]any{},
Duration: time.Duration(song.Duration * int(time.Second)),
},
}
if song.Genre != "" {
love.Track.Tags = []string{song.Genre}
}
return love
}