Sort by release date, not just release year, fixes #128
This commit is contained in:
@@ -419,7 +419,7 @@ Artist::getReleases(const std::vector<ClusterId>& clusterIds) const
|
|||||||
if (!clusterIds.empty())
|
if (!clusterIds.empty())
|
||||||
oss << " GROUP BY t.id HAVING COUNT(DISTINCT c.id) = " << clusterIds.size();
|
oss << " GROUP BY t.id HAVING COUNT(DISTINCT c.id) = " << clusterIds.size();
|
||||||
|
|
||||||
oss << " ORDER BY t.year DESC, r.name COLLATE NOCASE";
|
oss << " ORDER BY t.date DESC, r.name COLLATE NOCASE";
|
||||||
|
|
||||||
auto query {session()->query<Wt::Dbo::ptr<Release>>(oss.str())};
|
auto query {session()->query<Wt::Dbo::ptr<Release>>(oss.str())};
|
||||||
|
|
||||||
@@ -447,7 +447,7 @@ Artist::getTracks(std::optional<TrackArtistLinkType> linkType) const
|
|||||||
|
|
||||||
auto query {session()->query<Wt::Dbo::ptr<Track>>("SELECT DISTINCT t FROM track t INNER JOIN artist a ON a.id = t_a_l.artist_id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id")
|
auto query {session()->query<Wt::Dbo::ptr<Track>>("SELECT DISTINCT t FROM track t INNER JOIN artist a ON a.id = t_a_l.artist_id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id")
|
||||||
.where("a.id = ?").bind(getId())
|
.where("a.id = ?").bind(getId())
|
||||||
.orderBy("t.year DESC,t.release_id,t.disc_number,t.track_number")};
|
.orderBy("t.date DESC,t.release_id,t.disc_number,t.track_number")};
|
||||||
|
|
||||||
if (linkType)
|
if (linkType)
|
||||||
query.where("t_a_l.type = ?").bind(*linkType);
|
query.where("t_a_l.type = ?").bind(*linkType);
|
||||||
|
|||||||
@@ -249,9 +249,9 @@ Release::getByYear(Session& session, int yearFrom, int yearTo, std::optional<Ran
|
|||||||
{
|
{
|
||||||
auto res {session.getDboSession().query<Wt::Dbo::ptr<Release>>
|
auto res {session.getDboSession().query<Wt::Dbo::ptr<Release>>
|
||||||
("SELECT DISTINCT r from release r INNER JOIN track t ON r.id = t.release_id")
|
("SELECT DISTINCT r from release r INNER JOIN track t ON r.id = t.release_id")
|
||||||
.where("t.year >= ?").bind(yearFrom)
|
.where("t.date >= ?").bind(Wt::WDate {yearFrom, 1, 1})
|
||||||
.where("t.year <= ?").bind(yearTo)
|
.where("t.date <= ?").bind(Wt::WDate {yearTo, 12, 31})
|
||||||
.orderBy("t.year, r.name COLLATE NOCASE")
|
.orderBy("t.date, r.name COLLATE NOCASE")
|
||||||
.offset(range ? static_cast<int>(range->offset) : -1)
|
.offset(range ? static_cast<int>(range->offset) : -1)
|
||||||
.limit(range ? static_cast<int>(range->limit) : -1)
|
.limit(range ? static_cast<int>(range->limit) : -1)
|
||||||
.resultList()};
|
.resultList()};
|
||||||
@@ -383,19 +383,20 @@ Release::getReleaseYear(bool original) const
|
|||||||
{
|
{
|
||||||
assert(session());
|
assert(session());
|
||||||
|
|
||||||
const char* field {original ? "original_year" : "year"};
|
const char* field {original ? "original_date" : "date"};
|
||||||
|
|
||||||
Wt::Dbo::collection<int> dates = session()->query<int>(
|
auto dates {session()->query<Wt::WDate>(
|
||||||
std::string {"SELECT "} + "t." + field + " FROM track t INNER JOIN release r ON r.id = t.release_id")
|
std::string {"SELECT "} + "t." + field + " FROM track t INNER JOIN release r ON r.id = t.release_id")
|
||||||
.where("r.id = ?")
|
.where("r.id = ?")
|
||||||
.groupBy(field)
|
.groupBy(field)
|
||||||
.bind(getId());
|
.bind(getId())
|
||||||
|
.resultList()};
|
||||||
|
|
||||||
// various dates => no date
|
// various dates => no date
|
||||||
if (dates.empty() || dates.size() > 1)
|
if (dates.empty() || dates.size() > 1)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
auto date {dates.front()};
|
auto date {dates.front().year()};
|
||||||
|
|
||||||
if (date > 0)
|
if (date > 0)
|
||||||
return date;
|
return date;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ namespace Database
|
|||||||
{
|
{
|
||||||
|
|
||||||
using Version = std::size_t;
|
using Version = std::size_t;
|
||||||
static constexpr Version LMS_DATABASE_VERSION {30};
|
static constexpr Version LMS_DATABASE_VERSION {31};
|
||||||
|
|
||||||
class VersionInfo
|
class VersionInfo
|
||||||
{
|
{
|
||||||
@@ -329,6 +329,37 @@ CREATE TABLE "user_backup" (
|
|||||||
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
|
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
|
||||||
ScanSettings::get(*this).modify()->incScanVersion();
|
ScanSettings::get(*this).modify()->incScanVersion();
|
||||||
}
|
}
|
||||||
|
else if (version == 30)
|
||||||
|
{
|
||||||
|
// drop "year" and "original_year" (rescan needed to convert them into dates)
|
||||||
|
_session.execute(R"(
|
||||||
|
CREATE TABLE "track_backup" (
|
||||||
|
"id" integer primary key autoincrement,
|
||||||
|
"version" integer not null,
|
||||||
|
"scan_version" integer not null,
|
||||||
|
"track_number" integer not null,
|
||||||
|
"disc_number" integer not null,
|
||||||
|
"name" text not null,
|
||||||
|
"duration" integer,
|
||||||
|
"date" integer text,
|
||||||
|
"original_date" integer text,
|
||||||
|
"file_path" text not null,
|
||||||
|
"file_last_write" text,
|
||||||
|
"file_added" text,
|
||||||
|
"has_cover" boolean not null,
|
||||||
|
"mbid" text not null,
|
||||||
|
"copyright" text not null,
|
||||||
|
"copyright_url" text not null,
|
||||||
|
"release_id" bigint, total_disc INTEGER NOT NULL DEFAULT(0), total_track INTEGER NOT NULL DEFAULT(0), track_replay_gain REAL, release_replay_gain REAL, disc_subtitle TEXT NOT NULL DEFAULT '', recording_mbid TEXT,
|
||||||
|
constraint "fk_track_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred
|
||||||
|
))");
|
||||||
|
_session.execute("INSERT INTO track_backup SELECT id, version, scan_version, track_number, disc_number, name, duration, \"1900-01-01\", \"1900-01-01\", file_path, file_last_write, file_added, has_cover, mbid, copyright, copyright_url, release_id, total_disc, total_track, track_replay_gain, release_replay_gain, disc_subtitle, recording_mbid FROM track");
|
||||||
|
_session.execute("DROP TABLE track");
|
||||||
|
_session.execute("ALTER TABLE track_backup RENAME TO track");
|
||||||
|
|
||||||
|
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
|
||||||
|
ScanSettings::get(*this).modify()->incScanVersion();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration";
|
LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration";
|
||||||
@@ -442,8 +473,8 @@ Session::prepareTables()
|
|||||||
_session.execute("CREATE INDEX IF NOT EXISTS track_mbid_idx ON track(mbid)");
|
_session.execute("CREATE INDEX IF NOT EXISTS track_mbid_idx ON track(mbid)");
|
||||||
_session.execute("CREATE INDEX IF NOT EXISTS track_recording_mbid_idx ON track(recording_mbid)");
|
_session.execute("CREATE INDEX IF NOT EXISTS track_recording_mbid_idx ON track(recording_mbid)");
|
||||||
_session.execute("CREATE INDEX IF NOT EXISTS track_release_idx ON track(release_id)");
|
_session.execute("CREATE INDEX IF NOT EXISTS track_release_idx ON track(release_id)");
|
||||||
_session.execute("CREATE INDEX IF NOT EXISTS track_year_idx ON track(year)");
|
_session.execute("CREATE INDEX IF NOT EXISTS track_date_idx ON track(date)");
|
||||||
_session.execute("CREATE INDEX IF NOT EXISTS track_original_year_idx ON track(original_year)");
|
_session.execute("CREATE INDEX IF NOT EXISTS track_original_date_idx ON track(original_date)");
|
||||||
_session.execute("CREATE INDEX IF NOT EXISTS tracklist_name_idx ON tracklist(name)");
|
_session.execute("CREATE INDEX IF NOT EXISTS tracklist_name_idx ON tracklist(name)");
|
||||||
_session.execute("CREATE INDEX IF NOT EXISTS tracklist_user_idx ON tracklist(user_id)");
|
_session.execute("CREATE INDEX IF NOT EXISTS tracklist_user_idx ON tracklist(user_id)");
|
||||||
_session.execute("CREATE INDEX IF NOT EXISTS track_features_track_idx ON track_features(track_id)");
|
_session.execute("CREATE INDEX IF NOT EXISTS track_features_track_idx ON track_features(track_id)");
|
||||||
|
|||||||
@@ -483,13 +483,13 @@ Track::getTotalDisc() const
|
|||||||
std::optional<int>
|
std::optional<int>
|
||||||
Track::getYear() const
|
Track::getYear() const
|
||||||
{
|
{
|
||||||
return (_year > 0) ? std::make_optional<int>(_year) : std::nullopt;
|
return (_date.isValid() ? std::make_optional<int>(_date.year()) : std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<int>
|
std::optional<int>
|
||||||
Track::getOriginalYear() const
|
Track::getOriginalYear() const
|
||||||
{
|
{
|
||||||
return (_originalYear > 0) ? std::make_optional<int>(_originalYear) : std::nullopt;
|
return (_originalDate.isValid() ? std::make_optional<int>(_originalDate.year()) : std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string>
|
std::optional<std::string>
|
||||||
|
|||||||
@@ -81,9 +81,9 @@ class Release : public Object<Release, ReleaseId>
|
|||||||
static pointer create(Session& session, const std::string& name, const std::optional<UUID>& MBID = {});
|
static pointer create(Session& session, const std::string& name, const std::optional<UUID>& MBID = {});
|
||||||
|
|
||||||
// Utility functions
|
// Utility functions
|
||||||
std::optional<int> getReleaseYear(bool originalDate = false) const; // 0 if unknown or various
|
std::optional<int> getReleaseYear(bool originalDate = false) const;
|
||||||
std::optional<std::string> getCopyright() const;
|
std::optional<std::string> getCopyright() const;
|
||||||
std::optional<std::string> getCopyrightURL() const;
|
std::optional<std::string> getCopyrightURL() const;
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
const std::string& getName() const { return _name; }
|
const std::string& getName() const { return _name; }
|
||||||
|
|||||||
@@ -102,8 +102,8 @@ class Track : public Object<Track, TrackId>
|
|||||||
void setDuration(std::chrono::milliseconds duration) { _duration = duration; }
|
void setDuration(std::chrono::milliseconds duration) { _duration = duration; }
|
||||||
void setLastWriteTime(Wt::WDateTime time) { _fileLastWrite = time; }
|
void setLastWriteTime(Wt::WDateTime time) { _fileLastWrite = time; }
|
||||||
void setAddedTime(Wt::WDateTime time) { _fileAdded = time; }
|
void setAddedTime(Wt::WDateTime time) { _fileAdded = time; }
|
||||||
void setYear(int year) { _year = year; }
|
void setDate(const Wt::WDate& date) { _date = date; }
|
||||||
void setOriginalYear(int year) { _originalYear = year; }
|
void setOriginalDate(const Wt::WDate& date) { _originalDate = date; }
|
||||||
void setHasCover(bool hasCover) { _hasCover = hasCover; }
|
void setHasCover(bool hasCover) { _hasCover = hasCover; }
|
||||||
void setTrackMBID(const std::optional<UUID>& MBID) { _trackMBID = MBID ? MBID->getAsString() : ""; }
|
void setTrackMBID(const std::optional<UUID>& MBID) { _trackMBID = MBID ? MBID->getAsString() : ""; }
|
||||||
void setRecordingMBID(const std::optional<UUID>& MBID) { _recordingMBID = MBID ? MBID->getAsString() : ""; }
|
void setRecordingMBID(const std::optional<UUID>& MBID) { _recordingMBID = MBID ? MBID->getAsString() : ""; }
|
||||||
@@ -118,26 +118,26 @@ class Track : public Object<Track, TrackId>
|
|||||||
void setFeatures(const ObjectPtr<TrackFeatures>& features);
|
void setFeatures(const ObjectPtr<TrackFeatures>& features);
|
||||||
|
|
||||||
std::size_t getScanVersion() const { return _scanVersion; }
|
std::size_t getScanVersion() const { return _scanVersion; }
|
||||||
std::optional<std::size_t> getTrackNumber() const;
|
std::optional<std::size_t> getTrackNumber() const;
|
||||||
std::optional<std::size_t> getTotalTrack() const;
|
std::optional<std::size_t> getTotalTrack() const;
|
||||||
std::optional<std::size_t> getDiscNumber() const;
|
std::optional<std::size_t> getDiscNumber() const;
|
||||||
const std::string& getDiscSubtitle() const { return _discSubtitle; }
|
const std::string& getDiscSubtitle() const { return _discSubtitle; }
|
||||||
std::optional<std::size_t> getTotalDisc() const;
|
std::optional<std::size_t> getTotalDisc() const;
|
||||||
std::string getName() const { return _name; }
|
std::string getName() const { return _name; }
|
||||||
std::filesystem::path getPath() const { return _filePath; }
|
std::filesystem::path getPath() const { return _filePath; }
|
||||||
std::chrono::milliseconds getDuration() const { return _duration; }
|
std::chrono::milliseconds getDuration() const { return _duration; }
|
||||||
const Wt::WDateTime& getLastWritten() const { return _fileLastWrite; }
|
const Wt::WDateTime& getLastWritten() const { return _fileLastWrite; }
|
||||||
std::optional<int> getYear() const;
|
std::optional<int> getYear() const;
|
||||||
std::optional<int> getOriginalYear() const;
|
std::optional<int> getOriginalYear() const;
|
||||||
Wt::WDateTime getLastWriteTime() const { return _fileLastWrite; }
|
Wt::WDateTime getLastWriteTime() const { return _fileLastWrite; }
|
||||||
Wt::WDateTime getAddedTime() const { return _fileAdded; }
|
Wt::WDateTime getAddedTime() const { return _fileAdded; }
|
||||||
bool hasCover() const { return _hasCover; }
|
bool hasCover() const { return _hasCover; }
|
||||||
std::optional<UUID> getTrackMBID() const { return UUID::fromString(_trackMBID); }
|
std::optional<UUID> getTrackMBID() const { return UUID::fromString(_trackMBID); }
|
||||||
std::optional<UUID> getRecordingMBID() const { return UUID::fromString(_recordingMBID); }
|
std::optional<UUID> getRecordingMBID() const { return UUID::fromString(_recordingMBID); }
|
||||||
std::optional<std::string> getCopyright() const;
|
std::optional<std::string> getCopyright() const;
|
||||||
std::optional<std::string> getCopyrightURL() const;
|
std::optional<std::string> getCopyrightURL() const;
|
||||||
std::optional<float> getTrackReplayGain() const { return _trackReplayGain; }
|
std::optional<float> getTrackReplayGain() const { return _trackReplayGain; }
|
||||||
std::optional<float> getReleaseReplayGain() const { return _releaseReplayGain; }
|
std::optional<float> getReleaseReplayGain() const { return _releaseReplayGain; }
|
||||||
|
|
||||||
// no artistLinkTypes means get all
|
// no artistLinkTypes means get all
|
||||||
std::vector<ObjectPtr<Artist>> getArtists(EnumSet<TrackArtistLinkType> artistLinkTypes) const;
|
std::vector<ObjectPtr<Artist>> getArtists(EnumSet<TrackArtistLinkType> artistLinkTypes) const;
|
||||||
@@ -160,10 +160,10 @@ class Track : public Object<Track, TrackId>
|
|||||||
Wt::Dbo::field(a, _discSubtitle, "disc_subtitle");
|
Wt::Dbo::field(a, _discSubtitle, "disc_subtitle");
|
||||||
Wt::Dbo::field(a, _totalTrack, "total_track");
|
Wt::Dbo::field(a, _totalTrack, "total_track");
|
||||||
Wt::Dbo::field(a, _totalDisc, "total_disc");
|
Wt::Dbo::field(a, _totalDisc, "total_disc");
|
||||||
Wt::Dbo::field(a, _name, "name");
|
Wt::Dbo::field(a, _name, "name");
|
||||||
Wt::Dbo::field(a, _duration, "duration");
|
Wt::Dbo::field(a, _duration, "duration");
|
||||||
Wt::Dbo::field(a, _year, "year");
|
Wt::Dbo::field(a, _date, "date");
|
||||||
Wt::Dbo::field(a, _originalYear, "original_year");
|
Wt::Dbo::field(a, _originalDate, "original_date");
|
||||||
Wt::Dbo::field(a, _filePath, "file_path");
|
Wt::Dbo::field(a, _filePath, "file_path");
|
||||||
Wt::Dbo::field(a, _fileLastWrite, "file_last_write");
|
Wt::Dbo::field(a, _fileLastWrite, "file_last_write");
|
||||||
Wt::Dbo::field(a, _fileAdded, "file_added");
|
Wt::Dbo::field(a, _fileAdded, "file_added");
|
||||||
@@ -198,8 +198,8 @@ class Track : public Object<Track, TrackId>
|
|||||||
std::string _artistName;
|
std::string _artistName;
|
||||||
std::string _releaseName;
|
std::string _releaseName;
|
||||||
std::chrono::duration<int, std::milli> _duration {};
|
std::chrono::duration<int, std::milli> _duration {};
|
||||||
int _year {};
|
Wt::WDate _date;
|
||||||
int _originalYear {};
|
Wt::WDate _originalDate;
|
||||||
std::string _filePath;
|
std::string _filePath;
|
||||||
Wt::WDateTime _fileLastWrite;
|
Wt::WDateTime _fileLastWrite;
|
||||||
Wt::WDateTime _fileAdded;
|
Wt::WDateTime _fileAdded;
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
|
|
||||||
|
add_subdirectory(test)
|
||||||
|
|
||||||
add_library(lmsmetadata SHARED
|
add_library(lmsmetadata SHARED
|
||||||
impl/AvFormatParser.cpp
|
impl/AvFormatParser.cpp
|
||||||
impl/TagLibParser.cpp
|
impl/TagLibParser.cpp
|
||||||
|
impl/Utils.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(lmsmetadata INTERFACE
|
target_include_directories(lmsmetadata INTERFACE
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
#include "av/IAudioFile.hpp"
|
#include "av/IAudioFile.hpp"
|
||||||
#include "utils/Logger.hpp"
|
#include "utils/Logger.hpp"
|
||||||
#include "utils/String.hpp"
|
#include "utils/String.hpp"
|
||||||
|
#include "Utils.hpp"
|
||||||
|
|
||||||
namespace MetaData
|
namespace MetaData
|
||||||
{
|
{
|
||||||
@@ -186,12 +186,12 @@ AvFormatParser::parse(const std::filesystem::path& p, bool debug)
|
|||||||
|| tag == "YEAR"
|
|| tag == "YEAR"
|
||||||
|| tag == "WM/Year")
|
|| tag == "WM/Year")
|
||||||
{
|
{
|
||||||
track.year = StringUtils::readAs<int>(value);
|
track.date = Utils::parseDate(value);
|
||||||
}
|
}
|
||||||
else if (tag == "TDOR" // Original release time (ID3v2 2.4)
|
else if (tag == "TDOR" // Original release time (ID3v2 2.4)
|
||||||
|| tag == "TORY") // Original release year
|
|| tag == "TORY") // Original release year
|
||||||
{
|
{
|
||||||
track.originalYear = StringUtils::readAs<int>(value);
|
track.originalDate = Utils::parseDate(value);
|
||||||
}
|
}
|
||||||
else if (tag == "ACOUSTID ID")
|
else if (tag == "ACOUSTID ID")
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
#include "utils/Logger.hpp"
|
#include "utils/Logger.hpp"
|
||||||
#include "utils/String.hpp"
|
#include "utils/String.hpp"
|
||||||
|
#include "Utils.hpp"
|
||||||
|
|
||||||
namespace MetaData
|
namespace MetaData
|
||||||
{
|
{
|
||||||
@@ -214,18 +214,26 @@ TagLibParser::processTag(Track& track, const std::string& tag, const TagLib::Str
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (tag == "DATE")
|
else if (tag == "DATE")
|
||||||
track.year = StringUtils::readAs<int>(value);
|
|
||||||
else if (tag == "ORIGINALDATE" && !track.originalYear)
|
|
||||||
{
|
{
|
||||||
// Lower priority than ORIGINALYEAR
|
// Higher priority than YEAR
|
||||||
track.originalYear = StringUtils::readAs<int>(value);
|
if (const Wt::WDate date {Utils::parseDate(value)}; date.isValid())
|
||||||
|
track.date = date;
|
||||||
}
|
}
|
||||||
else if (tag == "ORIGINALYEAR")
|
else if (tag == "YEAR" && !track.date.isValid())
|
||||||
{
|
{
|
||||||
// Higher priority than ORIGINALDATE
|
// lower priority than DATE
|
||||||
auto originalYear = StringUtils::readAs<int>(value);
|
track.date = Utils::parseDate(value);
|
||||||
if (originalYear)
|
}
|
||||||
track.originalYear = originalYear;
|
else if (tag == "ORIGINALDATE")
|
||||||
|
{
|
||||||
|
// Higher priority than ORIGINALYEAR
|
||||||
|
if (const Wt::WDate date {Utils::parseDate(value)}; date.isValid())
|
||||||
|
track.originalDate = date;
|
||||||
|
}
|
||||||
|
else if (tag == "ORIGINALYEAR" && !track.originalDate.isValid())
|
||||||
|
{
|
||||||
|
// Lower priority than ORIGINALDATE
|
||||||
|
track.originalDate = Utils::parseDate(value);
|
||||||
}
|
}
|
||||||
else if (tag == "METADATA_BLOCK_PICTURE")
|
else if (tag == "METADATA_BLOCK_PICTURE")
|
||||||
track.hasCover = true;
|
track.hasCover = true;
|
||||||
@@ -260,7 +268,7 @@ TagLibParser::parse(const std::filesystem::path& p, bool debug)
|
|||||||
{
|
{
|
||||||
TagLib::FileRef f {p.string().c_str(),
|
TagLib::FileRef f {p.string().c_str(),
|
||||||
true, // read audio properties
|
true, // read audio properties
|
||||||
TagLib::AudioProperties::Fast};
|
TagLib::AudioProperties::Fast}; // TODO parametrize this
|
||||||
|
|
||||||
if (f.isNull())
|
if (f.isNull())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Utils.hpp"
|
||||||
|
#include <ctime>
|
||||||
|
#include <string_view>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace MetaData::Utils
|
||||||
|
{
|
||||||
|
Wt::WDate
|
||||||
|
parseDate(const std::string& dateStr)
|
||||||
|
{
|
||||||
|
static constexpr const char* formats[]
|
||||||
|
{
|
||||||
|
"%Y-%m-%d",
|
||||||
|
"%Y/%m/%d",
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const char* format : formats)
|
||||||
|
{
|
||||||
|
std::tm tm = {};
|
||||||
|
std::stringstream ss {dateStr};
|
||||||
|
ss >> std::get_time(&tm, format);
|
||||||
|
if (ss.fail())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const Wt::WDate res
|
||||||
|
{
|
||||||
|
tm.tm_year + 1900, // years since 1900
|
||||||
|
tm.tm_mon + 1, // months since January – [0, 11]
|
||||||
|
tm.tm_mday ? tm.tm_mday : 1 // day of the month – [1, 31]
|
||||||
|
};
|
||||||
|
if (!res.isValid())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Wt/WDate.h>
|
||||||
|
|
||||||
|
namespace MetaData::Utils
|
||||||
|
{
|
||||||
|
Wt::WDate parseDate(const std::string& dateStr);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <Wt/WDate.h>
|
||||||
#include "utils/UUID.hpp"
|
#include "utils/UUID.hpp"
|
||||||
|
|
||||||
namespace MetaData
|
namespace MetaData
|
||||||
@@ -56,21 +57,21 @@ namespace MetaData
|
|||||||
|
|
||||||
struct Track
|
struct Track
|
||||||
{
|
{
|
||||||
std::vector<Artist> artists;
|
std::vector<Artist> artists;
|
||||||
std::vector<Artist> albumArtists;
|
std::vector<Artist> albumArtists;
|
||||||
std::string title;
|
std::string title;
|
||||||
std::optional<UUID> trackMBID;
|
std::optional<UUID> trackMBID;
|
||||||
std::optional<UUID> recordingMBID;
|
std::optional<UUID> recordingMBID;
|
||||||
std::optional<Album> album;
|
std::optional<Album> album;
|
||||||
Clusters clusters;
|
Clusters clusters;
|
||||||
std::chrono::milliseconds duration;
|
std::chrono::milliseconds duration;
|
||||||
std::optional<std::size_t> trackNumber;
|
std::optional<std::size_t> trackNumber;
|
||||||
std::optional<std::size_t> totalTrack;
|
std::optional<std::size_t> totalTrack;
|
||||||
std::optional<std::size_t> discNumber;
|
std::optional<std::size_t> discNumber;
|
||||||
std::optional<std::size_t> totalDisc;
|
std::optional<std::size_t> totalDisc;
|
||||||
std::optional<int> year;
|
Wt::WDate date;
|
||||||
std::optional<int> originalYear;
|
Wt::WDate originalDate;
|
||||||
bool hasCover {};
|
bool hasCover {};
|
||||||
std::vector<AudioStream> audioStreams;
|
std::vector<AudioStream> audioStreams;
|
||||||
std::optional<UUID> acoustID;
|
std::optional<UUID> acoustID;
|
||||||
std::string copyright;
|
std::string copyright;
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
include(GoogleTest)
|
||||||
|
|
||||||
|
add_executable(test-metadata
|
||||||
|
Metadata.cpp
|
||||||
|
Utils.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(test-metadata PRIVATE
|
||||||
|
../impl
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(test-metadata PRIVATE
|
||||||
|
lmsmetadata
|
||||||
|
GTest::GTest
|
||||||
|
)
|
||||||
|
|
||||||
|
gtest_discover_tests(test-metadata)
|
||||||
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "Utils.hpp"
|
||||||
|
|
||||||
|
TEST(MetaData, parseDate)
|
||||||
|
{
|
||||||
|
using namespace MetaData::Utils;
|
||||||
|
|
||||||
|
struct TestCase
|
||||||
|
{
|
||||||
|
std::string str;
|
||||||
|
Wt::WDate result;
|
||||||
|
} testCases []
|
||||||
|
{
|
||||||
|
{ "1995-05-09", Wt::WDate {1995, 5, 9} },
|
||||||
|
{ "1995-01-01", Wt::WDate {1995, 1, 1} },
|
||||||
|
{ "1900-01-01", Wt::WDate {1900, 1, 1} },
|
||||||
|
{ "1899-01-01", Wt::WDate {1899, 1, 1} },
|
||||||
|
{ "1899-12-31", Wt::WDate {1899, 12, 31} },
|
||||||
|
{ "1899-11-30", Wt::WDate {1899, 11, 30} },
|
||||||
|
{ "1500-11-30", Wt::WDate {1500, 11, 30} },
|
||||||
|
{ "1000-11-30", Wt::WDate {1000, 11, 30} },
|
||||||
|
{ "1899-11-31", Wt::WDate {} }, // invalid day
|
||||||
|
{ "1899-13-01", Wt::WDate {} }, // invalid month
|
||||||
|
{ "1899-11", Wt::WDate {1899, 11, 1} }, // missing day
|
||||||
|
{ "1899", Wt::WDate {1899, 1, 1} }, // missing month and days
|
||||||
|
{ "1600", Wt::WDate {1600, 1, 1} }, // missing month and days
|
||||||
|
{ "1995/05/09", Wt::WDate {1995, 5, 9} },
|
||||||
|
{ "1995/01/01", Wt::WDate {1995, 1, 1} },
|
||||||
|
{ "1900/01/01", Wt::WDate {1900, 1, 1} },
|
||||||
|
{ "1899/01/01", Wt::WDate {1899, 1, 1} },
|
||||||
|
{ "1899/12/31", Wt::WDate {1899, 12, 31} },
|
||||||
|
{ "1899/11/30", Wt::WDate {1899, 11, 30} },
|
||||||
|
{ "1500/11/30", Wt::WDate {1500, 11, 30} },
|
||||||
|
{ "1000/11/30", Wt::WDate {1000, 11, 30} },
|
||||||
|
{ "1899/11/31", Wt::WDate {} }, // invalid day
|
||||||
|
{ "1899/13/01", Wt::WDate {} }, // invalid month
|
||||||
|
{ "1899/11", Wt::WDate {1899, 11, 1} }, // missing day
|
||||||
|
{ "1899", Wt::WDate {1899, 1, 1} }, // missing month and days
|
||||||
|
{ "1600", Wt::WDate {1600, 1, 1} }, // missing month and days
|
||||||
|
{ "1995/05-09", Wt::WDate {} }, // invalid mixup separators
|
||||||
|
{ "1995-05/09", Wt::WDate {} }, // invalid mixup separators
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
for (const TestCase& testCase : testCases)
|
||||||
|
{
|
||||||
|
const Wt::WDate parsed {parseDate(testCase.str)};
|
||||||
|
|
||||||
|
EXPECT_EQ(parsed.year(), testCase.result.year()) << " str was '" << testCase.str << "'";
|
||||||
|
EXPECT_EQ(parsed.month(), testCase.result.month()) << " str was '" << testCase.str << "'";
|
||||||
|
EXPECT_EQ(parsed.day(), testCase.result.day()) << " str was '" << testCase.str << "'";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -817,12 +817,12 @@ Scanner::scanAudioFile(const std::filesystem::path& file, bool forceScan, ScanSt
|
|||||||
track.modify()->setTotalDisc(trackInfo->totalDisc);
|
track.modify()->setTotalDisc(trackInfo->totalDisc);
|
||||||
if (!trackInfo->discSubtitle.empty())
|
if (!trackInfo->discSubtitle.empty())
|
||||||
track.modify()->setDiscSubtitle(trackInfo->discSubtitle);
|
track.modify()->setDiscSubtitle(trackInfo->discSubtitle);
|
||||||
track.modify()->setYear(trackInfo->year ? *trackInfo->year : 0);
|
track.modify()->setDate(trackInfo->date);
|
||||||
track.modify()->setOriginalYear(trackInfo->originalYear ? *trackInfo->originalYear : 0);
|
track.modify()->setOriginalDate(trackInfo->originalDate);
|
||||||
|
|
||||||
// If a file has an OriginalYear but no Year, set it to ease filtering
|
// If a file has an OriginalYear but no Year, set it to ease filtering
|
||||||
if (!trackInfo->year && trackInfo->originalYear)
|
if (!trackInfo->date.isValid() && trackInfo->originalDate.isValid())
|
||||||
track.modify()->setYear(*trackInfo->originalYear);
|
track.modify()->setDate(trackInfo->originalDate);
|
||||||
|
|
||||||
track.modify()->setRecordingMBID(trackInfo->recordingMBID);
|
track.modify()->setRecordingMBID(trackInfo->recordingMBID);
|
||||||
track.modify()->setTrackMBID(trackInfo->trackMBID);
|
track.modify()->setTrackMBID(trackInfo->trackMBID);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ add_executable(test-database
|
|||||||
Artist.cpp
|
Artist.cpp
|
||||||
Cluster.cpp
|
Cluster.cpp
|
||||||
DatabaseTest.cpp
|
DatabaseTest.cpp
|
||||||
|
Release.cpp
|
||||||
Track.cpp
|
Track.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -23,284 +23,6 @@
|
|||||||
|
|
||||||
using namespace Database;
|
using namespace Database;
|
||||||
|
|
||||||
TEST_F(DatabaseFixture, SingleRelease)
|
|
||||||
{
|
|
||||||
ScopedRelease release {session, "MyRelease"};
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction {session.createSharedTransaction()};
|
|
||||||
|
|
||||||
auto releases {Release::getAllOrphans(session)};
|
|
||||||
ASSERT_EQ(releases.size(), 1);
|
|
||||||
EXPECT_EQ(releases.front()->getId(), release.getId());
|
|
||||||
|
|
||||||
releases = Release::getAll(session);
|
|
||||||
ASSERT_EQ(releases.size(), 1);
|
|
||||||
EXPECT_EQ(releases.front()->getId(), release.getId());
|
|
||||||
EXPECT_EQ(release->getDuration(), std::chrono::seconds {0});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(DatabaseFixture, SingleTrackSingleRelease)
|
|
||||||
{
|
|
||||||
ScopedRelease release {session, "MyRelease"};
|
|
||||||
|
|
||||||
{
|
|
||||||
ScopedTrack track {session, "MyTrack"};
|
|
||||||
{
|
|
||||||
auto transaction {session.createUniqueTransaction()};
|
|
||||||
|
|
||||||
track.get().modify()->setRelease(release.get());
|
|
||||||
track.get().modify()->setName("MyTrackName");
|
|
||||||
release.get().modify()->setName("MyReleaseName");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction {session.createSharedTransaction()};
|
|
||||||
EXPECT_TRUE(Release::getAllOrphans(session).empty());
|
|
||||||
|
|
||||||
EXPECT_EQ(release->getTracksCount(), 1);
|
|
||||||
ASSERT_EQ(release->getTracks().size(), 1);
|
|
||||||
EXPECT_EQ(release->getTracks().front()->getId(), track.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction {session.createUniqueTransaction()};
|
|
||||||
|
|
||||||
ASSERT_TRUE(track->getRelease());
|
|
||||||
EXPECT_EQ(track->getRelease()->getId(), release.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction {session.createUniqueTransaction()};
|
|
||||||
auto tracks {Track::getByNameAndReleaseName(session, "MyTrackName", "MyReleaseName")};
|
|
||||||
ASSERT_EQ(tracks.size(), 1);
|
|
||||||
EXPECT_EQ(tracks.front()->getId(), track.getId());
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto transaction {session.createUniqueTransaction()};
|
|
||||||
auto tracks {Track::getByNameAndReleaseName(session, "MyTrackName", "MyReleaseFoo")};
|
|
||||||
EXPECT_EQ(tracks.size(), 0);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto transaction {session.createUniqueTransaction()};
|
|
||||||
auto tracks {Track::getByNameAndReleaseName(session, "MyTrackFoo", "MyReleaseName")};
|
|
||||||
EXPECT_EQ(tracks.size(), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction {session.createUniqueTransaction()};
|
|
||||||
|
|
||||||
EXPECT_TRUE(release->getTracks().empty());
|
|
||||||
|
|
||||||
auto releases {Release::getAllOrphans(session)};
|
|
||||||
ASSERT_EQ(releases.size(), 1);
|
|
||||||
EXPECT_EQ(releases.front()->getId(), release.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(DatabaseFixture, MulitpleReleaseSearchByName)
|
|
||||||
{
|
|
||||||
ScopedRelease release1 {session, "MyRelease"};
|
|
||||||
ScopedRelease release2 {session, "MyRelease%"};
|
|
||||||
ScopedRelease release3 {session, "%MyRelease"};
|
|
||||||
ScopedRelease release4 {session, "MyRelease%Foo"};
|
|
||||||
ScopedRelease release5 {session, "Foo%MyRelease"};
|
|
||||||
ScopedRelease release6 {session, "_yRelease"};
|
|
||||||
|
|
||||||
// filters does not work on orphans
|
|
||||||
ScopedTrack track1 {session, "MyTrack"};
|
|
||||||
ScopedTrack track2 {session, "MyTrack"};
|
|
||||||
ScopedTrack track3 {session, "MyTrack"};
|
|
||||||
ScopedTrack track4 {session, "MyTrack"};
|
|
||||||
ScopedTrack track5 {session, "MyTrack"};
|
|
||||||
ScopedTrack track6 {session, "MyTrack"};
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction {session.createUniqueTransaction()};
|
|
||||||
|
|
||||||
track1.get().modify()->setRelease(release1.get());
|
|
||||||
track2.get().modify()->setRelease(release2.get());
|
|
||||||
track3.get().modify()->setRelease(release3.get());
|
|
||||||
track4.get().modify()->setRelease(release4.get());
|
|
||||||
track5.get().modify()->setRelease(release5.get());
|
|
||||||
track6.get().modify()->setRelease(release6.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction {session.createSharedTransaction()};
|
|
||||||
|
|
||||||
bool more;
|
|
||||||
{
|
|
||||||
const auto releases {Release::getByFilter(session, {}, {"Release"}, std::nullopt, more)};
|
|
||||||
EXPECT_EQ(releases.size(), 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const auto releases {Release::getByFilter(session, {}, {"MyRelease"}, std::nullopt, more)};
|
|
||||||
EXPECT_EQ(releases.size(), 5);
|
|
||||||
EXPECT_TRUE(std::none_of(std::cbegin(releases), std::cend(releases), [&](const Release::pointer& release) { return release->getId() == release6.getId(); }));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const auto releases {Release::getByFilter(session, {}, {"MyRelease%"}, std::nullopt, more)};
|
|
||||||
ASSERT_EQ(releases.size(), 2);
|
|
||||||
EXPECT_EQ(releases[0]->getId(), release2.getId());
|
|
||||||
EXPECT_EQ(releases[1]->getId(), release4.getId());
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const auto releases {Release::getByFilter(session, {}, {"%MyRelease"}, std::nullopt, more)};
|
|
||||||
ASSERT_EQ(releases.size(), 2);
|
|
||||||
EXPECT_EQ(releases[0]->getId(), release3.getId());
|
|
||||||
EXPECT_EQ(releases[1]->getId(), release5.getId());
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const auto releases {Release::getByFilter(session, {}, {"Foo%MyRelease"}, std::nullopt, more)};
|
|
||||||
ASSERT_EQ(releases.size(), 1);
|
|
||||||
EXPECT_EQ(releases[0]->getId(), release5.getId());
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const auto releases {Release::getByFilter(session, {}, {"MyRelease%Foo"}, std::nullopt, more)};
|
|
||||||
ASSERT_EQ(releases.size(), 1);
|
|
||||||
EXPECT_EQ(releases[0]->getId(), release4.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(DatabaseFixture, MultiTracksSingleReleaseTotalDiscTrack)
|
|
||||||
{
|
|
||||||
ScopedRelease release1 {session, "MyRelease"};
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction {session.createSharedTransaction()};
|
|
||||||
|
|
||||||
EXPECT_FALSE(release1->getTotalTrack());
|
|
||||||
EXPECT_FALSE(release1->getTotalDisc());
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedTrack track1 {session, "MyTrack"};
|
|
||||||
{
|
|
||||||
auto transaction {session.createUniqueTransaction()};
|
|
||||||
|
|
||||||
track1.get().modify()->setRelease(release1.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction {session.createSharedTransaction()};
|
|
||||||
|
|
||||||
EXPECT_FALSE(release1->getTotalTrack());
|
|
||||||
EXPECT_FALSE(release1->getTotalDisc());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction {session.createUniqueTransaction()};
|
|
||||||
|
|
||||||
track1.get().modify()->setTotalTrack(36);
|
|
||||||
track1.get().modify()->setTotalDisc(6);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction {session.createSharedTransaction()};
|
|
||||||
|
|
||||||
ASSERT_TRUE(release1->getTotalTrack());
|
|
||||||
EXPECT_EQ(*release1->getTotalTrack(), 36);
|
|
||||||
ASSERT_TRUE(release1->getTotalDisc());
|
|
||||||
EXPECT_EQ(*release1->getTotalDisc(), 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedTrack track2 {session, "MyTrack2"};
|
|
||||||
{
|
|
||||||
auto transaction {session.createUniqueTransaction()};
|
|
||||||
|
|
||||||
track2.get().modify()->setRelease(release1.get());
|
|
||||||
track2.get().modify()->setTotalTrack(37);
|
|
||||||
track2.get().modify()->setTotalDisc(67);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction {session.createSharedTransaction()};
|
|
||||||
|
|
||||||
ASSERT_TRUE(release1->getTotalTrack());
|
|
||||||
EXPECT_EQ(*release1->getTotalTrack(), 37);
|
|
||||||
ASSERT_TRUE(release1->getTotalDisc());
|
|
||||||
EXPECT_EQ(*release1->getTotalDisc(), 67);
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedRelease release2 {session, "MyRelease2"};
|
|
||||||
{
|
|
||||||
auto transaction {session.createSharedTransaction()};
|
|
||||||
|
|
||||||
EXPECT_FALSE(release2->getTotalTrack());
|
|
||||||
EXPECT_FALSE(release2->getTotalDisc());
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedTrack track3 {session, "MyTrack3"};
|
|
||||||
{
|
|
||||||
auto transaction {session.createUniqueTransaction()};
|
|
||||||
|
|
||||||
track3.get().modify()->setRelease(release2.get());
|
|
||||||
track3.get().modify()->setTotalTrack(7);
|
|
||||||
track3.get().modify()->setTotalDisc(5);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto transaction {session.createSharedTransaction()};
|
|
||||||
|
|
||||||
ASSERT_TRUE(release1->getTotalTrack());
|
|
||||||
EXPECT_EQ(*release1->getTotalTrack(), 37);
|
|
||||||
ASSERT_TRUE(release1->getTotalDisc());
|
|
||||||
EXPECT_EQ(*release1->getTotalDisc(), 67);
|
|
||||||
ASSERT_TRUE(release2->getTotalTrack());
|
|
||||||
EXPECT_EQ(*release2->getTotalTrack(), 7);
|
|
||||||
ASSERT_TRUE(release2->getTotalDisc());
|
|
||||||
EXPECT_EQ(*release2->getTotalDisc(), 5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(DatabaseFixture, MultiTracksSingleReleaseFirstTrack)
|
|
||||||
{
|
|
||||||
ScopedRelease release1 {session, "MyRelease1"};
|
|
||||||
ScopedRelease release2 {session, "MyRelease2"};
|
|
||||||
|
|
||||||
ScopedTrack track1A {session, "MyTrack1A"};
|
|
||||||
ScopedTrack track1B {session, "MyTrack1B"};
|
|
||||||
ScopedTrack track2A {session, "MyTrack2A"};
|
|
||||||
ScopedTrack track2B {session, "MyTrack2B"};
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction {session.createSharedTransaction()};
|
|
||||||
|
|
||||||
EXPECT_FALSE(release1->getFirstTrack());
|
|
||||||
EXPECT_FALSE(release2->getFirstTrack());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction {session.createUniqueTransaction()};
|
|
||||||
|
|
||||||
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()->setTrackNumber(1);
|
|
||||||
track1B.get().modify()->setTrackNumber(2);
|
|
||||||
|
|
||||||
track2A.get().modify()->setDiscNumber(2);
|
|
||||||
track2A.get().modify()->setTrackNumber(1);
|
|
||||||
track2B.get().modify()->setTrackNumber(2);
|
|
||||||
track2B.get().modify()->setDiscNumber(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
auto transaction {session.createSharedTransaction()};
|
|
||||||
|
|
||||||
EXPECT_TRUE(release1->getFirstTrack());
|
|
||||||
EXPECT_TRUE(release2->getFirstTrack());
|
|
||||||
|
|
||||||
EXPECT_EQ(release1->getFirstTrack()->getId(), track1A.getId());
|
|
||||||
EXPECT_EQ(release2->getFirstTrack()->getId(), track2B.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(DatabaseFixture, MultiTracksSingleArtistSingleRelease)
|
TEST_F(DatabaseFixture, MultiTracksSingleArtistSingleRelease)
|
||||||
{
|
{
|
||||||
constexpr std::size_t nbTracks {10};
|
constexpr std::size_t nbTracks {10};
|
||||||
|
|||||||
@@ -0,0 +1,354 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Common.hpp"
|
||||||
|
|
||||||
|
using namespace Database;
|
||||||
|
|
||||||
|
TEST_F(DatabaseFixture, SingleRelease)
|
||||||
|
{
|
||||||
|
ScopedRelease release {session, "MyRelease"};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
|
||||||
|
auto releases {Release::getAllOrphans(session)};
|
||||||
|
ASSERT_EQ(releases.size(), 1);
|
||||||
|
EXPECT_EQ(releases.front()->getId(), release.getId());
|
||||||
|
|
||||||
|
releases = Release::getAll(session);
|
||||||
|
ASSERT_EQ(releases.size(), 1);
|
||||||
|
EXPECT_EQ(releases.front()->getId(), release.getId());
|
||||||
|
EXPECT_EQ(release->getDuration(), std::chrono::seconds {0});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DatabaseFixture, SingleTrackSingleRelease)
|
||||||
|
{
|
||||||
|
ScopedRelease release {session, "MyRelease"};
|
||||||
|
|
||||||
|
{
|
||||||
|
ScopedTrack track {session, "MyTrack"};
|
||||||
|
{
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
|
track.get().modify()->setRelease(release.get());
|
||||||
|
track.get().modify()->setName("MyTrackName");
|
||||||
|
release.get().modify()->setName("MyReleaseName");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
EXPECT_TRUE(Release::getAllOrphans(session).empty());
|
||||||
|
|
||||||
|
EXPECT_EQ(release->getTracksCount(), 1);
|
||||||
|
ASSERT_EQ(release->getTracks().size(), 1);
|
||||||
|
EXPECT_EQ(release->getTracks().front()->getId(), track.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
|
ASSERT_TRUE(track->getRelease());
|
||||||
|
EXPECT_EQ(track->getRelease()->getId(), release.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
auto tracks {Track::getByNameAndReleaseName(session, "MyTrackName", "MyReleaseName")};
|
||||||
|
ASSERT_EQ(tracks.size(), 1);
|
||||||
|
EXPECT_EQ(tracks.front()->getId(), track.getId());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
auto tracks {Track::getByNameAndReleaseName(session, "MyTrackName", "MyReleaseFoo")};
|
||||||
|
EXPECT_EQ(tracks.size(), 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
auto tracks {Track::getByNameAndReleaseName(session, "MyTrackFoo", "MyReleaseName")};
|
||||||
|
EXPECT_EQ(tracks.size(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
|
EXPECT_TRUE(release->getTracks().empty());
|
||||||
|
|
||||||
|
auto releases {Release::getAllOrphans(session)};
|
||||||
|
ASSERT_EQ(releases.size(), 1);
|
||||||
|
EXPECT_EQ(releases.front()->getId(), release.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DatabaseFixture, MulitpleReleaseSearchByName)
|
||||||
|
{
|
||||||
|
ScopedRelease release1 {session, "MyRelease"};
|
||||||
|
ScopedRelease release2 {session, "MyRelease%"};
|
||||||
|
ScopedRelease release3 {session, "%MyRelease"};
|
||||||
|
ScopedRelease release4 {session, "MyRelease%Foo"};
|
||||||
|
ScopedRelease release5 {session, "Foo%MyRelease"};
|
||||||
|
ScopedRelease release6 {session, "_yRelease"};
|
||||||
|
|
||||||
|
// filters does not work on orphans
|
||||||
|
ScopedTrack track1 {session, "MyTrack"};
|
||||||
|
ScopedTrack track2 {session, "MyTrack"};
|
||||||
|
ScopedTrack track3 {session, "MyTrack"};
|
||||||
|
ScopedTrack track4 {session, "MyTrack"};
|
||||||
|
ScopedTrack track5 {session, "MyTrack"};
|
||||||
|
ScopedTrack track6 {session, "MyTrack"};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
|
track1.get().modify()->setRelease(release1.get());
|
||||||
|
track2.get().modify()->setRelease(release2.get());
|
||||||
|
track3.get().modify()->setRelease(release3.get());
|
||||||
|
track4.get().modify()->setRelease(release4.get());
|
||||||
|
track5.get().modify()->setRelease(release5.get());
|
||||||
|
track6.get().modify()->setRelease(release6.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
|
||||||
|
bool more;
|
||||||
|
{
|
||||||
|
const auto releases {Release::getByFilter(session, {}, {"Release"}, std::nullopt, more)};
|
||||||
|
EXPECT_EQ(releases.size(), 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto releases {Release::getByFilter(session, {}, {"MyRelease"}, std::nullopt, more)};
|
||||||
|
EXPECT_EQ(releases.size(), 5);
|
||||||
|
EXPECT_TRUE(std::none_of(std::cbegin(releases), std::cend(releases), [&](const Release::pointer& release) { return release->getId() == release6.getId(); }));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const auto releases {Release::getByFilter(session, {}, {"MyRelease%"}, std::nullopt, more)};
|
||||||
|
ASSERT_EQ(releases.size(), 2);
|
||||||
|
EXPECT_EQ(releases[0]->getId(), release2.getId());
|
||||||
|
EXPECT_EQ(releases[1]->getId(), release4.getId());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const auto releases {Release::getByFilter(session, {}, {"%MyRelease"}, std::nullopt, more)};
|
||||||
|
ASSERT_EQ(releases.size(), 2);
|
||||||
|
EXPECT_EQ(releases[0]->getId(), release3.getId());
|
||||||
|
EXPECT_EQ(releases[1]->getId(), release5.getId());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const auto releases {Release::getByFilter(session, {}, {"Foo%MyRelease"}, std::nullopt, more)};
|
||||||
|
ASSERT_EQ(releases.size(), 1);
|
||||||
|
EXPECT_EQ(releases[0]->getId(), release5.getId());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const auto releases {Release::getByFilter(session, {}, {"MyRelease%Foo"}, std::nullopt, more)};
|
||||||
|
ASSERT_EQ(releases.size(), 1);
|
||||||
|
EXPECT_EQ(releases[0]->getId(), release4.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DatabaseFixture, MultiTracksSingleReleaseTotalDiscTrack)
|
||||||
|
{
|
||||||
|
ScopedRelease release1 {session, "MyRelease"};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
|
||||||
|
EXPECT_FALSE(release1->getTotalTrack());
|
||||||
|
EXPECT_FALSE(release1->getTotalDisc());
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedTrack track1 {session, "MyTrack"};
|
||||||
|
{
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
|
track1.get().modify()->setRelease(release1.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
|
||||||
|
EXPECT_FALSE(release1->getTotalTrack());
|
||||||
|
EXPECT_FALSE(release1->getTotalDisc());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
|
track1.get().modify()->setTotalTrack(36);
|
||||||
|
track1.get().modify()->setTotalDisc(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
|
||||||
|
ASSERT_TRUE(release1->getTotalTrack());
|
||||||
|
EXPECT_EQ(*release1->getTotalTrack(), 36);
|
||||||
|
ASSERT_TRUE(release1->getTotalDisc());
|
||||||
|
EXPECT_EQ(*release1->getTotalDisc(), 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedTrack track2 {session, "MyTrack2"};
|
||||||
|
{
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
|
track2.get().modify()->setRelease(release1.get());
|
||||||
|
track2.get().modify()->setTotalTrack(37);
|
||||||
|
track2.get().modify()->setTotalDisc(67);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
|
||||||
|
ASSERT_TRUE(release1->getTotalTrack());
|
||||||
|
EXPECT_EQ(*release1->getTotalTrack(), 37);
|
||||||
|
ASSERT_TRUE(release1->getTotalDisc());
|
||||||
|
EXPECT_EQ(*release1->getTotalDisc(), 67);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedRelease release2 {session, "MyRelease2"};
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
|
||||||
|
EXPECT_FALSE(release2->getTotalTrack());
|
||||||
|
EXPECT_FALSE(release2->getTotalDisc());
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedTrack track3 {session, "MyTrack3"};
|
||||||
|
{
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
|
track3.get().modify()->setRelease(release2.get());
|
||||||
|
track3.get().modify()->setTotalTrack(7);
|
||||||
|
track3.get().modify()->setTotalDisc(5);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
|
||||||
|
ASSERT_TRUE(release1->getTotalTrack());
|
||||||
|
EXPECT_EQ(*release1->getTotalTrack(), 37);
|
||||||
|
ASSERT_TRUE(release1->getTotalDisc());
|
||||||
|
EXPECT_EQ(*release1->getTotalDisc(), 67);
|
||||||
|
ASSERT_TRUE(release2->getTotalTrack());
|
||||||
|
EXPECT_EQ(*release2->getTotalTrack(), 7);
|
||||||
|
ASSERT_TRUE(release2->getTotalDisc());
|
||||||
|
EXPECT_EQ(*release2->getTotalDisc(), 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DatabaseFixture, MultiTracksSingleReleaseFirstTrack)
|
||||||
|
{
|
||||||
|
ScopedRelease release1 {session, "MyRelease1"};
|
||||||
|
ScopedRelease release2 {session, "MyRelease2"};
|
||||||
|
|
||||||
|
ScopedTrack track1A {session, "MyTrack1A"};
|
||||||
|
ScopedTrack track1B {session, "MyTrack1B"};
|
||||||
|
ScopedTrack track2A {session, "MyTrack2A"};
|
||||||
|
ScopedTrack track2B {session, "MyTrack2B"};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
|
||||||
|
EXPECT_FALSE(release1->getFirstTrack());
|
||||||
|
EXPECT_FALSE(release2->getFirstTrack());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
|
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()->setTrackNumber(1);
|
||||||
|
track1B.get().modify()->setTrackNumber(2);
|
||||||
|
|
||||||
|
track2A.get().modify()->setDiscNumber(2);
|
||||||
|
track2A.get().modify()->setTrackNumber(1);
|
||||||
|
track2B.get().modify()->setTrackNumber(2);
|
||||||
|
track2B.get().modify()->setDiscNumber(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
|
||||||
|
EXPECT_TRUE(release1->getFirstTrack());
|
||||||
|
EXPECT_TRUE(release2->getFirstTrack());
|
||||||
|
|
||||||
|
EXPECT_EQ(release1->getFirstTrack()->getId(), track1A.getId());
|
||||||
|
EXPECT_EQ(release2->getFirstTrack()->getId(), track2B.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DatabaseFixture, MultiTracksSingleReleaseDate)
|
||||||
|
{
|
||||||
|
ScopedRelease release1 {session, "MyRelease1"};
|
||||||
|
ScopedRelease release2 {session, "MyRelease2"};
|
||||||
|
const Wt::WDate release1Date {Wt::WDate {1994, 2, 3}};
|
||||||
|
const Wt::WDate release1OriginalDate {Wt::WDate {1993, 4, 5}};
|
||||||
|
|
||||||
|
ScopedTrack track1A {session, "MyTrack1A"};
|
||||||
|
ScopedTrack track1B {session, "MyTrack1B"};
|
||||||
|
ScopedTrack track2A {session, "MyTrack2A"};
|
||||||
|
ScopedTrack track2B {session, "MyTrack2B"};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
|
||||||
|
const auto releases {Release::getByYear(session, 0, 3000)};
|
||||||
|
EXPECT_EQ(releases.size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
|
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()->getReleaseYear(), release1Date.year());
|
||||||
|
EXPECT_EQ(release1.get()->getReleaseYear(true), release1OriginalDate.year());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
|
||||||
|
auto releases {Release::getByYear(session, 1950, 2000)};
|
||||||
|
ASSERT_EQ(releases.size(), 1);
|
||||||
|
EXPECT_EQ(releases.front()->getId(), release1.getId());
|
||||||
|
|
||||||
|
releases = Release::getByYear(session, 1994, 1994);
|
||||||
|
ASSERT_EQ(releases.size(), 1);
|
||||||
|
EXPECT_EQ(releases.front()->getId(), release1.getId());
|
||||||
|
|
||||||
|
releases = Release::getByYear(session, 1993, 1993);
|
||||||
|
ASSERT_EQ(releases.size(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -87,3 +87,28 @@ TEST_F(DatabaseFixture, MultipleTracksSearchByFilter)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DatabaseFixture, SingleTrackDate)
|
||||||
|
{
|
||||||
|
ScopedTrack track {session, "MyTrack"};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
EXPECT_EQ(track->getYear(), std::nullopt);
|
||||||
|
EXPECT_EQ(track->getOriginalYear(), std::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
track.get().modify()->setDate(Wt::WDate {1995, 5, 5});
|
||||||
|
track.get().modify()->setOriginalDate(Wt::WDate {1994, 2, 2});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
EXPECT_EQ(track->getYear(), 1995);
|
||||||
|
EXPECT_EQ(track->getOriginalYear(), 1994);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -134,11 +134,11 @@ void parse(MetaData::IParser& parser, const std::filesystem::path& file)
|
|||||||
if (track->totalDisc)
|
if (track->totalDisc)
|
||||||
std::cout << "TotalDisc: " << *track->totalDisc << std::endl;
|
std::cout << "TotalDisc: " << *track->totalDisc << std::endl;
|
||||||
|
|
||||||
if (track->year)
|
if (track->date.isValid())
|
||||||
std::cout << "Year: " << *track->year << std::endl;
|
std::cout << "Date: " << track->date.toString("yyyy-MM-dd") << std::endl;
|
||||||
|
|
||||||
if (track->originalYear)
|
if (track->originalDate.isValid())
|
||||||
std::cout << "Original year: " << *track->originalYear << std::endl;
|
std::cout << "Original date: " << track->originalDate.toString("yyyy-MM-dd") << std::endl;
|
||||||
|
|
||||||
std::cout << "HasCover = " << std::boolalpha << track->hasCover << std::endl;
|
std::cout << "HasCover = " << std::boolalpha << track->hasCover << std::endl;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user