Subsonic API: handle tracks and releases in getSimilarSongs endpoint. fixes #245
This commit is contained in:
@@ -1169,49 +1169,103 @@ handleGetArtistsRequest(RequestContext& context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
Response
|
std::vector<TrackId>
|
||||||
handleGetSimilarSongsRequestCommon(RequestContext& context, bool id3)
|
findSimilarSongs(RequestContext& context, ArtistId artistId, std::size_t count)
|
||||||
{
|
{
|
||||||
// Mandatory params
|
// API says: "Returns a random collection of songs from the given artist and similar artists"
|
||||||
const ArtistId artistId {getMandatoryParameterAs<ArtistId>(context.parameters, "id")};
|
const std::size_t similarArtistCount {count / 5};
|
||||||
|
std::vector<ArtistId> artistIds {Service<Recommendation::IRecommendationService>::get()->getSimilarArtists(artistId, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, similarArtistCount)};
|
||||||
|
artistIds.push_back(artistId);
|
||||||
|
|
||||||
// Optional params
|
const std::size_t meanTrackCountPerArtist {(count / artistIds.size()) + 1};
|
||||||
std::size_t count {getParameterAs<std::size_t>(context.parameters, "count").value_or(50)};
|
|
||||||
|
|
||||||
const auto similarArtistIds {Service<Recommendation::IRecommendationService>::get()->getSimilarArtists(artistId, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, 5)};
|
|
||||||
|
|
||||||
auto transaction {context.dbSession.createSharedTransaction()};
|
auto transaction {context.dbSession.createSharedTransaction()};
|
||||||
|
|
||||||
const Artist::pointer artist {Artist::find(context.dbSession, artistId)};
|
std::vector<TrackId> tracks;
|
||||||
if (!artist)
|
tracks.reserve(count);
|
||||||
throw RequestedDataNotFoundError {};
|
|
||||||
|
|
||||||
const User::pointer user {User::find(context.dbSession, context.userId)};
|
for (const ArtistId id : artistIds)
|
||||||
if (!user)
|
|
||||||
throw UserNotAuthorizedError {};
|
|
||||||
|
|
||||||
// "Returns a random collection of songs from the given artist and similar artists"
|
|
||||||
const auto trackResults {Track::find(context.dbSession, Track::FindParameters {}
|
|
||||||
.setArtist(artist->getId())
|
|
||||||
.setRange({0, count / 2})
|
|
||||||
.setSortMethod(TrackSortMethod::Random))};
|
|
||||||
|
|
||||||
std::vector<TrackId> tracks {trackResults.results};
|
|
||||||
|
|
||||||
for (const ArtistId similarArtistId : similarArtistIds)
|
|
||||||
{
|
{
|
||||||
const auto similarArtistTracks {Track::find(context.dbSession, Track::FindParameters {}
|
Track::FindParameters params;
|
||||||
.setArtist(similarArtistId)
|
params.setArtist(id);
|
||||||
.setRange({0, (count / 2) / 5})
|
params.setRange({0, meanTrackCountPerArtist});
|
||||||
.setSortMethod(TrackSortMethod::Random))};
|
params.setSortMethod(TrackSortMethod::Random);
|
||||||
|
|
||||||
|
const auto artistTracks {Track::find(context.dbSession, params)};
|
||||||
tracks.insert(std::end(tracks),
|
tracks.insert(std::end(tracks),
|
||||||
std::begin(similarArtistTracks.results),
|
std::begin(artistTracks.results),
|
||||||
std::end(similarArtistTracks.results));
|
std::end(artistTracks.results));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return tracks;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
std::vector<TrackId>
|
||||||
|
findSimilarSongs(RequestContext& context, ReleaseId releaseId, std::size_t count)
|
||||||
|
{
|
||||||
|
// API says: "Returns a random collection of songs from the given artist and similar artists"
|
||||||
|
// so let's extend this for release
|
||||||
|
const std::size_t similarReleaseCount {count / 5};
|
||||||
|
std::vector<ReleaseId> releaseIds {Service<Recommendation::IRecommendationService>::get()->getSimilarReleases(releaseId, similarReleaseCount)};
|
||||||
|
releaseIds.push_back(releaseId);
|
||||||
|
|
||||||
|
const std::size_t meanTrackCountPerRelease {(count / releaseIds.size()) + 1};
|
||||||
|
|
||||||
|
auto transaction {context.dbSession.createSharedTransaction()};
|
||||||
|
|
||||||
|
std::vector<TrackId> tracks;
|
||||||
|
tracks.reserve(count);
|
||||||
|
|
||||||
|
for (const ReleaseId id : releaseIds)
|
||||||
|
{
|
||||||
|
Track::FindParameters params;
|
||||||
|
params.setRelease(id);
|
||||||
|
params.setRange({0, meanTrackCountPerRelease});
|
||||||
|
params.setSortMethod(TrackSortMethod::Random);
|
||||||
|
|
||||||
|
const auto releaseTracks {Track::find(context.dbSession, params)};
|
||||||
|
tracks.insert(std::end(tracks),
|
||||||
|
std::begin(releaseTracks.results),
|
||||||
|
std::end(releaseTracks.results));
|
||||||
|
}
|
||||||
|
|
||||||
|
return tracks;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
std::vector<TrackId>
|
||||||
|
findSimilarSongs(RequestContext&, TrackId trackId, std::size_t count)
|
||||||
|
{
|
||||||
|
return Service<Recommendation::IRecommendationService>::get()->findSimilarTracks({trackId}, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
Response
|
||||||
|
handleGetSimilarSongsRequestCommon(RequestContext& context, bool id3)
|
||||||
|
{
|
||||||
|
// Optional params
|
||||||
|
std::size_t count {getParameterAs<std::size_t>(context.parameters, "count").value_or(50)};
|
||||||
|
|
||||||
|
std::vector<TrackId> tracks;
|
||||||
|
|
||||||
|
if (const auto artistId {getParameterAs<ArtistId>(context.parameters, "id")})
|
||||||
|
tracks = findSimilarSongs(context, *artistId, count);
|
||||||
|
else if (const auto releaseId {getParameterAs<ReleaseId>(context.parameters, "id")})
|
||||||
|
tracks = findSimilarSongs(context, *releaseId, count);
|
||||||
|
else if (const auto trackId {getParameterAs<TrackId>(context.parameters, "id")})
|
||||||
|
tracks = findSimilarSongs(context, *trackId, count);
|
||||||
|
else
|
||||||
|
throw BadParameterGenericError {"id"};
|
||||||
|
|
||||||
Random::shuffleContainer(tracks);
|
Random::shuffleContainer(tracks);
|
||||||
|
|
||||||
|
auto transaction {context.dbSession.createSharedTransaction()};
|
||||||
|
|
||||||
|
User::pointer user {User::find(context.dbSession, context.userId)};
|
||||||
|
if (!user)
|
||||||
|
throw UserNotAuthorizedError {};
|
||||||
|
|
||||||
Response response {Response::createOkResponse(context.serverProtocolVersion)};
|
Response response {Response::createOkResponse(context.serverProtocolVersion)};
|
||||||
Response::Node& similarSongsNode {response.createNode(id3 ? "similarSongs2" : "similarSongs")};
|
Response::Node& similarSongsNode {response.createNode(id3 ? "similarSongs2" : "similarSongs")};
|
||||||
for (const TrackId trackId : tracks)
|
for (const TrackId trackId : tracks)
|
||||||
|
|||||||
Reference in New Issue
Block a user