Made some more progress on theming
This commit is contained in:
@@ -33,9 +33,9 @@ Db::Db(const std::filesystem::path& dbPath)
|
||||
LMS_LOG(DB, INFO) << "Creating connection pool on file " << dbPath.string();
|
||||
|
||||
std::unique_ptr<Wt::Dbo::backend::Sqlite3> connection {std::make_unique<Wt::Dbo::backend::Sqlite3>(dbPath.string())};
|
||||
// connection->setProperty("show-queries", "true");
|
||||
connection->executeSql("pragma journal_mode=WAL");
|
||||
connection->executeSql("pragma synchronous=normal");
|
||||
// connection->setProperty("show-queries", "true");
|
||||
|
||||
auto connectionPool = std::make_unique<Wt::Dbo::FixedSqlConnectionPool>(std::move(connection), 10);
|
||||
connectionPool->setTimeout(std::chrono::seconds(10));
|
||||
@@ -43,6 +43,14 @@ Db::Db(const std::filesystem::path& dbPath)
|
||||
_connectionPool = std::move(connectionPool);
|
||||
}
|
||||
|
||||
void
|
||||
Db::executeSql(const std::string& sql)
|
||||
{
|
||||
auto connection {_connectionPool->getConnection()};
|
||||
connection->executeSql(sql);
|
||||
_connectionPool->returnConnection(std::move(connection));
|
||||
}
|
||||
|
||||
} // namespace Database
|
||||
|
||||
|
||||
|
||||
@@ -268,15 +268,29 @@ Release::getAllIdsWithClusters(Session& session, std::optional<std::size_t> limi
|
||||
|
||||
|
||||
std::optional<std::size_t>
|
||||
Release::getTotalTrackNumber(void) const
|
||||
Release::getTotalTrack(void) const
|
||||
{
|
||||
return (_totalTrackNumber > 0) ? std::make_optional<std::size_t>(_totalTrackNumber) : std::nullopt;
|
||||
assert(session());
|
||||
assert(IdIsValid(self()->id()));
|
||||
|
||||
int res = session()->query<int>("SELECT COALESCE(MAX(total_track),0) FROM track t INNER JOIN release r ON r.id = t.release_id")
|
||||
.where("r.id = ?")
|
||||
.bind(this->id());
|
||||
|
||||
return (res > 0) ? std::make_optional<std::size_t>(res) : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::size_t>
|
||||
Release::getTotalDiscNumber(void) const
|
||||
Release::getTotalDisc(void) const
|
||||
{
|
||||
return (_totalDiscNumber > 0) ? std::make_optional<std::size_t>(_totalDiscNumber) : std::nullopt;
|
||||
assert(session());
|
||||
assert(IdIsValid(self()->id()));
|
||||
|
||||
int res = session()->query<int>("SELECT COALESCE(MAX(total_disc),0) FROM track t INNER JOIN release r ON r.id = t.release_id")
|
||||
.where("r.id = ?")
|
||||
.bind(this->id());
|
||||
|
||||
return (res > 0) ? std::make_optional<std::size_t>(res) : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<int>
|
||||
@@ -287,7 +301,7 @@ Release::getReleaseYear(bool original) const
|
||||
const std::string field {original ? "original_year" : "year"};
|
||||
|
||||
Wt::Dbo::collection<int> dates = session()->query<int>(
|
||||
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 = ?")
|
||||
.groupBy(field)
|
||||
.bind(this->id());
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
namespace Database {
|
||||
|
||||
#define LMS_DATABASE_VERSION 16
|
||||
#define LMS_DATABASE_VERSION 18
|
||||
|
||||
using Version = std::size_t;
|
||||
|
||||
@@ -83,26 +83,31 @@ class VersionInfo
|
||||
void
|
||||
Session::doDatabaseMigrationIfNeeded()
|
||||
{
|
||||
auto uniqueTransaction {createUniqueTransaction()};
|
||||
|
||||
static const std::string outdatedMsg {"Outdated database, please rebuild it (delete the .db file and restart)"};
|
||||
|
||||
Version version;
|
||||
try
|
||||
{
|
||||
version = VersionInfo::getOrCreate(*this)->getVersion();
|
||||
LMS_LOG(DB, INFO) << "Database version = " << version << ", LMS binary version = " << LMS_DATABASE_VERSION;
|
||||
if (version == LMS_DATABASE_VERSION)
|
||||
return;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
LMS_LOG(DB, ERROR) << "Cannot get database version info: " << e.what();
|
||||
throw LmsException {outdatedMsg};
|
||||
}
|
||||
Db::ScopedNoForeignKeys noPragmaKeys {_db};
|
||||
|
||||
while (version < LMS_DATABASE_VERSION)
|
||||
while (1)
|
||||
{
|
||||
auto uniqueTransaction {createUniqueTransaction()};
|
||||
|
||||
Version version;
|
||||
try
|
||||
{
|
||||
version = VersionInfo::getOrCreate(*this)->getVersion();
|
||||
LMS_LOG(DB, INFO) << "Database version = " << version << ", LMS binary version = " << LMS_DATABASE_VERSION;
|
||||
if (version == LMS_DATABASE_VERSION)
|
||||
{
|
||||
LMS_LOG(DB, DEBUG) << "Lms database version " << LMS_DATABASE_VERSION << ": up to date!";
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
LMS_LOG(DB, ERROR) << "Cannot get database version info: " << e.what();
|
||||
throw LmsException {outdatedMsg};
|
||||
}
|
||||
|
||||
LMS_LOG(DB, INFO) << "Migrating database from version " << version << "...";
|
||||
|
||||
if (version == 5)
|
||||
@@ -173,15 +178,41 @@ CREATE TABLE IF NOT EXISTS "track_bookmark" (
|
||||
{
|
||||
_session.execute("ALTER TABLE user ADD ui_theme INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(User::defaultUITheme)) + ")");
|
||||
}
|
||||
else if (version == 16)
|
||||
{
|
||||
_session.execute("ALTER TABLE track ADD total_disc INTEGER NOT NULL DEFAULT(0)");
|
||||
_session.execute("ALTER TABLE track ADD total_track INTEGER NOT NULL DEFAULT(0)");
|
||||
|
||||
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
|
||||
ScanSettings::get(*this).modify()->incScanVersion();
|
||||
}
|
||||
else if (version == 17)
|
||||
{
|
||||
// Drop colums total_disc/total_track from release
|
||||
_session.execute(R"(
|
||||
CREATE TABLE "release_backup" (
|
||||
"id" integer primary key autoincrement,
|
||||
"version" integer not null,
|
||||
"name" text not null,
|
||||
"mbid" text not null
|
||||
))");
|
||||
_session.execute("INSERT INTO release_backup SELECT id,version,name,mbid FROM release");
|
||||
_session.execute("DROP TABLE release;");
|
||||
_session.execute("ALTER TABLE release_backup RENAME TO release");
|
||||
_session.execute("CREATE INDEX release_name_idx ON release(name)");
|
||||
_session.execute("CREATE INDEX release_name_nocase_idx ON release(name COLLATE NOCASE)");
|
||||
_session.execute("CREATE INDEX release_mbid_idx ON release(mbid)");
|
||||
|
||||
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
|
||||
ScanSettings::get(*this).modify()->incScanVersion();
|
||||
}
|
||||
else
|
||||
{
|
||||
LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration";
|
||||
throw LmsException { LMS_DATABASE_VERSION > version ? outdatedMsg : "Server binary outdated, please upgrade it to handle this database"};
|
||||
}
|
||||
|
||||
++version;
|
||||
|
||||
VersionInfo::get(*this).modify()->setVersion(LMS_DATABASE_VERSION);
|
||||
VersionInfo::get(*this).modify()->setVersion(++version);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ Track::getAllIdsWithClusters(Session& session, std::optional<std::size_t> limit)
|
||||
}
|
||||
|
||||
std::vector<Cluster::pointer>
|
||||
Track::getClusters(void) const
|
||||
Track::getClusters() const
|
||||
{
|
||||
std::vector< Cluster::pointer > clusters;
|
||||
std::copy(_clusters.begin(), _clusters.end(), std::back_inserter(clusters));
|
||||
@@ -185,7 +185,7 @@ Track::getClusters(void) const
|
||||
}
|
||||
|
||||
std::vector<IdType>
|
||||
Track::getClusterIds(void) const
|
||||
Track::getClusterIds() const
|
||||
{
|
||||
assert(self());
|
||||
assert(IdIsValid(self()->id()));
|
||||
@@ -355,17 +355,29 @@ Track::setFeatures(const Wt::Dbo::ptr<TrackFeatures>& features)
|
||||
}
|
||||
|
||||
std::optional<std::size_t>
|
||||
Track::getTrackNumber(void) const
|
||||
Track::getTrackNumber() const
|
||||
{
|
||||
return (_trackNumber > 0) ? std::make_optional<std::size_t>(_trackNumber) : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::size_t>
|
||||
Track::getDiscNumber(void) const
|
||||
Track::getTotalTrack() const
|
||||
{
|
||||
return (_totalTrack > 0) ? std::make_optional<std::size_t>(_totalTrack) : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::size_t>
|
||||
Track::getDiscNumber() const
|
||||
{
|
||||
return (_discNumber > 0) ? std::make_optional<std::size_t>(_discNumber) : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::size_t>
|
||||
Track::getTotalDisc() const
|
||||
{
|
||||
return (_totalDisc > 0) ? std::make_optional<std::size_t>(_totalDisc) : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<int>
|
||||
Track::getYear() const
|
||||
{
|
||||
|
||||
@@ -39,6 +39,25 @@ class Db
|
||||
std::shared_mutex& getMutex() { return _sharedMutex; }
|
||||
Wt::Dbo::SqlConnectionPool& getConnectionPool() { return *_connectionPool; }
|
||||
|
||||
class ScopedNoForeignKeys
|
||||
{
|
||||
public:
|
||||
ScopedNoForeignKeys(Db& db) : _db {db}
|
||||
{
|
||||
_db.executeSql("PRAGMA foreign_keys=OFF");
|
||||
}
|
||||
~ScopedNoForeignKeys()
|
||||
{
|
||||
_db.executeSql("PRAGMA foreign_keys=ON");
|
||||
}
|
||||
|
||||
private:
|
||||
Db& _db;
|
||||
|
||||
};
|
||||
|
||||
void executeSql(const std::string& sql);
|
||||
|
||||
std::shared_mutex _sharedMutex;
|
||||
std::unique_ptr<Wt::Dbo::SqlConnectionPool> _connectionPool;
|
||||
};
|
||||
|
||||
@@ -84,15 +84,11 @@ class Release : public Wt::Dbo::Dbo<Release>
|
||||
std::optional<std::string> getCopyright() const;
|
||||
std::optional<std::string> getCopyrightURL() const;
|
||||
|
||||
// Modifiers
|
||||
void setTotalDiscNumber(std::size_t num) { _totalDiscNumber = static_cast<int>(num); }
|
||||
void setTotalTrackNumber(std::size_t num) { _totalTrackNumber = static_cast<int>(num); }
|
||||
|
||||
// Accessors
|
||||
const std::string& getName() const { return _name; }
|
||||
std::optional<UUID> getMBID() const { return UUID::fromString(_MBID); }
|
||||
std::optional<std::size_t> getTotalTrackNumber() const;
|
||||
std::optional<std::size_t> getTotalDiscNumber() const;
|
||||
std::optional<std::size_t> getTotalTrack() const;
|
||||
std::optional<std::size_t> getTotalDisc() const;
|
||||
std::chrono::milliseconds getDuration() const;
|
||||
|
||||
// Get the artists of this release
|
||||
@@ -109,8 +105,6 @@ class Release : public Wt::Dbo::Dbo<Release>
|
||||
{
|
||||
Wt::Dbo::field(a, _name, "name");
|
||||
Wt::Dbo::field(a, _MBID, "mbid");
|
||||
Wt::Dbo::field(a, _totalDiscNumber, "total_disc_number");
|
||||
Wt::Dbo::field(a, _totalTrackNumber, "total_track_number");
|
||||
|
||||
Wt::Dbo::hasMany(a, _tracks, Wt::Dbo::ManyToOne, "release");
|
||||
Wt::Dbo::hasMany(a, _starringUsers, Wt::Dbo::ManyToMany, "user_release_starred", "", Wt::Dbo::OnDeleteCascade);
|
||||
@@ -121,8 +115,6 @@ class Release : public Wt::Dbo::Dbo<Release>
|
||||
|
||||
std::string _name;
|
||||
std::string _MBID;
|
||||
int _totalDiscNumber {};
|
||||
int _totalTrackNumber {};
|
||||
|
||||
Wt::Dbo::collection<Wt::Dbo::ptr<Track>> _tracks; // Tracks in the release
|
||||
Wt::Dbo::collection<Wt::Dbo::ptr<User>> _starringUsers; // Users that starred this release
|
||||
|
||||
@@ -88,6 +88,8 @@ class Track : public Wt::Dbo::Dbo<Track>
|
||||
void setScanVersion(std::size_t version) { _scanVersion = version; }
|
||||
void setTrackNumber(int num) { _trackNumber = num; }
|
||||
void setDiscNumber(int num) { _discNumber = num; }
|
||||
void setTotalTrack(std::optional<int> totalTrack) { totalTrack ? _totalTrack = *totalTrack : 0; }
|
||||
void setTotalDisc(std::optional<int> totalDisc) { totalDisc ? _totalDisc = *totalDisc : 0; }
|
||||
void setName(const std::string& name) { _name = std::string(name, 0, _maxNameLength); }
|
||||
void setDuration(std::chrono::milliseconds duration) { _duration = duration; }
|
||||
void setLastWriteTime(Wt::WDateTime time) { _fileLastWrite = time; }
|
||||
@@ -106,7 +108,9 @@ class Track : public Wt::Dbo::Dbo<Track>
|
||||
|
||||
std::size_t getScanVersion() const { return _scanVersion; }
|
||||
std::optional<std::size_t> getTrackNumber() const;
|
||||
std::optional<std::size_t> getTotalTrack() const;
|
||||
std::optional<std::size_t> getDiscNumber() const;
|
||||
std::optional<std::size_t> getTotalDisc() const;
|
||||
std::string getName() const { return _name; }
|
||||
std::filesystem::path getPath() const { return _filePath; }
|
||||
std::chrono::milliseconds getDuration() const { return _duration; }
|
||||
@@ -135,6 +139,8 @@ class Track : public Wt::Dbo::Dbo<Track>
|
||||
Wt::Dbo::field(a, _scanVersion, "scan_version");
|
||||
Wt::Dbo::field(a, _trackNumber, "track_number");
|
||||
Wt::Dbo::field(a, _discNumber, "disc_number");
|
||||
Wt::Dbo::field(a, _totalTrack, "total_track");
|
||||
Wt::Dbo::field(a, _totalDisc, "total_disc");
|
||||
Wt::Dbo::field(a, _name, "name");
|
||||
Wt::Dbo::field(a, _duration, "duration");
|
||||
Wt::Dbo::field(a, _year, "year");
|
||||
@@ -160,19 +166,21 @@ class Track : public Wt::Dbo::Dbo<Track>
|
||||
static const std::size_t _maxCopyrightLength = 128;
|
||||
static const std::size_t _maxCopyrightURLLength = 128;
|
||||
|
||||
int _scanVersion = 0;
|
||||
int _trackNumber = 0;
|
||||
int _discNumber = 0;
|
||||
int _scanVersion {};
|
||||
int _trackNumber {};
|
||||
int _discNumber {};
|
||||
int _totalTrack {};
|
||||
int _totalDisc {};
|
||||
std::string _name;
|
||||
std::string _artistName;
|
||||
std::string _releaseName;
|
||||
std::chrono::duration<int, std::milli> _duration;
|
||||
int _year = 0;
|
||||
int _originalYear = 0;
|
||||
int _year {};
|
||||
int _originalYear {};
|
||||
std::string _filePath;
|
||||
Wt::WDateTime _fileLastWrite;
|
||||
Wt::WDateTime _fileAdded;
|
||||
bool _hasCover = false;
|
||||
bool _hasCover {};
|
||||
std::string _MBID; // Musicbrainz Identifier
|
||||
std::string _copyright;
|
||||
std::string _copyrightURL;
|
||||
|
||||
Reference in New Issue
Block a user