Added release country support
This commit is contained in:
@@ -35,7 +35,7 @@ namespace lms::db
|
|||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
static constexpr Version LMS_DATABASE_VERSION{ 81 };
|
static constexpr Version LMS_DATABASE_VERSION{ 82 };
|
||||||
}
|
}
|
||||||
|
|
||||||
VersionInfo::VersionInfo()
|
VersionInfo::VersionInfo()
|
||||||
@@ -1072,6 +1072,30 @@ FROM tracklist)");
|
|||||||
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void migrateFromV81(Session& session)
|
||||||
|
{
|
||||||
|
// Add country + release country
|
||||||
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "country" (
|
||||||
|
"id" integer primary key autoincrement,
|
||||||
|
"version" integer not null,
|
||||||
|
"name" text not null
|
||||||
|
))");
|
||||||
|
|
||||||
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "release_country" (
|
||||||
|
"country_id" bigint,
|
||||||
|
"release_id" bigint,
|
||||||
|
primary key ("country_id", "release_id"),
|
||||||
|
constraint "fk_release_country_key1" foreign key ("country_id") references "country" ("id") on delete cascade deferrable initially deferred,
|
||||||
|
constraint "fk_release_country_key2" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred
|
||||||
|
))");
|
||||||
|
|
||||||
|
utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "release_country_country" on "release_country" ("country_id"))");
|
||||||
|
utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "release_country_release" on "release_country" ("release_id"))");
|
||||||
|
|
||||||
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
||||||
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
||||||
|
}
|
||||||
|
|
||||||
bool doDbMigration(Session& session)
|
bool doDbMigration(Session& session)
|
||||||
{
|
{
|
||||||
constexpr std::string_view outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
|
constexpr std::string_view outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
|
||||||
@@ -1129,6 +1153,7 @@ FROM tracklist)");
|
|||||||
{ 78, migrateFromV78 },
|
{ 78, migrateFromV78 },
|
||||||
{ 79, migrateFromV79 },
|
{ 79, migrateFromV79 },
|
||||||
{ 80, migrateFromV80 },
|
{ 80, migrateFromV80 },
|
||||||
|
{ 81, migrateFromV81 },
|
||||||
};
|
};
|
||||||
|
|
||||||
bool migrationPerformed{};
|
bool migrationPerformed{};
|
||||||
|
|||||||
@@ -253,6 +253,50 @@ namespace lms::db
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
Country::Country(std::string_view name)
|
||||||
|
: _name{ name }
|
||||||
|
{
|
||||||
|
// As we use the name to uniquely identoify release type, we must throw (and not truncate)
|
||||||
|
if (name.size() > _maxNameLength)
|
||||||
|
throw Exception{ "Country name is too long: " + std::string{ name } + "'" };
|
||||||
|
}
|
||||||
|
|
||||||
|
Country::pointer Country::create(Session& session, std::string_view name)
|
||||||
|
{
|
||||||
|
return session.getDboSession()->add(std::unique_ptr<Country>{ new Country{ name } });
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t Country::getCount(Session& session)
|
||||||
|
{
|
||||||
|
return utils::fetchQuerySingleResult(session.getDboSession()->query<int>("SELECT COUNT(*) FROM country"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Country::pointer Country::find(Session& session, CountryId id)
|
||||||
|
{
|
||||||
|
session.checkReadTransaction();
|
||||||
|
|
||||||
|
return utils::fetchQuerySingleResult(session.getDboSession()->query<Wt::Dbo::ptr<Country>>("SELECT c from country c").where("c.id = ?").bind(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
Country::pointer Country::find(Session& session, std::string_view name)
|
||||||
|
{
|
||||||
|
session.checkReadTransaction();
|
||||||
|
|
||||||
|
if (name.size() > _maxNameLength)
|
||||||
|
throw Exception{ "Requeted Country name is too long: " + std::string{ name } + "'" };
|
||||||
|
|
||||||
|
return utils::fetchQuerySingleResult(session.getDboSession()->query<Wt::Dbo::ptr<Country>>("SELECT c from country c").where("c.name = ?").bind(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
RangeResults<CountryId> Country::findOrphanIds(Session& session, std::optional<Range> range)
|
||||||
|
{
|
||||||
|
session.checkReadTransaction();
|
||||||
|
|
||||||
|
// select the labels that have no releases
|
||||||
|
auto query{ session.getDboSession()->query<CountryId>("select c.id from country c LEFT OUTER JOIN release_country r_c ON c.id = r_c.country_id WHERE r_c.release_id IS NULL") };
|
||||||
|
return utils::execRangeQuery<CountryId>(query, range);
|
||||||
|
}
|
||||||
|
|
||||||
Label::Label(std::string_view name)
|
Label::Label(std::string_view name)
|
||||||
: _name{ name }
|
: _name{ name }
|
||||||
{
|
{
|
||||||
@@ -612,6 +656,11 @@ namespace lms::db
|
|||||||
_labels.clear();
|
_labels.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Release::clearCountries()
|
||||||
|
{
|
||||||
|
_countries.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void Release::clearReleaseTypes()
|
void Release::clearReleaseTypes()
|
||||||
{
|
{
|
||||||
_releaseTypes.clear();
|
_releaseTypes.clear();
|
||||||
@@ -622,6 +671,11 @@ namespace lms::db
|
|||||||
_labels.insert(getDboPtr(label));
|
_labels.insert(getDboPtr(label));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Release::addCountry(ObjectPtr<Country> country)
|
||||||
|
{
|
||||||
|
_countries.insert(getDboPtr(country));
|
||||||
|
}
|
||||||
|
|
||||||
void Release::addReleaseType(ObjectPtr<ReleaseType> releaseType)
|
void Release::addReleaseType(ObjectPtr<ReleaseType> releaseType)
|
||||||
{
|
{
|
||||||
_releaseTypes.insert(getDboPtr(releaseType));
|
_releaseTypes.insert(getDboPtr(releaseType));
|
||||||
@@ -659,8 +713,22 @@ namespace lms::db
|
|||||||
{
|
{
|
||||||
std::vector<std::string> res;
|
std::vector<std::string> res;
|
||||||
|
|
||||||
for (const auto& label : _labels)
|
auto query{ _labels.find() };
|
||||||
|
utils::forEachQueryResult(query, [&](const Label::pointer& label) {
|
||||||
res.push_back(std::string{ label->getName() });
|
res.push_back(std::string{ label->getName() });
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> Release::getCountryNames() const
|
||||||
|
{
|
||||||
|
std::vector<std::string> res;
|
||||||
|
|
||||||
|
auto query{ _countries.find() };
|
||||||
|
utils::forEachQueryResult(query, [&](const Country::pointer& country) {
|
||||||
|
res.push_back(std::string{ country->getName() });
|
||||||
|
});
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -669,8 +737,10 @@ namespace lms::db
|
|||||||
{
|
{
|
||||||
std::vector<std::string> res;
|
std::vector<std::string> res;
|
||||||
|
|
||||||
for (const auto& releaseType : _releaseTypes)
|
auto query{ _releaseTypes.find() };
|
||||||
|
utils::forEachQueryResult(query, [&](const ReleaseType::pointer& releaseType) {
|
||||||
res.push_back(std::string{ releaseType->getName() });
|
res.push_back(std::string{ releaseType->getName() });
|
||||||
|
});
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ namespace lms::db
|
|||||||
_session.mapClass<AuthToken>("auth_token");
|
_session.mapClass<AuthToken>("auth_token");
|
||||||
_session.mapClass<Cluster>("cluster");
|
_session.mapClass<Cluster>("cluster");
|
||||||
_session.mapClass<ClusterType>("cluster_type");
|
_session.mapClass<ClusterType>("cluster_type");
|
||||||
|
_session.mapClass<Country>("country");
|
||||||
_session.mapClass<Directory>("directory");
|
_session.mapClass<Directory>("directory");
|
||||||
_session.mapClass<Image>("image");
|
_session.mapClass<Image>("image");
|
||||||
_session.mapClass<Label>("label");
|
_session.mapClass<Label>("label");
|
||||||
@@ -199,6 +200,9 @@ namespace lms::db
|
|||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS cluster_cluster_type_idx ON cluster(cluster_type_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS cluster_cluster_type_idx ON cluster(cluster_type_id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS cluster_type_name_idx ON cluster_type(name)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS cluster_type_name_idx ON cluster_type(name)");
|
||||||
|
|
||||||
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS country_id_idx ON country(id)");
|
||||||
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS country_name_idx ON country(name)");
|
||||||
|
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS directory_id_idx ON directory(id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS directory_id_idx ON directory(id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS directory_parent_directory_idx ON directory(parent_directory_id)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS directory_parent_directory_idx ON directory(parent_directory_id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS directory_path_idx ON directory(absolute_path)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS directory_path_idx ON directory(absolute_path)");
|
||||||
@@ -210,6 +214,7 @@ namespace lms::db
|
|||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS image_path_idx ON image(absolute_file_path)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS image_path_idx ON image(absolute_file_path)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS image_stem_idx ON image(stem COLLATE NOCASE)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS image_stem_idx ON image(stem COLLATE NOCASE)");
|
||||||
|
|
||||||
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS label_id_idx ON label(id)");
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS label_name_idx ON label(name)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS label_name_idx ON label(name)");
|
||||||
|
|
||||||
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS listen_backend_idx ON listen(backend)");
|
utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS listen_backend_idx ON listen(backend)");
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "database/IdType.hpp"
|
||||||
|
|
||||||
|
LMS_DECLARE_IDTYPE(CountryId)
|
||||||
@@ -33,6 +33,7 @@
|
|||||||
#include "core/UUID.hpp"
|
#include "core/UUID.hpp"
|
||||||
#include "database/ArtistId.hpp"
|
#include "database/ArtistId.hpp"
|
||||||
#include "database/ClusterId.hpp"
|
#include "database/ClusterId.hpp"
|
||||||
|
#include "database/CountryId.hpp"
|
||||||
#include "database/DirectoryId.hpp"
|
#include "database/DirectoryId.hpp"
|
||||||
#include "database/LabelId.hpp"
|
#include "database/LabelId.hpp"
|
||||||
#include "database/MediaLibraryId.hpp"
|
#include "database/MediaLibraryId.hpp"
|
||||||
@@ -53,6 +54,37 @@ namespace lms::db
|
|||||||
class Track;
|
class Track;
|
||||||
class User;
|
class User;
|
||||||
|
|
||||||
|
class Country final : public Object<Country, CountryId>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Country() = default;
|
||||||
|
|
||||||
|
static std::size_t getCount(Session& session);
|
||||||
|
static pointer find(Session& session, CountryId id);
|
||||||
|
static pointer find(Session& session, std::string_view name);
|
||||||
|
static RangeResults<CountryId> findOrphanIds(Session& session, std::optional<Range> range = std::nullopt);
|
||||||
|
|
||||||
|
// Accessors
|
||||||
|
std::string_view getName() const { return _name; }
|
||||||
|
|
||||||
|
template<class Action>
|
||||||
|
void persist(Action& a)
|
||||||
|
{
|
||||||
|
Wt::Dbo::field(a, _name, "name");
|
||||||
|
Wt::Dbo::hasMany(a, _releases, Wt::Dbo::ManyToMany, "release_country", "", Wt::Dbo::OnDeleteCascade);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr std::size_t _maxNameLength{ 32 };
|
||||||
|
|
||||||
|
friend class Session;
|
||||||
|
Country(std::string_view name);
|
||||||
|
static pointer create(Session& session, std::string_view name);
|
||||||
|
|
||||||
|
std::string _name;
|
||||||
|
Wt::Dbo::collection<Wt::Dbo::ptr<Release>> _releases; // releases that match this country
|
||||||
|
};
|
||||||
|
|
||||||
class Label final : public Object<Label, LabelId>
|
class Label final : public Object<Label, LabelId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -251,6 +283,7 @@ namespace lms::db
|
|||||||
std::size_t getTrackCount() const;
|
std::size_t getTrackCount() const;
|
||||||
std::vector<ObjectPtr<ReleaseType>> getReleaseTypes() const;
|
std::vector<ObjectPtr<ReleaseType>> getReleaseTypes() const;
|
||||||
std::vector<std::string> getLabelNames() const;
|
std::vector<std::string> getLabelNames() const;
|
||||||
|
std::vector<std::string> getCountryNames() const;
|
||||||
std::vector<std::string> getReleaseTypeNames() const;
|
std::vector<std::string> getReleaseTypeNames() const;
|
||||||
void visitLabels(const std::function<void(const Label::pointer& label)>& _func) const;
|
void visitLabels(const std::function<void(const Label::pointer& label)>& _func) const;
|
||||||
core::EnumSet<Advisory> getAdvisories() const;
|
core::EnumSet<Advisory> getAdvisories() const;
|
||||||
@@ -267,8 +300,10 @@ namespace lms::db
|
|||||||
void setArtistDisplayName(std::string_view name) { _artistDisplayName = name; }
|
void setArtistDisplayName(std::string_view name) { _artistDisplayName = name; }
|
||||||
void setCompilation(bool value) { _isCompilation = value; }
|
void setCompilation(bool value) { _isCompilation = value; }
|
||||||
void clearLabels();
|
void clearLabels();
|
||||||
|
void clearCountries();
|
||||||
void clearReleaseTypes();
|
void clearReleaseTypes();
|
||||||
void addLabel(ObjectPtr<Label> releaseType);
|
void addLabel(ObjectPtr<Label> label);
|
||||||
|
void addCountry(ObjectPtr<Country> country);
|
||||||
void addReleaseType(ObjectPtr<ReleaseType> releaseType);
|
void addReleaseType(ObjectPtr<ReleaseType> releaseType);
|
||||||
void setBarcode(std::string_view barcode) { _barcode = barcode; }
|
void setBarcode(std::string_view barcode) { _barcode = barcode; }
|
||||||
void setComment(std::string_view comment) { _comment = comment; }
|
void setComment(std::string_view comment) { _comment = comment; }
|
||||||
@@ -299,6 +334,7 @@ namespace lms::db
|
|||||||
Wt::Dbo::belongsTo(a, _image, "image", Wt::Dbo::OnDeleteSetNull);
|
Wt::Dbo::belongsTo(a, _image, "image", Wt::Dbo::OnDeleteSetNull);
|
||||||
Wt::Dbo::hasMany(a, _labels, Wt::Dbo::ManyToMany, "release_label", "", Wt::Dbo::OnDeleteCascade);
|
Wt::Dbo::hasMany(a, _labels, Wt::Dbo::ManyToMany, "release_label", "", Wt::Dbo::OnDeleteCascade);
|
||||||
Wt::Dbo::hasMany(a, _releaseTypes, Wt::Dbo::ManyToMany, "release_release_type", "", Wt::Dbo::OnDeleteCascade);
|
Wt::Dbo::hasMany(a, _releaseTypes, Wt::Dbo::ManyToMany, "release_release_type", "", Wt::Dbo::OnDeleteCascade);
|
||||||
|
Wt::Dbo::hasMany(a, _countries, Wt::Dbo::ManyToMany, "release_country", "", Wt::Dbo::OnDeleteCascade);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -325,6 +361,7 @@ namespace lms::db
|
|||||||
Wt::Dbo::collection<Wt::Dbo::ptr<Track>> _tracks;
|
Wt::Dbo::collection<Wt::Dbo::ptr<Track>> _tracks;
|
||||||
Wt::Dbo::collection<Wt::Dbo::ptr<Label>> _labels;
|
Wt::Dbo::collection<Wt::Dbo::ptr<Label>> _labels;
|
||||||
Wt::Dbo::collection<Wt::Dbo::ptr<ReleaseType>> _releaseTypes;
|
Wt::Dbo::collection<Wt::Dbo::ptr<ReleaseType>> _releaseTypes;
|
||||||
|
Wt::Dbo::collection<Wt::Dbo::ptr<Country>> _countries;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace lms::db
|
} // namespace lms::db
|
||||||
|
|||||||
@@ -340,6 +340,7 @@ VALUES
|
|||||||
|
|
||||||
EXPECT_FALSE(Artist::find(session, ArtistId{}));
|
EXPECT_FALSE(Artist::find(session, ArtistId{}));
|
||||||
EXPECT_FALSE(AuthToken::find(session, AuthTokenId{}));
|
EXPECT_FALSE(AuthToken::find(session, AuthTokenId{}));
|
||||||
|
EXPECT_FALSE(Country::find(session, CountryId{}));
|
||||||
EXPECT_FALSE(Cluster::find(session, ClusterId{}));
|
EXPECT_FALSE(Cluster::find(session, ClusterId{}));
|
||||||
EXPECT_FALSE(ClusterType::find(session, ClusterTypeId{}));
|
EXPECT_FALSE(ClusterType::find(session, ClusterTypeId{}));
|
||||||
EXPECT_FALSE(Directory::find(session, DirectoryId{}));
|
EXPECT_FALSE(Directory::find(session, DirectoryId{}));
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ namespace lms::db::tests
|
|||||||
{
|
{
|
||||||
using ScopedImage = ScopedEntity<db::Image>;
|
using ScopedImage = ScopedEntity<db::Image>;
|
||||||
using ScopedLabel = ScopedEntity<db::Label>;
|
using ScopedLabel = ScopedEntity<db::Label>;
|
||||||
|
using ScopedCountry = ScopedEntity<db::Country>;
|
||||||
using ScopedReleaseType = ScopedEntity<db::ReleaseType>;
|
using ScopedReleaseType = ScopedEntity<db::ReleaseType>;
|
||||||
|
|
||||||
TEST_F(DatabaseFixture, Release)
|
TEST_F(DatabaseFixture, Release)
|
||||||
@@ -808,6 +809,30 @@ namespace lms::db::tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DatabaseFixture, Release_getLabelNames)
|
||||||
|
{
|
||||||
|
ScopedRelease release{ session, "MyRelease" };
|
||||||
|
ScopedLabel label{ session, "MyLabel" };
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createReadTransaction() };
|
||||||
|
const auto names{ release.get()->getLabelNames() };
|
||||||
|
EXPECT_EQ(names.size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createWriteTransaction() };
|
||||||
|
release.get().modify()->addLabel(label.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createReadTransaction() };
|
||||||
|
const auto names{ release.get()->getLabelNames() };
|
||||||
|
ASSERT_EQ(names.size(), 1);
|
||||||
|
EXPECT_EQ(names[0], "MyLabel");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(DatabaseFixture, Label_orphan)
|
TEST_F(DatabaseFixture, Label_orphan)
|
||||||
{
|
{
|
||||||
ScopedLabel label{ session, "MyLabel" };
|
ScopedLabel label{ session, "MyLabel" };
|
||||||
@@ -845,6 +870,78 @@ namespace lms::db::tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DatabaseFixture, Country)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
auto transaction{ session.createReadTransaction() };
|
||||||
|
Country::pointer res{ Country::find(session, "country") };
|
||||||
|
EXPECT_EQ(res, Country::pointer{});
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedCountry country{ session, "MyCountry" };
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createReadTransaction() };
|
||||||
|
Country::pointer res{ Country::find(session, "MyCountry") };
|
||||||
|
EXPECT_EQ(res, country.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DatabaseFixture, Release_getCountryNames)
|
||||||
|
{
|
||||||
|
ScopedCountry country{ session, "MyCountry" };
|
||||||
|
ScopedRelease release{ session, "MyRelease" };
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createWriteTransaction() };
|
||||||
|
release.get().modify()->addCountry(country.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createReadTransaction() };
|
||||||
|
const auto names{ release.get()->getCountryNames() };
|
||||||
|
ASSERT_EQ(names.size(), 1);
|
||||||
|
EXPECT_EQ(names[0], "MyCountry");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DatabaseFixture, Country_orphan)
|
||||||
|
{
|
||||||
|
ScopedCountry country{ session, "MyCountry" };
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createReadTransaction() };
|
||||||
|
auto countries{ Country::findOrphanIds(session) };
|
||||||
|
ASSERT_EQ(countries.results.size(), 1);
|
||||||
|
EXPECT_EQ(countries.results.front(), country.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedRelease release{ session, "MyRelease" };
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createWriteTransaction() };
|
||||||
|
release.get().modify()->addCountry(country.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createReadTransaction() };
|
||||||
|
auto countries{ Country::findOrphanIds(session) };
|
||||||
|
EXPECT_EQ(countries.results.size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createWriteTransaction() };
|
||||||
|
release.get().modify()->clearCountries();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction{ session.createReadTransaction() };
|
||||||
|
auto countries{ Country::findOrphanIds(session) };
|
||||||
|
ASSERT_EQ(countries.results.size(), 1);
|
||||||
|
EXPECT_EQ(countries.results.front(), country.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(DatabaseFixture, ReleaseType)
|
TEST_F(DatabaseFixture, ReleaseType)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -521,6 +521,7 @@ namespace lms::metadata
|
|||||||
release->barcode = getTagValueAs<std::string>(tagReader, TagType::Barcode).value_or("");
|
release->barcode = getTagValueAs<std::string>(tagReader, TagType::Barcode).value_or("");
|
||||||
release->labels = getTagValuesAs<std::string>(tagReader, TagType::RecordLabel, _defaultTagDelimiters);
|
release->labels = getTagValuesAs<std::string>(tagReader, TagType::RecordLabel, _defaultTagDelimiters);
|
||||||
release->comment = getTagValueAs<std::string>(tagReader, TagType::AlbumComment).value_or("");
|
release->comment = getTagValueAs<std::string>(tagReader, TagType::AlbumComment).value_or("");
|
||||||
|
release->countries = getTagValuesAs<std::string>(tagReader, TagType::ReleaseCountry, _defaultTagDelimiters);
|
||||||
if (!release->mediumCount)
|
if (!release->mediumCount)
|
||||||
{
|
{
|
||||||
// mediumCount may be encoded as "position/count"
|
// mediumCount may be encoded as "position/count"
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ namespace lms::metadata
|
|||||||
bool isCompilation{};
|
bool isCompilation{};
|
||||||
std::string barcode;
|
std::string barcode;
|
||||||
std::string comment;
|
std::string comment;
|
||||||
|
std::vector<std::string> countries;
|
||||||
|
|
||||||
auto operator<=>(const Release&) const = default;
|
auto operator<=>(const Release&) const = default;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ namespace lms::metadata
|
|||||||
{ TagType::Producer, { "MyProducer1", "MyProducer2" } },
|
{ TagType::Producer, { "MyProducer1", "MyProducer2" } },
|
||||||
{ TagType::Remixer, { "MyRemixer1", "MyRemixer2" } },
|
{ TagType::Remixer, { "MyRemixer1", "MyRemixer2" } },
|
||||||
{ TagType::RecordLabel, { "Label1", "Label2" } },
|
{ TagType::RecordLabel, { "Label1", "Label2" } },
|
||||||
|
{ TagType::ReleaseCountry, { "MyCountry1", "MyCountry2" } },
|
||||||
{ TagType::Language, { "Language1", "Language2" } },
|
{ TagType::Language, { "Language1", "Language2" } },
|
||||||
{ TagType::Lyricist, { "MyLyricist1", "MyLyricist2" } },
|
{ TagType::Lyricist, { "MyLyricist1", "MyLyricist2" } },
|
||||||
{ TagType::OriginalReleaseDate, { "2019/02/03" } },
|
{ TagType::OriginalReleaseDate, { "2019/02/03" } },
|
||||||
@@ -224,6 +225,9 @@ namespace lms::metadata
|
|||||||
EXPECT_EQ(release.name, "MyAlbum");
|
EXPECT_EQ(release.name, "MyAlbum");
|
||||||
EXPECT_EQ(release.sortName, "MyAlbumSortName");
|
EXPECT_EQ(release.sortName, "MyAlbumSortName");
|
||||||
EXPECT_EQ(release.comment, "MyAlbumComment");
|
EXPECT_EQ(release.comment, "MyAlbumComment");
|
||||||
|
ASSERT_EQ(release.countries.size(), 2);
|
||||||
|
EXPECT_EQ(release.countries[0], "MyCountry1");
|
||||||
|
EXPECT_EQ(release.countries[1], "MyCountry2");
|
||||||
{
|
{
|
||||||
std::vector<std::string> expectedReleaseTypes{ "Album", "Compilation" };
|
std::vector<std::string> expectedReleaseTypes{ "Album", "Compilation" };
|
||||||
EXPECT_EQ(release.releaseTypes, expectedReleaseTypes);
|
EXPECT_EQ(release.releaseTypes, expectedReleaseTypes);
|
||||||
|
|||||||
@@ -141,6 +141,15 @@ namespace lms::scanner
|
|||||||
return releaseType;
|
return releaseType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
db::Country::pointer getOrCreateCountry(db::Session& session, std::string_view name)
|
||||||
|
{
|
||||||
|
db::Country::pointer country{ db::Country::find(session, name) };
|
||||||
|
if (!country)
|
||||||
|
country = session.create<db::Country>(name);
|
||||||
|
|
||||||
|
return country;
|
||||||
|
}
|
||||||
|
|
||||||
db::Label::pointer getOrCreateLabel(db::Session& session, std::string_view name)
|
db::Label::pointer getOrCreateLabel(db::Session& session, std::string_view name)
|
||||||
{
|
{
|
||||||
db::Label::pointer label{ db::Label::find(session, name) };
|
db::Label::pointer label{ db::Label::find(session, name) };
|
||||||
@@ -174,7 +183,12 @@ namespace lms::scanner
|
|||||||
for (std::string_view releaseType : releaseInfo.releaseTypes)
|
for (std::string_view releaseType : releaseInfo.releaseTypes)
|
||||||
release.modify()->addReleaseType(getOrCreateReleaseType(session, releaseType));
|
release.modify()->addReleaseType(getOrCreateReleaseType(session, releaseType));
|
||||||
}
|
}
|
||||||
|
if (release->getCountryNames() != releaseInfo.countries)
|
||||||
|
{
|
||||||
|
release.modify()->clearCountries();
|
||||||
|
for (std::string_view country : releaseInfo.countries)
|
||||||
|
release.modify()->addCountry(getOrCreateCountry(session, country));
|
||||||
|
}
|
||||||
if (release->getLabelNames() != releaseInfo.labels)
|
if (release->getLabelNames() != releaseInfo.labels)
|
||||||
{
|
{
|
||||||
release.modify()->clearLabels();
|
release.modify()->clearLabels();
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ namespace lms::scanner
|
|||||||
removeOrphanedReleases(context);
|
removeOrphanedReleases(context);
|
||||||
removeOrphanedReleaseTypes(context);
|
removeOrphanedReleaseTypes(context);
|
||||||
removeOrphanedLabels(context);
|
removeOrphanedLabels(context);
|
||||||
|
removeOrphanedCountries(context);
|
||||||
removeOrphanedDirectories(context);
|
removeOrphanedDirectories(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,6 +78,12 @@ namespace lms::scanner
|
|||||||
removeOrphanedEntries<db::Label>(context);
|
removeOrphanedEntries<db::Label>(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScanStepRemoveOrphanedDbEntries::removeOrphanedCountries(ScanContext& context)
|
||||||
|
{
|
||||||
|
LMS_LOG(DBUPDATER, DEBUG, "Checking orphaned countries...");
|
||||||
|
removeOrphanedEntries<db::Country>(context);
|
||||||
|
}
|
||||||
|
|
||||||
void ScanStepRemoveOrphanedDbEntries::removeOrphanedDirectories(ScanContext& context)
|
void ScanStepRemoveOrphanedDbEntries::removeOrphanedDirectories(ScanContext& context)
|
||||||
{
|
{
|
||||||
LMS_LOG(DBUPDATER, DEBUG, "Checking orphaned directories...");
|
LMS_LOG(DBUPDATER, DEBUG, "Checking orphaned directories...");
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ namespace lms::scanner
|
|||||||
void removeOrphanedReleases(ScanContext& context);
|
void removeOrphanedReleases(ScanContext& context);
|
||||||
void removeOrphanedReleaseTypes(ScanContext& context);
|
void removeOrphanedReleaseTypes(ScanContext& context);
|
||||||
void removeOrphanedLabels(ScanContext& context);
|
void removeOrphanedLabels(ScanContext& context);
|
||||||
|
void removeOrphanedCountries(ScanContext& context);
|
||||||
void removeOrphanedDirectories(ScanContext& context);
|
void removeOrphanedDirectories(ScanContext& context);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ namespace lms::metadata
|
|||||||
os << " '" << release.sortName << "'";
|
os << " '" << release.sortName << "'";
|
||||||
os << std::endl;
|
os << std::endl;
|
||||||
|
|
||||||
for (std::string_view label : release.labels)
|
for (std::string_view releaseType : release.releaseTypes)
|
||||||
std::cout << "\tLabel: " << label << std::endl;
|
std::cout << "\tRelease type: " << releaseType << std::endl;
|
||||||
|
|
||||||
if (release.mbid)
|
if (release.mbid)
|
||||||
os << "\tRelease MBID = " << release.mbid->getAsString() << std::endl;
|
os << "\tRelease MBID = " << release.mbid->getAsString() << std::endl;
|
||||||
@@ -96,6 +96,12 @@ namespace lms::metadata
|
|||||||
|
|
||||||
std::cout << "\tIsCompilation: " << std::boolalpha << release.isCompilation << std::endl;
|
std::cout << "\tIsCompilation: " << std::boolalpha << release.isCompilation << std::endl;
|
||||||
|
|
||||||
|
for (std::string_view label : release.labels)
|
||||||
|
std::cout << "\tLabel: " << label << std::endl;
|
||||||
|
|
||||||
|
for (std::string_view country : release.countries)
|
||||||
|
std::cout << "\tCountry: " << country << std::endl;
|
||||||
|
|
||||||
if (!release.barcode.empty())
|
if (!release.barcode.empty())
|
||||||
std::cout << "\tBarcode: " << release.barcode << std::endl;
|
std::cout << "\tBarcode: " << release.barcode << std::endl;
|
||||||
|
|
||||||
@@ -108,9 +114,6 @@ namespace lms::metadata
|
|||||||
for (const Artist& artist : release.artists)
|
for (const Artist& artist : release.artists)
|
||||||
std::cout << "\tRelease artist: " << artist << std::endl;
|
std::cout << "\tRelease artist: " << artist << std::endl;
|
||||||
|
|
||||||
for (std::string_view releaseType : release.releaseTypes)
|
|
||||||
std::cout << "\tRelease type: " << releaseType << std::endl;
|
|
||||||
|
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user