diff --git a/build/depends.py b/build/depends.py index 6e0dee6ecc7..ab8a5ec9b96 100644 --- a/build/depends.py +++ b/build/depends.py @@ -680,6 +680,8 @@ def sources(self, build): "preferences/settingsmanager.cpp", "preferences/replaygainsettings.cpp", "preferences/broadcastsettings.cpp", + "preferences/broadcastsettings_legacy.cpp", + "preferences/broadcastprofile.cpp", "preferences/upgrade.cpp", "preferences/dlgpreferencepage.cpp", diff --git a/src/broadcast/broadcastmanager.cpp b/src/broadcast/broadcastmanager.cpp index 0e328e2eeb0..c1da98d13c6 100644 --- a/src/broadcast/broadcastmanager.cpp +++ b/src/broadcast/broadcastmanager.cpp @@ -6,14 +6,15 @@ #include "engine/sidechain/enginesidechain.h" #include "soundio/soundmanager.h" -BroadcastManager::BroadcastManager(UserSettingsPointer pConfig, +BroadcastManager::BroadcastManager(SettingsManager* pSettingsManager, SoundManager* pSoundManager) - : m_pConfig(pConfig) { + : m_pConfig(pSettingsManager->settings()) { QSharedPointer pNetworkStream = pSoundManager->getNetworkStream(); if (!pNetworkStream.isNull()) { m_pBroadcast = QSharedPointer( - new EngineBroadcast(pConfig)); + new EngineBroadcast(m_pConfig, + pSettingsManager->broadcastSettings())); pNetworkStream->addWorker(m_pBroadcast); } m_pBroadcastEnabled = new ControlProxy( diff --git a/src/broadcast/broadcastmanager.h b/src/broadcast/broadcastmanager.h index 092e11f03fe..6d19ac61f9d 100644 --- a/src/broadcast/broadcastmanager.h +++ b/src/broadcast/broadcastmanager.h @@ -4,6 +4,7 @@ #include #include "engine/sidechain/enginebroadcast.h" +#include "preferences/settingsmanager.h" #include "preferences/usersettings.h" class SoundManager; @@ -11,7 +12,7 @@ class SoundManager; class BroadcastManager : public QObject { Q_OBJECT public: - BroadcastManager(UserSettingsPointer pConfig, + BroadcastManager(SettingsManager* pSettingsManager, SoundManager* pSoundManager); virtual ~BroadcastManager(); diff --git a/src/encoder/encoderbroadcastsettings.cpp b/src/encoder/encoderbroadcastsettings.cpp index 19f861fe52d..92c58124d66 100644 --- a/src/encoder/encoderbroadcastsettings.cpp +++ b/src/encoder/encoderbroadcastsettings.cpp @@ -10,9 +10,9 @@ #define DEFAULT_BITRATE 128 -EncoderBroadcastSettings::EncoderBroadcastSettings(BroadcastSettings settings) : -m_settings(settings) -{ +EncoderBroadcastSettings::EncoderBroadcastSettings( + BroadcastSettingsPointer settings) + : m_settings(settings) { m_qualList.append(32); m_qualList.append(48); m_qualList.append(64); @@ -26,41 +26,40 @@ m_settings(settings) m_qualList.append(256); m_qualList.append(320); } -EncoderBroadcastSettings::~EncoderBroadcastSettings() -{ - +EncoderBroadcastSettings::~EncoderBroadcastSettings() { } - -QList EncoderBroadcastSettings::getQualityValues() const -{ +QList EncoderBroadcastSettings::getQualityValues() const { return m_qualList; } // Sets the value -void EncoderBroadcastSettings::setQualityByValue(int qualityValue) -{ +void EncoderBroadcastSettings::setQualityByValue(int qualityValue) { + const BroadcastProfilePtr& profile = m_settings->profileAt(0); + if (m_qualList.contains(qualityValue)) { - m_settings.setBitrate(qualityValue); + profile->setBitrate(qualityValue); } else { qWarning() << "Invalid qualityValue given to EncoderBroadcastSettings: " << qualityValue << ". Ignoring it"; } } -void EncoderBroadcastSettings::setQualityByIndex(int qualityIndex) -{ +void EncoderBroadcastSettings::setQualityByIndex(int qualityIndex) { + const BroadcastProfilePtr& profile = m_settings->profileAt(0); + if (qualityIndex >= 0 && qualityIndex < m_qualList.size()) { - m_settings.setBitrate(m_qualList.at(qualityIndex)); + profile->setBitrate(m_qualList.at(qualityIndex)); } else { qWarning() << "Invalid qualityIndex given to EncoderBroadcastSettings: " << qualityIndex << ". Ignoring it"; } } -int EncoderBroadcastSettings::getQuality() const -{ - int bitrate = m_settings.getBitrate(); +int EncoderBroadcastSettings::getQuality() const { + const BroadcastProfilePtr& profile = m_settings->profileAt(0); + + int bitrate = profile->getBitrate(); if (m_qualList.contains(bitrate)) { return bitrate; } @@ -70,18 +69,21 @@ int EncoderBroadcastSettings::getQuality() const } return DEFAULT_BITRATE; } -int EncoderBroadcastSettings::getQualityIndex() const -{ + +int EncoderBroadcastSettings::getQualityIndex() const { return m_qualList.indexOf(getQuality()); } void EncoderBroadcastSettings::setChannelMode(EncoderSettings::ChannelMode mode) { - m_settings.setChannels(static_cast(mode)); + const BroadcastProfilePtr& profile = m_settings->profileAt(0); + profile->setChannels(static_cast(mode)); } -EncoderSettings::ChannelMode EncoderBroadcastSettings::getChannelMode() const -{ - switch(m_settings.getChannels()) { + +EncoderSettings::ChannelMode EncoderBroadcastSettings::getChannelMode() const { + const BroadcastProfilePtr& profile = m_settings->profileAt(0); + + switch(profile->getChannels()) { case 1: return EncoderSettings::ChannelMode::MONO; case 2: return EncoderSettings::ChannelMode::STEREO; case 0: // fallthrough diff --git a/src/encoder/encoderbroadcastsettings.h b/src/encoder/encoderbroadcastsettings.h index 4198f970e44..46ff12462db 100644 --- a/src/encoder/encoderbroadcastsettings.h +++ b/src/encoder/encoderbroadcastsettings.h @@ -15,7 +15,7 @@ class EncoderBroadcastSettings : public EncoderSettings { public: - EncoderBroadcastSettings(BroadcastSettings settings); + EncoderBroadcastSettings(BroadcastSettingsPointer settings); virtual ~EncoderBroadcastSettings(); // Indicates that it uses the quality slider section of the preferences @@ -39,7 +39,7 @@ class EncoderBroadcastSettings : public EncoderSettings { private: QList m_qualList; - BroadcastSettings m_settings; + BroadcastSettingsPointer m_settings; }; diff --git a/src/engine/sidechain/enginebroadcast.cpp b/src/engine/sidechain/enginebroadcast.cpp index b01a2b57e3d..6b7e765de75 100644 --- a/src/engine/sidechain/enginebroadcast.cpp +++ b/src/engine/sidechain/enginebroadcast.cpp @@ -30,7 +30,8 @@ static const int kMaxNetworkCache = 491520; // 10 s mp3 @ 192 kbit/s // http://wiki.shoutcast.com/wiki/SHOUTcast_DNAS_Server_2 static const int kMaxShoutFailures = 3; -EngineBroadcast::EngineBroadcast(UserSettingsPointer pConfig) +EngineBroadcast::EngineBroadcast(UserSettingsPointer pConfig, + BroadcastSettingsPointer pBroadcastSettings) : m_pTextCodec(nullptr), m_pMetaData(), m_pShout(nullptr), @@ -38,7 +39,7 @@ EngineBroadcast::EngineBroadcast(UserSettingsPointer pConfig) m_iMetaDataLife(0), m_iShoutStatus(0), m_iShoutFailures(0), - m_settings(pConfig), + m_settings(pBroadcastSettings), m_pConfig(pConfig), m_encoder(nullptr), m_pMasterSamplerate(new ControlProxy("[Master]", "samplerate")), @@ -135,6 +136,8 @@ QByteArray EngineBroadcast::encodeString(const QString& string) { } void EngineBroadcast::updateFromPreferences() { + const BroadcastProfilePtr& profile = m_settings->profileAt(0); + qDebug() << "EngineBroadcast: updating from preferences"; NetworkStreamWorker::debugState(); @@ -164,7 +167,7 @@ void EngineBroadcast::updateFromPreferences() { // Convert a bunch of QStrings to QByteArrays so we can get regular C char* // strings to pass to libshout. - QString codec = m_settings.getMetadataCharset(); + QString codec = profile->getMetadataCharset(); QByteArray baCodec = codec.toLatin1(); m_pTextCodec = QTextCodec::codecForName(baCodec); if (!m_pTextCodec) { @@ -175,9 +178,9 @@ void EngineBroadcast::updateFromPreferences() { // Indicates our metadata is in the provided charset. shout_metadata_add(m_pShoutMetaData, "charset", baCodec.constData()); - QString serverType = m_settings.getServertype(); + QString serverType = profile->getServertype(); - QString host = m_settings.getHost(); + QString host = profile->getHost(); int start = host.indexOf(QLatin1String("//")); if (start == -1) { // the host part requires preceding //. @@ -187,10 +190,10 @@ void EngineBroadcast::updateFromPreferences() { } QUrl serverUrl = host; - int port = m_settings.getPort(); + int port = profile->getPort(); serverUrl.setPort(port); - QString mountPoint = m_settings.getMountpoint(); + QString mountPoint = profile->getMountpoint(); if (!mountPoint.isEmpty()) { if (!mountPoint.startsWith('/')) { mountPoint.prepend('/'); @@ -198,43 +201,43 @@ void EngineBroadcast::updateFromPreferences() { serverUrl.setPath(mountPoint); } - QString login = m_settings.getLogin(); + QString login = profile->getLogin(); if (!login.isEmpty()) { serverUrl.setUserName(login); } qDebug() << "Using server URL:" << serverUrl; - QByteArray baPassword = m_settings.getPassword().toLatin1(); - QByteArray baFormat = m_settings.getFormat().toLatin1(); - int iBitrate = m_settings.getBitrate(); + QByteArray baPassword = profile->getPassword().toLatin1(); + QByteArray baFormat = profile->getFormat().toLatin1(); + int iBitrate = profile->getBitrate(); // Encode metadata like stream name, website, desc, genre, title/author with // the chosen TextCodec. - QByteArray baStreamName = encodeString(m_settings.getStreamName()); - QByteArray baStreamWebsite = encodeString(m_settings.getStreamWebsite()); - QByteArray baStreamDesc = encodeString(m_settings.getStreamDesc()); - QByteArray baStreamGenre = encodeString(m_settings.getStreamGenre()); + QByteArray baStreamName = encodeString(profile->getStreamName()); + QByteArray baStreamWebsite = encodeString(profile->getStreamWebsite()); + QByteArray baStreamDesc = encodeString(profile->getStreamDesc()); + QByteArray baStreamGenre = encodeString(profile->getStreamGenre()); // Whether the stream is public. - bool streamPublic = m_settings.getStreamPublic(); + bool streamPublic = profile->getStreamPublic(); // Dynamic Ogg metadata update - m_ogg_dynamic_update = m_settings.getOggDynamicUpdate(); + m_ogg_dynamic_update = profile->getOggDynamicUpdate(); - m_custom_metadata = m_settings.getEnableMetadata(); - m_customTitle = m_settings.getCustomTitle(); - m_customArtist = m_settings.getCustomArtist(); + m_custom_metadata = profile->getEnableMetadata(); + m_customTitle = profile->getCustomTitle(); + m_customArtist = profile->getCustomArtist(); - m_metadataFormat = m_settings.getMetadataFormat(); + m_metadataFormat = profile->getMetadataFormat(); - bool enableReconnect = m_settings.getEnableReconnect(); + bool enableReconnect = profile->getEnableReconnect(); if (enableReconnect) { - m_reconnectFirstDelay = m_settings.getReconnectFirstDelay(); - m_reconnectPeriod = m_settings.getReconnectPeriod(); - m_noDelayFirstReconnect = m_settings.getNoDelayFirstReconnect(); - m_limitReconnects = m_settings.getLimitReconnects(); - m_maximumRetries = m_settings.getMaximumRetries(); + m_reconnectFirstDelay = profile->getReconnectFirstDelay(); + m_reconnectPeriod = profile->getReconnectPeriod(); + m_noDelayFirstReconnect = profile->getNoDelayFirstReconnect(); + m_limitReconnects = profile->getLimitReconnects(); + m_maximumRetries = profile->getMaximumRetries(); } else { m_limitReconnects = true; m_maximumRetries = 0; diff --git a/src/engine/sidechain/enginebroadcast.h b/src/engine/sidechain/enginebroadcast.h index c7b2b2169e9..233cea21293 100644 --- a/src/engine/sidechain/enginebroadcast.h +++ b/src/engine/sidechain/enginebroadcast.h @@ -41,7 +41,8 @@ class EngineBroadcast STATUSCO_FAILURE = 3 // Happens when disconnected by an error }; - EngineBroadcast(UserSettingsPointer pConfig); + EngineBroadcast(UserSettingsPointer pConfig, + BroadcastSettingsPointer pBroadcastSettings); virtual ~EngineBroadcast(); // This is called by the Engine implementation for each sample. Encode and @@ -123,7 +124,7 @@ class EngineBroadcast int m_iMetaDataLife; long m_iShoutStatus; long m_iShoutFailures; - BroadcastSettings m_settings; + BroadcastSettingsPointer m_settings; UserSettingsPointer m_pConfig; EncoderPointer m_encoder; ControlPushButton* m_pBroadcastEnabled; diff --git a/src/mixxx.cpp b/src/mixxx.cpp index 238f2380379..94ee024c3cc 100644 --- a/src/mixxx.cpp +++ b/src/mixxx.cpp @@ -202,7 +202,8 @@ void MixxxMainWindow::initialize(QApplication* pApp, const CmdlineArgs& args) { #ifdef __BROADCAST__ - m_pBroadcastManager = new BroadcastManager(pConfig, m_pSoundManager); + m_pBroadcastManager = new BroadcastManager(m_pSettingsManager, + m_pSoundManager); #endif launchProgress(11); @@ -320,7 +321,7 @@ void MixxxMainWindow::initialize(QApplication* pApp, const CmdlineArgs& args) { // Initialize preference dialog m_pPrefDlg = new DlgPreferences(this, m_pSkinLoader, m_pSoundManager, m_pPlayerManager, m_pControllerManager, m_pVCManager, m_pEffectsManager, - pConfig, m_pLibrary); + m_pSettingsManager, m_pLibrary); m_pPrefDlg->setWindowIcon(QIcon(":/images/ic_mixxx_window.png")); m_pPrefDlg->setHidden(true); @@ -619,7 +620,6 @@ void MixxxMainWindow::finalize() { Sandbox::shutdown(); qDebug() << t.elapsed(false).debugMillisWithUnit() << "deleting SettingsManager"; - delete m_pSettingsManager; delete m_pKeyboard; delete m_pKbdConfig; diff --git a/src/mixxx.h b/src/mixxx.h index c8e2fd43ec4..8046b74f331 100644 --- a/src/mixxx.h +++ b/src/mixxx.h @@ -19,6 +19,7 @@ #define MIXXX_H #include +#include #include #include "preferences/configobject.h" @@ -48,6 +49,8 @@ class SoundManager; class VinylControlManager; class WMainMenuBar; +typedef QSharedPointer SettingsManagerPointer; + // This Class is the base class for Mixxx. It sets up the main // window and providing a menubar. // For the main view, an instance of class MixxxView is diff --git a/src/preferences/broadcastprofile.cpp b/src/preferences/broadcastprofile.cpp new file mode 100644 index 00000000000..c648d3a8165 --- /dev/null +++ b/src/preferences/broadcastprofile.cpp @@ -0,0 +1,492 @@ +// broadcastprofile.cpp +// Created June 2nd 2017 by Stéphane Lepin + +#include +#include +#include +#include +#include +#include + +#include "broadcast/defs_broadcast.h" +#include "defs_urls.h" +#include "util/xml.h" +#include "util/memory.h" + +#include "broadcastprofile.h" + +namespace { +const char* kDoctype = "broadcastprofile"; +const char* kDocumentRoot = "BroadcastProfile"; +const char* kBitrate = "Bitrate"; +const char* kChannels = "Channels"; +const char* kCustomArtist = "CustomArtist"; +const char* kCustomTitle = "CustomTitle"; +const char* kEnableMetadata = "EnableMetadata"; +const char* kEnableReconnect = "EnableReconnect"; +const char* kEnabled = "Enabled"; +const char* kFormat = "Format"; +const char* kHost = "Host"; +const char* kLimitReconnects = "LimitReconnects"; +const char* kLogin = "Login"; +const char* kMaximumRetries = "MaximumRetries"; +const char* kMetadataCharset = "MetadataCharset"; +const char* kMetadataFormat = "MetadataFormat"; +const char* kMountPoint = "Mountpoint"; +const char* kNoDelayFirstReconnect = "NoDelayFirstReconnect"; +const char* kOggDynamicUpdate = "OggDynamicUpdate"; +const char* kPassword = "Password"; +const char* kPort = "Port"; +const char* kReconnectFirstDelay = "ReconnectFirstDelay"; +const char* kReconnectPeriod = "ReconnectPeriod"; +const char* kServertype = "Servertype"; +const char* kStreamDesc = "StreamDesc"; +const char* kStreamGenre = "StreamGenre"; +const char* kStreamName = "StreamName"; +const char* kStreamPublic = "StreamPublic"; +const char* kStreamWebsite = "StreamWebsite"; +const char* kProfileNameAttr = "name"; + +const double kDefaultBitrate = 128; +const int kDefaultChannels = 2; +const bool kDefaultEnableMetadata = false; +const bool kDefaultEnableReconnect = true; +const bool kDefaultLimitReconnects = true; +const int kDefaultMaximumRetries = 10; +// No tr() here, see https://bugs.launchpad.net/mixxx/+bug/1419500 +const QString kDefaultMetadataFormat("$artist - $title"); +const bool kDefaultNoDelayFirstReconnect = true; +const bool kDefaultOggDynamicupdate = false; +double kDefaultReconnectFirstDelay = 0.0; +double kDefaultReconnectPeriod = 5.0; +const QString kDefaultStreamDesc = + QObject::tr("This stream is online for testing purposes!"); +const QString kDefaultStreamGenre = QObject::tr("Live Mix"); +const bool kDefaultStreamPublic = false; + +const QRegExp kForbiddenChars = + QRegExp("[<>:\"\\/|?*\\\\]|(\\.\\.)" + "|CON|AUX|PRN|COM(\\d+)|LPT(\\d+)|NUL"); +} // anonymous namespace + +BroadcastProfile::BroadcastProfile(const QString& profileName, + QObject* parent) + : QObject(parent) { + adoptDefaultValues(); + + // Direct assignment to avoid triggering the + // profileNameChanged signal + m_profileName = QString(profileName); +} + +bool BroadcastProfile::validName(const QString& str) { + return !str.contains(kForbiddenChars); +} + +QString BroadcastProfile::stripForbiddenChars(const QString& str) { + QString sourceText(str); + return sourceText.replace(kForbiddenChars, " "); +} + +BroadcastProfilePtr BroadcastProfile::loadFromFile( + const QString& filename) { + QFileInfo xmlFile(filename); + if(!xmlFile.exists()) + return BroadcastProfilePtr(nullptr); + + QString profileFilename = xmlFile.baseName(); + // The profile filename (without extension) is used to create the instance + // but the real profile name (with forbidden chars but suitable for + // non-filesystem uses) will be fetched from the XML file and set in the + // object during the call to loadValues() + BroadcastProfilePtr profile(new BroadcastProfile(profileFilename)); + profile->loadValues(filename); + return profile; +} + +void BroadcastProfile::adoptDefaultValues() { + m_enabled = true; + + m_host = QString(); + m_port = BROADCAST_DEFAULT_PORT; + m_serverType = QString(); + m_login = QString(); + m_password = QString(); + + m_enableReconnect = kDefaultEnableReconnect; + m_reconnectPeriod = kDefaultReconnectPeriod; + m_limitReconnects = kDefaultLimitReconnects; + + m_mountpoint = QString(); + m_streamDesc = kDefaultStreamDesc; + m_streamGenre = kDefaultStreamGenre; + m_streamName = QString(); + m_streamPublic = kDefaultStreamPublic; + m_streamWebsite = MIXXX_WEBSITE_URL; + + m_enableMetadata = kDefaultEnableMetadata; + m_metadataCharset = QString(); + m_customArtist = QString(); + m_customTitle = QString(); + m_metadataFormat = kDefaultMetadataFormat; + m_oggDynamicUpdate = kDefaultOggDynamicupdate; + + m_bitrate = kDefaultBitrate; + m_channels = kDefaultChannels; + m_format = QString(); + + m_noDelayFirstReconnect = kDefaultNoDelayFirstReconnect; + m_reconnectFirstDelay = kDefaultReconnectFirstDelay; + m_maximumRetries = kDefaultMaximumRetries; +} + +bool BroadcastProfile::loadValues(const QString& filename) { + QDomElement doc = XmlParse::openXMLFile(filename, kDoctype); + if(doc.childNodes().size() < 1) + return false; + + m_profileName = doc.attribute(kProfileNameAttr, m_profileName); + m_enabled = (bool)XmlParse::selectNodeInt(doc, kEnabled); + + m_host = XmlParse::selectNodeQString(doc, kHost); + m_port = XmlParse::selectNodeInt(doc, kPort); + m_serverType = XmlParse::selectNodeQString(doc, kServertype); + m_login = XmlParse::selectNodeQString(doc, kLogin); + m_password = XmlParse::selectNodeQString(doc, kPassword); + + m_enableReconnect = + (bool)XmlParse::selectNodeInt(doc, kEnableReconnect); + m_reconnectPeriod = + XmlParse::selectNodeDouble(doc, kReconnectPeriod); + + m_limitReconnects = + (bool)XmlParse::selectNodeInt(doc, kLimitReconnects); + m_maximumRetries = + XmlParse::selectNodeInt(doc, kMaximumRetries); + + m_noDelayFirstReconnect = + (bool)XmlParse::selectNodeInt(doc, kNoDelayFirstReconnect); + m_reconnectFirstDelay = + XmlParse::selectNodeDouble(doc, kReconnectFirstDelay); + + m_mountpoint = XmlParse::selectNodeQString(doc, kMountPoint); + m_streamName = XmlParse::selectNodeQString(doc, kStreamName); + m_streamDesc = XmlParse::selectNodeQString(doc, kStreamDesc); + m_streamGenre = XmlParse::selectNodeQString(doc, kStreamGenre); + m_streamPublic = (bool)XmlParse::selectNodeInt(doc, kStreamPublic); + m_streamWebsite = XmlParse::selectNodeQString(doc, kStreamWebsite); + + m_format = XmlParse::selectNodeQString(doc, kFormat); + m_bitrate = XmlParse::selectNodeInt(doc, kBitrate); + m_channels = XmlParse::selectNodeInt(doc, kChannels); + + m_enableMetadata = (bool)XmlParse::selectNodeInt(doc, kEnableMetadata); + m_metadataCharset = XmlParse::selectNodeQString(doc, kMetadataCharset); + m_customArtist = XmlParse::selectNodeQString(doc, kCustomArtist); + m_customTitle = XmlParse::selectNodeQString(doc, kCustomTitle); + m_metadataFormat = XmlParse::selectNodeQString(doc, kMetadataFormat); + m_oggDynamicUpdate = + (bool)XmlParse::selectNodeInt(doc, kMetadataFormat); + + return true; +} + +bool BroadcastProfile::save(const QString& filename) { + QDomDocument doc(kDoctype); + QDomElement docRoot = doc.createElement(kDocumentRoot); + docRoot.setAttribute(kProfileNameAttr, m_profileName); + + XmlParse::addElement(doc, docRoot, + kEnabled, QString::number((int)m_enabled)); + + XmlParse::addElement(doc, docRoot, kHost, m_host); + XmlParse::addElement(doc, docRoot, kPort, QString::number(m_port)); + XmlParse::addElement(doc, docRoot, kServertype, m_serverType); + XmlParse::addElement(doc, docRoot, kLogin, m_login); + XmlParse::addElement(doc, docRoot, kPassword, m_password); + + XmlParse::addElement(doc, docRoot, kEnableReconnect, + QString::number((int)m_enableReconnect)); + XmlParse::addElement(doc, docRoot, kReconnectPeriod, + QString::number(m_reconnectPeriod)); + + XmlParse::addElement(doc, docRoot, kLimitReconnects, + QString::number((int)m_limitReconnects)); + XmlParse::addElement(doc, docRoot, kMaximumRetries, + QString::number(m_maximumRetries)); + + XmlParse::addElement(doc, docRoot, kNoDelayFirstReconnect, + QString::number((int)m_noDelayFirstReconnect)); + XmlParse::addElement(doc, docRoot, kReconnectFirstDelay, + QString::number(m_reconnectFirstDelay)); + + XmlParse::addElement(doc, docRoot, kMountPoint, m_mountpoint); + XmlParse::addElement(doc, docRoot, kStreamName, m_streamName); + XmlParse::addElement(doc, docRoot, kStreamDesc, m_streamDesc); + XmlParse::addElement(doc, docRoot, kStreamGenre, m_streamGenre); + XmlParse::addElement(doc, docRoot, kStreamPublic, + QString::number((int)m_streamPublic)); + XmlParse::addElement(doc, docRoot, kStreamWebsite, m_streamWebsite); + + XmlParse::addElement(doc, docRoot, kFormat, m_format); + XmlParse::addElement(doc, docRoot, kBitrate, + QString::number(m_bitrate)); + XmlParse::addElement(doc, docRoot, kChannels, + QString::number(m_channels)); + + XmlParse::addElement(doc, docRoot, kEnableMetadata, + QString::number((int)m_enableMetadata)); + XmlParse::addElement(doc, docRoot, kMetadataCharset, m_metadataCharset); + XmlParse::addElement(doc, docRoot, kCustomArtist, m_customArtist); + XmlParse::addElement(doc, docRoot, kCustomTitle, m_customTitle); + XmlParse::addElement(doc, docRoot, kMetadataFormat, m_metadataFormat); + XmlParse::addElement(doc, docRoot, kOggDynamicUpdate, + QString::number((int)m_oggDynamicUpdate)); + + doc.appendChild(docRoot); + + QFile xmlFile(filename); + if(xmlFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream fileStream(&xmlFile); + doc.save(fileStream, 4); + xmlFile.close(); + + return true; + } + return false; +} + +void BroadcastProfile::setProfileName(const QString &profileName) { + QString oldName(m_profileName); + m_profileName = QString(profileName); + + emit profileNameChanged(oldName, m_profileName); +} + +QString BroadcastProfile::getProfileName() const { + return m_profileName; +} + +// This was useless before, but now comes in handy for multi-broadcasting, +// where it means "this connection is enabled and will be started by Mixxx" +bool BroadcastProfile::getEnabled() const { + return m_enabled; +} + +void BroadcastProfile::setEnabled(bool value) { + m_enabled = value; +} + +QString BroadcastProfile::getHost() const { + return m_host; +} + +void BroadcastProfile::setHost(const QString& value) { + m_host = QString(value); +} + +int BroadcastProfile::getPort() const { + // Valid port numbers are 0 .. 65535 (16 bit unsigned) + if (m_port < 0 || m_port > 0xFFFF) { + return BROADCAST_DEFAULT_PORT; + } + + return m_port; +} + +void BroadcastProfile::setPort(int value) { + m_port = value; +} + +QString BroadcastProfile::getServertype() const { + return m_serverType; +} + +void BroadcastProfile::setServertype(const QString& value) { + m_serverType = QString(value); +} + +QString BroadcastProfile::getLogin() const { + return m_login; +} + +void BroadcastProfile::setLogin(const QString& value) { + m_login = QString(value); +} + +// TODO(Palakis, June 2nd 2017): implement secure password storage +QString BroadcastProfile::getPassword() const { + return m_password; +} + +void BroadcastProfile::setPassword(const QString& value) { + m_password = QString(value); +} + +bool BroadcastProfile::getEnableReconnect() const { + return m_enableReconnect; +} + +void BroadcastProfile::setEnableReconnect(bool value) { + m_enableReconnect = value; +} + +double BroadcastProfile::getReconnectPeriod() const { + return m_reconnectPeriod; +} + +void BroadcastProfile::setReconnectPeriod(double value) { + m_reconnectPeriod = value; +} + +bool BroadcastProfile::getLimitReconnects() const { + return m_limitReconnects; +} + +void BroadcastProfile::setLimitReconnects(bool value) { + m_limitReconnects = value; +} + +int BroadcastProfile::getMaximumRetries() const { + return m_maximumRetries; +} + +void BroadcastProfile::setMaximumRetries(int value) { + m_maximumRetries = value; +} + +bool BroadcastProfile::getNoDelayFirstReconnect() const { + return m_noDelayFirstReconnect; +} + +void BroadcastProfile::setNoDelayFirstReconnect(bool value) { + m_noDelayFirstReconnect = value; +} + +double BroadcastProfile::getReconnectFirstDelay() const { + return m_reconnectFirstDelay; +} + +void BroadcastProfile::setReconnectFirstDelay(double value) { + m_reconnectFirstDelay = value; +} + +QString BroadcastProfile::getMountpoint() const { + return m_mountpoint; +} + +void BroadcastProfile::setMountPoint(const QString& value) { + m_mountpoint = QString(value); +} + +QString BroadcastProfile::getStreamName() const { + return m_streamName; +} + +void BroadcastProfile::setStreamName(const QString& value) { + m_streamName = QString(value); +} + +QString BroadcastProfile::getStreamDesc() const { + return m_streamDesc; +} + +void BroadcastProfile::setStreamDesc(const QString& value) { + m_streamDesc = QString(value); +} + +QString BroadcastProfile::getStreamGenre() const { + return m_streamGenre; +} + +void BroadcastProfile::setStreamGenre(const QString& value) { + m_streamGenre = QString(value); +} + +bool BroadcastProfile::getStreamPublic() const { + return m_streamPublic; +} + +void BroadcastProfile::setStreamPublic(bool value) { + m_streamPublic = value; +} + +QString BroadcastProfile::getStreamWebsite() const { + return m_streamWebsite; +} + +void BroadcastProfile::setStreamWebsite(const QString& value) { + m_streamWebsite = QString(value); +} + +QString BroadcastProfile::getFormat() const { + return m_format; +} + +void BroadcastProfile::setFormat(const QString& value) { + m_format = QString(value); +} + +int BroadcastProfile::getBitrate() const { + return m_bitrate; +} + +void BroadcastProfile::setBitrate(int value) { + m_bitrate = value; +} + +int BroadcastProfile::getChannels() const { + return m_channels; +} + +void BroadcastProfile::setChannels(int value) { + m_channels = value; +} + +bool BroadcastProfile::getEnableMetadata() const { + return m_enableMetadata; +} + +void BroadcastProfile::setEnableMetadata(bool value) { + m_enableMetadata = value; +} + +QString BroadcastProfile::getMetadataCharset() const { + return m_metadataCharset; +} + +void BroadcastProfile::setMetadataCharset(const QString& value) { + m_metadataCharset = QString(value); +} + +QString BroadcastProfile::getCustomArtist() const { + return m_customArtist; +} + +void BroadcastProfile::setCustomArtist(const QString& value) { + m_customArtist = QString(value); +} + +QString BroadcastProfile::getCustomTitle() const { + return m_customTitle; +} + +void BroadcastProfile::setCustomTitle(const QString& value) { + m_customTitle = QString(value); +} + +QString BroadcastProfile::getMetadataFormat() const { + return m_metadataFormat; +} + +void BroadcastProfile::setMetadataFormat(const QString& value) { + m_metadataFormat = QString(value); +} + +bool BroadcastProfile::getOggDynamicUpdate() const { + return m_oggDynamicUpdate; +} + +void BroadcastProfile::setOggDynamicUpdate(bool value) { + m_oggDynamicUpdate = value; +} diff --git a/src/preferences/broadcastprofile.h b/src/preferences/broadcastprofile.h new file mode 100644 index 00000000000..ea38d707a39 --- /dev/null +++ b/src/preferences/broadcastprofile.h @@ -0,0 +1,156 @@ +// broadcastprofile.h +// Created June 2nd 2017 by Stéphane Lepin + +#ifndef BROADCASTPROFILE_H +#define BROADCASTPROFILE_H + +#include +#include +#include + +#include "preferences/usersettings.h" + +class BroadcastProfile; +typedef QSharedPointer BroadcastProfilePtr; +Q_DECLARE_METATYPE(BroadcastProfilePtr) + +class BroadcastProfile : public QObject { + Q_OBJECT + + public: + explicit BroadcastProfile(const QString& profileName, + QObject* parent = nullptr); + bool save(const QString& filename); + + static BroadcastProfilePtr loadFromFile(const QString& filename); + static bool validName(const QString& str); + static QString stripForbiddenChars(const QString& str); + + void setProfileName(const QString& profileName); + QString getProfileName() const; + + bool getEnabled() const; + void setEnabled(bool value); + + QString getHost() const; + void setHost(const QString& value); + + int getPort() const; + void setPort(int value); + + QString getServertype() const; + void setServertype(const QString& value); + + QString getLogin() const; + void setLogin(const QString& value); + + QString getPassword() const; + void setPassword(const QString& value); + + bool getEnableReconnect() const; + void setEnableReconnect(bool value); + + double getReconnectPeriod() const; + void setReconnectPeriod(double value); + + bool getLimitReconnects() const; + void setLimitReconnects(bool value); + + int getMaximumRetries() const; + void setMaximumRetries(int value); + + bool getNoDelayFirstReconnect() const; + void setNoDelayFirstReconnect(bool value); + + double getReconnectFirstDelay() const; + void setReconnectFirstDelay(double value); + + QString getFormat() const; + void setFormat(const QString& value); + + int getBitrate() const; + void setBitrate(int value); + + int getChannels() const; + void setChannels(int value); + + QString getMountpoint() const; + void setMountPoint(const QString& value); + + QString getStreamName() const; + void setStreamName(const QString& value); + + QString getStreamDesc() const; + void setStreamDesc(const QString& value); + + QString getStreamGenre() const; + void setStreamGenre(const QString& value); + + bool getStreamPublic() const; + void setStreamPublic(bool value); + + QString getStreamWebsite() const; + void setStreamWebsite(const QString& value); + + bool getEnableMetadata() const; + void setEnableMetadata(bool value); + + QString getMetadataCharset() const; + void setMetadataCharset(const QString& value); + + QString getCustomArtist() const; + void setCustomArtist(const QString& value); + + QString getCustomTitle() const; + void setCustomTitle(const QString& value); + + QString getMetadataFormat() const; + void setMetadataFormat(const QString& value); + + bool getOggDynamicUpdate() const; + void setOggDynamicUpdate(bool value); + + signals: + void profileNameChanged(QString oldName, QString newName); + + private: + void adoptDefaultValues(); + bool loadValues(const QString& filename); + + QString m_profileName; + + bool m_enabled; + + QString m_host; + int m_port; + QString m_serverType; + QString m_login; + QString m_password; + + bool m_enableReconnect; + double m_reconnectPeriod; + bool m_limitReconnects; + int m_maximumRetries; + bool m_noDelayFirstReconnect; + double m_reconnectFirstDelay; + + QString m_mountpoint; + QString m_streamName; + QString m_streamDesc; + QString m_streamGenre; + bool m_streamPublic; + QString m_streamWebsite; + + QString m_format; + int m_bitrate; + int m_channels; + + bool m_enableMetadata; + QString m_metadataCharset; + QString m_customArtist; + QString m_customTitle; + QString m_metadataFormat; + bool m_oggDynamicUpdate; +}; + +#endif // BROADCASTPROFILE_H diff --git a/src/preferences/broadcastsettings.cpp b/src/preferences/broadcastsettings.cpp index 8f2185596d3..ec600bc48d5 100644 --- a/src/preferences/broadcastsettings.cpp +++ b/src/preferences/broadcastsettings.cpp @@ -1,417 +1,174 @@ -#include "preferences/broadcastsettings.h" +#include +#include +#include +#include + #include "broadcast/defs_broadcast.h" #include "defs_urls.h" +#include "preferences/broadcastsettings.h" +#include "util/logger.h" +#include "util/memory.h" namespace { const char* kConfigKey = "[Shoutcast]"; - -const char* kBitrate = "bitrate"; -const char* kChannels = "channels"; -const char* kCustomArtist = "custom_artist"; -const char* kCustomTitle = "custom_title"; -const char* kEnableMetadata = "enable_metadata"; -const char* kEnableReconnect = "enable_reconnect"; -const char* kEnabled = "enabled"; -const char* kFormat = "format"; -const char* kHost = "host"; -const char* kLimitReconnects = "limit_reconnects"; -const char* kLogin = "login"; -const char* kMaximumRetries = "maximum_retries"; -const char* kMetadataCharset = "metadata_charset"; -const char* kMetadataFormat = "metadata_format"; -const char* kMountPoint = "mountpoint"; -const char* kNoDelayFirstReconnect = "no_delay_first_reconnect"; -const char* kOggDynamicUpdate = "ogg_dynamicupdate"; -const char* kPassword = "password"; -const char* kPort = "port"; -const char* kReconnectFirstDelay = "reconnect_first_delay"; -const char* kReconnectPeriod = "reconnect_period"; -const char* kServertype = "servertype"; -const char* kStreamDesc = "stream_desc"; -const char* kStreamGenre = "stream_genre"; -const char* kStreamName = "stream_name"; -const char* kStreamPublic = "stream_public"; -const char* kStreamWebsite = "stream_website"; - -const double kDefaultBitrate = 128; -const int kDefaultChannels = 2; -const bool kDefaultEnableMetadata = false; -const bool kDefaultEnableReconnect = true; -const bool kDefaultLimitReconnects = true; -const int kDefaultMaximumRetries = 10; -// No tr() here, see https://bugs.launchpad.net/mixxx/+bug/1419500 -const QString kDefaultMetadataFormat("$artist - $title"); -const bool kDefaultNoDelayFirstReconnect = true; -const bool kDefaultOggDynamicupdate = false; -double kDefaultReconnectFirstDelay = 0.0; -double kDefaultReconnectPeriod = 5.0; -const QString kDefaultStreamDesc = QObject::tr("This stream is online for testing purposes!"); -const QString kDefaultStreamGenre = QObject::tr("Live Mix"); -const bool kDefaultStreamPublic = false; +const char* kCurrentProfile = "current_profile"; +const char* kProfilesSubfolder = "broadcast_profiles"; +const char* kDefaultProfile = "Default Profile"; +const mixxx::Logger kLogger("BroadcastSettings"); } // anonymous namespace -BroadcastSettings::BroadcastSettings(UserSettingsPointer pConfig) - : m_pConfig(pConfig) { -} - -int BroadcastSettings::getBitrate() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kBitrate), getDefaultBitrate()); -} - -void BroadcastSettings::setBitrate(int value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kBitrate), value); -} - -int BroadcastSettings::getDefaultBitrate() const { - return kDefaultBitrate; -} - -int BroadcastSettings::getChannels() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kChannels), getDefaultChannels()); -} - -void BroadcastSettings::setChannels(int value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kChannels), value); -} - -int BroadcastSettings::getDefaultChannels() const { - return kDefaultChannels; -} - -QString BroadcastSettings::getCustomArtist() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kCustomArtist), getDefaultCustomArtist()); - -} - -void BroadcastSettings::setCustomArtist(const QString& value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kCustomArtist), value); -} - -QString BroadcastSettings::getDefaultCustomArtist() const { - return QString(); -} - -QString BroadcastSettings::getCustomTitle() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kCustomTitle), getDefaultCustomTitle()); -} - -void BroadcastSettings::setCustomTitle(const QString& value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kCustomTitle), value); -} - -QString BroadcastSettings::getDefaultCustomTitle() const { - return QString(); -} - -bool BroadcastSettings::getEnableMetadata() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kEnableMetadata), getDefaultEnableMetadata()); -} - -void BroadcastSettings::setEnableMetadata(bool value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kEnableMetadata), value); -} - -bool BroadcastSettings::getDefaultEnableMetadata() const { - return kDefaultEnableMetadata; -} - -bool BroadcastSettings::getEnableReconnect() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kEnableReconnect), getDefaultEnableReconnect()); -} - -void BroadcastSettings::setEnableReconnect(bool value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kEnableReconnect), value); -} - -bool BroadcastSettings::getDefaultEnableReconnect() const { - return kDefaultEnableReconnect; -} - -// Unused, but we keep this to reserve the name -bool BroadcastSettings::getEnabled() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kEnabled), true); -} - -void BroadcastSettings::setEnabled(bool value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kEnabled), value); -} - -QString BroadcastSettings::getFormat() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kFormat), getDefaultFormat()); -} - -void BroadcastSettings::setFormat(const QString& value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kFormat), value); -} - -QString BroadcastSettings::getDefaultFormat() const { - return QString(); -} - -QString BroadcastSettings::getHost() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kHost), getDefaultHost()); -} - -void BroadcastSettings::setHost(const QString& value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kHost), value); -} - -QString BroadcastSettings::getDefaultHost() const { - return QString(); -} - -bool BroadcastSettings::getLimitReconnects() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kLimitReconnects), getDefaultLimitReconnects()); -} - -void BroadcastSettings::setLimitReconnects(bool value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kLimitReconnects), value); -} - -bool BroadcastSettings::getDefaultLimitReconnects() const { - return kDefaultLimitReconnects; -} - -QString BroadcastSettings::getLogin() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kLogin), getDefaultLogin()); -} - -void BroadcastSettings::setLogin(const QString& value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kLogin), value); -} - -QString BroadcastSettings::getDefaultLogin() const { - return QString(); -} - -int BroadcastSettings::getMaximumRetries() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kMaximumRetries), getDefaultMaximumRetries()); -} - -void BroadcastSettings::setMaximumRetries(int value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kMaximumRetries), value); -} - -int BroadcastSettings::getDefaultMaximumRetries() const { - return kDefaultMaximumRetries; -} - -QString BroadcastSettings::getMetadataCharset() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kMetadataCharset)); -} - -void BroadcastSettings::setMetadataCharset(const QString& value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kMetadataCharset), value); -} - -QString BroadcastSettings::getDefaultMetadataCharset() const { - return QString(); -} - -QString BroadcastSettings::getMetadataFormat() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kMetadataFormat), getDefaultMetadataFormat()); -} - -void BroadcastSettings::setMetadataFormat(const QString& value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kMetadataFormat), value); -} - -QString BroadcastSettings::getDefaultMetadataFormat() const { - return kDefaultMetadataFormat; -} - -QString BroadcastSettings::getMountpoint() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kMountPoint)); -} - -void BroadcastSettings::setMountPoint(const QString& value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kMountPoint), value); -} - -QString BroadcastSettings::getDefaultMountpoint() const { - return QString(); -} - -bool BroadcastSettings::getNoDelayFirstReconnect() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kNoDelayFirstReconnect), - getDefaultNoDelayFirstReconnect()); -} - -void BroadcastSettings::setNoDelayFirstReconnect(bool value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kNoDelayFirstReconnect), value); -} - -bool BroadcastSettings::getDefaultNoDelayFirstReconnect() const { - return kDefaultNoDelayFirstReconnect; +BroadcastSettings::BroadcastSettings( + UserSettingsPointer pConfig, QObject* parent) + : QAbstractListModel(parent), + m_pConfig(pConfig), + m_profiles() { + setHeaderData(0, Qt::Horizontal, QObject::tr("Name"), Qt::DisplayRole); + loadProfiles(); } -bool BroadcastSettings::getOggDynamicUpdate() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kOggDynamicUpdate), - getDefaultOggDynamicUpdate()); -} - -void BroadcastSettings::setOggDynamicUpdate(bool value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kOggDynamicUpdate), value); -} - -bool BroadcastSettings::getDefaultOggDynamicUpdate() const { - return kDefaultOggDynamicupdate; -} - -QString BroadcastSettings::getPassword() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kPassword), getDefaultPassword()); -} - -void BroadcastSettings::setPassword(const QString& value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kPassword), value); -} - -QString BroadcastSettings::getDefaultPassword() const { - return QString(); -} - -int BroadcastSettings::getPort() const { - // Valid port numbers are 0 .. 65535 (16 bit unsigned) - int port = m_pConfig->getValue( - ConfigKey(kConfigKey, kPort), getDefaultPort()); - if (port < 0 || port > 0xFFFF) { - return getDefaultPort(); +void BroadcastSettings::loadProfiles() { + QDir profilesFolder(getProfilesFolder()); + if(!profilesFolder.exists()) { + kLogger.info() << "Profiles folder doesn't exist. Creating it."; + profilesFolder.mkpath(profilesFolder.absolutePath()); } - return port; -} - -void BroadcastSettings::setPort(int value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kPort), value); -} - -int BroadcastSettings::getDefaultPort() const { - return BROADCAST_DEFAULT_PORT; -} -double BroadcastSettings::getReconnectFirstDelay() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kReconnectFirstDelay), - getDefaultReconnectFirstDelay()); -} - -void BroadcastSettings::setReconnectFirstDelay(double value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kReconnectFirstDelay), value); -} - -double BroadcastSettings::getDefaultReconnectFirstDelay() const { - return kDefaultReconnectFirstDelay; + QStringList nameFilters("*.bcp.xml"); + QFileInfoList files = + profilesFolder.entryInfoList(nameFilters, QDir::Files, QDir::Name); + + // If *.bcp.xml files exist in the profiles subfolder, those will be loaded + // and instanciated in the class' internal profile list for other by it and + // Mixxx subsystems related to Live Broadcasting. + // If that directory is empty (common reasons: it has been created by the + // code at the beginning, or all profiles were deleted) then a default + // profile with default values is created. That case could also mean that + // Mixxx has just been upgraded to a new version, so "legacy format" values + // has fetched from mixxx.cfg and loaded into the fresh default profile. + // It's important to take into account that the "legacy" settings are left + // in mixxx.cfg for retro-compatibility during alpha and beta testing. + + if(files.size() > 0) { + kLogger.info() << "Found " << files.size() << " profile(s)"; + + // Load profiles from filesystem + for(QFileInfo fileInfo : files) { + BroadcastProfilePtr profile = + BroadcastProfile::loadFromFile(fileInfo.absoluteFilePath()); + + if(profile) + addProfile(profile); + } + } else { + kLogger.info() << "No profiles found. Creating default profile."; + + BroadcastProfilePtr defaultProfile( + new BroadcastProfile(kDefaultProfile)); + // Upgrade from mixxx.cfg format to XML (if required) + loadLegacySettings(defaultProfile); + + addProfile(defaultProfile); + saveProfile(defaultProfile); + } } -double BroadcastSettings::getReconnectPeriod() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kReconnectPeriod), - getDefaultReconnectPeriod()); -} +void BroadcastSettings::addProfile(const BroadcastProfilePtr& profile) { + if(!profile) + return; -void BroadcastSettings::setReconnectPeriod(double value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kReconnectPeriod), value); -} + int position = m_profiles.size(); + beginInsertRows(QModelIndex(), position, position); -double BroadcastSettings::getDefaultReconnectPeriod() const { - return kDefaultReconnectPeriod; -} + // It is best to avoid using QSharedPointer::data(), especially when + // passing it to another function, as it puts the associated pointer + // at risk of being manually deleted. + // However it's fine with Qt's connect because it can be trusted that + // it won't delete the pointer. + connect(profile.data(), SIGNAL(profileNameChanged(QString, QString)), + this, SLOT(onProfileNameChanged(QString,QString))); + m_profiles.insert(profile->getProfileName(), BroadcastProfilePtr(profile)); -QString BroadcastSettings::getServertype() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kServertype), getDefaultServertype()); + endInsertRows(); } -void BroadcastSettings::setServertype(const QString& value) { - m_pConfig->set(ConfigKey(kConfigKey, kServertype), - ConfigValue(value)); -} +void BroadcastSettings::saveProfile(const BroadcastProfilePtr& profile) { + if(!profile) + return; -QString BroadcastSettings::getDefaultServertype() const { - return QString(); + profile->save(filePathForProfile(profile)); } -QString BroadcastSettings::getStreamDesc() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kStreamDesc), - getDefaultStreamDesc()); +QString BroadcastSettings::filePathForProfile(const QString& profileName) { + QString filename = profileName + ".bcp.xml"; + filename = BroadcastProfile::stripForbiddenChars(filename); + return QDir(getProfilesFolder()).absoluteFilePath(filename); } -void BroadcastSettings::setStreamDesc(const QString& value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kStreamDesc), value); -} +QString BroadcastSettings::filePathForProfile( + const BroadcastProfilePtr& profile) { + if(!profile) + return QString(); -QString BroadcastSettings::getDefaultStreamDesc() const { - return kDefaultStreamDesc; + return filePathForProfile(profile->getProfileName()); } -QString BroadcastSettings::getStreamGenre() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kStreamGenre), - getDefaultStreamGenre()); +QString BroadcastSettings::getProfilesFolder() { + QString profilesPath(m_pConfig->getSettingsPath()); + profilesPath.append(QDir::separator() + QString(kProfilesSubfolder)); + return profilesPath; } -void BroadcastSettings::setStreamGenre(const QString& value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kStreamGenre), value); +BroadcastProfilePtr BroadcastSettings::getProfileByName( + const QString& profileName) { + return m_profiles.value(profileName, BroadcastProfilePtr(nullptr)); } -QString BroadcastSettings::getDefaultStreamGenre() const { - return kDefaultStreamGenre; +void BroadcastSettings::saveAll() { + for(auto kv : m_profiles.values()) { + saveProfile(kv); + } } -QString BroadcastSettings::getStreamName() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kStreamName), - getDefaultStreamName()); -} +void BroadcastSettings::deleteProfile(const BroadcastProfilePtr& profile) { + if(!profile) + return; -void BroadcastSettings::setStreamName(const QString& value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kStreamName), value); -} + QFileInfo xmlFile(filePathForProfile(profile)); + if(xmlFile.exists()) + QFile::remove(xmlFile.absolutePath()); -QString BroadcastSettings::getDefaultStreamName() const { - return QString(); + int position = m_profiles.keys().indexOf(profile->getProfileName()); + if(position > -1) { + beginRemoveRows(QModelIndex(), position, position); + endRemoveRows(); + } + m_profiles.remove(profile->getProfileName()); } -bool BroadcastSettings::getStreamPublic() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kStreamPublic), getDefaultStreamPublic()); -} +void BroadcastSettings::onProfileNameChanged(QString oldName, QString newName) { + if(!m_profiles.contains(oldName)) + return; -void BroadcastSettings::setStreamPublic(bool value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kStreamPublic), value); + BroadcastProfilePtr oldItem = m_profiles.take(oldName); + m_profiles.insert(newName, oldItem); } -bool BroadcastSettings::getDefaultStreamPublic() const { - return kDefaultStreamPublic; +int BroadcastSettings::rowCount(const QModelIndex& parent) const { + return m_profiles.size(); } -QString BroadcastSettings::getStreamWebsite() const { - return m_pConfig->getValue( - ConfigKey(kConfigKey, kStreamWebsite), getDefaultStreamWebsite()); -} +QVariant BroadcastSettings::data(const QModelIndex& index, int role) const { + int rowIndex = index.row(); + if(!index.isValid() || rowIndex >= m_profiles.size()) + return QVariant(); -void BroadcastSettings::setStreamWebsite(const QString& value) { - m_pConfig->setValue(ConfigKey(kConfigKey, kStreamWebsite), value); + const BroadcastProfilePtr& profile = m_profiles.values().at(rowIndex); + if(profile && role == Qt::DisplayRole) + return profile->getProfileName(); + else + return QVariant(); } -QString BroadcastSettings::getDefaultStreamWebsite() const { - return MIXXX_WEBSITE_URL; +BroadcastProfilePtr BroadcastSettings::profileAt(int index) { + return m_profiles.values().value(index, BroadcastProfilePtr(nullptr)); } diff --git a/src/preferences/broadcastsettings.h b/src/preferences/broadcastsettings.h index 9c759bed5b3..00eb7067141 100644 --- a/src/preferences/broadcastsettings.h +++ b/src/preferences/broadcastsettings.h @@ -1,97 +1,46 @@ #ifndef PREFERENCES_BROADCASTSETTINGS_H #define PREFERENCES_BROADCASTSETTINGS_H +#include +#include +#include +#include +#include + #include "preferences/usersettings.h" #include "track/track.h" +#include "broadcastprofile.h" + +class BroadcastSettings : public QAbstractListModel { + Q_OBJECT -class BroadcastSettings { public: - BroadcastSettings(UserSettingsPointer pConfig); + BroadcastSettings(UserSettingsPointer pConfig, QObject* parent = nullptr); + + BroadcastProfilePtr getProfileByName(const QString& profileName); + void saveProfile(const BroadcastProfilePtr& profile); + void saveAll(); + void deleteProfile(const BroadcastProfilePtr& profile); - int getBitrate() const; - void setBitrate(int value); - int getDefaultBitrate() const; - int getChannels() const; - void setChannels(int value); - int getDefaultChannels() const; - QString getCustomArtist() const; - void setCustomArtist(const QString& value); - QString getDefaultCustomArtist() const; - QString getCustomTitle() const; - void setCustomTitle(const QString& value); - QString getDefaultCustomTitle() const; - bool getEnableMetadata() const; - void setEnableMetadata(bool value); - bool getDefaultEnableMetadata() const; - bool getEnableReconnect() const; - void setEnableReconnect(bool value); - bool getDefaultEnableReconnect() const; - bool getEnabled() const; - void setEnabled(bool value); - QString getFormat() const; - void setFormat(const QString& value); - QString getDefaultFormat() const; - QString getHost() const; - void setHost(const QString& value); - QString getDefaultHost() const; - bool getLimitReconnects() const; - void setLimitReconnects(bool value); - bool getDefaultLimitReconnects() const; - QString getLogin() const; - void setLogin(const QString& value); - QString getDefaultLogin() const; - int getMaximumRetries() const; - void setMaximumRetries(int value); - int getDefaultMaximumRetries() const; - QString getMetadataCharset() const; - void setMetadataCharset(const QString& value); - QString getDefaultMetadataCharset() const; - QString getMetadataFormat() const; - void setMetadataFormat(const QString& value); - QString getDefaultMetadataFormat() const; - QString getMountpoint() const; - void setMountPoint(const QString& value); - QString getDefaultMountpoint() const; - bool getNoDelayFirstReconnect() const; - void setNoDelayFirstReconnect(bool value); - bool getDefaultNoDelayFirstReconnect() const; - bool getOggDynamicUpdate() const; - void setOggDynamicUpdate(bool value); - bool getDefaultOggDynamicUpdate() const; - QString getPassword() const; - void setPassword(const QString& value); - QString getDefaultPassword() const; - int getPort() const; - void setPort(int value); - int getDefaultPort() const; - double getReconnectFirstDelay() const; - void setReconnectFirstDelay(double value); - double getDefaultReconnectFirstDelay() const; - double getReconnectPeriod() const; - void setReconnectPeriod(double value); - double getDefaultReconnectPeriod() const; - QString getServertype() const; - void setServertype(const QString& value); - QString getDefaultServertype() const; - QString getStreamDesc() const; - void setStreamDesc(const QString& value); - QString getDefaultStreamDesc() const; - QString getStreamGenre() const; - void setStreamGenre(const QString& value); - QString getDefaultStreamGenre() const; - QString getStreamName() const; - void setStreamName(const QString& value); - QString getDefaultStreamName() const; - bool getStreamPublic() const; - void setStreamPublic(bool value); - bool getDefaultStreamPublic() const; - QString getStreamWebsite() const; - void setStreamWebsite(const QString& value); - QString getDefaultStreamWebsite() const; + int rowCount(const QModelIndex& parent) const; + QVariant data(const QModelIndex& index, int role) const; + BroadcastProfilePtr profileAt(int index); + private slots: + void onProfileNameChanged(QString oldName, QString newName); private: + void loadProfiles(); + QString filePathForProfile(const BroadcastProfilePtr& profile); + QString filePathForProfile(const QString& profileName); + QString getProfilesFolder(); + void loadLegacySettings(const BroadcastProfilePtr& profile); + void addProfile(const BroadcastProfilePtr& profile); + // Pointer to config object UserSettingsPointer m_pConfig; + QMap m_profiles; }; +typedef QSharedPointer BroadcastSettingsPointer; + #endif /* PREFERENCES_BROADCASTSETTINGS_H */ diff --git a/src/preferences/broadcastsettings_legacy.cpp b/src/preferences/broadcastsettings_legacy.cpp new file mode 100644 index 00000000000..c8a931fd76b --- /dev/null +++ b/src/preferences/broadcastsettings_legacy.cpp @@ -0,0 +1,149 @@ +#include "preferences/broadcastsettings.h" + +namespace { +const char* kConfigKey = "[Shoutcast]"; +const char* kBitrate = "bitrate"; +const char* kChannels = "channels"; +const char* kCustomArtist = "custom_artist"; +const char* kCustomTitle = "custom_title"; +const char* kEnableMetadata = "enable_metadata"; +const char* kEnableReconnect = "enable_reconnect"; +const char* kEnabled = "enabled"; +const char* kFormat = "format"; +const char* kHost = "host"; +const char* kLimitReconnects = "limit_reconnects"; +const char* kLogin = "login"; +const char* kMaximumRetries = "maximum_retries"; +const char* kMetadataCharset = "metadata_charset"; +const char* kMetadataFormat = "metadata_format"; +const char* kMountPoint = "mountpoint"; +const char* kNoDelayFirstReconnect = "no_delay_first_reconnect"; +const char* kOggDynamicUpdate = "ogg_dynamicupdate"; +const char* kPassword = "password"; +const char* kPort = "port"; +const char* kReconnectFirstDelay = "reconnect_first_delay"; +const char* kReconnectPeriod = "reconnect_period"; +const char* kServertype = "servertype"; +const char* kStreamDesc = "stream_desc"; +const char* kStreamGenre = "stream_genre"; +const char* kStreamName = "stream_name"; +const char* kStreamPublic = "stream_public"; +const char* kStreamWebsite = "stream_website"; +} + +void BroadcastSettings::loadLegacySettings(const BroadcastProfilePtr& profile) { + if(!profile) + return; + + // For each value, the current value is kept if it can't be found in the + // general settings file. + profile->setEnabled(m_pConfig->getValue( + ConfigKey(kConfigKey, kEnabled), + profile->getEnabled())); + + profile->setHost(m_pConfig->getValue( + ConfigKey(kConfigKey, kHost), + profile->getHost())); + + profile->setPort(m_pConfig->getValue( + ConfigKey(kConfigKey, kPort), + profile->getPort())); + + profile->setServertype(m_pConfig->getValue( + ConfigKey(kConfigKey, kServertype), + profile->getServertype())); + + profile->setLogin(m_pConfig->getValue( + ConfigKey(kConfigKey, kLogin), + profile->getLogin())); + + profile->setPassword(m_pConfig->getValue( + ConfigKey(kConfigKey, kPassword), + profile->getPassword())); + + profile->setEnableReconnect(m_pConfig->getValue( + ConfigKey(kConfigKey, kEnableReconnect), + profile->getEnableReconnect())); + + profile->setReconnectPeriod(m_pConfig->getValue( + ConfigKey(kConfigKey, kReconnectPeriod), + profile->getReconnectPeriod())); + + profile->setLimitReconnects(m_pConfig->getValue( + ConfigKey(kConfigKey, kLimitReconnects), + profile->getLimitReconnects())); + + profile->setMountPoint(m_pConfig->getValue( + ConfigKey(kConfigKey, kMountPoint), + profile->getMountpoint())); + + profile->setStreamDesc(m_pConfig->getValue( + ConfigKey(kConfigKey, kStreamDesc), + profile->getStreamDesc())); + + profile->setStreamGenre(m_pConfig->getValue( + ConfigKey(kConfigKey, kStreamGenre), + profile->getStreamGenre())); + + profile->setStreamName(m_pConfig->getValue( + ConfigKey(kConfigKey, kStreamName), + profile->getStreamName())); + + profile->setStreamPublic(m_pConfig->getValue( + ConfigKey(kConfigKey, kStreamPublic), + profile->getStreamPublic())); + + profile->setStreamWebsite(m_pConfig->getValue( + ConfigKey(kConfigKey, kStreamWebsite), + profile->getStreamWebsite())); + + profile->setEnableMetadata(m_pConfig->getValue( + ConfigKey(kConfigKey, kEnableMetadata), + profile->getEnableMetadata())); + + profile->setMetadataCharset(m_pConfig->getValue( + ConfigKey(kConfigKey, kMetadataCharset), + profile->getMetadataCharset())); + + profile->setCustomArtist(m_pConfig->getValue( + ConfigKey(kConfigKey, kCustomArtist), + profile->getCustomArtist())); + + profile->setCustomTitle(m_pConfig->getValue( + ConfigKey(kConfigKey, kCustomTitle), + profile->getCustomTitle())); + + profile->setMetadataFormat(m_pConfig->getValue( + ConfigKey(kConfigKey, kMetadataFormat), + profile->getMetadataFormat())); + + profile->setOggDynamicUpdate(m_pConfig->getValue( + ConfigKey(kConfigKey, kOggDynamicUpdate), + profile->getOggDynamicUpdate())); + + profile->setBitrate(m_pConfig->getValue( + ConfigKey(kConfigKey, kBitrate), + profile->getBitrate())); + + profile->setChannels(m_pConfig->getValue( + ConfigKey(kConfigKey, kChannels), + profile->getChannels())); + + profile->setFormat(m_pConfig->getValue( + ConfigKey(kConfigKey, kFormat), + profile->getFormat())); + + profile->setNoDelayFirstReconnect( + m_pConfig->getValue( + ConfigKey(kConfigKey, kNoDelayFirstReconnect), + profile->getNoDelayFirstReconnect())); + + profile->setReconnectFirstDelay( + m_pConfig->getValue( + ConfigKey(kConfigKey, kReconnectFirstDelay), + profile->getReconnectFirstDelay())); + + profile->setMaximumRetries(m_pConfig->getValue( + ConfigKey(kConfigKey, kMaximumRetries), + profile->getMaximumRetries())); +} diff --git a/src/preferences/dialog/dlgprefbroadcast.cpp b/src/preferences/dialog/dlgprefbroadcast.cpp index f22c03c1b4f..ad515e65f5b 100644 --- a/src/preferences/dialog/dlgprefbroadcast.cpp +++ b/src/preferences/dialog/dlgprefbroadcast.cpp @@ -6,17 +6,20 @@ #include "preferences/dialog/dlgprefbroadcast.h" #include "encoder/encodersettings.h" -DlgPrefBroadcast::DlgPrefBroadcast(QWidget *parent, UserSettingsPointer _config) +DlgPrefBroadcast::DlgPrefBroadcast(QWidget *parent, + UserSettingsPointer _config, + BroadcastSettingsPointer pBroadcastSettings) : DlgPreferencePage(parent), - m_settings(_config) { + m_pBroadcastSettings(pBroadcastSettings) { setupUi(this); + const BroadcastProfilePtr& profile = m_pBroadcastSettings->profileAt(0); + m_pBroadcastEnabled = new ControlProxy( BROADCAST_PREF_KEY, "enabled", this); m_pBroadcastEnabled->connectValueChanged( SLOT(broadcastEnabledChanged(double))); - // Enable live broadcasting checkbox enableLiveBroadcasting->setChecked( m_pBroadcastEnabled->toBool()); @@ -26,31 +29,31 @@ DlgPrefBroadcast::DlgPrefBroadcast(QWidget *parent, UserSettingsPointer _config) comboBoxServerType->addItem(tr("Shoutcast 1"), BROADCAST_SERVER_SHOUTCAST); comboBoxServerType->addItem(tr("Icecast 1"), BROADCAST_SERVER_ICECAST1); - int tmp_index = comboBoxServerType->findData(m_settings.getServertype()); + int tmp_index = comboBoxServerType->findData(profile->getServertype()); if (tmp_index < 0) { // Set default if invalid. tmp_index = 0; } comboBoxServerType->setCurrentIndex(tmp_index); // Mountpoint - mountpoint->setText(m_settings.getMountpoint()); + mountpoint->setText(profile->getMountpoint()); // Host - host->setText(m_settings.getHost()); + host->setText(profile->getHost()); // Port - QString portString = QString::number(m_settings.getPort()); + QString portString = QString::number(profile->getPort()); port->setText(portString); // Login - login->setText(m_settings.getLogin()); + login->setText(profile->getLogin()); // Password - password->setText(m_settings.getPassword()); + password->setText(profile->getPassword()); // Enable automatic reconnect - bool enableReconnect = m_settings.getEnableReconnect(); + bool enableReconnect = profile->getEnableReconnect(); checkBoxEnableReconnect->setChecked(enableReconnect); widgetReconnectControls->setEnabled(enableReconnect); connect(checkBoxEnableReconnect, SIGNAL(stateChanged(int)), @@ -58,13 +61,13 @@ DlgPrefBroadcast::DlgPrefBroadcast(QWidget *parent, UserSettingsPointer _config) // Wait until first attempt - spinBoxFirstDelay->setValue(m_settings.getReconnectFirstDelay()); + spinBoxFirstDelay->setValue(profile->getReconnectFirstDelay()); // Retry Delay - spinBoxReconnectPeriod->setValue(m_settings.getReconnectPeriod()); + spinBoxReconnectPeriod->setValue(profile->getReconnectPeriod()); // Use Maximum Retries - bool limitConnects = m_settings.getLimitReconnects(); + bool limitConnects = profile->getLimitReconnects(); checkBoxLimitReconnects->setChecked( limitConnects); spinBoxMaximumRetries->setEnabled(limitConnects); @@ -72,23 +75,23 @@ DlgPrefBroadcast::DlgPrefBroadcast(QWidget *parent, UserSettingsPointer _config) this, SLOT(checkBoxLimitReconnectsChanged(int))); // Maximum Retries - spinBoxMaximumRetries->setValue(m_settings.getMaximumRetries()); + spinBoxMaximumRetries->setValue(profile->getMaximumRetries()); // Stream "public" checkbox - stream_public->setChecked(m_settings.getStreamPublic()); + stream_public->setChecked(profile->getStreamPublic()); // Stream name - stream_name->setText(m_settings.getStreamName()); + stream_name->setText(profile->getStreamName()); // Stream website - stream_website->setText(m_settings.getStreamWebsite()); + stream_website->setText(profile->getStreamWebsite()); // Stream description - stream_desc->setText(m_settings.getStreamDesc()); + stream_desc->setText(profile->getStreamDesc()); // Stream genre - stream_genre->setText(m_settings.getStreamGenre()); + stream_genre->setText(profile->getStreamGenre()); // Encoding bitrate combobox QString kbps_pattern = QString("%1 kbps"); @@ -110,7 +113,7 @@ DlgPrefBroadcast::DlgPrefBroadcast(QWidget *parent, UserSettingsPointer _config) kbps_pattern.arg(QString::number(kbps)), kbps); } - tmp_index = comboBoxEncodingBitrate->findData(m_settings.getBitrate()); + tmp_index = comboBoxEncodingBitrate->findData(profile->getBitrate()); if (tmp_index < 0) { tmp_index = comboBoxEncodingBitrate->findData(BROADCAST_BITRATE_128KBPS); } @@ -119,7 +122,7 @@ DlgPrefBroadcast::DlgPrefBroadcast(QWidget *parent, UserSettingsPointer _config) // Encoding format combobox comboBoxEncodingFormat->addItem(tr("MP3"), BROADCAST_FORMAT_MP3); comboBoxEncodingFormat->addItem(tr("Ogg Vorbis"), BROADCAST_FORMAT_OV); - tmp_index = comboBoxEncodingFormat->findData(m_settings.getFormat()); + tmp_index = comboBoxEncodingFormat->findData(profile->getFormat()); if (tmp_index < 0) { // Set default of MP3 if invalid. tmp_index = 0; @@ -133,23 +136,23 @@ DlgPrefBroadcast::DlgPrefBroadcast(QWidget *parent, UserSettingsPointer _config) static_cast(EncoderSettings::ChannelMode::MONO)); comboBoxEncodingChannels->addItem(tr("Stereo"), static_cast(EncoderSettings::ChannelMode::STEREO)); - tmp_index = comboBoxEncodingChannels->findData(m_settings.getChannels()); + tmp_index = comboBoxEncodingChannels->findData(profile->getChannels()); if (tmp_index < 0) { // Set default to automatic if invalid. tmp_index = 0; } comboBoxEncodingChannels->setCurrentIndex(tmp_index); // Metadata format - metadata_format->setText(m_settings.getMetadataFormat()); + metadata_format->setText(profile->getMetadataFormat()); // Static artist - custom_artist->setText(m_settings.getCustomArtist()); + custom_artist->setText(profile->getCustomArtist()); // Static title - custom_title->setText(m_settings.getCustomTitle()); + custom_title->setText(profile->getCustomTitle()); // "Enable static artist and title" checkbox - bool enableMetadata = m_settings.getEnableMetadata(); + bool enableMetadata = profile->getEnableMetadata(); enableCustomMetadata->setChecked(enableMetadata); custom_artist->setEnabled(enableMetadata); custom_title->setEnabled(enableMetadata); @@ -158,11 +161,11 @@ DlgPrefBroadcast::DlgPrefBroadcast(QWidget *parent, UserSettingsPointer _config) // "Enable UTF-8 metadata" checkbox // TODO(rryan): allow arbitrary codecs in the future? - QString charset = m_settings.getMetadataCharset(); + QString charset = profile->getMetadataCharset(); enableUtf8Metadata->setChecked(charset == "UTF-8"); // OGG "dynamicupdate" checkbox - ogg_dynamicupdate->setChecked(m_settings.getOggDynamicUpdate()); + ogg_dynamicupdate->setChecked(profile->getOggDynamicUpdate()); slotApply(); } @@ -171,42 +174,44 @@ DlgPrefBroadcast::~DlgPrefBroadcast() { } void DlgPrefBroadcast::slotResetToDefaults() { + BroadcastProfile dProfile("dontsave"); + // Make sure to keep these values in sync with the constructor. enableLiveBroadcasting->setChecked(false); comboBoxServerType->setCurrentIndex(0); - mountpoint->setText(m_settings.getDefaultMountpoint()); - host->setText(m_settings.getDefaultHost()); - int iPort = m_settings.getDefaultPort(); + mountpoint->setText(dProfile.getMountpoint()); + host->setText(dProfile.getHost()); + int iPort = dProfile.getPort(); VERIFY_OR_DEBUG_ASSERT(iPort != 0 && iPort <= 0xffff) { port->setText(QString()); } else { port->setText(QString::number(iPort)); } - login->setText(m_settings.getDefaultLogin()); - password->setText(m_settings.getDefaultPassword()); + login->setText(dProfile.getLogin()); + password->setText(dProfile.getPassword()); - checkBoxEnableReconnect->setChecked(m_settings.getDefaultEnableReconnect()); + checkBoxEnableReconnect->setChecked(dProfile.getEnableReconnect()); widgetReconnectControls->setEnabled(true); - spinBoxFirstDelay->setValue(m_settings.getDefaultReconnectFirstDelay()); - spinBoxReconnectPeriod->setValue(m_settings.getDefaultReconnectPeriod()); - checkBoxLimitReconnects->setChecked(m_settings.getDefaultLimitReconnects()); - spinBoxMaximumRetries->setValue(m_settings.getDefaultMaximumRetries()); + spinBoxFirstDelay->setValue(dProfile.getReconnectFirstDelay()); + spinBoxReconnectPeriod->setValue(dProfile.getReconnectPeriod()); + checkBoxLimitReconnects->setChecked(dProfile.getLimitReconnects()); + spinBoxMaximumRetries->setValue(dProfile.getMaximumRetries()); spinBoxMaximumRetries->setEnabled(true); - stream_name->setText(m_settings.getDefaultStreamName()); - stream_website->setText(m_settings.getDefaultStreamWebsite()); - stream_desc->setText(m_settings.getDefaultStreamDesc()); - stream_genre->setText(m_settings.getDefaultStreamGenre()); - stream_public->setChecked(m_settings.getDefaultStreamPublic()); - ogg_dynamicupdate->setChecked(m_settings.getDefaultOggDynamicUpdate()); + stream_name->setText(dProfile.getStreamName()); + stream_website->setText(dProfile.getStreamWebsite()); + stream_desc->setText(dProfile.getStreamDesc()); + stream_genre->setText(dProfile.getStreamGenre()); + stream_public->setChecked(dProfile.getStreamPublic()); + ogg_dynamicupdate->setChecked(dProfile.getOggDynamicUpdate()); comboBoxEncodingBitrate->setCurrentIndex(comboBoxEncodingBitrate->findData( - m_settings.getDefaultBitrate())); + dProfile.getBitrate())); comboBoxEncodingFormat->setCurrentIndex(0); comboBoxEncodingChannels->setCurrentIndex(0); enableUtf8Metadata->setChecked(false); enableCustomMetadata->setChecked(false); - metadata_format->setText(m_settings.getDefaultMetadataFormat()); - custom_artist->setText(m_settings.getDefaultCustomArtist()); - custom_title->setText(m_settings.getDefaultCustomTitle()); + metadata_format->setText(dProfile.getMetadataFormat()); + custom_artist->setText(dProfile.getCustomArtist()); + custom_title->setText(dProfile.getCustomTitle()); custom_artist->setEnabled(false); custom_title->setEnabled(false); } @@ -235,52 +240,56 @@ void DlgPrefBroadcast::slotApply() this->setEnabled(true); } + const BroadcastProfilePtr& profile = m_pBroadcastSettings->profileAt(0); + // Combo boxes, make sure to load their data not their display strings. - m_settings.setServertype(comboBoxServerType->itemData( + profile->setServertype(comboBoxServerType->itemData( comboBoxServerType->currentIndex()).toString()); - m_settings.setBitrate(comboBoxEncodingBitrate->itemData( + profile->setBitrate(comboBoxEncodingBitrate->itemData( comboBoxEncodingBitrate->currentIndex()).toInt()); - m_settings.setFormat(comboBoxEncodingFormat->itemData( + profile->setFormat(comboBoxEncodingFormat->itemData( comboBoxEncodingFormat->currentIndex()).toString()); - m_settings.setChannels(comboBoxEncodingChannels->itemData( + profile->setChannels(comboBoxEncodingChannels->itemData( comboBoxEncodingChannels->currentIndex()).toInt()); mountpoint->setText(mountpoint->text().trimmed()); - m_settings.setMountPoint(mountpoint->text()); - m_settings.setHost(host->text()); - m_settings.setPort(port->text().toInt()); - m_settings.setLogin(login->text()); - m_settings.setPassword(password->text()); - m_settings.setEnableReconnect(checkBoxEnableReconnect->isChecked()); - m_settings.setReconnectFirstDelay(spinBoxFirstDelay->value()); - m_settings.setReconnectPeriod(spinBoxReconnectPeriod->value()); - m_settings.setLimitReconnects(checkBoxLimitReconnects->isChecked()); - m_settings.setMaximumRetries(spinBoxMaximumRetries->value()); - m_settings.setStreamName(stream_name->text()); - m_settings.setStreamWebsite(stream_website->text()); - m_settings.setStreamDesc(stream_desc->toPlainText()); - m_settings.setStreamGenre(stream_genre->text()); - m_settings.setStreamPublic(stream_public->isChecked()); - m_settings.setOggDynamicUpdate(ogg_dynamicupdate->isChecked()); + profile->setMountPoint(mountpoint->text()); + profile->setHost(host->text()); + profile->setPort(port->text().toInt()); + profile->setLogin(login->text()); + profile->setPassword(password->text()); + profile->setEnableReconnect(checkBoxEnableReconnect->isChecked()); + profile->setReconnectFirstDelay(spinBoxFirstDelay->value()); + profile->setReconnectPeriod(spinBoxReconnectPeriod->value()); + profile->setLimitReconnects(checkBoxLimitReconnects->isChecked()); + profile->setMaximumRetries(spinBoxMaximumRetries->value()); + profile->setStreamName(stream_name->text()); + profile->setStreamWebsite(stream_website->text()); + profile->setStreamDesc(stream_desc->toPlainText()); + profile->setStreamGenre(stream_genre->text()); + profile->setStreamPublic(stream_public->isChecked()); + profile->setOggDynamicUpdate(ogg_dynamicupdate->isChecked()); QString charset = ""; if (enableUtf8Metadata->isChecked()) { charset = "UTF-8"; } - QString current_charset = m_settings.getMetadataCharset(); + QString current_charset = profile->getMetadataCharset(); // Only allow setting the config value if the current value is either empty // or "UTF-8". This way users can customize the charset to something else by // setting the value in their mixxx.cfg. Not sure if this will be useful but // it's good to leave the option open. if (current_charset.length() == 0 || current_charset == "UTF-8") { - m_settings.setMetadataCharset(charset); + profile->setMetadataCharset(charset); } - m_settings.setEnableMetadata(enableCustomMetadata->isChecked()); - m_settings.setCustomArtist(custom_artist->text()); - m_settings.setCustomTitle(custom_title->text()); - m_settings.setMetadataFormat(metadata_format->text()); + profile->setEnableMetadata(enableCustomMetadata->isChecked()); + profile->setCustomArtist(custom_artist->text()); + profile->setCustomTitle(custom_title->text()); + profile->setMetadataFormat(metadata_format->text()); + + m_pBroadcastSettings->saveAll(); } void DlgPrefBroadcast::broadcastEnabledChanged(double value) { diff --git a/src/preferences/dialog/dlgprefbroadcast.h b/src/preferences/dialog/dlgprefbroadcast.h index 2f1fc4d2420..4d31e430711 100644 --- a/src/preferences/dialog/dlgprefbroadcast.h +++ b/src/preferences/dialog/dlgprefbroadcast.h @@ -15,7 +15,9 @@ class ControlProxy; class DlgPrefBroadcast : public DlgPreferencePage, public Ui::DlgPrefBroadcastDlg { Q_OBJECT public: - DlgPrefBroadcast(QWidget *parent, UserSettingsPointer _config); + DlgPrefBroadcast(QWidget *parent, + UserSettingsPointer _config, + BroadcastSettingsPointer pBroadcastSettings); virtual ~DlgPrefBroadcast(); public slots: @@ -32,7 +34,7 @@ class DlgPrefBroadcast : public DlgPreferencePage, public Ui::DlgPrefBroadcastDl void apply(const QString &); private: - BroadcastSettings m_settings; + BroadcastSettingsPointer m_pBroadcastSettings; ControlProxy* m_pBroadcastEnabled; }; diff --git a/src/preferences/dialog/dlgpreferences.cpp b/src/preferences/dialog/dlgpreferences.cpp index e6382dfba55..bdf6c7f00e5 100644 --- a/src/preferences/dialog/dlgpreferences.cpp +++ b/src/preferences/dialog/dlgpreferences.cpp @@ -62,8 +62,9 @@ DlgPreferences::DlgPreferences(MixxxMainWindow * mixxx, SkinLoader* pSkinLoader, SoundManager * soundman, PlayerManager* pPlayerManager, ControllerManager * controllers, VinylControlManager *pVCManager, EffectsManager* pEffectsManager, - UserSettingsPointer pConfig, Library *pLibrary) - : m_pConfig(pConfig), + SettingsManager* pSettingsManager, + Library *pLibrary) + : m_pConfig(pSettingsManager->settings()), m_pageSizeHint(QSize(0, 0)), m_preferencesUpdated(ConfigKey("[Preferences]", "updated"), false) { setupUi(this); @@ -123,7 +124,8 @@ DlgPreferences::DlgPreferences(MixxxMainWindow * mixxx, SkinLoader* pSkinLoader, m_wrecord = new DlgPrefRecord(this, m_pConfig); addPageWidget(m_wrecord); #ifdef __BROADCAST__ - m_wbroadcast = new DlgPrefBroadcast(this, m_pConfig); + m_wbroadcast = new DlgPrefBroadcast(this, m_pConfig, + pSettingsManager->broadcastSettings()); addPageWidget(m_wbroadcast); #endif #ifdef __MODPLUG__ diff --git a/src/preferences/dialog/dlgpreferences.h b/src/preferences/dialog/dlgpreferences.h index d90cb53c8f0..be59bce92ab 100644 --- a/src/preferences/dialog/dlgpreferences.h +++ b/src/preferences/dialog/dlgpreferences.h @@ -27,6 +27,7 @@ #include "preferences/usersettings.h" #include "control/controlpushbutton.h" #include "preferences/dlgpreferencepage.h" +#include "preferences/settingsmanager.h" class MixxxMainWindow; class SoundManager; @@ -63,7 +64,7 @@ class DlgPreferences : public QDialog, public Ui::DlgPreferencesDlg { DlgPreferences(MixxxMainWindow* mixxx, SkinLoader* pSkinLoader, SoundManager* soundman, PlayerManager* pPlayerManager, ControllerManager* controllers, VinylControlManager* pVCManager, EffectsManager* pEffectsManager, - UserSettingsPointer pConfig, Library *pLibrary); + SettingsManager* pSettingsManager, Library *pLibrary); virtual ~DlgPreferences(); void addPageWidget(DlgPreferencePage* pWidget); diff --git a/src/preferences/settingsmanager.cpp b/src/preferences/settingsmanager.cpp index bd5d2188bbc..c5b531ed332 100644 --- a/src/preferences/settingsmanager.cpp +++ b/src/preferences/settingsmanager.cpp @@ -28,6 +28,11 @@ SettingsManager::SettingsManager(QObject* pParent, initializeDefaults(); ControlDoublePrivate::setUserConfig(m_pSettings); + + VERIFY_OR_DEBUG_ASSERT(!m_pBroadcastSettings.isNull()) { + m_pBroadcastSettings = BroadcastSettingsPointer( + new BroadcastSettings(m_pSettings)); + } } SettingsManager::~SettingsManager() { diff --git a/src/preferences/settingsmanager.h b/src/preferences/settingsmanager.h index 79880fdc5dd..d45ba67754c 100644 --- a/src/preferences/settingsmanager.h +++ b/src/preferences/settingsmanager.h @@ -2,8 +2,10 @@ #define PREFERENCES_SETTINGSMANAGER_H #include +#include #include +#include "preferences/broadcastsettings.h" #include "preferences/usersettings.h" class SettingsManager : public QObject { @@ -16,6 +18,10 @@ class SettingsManager : public QObject { return m_pSettings; } + BroadcastSettingsPointer broadcastSettings() const { + return m_pBroadcastSettings; + } + void save() { m_pSettings->save(); } @@ -29,7 +35,7 @@ class SettingsManager : public QObject { UserSettingsPointer m_pSettings; bool m_bShouldRescanLibrary; + BroadcastSettingsPointer m_pBroadcastSettings; }; - #endif /* PREFERENCES_SETTINGSMANAGER_H */ diff --git a/src/test/broadcastprofile_test.cpp b/src/test/broadcastprofile_test.cpp new file mode 100644 index 00000000000..072e1263fa8 --- /dev/null +++ b/src/test/broadcastprofile_test.cpp @@ -0,0 +1,195 @@ +#include +#include + +#include "test/mixxxtest.h" +#include "preferences/broadcastprofile.h" +#include "broadcast/defs_broadcast.h" +#include "defs_urls.h" + +namespace { + +TEST(BroadcastProfileTest, ConstructWithName) { + // instanciate BroadcastProfile with a specific name and + // assert its case-sensitive equality when getting it from getProfileName + QString name("unit testing profile"); + BroadcastProfile profile(name); + ASSERT_TRUE(profile.getProfileName() == name); +} + +TEST(BroadcastProfileTest, ForbiddenChars) { + // Test if validName works properly with valid values + ASSERT_TRUE(BroadcastProfile::validName("Default Profile")); + ASSERT_TRUE(BroadcastProfile::validName("This is a profile")); + ASSERT_TRUE(BroadcastProfile::validName("Radio1 - MP3 128k")); + ASSERT_TRUE(BroadcastProfile::validName("Radio1 - AACplus 96k")); + + // Test if validName works properly with invalid values + ASSERT_FALSE(BroadcastProfile::validName("Radio1 / MP3 128k")); + ASSERT_FALSE(BroadcastProfile::validName("Radio1 \\ AAC+ 96k")); + ASSERT_FALSE(BroadcastProfile::validName("Radio1 | OGG 96k")); + ASSERT_FALSE(BroadcastProfile::validName("Hello ?")); + ASSERT_FALSE(BroadcastProfile::validName("3 * 3")); + ASSERT_FALSE(BroadcastProfile::validName("Here it is: a profile")); + ASSERT_FALSE(BroadcastProfile::validName("Scrolltext")); + ASSERT_FALSE(BroadcastProfile::validName("It's called a \"profile\".")); + + // Test if forbidden chars are properly stripped + ASSERT_FALSE(BroadcastProfile::stripForbiddenChars( + "This is an invalid profile name: ?/").contains("?")); +} + +TEST(BroadcastProfileTest, SaveAndLoadXML) { + // Preliminary: set a discriminating value in one of the profile fields + QString streamName("unit testing in progress"); + + BroadcastProfile profile("Unit Testing Profile"); + profile.setStreamName(streamName); + + QString filename = profile.getProfileName() + QString(".bcp.xml"); + + // Call save() on a profile and assert it actually exists + QFile::remove(filename); // First, make sure it doesn't exists + profile.save(filename); + ASSERT_TRUE(QFile::exists(filename)); + + // Load XML file using static loadFromFile and assert + // the discriminating value is present + BroadcastProfilePtr savedProfile = BroadcastProfile::loadFromFile(filename); + ASSERT_NE(savedProfile, nullptr); + ASSERT_TRUE(savedProfile->getStreamName() == streamName); +} + +TEST(BroadcastProfileTest, SetGetValues) { + // For each attribute: + // - use its setter to set a specific value + // - use its getter to check if the specific value has been set + + BroadcastProfile profile("Get and set values Test"); + + // Long method ahead! + QString profileName("new name"); + profile.setProfileName(profileName); + ASSERT_TRUE(profile.getProfileName() == profileName); + + bool enabled = false; + profile.setEnabled(enabled); + ASSERT_EQ(profile.getEnabled(), enabled); + + QString hostname("streaming.website.com"); + profile.setHost(hostname); + ASSERT_TRUE(profile.getHost() == hostname); + + int port = 1238; + profile.setPort(port); + ASSERT_EQ(profile.getPort(), port); + + QString servertype("Shoutcast 1"); + profile.setServertype(servertype); + ASSERT_TRUE(profile.getServertype() == servertype); + + QString login("myusername"); + profile.setLogin(login); + ASSERT_TRUE(profile.getLogin() == login); + + QString password("changemepassword"); + profile.setPassword(password); + ASSERT_TRUE(profile.getPassword() == password); + + bool enableReconnect = false; + profile.setEnableReconnect(enableReconnect); + ASSERT_EQ(profile.getEnableReconnect(), enableReconnect); + + double reconnectPeriod = 3.14; + profile.setReconnectPeriod(reconnectPeriod); + ASSERT_EQ(profile.getReconnectPeriod(), reconnectPeriod); + + bool limitReconnects = false; + profile.setLimitReconnects(limitReconnects); + ASSERT_EQ(profile.getLimitReconnects(), limitReconnects); + + int maximumRetries = 3; + profile.setMaximumRetries(maximumRetries); + ASSERT_EQ(profile.getMaximumRetries(), maximumRetries); + + bool noDelayFirstReconnect = false; + profile.setNoDelayFirstReconnect(noDelayFirstReconnect); + ASSERT_EQ(profile.getNoDelayFirstReconnect(), noDelayFirstReconnect); + + double reconnectFirstDelay = 6.28; + profile.setReconnectFirstDelay(reconnectFirstDelay); + ASSERT_EQ(profile.getReconnectFirstDelay(), reconnectFirstDelay); + + QString format("Ogg Vorbis"); + profile.setFormat(format); + ASSERT_TRUE(profile.getFormat() == format); + + int bitrate = 320; + profile.setBitrate(bitrate); + ASSERT_EQ(profile.getBitrate(), bitrate); + + int channels = 1; + profile.setChannels(channels); + ASSERT_EQ(profile.getChannels(), channels); + + QString mountpoint("stream.ogg"); + profile.setMountPoint(mountpoint); + ASSERT_TRUE(profile.getMountpoint() == mountpoint); + + QString streamName("This is a test stream"); + profile.setStreamName(streamName); + ASSERT_TRUE(profile.getStreamName() == streamName); + + QString streamDesc("this is a stream description"); + profile.setStreamDesc(streamDesc); + ASSERT_TRUE(profile.getStreamDesc() == streamDesc); + + QString streamGenre("unit testing"); + profile.setStreamGenre(streamGenre); + ASSERT_TRUE(profile.getStreamGenre() == streamGenre); + + bool streamPublic = true; + profile.setStreamPublic(streamPublic); + ASSERT_EQ(profile.getStreamPublic(), streamPublic); + + QString streamWebsite("www.website.com"); + profile.setStreamWebsite(streamWebsite); + ASSERT_TRUE(profile.getStreamWebsite() == streamWebsite); + + bool enableMetadata = true; + profile.setEnableMetadata(enableMetadata); + ASSERT_EQ(profile.getEnableMetadata(), enableMetadata); + + QString metadataCharset("UTF-8"); + profile.setMetadataCharset(metadataCharset); + ASSERT_TRUE(profile.getMetadataCharset() == metadataCharset); + + QString customArtist("this is an artist"); + profile.setCustomArtist(customArtist); + ASSERT_TRUE(profile.getCustomArtist() == customArtist); + + QString customTitle("this is a title"); + profile.setCustomTitle(customTitle); + ASSERT_TRUE(profile.getCustomTitle() == customTitle); + + QString metadataFormat("no particular format"); + profile.setMetadataFormat(metadataFormat); + ASSERT_TRUE(profile.getMetadataFormat() == metadataFormat); + + bool oggDynamicUpdate = true; + profile.setOggDynamicUpdate(oggDynamicUpdate); + ASSERT_EQ(profile.getOggDynamicUpdate(), oggDynamicUpdate); +} + +TEST(BroadcastProfileTest, DefaultValues) { + BroadcastProfile profile("Unit Testing Default Values"); + + // Check if some select attributes have non-empty default values + ASSERT_TRUE(profile.getEnabled()); + ASSERT_EQ(profile.getPort(), BROADCAST_DEFAULT_PORT); + ASSERT_TRUE(profile.getStreamWebsite() == QString(MIXXX_WEBSITE_URL)); + ASSERT_FALSE(profile.getStreamDesc().isEmpty()); + ASSERT_FALSE(profile.getStreamGenre().isEmpty()); + ASSERT_FALSE(profile.getMetadataFormat().isEmpty()); +} + +} // namespace