fix(subsonic): Sort songs by presence of lyrics for getLyrics (#4237)

* fix(subsonic): Sort songs by presence of lyrics for `getLyrics`

The current implementation of `getLyrics` fetches any songs matching the artist and title.
However, this misses a case where there may be multiple matches for the same artist/song, and one has lyrics while the other doesn't.
Resolve this by adding a custom SQL dynamic column that checks for the presence of lyrics.

* add options to selectMediaFile, update test

* more robust testing of GetAllByLyrics

* fix(subsonic): refactor GetAllByLyrics to GetAll with lyrics sorting

Signed-off-by: Deluan <deluan@navidrome.org>

* use has_lyrics, and properly support multiple sort parts

* better handle complicated internal sorts

* just use a simpler filter

* add note to setSortMappings

* remove custom sort mapping, improve test with different updatedat

* refactor tests and mock

Signed-off-by: Deluan <deluan@navidrome.org>

* default order when not specified is `asc`

Signed-off-by: Deluan <deluan@navidrome.org>

---------

Signed-off-by: Deluan <deluan@navidrome.org>
Co-authored-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Kendall Garner
2025-06-16 16:04:41 +00:00
committed by GitHub
parent 873905bdf6
commit 8d594671c4
7 changed files with 106 additions and 11 deletions
+12
View File
@@ -136,6 +136,10 @@ var _ = Describe("sqlRepository", func() {
})
Describe("buildSortOrder", func() {
BeforeEach(func() {
r.sortMappings = map[string]string{}
})
Context("single field", func() {
It("sorts by specified field", func() {
sql := r.buildSortOrder("name", "desc")
@@ -163,6 +167,14 @@ var _ = Describe("sqlRepository", func() {
sql := r.buildSortOrder("name desc, age, status asc", "desc")
Expect(sql).To(Equal("name asc, age desc, status desc"))
})
It("handles spaces in mapped field", func() {
r.sortMappings = map[string]string{
"has_lyrics": "(lyrics != '[]'), updated_at",
}
sql := r.buildSortOrder("has_lyrics", "desc")
Expect(sql).To(Equal("(lyrics != '[]') desc, updated_at desc"))
})
})
Context("function fields", func() {
It("handles functions with multiple params", func() {