Added a new transcoding mode: transcode if and only if the format is not handled by the browser. Made the subsonic API configurable again in conf file. Moved the subsonic's artist list mode in the per-user settings page. fixes #46
This commit is contained in:
+177
-63
@@ -31,11 +31,13 @@
|
||||
#include "common/ValueStringModel.hpp"
|
||||
|
||||
#include "auth/IPasswordService.hpp"
|
||||
#include "utils/IConfig.hpp"
|
||||
#include "utils/Logger.hpp"
|
||||
#include "utils/Service.hpp"
|
||||
|
||||
#include "LmsApplication.hpp"
|
||||
#include "LmsTheme.hpp"
|
||||
#include "MediaPlayer.hpp"
|
||||
|
||||
namespace UserInterface {
|
||||
|
||||
@@ -46,22 +48,31 @@ class SettingsModel : public Wt::WFormModel
|
||||
public:
|
||||
// Associate each field with a unique string literal.
|
||||
static inline const Field DarkModeField {"dark-mode"};
|
||||
static inline const Field TranscodeEnableField {"transcoding-enable"};
|
||||
static inline const Field TranscodeFormatField {"transcoding-format"};
|
||||
static inline const Field TranscodeBitrateField {"transcoding-bitrate"};
|
||||
static inline const Field TranscodeModeField {"transcode-mode"};
|
||||
static inline const Field TranscodeFormatField {"transcode-format"};
|
||||
static inline const Field TranscodeBitrateField {"transcode-bitrate"};
|
||||
static inline const Field SubsonicArtistListModeField {"subsonic-artist-list-mode"};
|
||||
static inline const Field SubsonicTranscodeEnableField {"subsonic-transcode-enable"};
|
||||
static inline const Field SubsonicTranscodeFormatField {"subsonic-transcode-format"};
|
||||
static inline const Field SubsonicTranscodeBitrateField {"subsonic-transcode-bitrate"};
|
||||
static inline const Field PasswordOldField {"password-old"};
|
||||
static inline const Field PasswordField {"password"};
|
||||
static inline const Field PasswordConfirmField {"password-confirm"};
|
||||
|
||||
using TranscodeModeModel = ValueStringModel<MediaPlayer::TranscodeMode>;
|
||||
|
||||
SettingsModel(bool withOldPassword)
|
||||
: _withOldPassword {withOldPassword}
|
||||
{
|
||||
initializeModels();
|
||||
|
||||
addField(DarkModeField);
|
||||
addField(TranscodeEnableField);
|
||||
addField(TranscodeModeField);
|
||||
addField(TranscodeBitrateField);
|
||||
addField(TranscodeFormatField);
|
||||
addField(SubsonicTranscodeEnableField);
|
||||
addField(SubsonicTranscodeBitrateField);
|
||||
addField(SubsonicTranscodeFormatField);
|
||||
|
||||
if (_withOldPassword)
|
||||
addField(PasswordOldField);
|
||||
@@ -69,14 +80,19 @@ class SettingsModel : public Wt::WFormModel
|
||||
addField(PasswordField);
|
||||
addField(PasswordConfirmField);
|
||||
|
||||
setValidator(TranscodeModeField, createMandatoryValidator());
|
||||
setValidator(TranscodeBitrateField, createMandatoryValidator());
|
||||
setValidator(TranscodeFormatField, createMandatoryValidator());
|
||||
setValidator(SubsonicTranscodeBitrateField, createMandatoryValidator());
|
||||
setValidator(SubsonicTranscodeFormatField, createMandatoryValidator());
|
||||
|
||||
loadData();
|
||||
}
|
||||
|
||||
std::shared_ptr<Wt::WAbstractItemModel> transcodeBitrateModel() { return _transcodeBitrateModel; }
|
||||
std::shared_ptr<Wt::WAbstractItemModel> transcodeFormatModel() { return _transcodeFormatModel; }
|
||||
std::shared_ptr<TranscodeModeModel> getTranscodeModeModel() { return _transcodeModeModel; }
|
||||
std::shared_ptr<Wt::WAbstractItemModel> getTranscodeBitrateModel() { return _transcodeBitrateModel; }
|
||||
std::shared_ptr<Wt::WAbstractItemModel> getTranscodeFormatModel() { return _transcodeFormatModel; }
|
||||
std::shared_ptr<Wt::WAbstractItemModel> getSubsonicArtistListModeModel() { return _subsonicArtistListModeModel; }
|
||||
|
||||
void saveData()
|
||||
{
|
||||
@@ -97,43 +113,89 @@ class SettingsModel : public Wt::WFormModel
|
||||
user.modify()->setUITheme(newTheme);
|
||||
}
|
||||
|
||||
user.modify()->setAudioTranscodeEnable(Wt::asNumber(value(TranscodeEnableField)));
|
||||
{
|
||||
MediaPlayer::Settings settings;
|
||||
|
||||
auto transcodeBitrateRow {_transcodeBitrateModel->getRowFromString(valueText(TranscodeBitrateField))};
|
||||
if (transcodeBitrateRow)
|
||||
user.modify()->setAudioTranscodeBitrate(_transcodeBitrateModel->getValue(*transcodeBitrateRow));
|
||||
auto transcodeModeRow {_transcodeModeModel->getRowFromString(valueText(TranscodeModeField))};
|
||||
if (transcodeModeRow)
|
||||
settings.mode = _transcodeModeModel->getValue(*transcodeModeRow);
|
||||
|
||||
auto transcodeFormatRow {_transcodeFormatModel->getRowFromString(valueText(TranscodeFormatField))};
|
||||
if (transcodeFormatRow)
|
||||
user.modify()->setAudioTranscodeFormat(_transcodeFormatModel->getValue(*transcodeFormatRow));
|
||||
auto transcodeFormatRow {_transcodeFormatModel->getRowFromString(valueText(TranscodeFormatField))};
|
||||
if (transcodeFormatRow)
|
||||
settings.format = _transcodeFormatModel->getValue(*transcodeFormatRow);
|
||||
|
||||
auto transcodeBitrateRow {_transcodeBitrateModel->getRowFromString(valueText(TranscodeBitrateField))};
|
||||
if (transcodeBitrateRow)
|
||||
settings.bitrate = _transcodeBitrateModel->getValue(*transcodeBitrateRow);
|
||||
|
||||
LmsApp->getMediaPlayer()->setSettings(settings);
|
||||
}
|
||||
|
||||
{
|
||||
user.modify()->setSubsonicTranscodeEnable(Wt::asNumber(value(SubsonicTranscodeEnableField)));
|
||||
|
||||
auto subsonicTranscodeBitrateRow {_transcodeBitrateModel->getRowFromString(valueText(SubsonicTranscodeBitrateField))};
|
||||
if (subsonicTranscodeBitrateRow)
|
||||
user.modify()->setSubsonicTranscodeBitrate(_transcodeBitrateModel->getValue(*subsonicTranscodeBitrateRow));
|
||||
|
||||
auto subsonicTranscodeFormatRow {_transcodeFormatModel->getRowFromString(valueText(SubsonicTranscodeFormatField))};
|
||||
if (subsonicTranscodeFormatRow)
|
||||
user.modify()->setSubsonicTranscodeFormat(_transcodeFormatModel->getValue(*subsonicTranscodeFormatRow));
|
||||
}
|
||||
|
||||
if (!valueText(PasswordField).empty())
|
||||
{
|
||||
user.modify()->setPasswordHash(passwordHash);
|
||||
user.modify()->clearAuthTokens();
|
||||
}
|
||||
|
||||
auto subsonicArtistListModeRow {_subsonicArtistListModeModel->getRowFromString(valueText(SubsonicArtistListModeField))};
|
||||
if (subsonicArtistListModeRow)
|
||||
user.modify()->setSubsonicArtistListMode(_subsonicArtistListModeModel->getValue(*subsonicArtistListModeRow));
|
||||
}
|
||||
|
||||
void loadData()
|
||||
{
|
||||
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||
|
||||
setValue(DarkModeField, LmsApp->getUser()->getUITheme() == User::UITheme::Dark);
|
||||
User::pointer user {LmsApp->getUser()};
|
||||
|
||||
setValue(DarkModeField, user->getUITheme() == User::UITheme::Dark);
|
||||
|
||||
setValue(TranscodeEnableField, LmsApp->getUser()->getAudioTranscodeEnable());
|
||||
if (!LmsApp->getUser()->getAudioTranscodeEnable())
|
||||
{
|
||||
setReadOnly(TranscodeFormatField, true);
|
||||
setReadOnly(TranscodeBitrateField, true);
|
||||
const auto& settings {*LmsApp->getMediaPlayer()->getSettings()};
|
||||
|
||||
auto transcodeModeRow {_transcodeModeModel->getRowFromValue(settings.mode)};
|
||||
if (transcodeModeRow)
|
||||
setValue(TranscodeModeField, _transcodeModeModel->getString(*transcodeModeRow));
|
||||
|
||||
auto transcodeFormatRow {_transcodeFormatModel->getRowFromValue(settings.format)};
|
||||
if (transcodeFormatRow)
|
||||
setValue(TranscodeFormatField, _transcodeFormatModel->getString(*transcodeFormatRow));
|
||||
|
||||
auto transcodeBitrateRow {_transcodeBitrateModel->getRowFromValue(settings.bitrate)};
|
||||
if (transcodeBitrateRow)
|
||||
setValue(TranscodeBitrateField, _transcodeBitrateModel->getString(*transcodeBitrateRow));
|
||||
}
|
||||
|
||||
auto transcodeBitrateRow {_transcodeBitrateModel->getRowFromValue(LmsApp->getUser()->getAudioTranscodeBitrate())};
|
||||
if (transcodeBitrateRow)
|
||||
setValue(TranscodeBitrateField, _transcodeBitrateModel->getString(*transcodeBitrateRow));
|
||||
setValue(SubsonicTranscodeEnableField, LmsApp->getUser()->getSubsonicTranscodeEnable());
|
||||
if (!LmsApp->getUser()->getSubsonicTranscodeEnable())
|
||||
{
|
||||
setReadOnly(SubsonicTranscodeFormatField, true);
|
||||
setReadOnly(SubsonicTranscodeBitrateField, true);
|
||||
}
|
||||
|
||||
auto transcodeFormatRow {_transcodeFormatModel->getRowFromValue(LmsApp->getUser()->getAudioTranscodeFormat())};
|
||||
if (transcodeFormatRow)
|
||||
setValue(TranscodeFormatField, _transcodeFormatModel->getString(*transcodeFormatRow));
|
||||
auto subsonicTranscodeBitrateRow {_transcodeBitrateModel->getRowFromValue(user->getSubsonicTranscodeBitrate())};
|
||||
if (subsonicTranscodeBitrateRow)
|
||||
setValue(SubsonicTranscodeBitrateField, _transcodeBitrateModel->getString(*subsonicTranscodeBitrateRow));
|
||||
|
||||
auto subsonicTranscodeFormatRow {_transcodeFormatModel->getRowFromValue(user->getSubsonicTranscodeFormat())};
|
||||
if (subsonicTranscodeFormatRow)
|
||||
setValue(SubsonicTranscodeFormatField, _transcodeFormatModel->getString(*subsonicTranscodeFormatRow));
|
||||
|
||||
auto subsonicArtistListModeRow {_subsonicArtistListModeModel->getRowFromValue(user->getSubsonicArtistListMode())};
|
||||
if (subsonicArtistListModeRow)
|
||||
setValue(SubsonicArtistListModeField, _subsonicArtistListModeModel->getString(*subsonicArtistListModeRow));
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -207,33 +269,36 @@ class SettingsModel : public Wt::WFormModel
|
||||
|
||||
void initializeModels()
|
||||
{
|
||||
Bitrate maxAudioBitrate;
|
||||
{
|
||||
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||
maxAudioBitrate = LmsApp->getUser()->getMaxAudioTranscodeBitrate();
|
||||
}
|
||||
|
||||
_transcodeModeModel = std::make_shared<TranscodeModeModel>();
|
||||
_transcodeModeModel->add(Wt::WString::tr("Lms.Settings.transcode-mode.always"), MediaPlayer::TranscodeMode::Always);
|
||||
_transcodeModeModel->add(Wt::WString::tr("Lms.Settings.transcode-mode.never"), MediaPlayer::TranscodeMode::Never);
|
||||
_transcodeModeModel->add(Wt::WString::tr("Lms.Settings.transcode-mode.if-format-not-supported"), MediaPlayer::TranscodeMode::IfFormatNotSupported);
|
||||
|
||||
_subsonicArtistListModeModel = std::make_shared<ValueStringModel<User::SubsonicArtistListMode>>();
|
||||
_subsonicArtistListModeModel->add(Wt::WString::tr("Lms.Settings.subsonic-artist-list-mode.all-artists"), User::SubsonicArtistListMode::AllArtists);
|
||||
_subsonicArtistListModeModel->add(Wt::WString::tr("Lms.Settings.subsonic-artist-list-mode.release-artists"), User::SubsonicArtistListMode::ReleaseArtists);
|
||||
|
||||
_transcodeBitrateModel = std::make_shared<ValueStringModel<Bitrate>>();
|
||||
for (const Bitrate bitrate : User::audioTranscodeAllowedBitrates)
|
||||
{
|
||||
if (bitrate > maxAudioBitrate)
|
||||
break;
|
||||
|
||||
_transcodeBitrateModel->add(Wt::WString::fromUTF8(std::to_string(bitrate / 1000)), bitrate);
|
||||
}
|
||||
|
||||
_transcodeFormatModel = std::make_shared<ValueStringModel<AudioFormat>>();
|
||||
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcoding.mp3"), AudioFormat::MP3);
|
||||
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcoding.ogg_opus"), AudioFormat::OGG_OPUS);
|
||||
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcoding.matroska_opus"), AudioFormat::MATROSKA_OPUS);
|
||||
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcoding.ogg_vorbis"), AudioFormat::OGG_VORBIS);
|
||||
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcoding.webm_vorbis"), AudioFormat::WEBM_VORBIS);
|
||||
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcode-format.mp3"), AudioFormat::MP3);
|
||||
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcode-format.ogg_opus"), AudioFormat::OGG_OPUS);
|
||||
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcode-format.matroska_opus"), AudioFormat::MATROSKA_OPUS);
|
||||
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcode-format.ogg_vorbis"), AudioFormat::OGG_VORBIS);
|
||||
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcode-format.webm_vorbis"), AudioFormat::WEBM_VORBIS);
|
||||
}
|
||||
|
||||
bool _withOldPassword {};
|
||||
|
||||
std::shared_ptr<ValueStringModel<Bitrate>> _transcodeBitrateModel;
|
||||
std::shared_ptr<ValueStringModel<AudioFormat>> _transcodeFormatModel;
|
||||
std::shared_ptr<TranscodeModeModel> _transcodeModeModel;
|
||||
std::shared_ptr<ValueStringModel<Bitrate>> _transcodeBitrateModel;
|
||||
std::shared_ptr<ValueStringModel<AudioFormat>> _transcodeFormatModel;
|
||||
std::shared_ptr<ValueStringModel<User::SubsonicArtistListMode>> _subsonicArtistListModeModel;
|
||||
};
|
||||
|
||||
SettingsView::SettingsView()
|
||||
@@ -243,6 +308,11 @@ SettingsView::SettingsView()
|
||||
refreshView();
|
||||
}));
|
||||
|
||||
LmsApp->getEvents().mediaPlayerSettingsAvailable.connect([=]()
|
||||
{
|
||||
refreshView();
|
||||
});
|
||||
|
||||
refreshView();
|
||||
}
|
||||
|
||||
@@ -254,6 +324,10 @@ SettingsView::refreshView()
|
||||
|
||||
clear();
|
||||
|
||||
// Hack to wait for the audio player know the settings applied
|
||||
if (!LmsApp->getMediaPlayer()->getSettings())
|
||||
return;
|
||||
|
||||
auto t {addNew<Wt::WTemplateFormView>(Wt::WString::tr("Lms.Settings.template"))};
|
||||
|
||||
auto model {std::make_shared<SettingsModel>(!LmsApp->isUserAuthStrong())};
|
||||
@@ -268,6 +342,7 @@ SettingsView::refreshView()
|
||||
if (!LmsApp->isUserAuthStrong())
|
||||
{
|
||||
t->setCondition("if-has-old-password", true);
|
||||
|
||||
auto oldPassword {std::make_unique<Wt::WLineEdit>()};
|
||||
oldPassword->setEchoMode(Wt::EchoMode::Password);
|
||||
t->setFormWidget(SettingsModel::PasswordOldField, std::move(oldPassword));
|
||||
@@ -283,35 +358,74 @@ SettingsView::refreshView()
|
||||
passwordConfirm->setEchoMode(Wt::EchoMode::Password);
|
||||
t->setFormWidget(SettingsModel::PasswordConfirmField, std::move(passwordConfirm));
|
||||
|
||||
// Transcoding
|
||||
auto transcode {std::make_unique<Wt::WCheckBox>()};
|
||||
auto* transcodeRaw {transcode.get()};
|
||||
t->setFormWidget(SettingsModel::TranscodeEnableField, std::move(transcode));
|
||||
|
||||
// Format
|
||||
auto transcodeFormat {std::make_unique<Wt::WComboBox>()};
|
||||
transcodeFormat->setModel(model->transcodeFormatModel());
|
||||
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([=]()
|
||||
// Audio
|
||||
{
|
||||
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());
|
||||
});
|
||||
// Transcode
|
||||
auto transcodeMode {std::make_unique<Wt::WComboBox>()};
|
||||
auto* transcodeModeRaw {transcodeMode.get()};
|
||||
transcodeMode->setModel(model->getTranscodeModeModel());
|
||||
t->setFormWidget(SettingsModel::TranscodeModeField, std::move(transcodeMode));
|
||||
|
||||
// Format
|
||||
auto transcodeFormat {std::make_unique<Wt::WComboBox>()};
|
||||
transcodeFormat->setModel(model->getTranscodeFormatModel());
|
||||
t->setFormWidget(SettingsModel::TranscodeFormatField, std::move(transcodeFormat));
|
||||
|
||||
// Bitrate
|
||||
auto transcodeBitrate {std::make_unique<Wt::WComboBox>()};
|
||||
transcodeBitrate->setModel(model->getTranscodeBitrateModel());
|
||||
t->setFormWidget(SettingsModel::TranscodeBitrateField, std::move(transcodeBitrate));
|
||||
|
||||
transcodeModeRaw->sactivated().connect([=]()
|
||||
{
|
||||
auto row {model->getTranscodeModeModel()->getRowFromString(model->valueText(SettingsModel::TranscodeModeField))};
|
||||
const bool enable = (row && (model->getTranscodeModeModel()->getValue(*row) != MediaPlayer::TranscodeMode::Never));
|
||||
model->setReadOnly(SettingsModel::TranscodeFormatField, !enable);
|
||||
model->setReadOnly(SettingsModel::TranscodeBitrateField, !enable);
|
||||
t->updateModel(model.get());
|
||||
t->updateView(model.get());
|
||||
});
|
||||
}
|
||||
|
||||
// Subsonic
|
||||
{
|
||||
t->setCondition("if-has-subsonic-api", ServiceProvider<IConfig>::get()->getBool("api-subsonic", true));
|
||||
|
||||
// Transcode
|
||||
auto transcode {std::make_unique<Wt::WCheckBox>()};
|
||||
auto* transcodeRaw {transcode.get()};
|
||||
t->setFormWidget(SettingsModel::SubsonicTranscodeEnableField, std::move(transcode));
|
||||
|
||||
// Format
|
||||
auto transcodeFormat {std::make_unique<Wt::WComboBox>()};
|
||||
transcodeFormat->setModel(model->getTranscodeFormatModel());
|
||||
t->setFormWidget(SettingsModel::SubsonicTranscodeFormatField, std::move(transcodeFormat));
|
||||
|
||||
// Bitrate
|
||||
auto transcodeBitrate {std::make_unique<Wt::WComboBox>()};
|
||||
transcodeBitrate->setModel(model->getTranscodeBitrateModel());
|
||||
t->setFormWidget(SettingsModel::SubsonicTranscodeBitrateField, std::move(transcodeBitrate));
|
||||
|
||||
// Artist list mode
|
||||
auto artistListMode = std::make_unique<Wt::WComboBox>();
|
||||
artistListMode->setModel(model->getSubsonicArtistListModeModel());
|
||||
t->setFormWidget(SettingsModel::SubsonicArtistListModeField, std::move(artistListMode));
|
||||
|
||||
transcodeRaw->changed().connect([=]()
|
||||
{
|
||||
const bool enable {transcodeRaw->checkState() == Wt::CheckState::Checked};
|
||||
model->setReadOnly(SettingsModel::SubsonicTranscodeFormatField, !enable);
|
||||
model->setReadOnly(SettingsModel::SubsonicTranscodeBitrateField, !enable);
|
||||
t->updateModel(model.get());
|
||||
t->updateView(model.get());
|
||||
});
|
||||
}
|
||||
|
||||
// Buttons
|
||||
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")))};
|
||||
|
||||
saveBtn->clicked().connect(std::bind([=] ()
|
||||
saveBtn->clicked().connect([=]()
|
||||
{
|
||||
|
||||
{
|
||||
@@ -334,7 +448,7 @@ SettingsView::refreshView()
|
||||
|
||||
// Udate the view: Delete any validation message in the view, etc.
|
||||
t->updateView(model.get());
|
||||
}));
|
||||
});
|
||||
|
||||
discardBtn->clicked().connect(std::bind([=] ()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user