Subsonic API: some more factorizations in media retrievals
This commit is contained in:
@@ -144,9 +144,15 @@ static Response handleSearch3Request(RequestContext& context);
|
|||||||
static Response handleUpdatePlaylistRequest(RequestContext& context);
|
static Response handleUpdatePlaylistRequest(RequestContext& context);
|
||||||
|
|
||||||
// MediaRetrievals
|
// MediaRetrievals
|
||||||
using MediaRetrievalHandlerFunc = std::function<void(RequestContext&, Wt::Http::ResponseContinuation*, Wt::Http::Response& response)>;
|
struct MediaRetrievalResult
|
||||||
void handleStream(RequestContext& context, Wt::Http::ResponseContinuation* continuation, Wt::Http::Response& response);
|
{
|
||||||
void handleGetCoverArt(RequestContext& context, Wt::Http::ResponseContinuation* continuation, Wt::Http::Response& response);
|
std::string mimeType;
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
Wt::cpp17::any continuationData;
|
||||||
|
};
|
||||||
|
using MediaRetrievalHandlerFunc = std::function<MediaRetrievalResult(RequestContext&, Wt::Http::ResponseContinuation*)>;
|
||||||
|
MediaRetrievalResult handleStream(RequestContext& context, Wt::Http::ResponseContinuation* continuation);
|
||||||
|
MediaRetrievalResult handleGetCoverArt(RequestContext& context, Wt::Http::ResponseContinuation* continuation);
|
||||||
|
|
||||||
static std::map<std::string, RequestHandlerFunc> requestHandlers
|
static std::map<std::string, RequestHandlerFunc> requestHandlers
|
||||||
{
|
{
|
||||||
@@ -369,6 +375,9 @@ SubsonicResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Resp
|
|||||||
if (itHandler != requestHandlers.end())
|
if (itHandler != requestHandlers.end())
|
||||||
{
|
{
|
||||||
Response resp {(itHandler->second)(requestContext)};
|
Response resp {(itHandler->second)(requestContext)};
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
resp.write(response.out(), format);
|
resp.write(response.out(), format);
|
||||||
response.setMimeType(ResponseFormatToMimeType(format));
|
response.setMimeType(ResponseFormatToMimeType(format));
|
||||||
return;
|
return;
|
||||||
@@ -377,12 +386,31 @@ SubsonicResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Resp
|
|||||||
auto itStreamHandler {mediaRetrievalHandlers.find(request.path())};
|
auto itStreamHandler {mediaRetrievalHandlers.find(request.path())};
|
||||||
if (itStreamHandler != mediaRetrievalHandlers.end())
|
if (itStreamHandler != mediaRetrievalHandlers.end())
|
||||||
{
|
{
|
||||||
itStreamHandler->second(requestContext, request.continuation(), response);
|
MediaRetrievalResult res {itStreamHandler->second(requestContext, request.continuation())};
|
||||||
return;
|
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
|
if (!res.mimeType.empty())
|
||||||
|
response.setMimeType(res.mimeType);
|
||||||
|
if (!res.data.empty())
|
||||||
|
{
|
||||||
|
response.out().write(reinterpret_cast<const char *>(&res.data[0]), res.data.size());
|
||||||
|
if (!response.out())
|
||||||
|
{
|
||||||
|
LMS_LOG(API_SUBSONIC, ERROR) << "Write failed!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.continuationData.has_value())
|
||||||
|
{
|
||||||
|
auto continuation {response.createContinuation()};
|
||||||
|
continuation->setData(std::move(res.continuationData));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LMS_LOG(API_SUBSONIC, ERROR) << "Unhandled command '" << request.path() << "'";
|
LMS_LOG(API_SUBSONIC, ERROR) << "Unhandled command '" << request.path() << "'";
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (const Error& e)
|
catch (const Error& e)
|
||||||
{
|
{
|
||||||
@@ -1385,9 +1413,11 @@ createTranscoder(RequestContext& context)
|
|||||||
return std::make_shared<Av::Transcoder>(trackPath, parameters);
|
return std::make_shared<Av::Transcoder>(trackPath, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
MediaRetrievalResult
|
||||||
handleStream(RequestContext& context, Wt::Http::ResponseContinuation* continuation, Wt::Http::Response& response)
|
handleStream(RequestContext& context, Wt::Http::ResponseContinuation* continuation)
|
||||||
{
|
{
|
||||||
|
MediaRetrievalResult res;
|
||||||
|
|
||||||
// TODO store only weak ptrs and use a ring container to store shared_ptr?
|
// TODO store only weak ptrs and use a ring container to store shared_ptr?
|
||||||
std::shared_ptr<Av::Transcoder> transcoder;
|
std::shared_ptr<Av::Transcoder> transcoder;
|
||||||
|
|
||||||
@@ -1396,7 +1426,7 @@ handleStream(RequestContext& context, Wt::Http::ResponseContinuation* continuati
|
|||||||
transcoder = createTranscoder(context);
|
transcoder = createTranscoder(context);
|
||||||
transcoder->start();
|
transcoder->start();
|
||||||
|
|
||||||
response.setMimeType(transcoder->getOutputMimeType());
|
res.mimeType = transcoder->getOutputMimeType();
|
||||||
LMS_LOG(API_SUBSONIC, DEBUG) << "Mime type set to '" << transcoder->getOutputMimeType() << "'";
|
LMS_LOG(API_SUBSONIC, DEBUG) << "Mime type set to '" << transcoder->getOutputMimeType() << "'";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1410,53 +1440,44 @@ handleStream(RequestContext& context, Wt::Http::ResponseContinuation* continuati
|
|||||||
if (!transcoder->isComplete())
|
if (!transcoder->isComplete())
|
||||||
{
|
{
|
||||||
static constexpr std::size_t chunkSize {65536*4};
|
static constexpr std::size_t chunkSize {65536*4};
|
||||||
|
res.data.reserve(chunkSize);
|
||||||
|
|
||||||
std::vector<unsigned char> data;
|
transcoder->process(res.data, chunkSize);
|
||||||
data.reserve(chunkSize);
|
|
||||||
|
|
||||||
transcoder->process(data, chunkSize);
|
|
||||||
|
|
||||||
response.out().write(reinterpret_cast<char*>(&data[0]), data.size());
|
|
||||||
|
|
||||||
if (!response.out())
|
|
||||||
{
|
|
||||||
LMS_LOG(API_SUBSONIC, ERROR) << "Write failed!";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!transcoder->isComplete())
|
if (!transcoder->isComplete())
|
||||||
{
|
res.continuationData = std::move(transcoder);
|
||||||
continuation = response.createContinuation();
|
|
||||||
continuation->setData(transcoder);
|
return res;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
MediaRetrievalResult
|
||||||
handleGetCoverArt(RequestContext& context, Wt::Http::ResponseContinuation* continuation, Wt::Http::Response& response)
|
handleGetCoverArt(RequestContext& context, Wt::Http::ResponseContinuation*)
|
||||||
{
|
{
|
||||||
// Mandatory params
|
// Mandatory params
|
||||||
Id id {getMandatoryParameterAs<Id>(context.parameters, "id")};
|
Id id {getMandatoryParameterAs<Id>(context.parameters, "id")};
|
||||||
|
|
||||||
std::size_t size {getParameterAs<std::size_t>(context.parameters, "size").get_value_or(256)};
|
std::size_t size {getParameterAs<std::size_t>(context.parameters, "size").get_value_or(256)};
|
||||||
|
|
||||||
size = clamp(size, std::size_t {32}, std::size_t {1024});
|
size = clamp(size, std::size_t {32}, std::size_t {1024});
|
||||||
|
|
||||||
std::vector<uint8_t> cover;
|
MediaRetrievalResult res;
|
||||||
|
|
||||||
switch (id.type)
|
switch (id.type)
|
||||||
{
|
{
|
||||||
case Id::Type::Track:
|
case Id::Type::Track:
|
||||||
cover = getService<CoverArt::Grabber>()->getFromTrack(context.db.getSession(), id.value, Image::Format::JPEG, size);
|
res.data = getService<CoverArt::Grabber>()->getFromTrack(context.db.getSession(), id.value, Image::Format::JPEG, size);
|
||||||
break;
|
break;
|
||||||
case Id::Type::Release:
|
case Id::Type::Release:
|
||||||
cover = getService<CoverArt::Grabber>()->getFromRelease(context.db.getSession(), id.value, Image::Format::JPEG, size);
|
res.data = getService<CoverArt::Grabber>()->getFromRelease(context.db.getSession(), id.value, Image::Format::JPEG, size);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw Error {Error::CustomType::BadId};
|
throw Error {Error::CustomType::BadId};
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setMimeType( Image::format_to_mimeType(Image::Format::JPEG) );
|
res.mimeType = Image::format_to_mimeType(Image::Format::JPEG);
|
||||||
response.out().write(reinterpret_cast<const char *>(&cover[0]), cover.size());
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace api::subsonic
|
} // namespace api::subsonic
|
||||||
|
|||||||
Reference in New Issue
Block a user