Added album sort name in subsonic responses, fixes #419
This commit is contained in:
@@ -30,6 +30,15 @@
|
|||||||
|
|
||||||
namespace Database
|
namespace Database
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
static constexpr Version LMS_DATABASE_VERSION{ 53 };
|
||||||
|
}
|
||||||
|
|
||||||
|
VersionInfo::VersionInfo()
|
||||||
|
: _version{ LMS_DATABASE_VERSION }
|
||||||
|
{}
|
||||||
|
|
||||||
VersionInfo::pointer VersionInfo::getOrCreate(Session& session)
|
VersionInfo::pointer VersionInfo::getOrCreate(Session& session)
|
||||||
{
|
{
|
||||||
session.checkWriteTransaction();
|
session.checkWriteTransaction();
|
||||||
@@ -401,6 +410,16 @@ SELECT
|
|||||||
session.getDboSession().execute("ALTER TABLE scan_settings ADD default_tag_delimiters TEXT NOT NULL DEFAULT ''");
|
session.getDboSession().execute("ALTER TABLE scan_settings ADD default_tag_delimiters TEXT NOT NULL DEFAULT ''");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void migrateFromV52(Session& session)
|
||||||
|
{
|
||||||
|
// Add sort name for releases
|
||||||
|
session.getDboSession().execute("ALTER TABLE release ADD sort_name TEXT NOT NULL DEFAULT ''");
|
||||||
|
|
||||||
|
// Just increment the scan version of the settings to make the next scheduled scan rescan everything
|
||||||
|
session.getDboSession().execute("UPDATE scan_settings SET scan_version = scan_version + 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void doDbMigration(Session& session)
|
void doDbMigration(Session& session)
|
||||||
{
|
{
|
||||||
static const std::string outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
|
static const std::string outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
|
||||||
@@ -430,6 +449,7 @@ SELECT
|
|||||||
{49, migrateFromV49},
|
{49, migrateFromV49},
|
||||||
{50, migrateFromV50},
|
{50, migrateFromV50},
|
||||||
{51, migrateFromV51},
|
{51, migrateFromV51},
|
||||||
|
{52, migrateFromV52},
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,12 +26,14 @@ namespace Database
|
|||||||
class Session;
|
class Session;
|
||||||
|
|
||||||
using Version = std::size_t;
|
using Version = std::size_t;
|
||||||
static constexpr Version LMS_DATABASE_VERSION{ 52 };
|
|
||||||
class VersionInfo
|
class VersionInfo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using pointer = Wt::Dbo::ptr<VersionInfo>;
|
using pointer = Wt::Dbo::ptr<VersionInfo>;
|
||||||
|
|
||||||
|
VersionInfo();
|
||||||
|
|
||||||
static VersionInfo::pointer getOrCreate(Session& session);
|
static VersionInfo::pointer getOrCreate(Session& session);
|
||||||
static VersionInfo::pointer get(Session& session);
|
static VersionInfo::pointer get(Session& session);
|
||||||
|
|
||||||
@@ -45,7 +47,7 @@ namespace Database
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int _version{ LMS_DATABASE_VERSION };
|
int _version;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace Migration
|
namespace Migration
|
||||||
|
|||||||
@@ -143,7 +143,8 @@ namespace Database
|
|||||||
std::size_t getMeanBitrate() const;
|
std::size_t getMeanBitrate() const;
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
const std::string& getName() const { return _name; }
|
std::string_view getName() const { return _name; }
|
||||||
|
std::string_view getSortName() const { return _sortName; }
|
||||||
std::optional<UUID> getMBID() const { return UUID::fromString(_MBID); }
|
std::optional<UUID> getMBID() const { return UUID::fromString(_MBID); }
|
||||||
std::optional<std::size_t> getTotalDisc() const { return _totalDisc; }
|
std::optional<std::size_t> getTotalDisc() const { return _totalDisc; }
|
||||||
std::size_t getDiscCount() const; // may not be total disc (if incomplete for example)
|
std::size_t getDiscCount() const; // may not be total disc (if incomplete for example)
|
||||||
@@ -157,6 +158,7 @@ namespace Database
|
|||||||
|
|
||||||
// Setters
|
// Setters
|
||||||
void setName(std::string_view name) { _name = name; }
|
void setName(std::string_view name) { _name = name; }
|
||||||
|
void setSortName(std::string_view sortName) { _sortName = sortName; }
|
||||||
void setMBID(const std::optional<UUID>& mbid) { _MBID = mbid ? mbid->getAsString() : ""; }
|
void setMBID(const std::optional<UUID>& mbid) { _MBID = mbid ? mbid->getAsString() : ""; }
|
||||||
void setTotalDisc(std::optional<int> totalDisc) { _totalDisc = totalDisc; }
|
void setTotalDisc(std::optional<int> totalDisc) { _totalDisc = totalDisc; }
|
||||||
void setArtistDisplayName(std::string_view name) { _artistDisplayName = name; }
|
void setArtistDisplayName(std::string_view name) { _artistDisplayName = name; }
|
||||||
@@ -173,6 +175,7 @@ namespace Database
|
|||||||
void persist(Action& a)
|
void persist(Action& a)
|
||||||
{
|
{
|
||||||
Wt::Dbo::field(a, _name, "name");
|
Wt::Dbo::field(a, _name, "name");
|
||||||
|
Wt::Dbo::field(a, _sortName, "sort_name");
|
||||||
Wt::Dbo::field(a, _MBID, "mbid");
|
Wt::Dbo::field(a, _MBID, "mbid");
|
||||||
Wt::Dbo::field(a, _totalDisc, "total_disc");
|
Wt::Dbo::field(a, _totalDisc, "total_disc");
|
||||||
Wt::Dbo::field(a, _artistDisplayName, "artist_display_name");
|
Wt::Dbo::field(a, _artistDisplayName, "artist_display_name");
|
||||||
@@ -191,6 +194,7 @@ namespace Database
|
|||||||
static constexpr std::size_t _maxNameLength{ 256 };
|
static constexpr std::size_t _maxNameLength{ 256 };
|
||||||
|
|
||||||
std::string _name;
|
std::string _name;
|
||||||
|
std::string _sortName;
|
||||||
std::string _MBID;
|
std::string _MBID;
|
||||||
std::optional<int> _totalDisc{};
|
std::optional<int> _totalDisc{};
|
||||||
std::string _artistDisplayName;
|
std::string _artistDisplayName;
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ namespace MetaData
|
|||||||
{ TagType::AlbumArtistSortOrder, { "ALBUMARTISTSORT", "TSO2" } },
|
{ TagType::AlbumArtistSortOrder, { "ALBUMARTISTSORT", "TSO2" } },
|
||||||
{ TagType::AlbumArtists, { "ALBUMARTISTS" } },
|
{ TagType::AlbumArtists, { "ALBUMARTISTS" } },
|
||||||
{ TagType::AlbumArtistsSortOrder, { "ALBUMARTISTSSORT" } },
|
{ TagType::AlbumArtistsSortOrder, { "ALBUMARTISTSSORT" } },
|
||||||
{ TagType::AlbumSortOrder, { "ALBUMSORT" } },
|
{ TagType::AlbumSortOrder, { "ALBUMSORT", "ALBUM-SORT" } },
|
||||||
{ TagType::Arranger, { "ARRANGER" } },
|
{ TagType::Arranger, { "ARRANGER" } },
|
||||||
{ TagType::Artist, { "ARTIST" } },
|
{ TagType::Artist, { "ARTIST" } },
|
||||||
{ TagType::ArtistSortOrder, { "ARTISTSORT", "ARTIST-SORT", "WM/ARTISTSORTORDER" } },
|
{ TagType::ArtistSortOrder, { "ARTISTSORT", "ARTIST-SORT", "WM/ARTISTSORTORDER" } },
|
||||||
|
|||||||
@@ -372,6 +372,7 @@ namespace MetaData
|
|||||||
|
|
||||||
release.emplace();
|
release.emplace();
|
||||||
release->name = std::move(*releaseName);
|
release->name = std::move(*releaseName);
|
||||||
|
release->sortName = getTagValueAs<std::string>(tagReader, TagType::AlbumSortOrder).value_or("");
|
||||||
release->artistDisplayName = getTagValueAs<std::string>(tagReader, TagType::AlbumArtist).value_or(""); // TODO try to join albumartists if present
|
release->artistDisplayName = getTagValueAs<std::string>(tagReader, TagType::AlbumArtist).value_or(""); // TODO try to join albumartists if present
|
||||||
release->mbid = getTagValueAs<UUID>(tagReader, TagType::MusicBrainzReleaseID);
|
release->mbid = getTagValueAs<UUID>(tagReader, TagType::MusicBrainzReleaseID);
|
||||||
release->artists = getArtists(tagReader, { TagType::AlbumArtists, TagType::AlbumArtist }, { TagType::AlbumArtistsSortOrder, TagType::AlbumArtistSortOrder }, { TagType::MusicBrainzReleaseArtistID }, _artistTagDelimiters);
|
release->artists = getArtists(tagReader, { TagType::AlbumArtists, TagType::AlbumArtist }, { TagType::AlbumArtistsSortOrder, TagType::AlbumArtistSortOrder }, { TagType::MusicBrainzReleaseArtistID }, _artistTagDelimiters);
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ namespace MetaData
|
|||||||
{
|
{
|
||||||
std::optional<UUID> mbid;
|
std::optional<UUID> mbid;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
std::string sortName;
|
||||||
std::string artistDisplayName;
|
std::string artistDisplayName;
|
||||||
std::vector<Artist> artists;
|
std::vector<Artist> artists;
|
||||||
std::optional<std::size_t> mediumCount;
|
std::optional<std::size_t> mediumCount;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ namespace MetaData
|
|||||||
{
|
{
|
||||||
{ TagType::AcoustID, { "e987a441-e134-4960-8019-274eddacc418" } },
|
{ TagType::AcoustID, { "e987a441-e134-4960-8019-274eddacc418" } },
|
||||||
{ TagType::Album, { "MyAlbum" } },
|
{ TagType::Album, { "MyAlbum" } },
|
||||||
|
{ TagType::AlbumSortOrder, { "MyAlbumSortName" } },
|
||||||
{ TagType::Artist, { "MyArtist1 & MyArtist2" } },
|
{ TagType::Artist, { "MyArtist1 & MyArtist2" } },
|
||||||
{ TagType::Artists, { "MyArtist1", "MyArtist2" } },
|
{ TagType::Artists, { "MyArtist1", "MyArtist2" } },
|
||||||
{ TagType::ArtistSortOrder, { "MyArtist1SortName", "MyArtist2SortName" } },
|
{ TagType::ArtistSortOrder, { "MyArtist1SortName", "MyArtist2SortName" } },
|
||||||
@@ -195,6 +196,7 @@ namespace MetaData
|
|||||||
EXPECT_EQ(track->medium->release->mbid.value(), UUID::fromString("3fa39992-b786-4585-a70e-85d5cc15ef69"));
|
EXPECT_EQ(track->medium->release->mbid.value(), UUID::fromString("3fa39992-b786-4585-a70e-85d5cc15ef69"));
|
||||||
EXPECT_EQ(track->medium->release->mediumCount, 3);
|
EXPECT_EQ(track->medium->release->mediumCount, 3);
|
||||||
EXPECT_EQ(track->medium->release->name, "MyAlbum");
|
EXPECT_EQ(track->medium->release->name, "MyAlbum");
|
||||||
|
EXPECT_EQ(track->medium->release->sortName, "MyAlbumSortName");
|
||||||
{
|
{
|
||||||
std::vector<std::string> expectedReleaseTypes{ "Album", "Compilation" };
|
std::vector<std::string> expectedReleaseTypes{ "Album", "Compilation" };
|
||||||
EXPECT_EQ(track->medium->release->releaseTypes, expectedReleaseTypes);
|
EXPECT_EQ(track->medium->release->releaseTypes, expectedReleaseTypes);
|
||||||
|
|||||||
@@ -129,6 +129,8 @@ namespace Scanner
|
|||||||
{
|
{
|
||||||
if (release->getName() != releaseInfo.name)
|
if (release->getName() != releaseInfo.name)
|
||||||
release.modify()->setName(releaseInfo.name);
|
release.modify()->setName(releaseInfo.name);
|
||||||
|
if (release->getSortName() != releaseInfo.sortName)
|
||||||
|
release.modify()->setSortName(releaseInfo.sortName);
|
||||||
if (release->getTotalDisc() != releaseInfo.mediumCount)
|
if (release->getTotalDisc() != releaseInfo.mediumCount)
|
||||||
release.modify()->setTotalDisc(releaseInfo.mediumCount);
|
release.modify()->setTotalDisc(releaseInfo.mediumCount);
|
||||||
if (release->getArtistDisplayName() != releaseInfo.artistDisplayName)
|
if (release->getArtistDisplayName() != releaseInfo.artistDisplayName)
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ namespace Scrobbling::ListenBrainz
|
|||||||
trackMetadata["artist_name"] = Wt::Json::Value{ std::string{ track->getArtistDisplayName() } };
|
trackMetadata["artist_name"] = Wt::Json::Value{ std::string{ track->getArtistDisplayName() } };
|
||||||
trackMetadata["track_name"] = Wt::Json::Value{ track->getName() };
|
trackMetadata["track_name"] = Wt::Json::Value{ track->getName() };
|
||||||
if (track->getRelease())
|
if (track->getRelease())
|
||||||
trackMetadata["release_name"] = Wt::Json::Value{ track->getRelease()->getName() };
|
trackMetadata["release_name"] = Wt::Json::Value{ std::string {track->getRelease()->getName()} };
|
||||||
|
|
||||||
Wt::Json::Object payload;
|
Wt::Json::Object payload;
|
||||||
payload["track_metadata"] = std::move(trackMetadata);
|
payload["track_metadata"] = std::move(trackMetadata);
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ namespace API::Subsonic::Utils
|
|||||||
throw NotImplementedGenericError{};
|
throw NotImplementedGenericError{};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string makeNameFilesystemCompatible(const std::string& name)
|
std::string makeNameFilesystemCompatible(std::string_view name)
|
||||||
{
|
{
|
||||||
return StringUtils::replaceInString(name, "/", "_");
|
return StringUtils::replaceInString(name, "/", "_");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
namespace API::Subsonic::Utils
|
namespace API::Subsonic::Utils
|
||||||
{
|
{
|
||||||
void checkSetPasswordImplemented();
|
void checkSetPasswordImplemented();
|
||||||
std::string makeNameFilesystemCompatible(const std::string& name);
|
std::string makeNameFilesystemCompatible(std::string_view name);
|
||||||
}
|
}
|
||||||
@@ -106,6 +106,8 @@ namespace API::Subsonic
|
|||||||
return albumNode;
|
return albumNode;
|
||||||
|
|
||||||
// OpenSubsonic specific fields (must always be set)
|
// OpenSubsonic specific fields (must always be set)
|
||||||
|
albumNode.setAttribute("sortName", release->getSortName());
|
||||||
|
|
||||||
if (!id3)
|
if (!id3)
|
||||||
albumNode.setAttribute("mediaType", "album");
|
albumNode.setAttribute("mediaType", "album");
|
||||||
|
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ namespace UserInterface
|
|||||||
if (track->getRelease())
|
if (track->getRelease())
|
||||||
{
|
{
|
||||||
_release->setTextFormat(Wt::TextFormat::Plain);
|
_release->setTextFormat(Wt::TextFormat::Plain);
|
||||||
_release->setText(Wt::WString::fromUTF8(track->getRelease()->getName()));
|
_release->setText(Wt::WString::fromUTF8(std::string{ track->getRelease()->getName() }));
|
||||||
_release->setLink(Utils::createReleaseLink(track->getRelease()));
|
_release->setLink(Utils::createReleaseLink(track->getRelease()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -255,9 +255,10 @@ namespace UserInterface::Utils
|
|||||||
|
|
||||||
if (setText)
|
if (setText)
|
||||||
{
|
{
|
||||||
|
std::string releaseName{ release->getName() };
|
||||||
res->setTextFormat(Wt::TextFormat::Plain);
|
res->setTextFormat(Wt::TextFormat::Plain);
|
||||||
res->setText(Wt::WString::fromUTF8(release->getName()));
|
res->setText(Wt::WString::fromUTF8(releaseName));
|
||||||
res->setToolTip(Wt::WString::fromUTF8(release->getName()), Wt::TextFormat::Plain);
|
res->setToolTip(Wt::WString::fromUTF8(releaseName), Wt::TextFormat::Plain);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
@@ -238,14 +238,14 @@ namespace UserInterface
|
|||||||
if (!release)
|
if (!release)
|
||||||
throw ReleaseNotFoundException{};
|
throw ReleaseNotFoundException{};
|
||||||
|
|
||||||
LmsApp->setTitle(release->getName());
|
LmsApp->setTitle(std::string{ release->getName() });
|
||||||
_releaseId = *releaseId;
|
_releaseId = *releaseId;
|
||||||
|
|
||||||
refreshCopyright(release);
|
refreshCopyright(release);
|
||||||
refreshLinks(release);
|
refreshLinks(release);
|
||||||
refreshSimilarReleases(similarReleasesIds);
|
refreshSimilarReleases(similarReleasesIds);
|
||||||
|
|
||||||
bindString("name", Wt::WString::fromUTF8(release->getName()), Wt::TextFormat::Plain);
|
bindString("name", Wt::WString::fromUTF8(std::string{ release->getName() }), Wt::TextFormat::Plain);
|
||||||
|
|
||||||
Wt::WString year{ ReleaseHelpers::buildReleaseYearString(release->getYear(), release->getOriginalYear()) };
|
Wt::WString year{ ReleaseHelpers::buildReleaseYearString(release->getYear(), release->getOriginalYear()) };
|
||||||
if (!year.empty())
|
if (!year.empty())
|
||||||
|
|||||||
@@ -50,7 +50,10 @@ namespace
|
|||||||
os << release.name;
|
os << release.name;
|
||||||
|
|
||||||
if (release.mbid)
|
if (release.mbid)
|
||||||
os << " (" << release.mbid->getAsString() << ")" << std::endl;
|
os << " (" << release.mbid->getAsString() << ")";
|
||||||
|
if (!release.sortName.empty())
|
||||||
|
os << " '" << release.sortName << "'";
|
||||||
|
os << std::endl;
|
||||||
|
|
||||||
if (release.mediumCount)
|
if (release.mediumCount)
|
||||||
std::cout << "\tMediumCount: " << *release.mediumCount << std::endl;
|
std::cout << "\tMediumCount: " << *release.mediumCount << std::endl;
|
||||||
@@ -61,9 +64,8 @@ namespace
|
|||||||
for (const MetaData::Artist& artist : release.artists)
|
for (const MetaData::Artist& artist : release.artists)
|
||||||
std::cout << "\tRelease artist: " << artist << std::endl;
|
std::cout << "\tRelease artist: " << artist << std::endl;
|
||||||
|
|
||||||
std::cout << "Release types:" << std::endl;
|
|
||||||
for (std::string_view releaseType : release.releaseTypes)
|
for (std::string_view releaseType : release.releaseTypes)
|
||||||
std::cout << "\t" << releaseType << std::endl;
|
std::cout << "\tRelease type: " << releaseType << std::endl;
|
||||||
|
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
@@ -87,7 +89,7 @@ namespace
|
|||||||
std::cout << "\tReplay gain: " << *medium.replayGain << std::endl;
|
std::cout << "\tReplay gain: " << *medium.replayGain << std::endl;
|
||||||
|
|
||||||
if (medium.release)
|
if (medium.release)
|
||||||
std::cout << "Release: " << *medium.release << std::endl;
|
std::cout << "Release: " << *medium.release;
|
||||||
|
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ static void dumpTracksRecommendation(Session session, Recommendation::IRecommend
|
|||||||
|
|
||||||
res += track->getName();
|
res += track->getName();
|
||||||
if (track->getRelease())
|
if (track->getRelease())
|
||||||
res += " [" + track->getRelease()->getName() + "]";
|
res += " [" + std::string{ track->getRelease()->getName() } + "]";
|
||||||
for (auto artist : track->getArtists({ TrackArtistLinkType::Artist }))
|
for (auto artist : track->getArtists({ TrackArtistLinkType::Artist }))
|
||||||
res += " - " + artist->getName();
|
res += " - " + artist->getName();
|
||||||
for (auto cluster : track->getClusters())
|
for (auto cluster : track->getClusters())
|
||||||
@@ -88,7 +88,7 @@ static void dumpReleasesRecommendation(Session session, Recommendation::IRecomme
|
|||||||
auto transaction{ session.createReadTransaction() };
|
auto transaction{ session.createReadTransaction() };
|
||||||
|
|
||||||
Release::pointer release{ Release::find(session, releaseId) };
|
Release::pointer release{ Release::find(session, releaseId) };
|
||||||
return release->getName();
|
return std::string{ release->getName() };
|
||||||
};
|
};
|
||||||
|
|
||||||
std::cout << "Processing release '" << releaseToString(releaseId) << "'" << std::endl;
|
std::cout << "Processing release '" << releaseToString(releaseId) << "'" << std::endl;
|
||||||
|
|||||||
Reference in New Issue
Block a user