585 lines
30 KiB
C++
585 lines
30 KiB
C++
/*
|
|
* Copyright (C) 2018 Emeric Poupon
|
|
*
|
|
* This file is part of LMS.
|
|
*
|
|
* LMS is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* LMS is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "SettingsView.hpp"
|
|
|
|
#include <Wt/WCheckBox.h>
|
|
#include <Wt/WComboBox.h>
|
|
#include <Wt/WDoubleSpinBox.h>
|
|
#include <Wt/WFormModel.h>
|
|
#include <Wt/WLineEdit.h>
|
|
#include <Wt/WPushButton.h>
|
|
#include <Wt/WString.h>
|
|
#include <Wt/WTemplateFormView.h>
|
|
|
|
#include "common/DoubleValidator.hpp"
|
|
#include "common/MandatoryValidator.hpp"
|
|
#include "common/PasswordValidator.hpp"
|
|
#include "common/UUIDValidator.hpp"
|
|
#include "common/ValueStringModel.hpp"
|
|
|
|
#include "services/auth/IPasswordService.hpp"
|
|
#include "services/database/Session.hpp"
|
|
#include "services/database/User.hpp"
|
|
#include "utils/IConfig.hpp"
|
|
#include "utils/Logger.hpp"
|
|
#include "utils/Service.hpp"
|
|
|
|
#include "LmsApplication.hpp"
|
|
#include "MediaPlayer.hpp"
|
|
|
|
namespace UserInterface {
|
|
|
|
using namespace Database;
|
|
|
|
class SettingsModel : public Wt::WFormModel
|
|
{
|
|
public:
|
|
// Associate each field with a unique string literal.
|
|
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 ReplayGainModeField{ "replaygain-mode" };
|
|
static inline const Field ReplayGainPreAmpGainField{ "replaygain-preamp" };
|
|
static inline const Field ReplayGainPreAmpGainIfNoInfoField{ "replaygain-preamp-no-rg-info" };
|
|
static inline const Field SubsonicArtistListModeField{ "subsonic-artist-list-mode" };
|
|
static inline const Field SubsonicTranscodingOutputFormatField{ "subsonic-transcode-format" };
|
|
static inline const Field SubsonicTranscodingOutputBitrateField{ "subsonic-transcode-bitrate" };
|
|
static inline const Field FeedbackBackendField{ "feedback-backend" };
|
|
static inline const Field ScrobblingBackendField{ "scrobbling-backend" };
|
|
static inline const Field ListenBrainzTokenField{ "listenbrainz-token" };
|
|
static inline const Field PasswordOldField{ "password-old" };
|
|
static inline const Field PasswordField{ "password" };
|
|
static inline const Field PasswordConfirmField{ "password-confirm" };
|
|
|
|
using TranscodingModeModel = ValueStringModel<MediaPlayer::Settings::Transcode::Mode>;
|
|
using ReplayGainModeModel = ValueStringModel<MediaPlayer::Settings::ReplayGain::Mode>;
|
|
using FeedbackBackendModel = ValueStringModel<FeedbackBackend>;
|
|
using ScrobblingBackendModel = ValueStringModel<ScrobblingBackend>;
|
|
|
|
SettingsModel(::Auth::IPasswordService* authPasswordService, bool withOldPassword)
|
|
: _authPasswordService{ authPasswordService }
|
|
, _withOldPassword{ withOldPassword }
|
|
{
|
|
initializeModels();
|
|
|
|
addField(TranscodeModeField);
|
|
addField(TranscodeBitrateField);
|
|
addField(TranscodeFormatField);
|
|
addField(ReplayGainModeField);
|
|
addField(ReplayGainPreAmpGainField);
|
|
addField(ReplayGainPreAmpGainIfNoInfoField);
|
|
addField(SubsonicTranscodingOutputBitrateField);
|
|
addField(SubsonicTranscodingOutputFormatField);
|
|
addField(FeedbackBackendField);
|
|
addField(ScrobblingBackendField);
|
|
addField(ListenBrainzTokenField);
|
|
setValidator(ListenBrainzTokenField, createUUIDValidator());
|
|
|
|
if (_authPasswordService)
|
|
{
|
|
if (_withOldPassword)
|
|
{
|
|
addField(PasswordOldField);
|
|
setValidator(PasswordOldField, createPasswordCheckValidator());
|
|
}
|
|
|
|
addField(PasswordField);
|
|
setValidator(PasswordField, createPasswordStrengthValidator([] { return ::Auth::PasswordValidationContext{ LmsApp->getUserLoginName(), LmsApp->getUserType() }; }));
|
|
addField(PasswordConfirmField);
|
|
}
|
|
|
|
setValidator(TranscodeModeField, createMandatoryValidator());
|
|
setValidator(TranscodeBitrateField, createMandatoryValidator());
|
|
setValidator(TranscodeFormatField, createMandatoryValidator());
|
|
setValidator(ReplayGainModeField, createMandatoryValidator());
|
|
auto createPreAmpValidator{ []
|
|
{
|
|
return createDoubleValidator(MediaPlayer::Settings::ReplayGain::minPreAmpGain, MediaPlayer::Settings::ReplayGain::maxPreAmpGain);
|
|
} };
|
|
|
|
setValidator(ReplayGainPreAmpGainField, createPreAmpValidator());
|
|
setValidator(ReplayGainPreAmpGainIfNoInfoField, createPreAmpValidator());
|
|
setValidator(SubsonicTranscodingOutputBitrateField, createMandatoryValidator());
|
|
setValidator(SubsonicTranscodingOutputFormatField, createMandatoryValidator());
|
|
|
|
loadData();
|
|
}
|
|
|
|
std::shared_ptr<TranscodingModeModel> getTranscodingModeModel() { return _transcodingModeModeModel; }
|
|
std::shared_ptr<Wt::WAbstractItemModel> getTranscodingOutputBitrateModel() { return _transcodingOutputBitrateModel; }
|
|
std::shared_ptr<Wt::WAbstractItemModel> getTranscodeFormatModel() { return _transcodingOutputFormatModel; }
|
|
std::shared_ptr<ReplayGainModeModel> getReplayGainModeModel() { return _replayGainModeModel; }
|
|
std::shared_ptr<Wt::WAbstractItemModel> getSubsonicArtistListModeModel() { return _subsonicArtistListModeModel; }
|
|
std::shared_ptr<FeedbackBackendModel> getFeedbackBackendModel() { return _feedbackBackendModel; }
|
|
std::shared_ptr<ScrobblingBackendModel> getScrobblingBackendModel() { return _scrobblingBackendModel; }
|
|
|
|
void saveData()
|
|
{
|
|
auto transaction{ LmsApp->getDbSession().createUniqueTransaction() };
|
|
|
|
User::pointer user{ LmsApp->getUser() };
|
|
|
|
{
|
|
MediaPlayer::Settings settings;
|
|
|
|
auto transcodeModeRow{ _transcodingModeModeModel->getRowFromString(valueText(TranscodeModeField)) };
|
|
if (transcodeModeRow)
|
|
settings.transcode.mode = _transcodingModeModeModel->getValue(*transcodeModeRow);
|
|
|
|
auto transcodeFormatRow{ _transcodingOutputFormatModel->getRowFromString(valueText(TranscodeFormatField)) };
|
|
if (transcodeFormatRow)
|
|
settings.transcode.format = _transcodingOutputFormatModel->getValue(*transcodeFormatRow);
|
|
|
|
auto transcodeBitrateRow{ _transcodingOutputBitrateModel->getRowFromString(valueText(TranscodeBitrateField)) };
|
|
if (transcodeBitrateRow)
|
|
settings.transcode.bitrate = _transcodingOutputBitrateModel->getValue(*transcodeBitrateRow);
|
|
|
|
auto replayGainModeRow{ _replayGainModeModel->getRowFromString(valueText(ReplayGainModeField)) };
|
|
if (replayGainModeRow)
|
|
settings.replayGain.mode = _replayGainModeModel->getValue(*replayGainModeRow);
|
|
|
|
settings.replayGain.preAmpGain = Wt::asNumber(value(ReplayGainPreAmpGainField));
|
|
settings.replayGain.preAmpGainIfNoInfo = Wt::asNumber(value(ReplayGainPreAmpGainIfNoInfoField));
|
|
|
|
LmsApp->getMediaPlayer().setSettings(settings);
|
|
}
|
|
|
|
{
|
|
auto subsonicTranscodingOutputBitrateRow{ _transcodingOutputBitrateModel->getRowFromString(valueText(SubsonicTranscodingOutputBitrateField)) };
|
|
if (subsonicTranscodingOutputBitrateRow)
|
|
user.modify()->setSubsonicDefaultTranscodingOutputBitrate(_transcodingOutputBitrateModel->getValue(*subsonicTranscodingOutputBitrateRow));
|
|
|
|
auto subsonicTranscodingOutputFormatRow{ _transcodingOutputFormatModel->getRowFromString(valueText(SubsonicTranscodingOutputFormatField)) };
|
|
if (subsonicTranscodingOutputFormatRow)
|
|
user.modify()->setSubsonicDefaultTranscodintOutputFormat(_transcodingOutputFormatModel->getValue(*subsonicTranscodingOutputFormatRow));
|
|
|
|
auto subsonicArtistListModeRow{ _subsonicArtistListModeModel->getRowFromString(valueText(SubsonicArtistListModeField)) };
|
|
if (subsonicArtistListModeRow)
|
|
user.modify()->setSubsonicArtistListMode(_subsonicArtistListModeModel->getValue(*subsonicArtistListModeRow));
|
|
|
|
}
|
|
|
|
{
|
|
if (auto feedbackBackendRow{ _feedbackBackendModel->getRowFromString(valueText(FeedbackBackendField)) })
|
|
user.modify()->setFeedbackBackend(_feedbackBackendModel->getValue(*feedbackBackendRow));
|
|
|
|
user.modify()->setListenBrainzToken(UUID::fromString(Wt::asString(value(ListenBrainzTokenField)).toUTF8()));
|
|
}
|
|
|
|
{
|
|
if (auto scrobblingBackendRow{ _scrobblingBackendModel->getRowFromString(valueText(ScrobblingBackendField)) })
|
|
user.modify()->setScrobblingBackend(_scrobblingBackendModel->getValue(*scrobblingBackendRow));
|
|
|
|
user.modify()->setListenBrainzToken(UUID::fromString(Wt::asString(value(ListenBrainzTokenField)).toUTF8()));
|
|
}
|
|
|
|
if (_authPasswordService && !valueText(PasswordField).empty())
|
|
{
|
|
_authPasswordService->setPassword(user->getId(), valueText(PasswordField).toUTF8());
|
|
}
|
|
}
|
|
|
|
void loadData()
|
|
{
|
|
auto transaction{ LmsApp->getDbSession().createSharedTransaction() };
|
|
|
|
User::pointer user{ LmsApp->getUser() };
|
|
|
|
{
|
|
const auto settings{ *LmsApp->getMediaPlayer().getSettings() };
|
|
|
|
auto transcodeModeRow{ _transcodingModeModeModel->getRowFromValue(settings.transcode.mode) };
|
|
if (transcodeModeRow)
|
|
setValue(TranscodeModeField, _transcodingModeModeModel->getString(*transcodeModeRow));
|
|
|
|
auto transcodeFormatRow{ _transcodingOutputFormatModel->getRowFromValue(settings.transcode.format) };
|
|
if (transcodeFormatRow)
|
|
setValue(TranscodeFormatField, _transcodingOutputFormatModel->getString(*transcodeFormatRow));
|
|
|
|
auto transcodeBitrateRow{ _transcodingOutputBitrateModel->getRowFromValue(settings.transcode.bitrate) };
|
|
if (transcodeBitrateRow)
|
|
setValue(TranscodeBitrateField, _transcodingOutputBitrateModel->getString(*transcodeBitrateRow));
|
|
|
|
{
|
|
const bool usesTranscode{ settings.transcode.mode != MediaPlayer::Settings::Transcode::Mode::Never };
|
|
setReadOnly(SettingsModel::TranscodeFormatField, !usesTranscode);
|
|
setReadOnly(SettingsModel::TranscodeBitrateField, !usesTranscode);
|
|
}
|
|
|
|
auto replayGainModeRow{ _replayGainModeModel->getRowFromValue(settings.replayGain.mode) };
|
|
if (replayGainModeRow)
|
|
setValue(ReplayGainModeField, _replayGainModeModel->getString(*replayGainModeRow));
|
|
|
|
setValue(ReplayGainPreAmpGainField, settings.replayGain.preAmpGain);
|
|
setValue(ReplayGainPreAmpGainIfNoInfoField, settings.replayGain.preAmpGainIfNoInfo);
|
|
}
|
|
|
|
{
|
|
auto subsonicTranscodingOutputBitrateRow{ _transcodingOutputBitrateModel->getRowFromValue(user->getSubsonicDefaultTranscodingOutputBitrate()) };
|
|
if (subsonicTranscodingOutputBitrateRow)
|
|
setValue(SubsonicTranscodingOutputBitrateField, _transcodingOutputBitrateModel->getString(*subsonicTranscodingOutputBitrateRow));
|
|
|
|
auto subsonicTranscodingOutputFormatRow{ _transcodingOutputFormatModel->getRowFromValue(user->getSubsonicDefaultTranscodingOutputFormat()) };
|
|
if (subsonicTranscodingOutputFormatRow)
|
|
setValue(SubsonicTranscodingOutputFormatField, _transcodingOutputFormatModel->getString(*subsonicTranscodingOutputFormatRow));
|
|
|
|
auto subsonicArtistListModeRow{ _subsonicArtistListModeModel->getRowFromValue(user->getSubsonicArtistListMode()) };
|
|
if (subsonicArtistListModeRow)
|
|
setValue(SubsonicArtistListModeField, _subsonicArtistListModeModel->getString(*subsonicArtistListModeRow));
|
|
}
|
|
|
|
{
|
|
if (auto feedbackBackendRow{ _feedbackBackendModel->getRowFromValue(user->getFeedbackBackend()) })
|
|
setValue(FeedbackBackendField, _feedbackBackendModel->getString(*feedbackBackendRow));
|
|
|
|
if (auto scrobblingBackendRow{ _scrobblingBackendModel->getRowFromValue(user->getScrobblingBackend()) })
|
|
setValue(ScrobblingBackendField, _scrobblingBackendModel->getString(*scrobblingBackendRow));
|
|
|
|
if (auto listenBrainzToken{ user->getListenBrainzToken() })
|
|
{
|
|
LMS_LOG(UI, DEBUG) << "Read listenBrainzToken! value = " << listenBrainzToken->getAsString();
|
|
setValue(ListenBrainzTokenField, Wt::WString::fromUTF8(std::string{ listenBrainzToken->getAsString() }));
|
|
}
|
|
|
|
{
|
|
const bool usesListenBrainz{ user->getScrobblingBackend() == ScrobblingBackend::ListenBrainz || user->getFeedbackBackend() == FeedbackBackend::ListenBrainz };
|
|
setReadOnly(SettingsModel::ListenBrainzTokenField, !usesListenBrainz);
|
|
validator(SettingsModel::ListenBrainzTokenField)->setMandatory(usesListenBrainz);
|
|
}
|
|
}
|
|
if (_authPasswordService)
|
|
{
|
|
if (_withOldPassword)
|
|
setValue(PasswordOldField, "");
|
|
|
|
setValue(PasswordField, "");
|
|
setValue(PasswordConfirmField, "");
|
|
}
|
|
}
|
|
|
|
private:
|
|
bool validateField(Field field)
|
|
{
|
|
Wt::WString error;
|
|
|
|
if (field == PasswordOldField)
|
|
{
|
|
if (valueText(PasswordOldField).empty() && !valueText(PasswordField).empty())
|
|
error = Wt::WString::tr("Lms.Settings.password-must-fill-old-password");
|
|
else
|
|
return Wt::WFormModel::validateField(field);
|
|
}
|
|
else if (field == PasswordField)
|
|
{
|
|
if (!valueText(PasswordOldField).empty() && valueText(PasswordField).empty())
|
|
error = Wt::WString::tr("Wt.WValidator.Invalid");
|
|
else
|
|
return Wt::WFormModel::validateField(field);
|
|
}
|
|
else if (field == PasswordConfirmField)
|
|
{
|
|
if (validation(PasswordField).state() == Wt::ValidationState::Valid)
|
|
{
|
|
if (valueText(PasswordField) != valueText(PasswordConfirmField))
|
|
error = Wt::WString::tr("Lms.passwords-dont-match");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return Wt::WFormModel::validateField(field);
|
|
}
|
|
|
|
setValidation(field, Wt::WValidator::Result(error.empty() ? Wt::ValidationState::Valid : Wt::ValidationState::Invalid, error));
|
|
|
|
return (validation(field).state() == Wt::ValidationState::Valid);
|
|
}
|
|
|
|
private:
|
|
|
|
void initializeModels()
|
|
{
|
|
_transcodingModeModeModel = std::make_shared<TranscodingModeModel>();
|
|
_transcodingModeModeModel->add(Wt::WString::tr("Lms.Settings.transcode-mode.always"), MediaPlayer::Settings::Transcode::Mode::Always);
|
|
_transcodingModeModeModel->add(Wt::WString::tr("Lms.Settings.transcode-mode.never"), MediaPlayer::Settings::Transcode::Mode::Never);
|
|
_transcodingModeModeModel->add(Wt::WString::tr("Lms.Settings.transcode-mode.if-format-not-supported"), MediaPlayer::Settings::Transcode::Mode::IfFormatNotSupported);
|
|
|
|
_transcodingOutputBitrateModel = std::make_shared<ValueStringModel<Bitrate>>();
|
|
visitAllowedAudioBitrates([&](const Bitrate bitrate)
|
|
{
|
|
_transcodingOutputBitrateModel->add(Wt::WString::fromUTF8(std::to_string(bitrate / 1000)), bitrate);
|
|
});
|
|
|
|
_transcodingOutputFormatModel = std::make_shared<ValueStringModel<TranscodingOutputFormat>>();
|
|
_transcodingOutputFormatModel->add(Wt::WString::tr("Lms.Settings.transcode-format.mp3"), TranscodingOutputFormat::MP3);
|
|
_transcodingOutputFormatModel->add(Wt::WString::tr("Lms.Settings.transcode-format.ogg_opus"), TranscodingOutputFormat::OGG_OPUS);
|
|
_transcodingOutputFormatModel->add(Wt::WString::tr("Lms.Settings.transcode-format.matroska_opus"), TranscodingOutputFormat::MATROSKA_OPUS);
|
|
_transcodingOutputFormatModel->add(Wt::WString::tr("Lms.Settings.transcode-format.ogg_vorbis"), TranscodingOutputFormat::OGG_VORBIS);
|
|
_transcodingOutputFormatModel->add(Wt::WString::tr("Lms.Settings.transcode-format.webm_vorbis"), TranscodingOutputFormat::WEBM_VORBIS);
|
|
|
|
_replayGainModeModel = std::make_shared<ReplayGainModeModel>();
|
|
_replayGainModeModel->add(Wt::WString::tr("Lms.Settings.replaygain-mode.none"), MediaPlayer::Settings::ReplayGain::Mode::None);
|
|
_replayGainModeModel->add(Wt::WString::tr("Lms.Settings.replaygain-mode.auto"), MediaPlayer::Settings::ReplayGain::Mode::Auto);
|
|
_replayGainModeModel->add(Wt::WString::tr("Lms.Settings.replaygain-mode.track"), MediaPlayer::Settings::ReplayGain::Mode::Track);
|
|
_replayGainModeModel->add(Wt::WString::tr("Lms.Settings.replaygain-mode.release"), MediaPlayer::Settings::ReplayGain::Mode::Release);
|
|
|
|
_subsonicArtistListModeModel = std::make_shared<ValueStringModel<SubsonicArtistListMode>>();
|
|
_subsonicArtistListModeModel->add(Wt::WString::tr("Lms.Settings.subsonic-artist-list-mode.all-artists"), SubsonicArtistListMode::AllArtists);
|
|
_subsonicArtistListModeModel->add(Wt::WString::tr("Lms.Settings.subsonic-artist-list-mode.release-artists"), SubsonicArtistListMode::ReleaseArtists);
|
|
_subsonicArtistListModeModel->add(Wt::WString::tr("Lms.Settings.subsonic-artist-list-mode.track-artists"), SubsonicArtistListMode::TrackArtists);
|
|
|
|
_feedbackBackendModel = std::make_shared<ValueStringModel<FeedbackBackend>>();
|
|
_feedbackBackendModel->add(Wt::WString::tr("Lms.Settings.backend.internal"), FeedbackBackend::Internal);
|
|
_feedbackBackendModel->add(Wt::WString::tr("Lms.Settings.backend.listenbrainz"), FeedbackBackend::ListenBrainz);
|
|
|
|
_scrobblingBackendModel = std::make_shared<ValueStringModel<ScrobblingBackend>>();
|
|
_scrobblingBackendModel->add(Wt::WString::tr("Lms.Settings.backend.internal"), ScrobblingBackend::Internal);
|
|
_scrobblingBackendModel->add(Wt::WString::tr("Lms.Settings.backend.listenbrainz"), ScrobblingBackend::ListenBrainz);
|
|
}
|
|
|
|
::Auth::IPasswordService* _authPasswordService{};
|
|
bool _withOldPassword{};
|
|
|
|
std::shared_ptr<TranscodingModeModel> _transcodingModeModeModel;
|
|
std::shared_ptr<ValueStringModel<Bitrate>> _transcodingOutputBitrateModel;
|
|
std::shared_ptr<ValueStringModel<TranscodingOutputFormat>> _transcodingOutputFormatModel;
|
|
std::shared_ptr<ReplayGainModeModel> _replayGainModeModel;
|
|
std::shared_ptr<ValueStringModel<SubsonicArtistListMode>> _subsonicArtistListModeModel;
|
|
std::shared_ptr<FeedbackBackendModel> _feedbackBackendModel;
|
|
std::shared_ptr<ScrobblingBackendModel> _scrobblingBackendModel;
|
|
};
|
|
|
|
SettingsView::SettingsView()
|
|
{
|
|
wApp->internalPathChanged().connect(this, [this]
|
|
{
|
|
refreshView();
|
|
});
|
|
|
|
LmsApp->getMediaPlayer().settingsLoaded.connect([this]
|
|
{
|
|
refreshView();
|
|
});
|
|
|
|
refreshView();
|
|
}
|
|
|
|
void
|
|
SettingsView::refreshView()
|
|
{
|
|
if (!wApp->internalPathMatches("/settings"))
|
|
return;
|
|
|
|
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* authPasswordService{ Service<::Auth::IPasswordService>::get() };
|
|
if (authPasswordService && !authPasswordService->canSetPasswords())
|
|
authPasswordService = nullptr;
|
|
|
|
auto model{ std::make_shared<SettingsModel>(authPasswordService, !LmsApp->isUserAuthStrong()) };
|
|
|
|
if (authPasswordService)
|
|
{
|
|
t->setCondition("if-has-change-password", true);
|
|
|
|
// Old password
|
|
if (!LmsApp->isUserAuthStrong())
|
|
{
|
|
t->setCondition("if-has-old-password", true);
|
|
|
|
auto oldPassword{ std::make_unique<Wt::WLineEdit>() };
|
|
oldPassword->setEchoMode(Wt::EchoMode::Password);
|
|
oldPassword->setAttributeValue("autocomplete", "current-password");
|
|
t->setFormWidget(SettingsModel::PasswordOldField, std::move(oldPassword));
|
|
}
|
|
|
|
// Password
|
|
auto password{ std::make_unique<Wt::WLineEdit>() };
|
|
password->setEchoMode(Wt::EchoMode::Password);
|
|
password->setAttributeValue("autocomplete", "new-password");
|
|
t->setFormWidget(SettingsModel::PasswordField, std::move(password));
|
|
|
|
// Password confirm
|
|
auto passwordConfirm{ std::make_unique<Wt::WLineEdit>() };
|
|
passwordConfirm->setEchoMode(Wt::EchoMode::Password);
|
|
passwordConfirm->setAttributeValue("autocomplete", "new-password");
|
|
t->setFormWidget(SettingsModel::PasswordConfirmField, std::move(passwordConfirm));
|
|
}
|
|
|
|
// Audio
|
|
{
|
|
// Transcode
|
|
auto transcodeMode{ std::make_unique<Wt::WComboBox>() };
|
|
auto* transcodeModeRaw{ transcodeMode.get() };
|
|
transcodeMode->setModel(model->getTranscodingModeModel());
|
|
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->getTranscodingOutputBitrateModel());
|
|
t->setFormWidget(SettingsModel::TranscodeBitrateField, std::move(transcodeBitrate));
|
|
|
|
transcodeModeRaw->activated().connect([=](int row)
|
|
{
|
|
const bool enable{ model->getTranscodingModeModel()->getValue(row) != MediaPlayer::Settings::Transcode::Mode::Never };
|
|
model->setReadOnly(SettingsModel::TranscodeFormatField, !enable);
|
|
model->setReadOnly(SettingsModel::TranscodeBitrateField, !enable);
|
|
t->updateModel(model.get());
|
|
t->updateView(model.get());
|
|
});
|
|
|
|
// Replay gain mode
|
|
auto replayGainMode{ std::make_unique<Wt::WComboBox>() };
|
|
auto* replayGainModeRaw{ replayGainMode.get() };
|
|
replayGainMode->setModel(model->getReplayGainModeModel());
|
|
t->setFormWidget(SettingsModel::ReplayGainModeField, std::move(replayGainMode));
|
|
|
|
// Replay gain preampGain
|
|
auto replayGainPreampGain{ std::make_unique<Wt::WDoubleSpinBox>() };
|
|
replayGainPreampGain->setRange(MediaPlayer::Settings::ReplayGain::minPreAmpGain, MediaPlayer::Settings::ReplayGain::maxPreAmpGain);
|
|
t->setFormWidget(SettingsModel::ReplayGainPreAmpGainField, std::move(replayGainPreampGain));
|
|
|
|
// Replay gain preampGain if no info
|
|
auto replayGainPreampGainIfNoInfo{ std::make_unique<Wt::WDoubleSpinBox>() };
|
|
replayGainPreampGainIfNoInfo->setRange(MediaPlayer::Settings::ReplayGain::minPreAmpGain, MediaPlayer::Settings::ReplayGain::maxPreAmpGain);
|
|
t->setFormWidget(SettingsModel::ReplayGainPreAmpGainIfNoInfoField, std::move(replayGainPreampGainIfNoInfo));
|
|
|
|
replayGainModeRaw->activated().connect([=](int row)
|
|
{
|
|
const bool enable{ model->getReplayGainModeModel()->getValue(row) != MediaPlayer::Settings::ReplayGain::Mode::None };
|
|
model->setReadOnly(SettingsModel::SettingsModel::ReplayGainPreAmpGainField, !enable);
|
|
model->setReadOnly(SettingsModel::SettingsModel::ReplayGainPreAmpGainIfNoInfoField, !enable);
|
|
t->updateModel(model.get());
|
|
t->updateView(model.get());
|
|
});
|
|
if (LmsApp->getMediaPlayer().getSettings()->replayGain.mode == MediaPlayer::Settings::ReplayGain::Mode::None)
|
|
{
|
|
model->setReadOnly(SettingsModel::SettingsModel::ReplayGainPreAmpGainField, true);
|
|
model->setReadOnly(SettingsModel::SettingsModel::ReplayGainPreAmpGainIfNoInfoField, true);
|
|
}
|
|
}
|
|
|
|
// Subsonic
|
|
{
|
|
t->setCondition("if-has-subsonic-api", Service<IConfig>::get()->getBool("api-subsonic", true));
|
|
|
|
// Format
|
|
auto transcodeFormat{ std::make_unique<Wt::WComboBox>() };
|
|
transcodeFormat->setModel(model->getTranscodeFormatModel());
|
|
t->setFormWidget(SettingsModel::SubsonicTranscodingOutputFormatField, std::move(transcodeFormat));
|
|
|
|
// Bitrate
|
|
auto transcodeBitrate{ std::make_unique<Wt::WComboBox>() };
|
|
transcodeBitrate->setModel(model->getTranscodingOutputBitrateModel());
|
|
t->setFormWidget(SettingsModel::SubsonicTranscodingOutputBitrateField, std::move(transcodeBitrate));
|
|
|
|
// Artist list mode
|
|
auto artistListMode{ std::make_unique<Wt::WComboBox>() };
|
|
artistListMode->setModel(model->getSubsonicArtistListModeModel());
|
|
t->setFormWidget(SettingsModel::SubsonicArtistListModeField, std::move(artistListMode));
|
|
}
|
|
|
|
// Feedback
|
|
Wt::WComboBox* feedbackBackendRaw{};
|
|
{
|
|
auto feedbackBackend{ std::make_unique<Wt::WComboBox>() };
|
|
feedbackBackend->setModel(model->getFeedbackBackendModel());
|
|
feedbackBackendRaw = feedbackBackend.get();
|
|
t->setFormWidget(SettingsModel::FeedbackBackendField, std::move(feedbackBackend));
|
|
}
|
|
|
|
// Scrobbling
|
|
Wt::WComboBox* scrobblingBackendRaw;
|
|
{
|
|
auto scrobblingBackend{ std::make_unique<Wt::WComboBox>() };
|
|
scrobblingBackend->setModel(model->getScrobblingBackendModel());
|
|
scrobblingBackendRaw = scrobblingBackend.get();
|
|
t->setFormWidget(SettingsModel::ScrobblingBackendField, std::move(scrobblingBackend));
|
|
}
|
|
|
|
// Backend settings
|
|
{
|
|
auto listenbrainzToken{ std::make_unique<Wt::WLineEdit>() };
|
|
t->setFormWidget(SettingsModel::ListenBrainzTokenField, std::move(listenbrainzToken));
|
|
}
|
|
|
|
auto updateListenBrainzTokenField{ [=]
|
|
{
|
|
const bool enable{ model->getFeedbackBackendModel()->getValue(feedbackBackendRaw->currentIndex()) == FeedbackBackend::ListenBrainz
|
|
|| model->getScrobblingBackendModel()->getValue(scrobblingBackendRaw->currentIndex()) == ScrobblingBackend::ListenBrainz };
|
|
|
|
model->setReadOnly(SettingsModel::ListenBrainzTokenField, !enable);
|
|
model->validator(SettingsModel::ListenBrainzTokenField)->setMandatory(enable);
|
|
t->updateModel(model.get());
|
|
t->updateView(model.get());
|
|
} };
|
|
|
|
feedbackBackendRaw->activated().connect([=] { updateListenBrainzTokenField(); });
|
|
scrobblingBackendRaw->activated().connect([=] { updateListenBrainzTokenField();});
|
|
|
|
// 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([=]
|
|
{
|
|
{
|
|
auto transaction{ LmsApp->getDbSession().createSharedTransaction() };
|
|
|
|
if (LmsApp->getUser()->isDemo())
|
|
{
|
|
LmsApp->notifyMsg(Notification::Type::Warning, Wt::WString::tr("Lms.Settings.settings"), Wt::WString::tr("Lms.Settings.demo-cannot-save"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
t->updateModel(model.get());
|
|
|
|
if (model->validate())
|
|
{
|
|
model->saveData();
|
|
LmsApp->notifyMsg(Notification::Type::Info, Wt::WString::tr("Lms.Settings.settings"), Wt::WString::tr("Lms.Settings.settings-saved"));
|
|
}
|
|
|
|
// Udate the view: Delete any validation message in the view, etc.
|
|
t->updateView(model.get());
|
|
});
|
|
|
|
discardBtn->clicked().connect([=]
|
|
{
|
|
model->loadData();
|
|
model->validate();
|
|
t->updateView(model.get());
|
|
});
|
|
|
|
t->updateView(model.get());
|
|
}
|
|
|
|
} // namespace UserInterface
|