Added support for explcit tags (rtng, ITUNESADVISORY)
This commit is contained in:
@@ -35,7 +35,7 @@ namespace lms::db
|
|||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
static constexpr Version LMS_DATABASE_VERSION{ 78 };
|
static constexpr Version LMS_DATABASE_VERSION{ 79 };
|
||||||
}
|
}
|
||||||
|
|
||||||
VersionInfo::VersionInfo()
|
VersionInfo::VersionInfo()
|
||||||
@@ -1035,6 +1035,15 @@ FROM tracklist)");
|
|||||||
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings ADD COLUMN skip_single_release_playlists BOOLEAN NOT NULL DEFAULT(FALSE)");
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings ADD COLUMN skip_single_release_playlists BOOLEAN NOT NULL DEFAULT(FALSE)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void migrateFromV78(Session& session)
|
||||||
|
{
|
||||||
|
// added advisory tag support
|
||||||
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD COLUMN advisory INTEGER NOT NULL DEFAULT(0)"); // 0 means unset
|
||||||
|
|
||||||
|
// 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)" };
|
||||||
@@ -1089,6 +1098,7 @@ FROM tracklist)");
|
|||||||
{ 75, migrateFromV75 },
|
{ 75, migrateFromV75 },
|
||||||
{ 76, migrateFromV76 },
|
{ 76, migrateFromV76 },
|
||||||
{ 77, migrateFromV77 },
|
{ 77, migrateFromV77 },
|
||||||
|
{ 78, migrateFromV78 },
|
||||||
};
|
};
|
||||||
|
|
||||||
bool migrationPerformed{};
|
bool migrationPerformed{};
|
||||||
|
|||||||
@@ -666,6 +666,19 @@ namespace lms::db
|
|||||||
utils::forEachQueryResult(query, _func);
|
utils::forEachQueryResult(query, _func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
core::EnumSet<Advisory> Release::getAdvisories() const
|
||||||
|
{
|
||||||
|
core::EnumSet<Advisory> res;
|
||||||
|
|
||||||
|
auto query{ session()->query<Advisory>("SELECT DISTINCT advisory FROM track t").where("t.release_id = ?").bind(getId()) };
|
||||||
|
|
||||||
|
utils::forEachQueryResult(query, [&](Advisory advisory) {
|
||||||
|
res.insert(advisory);
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
std::chrono::milliseconds Release::getDuration() const
|
std::chrono::milliseconds Release::getDuration() const
|
||||||
{
|
{
|
||||||
assert(session());
|
assert(session());
|
||||||
|
|||||||
@@ -252,6 +252,7 @@ namespace lms::db
|
|||||||
std::vector<std::string> getLabelNames() const;
|
std::vector<std::string> getLabelNames() 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;
|
||||||
std::string_view getBarcode() const { return _barcode; }
|
std::string_view getBarcode() const { return _barcode; }
|
||||||
ObjectPtr<Image> getImage() const;
|
ObjectPtr<Image> getImage() const;
|
||||||
|
|
||||||
|
|||||||
@@ -193,12 +193,6 @@ namespace lms::db
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PathResult
|
|
||||||
{
|
|
||||||
TrackId trackId;
|
|
||||||
std::filesystem::path path;
|
|
||||||
};
|
|
||||||
|
|
||||||
Track() = default;
|
Track() = default;
|
||||||
|
|
||||||
// Find utility functions
|
// Find utility functions
|
||||||
@@ -244,6 +238,7 @@ namespace lms::db
|
|||||||
void setRecordingMBID(const std::optional<core::UUID>& MBID) { _recordingMBID = MBID ? MBID->getAsString() : ""; }
|
void setRecordingMBID(const std::optional<core::UUID>& MBID) { _recordingMBID = MBID ? MBID->getAsString() : ""; }
|
||||||
void setCopyright(std::string_view copyright);
|
void setCopyright(std::string_view copyright);
|
||||||
void setCopyrightURL(std::string_view copyrightURL);
|
void setCopyrightURL(std::string_view copyrightURL);
|
||||||
|
void setAdvisory(Advisory advisory) { _advisory = advisory; }
|
||||||
void setTrackReplayGain(std::optional<float> replayGain) { _trackReplayGain = replayGain; }
|
void setTrackReplayGain(std::optional<float> replayGain) { _trackReplayGain = replayGain; }
|
||||||
void setReleaseReplayGain(std::optional<float> replayGain) { _releaseReplayGain = replayGain; } // may be by disc!
|
void setReleaseReplayGain(std::optional<float> replayGain) { _releaseReplayGain = replayGain; } // may be by disc!
|
||||||
void setArtistDisplayName(std::string_view name) { _artistDisplayName = name; }
|
void setArtistDisplayName(std::string_view name) { _artistDisplayName = name; }
|
||||||
@@ -285,6 +280,7 @@ namespace lms::db
|
|||||||
std::optional<core::UUID> getRecordingMBID() const { return core::UUID::fromString(_recordingMBID); }
|
std::optional<core::UUID> getRecordingMBID() const { return core::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;
|
||||||
|
Advisory getAdvisory() const { return _advisory; }
|
||||||
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; }
|
||||||
std::string_view getArtistDisplayName() const { return _artistDisplayName; }
|
std::string_view getArtistDisplayName() const { return _artistDisplayName; }
|
||||||
@@ -333,6 +329,7 @@ namespace lms::db
|
|||||||
Wt::Dbo::field(a, _recordingMBID, "recording_mbid");
|
Wt::Dbo::field(a, _recordingMBID, "recording_mbid");
|
||||||
Wt::Dbo::field(a, _copyright, "copyright");
|
Wt::Dbo::field(a, _copyright, "copyright");
|
||||||
Wt::Dbo::field(a, _copyrightURL, "copyright_url");
|
Wt::Dbo::field(a, _copyrightURL, "copyright_url");
|
||||||
|
Wt::Dbo::field(a, _advisory, "advisory");
|
||||||
Wt::Dbo::field(a, _trackReplayGain, "track_replay_gain");
|
Wt::Dbo::field(a, _trackReplayGain, "track_replay_gain");
|
||||||
Wt::Dbo::field(a, _releaseReplayGain, "release_replay_gain"); // here in Track since Release does not have concept of "disc" (yet?)
|
Wt::Dbo::field(a, _releaseReplayGain, "release_replay_gain"); // here in Track since Release does not have concept of "disc" (yet?)
|
||||||
Wt::Dbo::field(a, _artistDisplayName, "artist_display_name");
|
Wt::Dbo::field(a, _artistDisplayName, "artist_display_name");
|
||||||
@@ -381,11 +378,11 @@ namespace lms::db
|
|||||||
std::string _recordingMBID;
|
std::string _recordingMBID;
|
||||||
std::string _copyright;
|
std::string _copyright;
|
||||||
std::string _copyrightURL;
|
std::string _copyrightURL;
|
||||||
|
Advisory _advisory{ Advisory::UnSet };
|
||||||
std::optional<float> _trackReplayGain;
|
std::optional<float> _trackReplayGain;
|
||||||
std::optional<float> _releaseReplayGain;
|
std::optional<float> _releaseReplayGain;
|
||||||
std::string _artistDisplayName;
|
std::string _artistDisplayName;
|
||||||
std::string _comment;
|
std::string _comment;
|
||||||
|
|
||||||
Wt::Dbo::ptr<Release> _release;
|
Wt::Dbo::ptr<Release> _release;
|
||||||
Wt::Dbo::ptr<MediaLibrary> _mediaLibrary;
|
Wt::Dbo::ptr<MediaLibrary> _mediaLibrary;
|
||||||
Wt::Dbo::ptr<Directory> _directory;
|
Wt::Dbo::ptr<Directory> _directory;
|
||||||
|
|||||||
@@ -256,4 +256,12 @@ namespace lms::db
|
|||||||
PlayList = 0, // user controlled playlists
|
PlayList = 0, // user controlled playlists
|
||||||
Internal = 1, // internal usage (current playqueue, history, ...)
|
Internal = 1, // internal usage (current playqueue, history, ...)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Advisory
|
||||||
|
{
|
||||||
|
UnSet = 0,
|
||||||
|
Unknown = 1,
|
||||||
|
Clean = 2,
|
||||||
|
Explicit = 3,
|
||||||
|
};
|
||||||
} // namespace lms::db
|
} // namespace lms::db
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ namespace lms::metadata
|
|||||||
// Mapping to internal avformat names and/or common alternative custom names
|
// Mapping to internal avformat names and/or common alternative custom names
|
||||||
static const std::unordered_map<TagType, std::vector<std::string>> tagMapping{
|
static const std::unordered_map<TagType, std::vector<std::string>> tagMapping{
|
||||||
{ TagType::AcoustID, { "ACOUSTID_ID", "ACOUSTID ID" } },
|
{ TagType::AcoustID, { "ACOUSTID_ID", "ACOUSTID ID" } },
|
||||||
|
{ TagType::Advisory, { "ITUNESADVISORY" } },
|
||||||
{ TagType::Album, { "ALBUM", "TALB", "WM/ALBUMTITLE" } },
|
{ TagType::Album, { "ALBUM", "TALB", "WM/ALBUMTITLE" } },
|
||||||
{ TagType::AlbumArtist, { "ALBUMARTIST", "ALBUM_ARTIST" } },
|
{ TagType::AlbumArtist, { "ALBUMARTIST", "ALBUM_ARTIST" } },
|
||||||
{ TagType::AlbumArtistSortOrder, { "ALBUMARTISTSORT", "TSO2" } },
|
{ TagType::AlbumArtistSortOrder, { "ALBUMARTISTSORT", "TSO2" } },
|
||||||
|
|||||||
@@ -26,12 +26,13 @@
|
|||||||
|
|
||||||
namespace lms::metadata
|
namespace lms::metadata
|
||||||
{
|
{
|
||||||
// using picard internal names
|
// prefer using picard internal names
|
||||||
// see https://picard-docs.musicbrainz.org/en/appendices/tag_mapping.html
|
// see https://picard-docs.musicbrainz.org/en/appendices/tag_mapping.html
|
||||||
enum class TagType
|
enum class TagType
|
||||||
{
|
{
|
||||||
AcoustID,
|
AcoustID,
|
||||||
AcoustIDFingerprint,
|
AcoustIDFingerprint,
|
||||||
|
Advisory, // non standard
|
||||||
Album,
|
Album,
|
||||||
AlbumArtist,
|
AlbumArtist,
|
||||||
AlbumArtists, // non standard
|
AlbumArtists, // non standard
|
||||||
|
|||||||
@@ -246,6 +246,25 @@ namespace lms::metadata
|
|||||||
|
|
||||||
return artistDisplayName;
|
return artistDisplayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<Track::Advisory> getAdvisory(const ITagReader& tagReader)
|
||||||
|
{
|
||||||
|
if (const auto value{ getTagValueAs<int>(tagReader, TagType::Advisory) })
|
||||||
|
{
|
||||||
|
switch (*value)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
case 4:
|
||||||
|
return Track::Advisory::Explicit;
|
||||||
|
case 2:
|
||||||
|
return Track::Advisory::Clean;
|
||||||
|
case 0:
|
||||||
|
return Track::Advisory::Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::unique_ptr<IParser> createParser(ParserBackend parserBackend, ParserReadStyle parserReadStyle)
|
std::unique_ptr<IParser> createParser(ParserBackend parserBackend, ParserReadStyle parserReadStyle)
|
||||||
@@ -370,6 +389,7 @@ namespace lms::metadata
|
|||||||
track.originalYear = utils::parseYear(*dateStr);
|
track.originalYear = utils::parseYear(*dateStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
track.advisory = getAdvisory(tagReader);
|
||||||
track.lyrics = getLyrics(tagReader); // no custom delimiter on lyrics
|
track.lyrics = getLyrics(tagReader); // no custom delimiter on lyrics
|
||||||
track.comments = getTagValuesAs<std::string>(tagReader, TagType::Comment, {} /* no custom delimiter on comments */);
|
track.comments = getTagValuesAs<std::string>(tagReader, TagType::Comment, {} /* no custom delimiter on comments */);
|
||||||
track.copyright = getTagValueAs<std::string>(tagReader, TagType::Copyright).value_or("");
|
track.copyright = getTagValueAs<std::string>(tagReader, TagType::Copyright).value_or("");
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ namespace lms::metadata
|
|||||||
// Mapping to internal taglib names and/or common alternative custom names
|
// Mapping to internal taglib names and/or common alternative custom names
|
||||||
const std::unordered_map<TagType, std::vector<std::string>> tagMapping{
|
const std::unordered_map<TagType, std::vector<std::string>> tagMapping{
|
||||||
{ TagType::AcoustID, { "ACOUSTID_ID", "ACOUSTID ID" } },
|
{ TagType::AcoustID, { "ACOUSTID_ID", "ACOUSTID ID" } },
|
||||||
|
{ TagType::Advisory, { "ITUNESADVISORY" } },
|
||||||
{ TagType::Album, { "ALBUM" } },
|
{ TagType::Album, { "ALBUM" } },
|
||||||
{ TagType::AlbumArtist, { "ALBUMARTIST" } },
|
{ TagType::AlbumArtist, { "ALBUMARTIST" } },
|
||||||
{ TagType::AlbumArtistSortOrder, { "ALBUMARTISTSORT" } },
|
{ TagType::AlbumArtistSortOrder, { "ALBUMARTISTSORT" } },
|
||||||
@@ -319,10 +320,18 @@ namespace lms::metadata
|
|||||||
// MP4
|
// MP4
|
||||||
else if (TagLib::MP4::File * mp4File{ dynamic_cast<TagLib::MP4::File*>(_file.file()) })
|
else if (TagLib::MP4::File * mp4File{ dynamic_cast<TagLib::MP4::File*>(_file.file()) })
|
||||||
{
|
{
|
||||||
TagLib::MP4::Item coverItem{ mp4File->tag()->item("covr") };
|
if (const TagLib::MP4::Item coverItem{ mp4File->tag()->item("covr") }; coverItem.isValid())
|
||||||
TagLib::MP4::CoverArtList coverArtList{ coverItem.toCoverArtList() };
|
{
|
||||||
if (!coverArtList.isEmpty())
|
if (coverItem.type() == TagLib::MP4::Item::Type::CoverArtList)
|
||||||
_hasEmbeddedCover = true;
|
_hasEmbeddedCover = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Taglib does not expose rtng in properties
|
||||||
|
if (const TagLib::MP4::Item rtngItem{ mp4File->tag()->item("rtng") }; rtngItem.isValid())
|
||||||
|
{
|
||||||
|
if (rtngItem.type() == TagLib::MP4::Item::Type::Byte)
|
||||||
|
_propertyMap["ITUNESADVISORY"] = TagLib::String{ std::to_string(rtngItem.toByte()) };
|
||||||
|
}
|
||||||
|
|
||||||
if (!_propertyMap.contains("ORIGINALDATE"))
|
if (!_propertyMap.contains("ORIGINALDATE"))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -102,6 +102,12 @@ namespace lms::metadata
|
|||||||
|
|
||||||
struct Track
|
struct Track
|
||||||
{
|
{
|
||||||
|
enum class Advisory
|
||||||
|
{
|
||||||
|
Unknown,
|
||||||
|
Explicit,
|
||||||
|
Clean,
|
||||||
|
};
|
||||||
AudioProperties audioProperties;
|
AudioProperties audioProperties;
|
||||||
std::optional<core::UUID> mbid;
|
std::optional<core::UUID> mbid;
|
||||||
std::optional<core::UUID> recordingMBID;
|
std::optional<core::UUID> recordingMBID;
|
||||||
@@ -117,6 +123,7 @@ namespace lms::metadata
|
|||||||
Wt::WDate date;
|
Wt::WDate date;
|
||||||
std::optional<int> originalYear{};
|
std::optional<int> originalYear{};
|
||||||
Wt::WDate originalDate;
|
Wt::WDate originalDate;
|
||||||
|
std::optional<Advisory> advisory;
|
||||||
bool hasCover{};
|
bool hasCover{};
|
||||||
std::optional<core::UUID> acoustID;
|
std::optional<core::UUID> acoustID;
|
||||||
std::string copyright;
|
std::string copyright;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ namespace lms::metadata
|
|||||||
TestTagReader testTags{
|
TestTagReader testTags{
|
||||||
{
|
{
|
||||||
{ TagType::AcoustID, { "e987a441-e134-4960-8019-274eddacc418" } },
|
{ TagType::AcoustID, { "e987a441-e134-4960-8019-274eddacc418" } },
|
||||||
|
{ TagType::Advisory, { "2" } },
|
||||||
{ TagType::Album, { "MyAlbum" } },
|
{ TagType::Album, { "MyAlbum" } },
|
||||||
{ TagType::AlbumSortOrder, { "MyAlbumSortName" } },
|
{ TagType::AlbumSortOrder, { "MyAlbumSortName" } },
|
||||||
{ TagType::Artist, { "MyArtist1 & MyArtist2" } },
|
{ TagType::Artist, { "MyArtist1 & MyArtist2" } },
|
||||||
@@ -99,6 +100,8 @@ namespace lms::metadata
|
|||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_EQ(track->acoustID, core::UUID::fromString("e987a441-e134-4960-8019-274eddacc418"));
|
EXPECT_EQ(track->acoustID, core::UUID::fromString("e987a441-e134-4960-8019-274eddacc418"));
|
||||||
|
ASSERT_TRUE(track->advisory.has_value());
|
||||||
|
EXPECT_EQ(track->advisory.value(), Track::Advisory::Clean);
|
||||||
EXPECT_EQ(track->artistDisplayName, "MyArtist1 & MyArtist2");
|
EXPECT_EQ(track->artistDisplayName, "MyArtist1 & MyArtist2");
|
||||||
ASSERT_EQ(track->artists.size(), 2);
|
ASSERT_EQ(track->artists.size(), 2);
|
||||||
EXPECT_EQ(track->artists[0].name, "MyArtist1");
|
EXPECT_EQ(track->artists[0].name, "MyArtist1");
|
||||||
@@ -581,4 +584,31 @@ namespace lms::metadata
|
|||||||
EXPECT_EQ(track->artists[1].mbid, std::nullopt);
|
EXPECT_EQ(track->artists[1].mbid, std::nullopt);
|
||||||
EXPECT_EQ(track->artistDisplayName, "Artist1, Artist2"); // reconstruct the artist display name
|
EXPECT_EQ(track->artistDisplayName, "Artist1, Artist2"); // reconstruct the artist display name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Parser, advisory)
|
||||||
|
{
|
||||||
|
auto doTest = [](std::string_view value, std::optional<Track::Advisory> expectedValue) {
|
||||||
|
const TestTagReader testTags{
|
||||||
|
{
|
||||||
|
{ TagType::Advisory, { value } },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Parser parser;
|
||||||
|
std::unique_ptr<Track> track{ Parser{}.parse(testTags) };
|
||||||
|
|
||||||
|
ASSERT_EQ(track->advisory.has_value(), expectedValue.has_value()) << "Value = '" << value << "'";
|
||||||
|
if (track->advisory.has_value())
|
||||||
|
{
|
||||||
|
EXPECT_EQ(track->advisory.value(), expectedValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
doTest("0", Track::Advisory::Unknown);
|
||||||
|
doTest("1", Track::Advisory::Explicit);
|
||||||
|
doTest("4", Track::Advisory::Explicit);
|
||||||
|
doTest("2", Track::Advisory::Clean);
|
||||||
|
doTest("", std::nullopt);
|
||||||
|
doTest("3", std::nullopt);
|
||||||
|
}
|
||||||
} // namespace lms::metadata
|
} // namespace lms::metadata
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#include "database/TrackArtistLink.hpp"
|
#include "database/TrackArtistLink.hpp"
|
||||||
#include "database/TrackFeatures.hpp"
|
#include "database/TrackFeatures.hpp"
|
||||||
#include "database/TrackLyrics.hpp"
|
#include "database/TrackLyrics.hpp"
|
||||||
|
#include "database/Types.hpp"
|
||||||
#include "metadata/Exception.hpp"
|
#include "metadata/Exception.hpp"
|
||||||
#include "metadata/IParser.hpp"
|
#include "metadata/IParser.hpp"
|
||||||
|
|
||||||
@@ -309,6 +310,24 @@ namespace lms::scanner
|
|||||||
return lyrics;
|
return lyrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
db::Advisory getAdvisory(std::optional<metadata::Track::Advisory> advisory)
|
||||||
|
{
|
||||||
|
if (!advisory)
|
||||||
|
return db::Advisory::UnSet;
|
||||||
|
|
||||||
|
switch (advisory.value())
|
||||||
|
{
|
||||||
|
case metadata::Track::Advisory::Clean:
|
||||||
|
return db::Advisory::Clean;
|
||||||
|
case metadata::Track::Advisory::Explicit:
|
||||||
|
return db::Advisory::Explicit;
|
||||||
|
case metadata::Track::Advisory::Unknown:
|
||||||
|
return db::Advisory::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
return db::Advisory::UnSet;
|
||||||
|
}
|
||||||
|
|
||||||
class AudioFileScanOperation : public IFileScanOperation
|
class AudioFileScanOperation : public IFileScanOperation
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -552,6 +571,7 @@ namespace lms::scanner
|
|||||||
track.modify()->setHasCover(_parsedTrack->hasCover);
|
track.modify()->setHasCover(_parsedTrack->hasCover);
|
||||||
track.modify()->setCopyright(_parsedTrack->copyright);
|
track.modify()->setCopyright(_parsedTrack->copyright);
|
||||||
track.modify()->setCopyrightURL(_parsedTrack->copyrightURL);
|
track.modify()->setCopyrightURL(_parsedTrack->copyrightURL);
|
||||||
|
track.modify()->setAdvisory(getAdvisory(_parsedTrack->advisory));
|
||||||
track.modify()->setComment(!_parsedTrack->comments.empty() ? _parsedTrack->comments.front() : ""); // only take the first one for now
|
track.modify()->setComment(!_parsedTrack->comments.empty() ? _parsedTrack->comments.front() : ""); // only take the first one for now
|
||||||
track.modify()->setTrackReplayGain(_parsedTrack->replayGain);
|
track.modify()->setTrackReplayGain(_parsedTrack->replayGain);
|
||||||
track.modify()->setArtistDisplayName(_parsedTrack->artistDisplayName);
|
track.modify()->setArtistDisplayName(_parsedTrack->artistDisplayName);
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "database/Image.hpp"
|
#include "database/Image.hpp"
|
||||||
#include "database/Release.hpp"
|
#include "database/Release.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/Track.hpp"
|
||||||
|
#include "database/Types.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/User.hpp"
|
||||||
#include "services/feedback/IFeedbackService.hpp"
|
#include "services/feedback/IFeedbackService.hpp"
|
||||||
#include "services/scrobbling/IScrobblingService.hpp"
|
#include "services/scrobbling/IScrobblingService.hpp"
|
||||||
@@ -223,6 +224,18 @@ namespace lms::api::subsonic
|
|||||||
albumNode.addArrayChild("recordLabels", createRecordLabel(label));
|
albumNode.addArrayChild("recordLabels", createRecordLabel(label));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
auto advisoryToExplicitStatus = [&](const core::EnumSet<db::Advisory> advisories) -> std::string_view {
|
||||||
|
if (advisories.contains(db::Advisory::Explicit))
|
||||||
|
return "explicit";
|
||||||
|
|
||||||
|
if (advisories.contains(db::Advisory::Clean))
|
||||||
|
return "clean";
|
||||||
|
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
albumNode.setAttribute("explicitStatus", advisoryToExplicitStatus(release->getAdvisories()));
|
||||||
|
|
||||||
return albumNode;
|
return albumNode;
|
||||||
}
|
}
|
||||||
} // namespace lms::api::subsonic
|
} // namespace lms::api::subsonic
|
||||||
@@ -32,6 +32,7 @@
|
|||||||
#include "database/Release.hpp"
|
#include "database/Release.hpp"
|
||||||
#include "database/Track.hpp"
|
#include "database/Track.hpp"
|
||||||
#include "database/TrackArtistLink.hpp"
|
#include "database/TrackArtistLink.hpp"
|
||||||
|
#include "database/Types.hpp"
|
||||||
#include "database/User.hpp"
|
#include "database/User.hpp"
|
||||||
#include "services/feedback/IFeedbackService.hpp"
|
#include "services/feedback/IFeedbackService.hpp"
|
||||||
#include "services/scrobbling/IScrobblingService.hpp"
|
#include "services/scrobbling/IScrobblingService.hpp"
|
||||||
@@ -227,6 +228,22 @@ namespace lms::api::subsonic
|
|||||||
for (const auto& genre : genres)
|
for (const auto& genre : genres)
|
||||||
trackResponse.addArrayChild("genres", createItemGenreNode(genre->getName()));
|
trackResponse.addArrayChild("genres", createItemGenreNode(genre->getName()));
|
||||||
|
|
||||||
|
auto advisoryToExplicitStatus = [](db::Advisory advisory) -> std::string_view {
|
||||||
|
switch (advisory)
|
||||||
|
{
|
||||||
|
case db::Advisory::Clean:
|
||||||
|
return "clean";
|
||||||
|
case db::Advisory::Explicit:
|
||||||
|
return "expicit";
|
||||||
|
case db::Advisory::Unknown:
|
||||||
|
case db::Advisory::UnSet:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
trackResponse.setAttribute("explicitStatus", advisoryToExplicitStatus(track->getAdvisory()));
|
||||||
|
|
||||||
trackResponse.addChild("replayGain", createReplayGainNode(track));
|
trackResponse.addChild("replayGain", createReplayGainNode(track));
|
||||||
|
|
||||||
return trackResponse;
|
return trackResponse;
|
||||||
|
|||||||
@@ -135,6 +135,24 @@ namespace lms::metadata
|
|||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, Track::Advisory advisory)
|
||||||
|
{
|
||||||
|
switch (advisory)
|
||||||
|
{
|
||||||
|
case Track::Advisory::Clean:
|
||||||
|
os << "clean";
|
||||||
|
break;
|
||||||
|
case Track::Advisory::Explicit:
|
||||||
|
os << "Explicit";
|
||||||
|
break;
|
||||||
|
case Track::Advisory::Unknown:
|
||||||
|
os << "Unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
void parse(IParser& parser, const std::filesystem::path& file)
|
void parse(IParser& parser, const std::filesystem::path& file)
|
||||||
{
|
{
|
||||||
using namespace metadata;
|
using namespace metadata;
|
||||||
@@ -250,6 +268,9 @@ namespace lms::metadata
|
|||||||
if (!track->copyrightURL.empty())
|
if (!track->copyrightURL.empty())
|
||||||
std::cout << "CopyrightURL: " << track->copyrightURL << std::endl;
|
std::cout << "CopyrightURL: " << track->copyrightURL << std::endl;
|
||||||
|
|
||||||
|
if (track->advisory)
|
||||||
|
std::cout << "Advisory: " << *track->advisory << std::endl;
|
||||||
|
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
} // namespace lms::metadata
|
} // namespace lms::metadata
|
||||||
|
|||||||
Reference in New Issue
Block a user