#782 for Jellyfin: treat usernames as case-insensitive

This commit is contained in:
Drew Weymouth
2026-01-24 11:05:12 -08:00
parent 8e2e4c6d89
commit e6127ccc11
5 changed files with 57 additions and 50 deletions
@@ -9,7 +9,7 @@ import (
"github.com/dweymouth/supersonic/sharedutil"
)
func (j *jellyfinMediaProvider) ArtistSortOrders() []string {
func (j *JellyfinMediaProvider) ArtistSortOrders() []string {
return []string{
mediaprovider.ArtistSortAlbumCount,
mediaprovider.ArtistSortNameAZ,
@@ -17,7 +17,7 @@ func (j *jellyfinMediaProvider) ArtistSortOrders() []string {
}
}
func (j *jellyfinMediaProvider) IterateArtists(sortOrder string, filter mediaprovider.ArtistFilter) mediaprovider.ArtistIterator {
func (j *JellyfinMediaProvider) IterateArtists(sortOrder string, filter mediaprovider.ArtistFilter) mediaprovider.ArtistIterator {
var jfSort jellyfin.Sort
var disablePagination bool
var sortFn func([]*jellyfin.Artist) []*jellyfin.Artist
@@ -63,7 +63,7 @@ func (j *jellyfinMediaProvider) IterateArtists(sortOrder string, filter mediapro
return helpers.NewArtistIterator(fetcher, filter, j.prefetchCoverCB)
}
func (j *jellyfinMediaProvider) SearchArtists(searchQuery string, filter mediaprovider.ArtistFilter) mediaprovider.ArtistIterator {
func (j *JellyfinMediaProvider) SearchArtists(searchQuery string, filter mediaprovider.ArtistFilter) mediaprovider.ArtistIterator {
// TODO: Jellyfin API is not returning search results for artists.
// Uncomment the following code once the issue is resolved.
// Related issue: https://github.com/jellyfin/jellyfin/issues/8222
+4 -4
View File
@@ -9,7 +9,7 @@ import (
"github.com/dweymouth/supersonic/sharedutil"
)
func (j *jellyfinMediaProvider) AlbumSortOrders() []string {
func (j *JellyfinMediaProvider) AlbumSortOrders() []string {
return []string{
mediaprovider.AlbumSortRecentlyAdded,
mediaprovider.AlbumSortRandom,
@@ -20,7 +20,7 @@ func (j *jellyfinMediaProvider) AlbumSortOrders() []string {
}
}
func (j *jellyfinMediaProvider) IterateAlbums(sortOrder string, filter mediaprovider.AlbumFilter) mediaprovider.AlbumIterator {
func (j *JellyfinMediaProvider) IterateAlbums(sortOrder string, filter mediaprovider.AlbumFilter) mediaprovider.AlbumIterator {
var jfSort jellyfin.Sort
switch sortOrder {
case mediaprovider.AlbumSortRecentlyAdded:
@@ -75,7 +75,7 @@ func (j *jellyfinMediaProvider) IterateAlbums(sortOrder string, filter mediaprov
return helpers.NewAlbumIterator(fetcher, modifiedFilter, j.prefetchCoverCB)
}
func (j *jellyfinMediaProvider) SearchAlbums(searchQuery string, filter mediaprovider.AlbumFilter) mediaprovider.AlbumIterator {
func (j *JellyfinMediaProvider) SearchAlbums(searchQuery string, filter mediaprovider.AlbumFilter) mediaprovider.AlbumIterator {
fetcher := func(offs, limit int) ([]*mediaprovider.Album, error) {
var opts jellyfin.QueryOpts
opts.Paging = jellyfin.Paging{StartIndex: offs, Limit: limit}
@@ -89,7 +89,7 @@ func (j *jellyfinMediaProvider) SearchAlbums(searchQuery string, filter mediapro
return helpers.NewAlbumIterator(fetcher, filter, j.prefetchCoverCB)
}
func (j *jellyfinMediaProvider) IterateTracks(searchQuery string) mediaprovider.TrackIterator {
func (j *JellyfinMediaProvider) IterateTracks(searchQuery string) mediaprovider.TrackIterator {
var fetcher helpers.TrackFetchFn
if searchQuery == "" {
fetcher = func(offs, limit int) ([]*mediaprovider.Track, error) {
@@ -38,9 +38,9 @@ func (j *JellyfinServer) MediaProvider() mediaprovider.MediaProvider {
return newJellyfinMediaProvider(&j.Client)
}
var _ mediaprovider.MediaProvider = (*jellyfinMediaProvider)(nil)
var _ mediaprovider.MediaProvider = (*JellyfinMediaProvider)(nil)
type jellyfinMediaProvider struct {
type JellyfinMediaProvider struct {
client *jellyfin.Client
prefetchCoverCB func(coverArtID string)
@@ -51,17 +51,17 @@ type jellyfinMediaProvider struct {
}
func newJellyfinMediaProvider(cli *jellyfin.Client) mediaprovider.MediaProvider {
return &jellyfinMediaProvider{
return &JellyfinMediaProvider{
client: cli,
genresCached: make([]*mediaprovider.Genre, 0),
}
}
func (j *jellyfinMediaProvider) SetPrefetchCoverCallback(cb func(coverArtID string)) {
func (j *JellyfinMediaProvider) SetPrefetchCoverCallback(cb func(coverArtID string)) {
j.prefetchCoverCB = cb
}
func (j *jellyfinMediaProvider) GetLibraries() ([]mediaprovider.Library, error) {
func (j *JellyfinMediaProvider) GetLibraries() ([]mediaprovider.Library, error) {
v, err := j.client.GetUserViews()
if err != nil {
return nil, err
@@ -71,41 +71,41 @@ func (j *jellyfinMediaProvider) GetLibraries() ([]mediaprovider.Library, error)
}), nil
}
func (j *jellyfinMediaProvider) SetLibrary(id string) error {
func (j *JellyfinMediaProvider) SetLibrary(id string) error {
j.currentLibraryID = id
j.genresCached = nil
return nil
}
func (j *jellyfinMediaProvider) CreatePlaylistWithTracks(name string, trackIDs []string) error {
func (j *JellyfinMediaProvider) CreatePlaylistWithTracks(name string, trackIDs []string) error {
return j.client.CreatePlaylist(name, "", false, trackIDs)
}
func (j *jellyfinMediaProvider) DeletePlaylist(id string) error {
func (j *JellyfinMediaProvider) DeletePlaylist(id string) error {
return j.client.DeletePlaylist(id)
}
func (j *jellyfinMediaProvider) CanMakePublicPlaylist() bool {
func (j *JellyfinMediaProvider) CanMakePublicPlaylist() bool {
return false
}
func (j *jellyfinMediaProvider) EditPlaylist(id, name, description string, public bool) error {
func (j *JellyfinMediaProvider) EditPlaylist(id, name, description string, public bool) error {
return j.client.UpdatePlaylistMetadata(id, name, description, false)
}
func (j *jellyfinMediaProvider) CreatePlaylist(name, description string, public bool) error {
func (j *JellyfinMediaProvider) CreatePlaylist(name, description string, public bool) error {
return j.client.CreatePlaylist(name, description, public, nil)
}
func (j *jellyfinMediaProvider) AddPlaylistTracks(id string, trackIDsToAdd []string) error {
func (j *JellyfinMediaProvider) AddPlaylistTracks(id string, trackIDsToAdd []string) error {
return j.client.AddSongsToPlaylist(id, trackIDsToAdd)
}
func (j *jellyfinMediaProvider) RemovePlaylistTracks(playlistID string, removeIdxs []int) error {
func (j *JellyfinMediaProvider) RemovePlaylistTracks(playlistID string, removeIdxs []int) error {
return j.client.RemoveSongsFromPlaylist(playlistID, removeIdxs)
}
func (j *jellyfinMediaProvider) ReplacePlaylistTracks(playlistID string, trackIDs []string) error {
func (j *JellyfinMediaProvider) ReplacePlaylistTracks(playlistID string, trackIDs []string) error {
pl, err := j.client.GetPlaylist(playlistID)
if err != nil {
return err
@@ -120,7 +120,7 @@ func (j *jellyfinMediaProvider) ReplacePlaylistTracks(playlistID string, trackID
return j.client.AddSongsToPlaylist(playlistID, trackIDs)
}
func (j *jellyfinMediaProvider) GetAlbum(albumID string) (*mediaprovider.AlbumWithTracks, error) {
func (j *JellyfinMediaProvider) GetAlbum(albumID string) (*mediaprovider.AlbumWithTracks, error) {
al, err := j.client.GetAlbum(albumID)
if err != nil {
return nil, err
@@ -138,7 +138,7 @@ func (j *jellyfinMediaProvider) GetAlbum(albumID string) (*mediaprovider.AlbumWi
return album, nil
}
func (j *jellyfinMediaProvider) GetAlbumInfo(albumID string) (*mediaprovider.AlbumInfo, error) {
func (j *JellyfinMediaProvider) GetAlbumInfo(albumID string) (*mediaprovider.AlbumInfo, error) {
al, err := j.client.GetAlbum(albumID)
if err != nil {
return nil, err
@@ -148,7 +148,7 @@ func (j *jellyfinMediaProvider) GetAlbumInfo(albumID string) (*mediaprovider.Alb
}, nil
}
func (j *jellyfinMediaProvider) GetArtist(artistID string) (*mediaprovider.ArtistWithAlbums, error) {
func (j *JellyfinMediaProvider) GetArtist(artistID string) (*mediaprovider.ArtistWithAlbums, error) {
ar, err := j.client.GetArtist(artistID)
if err != nil {
return nil, err
@@ -167,11 +167,11 @@ func (j *jellyfinMediaProvider) GetArtist(artistID string) (*mediaprovider.Artis
return artist, nil
}
func (j *jellyfinMediaProvider) GetArtistTracks(artistID string) ([]*mediaprovider.Track, error) {
func (j *JellyfinMediaProvider) GetArtistTracks(artistID string) ([]*mediaprovider.Track, error) {
return helpers.GetArtistTracks(j, artistID)
}
func (j *jellyfinMediaProvider) GetArtistInfo(artistID string) (*mediaprovider.ArtistInfo, error) {
func (j *JellyfinMediaProvider) GetArtistInfo(artistID string) (*mediaprovider.ArtistInfo, error) {
ar, err := j.client.GetArtist(artistID)
if err != nil {
return nil, err
@@ -186,7 +186,7 @@ func (j *jellyfinMediaProvider) GetArtistInfo(artistID string) (*mediaprovider.A
}, nil
}
func (j *jellyfinMediaProvider) GetTrack(trackID string) (*mediaprovider.Track, error) {
func (j *JellyfinMediaProvider) GetTrack(trackID string) (*mediaprovider.Track, error) {
tr, err := j.client.GetSong(trackID)
if err != nil {
return nil, err
@@ -194,7 +194,7 @@ func (j *jellyfinMediaProvider) GetTrack(trackID string) (*mediaprovider.Track,
return toTrack(tr), nil
}
func (j *jellyfinMediaProvider) GetTopTracks(artist mediaprovider.Artist, limit int) ([]*mediaprovider.Track, error) {
func (j *JellyfinMediaProvider) GetTopTracks(artist mediaprovider.Artist, limit int) ([]*mediaprovider.Track, error) {
var opts jellyfin.QueryOpts
opts.Paging.Limit = limit
opts.Filter.ArtistID = artist.ID
@@ -213,7 +213,7 @@ func (j *jellyfinMediaProvider) GetTopTracks(artist mediaprovider.Artist, limit
return sharedutil.MapSlice(tr, toTrack), nil
}
func (j *jellyfinMediaProvider) GetRandomTracks(genreName string, limit int) ([]*mediaprovider.Track, error) {
func (j *JellyfinMediaProvider) GetRandomTracks(genreName string, limit int) ([]*mediaprovider.Track, error) {
var opts jellyfin.QueryOpts
opts.Paging.Limit = limit
opts.Filter.Genres = []string{genreName}
@@ -228,7 +228,7 @@ func (j *jellyfinMediaProvider) GetRandomTracks(genreName string, limit int) ([]
return sharedutil.MapSlice(tr, toTrack), nil
}
func (j *jellyfinMediaProvider) GetSimilarTracks(artistID string, limit int) ([]*mediaprovider.Track, error) {
func (j *JellyfinMediaProvider) GetSimilarTracks(artistID string, limit int) ([]*mediaprovider.Track, error) {
tr, err := j.client.GetInstantMix(artistID, jellyfin.TypeArtist, limit)
if err != nil {
return nil, err
@@ -236,11 +236,11 @@ func (j *jellyfinMediaProvider) GetSimilarTracks(artistID string, limit int) ([]
return sharedutil.MapSlice(tr, toTrack), nil
}
func (j *jellyfinMediaProvider) GetCoverArt(id string, size int) (image.Image, error) {
func (j *JellyfinMediaProvider) GetCoverArt(id string, size int) (image.Image, error) {
return j.client.GetItemImage(id, "Primary", size, 92)
}
func (j *jellyfinMediaProvider) GetFavorites() (mediaprovider.Favorites, error) {
func (j *JellyfinMediaProvider) GetFavorites() (mediaprovider.Favorites, error) {
var wg sync.WaitGroup
var favorites mediaprovider.Favorites
@@ -278,7 +278,7 @@ func (j *jellyfinMediaProvider) GetFavorites() (mediaprovider.Favorites, error)
return favorites, nil
}
func (j *jellyfinMediaProvider) GetGenres() ([]*mediaprovider.Genre, error) {
func (j *JellyfinMediaProvider) GetGenres() ([]*mediaprovider.Genre, error) {
if j.genresCached != nil && time.Now().Unix()-j.genresCachedAt < cacheValidDurationSeconds {
return j.genresCached, nil
}
@@ -298,7 +298,7 @@ func (j *jellyfinMediaProvider) GetGenres() ([]*mediaprovider.Genre, error) {
return j.genresCached, nil
}
func (j *jellyfinMediaProvider) GetPlaylists() ([]*mediaprovider.Playlist, error) {
func (j *JellyfinMediaProvider) GetPlaylists() ([]*mediaprovider.Playlist, error) {
pl, err := j.client.GetPlaylists()
if err != nil {
return nil, err
@@ -306,7 +306,7 @@ func (j *jellyfinMediaProvider) GetPlaylists() ([]*mediaprovider.Playlist, error
return sharedutil.MapSlice(pl, j.toPlaylist), nil
}
func (j *jellyfinMediaProvider) GetPlaylist(playlistID string) (*mediaprovider.PlaylistWithTracks, error) {
func (j *JellyfinMediaProvider) GetPlaylist(playlistID string) (*mediaprovider.PlaylistWithTracks, error) {
tr, err := j.client.GetPlaylistSongs(playlistID)
if err != nil {
return nil, err
@@ -323,7 +323,7 @@ func (j *jellyfinMediaProvider) GetPlaylist(playlistID string) (*mediaprovider.P
return playlist, nil
}
func (j *jellyfinMediaProvider) SetFavorite(params mediaprovider.RatingFavoriteParameters, favorite bool) error {
func (j *JellyfinMediaProvider) SetFavorite(params mediaprovider.RatingFavoriteParameters, favorite bool) error {
var allIDs []string
allIDs = append(allIDs, params.AlbumIDs...)
allIDs = append(allIDs, params.ArtistIDs...)
@@ -357,7 +357,7 @@ func (j *jellyfinMediaProvider) SetFavorite(params mediaprovider.RatingFavoriteP
return err
}
func (j *jellyfinMediaProvider) GetStreamURL(trackID string, transcode *mediaprovider.TranscodeSettings, forceRaw bool) (string, error) {
func (j *JellyfinMediaProvider) GetStreamURL(trackID string, transcode *mediaprovider.TranscodeSettings, forceRaw bool) (string, error) {
var jfTranscode *jellyfin.TranscodeOptions
if transcode != nil {
jfTranscode = &jellyfin.TranscodeOptions{
@@ -371,7 +371,7 @@ func (j *jellyfinMediaProvider) GetStreamURL(trackID string, transcode *mediapro
return j.client.GetStreamURL(trackID, jfTranscode)
}
func (j *jellyfinMediaProvider) DownloadTrack(trackID string) (io.Reader, error) {
func (j *JellyfinMediaProvider) DownloadTrack(trackID string) (io.Reader, error) {
url, err := j.client.GetStreamURL(trackID, nil)
if err != nil {
return nil, err
@@ -383,23 +383,23 @@ func (j *jellyfinMediaProvider) DownloadTrack(trackID string) (io.Reader, error)
return resp.Body, nil
}
func (j *jellyfinMediaProvider) ClientDecidesScrobble() bool { return false }
func (j *JellyfinMediaProvider) ClientDecidesScrobble() bool { return false }
func (j *jellyfinMediaProvider) TrackBeganPlayback(trackID string) error {
func (j *JellyfinMediaProvider) TrackBeganPlayback(trackID string) error {
return j.client.UpdatePlayStatus(trackID, jellyfin.Start, 0)
}
func (j *jellyfinMediaProvider) TrackEndedPlayback(trackID string, position int, submission bool) error {
func (j *JellyfinMediaProvider) TrackEndedPlayback(trackID string, position int, submission bool) error {
return j.client.UpdatePlayStatus(trackID, jellyfin.Stop, int64(position)*runTimeTicksPerMicrosecond*1_000_000)
}
func (j *jellyfinMediaProvider) RescanLibrary() error {
func (j *JellyfinMediaProvider) RescanLibrary() error {
return j.client.RefreshLibrary()
}
var _ mediaprovider.LyricsProvider = (*jellyfinMediaProvider)(nil)
var _ mediaprovider.LyricsProvider = (*JellyfinMediaProvider)(nil)
func (j *jellyfinMediaProvider) GetLyrics(tr *mediaprovider.Track) (*mediaprovider.Lyrics, error) {
func (j *JellyfinMediaProvider) GetLyrics(tr *mediaprovider.Track) (*mediaprovider.Lyrics, error) {
l, err := j.client.GetLyrics(tr.ID)
if err != nil {
return nil, err
@@ -515,13 +515,13 @@ func fillAlbum(a *jellyfin.Album, album *mediaprovider.Album) {
album.ReleaseTypes = mediaprovider.ReleaseTypeAlbum
}
func (j *jellyfinMediaProvider) toPlaylist(p *jellyfin.Playlist) *mediaprovider.Playlist {
func (j *JellyfinMediaProvider) toPlaylist(p *jellyfin.Playlist) *mediaprovider.Playlist {
pl := &mediaprovider.Playlist{}
j.fillPlaylist(p, pl)
return pl
}
func (j *jellyfinMediaProvider) fillPlaylist(p *jellyfin.Playlist, pl *mediaprovider.Playlist) {
func (j *JellyfinMediaProvider) fillPlaylist(p *jellyfin.Playlist, pl *mediaprovider.Playlist) {
pl.Name = p.Name
pl.ID = p.ID
pl.CoverArtID = p.ID
@@ -533,7 +533,7 @@ func (j *jellyfinMediaProvider) fillPlaylist(p *jellyfin.Playlist, pl *mediaprov
pl.Public = false
}
func (j *jellyfinMediaProvider) GetSongRadio(trackID string, count int) ([]*mediaprovider.Track, error) {
func (j *JellyfinMediaProvider) GetSongRadio(trackID string, count int) ([]*mediaprovider.Track, error) {
tr, err := j.client.GetInstantMix(trackID, jellyfin.TypeSong, count)
if err != nil {
return nil, err
+2 -2
View File
@@ -11,7 +11,7 @@ import (
"github.com/dweymouth/supersonic/sharedutil"
)
func (j *jellyfinMediaProvider) SearchAll(searchQuery string, maxResults int) ([]*mediaprovider.SearchResult, error) {
func (j *JellyfinMediaProvider) SearchAll(searchQuery string, maxResults int) ([]*mediaprovider.SearchResult, error) {
limit := maxResults / 3
var wg sync.WaitGroup
var albums []*jellyfin.Album
@@ -75,7 +75,7 @@ func (j *jellyfinMediaProvider) SearchAll(searchQuery string, maxResults int) ([
return results, nil
}
func (j *jellyfinMediaProvider) mergeResults(
func (j *JellyfinMediaProvider) mergeResults(
albums []*jellyfin.Album,
artists []*jellyfin.Artist,
songs []*jellyfin.Song,
+8 -1
View File
@@ -12,6 +12,7 @@ import (
"fyne.io/fyne/v2/widget"
"github.com/deluan/sanitize"
"github.com/dweymouth/supersonic/backend/mediaprovider"
"github.com/dweymouth/supersonic/backend/mediaprovider/jellyfin"
"github.com/dweymouth/supersonic/sharedutil"
"github.com/dweymouth/supersonic/ui/util"
)
@@ -49,7 +50,13 @@ func (sp *SelectPlaylist) fetchUserOwnedPlaylists() {
log.Printf("error getting playlists: %s", err.Error())
}
userPlaylists := sharedutil.FilterSlice(playlists, func(playlist *mediaprovider.Playlist) bool {
return playlist.Owner == sp.loggedInUser
if _, isJellyfin := sp.mp.(*jellyfin.JellyfinMediaProvider); isJellyfin {
// Jellyfin usernames are case-insensitive
return strings.EqualFold(playlist.Owner, sp.loggedInUser)
} else {
// Subsonic usernames are case-sensitive
return playlist.Owner == sp.loggedInUser
}
})
sp.allPlaylistResuts = sharedutil.MapSlice(userPlaylists, sp.playlistToSearchResult)
}