Made transcoding optional
This commit is contained in:
@@ -42,17 +42,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2" for="${id:audio-bitrate-limit}">
|
<label class="control-label col-sm-2" for="${id:audio-transcode-bitrate-limit}">
|
||||||
${tr:Lms.Admin.User.audio-bitrate-limit}
|
${tr:Lms.Admin.User.audio-transcode-bitrate-limit}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-5">
|
<div class="col-sm-5">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
${audio-bitrate-limit}
|
${audio-transcode-bitrate-limit}
|
||||||
<span class="input-group-addon">kbps</span>
|
<span class="input-group-addon">kbps</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="help-block col-sm-5">
|
<div class="help-block col-sm-5">
|
||||||
${audio-bitrate-limit-info}
|
${audio-transcode-bitrate-limit-info}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${<if-demo>}
|
${<if-demo>}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
<message id="Lms.Admin.Users.users">Users</message>
|
<message id="Lms.Admin.Users.users">Users</message>
|
||||||
|
|
||||||
<!--User-->
|
<!--User-->
|
||||||
<message id="Lms.Admin.User.audio-bitrate-limit">Audio bitrate limit</message>
|
<message id="Lms.Admin.User.audio-transcode-bitrate-limit">Audio transcode bitrate limit</message>
|
||||||
<message id="Lms.Admin.User.demo-account">Demo account</message>
|
<message id="Lms.Admin.User.demo-account">Demo account</message>
|
||||||
<message id="Lms.Admin.User.demo-account-already-exists">Demo account already exists!</message>
|
<message id="Lms.Admin.User.demo-account-already-exists">Demo account already exists!</message>
|
||||||
<message id="Lms.Admin.User.demo-password-invalid">Demo password must be the login name!</message>
|
<message id="Lms.Admin.User.demo-password-invalid">Demo password must be the login name!</message>
|
||||||
@@ -114,14 +114,15 @@
|
|||||||
<!--Settings-->
|
<!--Settings-->
|
||||||
<message id="Lms.Settings.account">Account</message>
|
<message id="Lms.Settings.account">Account</message>
|
||||||
<message id="Lms.Settings.audio">Audio</message>
|
<message id="Lms.Settings.audio">Audio</message>
|
||||||
<message id="Lms.Settings.bitrate">Bitrate</message>
|
|
||||||
<message id="Lms.Settings.demo-cannot-save">Cannot save using a demo account!</message>
|
<message id="Lms.Settings.demo-cannot-save">Cannot save using a demo account!</message>
|
||||||
<message id="Lms.Settings.encoding">Encoding</message>
|
<message id="Lms.Settings.transcoding">Transcoding</message>
|
||||||
<message id="Lms.Settings.encoding.auto">Auto</message>
|
<message id="Lms.Settings.transcoding-bitrate">Transcode bitrate</message>
|
||||||
<message id="Lms.Settings.encoding.mp3">MP3</message>
|
<message id="Lms.Settings.transcoding-format">Transcode format</message>
|
||||||
<message id="Lms.Settings.encoding.ogg_opus">Ogg/Opus</message>
|
<message id="Lms.Settings.transcoding-enable">Enable transcoding</message>
|
||||||
<message id="Lms.Settings.encoding.ogg_vorbis">Ogg/Vorbis</message>
|
<message id="Lms.Settings.transcoding.mp3">MP3</message>
|
||||||
<message id="Lms.Settings.encoding.webm_vorbis">WebM/Vorbis</message>
|
<message id="Lms.Settings.transcoding.ogg_opus">Ogg/Opus</message>
|
||||||
|
<message id="Lms.Settings.transcoding.ogg_vorbis">Ogg/Vorbis</message>
|
||||||
|
<message id="Lms.Settings.transcoding.webm_vorbis">WebM/Vorbis</message>
|
||||||
<message id="Lms.Settings.settings">Settings</message>
|
<message id="Lms.Settings.settings">Settings</message>
|
||||||
<message id="Lms.Settings.settings-saved">New settings saved!</message>
|
<message id="Lms.Settings.settings-saved">New settings saved!</message>
|
||||||
|
|
||||||
|
|||||||
+10
-9
@@ -45,7 +45,7 @@
|
|||||||
<message id="Lms.Admin.Users.users">Utilisateurs</message>
|
<message id="Lms.Admin.Users.users">Utilisateurs</message>
|
||||||
|
|
||||||
<!--User-->
|
<!--User-->
|
||||||
<message id="Lms.Admin.User.audio-bitrate-limit">Limite du bitrate</message>
|
<message id="Lms.Admin.User.audio-transcode-bitrate-limit">Limite du bitrate pour le transcodage</message>
|
||||||
<message id="Lms.Admin.User.demo-account">Compte de démonstration</message>
|
<message id="Lms.Admin.User.demo-account">Compte de démonstration</message>
|
||||||
<message id="Lms.Admin.User.demo-account-already-exists">Le compte de démonstration existe déjà !</message>
|
<message id="Lms.Admin.User.demo-account-already-exists">Le compte de démonstration existe déjà !</message>
|
||||||
<message id="Lms.Admin.User.demo-password-invalid">Le password doit être égal au login !</message>
|
<message id="Lms.Admin.User.demo-password-invalid">Le password doit être égal au login !</message>
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
<message id="Lms.PlayQueue.clear">Effacer</message>
|
<message id="Lms.PlayQueue.clear">Effacer</message>
|
||||||
<message id="Lms.PlayQueue.nb-tracks">{1} pistes</message>
|
<message id="Lms.PlayQueue.nb-tracks">{1} pistes</message>
|
||||||
<message id="Lms.PlayQueue.nb-tracks-added">{1} pistes ajoutées</message>
|
<message id="Lms.PlayQueue.nb-tracks-added">{1} pistes ajoutées</message>
|
||||||
<message id="Lms.PlayQueue.nb-tracks-playing">{1} pistes ajouées</message>
|
<message id="Lms.PlayQueue.nb-tracks-playing">{1} pistes ajoutées</message>
|
||||||
<message id="Lms.PlayQueue.playqueue">Liste de lecture</message>
|
<message id="Lms.PlayQueue.playqueue">Liste de lecture</message>
|
||||||
<message id="Lms.PlayQueue.radio-mode">Mode radio</message>
|
<message id="Lms.PlayQueue.radio-mode">Mode radio</message>
|
||||||
<message id="Lms.PlayQueue.repeat">Repéter</message>
|
<message id="Lms.PlayQueue.repeat">Repéter</message>
|
||||||
@@ -115,14 +115,15 @@
|
|||||||
<message id="Lms.Settings.account">Compte</message>
|
<message id="Lms.Settings.account">Compte</message>
|
||||||
<message id="Lms.Settings.audio">Audio</message>
|
<message id="Lms.Settings.audio">Audio</message>
|
||||||
<message id="Lms.Settings.auto">Auto</message>
|
<message id="Lms.Settings.auto">Auto</message>
|
||||||
<message id="Lms.Settings.bitrate">Bitrate</message>
|
|
||||||
<message id="Lms.Settings.demo-cannot-save">Impossible de sauvegarder en utilisant un compte de démo !</message>
|
<message id="Lms.Settings.demo-cannot-save">Impossible de sauvegarder en utilisant un compte de démo !</message>
|
||||||
<message id="Lms.Settings.encoding">Encodage</message>
|
<message id="Lms.Settings.transcoding">Transcodage</message>
|
||||||
<message id="Lms.Settings.encoding.auto">Auto</message>
|
<message id="Lms.Settings.transcoding-bitrate">Bitrate du transcodage</message>
|
||||||
<message id="Lms.Settings.encoding.mp3">MP3</message>
|
<message id="Lms.Settings.transcoding-format">Format du transcodage</message>
|
||||||
<message id="Lms.Settings.encoding.ogg_opus">Ogg/Opus</message>
|
<message id="Lms.Settings.transcoding-enable">Activer le transcodage</message>
|
||||||
<message id="Lms.Settings.encoding.ogg_vorbis">Ogg/Vorbis</message>
|
<message id="Lms.Settings.transcoding.mp3">MP3</message>
|
||||||
<message id="Lms.Settings.encoding.webm_vorbis">WebM/Vorbis</message>
|
<message id="Lms.Settings.transcoding.ogg_opus">Ogg/Opus</message>
|
||||||
|
<message id="Lms.Settings.transcoding.ogg_vorbis">Ogg/Vorbis</message>
|
||||||
|
<message id="Lms.Settings.transcoding.webm_vorbis">WebM/Vorbis</message>
|
||||||
<message id="Lms.Settings.settings">Paramètres</message>
|
<message id="Lms.Settings.settings">Paramètres</message>
|
||||||
<message id="Lms.Settings.settings-saved">Paramètres sauvegardés !</message>
|
<message id="Lms.Settings.settings-saved">Paramètres sauvegardés !</message>
|
||||||
|
|
||||||
|
|||||||
+26
-15
@@ -7,28 +7,39 @@
|
|||||||
<legend>${tr:Lms.Settings.audio}</legend>
|
<legend>${tr:Lms.Settings.audio}</legend>
|
||||||
<div class="form-horizontal">
|
<div class="form-horizontal">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-2" for="${id:bitrate}">
|
<label class="control-label col-sm-2" for="${id:transcoding-enable}">
|
||||||
${tr:Lms.Settings.bitrate}
|
${tr:Lms.Settings.transcoding-enable}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-5">
|
||||||
|
${transcoding-enable}
|
||||||
|
</div>
|
||||||
|
<div class="help-block col-sm-5">
|
||||||
|
${transcoding-enable-info}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2" for="${id:transcoding}">
|
||||||
|
${tr:Lms.Settings.transcoding-format}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-5">
|
||||||
|
${transcoding-format}
|
||||||
|
</div>
|
||||||
|
<div class="help-block col-sm-5">
|
||||||
|
${transcoding-format-info}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2" for="${id:transcoding-bitrate}">
|
||||||
|
${tr:Lms.Settings.transcoding-bitrate}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-5">
|
<div class="col-sm-5">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
${bitrate}
|
${transcoding-bitrate}
|
||||||
<span class="input-group-addon">kbps</span>
|
<span class="input-group-addon">kbps</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="help-block col-sm-5">
|
<div class="help-block col-sm-5">
|
||||||
${bitrate-info}
|
${transcoding-bitrate-info}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-sm-2" for="${id:encoding}">
|
|
||||||
${tr:Lms.Settings.encoding}
|
|
||||||
</label>
|
|
||||||
<div class="col-sm-5">
|
|
||||||
${encoding}
|
|
||||||
</div>
|
|
||||||
<div class="help-block col-sm-5">
|
|
||||||
${encoding-info}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1354,9 +1354,9 @@ createTranscoder(RequestContext& context)
|
|||||||
|
|
||||||
// "If set to zero, no limit is imposed"
|
// "If set to zero, no limit is imposed"
|
||||||
if (!maxBitRate || *maxBitRate == 0)
|
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)};
|
auto track {Database::Track::getById(context.db.getSession(), id.value)};
|
||||||
if (!track)
|
if (!track)
|
||||||
|
|||||||
+36
-5
@@ -24,6 +24,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#include "utils/Logger.hpp"
|
#include "utils/Logger.hpp"
|
||||||
|
#include "utils/Utils.hpp"
|
||||||
|
|
||||||
namespace Av {
|
namespace Av {
|
||||||
|
|
||||||
@@ -51,10 +52,8 @@ void AvInit()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
MediaFile::MediaFile(const boost::filesystem::path& p)
|
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);
|
int error = avformat_open_input(&_context, _p.string().c_str(), nullptr, nullptr);
|
||||||
if (error < 0)
|
if (error < 0)
|
||||||
@@ -77,6 +76,12 @@ MediaFile::~MediaFile()
|
|||||||
avformat_close_input(&_context);
|
avformat_close_input(&_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
MediaFile::getFormatName() const
|
||||||
|
{
|
||||||
|
return _context->iformat->name;
|
||||||
|
}
|
||||||
|
|
||||||
std::chrono::milliseconds
|
std::chrono::milliseconds
|
||||||
MediaFile::getDuration() const
|
MediaFile::getDuration() const
|
||||||
{
|
{
|
||||||
@@ -127,9 +132,9 @@ MediaFile::getStreamInfo() const
|
|||||||
{
|
{
|
||||||
std::vector<StreamInfo> res;
|
std::vector<StreamInfo> 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
|
// Skip attached pics
|
||||||
if (avstream->disposition & AV_DISPOSITION_ATTACHED_PIC)
|
if (avstream->disposition & AV_DISPOSITION_ATTACHED_PIC)
|
||||||
@@ -233,4 +238,30 @@ MediaFile::getAttachedPictures(std::size_t nbMaxPictures) const
|
|||||||
return pictures;
|
return pictures;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::optional<MediaFileFormat> 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<std::string> 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
|
} // namespace Av
|
||||||
|
|||||||
+14
-3
@@ -69,9 +69,12 @@ class MediaFile
|
|||||||
MediaFile(const boost::filesystem::path& p);
|
MediaFile(const boost::filesystem::path& p);
|
||||||
~MediaFile();
|
~MediaFile();
|
||||||
|
|
||||||
// non copyable
|
|
||||||
MediaFile(const MediaFile&) = delete;
|
MediaFile(const MediaFile&) = delete;
|
||||||
MediaFile& operator=(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;};
|
const boost::filesystem::path& getPath() const {return _p;};
|
||||||
|
|
||||||
@@ -84,11 +87,19 @@ class MediaFile
|
|||||||
std::vector<Picture> getAttachedPictures(std::size_t nbMaxPictures) const;
|
std::vector<Picture> getAttachedPictures(std::size_t nbMaxPictures) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MediaFile();
|
|
||||||
|
|
||||||
boost::filesystem::path _p;
|
boost::filesystem::path _p;
|
||||||
AVFormatContext* _context;
|
AVFormatContext* _context {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct MediaFileFormat
|
||||||
|
{
|
||||||
|
std::string mimeType;
|
||||||
|
std::string format;
|
||||||
|
};
|
||||||
|
|
||||||
|
boost::optional<MediaFileFormat> guessMediaFileFormat(const boost::filesystem::path& file);
|
||||||
|
|
||||||
} // namespace Av
|
} // namespace Av
|
||||||
|
|
||||||
|
|||||||
+51
-32
@@ -22,6 +22,7 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "AvInfo.hpp"
|
||||||
#include "utils/Path.hpp"
|
#include "utils/Path.hpp"
|
||||||
#include "utils/Logger.hpp"
|
#include "utils/Logger.hpp"
|
||||||
|
|
||||||
@@ -99,10 +100,6 @@ Transcoder::start()
|
|||||||
args.push_back("-i");
|
args.push_back("-i");
|
||||||
args.push_back(_filePath.string());
|
args.push_back(_filePath.string());
|
||||||
|
|
||||||
// Output bitrates
|
|
||||||
args.push_back("-b:a");
|
|
||||||
args.push_back(std::to_string(_parameters.bitrate));
|
|
||||||
|
|
||||||
// Stream mapping, if set
|
// Stream mapping, if set
|
||||||
if (_parameters.stream)
|
if (_parameters.stream)
|
||||||
{
|
{
|
||||||
@@ -121,37 +118,63 @@ Transcoder::start()
|
|||||||
args.push_back("-vn");
|
args.push_back("-vn");
|
||||||
|
|
||||||
// Codecs and formats
|
// Codecs and formats
|
||||||
switch( _parameters.encoding)
|
if (_parameters.encoding)
|
||||||
{
|
{
|
||||||
case Encoding::MP3:
|
// Output bitrates
|
||||||
args.push_back("-f");
|
args.push_back("-b:a");
|
||||||
args.push_back("mp3");
|
args.push_back(std::to_string(_parameters.bitrate));
|
||||||
break;
|
|
||||||
|
|
||||||
case Encoding::OGG_OPUS:
|
switch (*_parameters.encoding)
|
||||||
args.push_back("-acodec");
|
{
|
||||||
args.push_back("libopus");
|
case Encoding::MP3:
|
||||||
args.push_back("-f");
|
args.push_back("-f");
|
||||||
args.push_back("ogg");
|
args.push_back("mp3");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Encoding::OGG_VORBIS:
|
case Encoding::OGG_OPUS:
|
||||||
args.push_back("-acodec");
|
args.push_back("-acodec");
|
||||||
args.push_back("libvorbis");
|
args.push_back("libopus");
|
||||||
args.push_back("-f");
|
args.push_back("-f");
|
||||||
args.push_back("ogg");
|
args.push_back("ogg");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Encoding::WEBM_VORBIS:
|
case Encoding::OGG_VORBIS:
|
||||||
args.push_back("-acodec");
|
args.push_back("-acodec");
|
||||||
args.push_back("libvorbis");
|
args.push_back("libvorbis");
|
||||||
args.push_back("-f");
|
args.push_back("-f");
|
||||||
args.push_back("webm");
|
args.push_back("ogg");
|
||||||
break;
|
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;
|
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");
|
args.push_back("pipe:1");
|
||||||
@@ -204,14 +227,10 @@ Transcoder::process(std::vector<unsigned char>& output, std::size_t maxSize)
|
|||||||
|
|
||||||
output.resize(maxSize);
|
output.resize(maxSize);
|
||||||
|
|
||||||
LMS_LOG_TRANSCODE(DEBUG) << "Reading up to " << output.size() << " bytes";
|
|
||||||
|
|
||||||
//Read on the output stream
|
//Read on the output stream
|
||||||
_child->out().read(reinterpret_cast<char*>(&output[0]), maxSize);
|
_child->out().read(reinterpret_cast<char*>(&output[0]), maxSize);
|
||||||
output.resize(_child->out().gcount());
|
output.resize(_child->out().gcount());
|
||||||
|
|
||||||
LMS_LOG_TRANSCODE(DEBUG) << "Read " << output.size() << " bytes";
|
|
||||||
|
|
||||||
if (_child->out().fail())
|
if (_child->out().fail())
|
||||||
{
|
{
|
||||||
LMS_LOG_TRANSCODE(DEBUG) << "Stdout FAILED";
|
LMS_LOG_TRANSCODE(DEBUG) << "Stdout FAILED";
|
||||||
|
|||||||
@@ -19,15 +19,13 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map>
|
#include <chrono>
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include <pstreams/pstream.h>
|
#include <pstreams/pstream.h>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
#include "AvInfo.hpp"
|
|
||||||
#include "AvTypes.hpp"
|
#include "AvTypes.hpp"
|
||||||
|
|
||||||
namespace Av {
|
namespace Av {
|
||||||
@@ -36,11 +34,11 @@ namespace Av {
|
|||||||
|
|
||||||
struct TranscodeParameters
|
struct TranscodeParameters
|
||||||
{
|
{
|
||||||
Encoding encoding {Encoding::MP3};
|
boost::optional<Encoding> encoding; // If not set, no transcoding is performed
|
||||||
std::size_t bitrate {128000};
|
std::size_t bitrate {128000};
|
||||||
boost::optional<std::size_t> stream; // Id of the stream to be transcoded (auto detect by default)
|
boost::optional<std::size_t> stream; // Id of the stream to be transcoded (auto detect by default)
|
||||||
boost::optional<std::chrono::seconds> offset;
|
boost::optional<std::chrono::seconds> offset {};
|
||||||
bool stripMetadata {true};
|
bool stripMetadata {true};
|
||||||
};
|
};
|
||||||
|
|
||||||
class Transcoder
|
class Transcoder
|
||||||
@@ -56,11 +54,13 @@ class Transcoder
|
|||||||
Transcoder& operator=(const Transcoder&) = delete;
|
Transcoder& operator=(const Transcoder&) = delete;
|
||||||
|
|
||||||
bool start();
|
bool start();
|
||||||
|
const std::string& getOutputMimeType() const { return _outputMimeType; }
|
||||||
void process(std::vector<unsigned char>& output, std::size_t maxSize);
|
void process(std::vector<unsigned char>& output, std::size_t maxSize);
|
||||||
bool isComplete(void) const { return _isComplete; }
|
bool isComplete(void) const { return _isComplete; }
|
||||||
|
|
||||||
const TranscodeParameters& getParameters() const { return _parameters; }
|
const TranscodeParameters& getParameters() const { return _parameters; }
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Transcoder();
|
Transcoder();
|
||||||
|
|
||||||
@@ -72,6 +72,7 @@ class Transcoder
|
|||||||
bool _isComplete = false;
|
bool _isComplete = false;
|
||||||
std::size_t _total = 0;
|
std::size_t _total = 0;
|
||||||
std::size_t _id;
|
std::size_t _id;
|
||||||
|
std::string _outputMimeType;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Av
|
} // namespace Av
|
||||||
|
|||||||
@@ -40,5 +40,10 @@ std::string encodingToMimetype(Encoding encoding)
|
|||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Encoding guessEncoding(const boost::filesystem::path& file)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-4
@@ -20,6 +20,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
#include "utils/Exception.hpp"
|
#include "utils/Exception.hpp"
|
||||||
|
|
||||||
@@ -34,10 +35,10 @@ class AvException : public LmsException
|
|||||||
enum class Encoding
|
enum class Encoding
|
||||||
{
|
{
|
||||||
// Values are important and must not be changed
|
// Values are important and must not be changed
|
||||||
MP3 = 0,
|
MP3 = 0,
|
||||||
OGG_OPUS = 1,
|
OGG_OPUS = 1,
|
||||||
OGG_VORBIS = 2,
|
OGG_VORBIS = 2,
|
||||||
WEBM_VORBIS = 3,
|
WEBM_VORBIS = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string encodingToMimetype(Encoding encoding);
|
std::string encodingToMimetype(Encoding encoding);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
|
|
||||||
namespace Database {
|
namespace Database {
|
||||||
|
|
||||||
#define LMS_DATABASE_VERSION 1
|
#define LMS_DATABASE_VERSION 2
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
Wt::Auth::AuthService authService;
|
Wt::Auth::AuthService authService;
|
||||||
|
|||||||
@@ -211,9 +211,9 @@ Track::getByFilter(Wt::Dbo::Session& session,
|
|||||||
.limit(size ? static_cast<int>(*size) + 1 : -1)
|
.limit(size ? static_cast<int>(*size) + 1 : -1)
|
||||||
.offset(offset ? static_cast<int>(*offset) : -1);
|
.offset(offset ? static_cast<int>(*offset) : -1);
|
||||||
|
|
||||||
auto res = std::vector<pointer>(collection.begin(), collection.end());
|
std::vector<pointer> res(collection.begin(), collection.end());
|
||||||
|
|
||||||
if (size && res.size() == static_cast<std::size_t>(*size) + 1)
|
if (size && (res.size() == static_cast<std::size_t>(*size) + 1))
|
||||||
{
|
{
|
||||||
moreResults = true;
|
moreResults = true;
|
||||||
res.pop_back();
|
res.pop_back();
|
||||||
|
|||||||
+17
-18
@@ -23,9 +23,8 @@
|
|||||||
|
|
||||||
namespace Database {
|
namespace Database {
|
||||||
|
|
||||||
// must be ordered
|
const std::set<Bitrate>
|
||||||
const std::vector<std::size_t>
|
User::audioTranscodeAllowedBitrates =
|
||||||
User::audioBitrates =
|
|
||||||
{
|
{
|
||||||
64000,
|
64000,
|
||||||
96000,
|
96000,
|
||||||
@@ -35,7 +34,7 @@ User::audioBitrates =
|
|||||||
};
|
};
|
||||||
|
|
||||||
User::User()
|
User::User()
|
||||||
: _maxAudioBitrate(audioBitrates.back())
|
: _maxAudioTranscodeBitrate{static_cast<int>(*audioTranscodeAllowedBitrates.rbegin())}
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -67,29 +66,29 @@ User::getById(Wt::Dbo::Session& session, IdType id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
User::setAudioBitrate(std::size_t bitrate)
|
User::setAudioTranscodeBitrate(Bitrate bitrate)
|
||||||
{
|
{
|
||||||
_audioBitrate = std::min(bitrate, static_cast<std::size_t>(_maxAudioBitrate));
|
_audioTranscodeBitrate = std::min(bitrate, static_cast<Bitrate>(_maxAudioTranscodeBitrate));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
User::setMaxAudioBitrate(std::size_t bitrate)
|
User::setMaxAudioTranscodeBitrate(Bitrate bitrate)
|
||||||
{
|
{
|
||||||
_maxAudioBitrate = std::min(bitrate, audioBitrates.back());
|
_maxAudioTranscodeBitrate = std::min(bitrate, *audioTranscodeAllowedBitrates.rbegin());
|
||||||
if (_audioBitrate > _maxAudioBitrate)
|
if (_audioTranscodeBitrate > _maxAudioTranscodeBitrate)
|
||||||
_audioBitrate = _maxAudioBitrate;
|
_audioTranscodeBitrate = _maxAudioTranscodeBitrate;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitrate
|
||||||
|
User::getAudioTranscodeBitrate(void) const
|
||||||
|
{
|
||||||
|
return _audioTranscodeBitrate;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
User::getAudioBitrate(void) const
|
User::getMaxAudioTranscodeBitrate(void) const
|
||||||
{
|
{
|
||||||
return _audioBitrate;
|
return _maxAudioTranscodeBitrate;
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t
|
|
||||||
User::getMaxAudioBitrate(void) const
|
|
||||||
{
|
|
||||||
return _maxAudioBitrate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Wt::Dbo::ptr<TrackList>
|
Wt::Dbo::ptr<TrackList>
|
||||||
|
|||||||
+37
-29
@@ -34,15 +34,17 @@ using AuthInfo = Wt::Auth::Dbo::AuthInfo<User>;
|
|||||||
class TrackList;
|
class TrackList;
|
||||||
|
|
||||||
// User selectable audio formats
|
// User selectable audio formats
|
||||||
enum class AudioEncoding
|
// Do not change values
|
||||||
|
enum class AudioFormat
|
||||||
{
|
{
|
||||||
AUTO = 0,
|
|
||||||
MP3 = 1,
|
MP3 = 1,
|
||||||
OGG_OPUS = 2,
|
OGG_OPUS = 2,
|
||||||
OGG_VORBIS = 3,
|
OGG_VORBIS = 3,
|
||||||
WEBM_VORBIS = 4,
|
WEBM_VORBIS = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using Bitrate = std::size_t;
|
||||||
|
|
||||||
class User : public Wt::Dbo::Dbo<User>
|
class User : public Wt::Dbo::Dbo<User>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -59,7 +61,7 @@ class User : public Wt::Dbo::Dbo<User>
|
|||||||
};
|
};
|
||||||
|
|
||||||
// list of audio parameters
|
// list of audio parameters
|
||||||
static const std::vector<std::size_t> audioBitrates;
|
static const std::set<Bitrate> audioTranscodeAllowedBitrates;
|
||||||
|
|
||||||
User();
|
User();
|
||||||
|
|
||||||
@@ -72,23 +74,25 @@ class User : public Wt::Dbo::Dbo<User>
|
|||||||
static pointer getDemo(Wt::Dbo::Session& session);
|
static pointer getDemo(Wt::Dbo::Session& session);
|
||||||
|
|
||||||
// write
|
// write
|
||||||
void setType(Type type) { _type = type; }
|
void setType(Type type) { _type = type; }
|
||||||
void setAudioBitrate(std::size_t bitrate);
|
void setAudioTranscodeEnable(bool value) { _audioTranscodeEnable = value; }
|
||||||
void setAudioEncoding(AudioEncoding encoding) { _audioEncoding = encoding; }
|
void setAudioTranscodeFormat(AudioFormat format) { _audioTranscodeFormat = format; }
|
||||||
void setMaxAudioBitrate(std::size_t bitrate);
|
void setAudioTranscodeBitrate(Bitrate bitrate);
|
||||||
void setCurPlayingTrackPos(std::size_t pos) { _curPlayingTrackPos = pos; }
|
void setMaxAudioTranscodeBitrate(Bitrate bitrate);
|
||||||
void setRadio(bool val) { _radio = val; }
|
void setCurPlayingTrackPos(std::size_t pos) { _curPlayingTrackPos = pos; }
|
||||||
void setRepeatAll(bool val) { _repeatAll = val; }
|
void setRadio(bool val) { _radio = val; }
|
||||||
|
void setRepeatAll(bool val) { _repeatAll = val; }
|
||||||
|
|
||||||
// read
|
// read
|
||||||
bool isAdmin() const { return _type == Type::ADMIN; }
|
bool isAdmin() const { return _type == Type::ADMIN; }
|
||||||
bool isDemo() const { return _type == Type::DEMO; }
|
bool isDemo() const { return _type == Type::DEMO; }
|
||||||
std::size_t getAudioBitrate() const;
|
bool getAudioTranscodeEnable() const { return _audioTranscodeEnable; }
|
||||||
AudioEncoding getAudioEncoding() const { return _audioEncoding; }
|
Bitrate getAudioTranscodeBitrate() const;
|
||||||
std::size_t getMaxAudioBitrate() const;
|
AudioFormat getAudioTranscodeFormat() const { return _audioTranscodeFormat; }
|
||||||
std::size_t getCurPlayingTrackPos() const { return _curPlayingTrackPos; }
|
Bitrate getMaxAudioTranscodeBitrate() const;
|
||||||
bool isRepeatAllSet() const { return _repeatAll; }
|
std::size_t getCurPlayingTrackPos() const { return _curPlayingTrackPos; }
|
||||||
bool isRadioSet() const { return _radio; }
|
bool isRepeatAllSet() const { return _repeatAll; }
|
||||||
|
bool isRadioSet() const { return _radio; }
|
||||||
|
|
||||||
Wt::Dbo::ptr<TrackList> getQueuedTrackList() const;
|
Wt::Dbo::ptr<TrackList> getQueuedTrackList() const;
|
||||||
Wt::Dbo::ptr<TrackList> getPlayedTrackList() const;
|
Wt::Dbo::ptr<TrackList> getPlayedTrackList() const;
|
||||||
@@ -96,10 +100,11 @@ class User : public Wt::Dbo::Dbo<User>
|
|||||||
template<class Action>
|
template<class Action>
|
||||||
void persist(Action& a)
|
void persist(Action& a)
|
||||||
{
|
{
|
||||||
Wt::Dbo::field(a, _maxAudioBitrate, "max_audio_bitrate");
|
|
||||||
Wt::Dbo::field(a, _type, "type");
|
Wt::Dbo::field(a, _type, "type");
|
||||||
Wt::Dbo::field(a, _audioBitrate, "audio_bitrate");
|
Wt::Dbo::field(a, _maxAudioTranscodeBitrate, "max_audio_bitrate");
|
||||||
Wt::Dbo::field(a, _audioEncoding, "audio_encoding");
|
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
|
// User's dynamic data
|
||||||
Wt::Dbo::field(a, _curPlayingTrackPos, "cur_playing_track_pos");
|
Wt::Dbo::field(a, _curPlayingTrackPos, "cur_playing_track_pos");
|
||||||
Wt::Dbo::field(a, _repeatAll, "repeat_all");
|
Wt::Dbo::field(a, _repeatAll, "repeat_all");
|
||||||
@@ -109,20 +114,23 @@ class User : public Wt::Dbo::Dbo<User>
|
|||||||
|
|
||||||
private:
|
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
|
// Admin defined settings
|
||||||
int _maxAudioBitrate;
|
int _maxAudioTranscodeBitrate;
|
||||||
Type _type = Type::REGULAR;
|
Type _type {Type::REGULAR};
|
||||||
|
|
||||||
// User defined settings
|
// User defined settings
|
||||||
int _audioBitrate = defaultAudioBitrate;
|
bool _audioTranscodeEnable {defaultAudioTranscodeEnable};
|
||||||
AudioEncoding _audioEncoding = AudioEncoding::AUTO;
|
AudioFormat _audioTranscodeFormat {defaultAudioTranscodeFormat};
|
||||||
|
int _audioTranscodeBitrate {defaultAudioTranscodeBitrate};
|
||||||
|
|
||||||
// User's dynamic data
|
// User's dynamic data
|
||||||
int _curPlayingTrackPos = 0; // Current track position in queue
|
int _curPlayingTrackPos {}; // Current track position in queue
|
||||||
bool _repeatAll = false;
|
bool _repeatAll {};
|
||||||
bool _radio = false;
|
bool _radio {};
|
||||||
|
|
||||||
Wt::Dbo::collection< Wt::Dbo::ptr<TrackList> > _tracklists;
|
Wt::Dbo::collection< Wt::Dbo::ptr<TrackList> > _tracklists;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -63,7 +63,7 @@ std::vector<std::string> generateWtConfig(std::string execPath)
|
|||||||
args.push_back("--http-address=" + Config::instance().getString("listen-addr", "0.0.0.0"));
|
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
|
// Generate the wt_config.xml file
|
||||||
boost::property_tree::ptree pt;
|
boost::property_tree::ptree pt;
|
||||||
|
|||||||
+106
-84
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <Wt/WString.h>
|
#include <Wt/WString.h>
|
||||||
#include <Wt/WPushButton.h>
|
#include <Wt/WPushButton.h>
|
||||||
|
#include <Wt/WCheckBox.h>
|
||||||
#include <Wt/WComboBox.h>
|
#include <Wt/WComboBox.h>
|
||||||
#include <Wt/WLineEdit.h>
|
#include <Wt/WLineEdit.h>
|
||||||
|
|
||||||
@@ -40,8 +41,9 @@ class SettingsModel : public Wt::WFormModel
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Associate each field with a unique string literal.
|
// Associate each field with a unique string literal.
|
||||||
static const Field BitrateField;
|
static const Field TranscodeEnableField;
|
||||||
static const Field EncodingField;
|
static const Field TranscodeFormatField;
|
||||||
|
static const Field TranscodeBitrateField;
|
||||||
static const Field PasswordField;
|
static const Field PasswordField;
|
||||||
static const Field PasswordConfirmField;
|
static const Field PasswordConfirmField;
|
||||||
|
|
||||||
@@ -50,49 +52,57 @@ class SettingsModel : public Wt::WFormModel
|
|||||||
{
|
{
|
||||||
initializeModels();
|
initializeModels();
|
||||||
|
|
||||||
addField(BitrateField);
|
addField(TranscodeEnableField);
|
||||||
addField(EncodingField);
|
addField(TranscodeBitrateField);
|
||||||
|
addField(TranscodeFormatField);
|
||||||
addField(PasswordField);
|
addField(PasswordField);
|
||||||
addField(PasswordConfirmField);
|
addField(PasswordConfirmField);
|
||||||
|
|
||||||
setValidator(BitrateField, createMandatoryValidator());
|
setValidator(TranscodeBitrateField, createMandatoryValidator());
|
||||||
setValidator(EncodingField, createMandatoryValidator());
|
setValidator(TranscodeFormatField, createMandatoryValidator());
|
||||||
|
|
||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Wt::WAbstractItemModel> bitrateModel() { return _bitrateModel; }
|
std::shared_ptr<Wt::WAbstractItemModel> transcodeBitrateModel() { return _transcodeBitrateModel; }
|
||||||
std::shared_ptr<Wt::WAbstractItemModel> encodingModel() { return _encodingModel; }
|
std::shared_ptr<Wt::WAbstractItemModel> transcodeFormatModel() { return _transcodeFormatModel; }
|
||||||
|
|
||||||
void loadData()
|
void loadData()
|
||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction {LmsApp->getDboSession()};
|
||||||
|
|
||||||
auto bitrate = getBitrateRow(LmsApp->getUser()->getAudioBitrate());
|
setValue(TranscodeEnableField, LmsApp->getUser()->getAudioTranscodeEnable());
|
||||||
if (bitrate)
|
if (!LmsApp->getUser()->getAudioTranscodeEnable())
|
||||||
setValue(BitrateField, bitrateString(*bitrate));
|
{
|
||||||
|
setReadOnly(TranscodeFormatField, true);
|
||||||
|
setReadOnly(TranscodeBitrateField, true);
|
||||||
|
}
|
||||||
|
|
||||||
auto encodingRow = getEncodingRow(LmsApp->getUser()->getAudioEncoding());
|
auto transcodeBitrateRow {getTranscodeBitrateRow(LmsApp->getUser()->getAudioTranscodeBitrate())};
|
||||||
if (encodingRow)
|
if (transcodeBitrateRow)
|
||||||
setValue(EncodingField, encodingString(*encodingRow));
|
setValue(TranscodeBitrateField, transcodeBitrateString(*transcodeBitrateRow));
|
||||||
|
|
||||||
|
auto transcodeFormatRow {getTranscodeFormatRow(LmsApp->getUser()->getAudioTranscodeFormat())};
|
||||||
|
if (transcodeFormatRow)
|
||||||
|
setValue(TranscodeFormatField, transcodeFormatString(*transcodeFormatRow));
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveData()
|
void saveData()
|
||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction {LmsApp->getDboSession()};
|
||||||
|
|
||||||
auto bitrateRow = getBitrateRow(Wt::asString(value(BitrateField)));
|
LmsApp->getUser().modify()->setAudioTranscodeEnable(Wt::asNumber(value(TranscodeEnableField)));
|
||||||
assert(bitrateRow);
|
|
||||||
LmsApp->getUser().modify()->setAudioBitrate(bitrate(*bitrateRow));
|
|
||||||
|
|
||||||
auto encodingRow = getEncodingRow(Wt::asString(value(EncodingField)));
|
auto transcodeBitrateRow {getTranscodeBitrateRow(Wt::asString(value(TranscodeBitrateField)))};
|
||||||
LmsApp->getUser().modify()->setAudioEncoding(encoding(*encodingRow));
|
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())
|
if (!valueText(PasswordField).empty())
|
||||||
{
|
|
||||||
Database::Handler::getPasswordService().updatePassword(LmsApp->getAuthUser(), valueText(PasswordField));
|
Database::Handler::getPasswordService().updatePassword(LmsApp->getAuthUser(), valueText(PasswordField));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool validateField(Field field)
|
bool validateField(Field field)
|
||||||
@@ -130,72 +140,72 @@ class SettingsModel : public Wt::WFormModel
|
|||||||
return (validation(field).state() == Wt::ValidationState::Valid);
|
return (validation(field).state() == Wt::ValidationState::Valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<int> getBitrateRow(Wt::WString value)
|
boost::optional<int> 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 i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<int> getBitrateRow(std::size_t value)
|
boost::optional<int> 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 i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t bitrate(int row)
|
std::size_t transcodeBitrate(int row)
|
||||||
{
|
{
|
||||||
return Wt::cpp17::any_cast<std::size_t>
|
return Wt::cpp17::any_cast<std::size_t>
|
||||||
(_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<Wt::WString>
|
return Wt::cpp17::any_cast<Wt::WString>
|
||||||
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::ItemDataRole::Display));
|
(_transcodeBitrateModel->data(_transcodeBitrateModel->index(row, 0), Wt::ItemDataRole::Display));
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<int> getEncodingRow(Wt::WString value)
|
boost::optional<int> 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 i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<int> getEncodingRow(Database::AudioEncoding enc)
|
boost::optional<int> 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 i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
Database::AudioEncoding encoding(int row)
|
Database::AudioFormat transcodeFormat(int row)
|
||||||
{
|
{
|
||||||
return Wt::cpp17::any_cast<Database::AudioEncoding>
|
return Wt::cpp17::any_cast<Database::AudioFormat>
|
||||||
(_encodingModel->data(_encodingModel->index(row, 0), Wt::ItemDataRole::User));
|
(_transcodeFormatModel->data(_transcodeFormatModel->index(row, 0), Wt::ItemDataRole::User));
|
||||||
}
|
}
|
||||||
|
|
||||||
Wt::WString encodingString(int row)
|
Wt::WString transcodeFormatString(int row)
|
||||||
{
|
{
|
||||||
return Wt::cpp17::any_cast<Wt::WString>
|
return Wt::cpp17::any_cast<Wt::WString>
|
||||||
(_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()
|
void initializeModels()
|
||||||
{
|
{
|
||||||
_bitrateModel = std::make_shared<Wt::WStringListModel>();
|
_transcodeBitrateModel = std::make_shared<Wt::WStringListModel>();
|
||||||
|
|
||||||
std::size_t maxAudioBitrate;
|
Database::Bitrate maxAudioBitrate;
|
||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction {LmsApp->getDboSession()};
|
||||||
maxAudioBitrate = LmsApp->getUser()->getMaxAudioBitrate();
|
maxAudioBitrate = LmsApp->getUser()->getMaxAudioTranscodeBitrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t id = 0;
|
std::size_t id = 0;
|
||||||
for (std::size_t bitrate : Database::User::audioBitrates)
|
for (Database::Bitrate bitrate : Database::User::audioTranscodeAllowedBitrates)
|
||||||
{
|
{
|
||||||
if (bitrate > maxAudioBitrate)
|
if (bitrate > maxAudioBitrate)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
_bitrateModel->addString( Wt::WString::fromUTF8(std::to_string(bitrate / 1000)) );
|
_transcodeBitrateModel->addString( Wt::WString::fromUTF8(std::to_string(bitrate / 1000)) );
|
||||||
_bitrateModel->setData( id, 0, bitrate, Wt::ItemDataRole::User);
|
_transcodeBitrateModel->setData( id, 0, bitrate, Wt::ItemDataRole::User);
|
||||||
id++;
|
id++;
|
||||||
}
|
}
|
||||||
|
|
||||||
_encodingModel = std::make_shared<Wt::WStringListModel>();
|
_transcodeFormatModel = std::make_shared<Wt::WStringListModel>();
|
||||||
|
|
||||||
_encodingModel->addString(Wt::WString::tr("Lms.Settings.encoding.auto"));
|
_transcodeFormatModel->addString(Wt::WString::tr("Lms.Settings.transcoding.mp3"));
|
||||||
_encodingModel->setData(0, 0, Database::AudioEncoding::AUTO, Wt::ItemDataRole::User);
|
_transcodeFormatModel->setData(0, 0, Database::AudioFormat::MP3, Wt::ItemDataRole::User);
|
||||||
|
|
||||||
_encodingModel->addString(Wt::WString::tr("Lms.Settings.encoding.mp3"));
|
_transcodeFormatModel->addString(Wt::WString::tr("Lms.Settings.transcoding.ogg_opus"));
|
||||||
_encodingModel->setData(1, 0, Database::AudioEncoding::MP3, Wt::ItemDataRole::User);
|
_transcodeFormatModel->setData(1, 0, Database::AudioFormat::OGG_OPUS, Wt::ItemDataRole::User);
|
||||||
|
|
||||||
_encodingModel->addString(Wt::WString::tr("Lms.Settings.encoding.ogg_opus"));
|
_transcodeFormatModel->addString(Wt::WString::tr("Lms.Settings.transcoding.ogg_vorbis"));
|
||||||
_encodingModel->setData(2, 0, Database::AudioEncoding::OGG_OPUS, Wt::ItemDataRole::User);
|
_transcodeFormatModel->setData(2, 0, Database::AudioFormat::OGG_VORBIS, Wt::ItemDataRole::User);
|
||||||
|
|
||||||
_encodingModel->addString(Wt::WString::tr("Lms.Settings.encoding.ogg_vorbis"));
|
_transcodeFormatModel->addString(Wt::WString::tr("Lms.Settings.transcoding.webm_vorbis"));
|
||||||
_encodingModel->setData(3, 0, Database::AudioEncoding::OGG_VORBIS, Wt::ItemDataRole::User);
|
_transcodeFormatModel->setData(3, 0, Database::AudioFormat::WEBM_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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Wt::WStringListModel> _bitrateModel;
|
std::shared_ptr<Wt::WStringListModel> _transcodeBitrateModel;
|
||||||
std::shared_ptr<Wt::WStringListModel> _encodingModel;
|
std::shared_ptr<Wt::WStringListModel> _transcodeFormatModel;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Wt::WFormModel::Field SettingsModel::BitrateField = "bitrate";
|
const Wt::WFormModel::Field SettingsModel::TranscodeEnableField = "transcoding-enable";
|
||||||
const Wt::WFormModel::Field SettingsModel::EncodingField = "encoding";
|
const Wt::WFormModel::Field SettingsModel::TranscodeBitrateField = "transcoding-bitrate";
|
||||||
const Wt::WFormModel::Field SettingsModel::PasswordField = "password";
|
const Wt::WFormModel::Field SettingsModel::TranscodeFormatField = "transcoding-format";
|
||||||
const Wt::WFormModel::Field SettingsModel::PasswordConfirmField = "password-confirm";
|
const Wt::WFormModel::Field SettingsModel::PasswordField = "password";
|
||||||
|
const Wt::WFormModel::Field SettingsModel::PasswordConfirmField = "password-confirm";
|
||||||
|
|
||||||
SettingsView::SettingsView()
|
SettingsView::SettingsView()
|
||||||
{
|
{
|
||||||
@@ -268,39 +276,53 @@ SettingsView::refreshView()
|
|||||||
|
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
auto t = addNew<Wt::WTemplateFormView>(Wt::WString::tr("Lms.Settings.template"));
|
auto t {addNew<Wt::WTemplateFormView>(Wt::WString::tr("Lms.Settings.template"))};
|
||||||
|
|
||||||
auto model = std::make_shared<SettingsModel>();
|
auto model {std::make_shared<SettingsModel>()};
|
||||||
|
|
||||||
// Password
|
// Password
|
||||||
auto password = std::make_unique<Wt::WLineEdit>();
|
auto password {std::make_unique<Wt::WLineEdit>()};
|
||||||
password->setEchoMode(Wt::EchoMode::Password);
|
password->setEchoMode(Wt::EchoMode::Password);
|
||||||
t->setFormWidget(SettingsModel::PasswordField, std::move(password));
|
t->setFormWidget(SettingsModel::PasswordField, std::move(password));
|
||||||
|
|
||||||
// Password confirm
|
// Password confirm
|
||||||
auto passwordConfirm = std::make_unique<Wt::WLineEdit>();
|
auto passwordConfirm {std::make_unique<Wt::WLineEdit>()};
|
||||||
passwordConfirm->setEchoMode(Wt::EchoMode::Password);
|
passwordConfirm->setEchoMode(Wt::EchoMode::Password);
|
||||||
t->setFormWidget(SettingsModel::PasswordConfirmField, std::move(passwordConfirm));
|
t->setFormWidget(SettingsModel::PasswordConfirmField, std::move(passwordConfirm));
|
||||||
|
|
||||||
// Bitrate
|
// Transcoding
|
||||||
auto bitrate = std::make_unique<Wt::WComboBox>();
|
auto transcode {std::make_unique<Wt::WCheckBox>()};
|
||||||
bitrate->setModel(model->bitrateModel());
|
auto* transcodeRaw {transcode.get()};
|
||||||
t->setFormWidget(SettingsModel::BitrateField, std::move(bitrate));
|
t->setFormWidget(SettingsModel::TranscodeEnableField, std::move(transcode));
|
||||||
|
|
||||||
// Encoding
|
// Format
|
||||||
auto encoding = std::make_unique<Wt::WComboBox>();
|
auto transcodeFormat {std::make_unique<Wt::WComboBox>()};
|
||||||
encoding->setModel(model->encodingModel());
|
transcodeFormat->setModel(model->transcodeFormatModel());
|
||||||
t->setFormWidget(SettingsModel::EncodingField, std::move(encoding));
|
t->setFormWidget(SettingsModel::TranscodeFormatField, std::move(transcodeFormat));
|
||||||
|
|
||||||
|
// Bitrate
|
||||||
|
auto transcodeBitrate {std::make_unique<Wt::WComboBox>()};
|
||||||
|
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
|
// Buttons
|
||||||
Wt::WPushButton *saveBtn = t->bindWidget("apply-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.apply")));
|
Wt::WPushButton *saveBtn {t->bindWidget("apply-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.apply")))};
|
||||||
Wt::WPushButton *discardBtn = t->bindWidget("discard-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.discard")));
|
Wt::WPushButton *discardBtn {t->bindWidget("discard-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.discard")))};
|
||||||
|
|
||||||
saveBtn->clicked().connect(std::bind([=] ()
|
saveBtn->clicked().connect(std::bind([=] ()
|
||||||
{
|
{
|
||||||
|
|
||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction {LmsApp->getDboSession()};
|
||||||
|
|
||||||
if (LmsApp->getUser()->isDemo())
|
if (LmsApp->getUser()->isDemo())
|
||||||
{
|
{
|
||||||
|
|||||||
+22
-22
@@ -45,7 +45,7 @@ class UserModel : public Wt::WFormModel
|
|||||||
public:
|
public:
|
||||||
static const Field LoginField;
|
static const Field LoginField;
|
||||||
static const Field PasswordField;
|
static const Field PasswordField;
|
||||||
static const Field BitrateLimitField;
|
static const Field AudioTranscodeBitrateLimitField;
|
||||||
static const Field DemoField;
|
static const Field DemoField;
|
||||||
|
|
||||||
UserModel(boost::optional<Database::IdType> userId)
|
UserModel(boost::optional<Database::IdType> userId)
|
||||||
@@ -59,7 +59,7 @@ class UserModel : public Wt::WFormModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
addField(PasswordField);
|
addField(PasswordField);
|
||||||
addField(BitrateLimitField);
|
addField(AudioTranscodeBitrateLimitField);
|
||||||
addField(DemoField);
|
addField(DemoField);
|
||||||
|
|
||||||
if (!_userId)
|
if (!_userId)
|
||||||
@@ -76,7 +76,7 @@ class UserModel : public Wt::WFormModel
|
|||||||
if (!_userId)
|
if (!_userId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction {LmsApp->getDboSession()};
|
||||||
|
|
||||||
auto authUser = LmsApp->getDb().getUserDatabase().findWithId( std::to_string(*_userId) );
|
auto authUser = LmsApp->getDb().getUserDatabase().findWithId( std::to_string(*_userId) );
|
||||||
auto user = LmsApp->getDb().getUser(authUser);
|
auto user = LmsApp->getDb().getUser(authUser);
|
||||||
@@ -84,14 +84,14 @@ class UserModel : public Wt::WFormModel
|
|||||||
if (user == LmsApp->getUser())
|
if (user == LmsApp->getUser())
|
||||||
throw LmsException("Cannot edit ourselves");
|
throw LmsException("Cannot edit ourselves");
|
||||||
|
|
||||||
auto bitrate = getBitrateLimitRow(user->getMaxAudioBitrate());
|
auto bitrate {getTranscodeBitrateLimitRow(user->getMaxAudioTranscodeBitrate())};
|
||||||
if (bitrate)
|
if (bitrate)
|
||||||
setValue(BitrateLimitField, bitrateLimitString(*bitrate));
|
setValue(AudioTranscodeBitrateLimitField, transcodeBitrateLimitString(*bitrate));
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveData()
|
void saveData()
|
||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction {LmsApp->getDboSession()};
|
||||||
|
|
||||||
if (_userId)
|
if (_userId)
|
||||||
{
|
{
|
||||||
@@ -103,9 +103,9 @@ class UserModel : public Wt::WFormModel
|
|||||||
if (!valueText(PasswordField).empty())
|
if (!valueText(PasswordField).empty())
|
||||||
Database::Handler::getPasswordService().updatePassword(authUser, valueText(PasswordField));
|
Database::Handler::getPasswordService().updatePassword(authUser, valueText(PasswordField));
|
||||||
|
|
||||||
auto bitrateLimitRow = getBitrateLimitRow(Wt::asString(value(BitrateLimitField)));
|
auto transcodeBitrateLimitRow = getTranscodeBitrateLimitRow(Wt::asString(value(AudioTranscodeBitrateLimitField)));
|
||||||
user.modify()->setMaxAudioBitrate(bitrateLimit(*bitrateLimitRow));
|
user.modify()->setMaxAudioTranscodeBitrate(transcodeBitrateLimit(*transcodeBitrateLimitRow));
|
||||||
LMS_LOG(UI, DEBUG) << "Max audio bitrate set to " << bitrateLimit(*bitrateLimitRow);
|
LMS_LOG(UI, DEBUG) << "Max audio bitrate set to " << transcodeBitrateLimit(*transcodeBitrateLimitRow);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -117,8 +117,8 @@ class UserModel : public Wt::WFormModel
|
|||||||
authUser.setIdentity(Wt::Auth::Identity::LoginName, valueText(LoginField));
|
authUser.setIdentity(Wt::Auth::Identity::LoginName, valueText(LoginField));
|
||||||
Database::Handler::getPasswordService().updatePassword(authUser, valueText(PasswordField));
|
Database::Handler::getPasswordService().updatePassword(authUser, valueText(PasswordField));
|
||||||
|
|
||||||
auto bitrateLimitRow = getBitrateLimitRow(Wt::asString(value(BitrateLimitField)));
|
auto transcodeBitrateLimitRow = getTranscodeBitrateLimitRow(Wt::asString(value(AudioTranscodeBitrateLimitField)));
|
||||||
user.modify()->setMaxAudioBitrate(bitrateLimit(*bitrateLimitRow));
|
user.modify()->setMaxAudioTranscodeBitrate(transcodeBitrateLimit(*transcodeBitrateLimitRow));
|
||||||
|
|
||||||
if (Wt::asNumber(value(DemoField)))
|
if (Wt::asNumber(value(DemoField)))
|
||||||
user.modify()->setType(Database::User::Type::DEMO);
|
user.modify()->setType(Database::User::Type::DEMO);
|
||||||
@@ -168,7 +168,7 @@ class UserModel : public Wt::WFormModel
|
|||||||
}
|
}
|
||||||
else if (field == DemoField)
|
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()))
|
if (Wt::asNumber(value(DemoField)) && Database::User::getDemo(LmsApp->getDboSession()))
|
||||||
error = Wt::WString::tr("Lms.Admin.User.demo-account-already-exists");
|
error = Wt::WString::tr("Lms.Admin.User.demo-account-already-exists");
|
||||||
@@ -182,35 +182,35 @@ class UserModel : public Wt::WFormModel
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<int> getBitrateLimitRow(Wt::WString value)
|
boost::optional<int> getTranscodeBitrateLimitRow(Wt::WString value)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _bitrateModel->rowCount(); ++i)
|
for (int i = 0; i < _bitrateModel->rowCount(); ++i)
|
||||||
{
|
{
|
||||||
if (bitrateLimitString(i) == value)
|
if (transcodeBitrateLimitString(i) == value)
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<int> getBitrateLimitRow(std::size_t value)
|
boost::optional<int> getTranscodeBitrateLimitRow(std::size_t value)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _bitrateModel->rowCount(); ++i)
|
for (int i = 0; i < _bitrateModel->rowCount(); ++i)
|
||||||
{
|
{
|
||||||
if (bitrateLimit(i) == value)
|
if (transcodeBitrateLimit(i) == value)
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t bitrateLimit(int row)
|
std::size_t transcodeBitrateLimit(int row)
|
||||||
{
|
{
|
||||||
return Wt::cpp17::any_cast<std::size_t>
|
return Wt::cpp17::any_cast<std::size_t>
|
||||||
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::ItemDataRole::User));
|
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::ItemDataRole::User));
|
||||||
}
|
}
|
||||||
|
|
||||||
Wt::WString bitrateLimitString(int row)
|
Wt::WString transcodeBitrateLimitString(int row)
|
||||||
{
|
{
|
||||||
return Wt::cpp17::any_cast<Wt::WString>
|
return Wt::cpp17::any_cast<Wt::WString>
|
||||||
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::ItemDataRole::Display));
|
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::ItemDataRole::Display));
|
||||||
@@ -225,7 +225,7 @@ class UserModel : public Wt::WFormModel
|
|||||||
_bitrateModel = std::make_shared<Wt::WStringListModel>();
|
_bitrateModel = std::make_shared<Wt::WStringListModel>();
|
||||||
|
|
||||||
std::size_t id = 0;
|
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->addString( Wt::WString::fromUTF8(std::to_string(bitrate / 1000)) );
|
||||||
_bitrateModel->setData( id++, 0, bitrate, Wt::ItemDataRole::User);
|
_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::LoginField = "login";
|
||||||
const Wt::WFormModel::Field UserModel::PasswordField = "password";
|
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";
|
const Wt::WFormModel::Field UserModel::DemoField = "demo";
|
||||||
|
|
||||||
UserView::UserView()
|
UserView::UserView()
|
||||||
@@ -293,10 +293,10 @@ UserView::refreshView()
|
|||||||
passwordEdit->setEchoMode(Wt::EchoMode::Password);
|
passwordEdit->setEchoMode(Wt::EchoMode::Password);
|
||||||
t->setFormWidget(UserModel::PasswordField, std::move(passwordEdit));
|
t->setFormWidget(UserModel::PasswordField, std::move(passwordEdit));
|
||||||
|
|
||||||
// Bitrate
|
// Transcode bitrate limit
|
||||||
auto bitrate = std::make_unique<Wt::WComboBox>();
|
auto bitrate = std::make_unique<Wt::WComboBox>();
|
||||||
bitrate->setModel(model->bitrateModel());
|
bitrate->setModel(model->bitrateModel());
|
||||||
t->setFormWidget(UserModel::BitrateLimitField, std::move(bitrate));
|
t->setFormWidget(UserModel::AudioTranscodeBitrateLimitField, std::move(bitrate));
|
||||||
|
|
||||||
// Demo account
|
// Demo account
|
||||||
t->setFormWidget(UserModel::DemoField, std::make_unique<Wt::WCheckBox>());
|
t->setFormWidget(UserModel::DemoField, std::make_unique<Wt::WCheckBox>());
|
||||||
|
|||||||
@@ -77,12 +77,12 @@ _filters(filters)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Database::Track::pointer>
|
std::vector<Database::Track::pointer>
|
||||||
Tracks::getTracks(int offset, int size, bool& moreResults)
|
Tracks::getTracks(boost::optional<std::size_t> offset, boost::optional<std::size_t> size, bool& moreResults)
|
||||||
{
|
{
|
||||||
auto searchKeywords = splitString(_search->text().toUTF8(), " ");
|
auto searchKeywords {splitString(_search->text().toUTF8(), " ")};
|
||||||
auto clusterIds = _filters->getClusterIds();
|
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);
|
return Track::getByFilter(LmsApp->getDboSession(), clusterIds, searchKeywords, offset, size, moreResults);
|
||||||
}
|
}
|
||||||
@@ -91,7 +91,7 @@ std::vector<Database::Track::pointer>
|
|||||||
Tracks::getTracks()
|
Tracks::getTracks()
|
||||||
{
|
{
|
||||||
bool moreResults;
|
bool moreResults;
|
||||||
return getTracks(-1, -1, moreResults);
|
return getTracks({}, {}, moreResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -104,15 +104,15 @@ Tracks::refresh()
|
|||||||
void
|
void
|
||||||
Tracks::addSome()
|
Tracks::addSome()
|
||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction {LmsApp->getDboSession()};
|
||||||
|
|
||||||
bool moreResults;
|
bool moreResults;
|
||||||
auto tracks = getTracks(_tracksContainer->count(), 20, moreResults);
|
auto tracks {getTracks(_tracksContainer->count(), 20, moreResults)};
|
||||||
|
|
||||||
for (auto track : tracks)
|
for (auto track : tracks)
|
||||||
{
|
{
|
||||||
auto trackId = track.id();
|
auto trackId {track.id()};
|
||||||
Wt::WTemplate* entry = _tracksContainer->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Tracks.template.entry"));
|
Wt::WTemplate* entry {_tracksContainer->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Tracks.template.entry"))};
|
||||||
|
|
||||||
entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::TextFormat::Plain);
|
entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::TextFormat::Plain);
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class Tracks : public Wt::WTemplate
|
|||||||
void refresh();
|
void refresh();
|
||||||
void addSome();
|
void addSome();
|
||||||
|
|
||||||
std::vector<Database::Track::pointer> getTracks(int offset, int size, bool& moreResults);
|
std::vector<Database::Track::pointer> getTracks(boost::optional<std::size_t> offset, boost::optional<std::size_t> size, bool& moreResults);
|
||||||
std::vector<Database::Track::pointer> getTracks();
|
std::vector<Database::Track::pointer> getTracks();
|
||||||
|
|
||||||
Wt::WContainerWidget* _tracksContainer;
|
Wt::WContainerWidget* _tracksContainer;
|
||||||
|
|||||||
@@ -49,8 +49,6 @@ AudioResource::handleRequest(const Wt::Http::Request& request,
|
|||||||
{
|
{
|
||||||
std::shared_ptr<Av::Transcoder> transcoder;
|
std::shared_ptr<Av::Transcoder> transcoder;
|
||||||
|
|
||||||
LMS_LOG(UI, DEBUG) << "Handling new request...";
|
|
||||||
|
|
||||||
// First, see if this request is for a continuation
|
// First, see if this request is for a continuation
|
||||||
Wt::Http::ResponseContinuation *continuation = request.continuation();
|
Wt::Http::ResponseContinuation *continuation = request.continuation();
|
||||||
if (continuation)
|
if (continuation)
|
||||||
@@ -62,8 +60,9 @@ AudioResource::handleRequest(const Wt::Http::Request& request,
|
|||||||
{
|
{
|
||||||
Database::IdType trackId;
|
Database::IdType trackId;
|
||||||
Av::TranscodeParameters parameters {};
|
Av::TranscodeParameters parameters {};
|
||||||
|
parameters.stripMetadata = true;
|
||||||
|
|
||||||
LMS_LOG(UI, DEBUG) << "No continuation yet";
|
LMS_LOG(UI, DEBUG) << "First request: creating transcoder";
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto trackIdStr = request.getParameter("trackid");
|
auto trackIdStr = request.getParameter("trackid");
|
||||||
@@ -75,8 +74,7 @@ AudioResource::handleRequest(const Wt::Http::Request& request,
|
|||||||
trackId = std::stol(*request.getParameter("trackid"));
|
trackId = std::stol(*request.getParameter("trackid"));
|
||||||
|
|
||||||
auto offsetStr = request.getParameter("offset");
|
auto offsetStr = request.getParameter("offset");
|
||||||
if (offsetStr)
|
parameters.offset = std::chrono::seconds(offsetStr ? std::stol(*offsetStr) : 0);
|
||||||
parameters.offset = std::chrono::seconds(std::stol(*offsetStr));
|
|
||||||
}
|
}
|
||||||
catch (std::exception &e)
|
catch (std::exception &e)
|
||||||
{
|
{
|
||||||
@@ -98,34 +96,35 @@ AudioResource::handleRequest(const Wt::Http::Request& request,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters.bitrate = LmsApp->getUser()->getAudioBitrate();
|
if (LmsApp->getUser()->getAudioTranscodeEnable())
|
||||||
|
|
||||||
switch (LmsApp->getUser()->getAudioEncoding())
|
|
||||||
{
|
{
|
||||||
case Database::AudioEncoding::AUTO:
|
parameters.bitrate = LmsApp->getUser()->getAudioTranscodeBitrate();
|
||||||
case Database::AudioEncoding::MP3:
|
|
||||||
parameters.encoding = Av::Encoding::MP3;
|
switch (LmsApp->getUser()->getAudioTranscodeFormat())
|
||||||
break;
|
{
|
||||||
case Database::AudioEncoding::OGG_OPUS:
|
case Database::AudioFormat::MP3:
|
||||||
parameters.encoding = Av::Encoding::OGG_OPUS;
|
parameters.encoding = Av::Encoding::MP3;
|
||||||
break;
|
break;
|
||||||
case Database::AudioEncoding::OGG_VORBIS:
|
case Database::AudioFormat::OGG_OPUS:
|
||||||
parameters.encoding = Av::Encoding::OGG_VORBIS;
|
parameters.encoding = Av::Encoding::OGG_OPUS;
|
||||||
break;
|
break;
|
||||||
case Database::AudioEncoding::WEBM_VORBIS:
|
case Database::AudioFormat::OGG_VORBIS:
|
||||||
parameters.encoding = Av::Encoding::WEBM_VORBIS;
|
parameters.encoding = Av::Encoding::OGG_VORBIS;
|
||||||
break;
|
break;
|
||||||
default:
|
case Database::AudioFormat::WEBM_VORBIS:
|
||||||
parameters.encoding = Av::Encoding::MP3;
|
parameters.encoding = Av::Encoding::WEBM_VORBIS;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
parameters.encoding = Av::Encoding::OGG_OPUS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
parameters.bitrate = 0;
|
||||||
|
|
||||||
transcoder = std::make_shared<Av::Transcoder>(track->getPath(), parameters);
|
transcoder = std::make_shared<Av::Transcoder>(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())
|
if (!transcoder->start())
|
||||||
{
|
{
|
||||||
@@ -134,6 +133,11 @@ AudioResource::handleRequest(const Wt::Http::Request& request,
|
|||||||
}
|
}
|
||||||
|
|
||||||
LMS_LOG(UI, DEBUG) << "Transcoder started";
|
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())
|
if (!transcoder->isComplete())
|
||||||
@@ -144,7 +148,7 @@ AudioResource::handleRequest(const Wt::Http::Request& request,
|
|||||||
transcoder->process(data, _chunkSize);
|
transcoder->process(data, _chunkSize);
|
||||||
|
|
||||||
response.out().write(reinterpret_cast<char*>(&data[0]), data.size());
|
response.out().write(reinterpret_cast<char*>(&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())
|
if (!response.out())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -482,6 +482,10 @@ testSingleTrackSingleCluster(Wt::Dbo::Session& session)
|
|||||||
auto tracks {Track::getByFilter(session, {clusterId})};
|
auto tracks {Track::getByFilter(session, {clusterId})};
|
||||||
CHECK(tracks.size() == 1);
|
CHECK(tracks.size() == 1);
|
||||||
CHECK(tracks.front().id() == trackId);
|
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})};
|
auto releases {Release::getByFilter(session, {clusterId})};
|
||||||
CHECK(releases.size() == 1);
|
CHECK(releases.size() == 1);
|
||||||
CHECK(releases.front().id() == releaseId);
|
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.size() == 1);
|
||||||
CHECK(artists.front().id() == artistId);
|
CHECK(artists.front().id() == artistId);
|
||||||
|
|
||||||
|
artists = Artist::getByFilter(session, {});
|
||||||
|
CHECK(artists.size() == 1);
|
||||||
|
CHECK(artists.front().id() == artistId);
|
||||||
|
|
||||||
artists = Artist::getByFilter(session, {cluster2Id});
|
artists = Artist::getByFilter(session, {cluster2Id});
|
||||||
CHECK(artists.empty());
|
CHECK(artists.empty());
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user