mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-04 13:07:36 +03:00
Make sorting lists by name/title case-insensitive (#2993)
* Make sort by order_* fields case-insensitive. * Sort internet radios by name case-insensitive
This commit is contained in:
parent
6408dda948
commit
c4b05dac28
9 changed files with 52 additions and 28 deletions
|
@ -9,7 +9,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Masterminds/squirrel"
|
"github.com/Masterminds/squirrel"
|
||||||
"github.com/deluan/sanitize"
|
|
||||||
"github.com/navidrome/navidrome/conf"
|
"github.com/navidrome/navidrome/conf"
|
||||||
"github.com/navidrome/navidrome/core/agents"
|
"github.com/navidrome/navidrome/core/agents"
|
||||||
_ "github.com/navidrome/navidrome/core/agents/lastfm"
|
_ "github.com/navidrome/navidrome/core/agents/lastfm"
|
||||||
|
@ -414,7 +413,7 @@ func (e *externalMetadata) findMatchingTrack(ctx context.Context, mbid string, a
|
||||||
squirrel.Eq{"artist_id": artistID},
|
squirrel.Eq{"artist_id": artistID},
|
||||||
squirrel.Eq{"album_artist_id": artistID},
|
squirrel.Eq{"album_artist_id": artistID},
|
||||||
},
|
},
|
||||||
squirrel.Like{"order_title": strings.TrimSpace(sanitize.Accents(title))},
|
squirrel.Like{"order_title": utils.SanitizeFieldForSorting(title)},
|
||||||
},
|
},
|
||||||
Sort: "starred desc, rating desc, year asc, compilation asc ",
|
Sort: "starred desc, rating desc, year asc, compilation asc ",
|
||||||
Max: 1,
|
Max: 1,
|
||||||
|
|
|
@ -85,6 +85,7 @@ func NewAlbumRepository(ctx context.Context, db dbx.Builder) model.AlbumReposito
|
||||||
r.sortMappings = map[string]string{
|
r.sortMappings = map[string]string{
|
||||||
"name": "order_album_name asc, order_album_artist_name asc",
|
"name": "order_album_name asc, order_album_artist_name asc",
|
||||||
"artist": "compilation asc, order_album_artist_name asc, order_album_name asc",
|
"artist": "compilation asc, order_album_artist_name asc, order_album_name asc",
|
||||||
|
"albumArtist": "compilation asc, order_album_artist_name asc, order_album_name asc",
|
||||||
"max_year": "coalesce(nullif(original_date,''), cast(max_year as text)), release_date, name, order_album_name asc",
|
"max_year": "coalesce(nullif(original_date,''), cast(max_year as text)), release_date, name, order_album_name asc",
|
||||||
"random": "RANDOM()",
|
"random": "RANDOM()",
|
||||||
"recently_added": recentlyAddedSort(),
|
"recently_added": recentlyAddedSort(),
|
||||||
|
|
|
@ -41,6 +41,7 @@ func NewMediaFileRepository(ctx context.Context, db dbx.Builder) *mediaFileRepos
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
r.sortMappings = map[string]string{
|
r.sortMappings = map[string]string{
|
||||||
|
"title": "order_title",
|
||||||
"artist": "order_artist_name asc, order_album_name asc, release_date asc, disc_number asc, track_number asc",
|
"artist": "order_artist_name asc, order_album_name asc, release_date asc, disc_number asc, track_number asc",
|
||||||
"album": "order_album_name asc, release_date asc, disc_number asc, track_number asc, order_artist_name asc, title asc",
|
"album": "order_album_name asc, release_date asc, disc_number asc, track_number asc, order_artist_name asc, title asc",
|
||||||
"random": "RANDOM()",
|
"random": "RANDOM()",
|
||||||
|
|
|
@ -30,10 +30,12 @@ func (r *playlistRepository) Tracks(playlistId string, refreshSmartPlaylist bool
|
||||||
"id": "playlist_tracks.id",
|
"id": "playlist_tracks.id",
|
||||||
"artist": "order_artist_name asc",
|
"artist": "order_artist_name asc",
|
||||||
"album": "order_album_name asc, order_album_artist_name asc",
|
"album": "order_album_name asc, order_album_artist_name asc",
|
||||||
|
"title": "order_title",
|
||||||
}
|
}
|
||||||
if conf.Server.PreferSortTags {
|
if conf.Server.PreferSortTags {
|
||||||
p.sortMappings["artist"] = "COALESCE(NULLIF(sort_artist_name,''),order_artist_name) asc"
|
p.sortMappings["artist"] = "COALESCE(NULLIF(sort_artist_name,''),order_artist_name) asc"
|
||||||
p.sortMappings["album"] = "COALESCE(NULLIF(sort_album_name,''),order_album_name)"
|
p.sortMappings["album"] = "COALESCE(NULLIF(sort_album_name,''),order_album_name)"
|
||||||
|
p.sortMappings["title"] = "COALESCE(NULLIF(sort_title,''),title)"
|
||||||
}
|
}
|
||||||
|
|
||||||
pls, err := r.Get(playlistId)
|
pls, err := r.Get(playlistId)
|
||||||
|
|
|
@ -26,6 +26,9 @@ func NewRadioRepository(ctx context.Context, db dbx.Builder) model.RadioReposito
|
||||||
r.filterMappings = map[string]filterFunc{
|
r.filterMappings = map[string]filterFunc{
|
||||||
"name": containsFilter,
|
"name": containsFilter,
|
||||||
}
|
}
|
||||||
|
r.sortMappings = map[string]string{
|
||||||
|
"name": "(name collate nocase), name",
|
||||||
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/deluan/sanitize"
|
|
||||||
"github.com/navidrome/navidrome/conf"
|
"github.com/navidrome/navidrome/conf"
|
||||||
"github.com/navidrome/navidrome/consts"
|
"github.com/navidrome/navidrome/consts"
|
||||||
"github.com/navidrome/navidrome/model"
|
"github.com/navidrome/navidrome/model"
|
||||||
|
@ -56,10 +55,10 @@ func (s MediaFileMapper) ToMediaFile(md metadata.Tags) model.MediaFile {
|
||||||
mf.SortAlbumName = md.SortAlbum()
|
mf.SortAlbumName = md.SortAlbum()
|
||||||
mf.SortArtistName = md.SortArtist()
|
mf.SortArtistName = md.SortArtist()
|
||||||
mf.SortAlbumArtistName = md.SortAlbumArtist()
|
mf.SortAlbumArtistName = md.SortAlbumArtist()
|
||||||
mf.OrderTitle = strings.TrimSpace(sanitize.Accents(mf.Title))
|
mf.OrderTitle = utils.SanitizeFieldForSorting(mf.Title)
|
||||||
mf.OrderAlbumName = sanitizeFieldForSorting(mf.Album)
|
mf.OrderAlbumName = utils.SanitizeFieldForSortingNoArticle(mf.Album)
|
||||||
mf.OrderArtistName = sanitizeFieldForSorting(mf.Artist)
|
mf.OrderArtistName = utils.SanitizeFieldForSortingNoArticle(mf.Artist)
|
||||||
mf.OrderAlbumArtistName = sanitizeFieldForSorting(mf.AlbumArtist)
|
mf.OrderAlbumArtistName = utils.SanitizeFieldForSortingNoArticle(mf.AlbumArtist)
|
||||||
mf.CatalogNum = md.CatalogNum()
|
mf.CatalogNum = md.CatalogNum()
|
||||||
mf.MbzRecordingID = md.MbzRecordingID()
|
mf.MbzRecordingID = md.MbzRecordingID()
|
||||||
mf.MbzReleaseTrackID = md.MbzReleaseTrackID()
|
mf.MbzReleaseTrackID = md.MbzReleaseTrackID()
|
||||||
|
@ -81,11 +80,6 @@ func (s MediaFileMapper) ToMediaFile(md metadata.Tags) model.MediaFile {
|
||||||
return *mf
|
return *mf
|
||||||
}
|
}
|
||||||
|
|
||||||
func sanitizeFieldForSorting(originalValue string) string {
|
|
||||||
v := strings.TrimSpace(sanitize.Accents(originalValue))
|
|
||||||
return utils.NoArticle(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s MediaFileMapper) mapTrackTitle(md metadata.Tags) string {
|
func (s MediaFileMapper) mapTrackTitle(md metadata.Tags) string {
|
||||||
if md.Title() == "" {
|
if md.Title() == "" {
|
||||||
s := strings.TrimPrefix(md.FilePath(), s.rootFolder+string(os.PathSeparator))
|
s := strings.TrimPrefix(md.FilePath(), s.rootFolder+string(os.PathSeparator))
|
||||||
|
|
|
@ -3,7 +3,6 @@ package scanner
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/navidrome/navidrome/conf"
|
|
||||||
"github.com/navidrome/navidrome/model"
|
"github.com/navidrome/navidrome/model"
|
||||||
"github.com/navidrome/navidrome/scanner/metadata"
|
"github.com/navidrome/navidrome/scanner/metadata"
|
||||||
"github.com/navidrome/navidrome/tests"
|
"github.com/navidrome/navidrome/tests"
|
||||||
|
@ -161,19 +160,4 @@ var _ = Describe("mapping", func() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("sanitizeFieldForSorting", func() {
|
|
||||||
BeforeEach(func() {
|
|
||||||
conf.Server.IgnoredArticles = "The O"
|
|
||||||
})
|
|
||||||
It("sanitize accents", func() {
|
|
||||||
Expect(sanitizeFieldForSorting("Céu")).To(Equal("Ceu"))
|
|
||||||
})
|
|
||||||
It("removes articles", func() {
|
|
||||||
Expect(sanitizeFieldForSorting("The Beatles")).To(Equal("Beatles"))
|
|
||||||
})
|
|
||||||
It("removes accented articles", func() {
|
|
||||||
Expect(sanitizeFieldForSorting("Õ Blésq Blom")).To(Equal("Blesq Blom"))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,6 +3,7 @@ package utils
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/deluan/sanitize"
|
||||||
"github.com/navidrome/navidrome/conf"
|
"github.com/navidrome/navidrome/conf"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,3 +33,13 @@ func LongestCommonPrefix(list []string) string {
|
||||||
}
|
}
|
||||||
return list[0]
|
return list[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SanitizeFieldForSorting(originalValue string) string {
|
||||||
|
v := strings.TrimSpace(sanitize.Accents(originalValue))
|
||||||
|
return strings.ToLower(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SanitizeFieldForSortingNoArticle(originalValue string) string {
|
||||||
|
v := strings.TrimSpace(sanitize.Accents(originalValue))
|
||||||
|
return strings.ToLower(NoArticle(v))
|
||||||
|
}
|
||||||
|
|
|
@ -35,6 +35,35 @@ var _ = Describe("Strings", func() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Describe("sanitizeFieldForSorting", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
conf.Server.IgnoredArticles = "The O"
|
||||||
|
})
|
||||||
|
It("sanitize accents", func() {
|
||||||
|
Expect(SanitizeFieldForSorting("Céu")).To(Equal("ceu"))
|
||||||
|
})
|
||||||
|
It("removes articles", func() {
|
||||||
|
Expect(SanitizeFieldForSorting("The Beatles")).To(Equal("the beatles"))
|
||||||
|
})
|
||||||
|
It("removes accented articles", func() {
|
||||||
|
Expect(SanitizeFieldForSorting("Õ Blésq Blom")).To(Equal("o blesq blom"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
Describe("SanitizeFieldForSortingNoArticle", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
conf.Server.IgnoredArticles = "The O"
|
||||||
|
})
|
||||||
|
It("sanitize accents", func() {
|
||||||
|
Expect(SanitizeFieldForSortingNoArticle("Céu")).To(Equal("ceu"))
|
||||||
|
})
|
||||||
|
It("removes articles", func() {
|
||||||
|
Expect(SanitizeFieldForSortingNoArticle("The Beatles")).To(Equal("beatles"))
|
||||||
|
})
|
||||||
|
It("removes accented articles", func() {
|
||||||
|
Expect(SanitizeFieldForSortingNoArticle("Õ Blésq Blom")).To(Equal("blesq blom"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
Describe("LongestCommonPrefix", func() {
|
Describe("LongestCommonPrefix", func() {
|
||||||
It("finds the longest common prefix", func() {
|
It("finds the longest common prefix", func() {
|
||||||
Expect(LongestCommonPrefix(testPaths)).To(Equal("/Music/iTunes 1/iTunes Media/Music/"))
|
Expect(LongestCommonPrefix(testPaths)).To(Equal("/Music/iTunes 1/iTunes Media/Music/"))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue