diff --git a/approot/admin-user.xml b/approot/admin-user.xml index 754f7ab5..e20f21a6 100644 --- a/approot/admin-user.xml +++ b/approot/admin-user.xml @@ -42,17 +42,17 @@
-
${} diff --git a/approot/messages.xml b/approot/messages.xml index e0a253c0..8527a8ea 100644 --- a/approot/messages.xml +++ b/approot/messages.xml @@ -45,7 +45,7 @@ Users -Audio bitrate limit +Audio transcode bitrate limit Demo account Demo account already exists! Demo password must be the login name! @@ -114,14 +114,15 @@ Account Audio -Bitrate Cannot save using a demo account! -Encoding -Auto -MP3 -Ogg/Opus -Ogg/Vorbis -WebM/Vorbis +Transcoding +Transcode bitrate +Transcode format +Enable transcoding +MP3 +Ogg/Opus +Ogg/Vorbis +WebM/Vorbis Settings New settings saved! diff --git a/approot/messages_fr.xml b/approot/messages_fr.xml index 0c5eddbc..3bf1c739 100644 --- a/approot/messages_fr.xml +++ b/approot/messages_fr.xml @@ -45,7 +45,7 @@ Utilisateurs -Limite du bitrate +Limite du bitrate pour le transcodage Compte de démonstration Le compte de démonstration existe déjà ! Le password doit être égal au login ! @@ -101,7 +101,7 @@ Effacer {1} pistes {1} pistes ajoutées -{1} pistes ajouées +{1} pistes ajoutées Liste de lecture Mode radio Repéter @@ -115,14 +115,15 @@ Compte Audio Auto -Bitrate Impossible de sauvegarder en utilisant un compte de démo ! -Encodage -Auto -MP3 -Ogg/Opus -Ogg/Vorbis -WebM/Vorbis +Transcodage +Bitrate du transcodage +Format du transcodage +Activer le transcodage +MP3 +Ogg/Opus +Ogg/Vorbis +WebM/Vorbis Paramètres Paramètres sauvegardés ! diff --git a/approot/settings.xml b/approot/settings.xml index 8770bd6e..ad628e9c 100644 --- a/approot/settings.xml +++ b/approot/settings.xml @@ -7,28 +7,39 @@ ${tr:Lms.Settings.audio}
-
+
+ +
+ ${transcoding-format} +
+
+ ${transcoding-format-info} +
+
+
+
- ${bitrate} + ${transcoding-bitrate} kbps
- ${bitrate-info} -
-
-
- -
- ${encoding} -
-
- ${encoding-info} + ${transcoding-bitrate-info}
diff --git a/src/api/subsonic/SubsonicResource.cpp b/src/api/subsonic/SubsonicResource.cpp index 71f70c53..ebf97b3b 100644 --- a/src/api/subsonic/SubsonicResource.cpp +++ b/src/api/subsonic/SubsonicResource.cpp @@ -1354,9 +1354,9 @@ createTranscoder(RequestContext& context) // "If set to zero, no limit is imposed" if (!maxBitRate || *maxBitRate == 0) - maxBitRate = user->getAudioBitrate() / 1000; + maxBitRate = user->getAudioTranscodeBitrate() / 1000; - *maxBitRate = clamp(*maxBitRate, std::size_t {48}, user->getMaxAudioBitrate() / 1000); + *maxBitRate = clamp(*maxBitRate, std::size_t {48}, user->getMaxAudioTranscodeBitrate() / 1000); auto track {Database::Track::getById(context.db.getSession(), id.value)}; if (!track) diff --git a/src/av/AvInfo.cpp b/src/av/AvInfo.cpp index 62745243..9569c447 100644 --- a/src/av/AvInfo.cpp +++ b/src/av/AvInfo.cpp @@ -24,6 +24,7 @@ #include #include "utils/Logger.hpp" +#include "utils/Utils.hpp" namespace Av { @@ -51,10 +52,8 @@ void AvInit() } - - MediaFile::MediaFile(const boost::filesystem::path& p) -: _p(p), _context(nullptr) +: _p {p} { int error = avformat_open_input(&_context, _p.string().c_str(), nullptr, nullptr); if (error < 0) @@ -77,6 +76,12 @@ MediaFile::~MediaFile() avformat_close_input(&_context); } +std::string +MediaFile::getFormatName() const +{ + return _context->iformat->name; +} + std::chrono::milliseconds MediaFile::getDuration() const { @@ -127,9 +132,9 @@ MediaFile::getStreamInfo() const { std::vector res; - for (std::size_t i = 0; i < _context->nb_streams; ++i) + for (std::size_t i {}; i < _context->nb_streams; ++i) { - AVStream* avstream = _context->streams[i]; + AVStream* avstream { _context->streams[i]}; // Skip attached pics if (avstream->disposition & AV_DISPOSITION_ATTACHED_PIC) @@ -233,4 +238,30 @@ MediaFile::getAttachedPictures(std::size_t nbMaxPictures) const return pictures; } +boost::optional guessMediaFileFormat(const boost::filesystem::path& file) +{ + AVOutputFormat* format {av_guess_format(NULL,file.string().c_str(),NULL)}; + if (!format || !format->name) + return {}; + + auto formats {splitString(format->name, ",")}; + if (formats.size() > 1) + LMS_LOG(AV, INFO) << "File '" << file.string() << "' reported several formats: '" << format->name << "'"; + + std::vector mimeTypes; + if (format->mime_type) + mimeTypes = splitString(format->mime_type, ","); + + if (mimeTypes.empty()) + LMS_LOG(AV, INFO) << "File '" << file.string() << "', no mime type found!"; + else if (mimeTypes.size() > 1) + LMS_LOG(AV, INFO) << "File '" << file.string() << "' reported several mime types: '" << format->mime_type << "'"; + + MediaFileFormat res; + res.format = formats.front(); + res.mimeType = mimeTypes.empty() ? "application/octet-stream" : mimeTypes.front(); + + return res; +} + } // namespace Av diff --git a/src/av/AvInfo.hpp b/src/av/AvInfo.hpp index acdfae43..11f6dfd4 100644 --- a/src/av/AvInfo.hpp +++ b/src/av/AvInfo.hpp @@ -69,9 +69,12 @@ class MediaFile MediaFile(const boost::filesystem::path& p); ~MediaFile(); - // non copyable MediaFile(const MediaFile&) = delete; MediaFile& operator=(const MediaFile&) = delete; + MediaFile(MediaFile&&) = delete; + MediaFile& operator=(MediaFile&&) = delete; + + std::string getFormatName() const; const boost::filesystem::path& getPath() const {return _p;}; @@ -84,11 +87,19 @@ class MediaFile std::vector getAttachedPictures(std::size_t nbMaxPictures) const; private: - MediaFile(); boost::filesystem::path _p; - AVFormatContext* _context; + AVFormatContext* _context {}; }; + +struct MediaFileFormat +{ + std::string mimeType; + std::string format; +}; + +boost::optional guessMediaFileFormat(const boost::filesystem::path& file); + } // namespace Av diff --git a/src/av/AvTranscoder.cpp b/src/av/AvTranscoder.cpp index 84ccae8e..1a37cb56 100644 --- a/src/av/AvTranscoder.cpp +++ b/src/av/AvTranscoder.cpp @@ -22,6 +22,7 @@ #include #include +#include "AvInfo.hpp" #include "utils/Path.hpp" #include "utils/Logger.hpp" @@ -99,10 +100,6 @@ Transcoder::start() args.push_back("-i"); args.push_back(_filePath.string()); - // Output bitrates - args.push_back("-b:a"); - args.push_back(std::to_string(_parameters.bitrate)); - // Stream mapping, if set if (_parameters.stream) { @@ -121,37 +118,63 @@ Transcoder::start() args.push_back("-vn"); // Codecs and formats - switch( _parameters.encoding) + if (_parameters.encoding) { - case Encoding::MP3: - args.push_back("-f"); - args.push_back("mp3"); - break; + // Output bitrates + args.push_back("-b:a"); + args.push_back(std::to_string(_parameters.bitrate)); - case Encoding::OGG_OPUS: - args.push_back("-acodec"); - args.push_back("libopus"); - args.push_back("-f"); - args.push_back("ogg"); - break; + switch (*_parameters.encoding) + { + case Encoding::MP3: + args.push_back("-f"); + args.push_back("mp3"); + break; - case Encoding::OGG_VORBIS: - args.push_back("-acodec"); - args.push_back("libvorbis"); - args.push_back("-f"); - args.push_back("ogg"); - break; + case Encoding::OGG_OPUS: + args.push_back("-acodec"); + args.push_back("libopus"); + args.push_back("-f"); + args.push_back("ogg"); + break; - case Encoding::WEBM_VORBIS: - args.push_back("-acodec"); - args.push_back("libvorbis"); - args.push_back("-f"); - args.push_back("webm"); - break; + case Encoding::OGG_VORBIS: + args.push_back("-acodec"); + args.push_back("libvorbis"); + args.push_back("-f"); + args.push_back("ogg"); + break; + + case Encoding::WEBM_VORBIS: + args.push_back("-acodec"); + args.push_back("libvorbis"); + args.push_back("-f"); + args.push_back("webm"); + break; - default: + 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() << "'"; return false; + } + + args.push_back("-acodec"); + args.push_back("copy"); + args.push_back("-f"); + args.push_back(mediaFileFormat->format); + + _outputMimeType = mediaFileFormat->mimeType; } args.push_back("pipe:1"); @@ -204,14 +227,10 @@ Transcoder::process(std::vector& output, std::size_t maxSize) output.resize(maxSize); - LMS_LOG_TRANSCODE(DEBUG) << "Reading up to " << output.size() << " bytes"; - //Read on the output stream _child->out().read(reinterpret_cast(&output[0]), maxSize); output.resize(_child->out().gcount()); - LMS_LOG_TRANSCODE(DEBUG) << "Read " << output.size() << " bytes"; - if (_child->out().fail()) { LMS_LOG_TRANSCODE(DEBUG) << "Stdout FAILED"; diff --git a/src/av/AvTranscoder.hpp b/src/av/AvTranscoder.hpp index b089183f..b8e0fcd1 100644 --- a/src/av/AvTranscoder.hpp +++ b/src/av/AvTranscoder.hpp @@ -19,15 +19,13 @@ #pragma once -#include -#include +#include #include #include #include -#include "AvInfo.hpp" #include "AvTypes.hpp" namespace Av { @@ -36,11 +34,11 @@ namespace Av { struct TranscodeParameters { - Encoding encoding {Encoding::MP3}; - std::size_t bitrate {128000}; - boost::optional stream; // Id of the stream to be transcoded (auto detect by default) - boost::optional offset; - bool stripMetadata {true}; + boost::optional encoding; // If not set, no transcoding is performed + std::size_t bitrate {128000}; + boost::optional stream; // Id of the stream to be transcoded (auto detect by default) + boost::optional offset {}; + bool stripMetadata {true}; }; class Transcoder @@ -56,11 +54,13 @@ class Transcoder Transcoder& operator=(const Transcoder&) = delete; bool start(); + const std::string& getOutputMimeType() const { return _outputMimeType; } void process(std::vector& output, std::size_t maxSize); bool isComplete(void) const { return _isComplete; } const TranscodeParameters& getParameters() const { return _parameters; } + private: Transcoder(); @@ -72,6 +72,7 @@ class Transcoder bool _isComplete = false; std::size_t _total = 0; std::size_t _id; + std::string _outputMimeType; }; } // namespace Av diff --git a/src/av/AvTypes.cpp b/src/av/AvTypes.cpp index d6d2499b..1384db80 100644 --- a/src/av/AvTypes.cpp +++ b/src/av/AvTypes.cpp @@ -40,5 +40,10 @@ std::string encodingToMimetype(Encoding encoding) return it->second; } +Encoding guessEncoding(const boost::filesystem::path& file) +{ + +} + } diff --git a/src/av/AvTypes.hpp b/src/av/AvTypes.hpp index 46f3670a..a04185c7 100644 --- a/src/av/AvTypes.hpp +++ b/src/av/AvTypes.hpp @@ -20,6 +20,7 @@ #pragma once #include +#include #include "utils/Exception.hpp" @@ -34,10 +35,10 @@ class AvException : public LmsException enum class Encoding { // Values are important and must not be changed - MP3 = 0, - OGG_OPUS = 1, - OGG_VORBIS = 2, - WEBM_VORBIS = 3, + MP3 = 0, + OGG_OPUS = 1, + OGG_VORBIS = 2, + WEBM_VORBIS = 3, }; std::string encodingToMimetype(Encoding encoding); diff --git a/src/database/DatabaseHandler.cpp b/src/database/DatabaseHandler.cpp index a2302554..ec6b828b 100644 --- a/src/database/DatabaseHandler.cpp +++ b/src/database/DatabaseHandler.cpp @@ -46,7 +46,7 @@ namespace Database { -#define LMS_DATABASE_VERSION 1 +#define LMS_DATABASE_VERSION 2 namespace { Wt::Auth::AuthService authService; diff --git a/src/database/Track.cpp b/src/database/Track.cpp index 4e1102b3..608f70d2 100644 --- a/src/database/Track.cpp +++ b/src/database/Track.cpp @@ -211,9 +211,9 @@ Track::getByFilter(Wt::Dbo::Session& session, .limit(size ? static_cast(*size) + 1 : -1) .offset(offset ? static_cast(*offset) : -1); - auto res = std::vector(collection.begin(), collection.end()); + std::vector res(collection.begin(), collection.end()); - if (size && res.size() == static_cast(*size) + 1) + if (size && (res.size() == static_cast(*size) + 1)) { moreResults = true; res.pop_back(); diff --git a/src/database/User.cpp b/src/database/User.cpp index 0f0e6b16..cf31677b 100644 --- a/src/database/User.cpp +++ b/src/database/User.cpp @@ -23,9 +23,8 @@ namespace Database { -// must be ordered -const std::vector -User::audioBitrates = +const std::set +User::audioTranscodeAllowedBitrates = { 64000, 96000, @@ -35,7 +34,7 @@ User::audioBitrates = }; User::User() -: _maxAudioBitrate(audioBitrates.back()) +: _maxAudioTranscodeBitrate{static_cast(*audioTranscodeAllowedBitrates.rbegin())} { } @@ -67,29 +66,29 @@ User::getById(Wt::Dbo::Session& session, IdType id) } void -User::setAudioBitrate(std::size_t bitrate) +User::setAudioTranscodeBitrate(Bitrate bitrate) { - _audioBitrate = std::min(bitrate, static_cast(_maxAudioBitrate)); + _audioTranscodeBitrate = std::min(bitrate, static_cast(_maxAudioTranscodeBitrate)); } void -User::setMaxAudioBitrate(std::size_t bitrate) +User::setMaxAudioTranscodeBitrate(Bitrate bitrate) { - _maxAudioBitrate = std::min(bitrate, audioBitrates.back()); - if (_audioBitrate > _maxAudioBitrate) - _audioBitrate = _maxAudioBitrate; + _maxAudioTranscodeBitrate = std::min(bitrate, *audioTranscodeAllowedBitrates.rbegin()); + if (_audioTranscodeBitrate > _maxAudioTranscodeBitrate) + _audioTranscodeBitrate = _maxAudioTranscodeBitrate; +} + +Bitrate +User::getAudioTranscodeBitrate(void) const +{ + return _audioTranscodeBitrate; } std::size_t -User::getAudioBitrate(void) const +User::getMaxAudioTranscodeBitrate(void) const { - return _audioBitrate; -} - -std::size_t -User::getMaxAudioBitrate(void) const -{ - return _maxAudioBitrate; + return _maxAudioTranscodeBitrate; } Wt::Dbo::ptr diff --git a/src/database/User.hpp b/src/database/User.hpp index 255ec82c..9a990615 100644 --- a/src/database/User.hpp +++ b/src/database/User.hpp @@ -34,15 +34,17 @@ using AuthInfo = Wt::Auth::Dbo::AuthInfo; class TrackList; // User selectable audio formats -enum class AudioEncoding +// Do not change values +enum class AudioFormat { - AUTO = 0, MP3 = 1, OGG_OPUS = 2, OGG_VORBIS = 3, WEBM_VORBIS = 4, }; +using Bitrate = std::size_t; + class User : public Wt::Dbo::Dbo { public: @@ -59,7 +61,7 @@ class User : public Wt::Dbo::Dbo }; // list of audio parameters - static const std::vector audioBitrates; + static const std::set audioTranscodeAllowedBitrates; User(); @@ -72,23 +74,25 @@ class User : public Wt::Dbo::Dbo static pointer getDemo(Wt::Dbo::Session& session); // write - void setType(Type type) { _type = type; } - void setAudioBitrate(std::size_t bitrate); - void setAudioEncoding(AudioEncoding encoding) { _audioEncoding = encoding; } - void setMaxAudioBitrate(std::size_t bitrate); - void setCurPlayingTrackPos(std::size_t pos) { _curPlayingTrackPos = pos; } - void setRadio(bool val) { _radio = val; } - void setRepeatAll(bool val) { _repeatAll = val; } + void setType(Type type) { _type = type; } + void setAudioTranscodeEnable(bool value) { _audioTranscodeEnable = value; } + void setAudioTranscodeFormat(AudioFormat format) { _audioTranscodeFormat = format; } + void setAudioTranscodeBitrate(Bitrate bitrate); + void setMaxAudioTranscodeBitrate(Bitrate bitrate); + void setCurPlayingTrackPos(std::size_t pos) { _curPlayingTrackPos = pos; } + void setRadio(bool val) { _radio = val; } + void setRepeatAll(bool val) { _repeatAll = val; } // read - bool isAdmin() const { return _type == Type::ADMIN; } - bool isDemo() const { return _type == Type::DEMO; } - std::size_t getAudioBitrate() const; - AudioEncoding getAudioEncoding() const { return _audioEncoding; } - std::size_t getMaxAudioBitrate() const; - std::size_t getCurPlayingTrackPos() const { return _curPlayingTrackPos; } - bool isRepeatAllSet() const { return _repeatAll; } - bool isRadioSet() const { return _radio; } + bool isAdmin() const { return _type == Type::ADMIN; } + bool isDemo() const { return _type == Type::DEMO; } + bool getAudioTranscodeEnable() const { return _audioTranscodeEnable; } + Bitrate getAudioTranscodeBitrate() const; + AudioFormat getAudioTranscodeFormat() const { return _audioTranscodeFormat; } + Bitrate getMaxAudioTranscodeBitrate() const; + std::size_t getCurPlayingTrackPos() const { return _curPlayingTrackPos; } + bool isRepeatAllSet() const { return _repeatAll; } + bool isRadioSet() const { return _radio; } Wt::Dbo::ptr getQueuedTrackList() const; Wt::Dbo::ptr getPlayedTrackList() const; @@ -96,10 +100,11 @@ class User : public Wt::Dbo::Dbo template void persist(Action& a) { - Wt::Dbo::field(a, _maxAudioBitrate, "max_audio_bitrate"); Wt::Dbo::field(a, _type, "type"); - Wt::Dbo::field(a, _audioBitrate, "audio_bitrate"); - Wt::Dbo::field(a, _audioEncoding, "audio_encoding"); + Wt::Dbo::field(a, _maxAudioTranscodeBitrate, "max_audio_bitrate"); + Wt::Dbo::field(a, _audioTranscodeEnable, "audio_transcode_enable"); + Wt::Dbo::field(a, _audioTranscodeBitrate, "audio_transcode_bitrate"); + Wt::Dbo::field(a, _audioTranscodeFormat, "audio_transcode_format"); // User's dynamic data Wt::Dbo::field(a, _curPlayingTrackPos, "cur_playing_track_pos"); Wt::Dbo::field(a, _repeatAll, "repeat_all"); @@ -109,20 +114,23 @@ class User : public Wt::Dbo::Dbo private: - static const std::size_t defaultAudioBitrate = 128000; + static const bool defaultAudioTranscodeEnable {true}; + static const AudioFormat defaultAudioTranscodeFormat {AudioFormat::OGG_OPUS}; + static const Bitrate defaultAudioTranscodeBitrate {128000}; // Admin defined settings - int _maxAudioBitrate; - Type _type = Type::REGULAR; + int _maxAudioTranscodeBitrate; + Type _type {Type::REGULAR}; // User defined settings - int _audioBitrate = defaultAudioBitrate; - AudioEncoding _audioEncoding = AudioEncoding::AUTO; + bool _audioTranscodeEnable {defaultAudioTranscodeEnable}; + AudioFormat _audioTranscodeFormat {defaultAudioTranscodeFormat}; + int _audioTranscodeBitrate {defaultAudioTranscodeBitrate}; // User's dynamic data - int _curPlayingTrackPos = 0; // Current track position in queue - bool _repeatAll = false; - bool _radio = false; + int _curPlayingTrackPos {}; // Current track position in queue + bool _repeatAll {}; + bool _radio {}; Wt::Dbo::collection< Wt::Dbo::ptr > _tracklists; diff --git a/src/main/main.cpp b/src/main/main.cpp index 89fb3be6..01456fcd 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -63,7 +63,7 @@ std::vector generateWtConfig(std::string execPath) args.push_back("--http-address=" + Config::instance().getString("listen-addr", "0.0.0.0")); } - args.push_back("--gdb"); + // args.push_back("--gdb"); // Generate the wt_config.xml file boost::property_tree::ptree pt; diff --git a/src/ui/SettingsView.cpp b/src/ui/SettingsView.cpp index ba738900..a23dc24b 100644 --- a/src/ui/SettingsView.cpp +++ b/src/ui/SettingsView.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -40,8 +41,9 @@ class SettingsModel : public Wt::WFormModel { public: // Associate each field with a unique string literal. - static const Field BitrateField; - static const Field EncodingField; + static const Field TranscodeEnableField; + static const Field TranscodeFormatField; + static const Field TranscodeBitrateField; static const Field PasswordField; static const Field PasswordConfirmField; @@ -50,49 +52,57 @@ class SettingsModel : public Wt::WFormModel { initializeModels(); - addField(BitrateField); - addField(EncodingField); + addField(TranscodeEnableField); + addField(TranscodeBitrateField); + addField(TranscodeFormatField); addField(PasswordField); addField(PasswordConfirmField); - setValidator(BitrateField, createMandatoryValidator()); - setValidator(EncodingField, createMandatoryValidator()); + setValidator(TranscodeBitrateField, createMandatoryValidator()); + setValidator(TranscodeFormatField, createMandatoryValidator()); loadData(); } - std::shared_ptr bitrateModel() { return _bitrateModel; } - std::shared_ptr encodingModel() { return _encodingModel; } + std::shared_ptr transcodeBitrateModel() { return _transcodeBitrateModel; } + std::shared_ptr transcodeFormatModel() { return _transcodeFormatModel; } void loadData() { - Wt::Dbo::Transaction transaction(LmsApp->getDboSession()); + Wt::Dbo::Transaction transaction {LmsApp->getDboSession()}; - auto bitrate = getBitrateRow(LmsApp->getUser()->getAudioBitrate()); - if (bitrate) - setValue(BitrateField, bitrateString(*bitrate)); + setValue(TranscodeEnableField, LmsApp->getUser()->getAudioTranscodeEnable()); + if (!LmsApp->getUser()->getAudioTranscodeEnable()) + { + setReadOnly(TranscodeFormatField, true); + setReadOnly(TranscodeBitrateField, true); + } - auto encodingRow = getEncodingRow(LmsApp->getUser()->getAudioEncoding()); - if (encodingRow) - setValue(EncodingField, encodingString(*encodingRow)); + auto transcodeBitrateRow {getTranscodeBitrateRow(LmsApp->getUser()->getAudioTranscodeBitrate())}; + if (transcodeBitrateRow) + setValue(TranscodeBitrateField, transcodeBitrateString(*transcodeBitrateRow)); + auto transcodeFormatRow {getTranscodeFormatRow(LmsApp->getUser()->getAudioTranscodeFormat())}; + if (transcodeFormatRow) + setValue(TranscodeFormatField, transcodeFormatString(*transcodeFormatRow)); } void saveData() { - Wt::Dbo::Transaction transaction(LmsApp->getDboSession()); + Wt::Dbo::Transaction transaction {LmsApp->getDboSession()}; - auto bitrateRow = getBitrateRow(Wt::asString(value(BitrateField))); - assert(bitrateRow); - LmsApp->getUser().modify()->setAudioBitrate(bitrate(*bitrateRow)); + LmsApp->getUser().modify()->setAudioTranscodeEnable(Wt::asNumber(value(TranscodeEnableField))); - auto encodingRow = getEncodingRow(Wt::asString(value(EncodingField))); - LmsApp->getUser().modify()->setAudioEncoding(encoding(*encodingRow)); + auto transcodeBitrateRow {getTranscodeBitrateRow(Wt::asString(value(TranscodeBitrateField)))}; + if (transcodeBitrateRow) + LmsApp->getUser().modify()->setAudioTranscodeBitrate(transcodeBitrate(*transcodeBitrateRow)); + + auto transcodeFormatRow {getTranscodeFormatRow(Wt::asString(value(TranscodeFormatField)))}; + if (transcodeFormatRow) + LmsApp->getUser().modify()->setAudioTranscodeFormat(transcodeFormat(*transcodeFormatRow)); if (!valueText(PasswordField).empty()) - { Database::Handler::getPasswordService().updatePassword(LmsApp->getAuthUser(), valueText(PasswordField)); - } } bool validateField(Field field) @@ -130,72 +140,72 @@ class SettingsModel : public Wt::WFormModel return (validation(field).state() == Wt::ValidationState::Valid); } - boost::optional getBitrateRow(Wt::WString value) + boost::optional getTranscodeBitrateRow(Wt::WString value) { - for (int i = 0; i < _bitrateModel->rowCount(); ++i) + for (int i = 0; i < _transcodeBitrateModel->rowCount(); ++i) { - if (bitrateString(i) == value) + if (transcodeBitrateString(i) == value) return i; } return boost::none; } - boost::optional getBitrateRow(std::size_t value) + boost::optional getTranscodeBitrateRow(std::size_t value) { - for (int i = 0; i < _bitrateModel->rowCount(); ++i) + for (int i = 0; i < _transcodeBitrateModel->rowCount(); ++i) { - if (bitrate(i) == value) + if (transcodeBitrate(i) == value) return i; } return boost::none; } - std::size_t bitrate(int row) + std::size_t transcodeBitrate(int row) { return Wt::cpp17::any_cast - (_bitrateModel->data(_bitrateModel->index(row, 0), Wt::ItemDataRole::User)); + (_transcodeBitrateModel->data(_transcodeBitrateModel->index(row, 0), Wt::ItemDataRole::User)); } - Wt::WString bitrateString(int row) + Wt::WString transcodeBitrateString(int row) { return Wt::cpp17::any_cast - (_bitrateModel->data(_bitrateModel->index(row, 0), Wt::ItemDataRole::Display)); + (_transcodeBitrateModel->data(_transcodeBitrateModel->index(row, 0), Wt::ItemDataRole::Display)); } - boost::optional getEncodingRow(Wt::WString value) + boost::optional getTranscodeFormatRow(Wt::WString value) { - for (int i = 0; i < _encodingModel->rowCount(); ++i) + for (int i = 0; i < _transcodeFormatModel->rowCount(); ++i) { - if (encodingString(i) == value) + if (transcodeFormatString(i) == value) return i; } return boost::none; } - boost::optional getEncodingRow(Database::AudioEncoding enc) + boost::optional getTranscodeFormatRow(Database::AudioFormat format) { - for (int i = 0; i < _encodingModel->rowCount(); ++i) + for (int i = 0; i < _transcodeFormatModel->rowCount(); ++i) { - if (encoding(i) == enc) + if (transcodeFormat(i) == format) return i; } return boost::none; } - Database::AudioEncoding encoding(int row) + Database::AudioFormat transcodeFormat(int row) { - return Wt::cpp17::any_cast - (_encodingModel->data(_encodingModel->index(row, 0), Wt::ItemDataRole::User)); + return Wt::cpp17::any_cast + (_transcodeFormatModel->data(_transcodeFormatModel->index(row, 0), Wt::ItemDataRole::User)); } - Wt::WString encodingString(int row) + Wt::WString transcodeFormatString(int row) { return Wt::cpp17::any_cast - (_encodingModel->data(_encodingModel->index(row, 0), Wt::ItemDataRole::Display)); + (_transcodeFormatModel->data(_transcodeFormatModel->index(row, 0), Wt::ItemDataRole::Display)); } @@ -203,52 +213,50 @@ class SettingsModel : public Wt::WFormModel void initializeModels() { - _bitrateModel = std::make_shared(); + _transcodeBitrateModel = std::make_shared(); - std::size_t maxAudioBitrate; + Database::Bitrate maxAudioBitrate; { - Wt::Dbo::Transaction transaction(LmsApp->getDboSession()); - maxAudioBitrate = LmsApp->getUser()->getMaxAudioBitrate(); + Wt::Dbo::Transaction transaction {LmsApp->getDboSession()}; + maxAudioBitrate = LmsApp->getUser()->getMaxAudioTranscodeBitrate(); } std::size_t id = 0; - for (std::size_t bitrate : Database::User::audioBitrates) + for (Database::Bitrate bitrate : Database::User::audioTranscodeAllowedBitrates) { if (bitrate > maxAudioBitrate) break; - _bitrateModel->addString( Wt::WString::fromUTF8(std::to_string(bitrate / 1000)) ); - _bitrateModel->setData( id, 0, bitrate, Wt::ItemDataRole::User); + _transcodeBitrateModel->addString( Wt::WString::fromUTF8(std::to_string(bitrate / 1000)) ); + _transcodeBitrateModel->setData( id, 0, bitrate, Wt::ItemDataRole::User); id++; } - _encodingModel = std::make_shared(); + _transcodeFormatModel = std::make_shared(); - _encodingModel->addString(Wt::WString::tr("Lms.Settings.encoding.auto")); - _encodingModel->setData(0, 0, Database::AudioEncoding::AUTO, Wt::ItemDataRole::User); + _transcodeFormatModel->addString(Wt::WString::tr("Lms.Settings.transcoding.mp3")); + _transcodeFormatModel->setData(0, 0, Database::AudioFormat::MP3, Wt::ItemDataRole::User); - _encodingModel->addString(Wt::WString::tr("Lms.Settings.encoding.mp3")); - _encodingModel->setData(1, 0, Database::AudioEncoding::MP3, Wt::ItemDataRole::User); + _transcodeFormatModel->addString(Wt::WString::tr("Lms.Settings.transcoding.ogg_opus")); + _transcodeFormatModel->setData(1, 0, Database::AudioFormat::OGG_OPUS, Wt::ItemDataRole::User); - _encodingModel->addString(Wt::WString::tr("Lms.Settings.encoding.ogg_opus")); - _encodingModel->setData(2, 0, Database::AudioEncoding::OGG_OPUS, Wt::ItemDataRole::User); + _transcodeFormatModel->addString(Wt::WString::tr("Lms.Settings.transcoding.ogg_vorbis")); + _transcodeFormatModel->setData(2, 0, Database::AudioFormat::OGG_VORBIS, Wt::ItemDataRole::User); - _encodingModel->addString(Wt::WString::tr("Lms.Settings.encoding.ogg_vorbis")); - _encodingModel->setData(3, 0, Database::AudioEncoding::OGG_VORBIS, Wt::ItemDataRole::User); - - _encodingModel->addString(Wt::WString::tr("Lms.Settings.encoding.webm_vorbis")); - _encodingModel->setData(4, 0, Database::AudioEncoding::WEBM_VORBIS, Wt::ItemDataRole::User); + _transcodeFormatModel->addString(Wt::WString::tr("Lms.Settings.transcoding.webm_vorbis")); + _transcodeFormatModel->setData(3, 0, Database::AudioFormat::WEBM_VORBIS, Wt::ItemDataRole::User); } - std::shared_ptr _bitrateModel; - std::shared_ptr _encodingModel; + std::shared_ptr _transcodeBitrateModel; + std::shared_ptr _transcodeFormatModel; }; -const Wt::WFormModel::Field SettingsModel::BitrateField = "bitrate"; -const Wt::WFormModel::Field SettingsModel::EncodingField = "encoding"; -const Wt::WFormModel::Field SettingsModel::PasswordField = "password"; -const Wt::WFormModel::Field SettingsModel::PasswordConfirmField = "password-confirm"; +const Wt::WFormModel::Field SettingsModel::TranscodeEnableField = "transcoding-enable"; +const Wt::WFormModel::Field SettingsModel::TranscodeBitrateField = "transcoding-bitrate"; +const Wt::WFormModel::Field SettingsModel::TranscodeFormatField = "transcoding-format"; +const Wt::WFormModel::Field SettingsModel::PasswordField = "password"; +const Wt::WFormModel::Field SettingsModel::PasswordConfirmField = "password-confirm"; SettingsView::SettingsView() { @@ -268,39 +276,53 @@ SettingsView::refreshView() clear(); - auto t = addNew(Wt::WString::tr("Lms.Settings.template")); + auto t {addNew(Wt::WString::tr("Lms.Settings.template"))}; - auto model = std::make_shared(); + auto model {std::make_shared()}; // Password - auto password = std::make_unique(); + auto password {std::make_unique()}; password->setEchoMode(Wt::EchoMode::Password); t->setFormWidget(SettingsModel::PasswordField, std::move(password)); // Password confirm - auto passwordConfirm = std::make_unique(); + auto passwordConfirm {std::make_unique()}; passwordConfirm->setEchoMode(Wt::EchoMode::Password); t->setFormWidget(SettingsModel::PasswordConfirmField, std::move(passwordConfirm)); - // Bitrate - auto bitrate = std::make_unique(); - bitrate->setModel(model->bitrateModel()); - t->setFormWidget(SettingsModel::BitrateField, std::move(bitrate)); + // Transcoding + auto transcode {std::make_unique()}; + auto* transcodeRaw {transcode.get()}; + t->setFormWidget(SettingsModel::TranscodeEnableField, std::move(transcode)); - // Encoding - auto encoding = std::make_unique(); - encoding->setModel(model->encodingModel()); - t->setFormWidget(SettingsModel::EncodingField, std::move(encoding)); + // Format + auto transcodeFormat {std::make_unique()}; + transcodeFormat->setModel(model->transcodeFormatModel()); + t->setFormWidget(SettingsModel::TranscodeFormatField, std::move(transcodeFormat)); + + // Bitrate + auto transcodeBitrate {std::make_unique()}; + transcodeBitrate->setModel(model->transcodeBitrateModel()); + t->setFormWidget(SettingsModel::TranscodeBitrateField, std::move(transcodeBitrate)); + + transcodeRaw->changed().connect([=]() + { + bool enable {transcodeRaw->checkState() == Wt::CheckState::Checked}; + model->setReadOnly(SettingsModel::TranscodeFormatField, !enable); + model->setReadOnly(SettingsModel::TranscodeBitrateField, !enable); + t->updateModel(model.get()); + t->updateView(model.get()); + }); // Buttons - Wt::WPushButton *saveBtn = t->bindWidget("apply-btn", std::make_unique(Wt::WString::tr("Lms.apply"))); - Wt::WPushButton *discardBtn = t->bindWidget("discard-btn", std::make_unique(Wt::WString::tr("Lms.discard"))); + Wt::WPushButton *saveBtn {t->bindWidget("apply-btn", std::make_unique(Wt::WString::tr("Lms.apply")))}; + Wt::WPushButton *discardBtn {t->bindWidget("discard-btn", std::make_unique(Wt::WString::tr("Lms.discard")))}; saveBtn->clicked().connect(std::bind([=] () { { - Wt::Dbo::Transaction transaction(LmsApp->getDboSession()); + Wt::Dbo::Transaction transaction {LmsApp->getDboSession()}; if (LmsApp->getUser()->isDemo()) { diff --git a/src/ui/admin/UserView.cpp b/src/ui/admin/UserView.cpp index de075452..fc73d1af 100644 --- a/src/ui/admin/UserView.cpp +++ b/src/ui/admin/UserView.cpp @@ -45,7 +45,7 @@ class UserModel : public Wt::WFormModel public: static const Field LoginField; static const Field PasswordField; - static const Field BitrateLimitField; + static const Field AudioTranscodeBitrateLimitField; static const Field DemoField; UserModel(boost::optional userId) @@ -59,7 +59,7 @@ class UserModel : public Wt::WFormModel } addField(PasswordField); - addField(BitrateLimitField); + addField(AudioTranscodeBitrateLimitField); addField(DemoField); if (!_userId) @@ -76,7 +76,7 @@ class UserModel : public Wt::WFormModel if (!_userId) return; - Wt::Dbo::Transaction transaction(LmsApp->getDboSession()); + Wt::Dbo::Transaction transaction {LmsApp->getDboSession()}; auto authUser = LmsApp->getDb().getUserDatabase().findWithId( std::to_string(*_userId) ); auto user = LmsApp->getDb().getUser(authUser); @@ -84,14 +84,14 @@ class UserModel : public Wt::WFormModel if (user == LmsApp->getUser()) throw LmsException("Cannot edit ourselves"); - auto bitrate = getBitrateLimitRow(user->getMaxAudioBitrate()); + auto bitrate {getTranscodeBitrateLimitRow(user->getMaxAudioTranscodeBitrate())}; if (bitrate) - setValue(BitrateLimitField, bitrateLimitString(*bitrate)); + setValue(AudioTranscodeBitrateLimitField, transcodeBitrateLimitString(*bitrate)); } void saveData() { - Wt::Dbo::Transaction transaction(LmsApp->getDboSession()); + Wt::Dbo::Transaction transaction {LmsApp->getDboSession()}; if (_userId) { @@ -103,9 +103,9 @@ class UserModel : public Wt::WFormModel if (!valueText(PasswordField).empty()) Database::Handler::getPasswordService().updatePassword(authUser, valueText(PasswordField)); - auto bitrateLimitRow = getBitrateLimitRow(Wt::asString(value(BitrateLimitField))); - user.modify()->setMaxAudioBitrate(bitrateLimit(*bitrateLimitRow)); - LMS_LOG(UI, DEBUG) << "Max audio bitrate set to " << bitrateLimit(*bitrateLimitRow); + auto transcodeBitrateLimitRow = getTranscodeBitrateLimitRow(Wt::asString(value(AudioTranscodeBitrateLimitField))); + user.modify()->setMaxAudioTranscodeBitrate(transcodeBitrateLimit(*transcodeBitrateLimitRow)); + LMS_LOG(UI, DEBUG) << "Max audio bitrate set to " << transcodeBitrateLimit(*transcodeBitrateLimitRow); } else { @@ -117,8 +117,8 @@ class UserModel : public Wt::WFormModel authUser.setIdentity(Wt::Auth::Identity::LoginName, valueText(LoginField)); Database::Handler::getPasswordService().updatePassword(authUser, valueText(PasswordField)); - auto bitrateLimitRow = getBitrateLimitRow(Wt::asString(value(BitrateLimitField))); - user.modify()->setMaxAudioBitrate(bitrateLimit(*bitrateLimitRow)); + auto transcodeBitrateLimitRow = getTranscodeBitrateLimitRow(Wt::asString(value(AudioTranscodeBitrateLimitField))); + user.modify()->setMaxAudioTranscodeBitrate(transcodeBitrateLimit(*transcodeBitrateLimitRow)); if (Wt::asNumber(value(DemoField))) user.modify()->setType(Database::User::Type::DEMO); @@ -168,7 +168,7 @@ class UserModel : public Wt::WFormModel } else if (field == DemoField) { - Wt::Dbo::Transaction transaction(LmsApp->getDboSession()); + Wt::Dbo::Transaction transaction {LmsApp->getDboSession()}; if (Wt::asNumber(value(DemoField)) && Database::User::getDemo(LmsApp->getDboSession())) error = Wt::WString::tr("Lms.Admin.User.demo-account-already-exists"); @@ -182,35 +182,35 @@ class UserModel : public Wt::WFormModel return false; } - boost::optional getBitrateLimitRow(Wt::WString value) + boost::optional getTranscodeBitrateLimitRow(Wt::WString value) { for (int i = 0; i < _bitrateModel->rowCount(); ++i) { - if (bitrateLimitString(i) == value) + if (transcodeBitrateLimitString(i) == value) return i; } return boost::none; } - boost::optional getBitrateLimitRow(std::size_t value) + boost::optional getTranscodeBitrateLimitRow(std::size_t value) { for (int i = 0; i < _bitrateModel->rowCount(); ++i) { - if (bitrateLimit(i) == value) + if (transcodeBitrateLimit(i) == value) return i; } return boost::none; } - std::size_t bitrateLimit(int row) + std::size_t transcodeBitrateLimit(int row) { return Wt::cpp17::any_cast (_bitrateModel->data(_bitrateModel->index(row, 0), Wt::ItemDataRole::User)); } - Wt::WString bitrateLimitString(int row) + Wt::WString transcodeBitrateLimitString(int row) { return Wt::cpp17::any_cast (_bitrateModel->data(_bitrateModel->index(row, 0), Wt::ItemDataRole::Display)); @@ -225,7 +225,7 @@ class UserModel : public Wt::WFormModel _bitrateModel = std::make_shared(); std::size_t id = 0; - for (auto bitrate : Database::User::audioBitrates) + for (auto bitrate : Database::User::audioTranscodeAllowedBitrates) { _bitrateModel->addString( Wt::WString::fromUTF8(std::to_string(bitrate / 1000)) ); _bitrateModel->setData( id++, 0, bitrate, Wt::ItemDataRole::User); @@ -240,7 +240,7 @@ class UserModel : public Wt::WFormModel const Wt::WFormModel::Field UserModel::LoginField = "login"; const Wt::WFormModel::Field UserModel::PasswordField = "password"; -const Wt::WFormModel::Field UserModel::BitrateLimitField = "audio-bitrate-limit"; +const Wt::WFormModel::Field UserModel::AudioTranscodeBitrateLimitField = "audio-transcode-bitrate-limit"; const Wt::WFormModel::Field UserModel::DemoField = "demo"; UserView::UserView() @@ -293,10 +293,10 @@ UserView::refreshView() passwordEdit->setEchoMode(Wt::EchoMode::Password); t->setFormWidget(UserModel::PasswordField, std::move(passwordEdit)); - // Bitrate + // Transcode bitrate limit auto bitrate = std::make_unique(); bitrate->setModel(model->bitrateModel()); - t->setFormWidget(UserModel::BitrateLimitField, std::move(bitrate)); + t->setFormWidget(UserModel::AudioTranscodeBitrateLimitField, std::move(bitrate)); // Demo account t->setFormWidget(UserModel::DemoField, std::make_unique()); diff --git a/src/ui/explore/TracksView.cpp b/src/ui/explore/TracksView.cpp index 79b0a41b..30347c0d 100644 --- a/src/ui/explore/TracksView.cpp +++ b/src/ui/explore/TracksView.cpp @@ -77,12 +77,12 @@ _filters(filters) } std::vector -Tracks::getTracks(int offset, int size, bool& moreResults) +Tracks::getTracks(boost::optional offset, boost::optional size, bool& moreResults) { - auto searchKeywords = splitString(_search->text().toUTF8(), " "); - auto clusterIds = _filters->getClusterIds(); + auto searchKeywords {splitString(_search->text().toUTF8(), " ")}; + auto clusterIds {_filters->getClusterIds()}; - Wt::Dbo::Transaction transaction(LmsApp->getDboSession()); + Wt::Dbo::Transaction transaction {LmsApp->getDboSession()}; return Track::getByFilter(LmsApp->getDboSession(), clusterIds, searchKeywords, offset, size, moreResults); } @@ -91,7 +91,7 @@ std::vector Tracks::getTracks() { bool moreResults; - return getTracks(-1, -1, moreResults); + return getTracks({}, {}, moreResults); } void @@ -104,15 +104,15 @@ Tracks::refresh() void Tracks::addSome() { - Wt::Dbo::Transaction transaction(LmsApp->getDboSession()); + Wt::Dbo::Transaction transaction {LmsApp->getDboSession()}; bool moreResults; - auto tracks = getTracks(_tracksContainer->count(), 20, moreResults); + auto tracks {getTracks(_tracksContainer->count(), 20, moreResults)}; for (auto track : tracks) { - auto trackId = track.id(); - Wt::WTemplate* entry = _tracksContainer->addNew(Wt::WString::tr("Lms.Explore.Tracks.template.entry")); + auto trackId {track.id()}; + Wt::WTemplate* entry {_tracksContainer->addNew(Wt::WString::tr("Lms.Explore.Tracks.template.entry"))}; entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::TextFormat::Plain); diff --git a/src/ui/explore/TracksView.hpp b/src/ui/explore/TracksView.hpp index d8bc9a3d..c8dd0988 100644 --- a/src/ui/explore/TracksView.hpp +++ b/src/ui/explore/TracksView.hpp @@ -45,7 +45,7 @@ class Tracks : public Wt::WTemplate void refresh(); void addSome(); - std::vector getTracks(int offset, int size, bool& moreResults); + std::vector getTracks(boost::optional offset, boost::optional size, bool& moreResults); std::vector getTracks(); Wt::WContainerWidget* _tracksContainer; diff --git a/src/ui/resource/AudioResource.cpp b/src/ui/resource/AudioResource.cpp index 5977aeaa..101e80d1 100644 --- a/src/ui/resource/AudioResource.cpp +++ b/src/ui/resource/AudioResource.cpp @@ -49,8 +49,6 @@ AudioResource::handleRequest(const Wt::Http::Request& request, { std::shared_ptr transcoder; - LMS_LOG(UI, DEBUG) << "Handling new request..."; - // First, see if this request is for a continuation Wt::Http::ResponseContinuation *continuation = request.continuation(); if (continuation) @@ -62,8 +60,9 @@ AudioResource::handleRequest(const Wt::Http::Request& request, { Database::IdType trackId; Av::TranscodeParameters parameters {}; + parameters.stripMetadata = true; - LMS_LOG(UI, DEBUG) << "No continuation yet"; + LMS_LOG(UI, DEBUG) << "First request: creating transcoder"; try { auto trackIdStr = request.getParameter("trackid"); @@ -75,8 +74,7 @@ AudioResource::handleRequest(const Wt::Http::Request& request, trackId = std::stol(*request.getParameter("trackid")); auto offsetStr = request.getParameter("offset"); - if (offsetStr) - parameters.offset = std::chrono::seconds(std::stol(*offsetStr)); + parameters.offset = std::chrono::seconds(offsetStr ? std::stol(*offsetStr) : 0); } catch (std::exception &e) { @@ -98,34 +96,35 @@ AudioResource::handleRequest(const Wt::Http::Request& request, return; } - parameters.bitrate = LmsApp->getUser()->getAudioBitrate(); - - switch (LmsApp->getUser()->getAudioEncoding()) + if (LmsApp->getUser()->getAudioTranscodeEnable()) { - case Database::AudioEncoding::AUTO: - case Database::AudioEncoding::MP3: - parameters.encoding = Av::Encoding::MP3; - break; - case Database::AudioEncoding::OGG_OPUS: - parameters.encoding = Av::Encoding::OGG_OPUS; - break; - case Database::AudioEncoding::OGG_VORBIS: - parameters.encoding = Av::Encoding::OGG_VORBIS; - break; - case Database::AudioEncoding::WEBM_VORBIS: - parameters.encoding = Av::Encoding::WEBM_VORBIS; - break; - default: - parameters.encoding = Av::Encoding::MP3; - break; + parameters.bitrate = LmsApp->getUser()->getAudioTranscodeBitrate(); + + switch (LmsApp->getUser()->getAudioTranscodeFormat()) + { + case Database::AudioFormat::MP3: + parameters.encoding = Av::Encoding::MP3; + break; + case Database::AudioFormat::OGG_OPUS: + parameters.encoding = Av::Encoding::OGG_OPUS; + break; + case Database::AudioFormat::OGG_VORBIS: + parameters.encoding = Av::Encoding::OGG_VORBIS; + break; + case Database::AudioFormat::WEBM_VORBIS: + parameters.encoding = Av::Encoding::WEBM_VORBIS; + break; + default: + parameters.encoding = Av::Encoding::OGG_OPUS; + break; + } } + else + parameters.bitrate = 0; + transcoder = std::make_shared(track->getPath(), parameters); } - std::string mimeType = Av::encodingToMimetype(transcoder->getParameters().encoding); - - LMS_LOG(UI, DEBUG) << "Mime type set to '" << mimeType << "'"; - response.setMimeType(mimeType); if (!transcoder->start()) { @@ -134,6 +133,11 @@ AudioResource::handleRequest(const Wt::Http::Request& request, } LMS_LOG(UI, DEBUG) << "Transcoder started"; + + std::string mimeType {transcoder->getOutputMimeType()}; + response.setMimeType(mimeType); + LMS_LOG(UI, DEBUG) << "Mime type set to '" << mimeType << "'"; + } if (!transcoder->isComplete()) @@ -144,7 +148,7 @@ AudioResource::handleRequest(const Wt::Http::Request& request, transcoder->process(data, _chunkSize); response.out().write(reinterpret_cast(&data[0]), data.size()); - LMS_LOG(UI, DEBUG) << "Written " << data.size() << " bytes! complete = " << std::boolalpha << transcoder->isComplete(); + LMS_LOG(UI, DEBUG) << "Written " << data.size() << " bytes! complete = " << (transcoder->isComplete() ? "true" : "false"); if (!response.out()) { diff --git a/test/database/DatabaseTest.cpp b/test/database/DatabaseTest.cpp index 4486c53f..3709329b 100644 --- a/test/database/DatabaseTest.cpp +++ b/test/database/DatabaseTest.cpp @@ -482,6 +482,10 @@ testSingleTrackSingleCluster(Wt::Dbo::Session& session) auto tracks {Track::getByFilter(session, {clusterId})}; CHECK(tracks.size() == 1); CHECK(tracks.front().id() == trackId); + + tracks = Track::getByFilter(session, {}); + CHECK(tracks.size() == 1); + CHECK(tracks.front().id() == trackId); } { @@ -535,6 +539,10 @@ testSingleTrackSingleReleaseSingleCluster(Wt::Dbo::Session& session) auto releases {Release::getByFilter(session, {clusterId})}; CHECK(releases.size() == 1); CHECK(releases.front().id() == releaseId); + + releases = Release::getByFilter(session, {clusterId}); + CHECK(releases.size() == 1); + CHECK(releases.front().id() == releaseId); } { @@ -595,6 +603,10 @@ testSingleTrackSingleArtistMultiClusters(Wt::Dbo::Session& session) CHECK(artists.size() == 1); CHECK(artists.front().id() == artistId); + artists = Artist::getByFilter(session, {}); + CHECK(artists.size() == 1); + CHECK(artists.front().id() == artistId); + artists = Artist::getByFilter(session, {cluster2Id}); CHECK(artists.empty());