Initial support for multi library, still WIP
This commit is contained in:
@@ -31,6 +31,12 @@ namespace API::Subsonic
|
||||
return "ar-" + id.toString();
|
||||
}
|
||||
|
||||
std::string idToString(Database::MediaLibraryId id)
|
||||
{
|
||||
// No need to prefix as this is only used at well known places
|
||||
return id.toString();
|
||||
}
|
||||
|
||||
std::string idToString(Database::ReleaseId id)
|
||||
{
|
||||
return "al-" + id.toString();
|
||||
@@ -70,6 +76,15 @@ namespace StringUtils
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::optional<Database::MediaLibraryId> readAs(std::string_view str)
|
||||
{
|
||||
if (const auto value{ StringUtils::readAs<Database::MediaLibraryId::ValueType>(str) })
|
||||
return Database::MediaLibraryId{ *value };
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::optional<Database::ReleaseId> readAs(std::string_view str)
|
||||
{
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "database/ArtistId.hpp"
|
||||
#include "database/MediaLibraryId.hpp"
|
||||
#include "database/ReleaseId.hpp"
|
||||
#include "database/TrackId.hpp"
|
||||
#include "database/TrackListId.hpp"
|
||||
@@ -30,6 +31,7 @@ namespace API::Subsonic
|
||||
struct RootId {};
|
||||
|
||||
std::string idToString(Database::ArtistId id);
|
||||
std::string idToString(Database::MediaLibraryId id);
|
||||
std::string idToString(Database::ReleaseId id);
|
||||
std::string idToString(Database::TrackId id);
|
||||
std::string idToString(Database::TrackListId id);
|
||||
@@ -45,6 +47,9 @@ namespace StringUtils
|
||||
template<>
|
||||
std::optional<Database::ArtistId> readAs(std::string_view str);
|
||||
|
||||
template<>
|
||||
std::optional<Database::MediaLibraryId> readAs(std::string_view str);
|
||||
|
||||
template<>
|
||||
std::optional<Database::ReleaseId> readAs(std::string_view str);
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "responses/Song.hpp"
|
||||
#include "utils/Service.hpp"
|
||||
#include "ParameterParsing.hpp"
|
||||
#include "SubsonicId.hpp"
|
||||
|
||||
namespace API::Subsonic
|
||||
{
|
||||
@@ -45,6 +46,7 @@ namespace API::Subsonic
|
||||
const std::string type{ getMandatoryParameterAs<std::string>(context.parameters, "type") };
|
||||
|
||||
// Optional params
|
||||
const MediaLibraryId mediaLibraryId{ getParameterAs<MediaLibraryId>(context.parameters, "musicFolderId").value_or(MediaLibraryId{}) };
|
||||
const std::size_t size{ getParameterAs<std::size_t>(context.parameters, "size").value_or(10) };
|
||||
const std::size_t offset{ getParameterAs<std::size_t>(context.parameters, "offset").value_or(0) };
|
||||
if (size > defaultMaxCountSize)
|
||||
@@ -67,12 +69,18 @@ namespace API::Subsonic
|
||||
Release::FindParameters params;
|
||||
params.setSortMethod(ReleaseSortMethod::Name);
|
||||
params.setRange(range);
|
||||
params.setMediaLibrary(mediaLibraryId);
|
||||
|
||||
releases = Release::findIds(context.dbSession, params);
|
||||
}
|
||||
else if (type == "alphabeticalByArtist")
|
||||
{
|
||||
releases = Release::findIdsOrderedByArtist(context.dbSession, range);
|
||||
Release::FindParameters params;
|
||||
params.setSortMethod(ReleaseSortMethod::ArtistNameThenName);
|
||||
params.setRange(range);
|
||||
params.setMediaLibrary(mediaLibraryId);
|
||||
|
||||
releases = Release::findIds(context.dbSession, params);
|
||||
}
|
||||
else if (type == "byGenre")
|
||||
{
|
||||
@@ -87,6 +95,7 @@ namespace API::Subsonic
|
||||
params.setClusters({ cluster->getId() });
|
||||
params.setSortMethod(ReleaseSortMethod::Name);
|
||||
params.setRange(range);
|
||||
params.setMediaLibrary(mediaLibraryId);
|
||||
|
||||
releases = Release::findIds(context.dbSession, params);
|
||||
}
|
||||
@@ -101,18 +110,25 @@ namespace API::Subsonic
|
||||
params.setSortMethod(ReleaseSortMethod::Date);
|
||||
params.setRange(range);
|
||||
params.setDateRange(DateRange::fromYearRange(fromYear, toYear));
|
||||
params.setMediaLibrary(mediaLibraryId);
|
||||
|
||||
releases = Release::findIds(context.dbSession, params);
|
||||
}
|
||||
else if (type == "frequent")
|
||||
{
|
||||
releases = scrobblingService.getTopReleases(context.userId, {}, range);
|
||||
Scrobbling::IScrobblingService::FindParameters params;
|
||||
params.setUser(context.userId);
|
||||
params.setRange(range);
|
||||
params.setMediaLibrary(mediaLibraryId);
|
||||
|
||||
releases = scrobblingService.getTopReleases(params);
|
||||
}
|
||||
else if (type == "newest")
|
||||
{
|
||||
Release::FindParameters params;
|
||||
params.setSortMethod(ReleaseSortMethod::LastWritten);
|
||||
params.setRange(range);
|
||||
params.setMediaLibrary(mediaLibraryId);
|
||||
|
||||
releases = Release::findIds(context.dbSession, params);
|
||||
}
|
||||
@@ -123,18 +139,26 @@ namespace API::Subsonic
|
||||
Release::FindParameters params;
|
||||
params.setSortMethod(ReleaseSortMethod::Random);
|
||||
params.setRange(Range{ 0, size });
|
||||
params.setMediaLibrary(mediaLibraryId);
|
||||
|
||||
releases = Release::findIds(context.dbSession, params);
|
||||
}
|
||||
else if (type == "recent")
|
||||
{
|
||||
releases = scrobblingService.getRecentReleases(context.userId, {}, range);
|
||||
Scrobbling::IScrobblingService::FindParameters params;
|
||||
params.setUser(context.userId);
|
||||
params.setRange(range);
|
||||
params.setMediaLibrary(mediaLibraryId);
|
||||
|
||||
releases = scrobblingService.getRecentReleases(params);
|
||||
}
|
||||
else if (type == "starred")
|
||||
{
|
||||
Feedback::IFeedbackService::FindParameters params;
|
||||
params.setUser(context.userId);
|
||||
params.setRange(range);
|
||||
params.setMediaLibrary(mediaLibraryId);
|
||||
|
||||
releases = feedbackService.findStarredReleases(params);
|
||||
}
|
||||
else
|
||||
@@ -156,6 +180,9 @@ namespace API::Subsonic
|
||||
|
||||
Response handleGetStarredRequestCommon(RequestContext& context, bool id3)
|
||||
{
|
||||
// Optional parameters
|
||||
const MediaLibraryId mediaLibrary{ getParameterAs<MediaLibraryId>(context.parameters, "musicFolderId").value_or(MediaLibraryId{}) };
|
||||
|
||||
auto transaction{ context.dbSession.createReadTransaction() };
|
||||
|
||||
User::pointer user{ User::find(context.dbSession, context.userId) };
|
||||
@@ -167,9 +194,6 @@ namespace API::Subsonic
|
||||
|
||||
Feedback::IFeedbackService& feedbackService{ *Service<Feedback::IFeedbackService>::get() };
|
||||
|
||||
Feedback::IFeedbackService::FindParameters findParameters;
|
||||
findParameters.setUser(context.userId);
|
||||
|
||||
{
|
||||
Feedback::IFeedbackService::ArtistFindParameters artistFindParams;
|
||||
artistFindParams.setUser(context.userId);
|
||||
@@ -181,6 +205,10 @@ namespace API::Subsonic
|
||||
}
|
||||
}
|
||||
|
||||
Feedback::IFeedbackService::FindParameters findParameters;
|
||||
findParameters.setUser(context.userId);
|
||||
findParameters.setMediaLibrary(mediaLibrary);
|
||||
|
||||
for (const ReleaseId releaseId : feedbackService.findStarredReleases(findParameters).results)
|
||||
{
|
||||
if (auto release{ Release::find(context.dbSession, releaseId) })
|
||||
@@ -210,6 +238,7 @@ namespace API::Subsonic
|
||||
Response handleGetRandomSongsRequest(RequestContext& context)
|
||||
{
|
||||
// Optional params
|
||||
const MediaLibraryId mediaLibraryId{ getParameterAs<MediaLibraryId>(context.parameters, "musicFolderId").value_or(MediaLibraryId{}) };
|
||||
std::size_t size{ getParameterAs<std::size_t>(context.parameters, "size").value_or(50) };
|
||||
if (size > defaultMaxCountSize)
|
||||
throw ParameterValueTooHighGenericError{ "size", defaultMaxCountSize };
|
||||
@@ -226,6 +255,7 @@ namespace API::Subsonic
|
||||
Track::FindParameters params;
|
||||
params.setSortMethod(TrackSortMethod::Random);
|
||||
params.setRange(Range{ 0, size });
|
||||
params.setMediaLibrary(mediaLibraryId);
|
||||
|
||||
Track::find(context.dbSession, params, [&](const Track::pointer& track)
|
||||
{
|
||||
@@ -241,6 +271,7 @@ namespace API::Subsonic
|
||||
std::string genre{ getMandatoryParameterAs<std::string>(context.parameters, "genre") };
|
||||
|
||||
// Optional params
|
||||
const MediaLibraryId mediaLibrary{ getParameterAs<MediaLibraryId>(context.parameters, "musicFolderId").value_or(MediaLibraryId{}) };
|
||||
std::size_t count{ getParameterAs<std::size_t>(context.parameters, "count").value_or(10) };
|
||||
if (count > defaultMaxCountSize)
|
||||
throw ParameterValueTooHighGenericError{"count", defaultMaxCountSize};
|
||||
@@ -267,6 +298,7 @@ namespace API::Subsonic
|
||||
Track::FindParameters params;
|
||||
params.setClusters({ cluster->getId() });
|
||||
params.setRange(Range{ offset, count });
|
||||
params.setMediaLibrary(mediaLibrary);
|
||||
|
||||
Track::find(context.dbSession, params, [&](const Track::pointer& track)
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "database/Artist.hpp"
|
||||
#include "database/Cluster.hpp"
|
||||
#include "database/MediaLibrary.hpp"
|
||||
#include "database/Session.hpp"
|
||||
#include "database/Release.hpp"
|
||||
#include "database/Track.hpp"
|
||||
@@ -92,6 +93,9 @@ namespace API::Subsonic
|
||||
|
||||
Response handleGetArtistsRequestCommon(RequestContext& context, bool id3)
|
||||
{
|
||||
// Optional params
|
||||
const MediaLibraryId mediaLibrary{ getParameterAs<MediaLibraryId>(context.parameters, "musicFolderId").value_or(MediaLibraryId{}) };
|
||||
|
||||
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
|
||||
|
||||
Response::Node& artistsNode{ response.createNode(id3 ? "artists" : "indexes") };
|
||||
@@ -119,6 +123,7 @@ namespace API::Subsonic
|
||||
break;
|
||||
}
|
||||
}
|
||||
parameters.setMediaLibrary(mediaLibrary);
|
||||
|
||||
// This endpoint does not scale: make sort lived transactions in order not to block the whole application
|
||||
|
||||
@@ -283,9 +288,14 @@ namespace API::Subsonic
|
||||
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
|
||||
Response::Node& musicFoldersNode{ response.createNode("musicFolders") };
|
||||
|
||||
Response::Node& musicFolderNode{ musicFoldersNode.createArrayChild("musicFolder") };
|
||||
musicFolderNode.setAttribute("id", "0");
|
||||
musicFolderNode.setAttribute("name", "Music");
|
||||
auto transaction{ context.dbSession.createReadTransaction() };
|
||||
MediaLibrary::find(context.dbSession, [&](const MediaLibrary::pointer& library)
|
||||
{
|
||||
Response::Node& musicFolderNode{ musicFoldersNode.createArrayChild("musicFolder") };
|
||||
|
||||
musicFolderNode.setAttribute("id", idToString(library->getId()));
|
||||
musicFolderNode.setAttribute("name", library->getName());
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -502,7 +512,12 @@ namespace API::Subsonic
|
||||
Response response{ Response::createOkResponse(context.serverProtocolVersion) };
|
||||
Response::Node& topSongs{ response.createNode("topSongs") };
|
||||
|
||||
const auto trackIds{ Service<Scrobbling::IScrobblingService>::get()->getTopTracks(context.userId, artists.front()->getId(), {}, Database::Range{ 0, count }) };
|
||||
Scrobbling::IScrobblingService::FindParameters params;
|
||||
params.setUser(context.userId);
|
||||
params.setRange(Database::Range{ 0, count });
|
||||
params.setArtist(artists.front()->getId());
|
||||
|
||||
const auto trackIds{ Service<Scrobbling::IScrobblingService>::get()->getTopTracks(params) };
|
||||
for (const TrackId trackId : trackIds.results)
|
||||
{
|
||||
if (Track::pointer track{ Track::find(context.dbSession, trackId) })
|
||||
|
||||
@@ -28,9 +28,8 @@
|
||||
#include "responses/Artist.hpp"
|
||||
#include "responses/Song.hpp"
|
||||
#include "ParameterParsing.hpp"
|
||||
#include "SubsonicId.hpp"
|
||||
|
||||
#include "ParameterParsing.hpp"
|
||||
|
||||
namespace API::Subsonic
|
||||
{
|
||||
using namespace Database;
|
||||
@@ -43,6 +42,9 @@ namespace API::Subsonic
|
||||
std::string queryString{ getMandatoryParameterAs<std::string>(context.parameters, "query") };
|
||||
std::string_view query{ queryString };
|
||||
|
||||
// Optional params
|
||||
const MediaLibraryId mediaLibrary{ getParameterAs<MediaLibraryId>(context.parameters, "musicFolderId").value_or(MediaLibraryId{}) };
|
||||
|
||||
// Symfonium adds extra ""
|
||||
if (context.clientInfo.name == "Symfonium")
|
||||
query = StringUtils::stringTrim(query, "\"");
|
||||
@@ -78,6 +80,7 @@ namespace API::Subsonic
|
||||
Artist::FindParameters params;
|
||||
params.setKeywords(keywords);
|
||||
params.setRange(Range{ artistOffset, artistCount });
|
||||
params.setMediaLibrary(mediaLibrary);
|
||||
|
||||
Artist::find(context.dbSession, params, [&](const Artist::pointer& artist)
|
||||
{
|
||||
@@ -90,6 +93,7 @@ namespace API::Subsonic
|
||||
Release::FindParameters params;
|
||||
params.setKeywords(keywords);
|
||||
params.setRange(Range{ albumOffset, albumCount });
|
||||
params.setMediaLibrary(mediaLibrary);
|
||||
|
||||
Release::find(context.dbSession, params, [&](const Release::pointer& release)
|
||||
{
|
||||
@@ -102,6 +106,7 @@ namespace API::Subsonic
|
||||
Track::FindParameters params;
|
||||
params.setKeywords(keywords);
|
||||
params.setRange(Range{ songOffset, songCount });
|
||||
params.setMediaLibrary(mediaLibrary);
|
||||
|
||||
Track::find(context.dbSession, params, [&](const Track::pointer& track)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user