From f12b2f673d01725b9ac68dc5f148d70aec34e166 Mon Sep 17 00:00:00 2001 From: emeric Date: Thu, 19 Feb 2026 23:14:44 +0100 Subject: [PATCH] OS API: prefer using original date when fetching albums using byYear, fixes #819 --- src/libs/database/impl/objects/Release.cpp | 9 ++++ .../include/database/objects/Release.hpp | 6 +++ src/libs/database/test/Release.cpp | 54 +++++++++++++++++++ .../impl/endpoints/AlbumSongLists.cpp | 4 +- 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/libs/database/impl/objects/Release.cpp b/src/libs/database/impl/objects/Release.cpp index bd439d1a..96d3a427 100644 --- a/src/libs/database/impl/objects/Release.cpp +++ b/src/libs/database/impl/objects/Release.cpp @@ -73,6 +73,7 @@ namespace lms::db || params.sortMethod == ReleaseSortMethod::OriginalDateDesc || params.writtenAfter.isValid() || params.dateRange + || params.originalDateRange || params.trackArtist.isValid() || params.filters.clusters.size() == 1 || params.filters.mediaLibrary.isValid() @@ -120,10 +121,18 @@ namespace lms::db if (params.dateRange) { + assert(!params.originalDateRange); query.where("CAST(SUBSTR(t.date, 1, 4) AS INTEGER) >= ?").bind(params.dateRange->begin); query.where("CAST(SUBSTR(t.date, 1, 4) AS INTEGER) <= ?").bind(params.dateRange->end); } + if (params.originalDateRange) + { + assert(!params.dateRange); + query.where("CAST(SUBSTR(COALESCE(t.original_date, t.date), 1, 4) AS INTEGER) >= ?").bind(params.originalDateRange->begin); + query.where("CAST(SUBSTR(COALESCE(t.original_date, t.date), 1, 4) AS INTEGER) <= ?").bind(params.originalDateRange->end); + } + if (!params.name.empty()) query.where("r.name = ?").bind(params.name); diff --git a/src/libs/database/include/database/objects/Release.hpp b/src/libs/database/include/database/objects/Release.hpp index 6f6c1035..81af2ae6 100644 --- a/src/libs/database/include/database/objects/Release.hpp +++ b/src/libs/database/include/database/objects/Release.hpp @@ -169,6 +169,7 @@ namespace lms::db std::optional range; Wt::WDateTime writtenAfter; std::optional dateRange; + std::optional originalDateRange; UserId starringUser; // only releases starred by this user std::optional feedbackBackend; // and for this backend ArtistId artist; // only releases by this release artist @@ -214,6 +215,11 @@ namespace lms::db dateRange = _dateRange; return *this; } + FindParameters& setOriginalDateRange(const std::optional& _originalDateRange) + { + originalDateRange = _originalDateRange; + return *this; + } FindParameters& setStarringUser(UserId _user, FeedbackBackend _feedbackBackend) { starringUser = _user; diff --git a/src/libs/database/test/Release.cpp b/src/libs/database/test/Release.cpp index f81332b4..9f5e3932 100644 --- a/src/libs/database/test/Release.cpp +++ b/src/libs/database/test/Release.cpp @@ -607,6 +607,60 @@ namespace lms::db::tests } } + TEST_F(DatabaseFixture, MultiTracksSingleReleaseOriginalDate) + { + ScopedRelease release1{ session, "MyRelease1" }; + ScopedRelease release2{ session, "MyRelease2" }; + + const core::PartialDateTime release1Date{ 1994, 2, 3 }; + const core::PartialDateTime release1OriginalDate{ 1993, 4, 5 }; + + ScopedTrack track1A{ session }; + ScopedTrack track1B{ session }; + ScopedTrack track2A{ session }; + ScopedTrack track2B{ session }; + + { + auto transaction{ session.createReadTransaction() }; + + const auto releases{ Release::findIds(session, Release::FindParameters{}.setOriginalDateRange(YearRange{ -3000, 3000 })) }; + EXPECT_EQ(releases.results.size(), 0); + } + + { + auto transaction{ session.createWriteTransaction() }; + + track1A.get().modify()->setRelease(release1.get()); + track1B.get().modify()->setRelease(release1.get()); + track2A.get().modify()->setRelease(release2.get()); + track2B.get().modify()->setRelease(release2.get()); + + track1A.get().modify()->setDate(release1Date); + track1B.get().modify()->setDate(release1Date); + + track1A.get().modify()->setOriginalDate(release1OriginalDate); + track1B.get().modify()->setOriginalDate(release1OriginalDate); + + EXPECT_EQ(release1.get()->getOriginalDate(), release1OriginalDate); + EXPECT_EQ(release1.get()->getOriginalYear(), release1OriginalDate.getYear()); + } + + { + auto transaction{ session.createReadTransaction() }; + + auto releases = Release::findIds(session, Release::FindParameters{}.setOriginalDateRange(YearRange{ 1950, 2000 })); + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release1.getId()); + + releases = Release::findIds(session, Release::FindParameters{}.setOriginalDateRange(YearRange{ 1993, 1993 })); + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release1.getId()); + + releases = Release::findIds(session, Release::FindParameters{}.setOriginalDateRange(YearRange{ 1994, 1994 })); + ASSERT_EQ(releases.results.size(), 0); + } + } + TEST_F(DatabaseFixture, MultiTracksSingleReleaseYear) { ScopedRelease release1{ session, "MyRelease1" }; diff --git a/src/libs/subsonic/impl/endpoints/AlbumSongLists.cpp b/src/libs/subsonic/impl/endpoints/AlbumSongLists.cpp index f79ef605..2ac52f1c 100644 --- a/src/libs/subsonic/impl/endpoints/AlbumSongLists.cpp +++ b/src/libs/subsonic/impl/endpoints/AlbumSongLists.cpp @@ -107,9 +107,9 @@ namespace lms::api::subsonic const int toYear{ getMandatoryParameterAs(context.getParameters(), "toYear") }; Release::FindParameters params; - params.setSortMethod(fromYear > toYear ? ReleaseSortMethod::DateDesc : ReleaseSortMethod::DateAsc); + params.setSortMethod(fromYear > toYear ? ReleaseSortMethod::OriginalDateDesc : ReleaseSortMethod::OriginalDate); params.setRange(range); - params.setDateRange(YearRange{ std::min(fromYear, toYear), std::max(fromYear, toYear) }); + params.setOriginalDateRange(YearRange{ std::min(fromYear, toYear), std::max(fromYear, toYear) }); params.filters.setMediaLibrary(mediaLibraryId); releases = Release::findIds(context.getDbSession(), params);