diff --git a/src/libs/av/CMakeLists.txt b/src/libs/av/CMakeLists.txt index 4205e35c..7c556cb3 100644 --- a/src/libs/av/CMakeLists.txt +++ b/src/libs/av/CMakeLists.txt @@ -1,6 +1,7 @@ add_library(lmsav SHARED impl/AudioFile.cpp + impl/RawResourceHandlerCreator.cpp impl/Transcoder.cpp impl/TranscodeResourceHandler.cpp impl/Types.cpp diff --git a/src/libs/av/impl/RawResourceHandlerCreator.cpp b/src/libs/av/impl/RawResourceHandlerCreator.cpp new file mode 100644 index 00000000..5272b421 --- /dev/null +++ b/src/libs/av/impl/RawResourceHandlerCreator.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 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 . + */ + +#include "av/RawResourceHandlerCreator.hpp" + +#include "av/IAudioFile.hpp" +#include "utils/FileResourceHandlerCreator.hpp" + +namespace Av +{ + std::unique_ptr createRawResourceHandler(const std::filesystem::path& path) + { + std::string mimeType; + const auto guessedAudioFormat{ Av::guessAudioFileFormat(path) }; + if (guessedAudioFormat) + mimeType = guessedAudioFormat->mimeType; + else + mimeType = "application/octet-stream"; + + return createFileResourceHandler(path, mimeType); + } +} diff --git a/src/libs/av/include/av/RawResourceHandlerCreator.hpp b/src/libs/av/include/av/RawResourceHandlerCreator.hpp new file mode 100644 index 00000000..ed7b0e14 --- /dev/null +++ b/src/libs/av/include/av/RawResourceHandlerCreator.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 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 . + */ + +#pragma once + +#include +#include + +#include "utils/IResourceHandler.hpp" + +namespace Av +{ + std::unique_ptr createRawResourceHandler(const std::filesystem::path& path); +} \ No newline at end of file diff --git a/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp b/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp index cddc758c..c452f48a 100644 --- a/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp +++ b/src/libs/subsonic/impl/entrypoints/MediaRetrieval.cpp @@ -20,6 +20,7 @@ #include "MediaRetrieval.hpp" #include "av/IAudioFile.hpp" +#include "av/RawResourceHandlerCreator.hpp" #include "av/TranscodeParameters.hpp" #include "av/TranscodeResourceHandlerCreator.hpp" #include "av/Types.hpp" @@ -160,7 +161,7 @@ namespace API::Subsonic trackPath = track->getPath(); } - resourceHandler = createFileResourceHandler(trackPath); + resourceHandler = Av::createRawResourceHandler(trackPath); } else { @@ -185,7 +186,7 @@ namespace API::Subsonic if (streamParameters.transcodeParameters) resourceHandler = Av::createTranscodeResourceHandler(streamParameters.inputFileParameters, *streamParameters.transcodeParameters, streamParameters.estimateContentLength); else - resourceHandler = createFileResourceHandler(streamParameters.inputFileParameters.trackPath); + resourceHandler = Av::createRawResourceHandler(streamParameters.inputFileParameters.trackPath); } else { diff --git a/src/libs/utils/impl/FileResourceHandler.cpp b/src/libs/utils/impl/FileResourceHandler.cpp index 8e3a6eb0..b0b4e53f 100644 --- a/src/libs/utils/impl/FileResourceHandler.cpp +++ b/src/libs/utils/impl/FileResourceHandler.cpp @@ -24,108 +24,106 @@ #include "utils/Logger.hpp" std::unique_ptr -createFileResourceHandler(const std::filesystem::path& path) +createFileResourceHandler(const std::filesystem::path& path, std::string_view mimeType) { - return std::make_unique(path); + return std::make_unique(path, mimeType); } - -FileResourceHandler::FileResourceHandler(const std::filesystem::path& path) -: _path {path} +FileResourceHandler::FileResourceHandler(const std::filesystem::path& path, std::string_view mimeType) + : _path{ path } + , _mimeType{ mimeType } { } - Wt::Http::ResponseContinuation* FileResourceHandler::processRequest(const Wt::Http::Request& request, Wt::Http::Response& response) { - ::uint64_t startByte {_offset}; - std::ifstream ifs {_path.string().c_str(), std::ios::in | std::ios::binary}; + ::uint64_t startByte{ _offset }; + std::ifstream ifs{ _path.string().c_str(), std::ios::in | std::ios::binary }; - if (startByte == 0) - { - if (!ifs) - { - LMS_LOG(UTILS, ERROR) << "Cannot open file stream for '" << _path.string() << "'"; - response.setStatus(404); - return {}; - } - else - { - response.setStatus(200); - } + if (startByte == 0) + { + if (!ifs) + { + LMS_LOG(UTILS, ERROR) << "Cannot open file stream for '" << _path.string() << "'"; + response.setStatus(404); + return {}; + } - ifs.seekg(0, std::ios::end); - const ::uint64_t fileSize {static_cast<::uint64_t>(ifs.tellg())}; - ifs.seekg(0, std::ios::beg); + 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) << "File '" << _path.string() << "', fileSize = " << fileSize; + LMS_LOG(UTILS, DEBUG) << "File '" << _path.string() << "', 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()); + 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 {}; - } + LMS_LOG(UTILS, DEBUG) << "Range not satisfiable"; + return {}; + } - if (ranges.size() == 1) - { - LMS_LOG(UTILS, DEBUG) << "Range requested = " << ranges[0].firstByte() << "/" << ranges[0].lastByte(); + if (ranges.size() == 1) + { + LMS_LOG(UTILS, DEBUG) << "Range requested = " << ranges[0].firstByte() << "/" << ranges[0].lastByte(); - response.setStatus(206); - startByte = ranges[0].firstByte(); - _beyondLastByte = ranges[0].lastByte() + 1; + response.setStatus(206); + startByte = ranges[0].firstByte(); + _beyondLastByte = ranges[0].lastByte() + 1; - std::ostringstream contentRange; - contentRange << "bytes " << startByte << "-" - << _beyondLastByte - 1 << "/" << fileSize; + std::ostringstream contentRange; + contentRange << "bytes " << startByte << "-" + << _beyondLastByte - 1 << "/" << fileSize; - response.addHeader("Content-Range", contentRange.str()); - response.setContentLength(_beyondLastByte - startByte); - } - else - { - LMS_LOG(UTILS, DEBUG) << "No range requested"; + response.addHeader("Content-Range", contentRange.str()); + response.setContentLength(_beyondLastByte - startByte); + } + else + { + LMS_LOG(UTILS, DEBUG) << "No range requested"; - _beyondLastByte = fileSize; - response.setContentLength(_beyondLastByte); - } - } - else if (!ifs) - { - LMS_LOG(UTILS, ERROR) << "Cannot reopen file stream for '" << _path.string() << "'"; - return {}; - } + response.setStatus(200); + _beyondLastByte = fileSize; + response.setContentLength(_beyondLastByte); + } - ifs.seekg(static_cast(startByte)); + LMS_LOG(UTILS, DEBUG) << "Mimetype set to '" << _mimeType << "'"; + response.setMimeType(_mimeType); + } + else if (!ifs) + { + LMS_LOG(UTILS, ERROR) << "Cannot reopen file stream for '" << _path.string() << "'"; + return {}; + } - std::vector buf; - buf.resize(_chunkSize); + ifs.seekg(static_cast(startByte)); - ::uint64_t restSize = _beyondLastByte - startByte; - ::uint64_t pieceSize = buf.size() > restSize ? restSize : buf.size(); + std::vector buf; + buf.resize(_chunkSize); - ifs.read(&buf[0], pieceSize); - const ::uint64_t actualPieceSize {static_cast<::uint64_t>(ifs.gcount())}; - response.out().write(&buf[0], actualPieceSize); + ::uint64_t restSize = _beyondLastByte - startByte; + ::uint64_t pieceSize = buf.size() > restSize ? restSize : buf.size(); - LMS_LOG(UTILS, DEBUG) << "Written " << actualPieceSize << " bytes"; + 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) << "Progress: " << actualPieceSize << "/" << restSize; - if (ifs.good() && actualPieceSize < restSize) - { - _offset = startByte + actualPieceSize; - LMS_LOG(UTILS, DEBUG) << "Job not complete! Next chunk offset = " << _offset; + LMS_LOG(UTILS, DEBUG) << "Written " << actualPieceSize << " bytes"; - return response.createContinuation(); - } + LMS_LOG(UTILS, DEBUG) << "Progress: " << actualPieceSize << "/" << restSize; + if (ifs.good() && actualPieceSize < restSize) + { + _offset = startByte + actualPieceSize; + LMS_LOG(UTILS, DEBUG) << "Job not complete! Next chunk offset = " << _offset; - LMS_LOG(UTILS, DEBUG) << "Job complete!"; - return nullptr; + return response.createContinuation(); + } + + LMS_LOG(UTILS, DEBUG) << "Job complete!"; + return nullptr; } - diff --git a/src/libs/utils/impl/FileResourceHandler.hpp b/src/libs/utils/impl/FileResourceHandler.hpp index 8293f587..c6e8e58a 100644 --- a/src/libs/utils/impl/FileResourceHandler.hpp +++ b/src/libs/utils/impl/FileResourceHandler.hpp @@ -20,21 +20,24 @@ #pragma once #include +#include +#include #include "utils/IResourceHandler.hpp" class FileResourceHandler final : public IResourceHandler { - public: - FileResourceHandler(const std::filesystem::path& filePath); +public: + FileResourceHandler(const std::filesystem::path& filePath, std::string_view mimeType); - private: - Wt::Http::ResponseContinuation* processRequest(const Wt::Http::Request& request, Wt::Http::Response& response) override; - void abort() override {}; +private: + Wt::Http::ResponseContinuation* processRequest(const Wt::Http::Request& request, Wt::Http::Response& response) override; + void abort() override {}; - static constexpr std::size_t _chunkSize {65536}; + static constexpr std::size_t _chunkSize{ 65536 }; - std::filesystem::path _path; - ::uint64_t _beyondLastByte {}; - ::uint64_t _offset {}; + std::filesystem::path _path; + std::string _mimeType; + ::uint64_t _beyondLastByte{}; + ::uint64_t _offset{}; }; diff --git a/src/libs/utils/include/utils/FileResourceHandlerCreator.hpp b/src/libs/utils/include/utils/FileResourceHandlerCreator.hpp index 8f69e18a..00106721 100644 --- a/src/libs/utils/include/utils/FileResourceHandlerCreator.hpp +++ b/src/libs/utils/include/utils/FileResourceHandlerCreator.hpp @@ -21,8 +21,9 @@ #include #include +#include #include "utils/IResourceHandler.hpp" -std::unique_ptr createFileResourceHandler(const std::filesystem::path& path); +std::unique_ptr createFileResourceHandler(const std::filesystem::path& path, std::string_view mimeType); diff --git a/src/lms/ui/resource/AudioFileResource.cpp b/src/lms/ui/resource/AudioFileResource.cpp index e7a8809b..293e7aac 100644 --- a/src/lms/ui/resource/AudioFileResource.cpp +++ b/src/lms/ui/resource/AudioFileResource.cpp @@ -23,9 +23,9 @@ #include #include "av/IAudioFile.hpp" +#include "av/RawResourceHandlerCreator.hpp" #include "services/database/Session.hpp" #include "services/database/Track.hpp" -#include "utils/FileResourceHandlerCreator.hpp" #include "utils/Logger.hpp" #include "utils/String.hpp" #include "LmsApplication.hpp" @@ -94,16 +94,7 @@ AudioFileResource::handleRequest(const Wt::Http::Request& request, if (!trackPath) return; - fileResourceHandler = createFileResourceHandler(*trackPath); - - const auto guessedAudioFormat {Av::guessAudioFileFormat(*trackPath)}; - if (guessedAudioFormat) - { - LOG(DEBUG) << "Set mime type to " << guessedAudioFormat->mimeType; - response.setMimeType(guessedAudioFormat->mimeType); - } - else - response.setMimeType("application/octet-stream"); + fileResourceHandler = Av::createRawResourceHandler(*trackPath); } else {