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