Extracted genre, mood, language and grouping from generic clusters

This commit is contained in:
emeric
2026-06-17 21:34:29 +02:00
parent 77f9b033ed
commit dbd6e57160
82 changed files with 3648 additions and 275 deletions
@@ -25,7 +25,7 @@
#include "database/Session.hpp"
#include "database/Types.hpp"
#include "database/objects/Artist.hpp"
#include "database/objects/Cluster.hpp"
#include "database/objects/Genre.hpp"
#include "database/objects/Release.hpp"
#include "database/objects/Track.hpp"
#include "database/objects/User.hpp"
@@ -87,18 +87,15 @@ namespace lms::api::subsonic
// Mandatory param
const std::string genre{ getMandatoryParameterAs<std::string>(context.getParameters(), "genre") };
if (const ClusterType::pointer clusterType{ ClusterType::find(context.getDbSession(), "GENRE") })
if (const Genre::pointer genreObj{ Genre::find(context.getDbSession(), genre) })
{
if (const Cluster::pointer cluster{ clusterType->getCluster(genre) })
{
Release::FindParameters params;
params.filters.setMediaLibrary(mediaLibraryId);
params.filters.setClusters(std::initializer_list<ClusterId>{ cluster->getId() });
params.setSortMethod(ReleaseSortMethod::Name);
params.setRange(range);
Release::FindParameters params;
params.filters.setMediaLibrary(mediaLibraryId);
params.filters.setGenre(genreObj->getId());
params.setSortMethod(ReleaseSortMethod::Name);
params.setRange(range);
releases = Release::findIds(context.getDbSession(), params);
}
releases = Release::findIds(context.getDbSession(), params);
}
}
else if (type == "byYear")
@@ -273,19 +270,15 @@ namespace lms::api::subsonic
auto transaction{ context.getDbSession().createReadTransaction() };
auto clusterType{ ClusterType::find(context.getDbSession(), "GENRE") };
if (!clusterType)
throw RequestedDataNotFoundError{};
auto cluster{ clusterType->getCluster(genre) };
if (!cluster)
const Genre::pointer genreObj{ Genre::find(context.getDbSession(), genre) };
if (!genreObj)
throw RequestedDataNotFoundError{};
Response response{ Response::createOkResponse(context.getServerProtocolVersion()) };
Response::Node& songsByGenreNode{ response.createNode("songsByGenre") };
Track::FindParameters params;
params.filters.setClusters(std::initializer_list<ClusterId>{ cluster->getId() });
params.filters.setGenre(genreObj->getId());
params.filters.setMediaLibrary(mediaLibrary);
params.setRange(Range{ offset, count });
@@ -30,6 +30,7 @@
#include "database/objects/ArtistInfo.hpp"
#include "database/objects/Cluster.hpp"
#include "database/objects/Directory.hpp"
#include "database/objects/Genre.hpp"
#include "database/objects/MediaLibrary.hpp"
#include "database/objects/Release.hpp"
#include "database/objects/Track.hpp"
@@ -379,14 +380,9 @@ namespace lms::api::subsonic
auto transaction{ context.getDbSession().createReadTransaction() };
const ClusterType::pointer clusterType{ ClusterType::find(context.getDbSession(), "GENRE") };
if (clusterType)
{
const auto clusters{ clusterType->getClusters() };
for (const Cluster::pointer& cluster : clusters)
genresNode.addArrayChild("genre", createGenreNode(context, cluster));
}
db::Genre::find(context.getDbSession(), db::Genre::FindParameters{}.setSortMethod(db::GenreSortMethod::Name), [&](const db::Genre::pointer& genre) {
genresNode.addArrayChild("genre", createGenreNode(context, genre));
});
return response;
}
+27 -24
View File
@@ -25,9 +25,11 @@
#include "database/Types.hpp"
#include "database/objects/Artist.hpp"
#include "database/objects/Artwork.hpp"
#include "database/objects/Cluster.hpp"
#include "database/objects/Directory.hpp"
#include "database/objects/Genre.hpp"
#include "database/objects/Grouping.hpp"
#include "database/objects/Medium.hpp"
#include "database/objects/Mood.hpp"
#include "database/objects/Release.hpp"
#include "database/objects/ReleaseArtistLink.hpp"
#include "database/objects/Track.hpp"
@@ -134,13 +136,14 @@ namespace lms::api::subsonic
albumNode.setAttribute("playCount", core::Service<scrobbling::IScrobblingService>::get()->getCount(context.getUser()->getId(), release->getId()));
// Report the first GENRE for this track
const ClusterType::pointer genreClusterType{ ClusterType::find(context.getDbSession(), "GENRE") };
if (genreClusterType)
// Report the first genre for this release
{
const auto clusters{ release->getClusters(genreClusterType->getId(), 1) };
if (!clusters.empty())
albumNode.setAttribute("genre", clusters.front()->getName());
Genre::FindParameters p;
p.setRelease(release->getId());
p.setRange(Range{ 0, 1 });
const auto genres{ Genre::find(context.getDbSession(), p).results };
if (!genres.empty())
albumNode.setAttribute("genre", genres.front()->getName());
}
if (const Wt::WDateTime dateTime{ core::Service<feedback::IFeedbackService>::get()->getStarredDateTime(context.getUser()->getId(), release->getId()) }; dateTime.isValid())
@@ -168,31 +171,31 @@ namespace lms::api::subsonic
albumNode.setAttribute("musicBrainzId", mbid ? mbid->toString() : "");
}
auto addClusters{ [&](Response::Node::Key field, std::string_view clusterTypeName) {
albumNode.createEmptyArrayValue(field);
Cluster::FindParameters params;
albumNode.createEmptyArrayValue("moods");
{
Mood::FindParameters params;
params.setRelease(release->getId());
params.setClusterTypeName(clusterTypeName);
Cluster::find(context.getDbSession(), params, [&](const Cluster::pointer& cluster) {
albumNode.addArrayValue(field, cluster->getName());
Mood::find(context.getDbSession(), params, [&](const Mood::pointer& mood) {
albumNode.addArrayValue("moods", mood->getName());
});
} };
}
addClusters("moods", "MOOD");
addClusters("groupings", "GROUPING");
albumNode.createEmptyArrayValue("groupings");
{
Grouping::FindParameters params;
params.setRelease(release->getId());
Grouping::find(context.getDbSession(), params, [&](const Grouping::pointer& grouping) {
albumNode.addArrayValue("groupings", grouping->getName());
});
}
// Genres
albumNode.createEmptyArrayChild("genres");
if (genreClusterType)
{
Cluster::FindParameters params;
Genre::FindParameters params;
params.setRelease(release->getId());
params.setClusterType(genreClusterType->getId());
Cluster::find(context.getDbSession(), params, [&](const Cluster::pointer& cluster) {
albumNode.addArrayChild("genres", createItemGenreNode(cluster->getName()));
Genre::find(context.getDbSession(), params, [&](const Genre::pointer& genre) {
albumNode.addArrayChild("genres", createItemGenreNode(genre->getName()));
});
}
+9 -9
View File
@@ -19,28 +19,28 @@
#include "responses/Genre.hpp"
#include "database/objects/Cluster.hpp"
#include "database/objects/Genre.hpp"
#include "RequestContext.hpp"
namespace lms::api::subsonic
{
Response::Node createGenreNode(RequestContext& context, const db::Cluster::pointer& cluster)
Response::Node createGenreNode(RequestContext& context, const db::Genre::pointer& genre)
{
Response::Node clusterNode;
Response::Node genreNode;
switch (context.getResponseFormat())
{
case ResponseFormat::json:
clusterNode.setAttribute("value", cluster->getName());
genreNode.setAttribute("value", genre->getName());
break;
case ResponseFormat::xml:
clusterNode.setValue(cluster->getName());
genreNode.setValue(genre->getName());
break;
}
clusterNode.setAttribute("songCount", cluster->getTrackCount());
clusterNode.setAttribute("albumCount", cluster->getReleasesCount());
genreNode.setAttribute("songCount", genre->getTrackCount());
genreNode.setAttribute("albumCount", genre->getReleaseCount());
return clusterNode;
return genreNode;
}
} // namespace lms::api::subsonic
} // namespace lms::api::subsonic
+2 -2
View File
@@ -25,12 +25,12 @@
namespace lms::db
{
class Cluster;
class Genre;
}
namespace lms::api::subsonic
{
class RequestContext;
Response::Node createGenreNode(RequestContext& context, const db::ObjectPtr<db::Cluster>& cluster);
Response::Node createGenreNode(RequestContext& context, const db::ObjectPtr<db::Genre>& genre);
} // namespace lms::api::subsonic
+13 -24
View File
@@ -31,10 +31,12 @@
#include "database/Types.hpp"
#include "database/objects/Artist.hpp"
#include "database/objects/Artwork.hpp"
#include "database/objects/Cluster.hpp"
#include "database/objects/Directory.hpp"
#include "database/objects/Genre.hpp"
#include "database/objects/Grouping.hpp"
#include "database/objects/MediaLibrary.hpp"
#include "database/objects/Medium.hpp"
#include "database/objects/Mood.hpp"
#include "database/objects/Release.hpp"
#include "database/objects/ReleaseArtistLink.hpp"
#include "database/objects/Track.hpp"
@@ -162,17 +164,10 @@ namespace lms::api::subsonic
if (const Wt::WDateTime dateTime{ core::Service<feedback::IFeedbackService>::get()->getStarredDateTime(context.getUser()->getId(), track->getId()) }; dateTime.isValid())
trackResponse.setAttribute("starred", core::stringUtils::toISO8601String(dateTime));
// Report the first GENRE for this track
std::vector<db::Cluster::pointer> genres;
{
db::Cluster::FindParameters params;
params.setTrack(track->getId());
params.setClusterTypeName("GENRE");
genres = db::Cluster::find(context.getDbSession(), params).results;
if (!genres.empty())
trackResponse.setAttribute("genre", genres.front()->getName());
}
// Report the first genre for this track
const auto genres{ track->getGenres() };
if (!genres.empty())
trackResponse.setAttribute("genre", genres.front()->getName());
// OpenSubsonic specific fields (must always be set)
if (!context.isOpenSubsonicEnabled())
@@ -223,19 +218,13 @@ namespace lms::api::subsonic
if (release)
trackResponse.setAttribute("displayAlbumArtist", release->getArtistDisplayName());
auto addClusters{ [&](Response::Node::Key field, std::string_view clusterTypeName) {
trackResponse.createEmptyArrayValue(field);
trackResponse.createEmptyArrayValue("moods");
for (const auto& mood : track->getMoods())
trackResponse.addArrayValue("moods", mood->getName());
db::Cluster::FindParameters params;
params.setTrack(track->getId());
params.setClusterTypeName(clusterTypeName);
for (const auto& cluster : db::Cluster::find(context.getDbSession(), params).results)
trackResponse.addArrayValue(field, cluster->getName());
} };
addClusters("moods", "MOOD");
addClusters("groupings", "GROUPING");
trackResponse.createEmptyArrayValue("groupings");
for (const auto& grouping : track->getGroupings())
trackResponse.addArrayValue("groupings", grouping->getName());
// Genres
trackResponse.createEmptyArrayChild("genres");