From 4f25331e1de0bb4cad67824c78785b942e7758c2 Mon Sep 17 00:00:00 2001 From: emeric Date: Wed, 4 Oct 2023 18:51:38 +0200 Subject: [PATCH] Subsonic API: added 'contributor' in song entries --- src/libs/subsonic/CMakeLists.txt | 1 + src/libs/subsonic/impl/SubsonicResponse.cpp | 11 ++++- src/libs/subsonic/impl/SubsonicResponse.hpp | 3 +- src/libs/subsonic/impl/responses/Artist.cpp | 40 ++++++++++++------- src/libs/subsonic/impl/responses/Artist.hpp | 1 + .../subsonic/impl/responses/Contributor.cpp | 40 +++++++++++++++++++ .../subsonic/impl/responses/Contributor.hpp | 33 +++++++++++++++ src/libs/subsonic/impl/responses/Song.cpp | 10 +++++ 8 files changed, 122 insertions(+), 17 deletions(-) create mode 100644 src/libs/subsonic/impl/responses/Contributor.cpp create mode 100644 src/libs/subsonic/impl/responses/Contributor.hpp diff --git a/src/libs/subsonic/CMakeLists.txt b/src/libs/subsonic/CMakeLists.txt index 4403232a..118548a7 100644 --- a/src/libs/subsonic/CMakeLists.txt +++ b/src/libs/subsonic/CMakeLists.txt @@ -12,6 +12,7 @@ add_library(lmssubsonic SHARED impl/responses/Album.cpp impl/responses/Artist.cpp impl/responses/Bookmark.cpp + impl/responses/Contributor.cpp impl/responses/Genre.cpp impl/responses/Playlist.cpp impl/responses/Song.cpp diff --git a/src/libs/subsonic/impl/SubsonicResponse.cpp b/src/libs/subsonic/impl/SubsonicResponse.cpp index 7c86e0ff..4b2dda49 100644 --- a/src/libs/subsonic/impl/SubsonicResponse.cpp +++ b/src/libs/subsonic/impl/SubsonicResponse.cpp @@ -73,6 +73,15 @@ namespace API::Subsonic _children[key].emplace_back(std::move(node)); } + void Response::Node::createEmptyArrayChild(const std::string& key) + { + if (_value) + throw LmsException{ "Node already has a value" }; + + _childrenArrays.emplace(key, std::vector{}); + + } + void Response::Node::addArrayChild(const std::string& key, Node node) { if (_value) @@ -81,7 +90,7 @@ namespace API::Subsonic _childrenArrays[key].emplace_back(std::move(node)); } - void Response::Node::createArrayValue(const std::string& key) + void Response::Node::createEmptyArrayValue(const std::string& key) { if (_value) throw LmsException{ "Node already has a value" }; diff --git a/src/libs/subsonic/impl/SubsonicResponse.hpp b/src/libs/subsonic/impl/SubsonicResponse.hpp index 0ffe4f02..4c72b9ce 100644 --- a/src/libs/subsonic/impl/SubsonicResponse.hpp +++ b/src/libs/subsonic/impl/SubsonicResponse.hpp @@ -209,8 +209,9 @@ namespace API::Subsonic Node& createArrayChild(const std::string& key); void addChild(const std::string& key, Node node); + void createEmptyArrayChild(const std::string& key); void addArrayChild(const std::string& key, Node node); - void createArrayValue(const std::string& key); + void createEmptyArrayValue(const std::string& key); void addArrayValue(const std::string& key, std::string_view value); private: diff --git a/src/libs/subsonic/impl/responses/Artist.cpp b/src/libs/subsonic/impl/responses/Artist.cpp index 94ba72ef..be2b9a4f 100644 --- a/src/libs/subsonic/impl/responses/Artist.cpp +++ b/src/libs/subsonic/impl/responses/Artist.cpp @@ -56,17 +56,17 @@ namespace API::Subsonic { switch (type) { - case TrackArtistLinkType::Arranger: return "arranger"; - case TrackArtistLinkType::Artist: return "artist"; - case TrackArtistLinkType::Composer: return "composer"; - case TrackArtistLinkType::Conductor: return "conductor"; - case TrackArtistLinkType::Lyricist: return "lyricist"; - case TrackArtistLinkType::Mixer: return "mixer"; - case TrackArtistLinkType::Performer: return "performer"; - case TrackArtistLinkType::Producer: return "producrer"; - case TrackArtistLinkType::ReleaseArtist: return "albumartist"; - case TrackArtistLinkType::Remixer: return "remixer"; - case TrackArtistLinkType::Writer: return "writer"; + case TrackArtistLinkType::Arranger: return "arranger"; + case TrackArtistLinkType::Artist: return "artist"; + case TrackArtistLinkType::Composer: return "composer"; + case TrackArtistLinkType::Conductor: return "conductor"; + case TrackArtistLinkType::Lyricist: return "lyricist"; + case TrackArtistLinkType::Mixer: return "mixer"; + case TrackArtistLinkType::Performer: return "performer"; + case TrackArtistLinkType::Producer: return "producrer"; + case TrackArtistLinkType::ReleaseArtist: return "albumartist"; + case TrackArtistLinkType::Remixer: return "remixer"; + case TrackArtistLinkType::Writer: return "writer"; } return "unknown"; @@ -75,7 +75,7 @@ namespace API::Subsonic Response::Node createArtistNode(const Artist::pointer& artist, Session& session, const User::pointer& user, bool id3) { - Response::Node artistNode; + Response::Node artistNode{ createArtistNode(artist) }; artistNode.setAttribute("id", idToString(artist->getId())); artistNode.setAttribute("name", artist->getName()); @@ -91,18 +91,28 @@ namespace API::Subsonic // OpenSubsonic specific fields (must always be set) { - std::optional mbid {artist->getMBID()}; + std::optional mbid{ artist->getMBID() }; artistNode.setAttribute("musicBrainzId", mbid ? mbid->getAsString() : ""); } artistNode.setAttribute("sortName", artist->getSortName()); - + // roles Response::Node roles; - artistNode.createArrayValue("roles"); + artistNode.createEmptyArrayValue("roles"); for (const TrackArtistLinkType linkType : TrackArtistLink::findUsedTypes(session, artist->getId())) artistNode.addArrayValue("roles", Utils::toString(linkType)); return artistNode; } + + Response::Node createArtistNode(const Artist::pointer& artist) + { + Response::Node artistNode; + + artistNode.setAttribute("id", idToString(artist->getId())); + artistNode.setAttribute("name", artist->getName()); + + return artistNode; + } } \ No newline at end of file diff --git a/src/libs/subsonic/impl/responses/Artist.hpp b/src/libs/subsonic/impl/responses/Artist.hpp index e27352ed..4b293d5e 100644 --- a/src/libs/subsonic/impl/responses/Artist.hpp +++ b/src/libs/subsonic/impl/responses/Artist.hpp @@ -40,4 +40,5 @@ namespace API::Subsonic std::string_view toString(Database::TrackArtistLinkType type); } Response::Node createArtistNode(const Database::ObjectPtr& artist, Database::Session& session, const Database::ObjectPtr& user, bool id3); + Response::Node createArtistNode(const Database::ObjectPtr& artist); // only minimal info } \ No newline at end of file diff --git a/src/libs/subsonic/impl/responses/Contributor.cpp b/src/libs/subsonic/impl/responses/Contributor.cpp new file mode 100644 index 00000000..755b44dd --- /dev/null +++ b/src/libs/subsonic/impl/responses/Contributor.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 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 . + */ + +#include "responses/Contributor.hpp" + +#include "services/database/Object.hpp" +#include "services/database/TrackArtistLink.hpp" +#include "SubsonicResponse.hpp" +#include "responses/Artist.hpp" + +namespace API::Subsonic +{ + Response::Node createContributorNode(const Database::ObjectPtr& trackArtistLink) + { + Response::Node contributorNode; + + contributorNode.setAttribute("role", Utils::toString(trackArtistLink->getType())); + if (!trackArtistLink->getSubType().empty()) + contributorNode.setAttribute("subRole", trackArtistLink->getSubType()); + contributorNode.addChild("artist", createArtistNode(trackArtistLink->getArtist())); + + return contributorNode; + } +} \ No newline at end of file diff --git a/src/libs/subsonic/impl/responses/Contributor.hpp b/src/libs/subsonic/impl/responses/Contributor.hpp new file mode 100644 index 00000000..80a0d0d9 --- /dev/null +++ b/src/libs/subsonic/impl/responses/Contributor.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 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 . + */ + +#pragma once + +#include "services/database/Object.hpp" +#include "SubsonicResponse.hpp" + +namespace Database +{ + class TrackArtistLink; +} + +namespace API::Subsonic +{ + Response::Node createContributorNode(const Database::ObjectPtr& trackArtistLink); +} \ No newline at end of file diff --git a/src/libs/subsonic/impl/responses/Song.cpp b/src/libs/subsonic/impl/responses/Song.cpp index 8e92b284..66762814 100644 --- a/src/libs/subsonic/impl/responses/Song.cpp +++ b/src/libs/subsonic/impl/responses/Song.cpp @@ -25,11 +25,13 @@ #include "services/database/Cluster.hpp" #include "services/database/Release.hpp" #include "services/database/Track.hpp" +#include "services/database/TrackArtistLink.hpp" #include "services/database/User.hpp" #include "services/scrobbling/IScrobblingService.hpp" #include "utils/Service.hpp" #include "utils/String.hpp" #include "responses/Artist.hpp" +#include "responses/Contributor.hpp" #include "SubsonicId.hpp" #include "Utils.hpp" @@ -159,6 +161,14 @@ namespace API::Subsonic trackResponse.setAttribute("musicBrainzId", mbid ? mbid->getAsString() : ""); } + trackResponse.createEmptyArrayChild("contributors"); + for (const TrackArtistLinkId linkId : TrackArtistLink::find(dbSession, TrackArtistLink::FindParameters{}.setTrack(track->getId())).results) + { + TrackArtistLink::pointer link{ TrackArtistLink::find(dbSession, linkId) }; + if (link) + trackResponse.addArrayChild("contributors", createContributorNode(link)); + } + return trackResponse; } } \ No newline at end of file