Files
supersonic/backend/mediaprovider/mediaprovider.go
T

282 lines
6.5 KiB
Go

package mediaprovider
import (
"image"
"io"
"net/url"
"strings"
)
type MediaIterator[M any] interface {
Next() *M
}
type ArtistIterator = MediaIterator[Artist]
type AlbumIterator = MediaIterator[Album]
type TrackIterator = MediaIterator[Track]
type MediaFilter[M, F any] interface {
Options() F
SetOptions(F)
Clone() MediaFilter[M, F]
IsNil() bool
Matches(*M) bool
}
type AlbumFilter = MediaFilter[Album, AlbumFilterOptions]
type AlbumFilterOptions struct {
MinYear int
MaxYear int // 0 == unset/match any
Genres []string // len(0) == unset/match any
ExcludeFavorited bool // mut. exc. with ExcludeUnfavorited
ExcludeUnfavorited bool // mut. exc. with ExcludeFavorited
}
// Clone returns a deep copy of the filter options
func (o AlbumFilterOptions) Clone() AlbumFilterOptions {
genres := make([]string, len(o.Genres))
copy(genres, o.Genres)
return AlbumFilterOptions{
MinYear: o.MinYear,
MaxYear: o.MaxYear,
Genres: genres,
ExcludeFavorited: o.ExcludeFavorited,
ExcludeUnfavorited: o.ExcludeUnfavorited,
}
}
type albumFilter struct {
options AlbumFilterOptions
}
func NewAlbumFilter(options AlbumFilterOptions) *albumFilter {
return &albumFilter{options}
}
func (a albumFilter) Options() AlbumFilterOptions {
return a.options
}
func (a *albumFilter) SetOptions(options AlbumFilterOptions) {
a.options = options
}
// Clone returns a deep copy of the filter
func (a albumFilter) Clone() AlbumFilter {
return NewAlbumFilter(a.options.Clone())
}
// Returns true if the filter is the nil filter - i.e. matches everything
func (a albumFilter) IsNil() bool {
return a.options.MinYear == 0 && a.options.MaxYear == 0 &&
len(a.options.Genres) == 0 &&
!a.options.ExcludeFavorited && !a.options.ExcludeUnfavorited
}
func (f albumFilter) Matches(album *Album) bool {
if album == nil {
return false
}
if f.options.ExcludeFavorited && album.Favorite {
return false
}
if f.options.ExcludeUnfavorited && !album.Favorite {
return false
}
if y := album.Year; y < f.options.MinYear || (f.options.MaxYear > 0 && y > f.options.MaxYear) {
return false
}
if len(f.options.Genres) == 0 {
return true
}
return genresMatch(f.options.Genres, album.Genres)
}
type ArtistFilter = MediaFilter[Artist, ArtistFilterOptions]
type ArtistFilterOptions struct{}
// Clone returns a deep copy of the filter options
func (o ArtistFilterOptions) Clone() ArtistFilterOptions {
return ArtistFilterOptions{}
}
type artistFilter struct {
options ArtistFilterOptions
}
func NewArtistFilter(options ArtistFilterOptions) *artistFilter {
return &artistFilter{options}
}
func (a artistFilter) Options() ArtistFilterOptions {
return a.options
}
func (a *artistFilter) SetOptions(o ArtistFilterOptions) {
a.options = o
}
// Clone returns a deep copy of the filter
func (a artistFilter) Clone() ArtistFilter {
return NewArtistFilter(a.options.Clone())
}
// Returns true if the filter is the nil filter - i.e. matches everything
func (a artistFilter) IsNil() bool {
return true
}
func (f artistFilter) Matches(artist *Artist) bool {
if artist == nil {
return false
}
return false
}
type RatingFavoriteParameters struct {
AlbumIDs []string
ArtistIDs []string
TrackIDs []string
}
type Favorites struct {
Albums []*Album
Artists []*Artist
Tracks []*Track
}
type LoginResponse struct {
Error error
IsAuthError bool
}
type Server interface {
Login(username, password string) LoginResponse
MediaProvider() MediaProvider
}
type MediaProvider interface {
SetPrefetchCoverCallback(cb func(coverArtID string))
GetTrack(trackID string) (*Track, error)
GetAlbum(albumID string) (*AlbumWithTracks, error)
GetAlbumInfo(albumID string) (*AlbumInfo, error)
GetArtist(artistID string) (*ArtistWithAlbums, error)
GetArtistInfo(artistID string) (*ArtistInfo, error)
GetPlaylist(playlistID string) (*PlaylistWithTracks, error)
GetCoverArt(coverArtID string, size int) (image.Image, error)
AlbumSortOrders() []string
IterateAlbums(sortOrder string, filter AlbumFilter) AlbumIterator
IterateTracks(searchQuery string) TrackIterator
SearchAlbums(searchQuery string, filter AlbumFilter) AlbumIterator
SearchAll(searchQuery string, maxResults int) ([]*SearchResult, error)
GetRandomTracks(genre string, count int) ([]*Track, error)
GetSimilarTracks(artistID string, count int) ([]*Track, error)
GetSongRadio(trackID string, count int) ([]*Track, error)
ArtistSortOrders() []string
IterateArtists(sortOrder string, filter ArtistFilter) ArtistIterator
SearchArtists(searchQuery string, filter ArtistFilter) ArtistIterator
GetGenres() ([]*Genre, error)
GetFavorites() (Favorites, error)
GetStreamURL(trackID string, forceRaw bool) (string, error)
GetTopTracks(artist Artist, count int) ([]*Track, error)
SetFavorite(params RatingFavoriteParameters, favorite bool) error
GetPlaylists() ([]*Playlist, error)
CreatePlaylist(name string, trackIDs []string) error
CanMakePublicPlaylist() bool
EditPlaylist(id, name, description string, public bool) error
AddPlaylistTracks(id string, trackIDsToAdd []string) error
RemovePlaylistTracks(id string, trackIdxsToRemove []int) error
ReplacePlaylistTracks(id string, trackIDs []string) error
DeletePlaylist(id string) error
// True if the `submission` parameter to TrackEndedPlayback will be respected
// If false, the begin playback scrobble registers a play count immediately
// when TrackBeganPlayback is invoked.
ClientDecidesScrobble() bool
TrackBeganPlayback(trackID string) error
TrackEndedPlayback(trackID string, positionSecs int, submission bool) error
DownloadTrack(trackID string) (io.Reader, error)
RescanLibrary() error
}
type SupportsRating interface {
SetRating(params RatingFavoriteParameters, rating int) error
}
type SupportsSharing interface {
CreateShareURL(id string) (*url.URL, error)
CanShareArtists() bool
}
type LyricsProvider interface {
GetLyrics(track *Track) (*Lyrics, error)
}
type JukeboxProvider interface {
JukeboxStart() error
JukeboxStop() error
JukeboxSeek(idx, seconds int) error
JukeboxClear() error
JukeboxSet(trackID string) error
JukeboxAdd(trackID string) error
JukeboxRemove(idx int) error
JukeboxSetVolume(vol int) error
JukeboxGetStatus() (*JukeboxStatus, error)
}
type JukeboxStatus struct {
Volume int
CurrentTrack int
Playing bool
PositionSeconds float64
}
func genresMatch(filterGenres, albumGenres []string) bool {
for _, g1 := range filterGenres {
for _, g2 := range albumGenres {
if strings.EqualFold(g1, g2) {
return true
}
}
}
return false
}