Subsonic API: added 'roles' in artist entries
This commit is contained in:
@@ -28,74 +28,84 @@
|
|||||||
|
|
||||||
namespace Database
|
namespace Database
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
Wt::Dbo::Query<TrackArtistLinkId> createQuery(Session& session, const TrackArtistLink::FindParameters& params)
|
||||||
|
{
|
||||||
|
session.checkSharedLocked();
|
||||||
|
|
||||||
static
|
auto query{ session.getDboSession().query<TrackArtistLinkId>("SELECT DISTINCT t_a_l.id FROM track_artist_link t_a_l") };
|
||||||
Wt::Dbo::Query<TrackArtistLinkId>
|
|
||||||
createQuery(Session& session, const TrackArtistLink::FindParameters& params)
|
|
||||||
{
|
|
||||||
session.checkSharedLocked();
|
|
||||||
|
|
||||||
auto query {session.getDboSession().query<TrackArtistLinkId>("SELECT DISTINCT t_a_l.id FROM track_artist_link t_a_l")};
|
if (params.linkType)
|
||||||
|
query.where("t_a_l.type = ?").bind(*params.linkType);
|
||||||
|
|
||||||
if (params.linkType)
|
if (params.track.isValid() || params.release.isValid())
|
||||||
query.where("t_a_l.type = ?").bind(*params.linkType);
|
query.join("track t ON t.id = t_a_l.track_id");
|
||||||
|
|
||||||
if (params.track.isValid() || params.release.isValid())
|
if (params.artist.isValid())
|
||||||
query.join("track t ON t.id = t_a_l.track_id");
|
query.join("artist a ON a.id = t_a_l.artist_id");
|
||||||
|
|
||||||
if (params.track.isValid())
|
if (params.release.isValid())
|
||||||
query.where("t.id = ?").bind(params.track);
|
query.where("t.release_id = ?").bind(params.release);
|
||||||
|
|
||||||
if (params.release.isValid())
|
if (params.track.isValid())
|
||||||
query.where("t.release_id = ?").bind(params.release);
|
query.where("t.id = ?").bind(params.track);
|
||||||
|
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TrackArtistLink::TrackArtistLink(ObjectPtr<Track> track, ObjectPtr<Artist> artist, TrackArtistLinkType type, std::string_view subType)
|
TrackArtistLink::TrackArtistLink(ObjectPtr<Track> track, ObjectPtr<Artist> artist, TrackArtistLinkType type, std::string_view subType)
|
||||||
: _type {type}
|
: _type{ type }
|
||||||
, _subType {subType}
|
, _subType{ subType }
|
||||||
, _track {getDboPtr(track)}
|
, _track{ getDboPtr(track) }
|
||||||
, _artist {getDboPtr(artist)}
|
, _artist{ getDboPtr(artist) }
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackArtistLink::pointer
|
TrackArtistLink::pointer TrackArtistLink::create(Session& session, ObjectPtr<Track> track, ObjectPtr<Artist> artist, TrackArtistLinkType type, std::string_view subType)
|
||||||
TrackArtistLink::create(Session& session, ObjectPtr<Track> track, ObjectPtr<Artist> artist, TrackArtistLinkType type, std::string_view subType)
|
{
|
||||||
{
|
session.checkUniqueLocked();
|
||||||
session.checkUniqueLocked();
|
|
||||||
|
|
||||||
TrackArtistLink::pointer res {session.getDboSession().add(std::make_unique<TrackArtistLink>(track, artist, type, subType))};
|
TrackArtistLink::pointer res{ session.getDboSession().add(std::make_unique<TrackArtistLink>(track, artist, type, subType)) };
|
||||||
session.getDboSession().flush();
|
session.getDboSession().flush();
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackArtistLink::pointer
|
TrackArtistLink::pointer TrackArtistLink::find(Session& session, TrackArtistLinkId id)
|
||||||
TrackArtistLink::find(Session& session, TrackArtistLinkId id)
|
{
|
||||||
{
|
session.checkSharedLocked();
|
||||||
session.checkSharedLocked();
|
return session.getDboSession().find<TrackArtistLink>().where("id = ?").bind(id).resultValue();
|
||||||
return session.getDboSession().find<TrackArtistLink>().where("id = ?").bind(id).resultValue();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
RangeResults<TrackArtistLinkId>
|
RangeResults<TrackArtistLinkId> TrackArtistLink::find(Session& session, const FindParameters& params)
|
||||||
TrackArtistLink::find(Session& session, const FindParameters& params)
|
{
|
||||||
{
|
session.checkSharedLocked();
|
||||||
session.checkSharedLocked();
|
|
||||||
|
|
||||||
auto query {createQuery(session, params)};
|
auto query{ createQuery(session, params) };
|
||||||
return Utils::execQuery(query, params.range);
|
return Utils::execQuery(query, params.range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EnumSet<TrackArtistLinkType> TrackArtistLink::findUsedTypes(Session& session)
|
||||||
|
{
|
||||||
|
session.checkSharedLocked();
|
||||||
|
|
||||||
EnumSet<TrackArtistLinkType>
|
auto res{ session.getDboSession().query<TrackArtistLinkType>("SELECT DISTINCT type from track_artist_link").resultList() };
|
||||||
TrackArtistLink::findUsedTypes(Session& session)
|
|
||||||
{
|
|
||||||
session.checkSharedLocked();
|
|
||||||
|
|
||||||
auto res {session.getDboSession().query<TrackArtistLinkType>("SELECT DISTINCT type from track_artist_link").resultList()};
|
return EnumSet<TrackArtistLinkType>(std::begin(res), std::end(res));
|
||||||
|
}
|
||||||
|
|
||||||
return EnumSet<TrackArtistLinkType>(std::begin(res), std::end(res));
|
EnumSet<TrackArtistLinkType> TrackArtistLink::findUsedTypes(Session& session, ArtistId artistId)
|
||||||
}
|
{
|
||||||
|
session.checkSharedLocked();
|
||||||
|
|
||||||
|
auto res{ session.getDboSession()
|
||||||
|
.query<TrackArtistLinkType>("SELECT DISTINCT type from track_artist_link")
|
||||||
|
.where("artist_id = ?").bind(artistId)
|
||||||
|
.resultList() };
|
||||||
|
|
||||||
|
return EnumSet<TrackArtistLinkType>(std::begin(res), std::end(res));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include <Wt/Dbo/Dbo.h>
|
#include <Wt/Dbo/Dbo.h>
|
||||||
|
|
||||||
|
#include "services/database/ArtistId.hpp"
|
||||||
#include "services/database/IdType.hpp"
|
#include "services/database/IdType.hpp"
|
||||||
#include "services/database/Object.hpp"
|
#include "services/database/Object.hpp"
|
||||||
#include "services/database/ReleaseId.hpp"
|
#include "services/database/ReleaseId.hpp"
|
||||||
@@ -35,55 +36,58 @@ LMS_DECLARE_IDTYPE(TrackArtistLinkId)
|
|||||||
|
|
||||||
namespace Database
|
namespace Database
|
||||||
{
|
{
|
||||||
class Artist;
|
class Artist;
|
||||||
class Session;
|
class Session;
|
||||||
class Track;
|
class Track;
|
||||||
|
|
||||||
class TrackArtistLink final : public Object<TrackArtistLink, TrackArtistLinkId>
|
class TrackArtistLink final : public Object<TrackArtistLink, TrackArtistLinkId>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct FindParameters
|
struct FindParameters
|
||||||
{
|
{
|
||||||
Range range;
|
Range range;
|
||||||
std::optional<TrackArtistLinkType> linkType; // if set, only artists that have produced at least one track with this link type
|
std::optional<TrackArtistLinkType> linkType; // if set, only artists that have produced at least one track with this link type
|
||||||
TrackId track; // artists involved in this track
|
ArtistId artist; // if set, links involved with this artist
|
||||||
ReleaseId release; // artists involved in this release
|
ReleaseId release; // if set, artists involved in this release
|
||||||
|
TrackId track; // if set, artists involved in this track
|
||||||
|
|
||||||
FindParameters& setRange(Range _range) {range = _range; return *this; }
|
FindParameters& setRange(Range _range) { range = _range; return *this; }
|
||||||
FindParameters& setLinkType(std::optional<TrackArtistLinkType> _linkType) { linkType = _linkType; return *this; }
|
FindParameters& setLinkType(std::optional<TrackArtistLinkType> _linkType) { linkType = _linkType; return *this; }
|
||||||
FindParameters& setTrack(TrackId _track) { track = _track; return *this; }
|
FindParameters& setArtist(ArtistId _artist) { artist = _artist; return *this; }
|
||||||
FindParameters& setRelease(ReleaseId _release) { release = _release; return *this; }
|
FindParameters& setRelease(ReleaseId _release) { release = _release; return *this; }
|
||||||
};
|
FindParameters& setTrack(TrackId _track) { track = _track; return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
TrackArtistLink() = default;
|
TrackArtistLink() = default;
|
||||||
TrackArtistLink(ObjectPtr<Track> track, ObjectPtr<Artist> artist, TrackArtistLinkType type, std::string_view subType);
|
TrackArtistLink(ObjectPtr<Track> track, ObjectPtr<Artist> artist, TrackArtistLinkType type, std::string_view subType);
|
||||||
|
|
||||||
static RangeResults<TrackArtistLinkId> find(Session& session, const FindParameters& parameters);
|
static RangeResults<TrackArtistLinkId> find(Session& session, const FindParameters& parameters);
|
||||||
static pointer find(Session& session, TrackArtistLinkId linkId);
|
static pointer find(Session& session, TrackArtistLinkId linkId);
|
||||||
static pointer create(Session& session, ObjectPtr<Track> track, ObjectPtr<Artist> artist, TrackArtistLinkType type, std::string_view subType = {});
|
static pointer create(Session& session, ObjectPtr<Track> track, ObjectPtr<Artist> artist, TrackArtistLinkType type, std::string_view subType = {});
|
||||||
static EnumSet<TrackArtistLinkType> findUsedTypes(Session& session);
|
static EnumSet<TrackArtistLinkType> findUsedTypes(Session& session);
|
||||||
|
static EnumSet<TrackArtistLinkType> findUsedTypes(Session& session, ArtistId _artist);
|
||||||
|
|
||||||
ObjectPtr<Track> getTrack() const { return _track; }
|
ObjectPtr<Track> getTrack() const { return _track; }
|
||||||
ObjectPtr<Artist> getArtist() const { return _artist; }
|
ObjectPtr<Artist> getArtist() const { return _artist; }
|
||||||
TrackArtistLinkType getType() const { return _type; }
|
TrackArtistLinkType getType() const { return _type; }
|
||||||
std::string_view getSubType() const { return _subType; }
|
std::string_view getSubType() const { return _subType; }
|
||||||
|
|
||||||
template<class Action>
|
template<class Action>
|
||||||
void persist(Action& a)
|
void persist(Action& a)
|
||||||
{
|
{
|
||||||
Wt::Dbo::field(a, _type, "type");
|
Wt::Dbo::field(a, _type, "type");
|
||||||
Wt::Dbo::field(a, _subType, "subtype");
|
Wt::Dbo::field(a, _subType, "subtype");
|
||||||
|
|
||||||
Wt::Dbo::belongsTo(a, _track, "track", Wt::Dbo::OnDeleteCascade);
|
Wt::Dbo::belongsTo(a, _track, "track", Wt::Dbo::OnDeleteCascade);
|
||||||
Wt::Dbo::belongsTo(a, _artist, "artist", Wt::Dbo::OnDeleteCascade);
|
Wt::Dbo::belongsTo(a, _artist, "artist", Wt::Dbo::OnDeleteCascade);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TrackArtistLinkType _type;
|
TrackArtistLinkType _type;
|
||||||
std::string _subType;
|
std::string _subType;
|
||||||
|
|
||||||
Wt::Dbo::ptr<Track> _track;
|
Wt::Dbo::ptr<Track> _track;
|
||||||
Wt::Dbo::ptr<Artist> _artist;
|
Wt::Dbo::ptr<Artist> _artist;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -179,6 +179,15 @@ TEST_F(DatabaseFixture, Artist_singleTracktMultiRoles)
|
|||||||
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist.getId(), {TrackArtistLinkType::Composer}));
|
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist.getId(), {TrackArtistLinkType::Composer}));
|
||||||
EXPECT_EQ(tracks.results.size(), 0);
|
EXPECT_EQ(tracks.results.size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto transaction {session.createSharedTransaction()};
|
||||||
|
EnumSet<TrackArtistLinkType> types{ TrackArtistLink::findUsedTypes(session, artist.getId()) };
|
||||||
|
EXPECT_TRUE(types.contains(TrackArtistLinkType::ReleaseArtist));
|
||||||
|
EXPECT_TRUE(types.contains(TrackArtistLinkType::Artist));
|
||||||
|
EXPECT_TRUE(types.contains(TrackArtistLinkType::Writer));
|
||||||
|
EXPECT_FALSE(types.contains(TrackArtistLinkType::Composer));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DatabaseFixture, Artist_singleTrackMultiArtists)
|
TEST_F(DatabaseFixture, Artist_singleTrackMultiArtists)
|
||||||
|
|||||||
@@ -307,7 +307,7 @@ namespace API::Subsonic
|
|||||||
Response resp{ (itEntryPoint->second.func)(requestContext) };
|
Response resp{ (itEntryPoint->second.func)(requestContext) };
|
||||||
|
|
||||||
resp.write(response.out(), format);
|
resp.write(response.out(), format);
|
||||||
response.setMimeType(ResponseFormatToMimeType(format));
|
response.setMimeType(std::string{ ResponseFormatToMimeType(format) });
|
||||||
|
|
||||||
LMS_LOG(API_SUBSONIC, DEBUG) << "Request " << requestId << " '" << requestPath << "' handled!";
|
LMS_LOG(API_SUBSONIC, DEBUG) << "Request " << requestId << " '" << requestPath << "' handled!";
|
||||||
return;
|
return;
|
||||||
@@ -331,7 +331,7 @@ namespace API::Subsonic
|
|||||||
<< ", code = " << static_cast<int>(e.getCode()) << ", msg = '" << e.getMessage() << "'";
|
<< ", code = " << static_cast<int>(e.getCode()) << ", msg = '" << e.getMessage() << "'";
|
||||||
Response resp{ Response::createFailedResponse(protocolVersion, e) };
|
Response resp{ Response::createFailedResponse(protocolVersion, e) };
|
||||||
resp.write(response.out(), format);
|
resp.write(response.out(), format);
|
||||||
response.setMimeType(ResponseFormatToMimeType(format));
|
response.setMimeType(std::string{ ResponseFormatToMimeType(format) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,8 +33,7 @@
|
|||||||
|
|
||||||
namespace API::Subsonic
|
namespace API::Subsonic
|
||||||
{
|
{
|
||||||
|
std::string_view ResponseFormatToMimeType(ResponseFormat format)
|
||||||
std::string ResponseFormatToMimeType(ResponseFormat format)
|
|
||||||
{
|
{
|
||||||
switch (format)
|
switch (format)
|
||||||
{
|
{
|
||||||
@@ -47,7 +46,7 @@ namespace API::Subsonic
|
|||||||
|
|
||||||
void Response::Node::setValue(std::string_view value)
|
void Response::Node::setValue(std::string_view value)
|
||||||
{
|
{
|
||||||
if (!_children.empty() || !_childrenArrays.empty())
|
if (!_children.empty() || !_childrenArrays.empty() || !_childrenValues.empty())
|
||||||
throw LmsException{ "Node already has children" };
|
throw LmsException{ "Node already has children" };
|
||||||
|
|
||||||
_value = std::string{ value };
|
_value = std::string{ value };
|
||||||
@@ -55,7 +54,7 @@ namespace API::Subsonic
|
|||||||
|
|
||||||
void Response::Node::setValue(long long value)
|
void Response::Node::setValue(long long value)
|
||||||
{
|
{
|
||||||
if (!_children.empty() || !_childrenArrays.empty())
|
if (!_children.empty() || !_childrenArrays.empty() || !_childrenValues.empty())
|
||||||
throw LmsException{ "Node already has children" };
|
throw LmsException{ "Node already has children" };
|
||||||
|
|
||||||
_value = value;
|
_value = value;
|
||||||
@@ -82,6 +81,21 @@ namespace API::Subsonic
|
|||||||
_childrenArrays[key].emplace_back(std::move(node));
|
_childrenArrays[key].emplace_back(std::move(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Response::Node::createArrayValue(const std::string& key)
|
||||||
|
{
|
||||||
|
if (_value)
|
||||||
|
throw LmsException{ "Node already has a value" };
|
||||||
|
|
||||||
|
_childrenValues.emplace(key, std::vector<std::string>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Response::Node::addArrayValue(const std::string& key, std::string_view value)
|
||||||
|
{
|
||||||
|
if (_value)
|
||||||
|
throw LmsException{ "Node already has a value" };
|
||||||
|
|
||||||
|
_childrenValues[key].push_back(std::string{ value });
|
||||||
|
}
|
||||||
|
|
||||||
Response::Node& Response::Node::createChild(const std::string& key)
|
Response::Node& Response::Node::createChild(const std::string& key)
|
||||||
{
|
{
|
||||||
@@ -236,21 +250,28 @@ namespace API::Subsonic
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (auto itChildNode : node._children)
|
for (const auto& [key, childNodes] : node._children)
|
||||||
{
|
{
|
||||||
for (const Response::Node& childNode : itChildNode.second)
|
for (const Response::Node& childNode : childNodes)
|
||||||
res[itChildNode.first] = nodeToJsonObject(childNode);
|
res[key] = nodeToJsonObject(childNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto itChildArrayNode : node._childrenArrays)
|
for (const auto& [key, childArrayNodes] : node._childrenArrays)
|
||||||
{
|
{
|
||||||
const std::vector<Response::Node>& childArrayNodes{ itChildArrayNode.second };
|
|
||||||
|
|
||||||
Json::Array array;
|
Json::Array array;
|
||||||
for (const Response::Node& childNode : childArrayNodes)
|
for (const Response::Node& childNode : childArrayNodes)
|
||||||
array.emplace_back(nodeToJsonObject(childNode));
|
array.emplace_back(nodeToJsonObject(childNode));
|
||||||
|
|
||||||
res[itChildArrayNode.first] = std::move(array);
|
res[key] = std::move(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& [key, childValues] : node._childrenValues)
|
||||||
|
{
|
||||||
|
Json::Array array;
|
||||||
|
for (const std::string& childValue : childValues)
|
||||||
|
array.emplace_back(Json::Value{ childValue });
|
||||||
|
|
||||||
|
res[key] = std::move(array);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace API::Subsonic
|
|||||||
json,
|
json,
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string ResponseFormatToMimeType(ResponseFormat format);
|
std::string_view ResponseFormatToMimeType(ResponseFormat format);
|
||||||
|
|
||||||
class Error
|
class Error
|
||||||
{
|
{
|
||||||
@@ -202,7 +202,7 @@ namespace API::Subsonic
|
|||||||
_attributes[std::string{ key }] = static_cast<long long>(value);
|
_attributes[std::string{ key }] = static_cast<long long>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Node has either a value or some children
|
// A Node has either a single value or an array of values or some children
|
||||||
void setValue(std::string_view value);
|
void setValue(std::string_view value);
|
||||||
void setValue(long long value);
|
void setValue(long long value);
|
||||||
Node& createChild(const std::string& key);
|
Node& createChild(const std::string& key);
|
||||||
@@ -210,6 +210,8 @@ namespace API::Subsonic
|
|||||||
|
|
||||||
void addChild(const std::string& key, Node node);
|
void addChild(const std::string& key, Node node);
|
||||||
void addArrayChild(const std::string& key, Node node);
|
void addArrayChild(const std::string& key, Node node);
|
||||||
|
void createArrayValue(const std::string& key);
|
||||||
|
void addArrayValue(const std::string& key, std::string_view value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setVersionAttribute(ProtocolVersion version);
|
void setVersionAttribute(ProtocolVersion version);
|
||||||
@@ -220,6 +222,7 @@ namespace API::Subsonic
|
|||||||
std::optional<ValueType> _value;
|
std::optional<ValueType> _value;
|
||||||
std::map<std::string, std::vector<Node>> _children;
|
std::map<std::string, std::vector<Node>> _children;
|
||||||
std::map<std::string, std::vector<Node>> _childrenArrays;
|
std::map<std::string, std::vector<Node>> _childrenArrays;
|
||||||
|
std::map<std::string, std::vector<std::string>> _childrenValues;
|
||||||
};
|
};
|
||||||
|
|
||||||
static Response createOkResponse(ProtocolVersion protocolVersion);
|
static Response createOkResponse(ProtocolVersion protocolVersion);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include "services/database/Artist.hpp"
|
#include "services/database/Artist.hpp"
|
||||||
#include "services/database/Release.hpp"
|
#include "services/database/Release.hpp"
|
||||||
|
#include "services/database/TrackArtistLink.hpp"
|
||||||
#include "services/database/User.hpp"
|
#include "services/database/User.hpp"
|
||||||
#include "services/scrobbling/IScrobblingService.hpp"
|
#include "services/scrobbling/IScrobblingService.hpp"
|
||||||
#include "utils/Service.hpp"
|
#include "utils/Service.hpp"
|
||||||
@@ -50,6 +51,26 @@ namespace API::Subsonic
|
|||||||
|
|
||||||
return StringUtils::joinStrings(names, ", ");
|
return StringUtils::joinStrings(names, ", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string_view toString(TrackArtistLinkType type)
|
||||||
|
{
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Response::Node createArtistNode(const Artist::pointer& artist, Session& session, const User::pointer& user, bool id3)
|
Response::Node createArtistNode(const Artist::pointer& artist, Session& session, const User::pointer& user, bool id3)
|
||||||
@@ -73,8 +94,15 @@ namespace API::Subsonic
|
|||||||
std::optional<UUID> mbid {artist->getMBID()};
|
std::optional<UUID> mbid {artist->getMBID()};
|
||||||
artistNode.setAttribute("musicBrainzId", mbid ? mbid->getAsString() : "");
|
artistNode.setAttribute("musicBrainzId", mbid ? mbid->getAsString() : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
artistNode.setAttribute("sortName", artist->getSortName());
|
artistNode.setAttribute("sortName", artist->getSortName());
|
||||||
|
|
||||||
|
// roles
|
||||||
|
Response::Node roles;
|
||||||
|
artistNode.createArrayValue("roles");
|
||||||
|
for (const TrackArtistLinkType linkType : TrackArtistLink::findUsedTypes(session, artist->getId()))
|
||||||
|
artistNode.addArrayValue("roles", Utils::toString(linkType));
|
||||||
|
|
||||||
return artistNode;
|
return artistNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "services/database/Object.hpp"
|
#include "services/database/Object.hpp"
|
||||||
|
#include "services/database/Types.hpp"
|
||||||
#include "SubsonicResponse.hpp"
|
#include "SubsonicResponse.hpp"
|
||||||
|
|
||||||
namespace Database
|
namespace Database
|
||||||
@@ -36,6 +37,7 @@ namespace API::Subsonic
|
|||||||
namespace Utils
|
namespace Utils
|
||||||
{
|
{
|
||||||
std::string joinArtistNames(const std::vector<Database::ObjectPtr<Database::Artist>>& artists);
|
std::string joinArtistNames(const std::vector<Database::ObjectPtr<Database::Artist>>& artists);
|
||||||
|
std::string_view toString(Database::TrackArtistLinkType type);
|
||||||
}
|
}
|
||||||
Response::Node createArtistNode(const Database::ObjectPtr<Database::Artist>& artist, Database::Session& session, const Database::ObjectPtr<Database::User>& user, bool id3);
|
Response::Node createArtistNode(const Database::ObjectPtr<Database::Artist>& artist, Database::Session& session, const Database::ObjectPtr<Database::User>& user, bool id3);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user