Isolate Audio file resource request handler to ease reuse. ref #36
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Emeric Poupon
|
||||
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
namespace Av {
|
||||
|
||||
#define LMS_LOG_TRANSCODE(sev) LMS_LOG(TRANSCODE, sev) << "[" << _id << "] - "
|
||||
#define LOG(sev) LMS_LOG(TRANSCODE, sev) << "[" << _id << "] - "
|
||||
|
||||
static std::atomic<size_t> globalId {};
|
||||
static std::filesystem::path ffmpegPath;
|
||||
@@ -59,7 +59,7 @@ Transcoder::start()
|
||||
else if (!std::filesystem::is_regular_file( _filePath) )
|
||||
return false;
|
||||
|
||||
LMS_LOG_TRANSCODE(INFO) << "Transcoding file '" << _filePath.string() << "'";
|
||||
LOG(INFO) << "Transcoding file '" << _filePath.string() << "'";
|
||||
|
||||
std::vector<std::string> args;
|
||||
|
||||
@@ -99,84 +99,57 @@ Transcoder::start()
|
||||
// Skip video flows (including covers)
|
||||
args.emplace_back("-vn");
|
||||
|
||||
// Output bitrates
|
||||
args.emplace_back("-b:a");
|
||||
args.emplace_back(std::to_string(_parameters.bitrate));
|
||||
|
||||
// Codecs and formats
|
||||
if (_parameters.encoding)
|
||||
switch (_parameters.encoding)
|
||||
{
|
||||
// Output bitrates
|
||||
args.emplace_back("-b:a");
|
||||
args.emplace_back(std::to_string(_parameters.bitrate));
|
||||
case Encoding::MP3:
|
||||
args.emplace_back("-f");
|
||||
args.emplace_back("mp3");
|
||||
break;
|
||||
|
||||
switch (*_parameters.encoding)
|
||||
{
|
||||
case Encoding::MP3:
|
||||
args.emplace_back("-f");
|
||||
args.emplace_back("mp3");
|
||||
break;
|
||||
case Encoding::OGG_OPUS:
|
||||
args.emplace_back("-acodec");
|
||||
args.emplace_back("libopus");
|
||||
args.emplace_back("-f");
|
||||
args.emplace_back("ogg");
|
||||
break;
|
||||
|
||||
case Encoding::OGG_OPUS:
|
||||
args.emplace_back("-acodec");
|
||||
args.emplace_back("libopus");
|
||||
args.emplace_back("-f");
|
||||
args.emplace_back("ogg");
|
||||
break;
|
||||
case Encoding::MATROSKA_OPUS:
|
||||
args.emplace_back("-acodec");
|
||||
args.emplace_back("libopus");
|
||||
args.emplace_back("-f");
|
||||
args.emplace_back("matroska");
|
||||
break;
|
||||
|
||||
case Encoding::MATROSKA_OPUS:
|
||||
args.emplace_back("-acodec");
|
||||
args.emplace_back("libopus");
|
||||
args.emplace_back("-f");
|
||||
args.emplace_back("matroska");
|
||||
break;
|
||||
case Encoding::OGG_VORBIS:
|
||||
args.emplace_back("-acodec");
|
||||
args.emplace_back("libvorbis");
|
||||
args.emplace_back("-f");
|
||||
args.emplace_back("ogg");
|
||||
break;
|
||||
|
||||
case Encoding::OGG_VORBIS:
|
||||
args.emplace_back("-acodec");
|
||||
args.emplace_back("libvorbis");
|
||||
args.emplace_back("-f");
|
||||
args.emplace_back("ogg");
|
||||
break;
|
||||
case Encoding::WEBM_VORBIS:
|
||||
args.emplace_back("-acodec");
|
||||
args.emplace_back("libvorbis");
|
||||
args.emplace_back("-f");
|
||||
args.emplace_back("webm");
|
||||
break;
|
||||
|
||||
case Encoding::WEBM_VORBIS:
|
||||
args.emplace_back("-acodec");
|
||||
args.emplace_back("libvorbis");
|
||||
args.emplace_back("-f");
|
||||
args.emplace_back("webm");
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
_outputMimeType = encodingToMimetype(*_parameters.encoding);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto mediaFileFormat {guessMediaFileFormat(_filePath)};
|
||||
|
||||
if (!mediaFileFormat)
|
||||
{
|
||||
LMS_LOG(AV, ERROR) << "Cannot guess media file format for '" << _filePath.string() << "'";
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
args.emplace_back("-acodec");
|
||||
args.emplace_back("copy");
|
||||
args.emplace_back("-f");
|
||||
args.emplace_back(mediaFileFormat->format);
|
||||
|
||||
// Workaround for ipod streams: make it compatible with non seekable output
|
||||
if (mediaFileFormat->format == "ipod")
|
||||
{
|
||||
args.emplace_back("-movflags");
|
||||
args.emplace_back("frag_keyframe");
|
||||
}
|
||||
|
||||
_outputMimeType = mediaFileFormat->mimeType;
|
||||
}
|
||||
|
||||
_outputMimeType = encodingToMimetype(_parameters.encoding);
|
||||
|
||||
args.emplace_back("pipe:1");
|
||||
|
||||
LMS_LOG_TRANSCODE(DEBUG) << "Dumping args (" << args.size() << ")";
|
||||
LOG(DEBUG) << "Dumping args (" << args.size() << ")";
|
||||
for (const std::string& arg : args)
|
||||
LMS_LOG_TRANSCODE(DEBUG) << "Arg = '" << arg << "'";
|
||||
LOG(DEBUG) << "Arg = '" << arg << "'";
|
||||
|
||||
// make sure only one thread is executing this part of code
|
||||
{
|
||||
@@ -190,18 +163,18 @@ Transcoder::start()
|
||||
_child->open(ffmpegPath.string(), args);
|
||||
if (!_child->is_open())
|
||||
{
|
||||
LMS_LOG_TRANSCODE(DEBUG) << "Exec failed!";
|
||||
LOG(DEBUG) << "Exec failed!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_child->out().eof())
|
||||
{
|
||||
LMS_LOG_TRANSCODE(DEBUG) << "Early end of file!";
|
||||
LOG(DEBUG) << "Early end of file!";
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
LMS_LOG_TRANSCODE(DEBUG) << "Stream opened!";
|
||||
LOG(DEBUG) << "Stream opened!";
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -214,12 +187,12 @@ Transcoder::process(std::vector<unsigned char>& output, std::size_t maxSize)
|
||||
|
||||
if (_child->out().fail())
|
||||
{
|
||||
LMS_LOG_TRANSCODE(DEBUG) << "Stdout FAILED 2";
|
||||
LOG(DEBUG) << "Stdout FAILED 2";
|
||||
}
|
||||
|
||||
if (_child->out().eof())
|
||||
{
|
||||
LMS_LOG_TRANSCODE(DEBUG) << "Stdout ENDED 2";
|
||||
LOG(DEBUG) << "Stdout ENDED 2";
|
||||
}
|
||||
|
||||
output.resize(maxSize);
|
||||
@@ -230,12 +203,12 @@ Transcoder::process(std::vector<unsigned char>& output, std::size_t maxSize)
|
||||
|
||||
if (_child->out().fail())
|
||||
{
|
||||
LMS_LOG_TRANSCODE(DEBUG) << "Stdout FAILED";
|
||||
LOG(DEBUG) << "Stdout FAILED";
|
||||
}
|
||||
|
||||
if (_child->out().eof())
|
||||
{
|
||||
LMS_LOG_TRANSCODE(DEBUG) << "Stdout EOF!";
|
||||
LOG(DEBUG) << "Stdout EOF!";
|
||||
_child->clear();
|
||||
|
||||
_isComplete = true;
|
||||
@@ -244,20 +217,20 @@ Transcoder::process(std::vector<unsigned char>& output, std::size_t maxSize)
|
||||
|
||||
_total += output.size();
|
||||
|
||||
LMS_LOG_TRANSCODE(DEBUG) << "nb bytes = " << output.size() << ", total = " << _total;
|
||||
LOG(DEBUG) << "nb bytes = " << output.size() << ", total = " << _total;
|
||||
}
|
||||
|
||||
Transcoder::~Transcoder()
|
||||
{
|
||||
LMS_LOG_TRANSCODE(DEBUG) << ", ~Transcoder called! Total produced bytes = " << _total;
|
||||
LOG(DEBUG) << ", ~Transcoder called! Total produced bytes = " << _total;
|
||||
|
||||
if (_child)
|
||||
{
|
||||
LMS_LOG_TRANSCODE(DEBUG) << "Child still here!";
|
||||
LOG(DEBUG) << "Child still here!";
|
||||
_child->rdbuf()->kill(SIGKILL);
|
||||
LMS_LOG_TRANSCODE(DEBUG) << "Closing...";
|
||||
LOG(DEBUG) << "Closing...";
|
||||
_child->rdbuf()->close();
|
||||
LMS_LOG_TRANSCODE(DEBUG) << "Closing DONE";
|
||||
LOG(DEBUG) << "Closing DONE";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Av {
|
||||
|
||||
struct TranscodeParameters
|
||||
{
|
||||
std::optional<Encoding> encoding; // If not set, no transcoding is performed
|
||||
Encoding encoding;
|
||||
std::size_t bitrate {128000};
|
||||
std::optional<std::size_t> stream; // Id of the stream to be transcoded (auto detect by default)
|
||||
std::optional<std::chrono::seconds> offset;
|
||||
|
||||
@@ -1845,7 +1845,6 @@ handleStream(RequestContext& context, Wt::Http::ResponseContinuation* continuati
|
||||
{
|
||||
MediaRetrievalResult res;
|
||||
|
||||
// TODO store only weak ptrs and use a ring container to store shared_ptr?
|
||||
std::shared_ptr<Av::Transcoder> transcoder;
|
||||
|
||||
if (!continuation)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
add_library(lmsutils SHARED
|
||||
impl/Config.cpp
|
||||
impl/FileResourceHandler.cpp
|
||||
impl/Logger.cpp
|
||||
impl/NetAddress.cpp
|
||||
impl/Path.cpp
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "utils/FileResourceHandler.hpp"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "utils/Logger.hpp"
|
||||
|
||||
namespace FileResourceHandler
|
||||
{
|
||||
|
||||
static constexpr std::size_t _chunkSize {262144};
|
||||
|
||||
static
|
||||
std::optional<ContinuationData>
|
||||
handleRequestPiecewise(const Wt::Http::Request& request,
|
||||
Wt::Http::Response& response,
|
||||
ContinuationData continuationData)
|
||||
{
|
||||
::uint64_t startByte {continuationData.offset};
|
||||
std::ifstream ifs {continuationData.path.string().c_str(), std::ios::in | std::ios::binary};
|
||||
|
||||
LMS_LOG(UTILS, DEBUG) << "startByte = " << startByte;
|
||||
|
||||
if (startByte == 0)
|
||||
{
|
||||
if (!ifs)
|
||||
{
|
||||
LMS_LOG(UTILS, ERROR) << "Cannot open file stream for '" << continuationData.path.string() << "'";
|
||||
response.setStatus(404);
|
||||
return std::nullopt;
|
||||
}
|
||||
else
|
||||
{
|
||||
response.setStatus(200);
|
||||
}
|
||||
|
||||
ifs.seekg(0, std::ios::end);
|
||||
const ::uint64_t fileSize {static_cast<::uint64_t>(ifs.tellg())};
|
||||
ifs.seekg(0, std::ios::beg);
|
||||
|
||||
LMS_LOG(UTILS, DEBUG) << "fileSize = " << fileSize;
|
||||
|
||||
const Wt::Http::Request::ByteRangeSpecifier ranges {request.getRanges(fileSize)};
|
||||
if (!ranges.isSatisfiable())
|
||||
{
|
||||
std::ostringstream contentRange;
|
||||
contentRange << "bytes */" << fileSize;
|
||||
response.setStatus(416); // Requested range not satisfiable
|
||||
response.addHeader("Content-Range", contentRange.str());
|
||||
|
||||
LMS_LOG(UTILS, DEBUG) << "Range not satisfiable";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (ranges.size() == 1)
|
||||
{
|
||||
LMS_LOG(UTILS, DEBUG) << "Range requested = " << ranges[0].firstByte() << "/" << ranges[0].lastByte();
|
||||
|
||||
response.setStatus(206);
|
||||
startByte = ranges[0].firstByte();
|
||||
continuationData.beyondLastByte = ranges[0].lastByte() + 1;
|
||||
|
||||
std::ostringstream contentRange;
|
||||
contentRange << "bytes " << startByte << "-"
|
||||
<< continuationData.beyondLastByte - 1 << "/" << fileSize;
|
||||
|
||||
response.addHeader("Content-Range", contentRange.str());
|
||||
response.setContentLength(continuationData.beyondLastByte - startByte);
|
||||
}
|
||||
else
|
||||
{
|
||||
LMS_LOG(UTILS, DEBUG) << "No range requested";
|
||||
|
||||
continuationData.beyondLastByte = fileSize;
|
||||
response.setContentLength(continuationData.beyondLastByte);
|
||||
}
|
||||
}
|
||||
|
||||
ifs.seekg(static_cast<std::istream::pos_type>(startByte));
|
||||
|
||||
std::vector<char> buf;
|
||||
buf.resize(_chunkSize);
|
||||
|
||||
::uint64_t restSize = continuationData.beyondLastByte - startByte;
|
||||
::uint64_t pieceSize = buf.size() > restSize ? restSize : buf.size();
|
||||
|
||||
ifs.read(&buf[0], pieceSize);
|
||||
const ::uint64_t actualPieceSize {static_cast<::uint64_t>(ifs.gcount())};
|
||||
response.out().write(&buf[0], actualPieceSize);
|
||||
|
||||
LMS_LOG(UTILS, DEBUG) << "Written " << actualPieceSize << " bytes";
|
||||
|
||||
LMS_LOG(UTILS, DEBUG) << "Progress: " << actualPieceSize << "/" << restSize;
|
||||
if (ifs.good() && actualPieceSize < restSize)
|
||||
{
|
||||
ContinuationData newContinuationData {continuationData};
|
||||
newContinuationData.offset = startByte + actualPieceSize;
|
||||
|
||||
LMS_LOG(UTILS, DEBUG) << "Job not complete! Next chunk offset = " << newContinuationData.offset;
|
||||
|
||||
return newContinuationData;
|
||||
}
|
||||
else
|
||||
{
|
||||
LMS_LOG(UTILS, DEBUG) << "Job complete!";
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
std::optional<ContinuationData>
|
||||
handleInitialRequest(const Wt::Http::Request& request,
|
||||
Wt::Http::Response& response,
|
||||
const std::filesystem::path& path)
|
||||
{
|
||||
ContinuationData continuationData;
|
||||
continuationData.path = path;
|
||||
continuationData.offset = 0;
|
||||
continuationData.beyondLastByte = 0;
|
||||
|
||||
LMS_LOG(UTILS, DEBUG) << "Initial request for file '" << path << "'";
|
||||
|
||||
return handleRequestPiecewise(request, response, continuationData);
|
||||
}
|
||||
|
||||
std::optional<ContinuationData>
|
||||
handleContinuationRequest(const Wt::Http::Request& request,
|
||||
Wt::Http::Response& response,
|
||||
const ContinuationData& continuationData)
|
||||
{
|
||||
LMS_LOG(UTILS, DEBUG) << "Continuation request for file '" << continuationData.path << "', offset = " << continuationData.offset;
|
||||
return handleRequestPiecewise(request, response, continuationData);
|
||||
}
|
||||
|
||||
} // ns FileResourceHandler
|
||||
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ const char* getModuleName(Module mod)
|
||||
case Module::RECOMMENDATION: return "RECOMMENDATION";
|
||||
case Module::TRANSCODE: return "TRANSCODE";
|
||||
case Module::UI: return "UI";
|
||||
case Module::UTILS: return "UTILS";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Emeric Poupon
|
||||
*
|
||||
* This file is part of LMS.
|
||||
*
|
||||
* LMS is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* LMS is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <Wt/Http/Request.h>
|
||||
#include <Wt/Http/Response.h>
|
||||
|
||||
|
||||
// Helper used to deliver file contents from a WResource
|
||||
namespace FileResourceHandler
|
||||
{
|
||||
struct ContinuationData
|
||||
{
|
||||
std::filesystem::path path;
|
||||
::uint64_t beyondLastByte;
|
||||
::uint64_t offset;
|
||||
};
|
||||
|
||||
std::optional<ContinuationData> handleInitialRequest(const Wt::Http::Request& request, Wt::Http::Response& response, const std::filesystem::path& path);
|
||||
std::optional<ContinuationData> handleContinuationRequest(const Wt::Http::Request& request, Wt::Http::Response& response, const ContinuationData& continuationData);
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ enum class Module
|
||||
RECOMMENDATION,
|
||||
TRANSCODE,
|
||||
UI,
|
||||
UTILS,
|
||||
};
|
||||
|
||||
const char* getModuleName(Module mod);
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "av/AvInfo.hpp"
|
||||
#include "database/Track.hpp"
|
||||
#include "utils/FileResourceHandler.hpp"
|
||||
#include "utils/Logger.hpp"
|
||||
#include "utils/String.hpp"
|
||||
#include "LmsApplication.hpp"
|
||||
@@ -87,126 +88,28 @@ void
|
||||
AudioFileResource::handleRequest(const Wt::Http::Request& request,
|
||||
Wt::Http::Response& response)
|
||||
{
|
||||
std::optional<FileResourceHandler::ContinuationData> continuationData;
|
||||
if (!request.continuation())
|
||||
{
|
||||
LOG(DEBUG) << "Initial request";
|
||||
|
||||
auto trackPath {getTrackPathFromURLArgs(request)};
|
||||
if (!trackPath)
|
||||
return;
|
||||
|
||||
ContinuationData continuationData;
|
||||
continuationData.path = *trackPath;
|
||||
continuationData.offset = 0;
|
||||
continuationData.beyondLastByte = 0;
|
||||
|
||||
{
|
||||
std::error_code ec;
|
||||
continuationData.fileSize = std::filesystem::file_size(*trackPath, ec);
|
||||
|
||||
if (ec)
|
||||
{
|
||||
LOG(ERROR) << "Cannot get file size for '" << *trackPath << "': " << ec.message();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "Initial request. File = '" << continuationData.path << "', size = " << continuationData.fileSize;
|
||||
|
||||
handleRequestPiecewise(request, response, continuationData);
|
||||
|
||||
const auto fileFormat {Av::guessMediaFileFormat(*trackPath)};
|
||||
const std::string mimeType {fileFormat ? fileFormat->mimeType : "application/octet-stream"};
|
||||
response.setMimeType(mimeType);
|
||||
|
||||
LOG(DEBUG) << "Mime type set to '" << mimeType << "'";
|
||||
continuationData = FileResourceHandler::handleInitialRequest(request, response, *trackPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
ContinuationData continuationData {Wt::cpp17::any_cast<ContinuationData>(request.continuation()->data())};
|
||||
handleRequestPiecewise(request, response, continuationData);
|
||||
auto currentContinuationData {Wt::cpp17::any_cast<FileResourceHandler::ContinuationData>(request.continuation()->data())};
|
||||
continuationData = FileResourceHandler::handleContinuationRequest(request, response, currentContinuationData);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioFileResource::handleRequestPiecewise(const Wt::Http::Request& request,
|
||||
Wt::Http::Response& response,
|
||||
ContinuationData continuationData)
|
||||
{
|
||||
LOG(DEBUG) << "Handling request. File = '" << continuationData.path << "', size = " << continuationData.fileSize << ", offset = " << continuationData.offset << ", beyondLastByte = " << continuationData.beyondLastByte;
|
||||
|
||||
::uint64_t startByte {continuationData.offset};
|
||||
std::ifstream ifs {continuationData.path.string().c_str(), std::ios::in | std::ios::binary};
|
||||
|
||||
if (startByte == 0)
|
||||
if (continuationData)
|
||||
{
|
||||
if (!ifs)
|
||||
{
|
||||
response.setStatus(404);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
response.setStatus(200);
|
||||
}
|
||||
|
||||
const Wt::Http::Request::ByteRangeSpecifier ranges {request.getRanges(continuationData.fileSize)};
|
||||
if (!ranges.isSatisfiable())
|
||||
{
|
||||
std::ostringstream contentRange;
|
||||
contentRange << "bytes */" << continuationData.fileSize;
|
||||
response.setStatus(416); // Requested range not satisfiable
|
||||
response.addHeader("Content-Range", contentRange.str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (ranges.size() == 1)
|
||||
{
|
||||
response.setStatus(206);
|
||||
startByte = ranges[0].firstByte();
|
||||
continuationData.beyondLastByte = ranges[0].lastByte() + 1;
|
||||
|
||||
std::ostringstream contentRange;
|
||||
contentRange << "bytes " << startByte << "-"
|
||||
<< continuationData.beyondLastByte - 1 << "/" << continuationData.fileSize;
|
||||
|
||||
response.addHeader("Content-Range", contentRange.str());
|
||||
response.setContentLength(continuationData.beyondLastByte - startByte);
|
||||
}
|
||||
else
|
||||
{
|
||||
continuationData.beyondLastByte = continuationData.fileSize;
|
||||
response.setContentLength(continuationData.beyondLastByte);
|
||||
}
|
||||
}
|
||||
|
||||
ifs.seekg(static_cast<std::istream::pos_type>(startByte));
|
||||
|
||||
std::vector<char> buf;
|
||||
buf.resize(_chunkSize);
|
||||
|
||||
::uint64_t restSize = continuationData.beyondLastByte - startByte;
|
||||
::uint64_t pieceSize = buf.size() > restSize ? restSize : buf.size();
|
||||
|
||||
ifs.read(&buf[0], pieceSize);
|
||||
const ::uint64_t actualPieceSize {static_cast<::uint64_t>(ifs.gcount())};
|
||||
response.out().write(&buf[0], actualPieceSize);
|
||||
|
||||
LOG(DEBUG) << "Written " << actualPieceSize << " bytes!";
|
||||
|
||||
if (ifs.good() && actualPieceSize < restSize)
|
||||
{
|
||||
LOG(DEBUG) << "Still more to do";
|
||||
|
||||
auto* continuation {response.createContinuation()};
|
||||
ContinuationData newContinuationData {continuationData};
|
||||
newContinuationData.offset = startByte + actualPieceSize;
|
||||
continuation->setData(newContinuationData);
|
||||
continuation->setData(*continuationData);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace UserInterface
|
||||
|
||||
|
||||
@@ -38,20 +38,8 @@ class AudioFileResource : public Wt::WResource
|
||||
|
||||
static constexpr std::size_t _chunkSize {262144};
|
||||
|
||||
struct ContinuationData
|
||||
{
|
||||
std::filesystem::path path;
|
||||
::uint64_t beyondLastByte;
|
||||
::uint64_t fileSize;
|
||||
::uint64_t offset;
|
||||
};
|
||||
|
||||
void handleRequest(const Wt::Http::Request& request,
|
||||
Wt::Http::Response& response);
|
||||
|
||||
void handleRequestPiecewise(const Wt::Http::Request& request,
|
||||
Wt::Http::Response& response,
|
||||
ContinuationData continuationData);
|
||||
Wt::Http::Response& response) override;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user