OpenSubsonic: changed the originalReleaseDate field format, fixes #400
This commit is contained in:
@@ -299,6 +299,16 @@ CREATE TABLE IF NOT EXISTS "track_backup" (
|
||||
session.getDboSession().execute("UPDATE scan_settings SET scan_version = scan_version + 1");
|
||||
}
|
||||
|
||||
void migrateFromV49(Session& session)
|
||||
{
|
||||
// Add year / originalYear fields, as date / originalDate are not enough (we don't want a wrong date but year or nothing)
|
||||
session.getDboSession().execute("ALTER TABLE track ADD year INTEGER");
|
||||
session.getDboSession().execute("ALTER TABLE track ADD original_year INTEGER");
|
||||
|
||||
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
|
||||
session.getDboSession().execute("UPDATE scan_settings SET scan_version = scan_version + 1");
|
||||
}
|
||||
|
||||
void doDbMigration(Session& session)
|
||||
{
|
||||
static const std::string outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
|
||||
@@ -326,6 +336,7 @@ CREATE TABLE IF NOT EXISTS "track_backup" (
|
||||
{46, migrateFromV46},
|
||||
{47, migrateFromV47},
|
||||
{48, migrateFromV48},
|
||||
{49, migrateFromV49},
|
||||
};
|
||||
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Database
|
||||
class Session;
|
||||
|
||||
using Version = std::size_t;
|
||||
static constexpr Version LMS_DATABASE_VERSION{ 49 };
|
||||
static constexpr Version LMS_DATABASE_VERSION{ 50 };
|
||||
class VersionInfo
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -66,8 +66,8 @@ namespace Database
|
||||
|
||||
if (params.dateRange)
|
||||
{
|
||||
query.where("t.date >= ?").bind(params.dateRange->begin);
|
||||
query.where("t.date <= ?").bind(params.dateRange->end);
|
||||
query.where("COALESCE(CAST(SUBSTR(t.date, 1, 4) AS INTEGER), t.year) >= ?").bind(params.dateRange->begin);
|
||||
query.where("COALESCE(CAST(SUBSTR(t.date, 1, 4) AS INTEGER), t.year) <= ?").bind(params.dateRange->end);
|
||||
}
|
||||
|
||||
for (std::string_view keyword : params.keywords)
|
||||
@@ -170,13 +170,13 @@ namespace Database
|
||||
query.orderBy("t.file_last_write DESC");
|
||||
break;
|
||||
case ReleaseSortMethod::Date:
|
||||
query.orderBy("t.date, r.name COLLATE NOCASE");
|
||||
query.orderBy("COALESCE(t.date, CAST(t.year AS TEXT)), r.name COLLATE NOCASE");
|
||||
break;
|
||||
case ReleaseSortMethod::OriginalDate:
|
||||
query.orderBy("CASE WHEN t.original_date IS NULL THEN t.date ELSE t.original_date END, t.date, r.name COLLATE NOCASE");
|
||||
query.orderBy("COALESCE(original_date, CAST(original_year AS TEXT), date, CAST(year AS TEXT)), r.name COLLATE NOCASE");
|
||||
break;
|
||||
case ReleaseSortMethod::OriginalDateDesc:
|
||||
query.orderBy("CASE WHEN t.original_date IS NULL THEN t.date ELSE t.original_date END DESC, t.date, r.name COLLATE NOCASE");
|
||||
query.orderBy("COALESCE(original_date, CAST(original_year AS TEXT), date, CAST(year AS TEXT)) DESC, r.name COLLATE NOCASE");
|
||||
break;
|
||||
case ReleaseSortMethod::StarredDateDesc:
|
||||
assert(params.starringUser.isValid());
|
||||
@@ -359,17 +359,17 @@ namespace Database
|
||||
return discs;
|
||||
}
|
||||
|
||||
Wt::WDate Release::getReleaseDate() const
|
||||
Wt::WDate Release::getDate() const
|
||||
{
|
||||
return getReleaseDate(false);
|
||||
return getDate(false);
|
||||
}
|
||||
|
||||
Wt::WDate Release::getOriginalReleaseDate() const
|
||||
Wt::WDate Release::getOriginalDate() const
|
||||
{
|
||||
return getReleaseDate(true);
|
||||
return getDate(true);
|
||||
}
|
||||
|
||||
Wt::WDate Release::getReleaseDate(bool original) const
|
||||
Wt::WDate Release::getDate(bool original) const
|
||||
{
|
||||
assert(session());
|
||||
|
||||
@@ -388,6 +388,35 @@ namespace Database
|
||||
|
||||
return dates.front();
|
||||
}
|
||||
|
||||
std::optional<int> Release::getYear() const
|
||||
{
|
||||
return getYear(false);
|
||||
}
|
||||
|
||||
std::optional<int> Release::getOriginalYear() const
|
||||
{
|
||||
return getYear(true);
|
||||
}
|
||||
|
||||
std::optional<int> Release::getYear(bool original) const
|
||||
{
|
||||
assert(session());
|
||||
|
||||
const char* field{ original ? "original_year" : "year" };
|
||||
|
||||
auto years{ session()->query<std::optional<int>>(
|
||||
std::string {"SELECT "} + "t." + field + " FROM track t INNER JOIN release r ON r.id = t.release_id")
|
||||
.where("r.id = ?").bind(getId())
|
||||
.groupBy(field)
|
||||
.resultList() };
|
||||
|
||||
// various years => invalid years
|
||||
if (years.empty() || years.size() > 1)
|
||||
return std::nullopt;
|
||||
|
||||
return years.front();
|
||||
}
|
||||
|
||||
std::optional<std::string> Release::getCopyright() const
|
||||
{
|
||||
|
||||
@@ -158,7 +158,7 @@ namespace Database
|
||||
query.orderBy("t.name COLLATE NOCASE");
|
||||
break;
|
||||
case TrackSortMethod::DateDescAndRelease:
|
||||
query.orderBy("t.date DESC,t.release_id,t.disc_number,t.track_number");
|
||||
query.orderBy("COALESCE(t.date, CAST(t.year AS TEXT)) DESC,t.release_id,t.disc_number,t.track_number");
|
||||
break;
|
||||
case TrackSortMethod::Release:
|
||||
query.orderBy("t.disc_number,t.track_number");
|
||||
@@ -382,16 +382,6 @@ namespace Database
|
||||
_clusters.insert(getDboPtr(cluster));
|
||||
}
|
||||
|
||||
std::optional<int> Track::getYear() const
|
||||
{
|
||||
return (_date.isValid() ? std::make_optional<int>(_date.year()) : std::nullopt);
|
||||
}
|
||||
|
||||
std::optional<int> Track::getOriginalYear() const
|
||||
{
|
||||
return (_originalDate.isValid() ? std::make_optional<int>(_originalDate.year()) : std::nullopt);
|
||||
}
|
||||
|
||||
std::optional<std::string> Track::getCopyright() const
|
||||
{
|
||||
return _copyright != "" ? std::make_optional<std::string>(_copyright) : std::nullopt;
|
||||
|
||||
@@ -23,30 +23,29 @@
|
||||
|
||||
namespace Database
|
||||
{
|
||||
static const std::set<Bitrate> allowedAudioBitrates
|
||||
{
|
||||
64000,
|
||||
96000,
|
||||
128000,
|
||||
192000,
|
||||
320000,
|
||||
};
|
||||
static const std::set<Bitrate> allowedAudioBitrates
|
||||
{
|
||||
64000,
|
||||
96000,
|
||||
128000,
|
||||
192000,
|
||||
320000,
|
||||
};
|
||||
|
||||
void visitAllowedAudioBitrates(std::function<void(Bitrate)> func)
|
||||
{
|
||||
for (Bitrate bitrate : allowedAudioBitrates)
|
||||
func(bitrate);
|
||||
}
|
||||
void visitAllowedAudioBitrates(std::function<void(Bitrate)> func)
|
||||
{
|
||||
for (Bitrate bitrate : allowedAudioBitrates)
|
||||
func(bitrate);
|
||||
}
|
||||
|
||||
bool isAudioBitrateAllowed(Bitrate bitrate)
|
||||
{
|
||||
return allowedAudioBitrates.find(bitrate) != std::cend(allowedAudioBitrates);
|
||||
}
|
||||
bool isAudioBitrateAllowed(Bitrate bitrate)
|
||||
{
|
||||
return allowedAudioBitrates.find(bitrate) != std::cend(allowedAudioBitrates);
|
||||
}
|
||||
|
||||
DateRange
|
||||
DateRange::fromYearRange(int from, int to)
|
||||
{
|
||||
return DateRange {{from, 1, 1}, {to, 12, 31}};
|
||||
}
|
||||
DateRange DateRange::fromYearRange(int from, int to)
|
||||
{
|
||||
return DateRange{ from, to };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -132,8 +132,10 @@ namespace Database
|
||||
std::vector<std::vector<ObjectPtr<Cluster>>> getClusterGroups(const std::vector<ClusterTypeId>& clusterTypeIds, std::size_t size) const;
|
||||
|
||||
// Utility functions (if all tracks have the same values, which is legit to not be the case)
|
||||
Wt::WDate getReleaseDate() const;
|
||||
Wt::WDate getOriginalReleaseDate() const;
|
||||
Wt::WDate getDate() const;
|
||||
std::optional<int> getYear() const;
|
||||
Wt::WDate getOriginalDate() const;
|
||||
std::optional<int> getOriginalYear() const;
|
||||
std::optional<std::string> getCopyright() const;
|
||||
std::optional<std::string> getCopyrightURL() const;
|
||||
std::size_t getMeanBitrate() const;
|
||||
@@ -181,7 +183,8 @@ namespace Database
|
||||
Release(const std::string& name, const std::optional<UUID>& MBID = {});
|
||||
static pointer create(Session& session, const std::string& name, const std::optional<UUID>& MBID = {});
|
||||
|
||||
Wt::WDate getReleaseDate(bool original) const;
|
||||
Wt::WDate getDate(bool original) const;
|
||||
std::optional<int> getYear(bool original) const;
|
||||
|
||||
static constexpr std::size_t _maxNameLength{ 128 };
|
||||
|
||||
|
||||
@@ -132,7 +132,9 @@ namespace Database {
|
||||
void setLastWriteTime(Wt::WDateTime time) { _fileLastWrite = time; }
|
||||
void setAddedTime(Wt::WDateTime time) { _fileAdded = time; }
|
||||
void setDate(const Wt::WDate& date) { _date = date; }
|
||||
void setYear(std::optional<int> year) { _year = year; }
|
||||
void setOriginalDate(const Wt::WDate& date) { _originalDate = date; }
|
||||
void setOriginalYear(std::optional<int> year) { _originalYear = year; }
|
||||
void setHasCover(bool hasCover) { _hasCover = hasCover; }
|
||||
void setTrackMBID(const std::optional<UUID>& MBID) { _trackMBID = MBID ? MBID->getAsString() : ""; }
|
||||
void setRecordingMBID(const std::optional<UUID>& MBID) { _recordingMBID = MBID ? MBID->getAsString() : ""; }
|
||||
@@ -156,8 +158,10 @@ namespace Database {
|
||||
std::chrono::milliseconds getDuration() const { return _duration; }
|
||||
std::size_t getBitrate() const { return _bitrate; }
|
||||
const Wt::WDateTime& getLastWritten() const { return _fileLastWrite; }
|
||||
std::optional<int> getYear() const;
|
||||
std::optional<int> getOriginalYear() const;
|
||||
const Wt::WDate& getDate() const { return _date; }
|
||||
std::optional<int> getYear() const { return _year; }
|
||||
const Wt::WDate& getOriginalDate() const { return _originalDate; }
|
||||
std::optional<int> getOriginalYear() const { return _originalYear; };
|
||||
Wt::WDateTime getLastWriteTime() const { return _fileLastWrite; }
|
||||
Wt::WDateTime getAddedTime() const { return _fileAdded; }
|
||||
bool hasCover() const { return _hasCover; }
|
||||
@@ -190,7 +194,9 @@ namespace Database {
|
||||
Wt::Dbo::field(a, _duration, "duration");
|
||||
Wt::Dbo::field(a, _bitrate, "bitrate");
|
||||
Wt::Dbo::field(a, _date, "date");
|
||||
Wt::Dbo::field(a, _year, "year");
|
||||
Wt::Dbo::field(a, _originalDate, "original_date");
|
||||
Wt::Dbo::field(a, _originalYear, "original_year");
|
||||
Wt::Dbo::field(a, _filePath, "file_path");
|
||||
Wt::Dbo::field(a, _fileLastWrite, "file_last_write");
|
||||
Wt::Dbo::field(a, _fileAdded, "file_added");
|
||||
@@ -225,7 +231,9 @@ namespace Database {
|
||||
std::chrono::duration<int, std::milli> _duration{};
|
||||
int _bitrate; // in bps
|
||||
Wt::WDate _date;
|
||||
std::optional<int> _year;
|
||||
Wt::WDate _originalDate;
|
||||
std::optional<int> _originalYear;
|
||||
std::string _filePath;
|
||||
Wt::WDateTime _fileLastWrite;
|
||||
Wt::WDateTime _fileAdded;
|
||||
|
||||
@@ -93,8 +93,8 @@ namespace Database
|
||||
|
||||
struct DateRange
|
||||
{
|
||||
Wt::WDate begin;
|
||||
Wt::WDate end;
|
||||
int begin;
|
||||
int end;
|
||||
|
||||
static DateRange fromYearRange(int from, int to);
|
||||
};
|
||||
|
||||
@@ -402,8 +402,61 @@ TEST_F(DatabaseFixture, MultiTracksSingleReleaseDate)
|
||||
track1A.get().modify()->setOriginalDate(release1OriginalDate);
|
||||
track1B.get().modify()->setOriginalDate(release1OriginalDate);
|
||||
|
||||
EXPECT_EQ(release1.get()->getReleaseDate(), release1Date);
|
||||
EXPECT_EQ(release1.get()->getOriginalReleaseDate(), release1OriginalDate);
|
||||
EXPECT_EQ(release1.get()->getDate(), release1Date);
|
||||
EXPECT_EQ(release1.get()->getOriginalDate(), release1OriginalDate);
|
||||
}
|
||||
|
||||
{
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
auto releases{ Release::findIds(session, Release::FindParameters {}.setDateRange(DateRange::fromYearRange(1950, 2000))) };
|
||||
ASSERT_EQ(releases.results.size(), 1);
|
||||
EXPECT_EQ(releases.results.front(), release1.getId());
|
||||
|
||||
releases = Release::findIds(session, Release::FindParameters{}.setDateRange(DateRange::fromYearRange(1994, 1994)));
|
||||
ASSERT_EQ(releases.results.size(), 1);
|
||||
EXPECT_EQ(releases.results.front(), release1.getId());
|
||||
|
||||
releases = Release::findIds(session, Release::FindParameters{}.setDateRange(DateRange::fromYearRange(1993, 1993)));
|
||||
ASSERT_EQ(releases.results.size(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DatabaseFixture, MultiTracksSingleReleaseYear)
|
||||
{
|
||||
ScopedRelease release1{ session, "MyRelease1" };
|
||||
ScopedRelease release2{ session, "MyRelease2" };
|
||||
const int release1Year{ 1994 };
|
||||
const int release1OriginalYear{ 1993 };
|
||||
|
||||
ScopedTrack track1A{ session, "MyTrack1A" };
|
||||
ScopedTrack track1B{ session, "MyTrack1B" };
|
||||
ScopedTrack track2A{ session, "MyTrack2A" };
|
||||
ScopedTrack track2B{ session, "MyTrack2B" };
|
||||
|
||||
{
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
|
||||
const auto releases{ Release::findIds(session, Release::FindParameters {}.setDateRange(DateRange::fromYearRange(0, 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()->setYear(release1Year);
|
||||
track1B.get().modify()->setYear(release1Year);
|
||||
track1A.get().modify()->setOriginalYear(release1OriginalYear);
|
||||
track1B.get().modify()->setOriginalYear(release1OriginalYear);
|
||||
|
||||
EXPECT_EQ(release1.get()->getYear(), release1Year);
|
||||
EXPECT_EQ(release1.get()->getOriginalYear(), release1OriginalYear);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -126,7 +126,8 @@ TEST_F(DatabaseFixture, MultipleTracksSearchByFilter)
|
||||
TEST_F(DatabaseFixture, Track_date)
|
||||
{
|
||||
ScopedTrack track{ session, "MyTrack" };
|
||||
|
||||
const Wt::WDate date{ 1995, 5, 5 };
|
||||
const Wt::WDate originalDate{ 1994, 2, 2 };
|
||||
{
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
EXPECT_EQ(track->getYear(), std::nullopt);
|
||||
@@ -135,14 +136,28 @@ TEST_F(DatabaseFixture, Track_date)
|
||||
|
||||
{
|
||||
auto transaction{ session.createWriteTransaction() };
|
||||
track.get().modify()->setDate(Wt::WDate{ 1995, 5, 5 });
|
||||
track.get().modify()->setOriginalDate(Wt::WDate{ 1994, 2, 2 });
|
||||
track.get().modify()->setDate(date);
|
||||
track.get().modify()->setOriginalDate(originalDate);
|
||||
}
|
||||
|
||||
{
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
EXPECT_EQ(track->getYear(), 1995);
|
||||
EXPECT_EQ(track->getOriginalYear(), 1994);
|
||||
EXPECT_EQ(track->getYear(), std::nullopt);
|
||||
EXPECT_EQ(track->getOriginalYear(), std::nullopt);
|
||||
EXPECT_EQ(track->getDate(), date);
|
||||
EXPECT_EQ(track->getOriginalDate(), originalDate);
|
||||
}
|
||||
|
||||
{
|
||||
auto transaction{ session.createWriteTransaction() };
|
||||
track.get().modify()->setYear(date.year());
|
||||
track.get().modify()->setOriginalYear(originalDate.year());
|
||||
}
|
||||
|
||||
{
|
||||
auto transaction{ session.createReadTransaction() };
|
||||
EXPECT_EQ(track->getYear(), date.year());
|
||||
EXPECT_EQ(track->getOriginalYear(), originalDate.year());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user