diff --git a/README b/README index 3697bef9c..daac027a2 100644 --- a/README +++ b/README @@ -30,12 +30,20 @@ QJson (Qt JSON library) $ ./configure && make $ sudo make install +libEchonest 0.1 +--------------- + + $ git clone git://git.kde.org/libechonest.git + $ cd libechonest + $ mkdir build && cd build + $ cmake .. + $ make + $ sudo make install Now compile Tomahawk ------------------- $ sudo ldconfig -v | grep -Ei 'qjson|gloox' - $ mkdir build - $ cd build + $ mkdir build && cd build $ cmake .. $ make $ ./tomahawk diff --git a/include/tomahawk/infosystem.h b/include/tomahawk/infosystem.h new file mode 100644 index 000000000..983305c47 --- /dev/null +++ b/include/tomahawk/infosystem.h @@ -0,0 +1,148 @@ +#ifndef TOMAHAWK_INFOSYSTEM_H +#define TOMAHAWK_INFOSYSTEM_H + +#include +#include +#include +#include +#include +#include +#include + + +namespace Tomahawk { + +namespace InfoSystem { + +enum InfoType { + InfoTrackID, + InfoTrackArtist, + InfoTrackAlbum, + InfoTrackGenre, + InfoTrackComposer, + InfoTrackDate, + InfoTrackNumber, + InfoTrackDiscNumber, + InfoTrackBitRate, + InfoTrackLength, + InfoTrackSampleRate, + InfoTrackFileSize, + InfoTrackBPM, + InfoTrackReplayGain, + InfoTrackReplayPeakGain, + InfoTrackLyrics, + InfoTrackLocation, + InfoTrackProfile, + InfoTrackEnergy, + InfoTrackDanceability, + InfoTrackTempo, + InfoTrackLoudness, + + InfoArtistID, + InfoArtistName, + InfoArtistBiography, + InfoArtistBlog, + InfoArtistFamiliarity, + InfoArtistHotttness, + InfoArtistImages, + InfoArtistNews, + InfoArtistProfile, + InfoArtistReviews, + InfoArtistSongs, + InfoArtistSimilars, + InfoArtistTerms, + InfoArtistLinks, + InfoArtistVideos, + + InfoAlbumID, + InfoAlbumName, + InfoAlbumArtist, + InfoAlbumDate, + InfoAlbumGenre, + InfoAlbumComposer, + InfoMiscTopHotttness, + InfoMiscTopTerms, + + InfoNoInfo +}; + +typedef QMap< InfoType, QVariant > InfoMap; +typedef QMap< QString, QMap< QString, QString > > InfoGenericMap; +typedef QHash InfoCustomDataHash; +typedef QHash MusixMatchHash; + +class InfoPlugin : public QObject +{ + Q_OBJECT + +public: + InfoPlugin(QObject *parent) + :QObject(parent) + { + qDebug() << Q_FUNC_INFO; + } + ~InfoPlugin() + { + qDebug() << Q_FUNC_INFO; + } + + virtual void getInfo(const QString &caller, const InfoType type, const QVariant &data, Tomahawk::InfoSystem::InfoCustomDataHash customData) = 0; + +signals: + void info(QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData); + void finished(QString, Tomahawk::InfoSystem::InfoType); + +protected: + InfoType m_type; +}; + +typedef QWeakPointer< InfoPlugin > InfoPluginPtr; + +class InfoSystem : public QObject +{ + Q_OBJECT + +public: + + + InfoSystem(QObject *parent); + ~InfoSystem() + { + qDebug() << Q_FUNC_INFO; + } + + void registerInfoTypes(const InfoPluginPtr &plugin, const QSet< InfoType > &types); + + void getInfo(const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData); + void getInfo(const QString &caller, const InfoMap &input, InfoCustomDataHash customData); + +signals: + void info(QString caller, Tomahawk::InfoSystem::InfoType, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData); + void finished(QString target); + +public slots: + void infoSlot(QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData); + void finishedSlot(QString target,Tomahawk::InfoSystem::InfoType type); + +private: + + QLinkedList< InfoPluginPtr > determineOrderedMatches(const InfoType type) const; + + QMap< InfoType, QLinkedList > m_infoMap; + + // For now, statically instantiate plugins; this is just somewhere to keep them + QLinkedList m_plugins; + + QHash< QString, QHash< Tomahawk::InfoSystem::InfoType, int > > m_dataTracker; + +}; + +} + +} + +Q_DECLARE_METATYPE(Tomahawk::InfoSystem::InfoGenericMap) +Q_DECLARE_METATYPE(Tomahawk::InfoSystem::InfoCustomDataHash); +Q_DECLARE_METATYPE(Tomahawk::InfoSystem::MusixMatchHash) + +#endif // TOMAHAWK_INFOSYSTEM_H diff --git a/include/tomahawk/tomahawkapp.h b/include/tomahawk/tomahawkapp.h index d60f8a5b7..32df60925 100644 --- a/include/tomahawk/tomahawkapp.h +++ b/include/tomahawk/tomahawkapp.h @@ -29,9 +29,18 @@ class Database; class Jabber; +class XMPPBot; class TomahawkZeroconf; class TomahawkSettings; +namespace Tomahawk +{ + namespace InfoSystem + { + class InfoSystem; + } +} + #ifndef TOMAHAWK_HEADLESS class AudioEngine; class TomahawkWindow; @@ -64,6 +73,9 @@ public: SourceList& sourcelist() { return m_sources; } Servent& servent() { return m_servent; } QNetworkAccessManager* nam() { return m_nam; } + QNetworkProxy* proxy() { return m_proxy; } + Tomahawk::InfoSystem::InfoSystem* infoSystem() { return m_infoSystem; } + XMPPBot* xmppBot() { return m_xmppBot; } const QString& nodeID() const; #ifndef TOMAHAWK_HEADLESS @@ -114,6 +126,7 @@ private: SourceList m_sources; TomahawkZeroconf* m_zeroconf; QSharedPointer m_jabber; + XMPPBot* m_xmppBot; #ifndef TOMAHAWK_HEADLESS TomahawkWindow* m_mainwindow; @@ -129,6 +142,9 @@ private: TomahawkSettings* m_settings; QNetworkAccessManager* m_nam; + QNetworkProxy* m_proxy; + + Tomahawk::InfoSystem::InfoSystem* m_infoSystem; QxtHttpServerConnector m_connector; QxtHttpSessionManager m_session; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ef29179dc..cdfc7d2d1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,6 +36,10 @@ SET( tomahawkSources ${tomahawkSources} utils/tomahawkutils.cpp jabber/jabber_p.cpp + infosystem/infosystem.cpp + infosystem/infoplugins/echonestplugin.cpp + infosystem/infoplugins/musixmatchplugin.cpp + bufferiodevice.cpp connection.cpp msgprocessor.cpp @@ -73,6 +77,8 @@ SET( tomahawkSources ${tomahawkSources} database/databasecommand_updatesearchindex.cpp database/databasecollection.cpp + xmppbot/xmppbot.cpp + web/api_v1.cpp tomahawksettings.cpp @@ -139,6 +145,8 @@ SET( tomahawkHeaders ${tomahawkHeaders} "${TOMAHAWK_INC_DIR}/tomahawk/track.h" "${TOMAHAWK_INC_DIR}/tomahawk/playlist.h" + "${TOMAHAWK_INC_DIR}/tomahawk/infosystem.h" + "${TOMAHAWK_INC_DIR}/tomahawk/functimeout.h" # "${TOMAHAWK_INC_DIR}/tomahawk/tomahawkplugin.h" @@ -170,6 +178,9 @@ SET( tomahawkHeaders ${tomahawkHeaders} jabber/jabber.h jabber/jabber_p.h + infosystem/infoplugins/echonestplugin.h + infosystem/infoplugins/musixmatchplugin.h + bufferiodevice.h connection.h msgprocessor.h @@ -183,6 +194,8 @@ SET( tomahawkHeaders ${tomahawkHeaders} scriptresolver.h tomahawksettings.h + xmppbot/xmppbot.h + web/api_v1.h ) @@ -231,12 +244,13 @@ SET( tomahawkHeadersGui ${tomahawkHeadersGui} ) SET( tomahawkUI ${tomahawkUI} - tomahawkwindow.ui - settingsdialog.ui + tomahawkwindow.ui + settingsdialog.ui + proxydialog.ui - audiocontrols.ui - sourcetree/sourcetreeitemwidget.ui - topbar/topbar.ui + audiocontrols.ui + sourcetree/sourcetreeitemwidget.ui + topbar/topbar.ui ) INCLUDE_DIRECTORIES( @@ -258,6 +272,7 @@ INCLUDE_DIRECTORIES( /usr/include/taglib /usr/local/include/taglib + /usr/local/include/echonest /usr/local/include ) @@ -304,6 +319,7 @@ TARGET_LINK_LIBRARIES( tomahawk ${QT_LIBRARIES} ${MAC_EXTRA_LIBS} ${OS_SPECIFIC_LINK_LIBRARIES} + echonest portfwd ) diff --git a/src/CMakeLists.unix.txt b/src/CMakeLists.unix.txt index a2d31ad89..19a6ee285 100644 --- a/src/CMakeLists.unix.txt +++ b/src/CMakeLists.unix.txt @@ -25,4 +25,4 @@ ELSE() SET( tomahawkSourcesGui ${tomahawkSourcesGui} audio/vorbistranscode.cpp scrobbler.cpp ) SET( tomahawkHeadersGui ${tomahawkHeadersGui} audio/vorbistranscode.h scrobbler.h ) -ENDIF() \ No newline at end of file +ENDIF() diff --git a/src/CPack.txt b/src/CPack.txt index fac0a04c5..46174a970 100644 --- a/src/CPack.txt +++ b/src/CPack.txt @@ -13,8 +13,8 @@ SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "rj@tomahawk.org") SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/../README") SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/../LICENSE.txt") SET(CPACK_PACKAGE_VERSION_MAJOR "0") -SET(CPACK_PACKAGE_VERSION_MINOR "1") -SET(CPACK_PACKAGE_VERSION_PATCH "3") +SET(CPACK_PACKAGE_VERSION_MINOR "0") +SET(CPACK_PACKAGE_VERSION_PATCH "1") SET(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") #SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "i386") # Default: Output of dpkg --print-architecture or i386 @@ -51,7 +51,7 @@ ENDIF(WIN32 AND NOT UNIX) # Nsis only? SET(CPACK_PACKAGE_EXECUTABLES "tomahawk" "tomahawk") #gnutls is in here because gloox needs it, and we link statically to gloox: -SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libqtgui4 (>=4:4.6.2-0ubuntu5), libtag1c2a (>=1.6.2-0ubuntu1), liblastfm-dev (>=0.4.0~git20090710-1), libqt4-sql-sqlite (>=4:4.6.2-0ubuntu5), libvorbis0a (>=1.2.3-3ubuntu1), libmad0 (>=0.15.1b-4ubuntu1), libasound2 (>=1.0.22-0ubuntu7), zlib1g (>=1:1.2.3.3.dfsg-15ubuntu1), libqjson-dev (>=0.7.1-1), libgnutls26 (>= 2.7.14-0)") +SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libqtgui4 (>=4:4.7.0-0ubuntu1), libtag1c2a (>=1.6.2-0ubuntu1), liblastfm-dev (>=0.4.0~really0.3.3-0ubuntu1), libqt4-sql-sqlite (>=4:4.7.0-0ubuntu1), libvorbis0a (>=1.2.3-3ubuntu1), libmad0 (>=0.15.1b-4ubuntu1), libasound2 (>=1.0.22-0ubuntu7), zlib1g (>=1:1.2.3.3.dfsg-15ubuntu1), libqjson-dev (>=0.7.1-1), libgnutls26 (>= 2.7.14-0), libgloox8 (>=1.0-1)") #SET(CPACK_DEBIAN_PACKAGE_SECTION "music") diff --git a/src/infosystem/infoplugins/echonestplugin.cpp b/src/infosystem/infoplugins/echonestplugin.cpp new file mode 100644 index 000000000..5dc0c2c4f --- /dev/null +++ b/src/infosystem/infoplugins/echonestplugin.cpp @@ -0,0 +1,260 @@ +#include "tomahawk/infosystem.h" +#include "tomahawk/tomahawkapp.h" +#include "echonestplugin.h" +#include +#include + +using namespace Tomahawk::InfoSystem; +using namespace Echonest; + +// for internal neatness + +EchoNestPlugin::EchoNestPlugin(QObject *parent) + : InfoPlugin(parent) +{ + qDebug() << Q_FUNC_INFO; + Config::instance()->setAPIKey("JGJCRKWLXLBZIFAZB"); + QSet< InfoType > supportedTypes; + supportedTypes << Tomahawk::InfoSystem::InfoArtistBiography << Tomahawk::InfoSystem::InfoArtistFamiliarity << Tomahawk::InfoSystem::InfoArtistHotttness << Tomahawk::InfoSystem::InfoArtistTerms << Tomahawk::InfoSystem::InfoMiscTopTerms; + qobject_cast< InfoSystem* >(parent)->registerInfoTypes(this, supportedTypes); +} + +EchoNestPlugin::~EchoNestPlugin() +{ + qDebug() << Q_FUNC_INFO; +} + +void EchoNestPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash customData) +{ + switch (type) + { + case Tomahawk::InfoSystem::InfoArtistBiography: + return getArtistBiography(caller, data, customData); + case Tomahawk::InfoSystem::InfoArtistFamiliarity: + return getArtistFamiliarity(caller, data, customData); + case Tomahawk::InfoSystem::InfoArtistHotttness: + return getArtistHotttnesss(caller, data, customData); + case Tomahawk::InfoSystem::InfoArtistTerms: + return getArtistTerms(caller, data, customData); + case Tomahawk::InfoSystem::InfoTrackEnergy: + return getSongProfile(caller, data, customData, "energy"); + case Tomahawk::InfoSystem::InfoMiscTopTerms: + return getMiscTopTerms(caller, data, customData); + default: + { + emit info(caller, Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + return; + } + } +} + +void EchoNestPlugin::getSongProfile(const QString &caller, const QVariant& data, InfoCustomDataHash &customData, const QString &item) +{ + //WARNING: Totally not implemented yet + + if( !isValidTrackData( caller, data, customData ) ) + return; + +// Track track( data.toString() ); +// Artist artist( customData.data()->property("artistName").toString() ); +// reply->setProperty("artist", QVariant::fromValue(artist)); +// reply->setProperty( "data", data ); +// m_replyMap[reply] = customData; +// connect(reply, SIGNAL(finished()), SLOT(getArtistBiographySlot())); +} + +void EchoNestPlugin::getArtistBiography(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +{ + if( !isValidArtistData( caller, data, customData ) ) + return; + + Artist artist( data.toString() ); + QNetworkReply *reply = artist.fetchBiographies(); + reply->setProperty("artist", QVariant::fromValue(artist)); + reply->setProperty( "data", data ); + m_replyMap[reply] = customData; + m_callerMap[reply] = caller; + connect(reply, SIGNAL(finished()), SLOT(getArtistBiographySlot())); +} + +void EchoNestPlugin::getArtistFamiliarity(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +{ + if( !isValidArtistData( caller, data, customData ) ) + return; + + qDebug() << "Fetching artist familiarity!" << data; + Artist artist( data.toString() ); + QNetworkReply* reply = artist.fetchFamiliarity(); + reply->setProperty( "artist", QVariant::fromValue(artist)); + reply->setProperty( "data", data ); + m_replyMap[reply] = customData; + m_callerMap[reply] = caller; + connect(reply, SIGNAL(finished()), SLOT(getArtistFamiliaritySlot())); +} + +void EchoNestPlugin::getArtistHotttnesss(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +{ + if( !isValidArtistData( caller, data, customData ) ) + return; + + Artist artist( data.toString() ); + QNetworkReply* reply = artist.fetchHotttnesss(); + reply->setProperty( "artist", QVariant::fromValue(artist)); + reply->setProperty( "data", data ); + m_replyMap[reply] = customData; + m_callerMap[reply] = caller; + connect(reply, SIGNAL(finished()), SLOT(getArtistHotttnesssSlot())); +} + +void EchoNestPlugin::getArtistTerms(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +{ + if( !isValidArtistData( caller, data, customData ) ) + return; + + Artist artist( data.toString() ); + QNetworkReply* reply = artist.fetchTerms( Echonest::Artist::Weight ); + reply->setProperty( "artist", QVariant::fromValue(artist)); + reply->setProperty( "data", data ); + m_replyMap[reply] = customData; + m_callerMap[reply] = caller; + connect(reply, SIGNAL(finished()), SLOT(getArtistTermsSlot())); +} + +void EchoNestPlugin::getMiscTopTerms(const QString &caller, const QVariant& data, InfoCustomDataHash& customData) +{ + QNetworkReply* reply = Artist::topTerms( 20 ); + m_replyMap[reply] = customData; + m_callerMap[reply] = caller; + connect( reply,SIGNAL(finished()), SLOT( getMiscTopSlot())); +} + + +void EchoNestPlugin::getArtistBiographySlot() +{ + QNetworkReply* reply = qobject_cast( sender() ); + Artist artist = artistFromReply( reply ); + BiographyList biographies = artist.biographies(); + InfoGenericMap biographyMap; + Q_FOREACH(const Biography& biography, biographies) + { + biographyMap[biography.site()]["site"] = biography.site(); + biographyMap[biography.site()]["url"] = biography.url().toString(); + biographyMap[biography.site()]["text"] = biography.text(); + biographyMap[biography.site()]["attribution"] = biography.license().attribution; + biographyMap[biography.site()]["licensetype"] = biography.license().type; + biographyMap[biography.site()]["attribution"] = biography.license().url.toString(); + + } + emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistBiography, reply->property( "data" ), QVariant::fromValue(biographyMap), m_replyMap[reply] ); + emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistBiography); + m_replyMap.remove(reply); + m_callerMap.remove(reply); + reply->deleteLater(); +} + +void EchoNestPlugin::getArtistFamiliaritySlot() +{ + QNetworkReply* reply = qobject_cast( sender() ); + Artist artist = artistFromReply( reply ); + qreal familiarity = artist.familiarity(); + emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistFamiliarity, reply->property( "data" ), familiarity, m_replyMap[reply] ); + emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistFamiliarity); + m_replyMap.remove(reply); + m_callerMap.remove(reply); + reply->deleteLater(); +} + +void EchoNestPlugin::getArtistHotttnesssSlot() +{ + QNetworkReply* reply = qobject_cast( sender() ); + Artist artist = artistFromReply( reply ); + qreal hotttnesss = artist.hotttnesss(); + emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistHotttness, reply->property( "data" ), hotttnesss, m_replyMap[reply] ); + emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistHotttness); + m_replyMap.remove(reply); + m_callerMap.remove(reply); + reply->deleteLater(); +} + +void EchoNestPlugin::getArtistTermsSlot() +{ + QNetworkReply* reply = qobject_cast( sender() ); + Artist artist = artistFromReply( reply ); + TermList terms = artist.terms(); + InfoGenericMap termsMap; + Q_FOREACH( const Echonest::Term& term, terms ) { + QMap< QString, QString > termMap; + termMap[ "weight" ] = QString::number(term.weight()); + termMap[ "frequency" ] = QString::number(term.frequency()); + termsMap[ term.name() ] = termMap; + } + emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistTerms, reply->property( "data" ), QVariant::fromValue(termsMap), m_replyMap[reply] ); + emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoArtistTerms); + m_replyMap.remove(reply); + m_callerMap.remove(reply); + reply->deleteLater(); +} + +void EchoNestPlugin::getMiscTopSlot() +{ + QNetworkReply* reply = qobject_cast( sender() ); + TermList terms = Artist::parseTopTerms( reply ); + InfoGenericMap termsMap; + Q_FOREACH( const Echonest::Term& term, terms ) { + QMap< QString, QString > termMap; + termMap[ "weight" ] = QString::number( term.weight() ); + termMap[ "frequency" ] = QString::number( term.frequency() ); + termsMap[ term.name().toLower() ] = termMap; + } + emit info( m_callerMap[reply], Tomahawk::InfoSystem::InfoMiscTopTerms, QVariant(), QVariant::fromValue(termsMap), m_replyMap[reply] ); + emit finished( m_callerMap[reply], Tomahawk::InfoSystem::InfoMiscTopTerms); + m_replyMap.remove(reply); + m_callerMap.remove(reply); + reply->deleteLater(); +} + +bool EchoNestPlugin::isValidArtistData(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +{ + if (data.isNull() || !data.isValid() || !data.canConvert()) + { + emit info(caller, Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + return false; + } + QString artistName = data.toString(); + if (artistName.isEmpty() ) + { + emit info(caller, Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + return false; + } + return true; +} + +bool EchoNestPlugin::isValidTrackData(const QString &caller, const QVariant& data, InfoCustomDataHash &customData) +{ + if (data.isNull() || !data.isValid() || !data.canConvert()) + { + emit info(caller, Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + return false; + } + QString trackName = data.toString(); + if (trackName.isEmpty() ) + { + emit info(caller, Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + return false; + } + if (!customData.contains("artistName") || + customData["artistName"].toString().isEmpty()) + return false; + return true; +} + +Artist EchoNestPlugin::artistFromReply(QNetworkReply* reply) +{ + Artist artist = reply->property("artist").value(); + try { + artist.parseProfile(reply); + } catch( const Echonest::ParseError& e ) { + qWarning() << "Caught parser error from echonest!" << e.what(); + } + return artist; +} diff --git a/src/infosystem/infoplugins/echonestplugin.h b/src/infosystem/infoplugins/echonestplugin.h new file mode 100644 index 000000000..8ffb120b2 --- /dev/null +++ b/src/infosystem/infoplugins/echonestplugin.h @@ -0,0 +1,54 @@ +#ifndef ECHONESTPLUGIN_H +#define ECHONESTPLUGIN_H +#include "tomahawk/infosystem.h" + +class QNetworkReply; +namespace Echonest { +class Artist; +} + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class EchoNestPlugin : public InfoPlugin +{ + Q_OBJECT + +public: + EchoNestPlugin(QObject *parent); + virtual ~EchoNestPlugin(); + + void getInfo( const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData ); + +private: + void getSongProfile( const QString &caller, const QVariant &data, InfoCustomDataHash &customData, const QString &item = QString() ); + void getArtistBiography ( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); + void getArtistFamiliarity( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); + void getArtistHotttnesss( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); + void getArtistTerms( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); + void getMiscTopTerms( const QString &caller, const QVariant &data, InfoCustomDataHash &customData ); + + bool isValidArtistData( const QString &caller, const QVariant& data, InfoCustomDataHash& customData ); + bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomDataHash& customData ); + Echonest::Artist artistFromReply( QNetworkReply* ); + +private slots: + void getArtistBiographySlot(); + void getArtistFamiliaritySlot(); + void getArtistHotttnesssSlot(); + void getArtistTermsSlot(); + void getMiscTopSlot(); + +private: + QHash< QNetworkReply*, InfoCustomDataHash > m_replyMap; + QHash< QNetworkReply*, QString > m_callerMap; +}; + +} + +} + +#endif // ECHONESTPLUGIN_H diff --git a/src/infosystem/infoplugins/musixmatchplugin.cpp b/src/infosystem/infoplugins/musixmatchplugin.cpp new file mode 100644 index 000000000..85757ff7e --- /dev/null +++ b/src/infosystem/infoplugins/musixmatchplugin.cpp @@ -0,0 +1,134 @@ +#include "tomahawk/infosystem.h" +#include "tomahawk/tomahawkapp.h" +#include "musixmatchplugin.h" +#include + +using namespace Tomahawk::InfoSystem; + +// for internal neatness + +MusixMatchPlugin::MusixMatchPlugin(QObject *parent) + : InfoPlugin(parent) + , m_apiKey("61be4ea5aea7dd942d52b2f1311dd9fe") +{ + qDebug() << Q_FUNC_INFO; + QSet< InfoType > supportedTypes; + supportedTypes << Tomahawk::InfoSystem::InfoTrackLyrics; + qobject_cast< InfoSystem* >(parent)->registerInfoTypes(this, supportedTypes); +} + +MusixMatchPlugin::~MusixMatchPlugin() +{ + qDebug() << Q_FUNC_INFO; +} + +void MusixMatchPlugin::getInfo(const QString &caller, const InfoType type, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash customData) +{ + qDebug() << Q_FUNC_INFO; + if( !isValidTrackData(caller, data, customData) || !data.canConvert()) + return; + Tomahawk::InfoSystem::MusixMatchHash hash = data.value(); + QString artist = hash["artistName"]; + QString track = hash["trackName"]; + if( artist.isEmpty() || track.isEmpty() ) + { + emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); + emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); + return; + } + qDebug() << "artist is " << artist << ", track is " << track; + QString requestString("http://api.musixmatch.com/ws/1.1/track.search?format=xml&page_size=1&f_has_lyrics=1"); + QUrl url(requestString); + url.addQueryItem("apikey", m_apiKey); + url.addQueryItem("q_artist", artist); + url.addQueryItem("q_track", track); + QNetworkReply* reply = TomahawkApp::instance()->nam()->get(QNetworkRequest(url)); + reply->setProperty("customData", QVariant::fromValue(customData)); + reply->setProperty("origData", data); + reply->setProperty("caller", caller); + + connect(reply, SIGNAL(finished()), SLOT(trackSearchSlot())); +} + +bool MusixMatchPlugin::isValidTrackData(const QString &caller, const QVariant& data, Tomahawk::InfoSystem::InfoCustomDataHash &customData) +{ + qDebug() << Q_FUNC_INFO; + if (data.isNull() || !data.isValid() || !data.canConvert()) + { + emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); + emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); + qDebug() << "MusixMatchPlugin::isValidTrackData: Data null, invalid, or can't convert"; + return false; + } + MusixMatchHash hash = data.value(); + if (hash["trackName"].isEmpty() ) + { + emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); + emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); + qDebug() << "MusixMatchPlugin::isValidTrackData: Track name is empty"; + return false; + } + if (hash["artistName"].isEmpty() ) + { + emit info(caller, Tomahawk::InfoSystem::InfoTrackLyrics, data, QVariant(), customData); + emit finished(caller, Tomahawk::InfoSystem::InfoTrackLyrics); + qDebug() << "MusixMatchPlugin::isValidTrackData: No artist name found"; + return false; + } + return true; +} + +void MusixMatchPlugin::trackSearchSlot() +{ + qDebug() << Q_FUNC_INFO; + QNetworkReply* oldReply = qobject_cast( sender() ); + if (!oldReply) + { + emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomDataHash()); + return; + } + QDomDocument doc; + doc.setContent(oldReply->readAll()); + qDebug() << doc.toString(); + QDomNodeList domNodeList = doc.elementsByTagName("track_id"); + if (domNodeList.isEmpty()) + { + emit info(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, oldReply->property("origData"), QVariant(), oldReply->property("customData").value()); + emit finished(oldReply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); + return; + } + QString track_id = domNodeList.at(0).toElement().text(); + QString requestString("http://api.musixmatch.com/ws/1.1/track.lyrics.get?track_id=%1&format=xml&apikey=%2"); + QUrl url(requestString); + url.addQueryItem("apikey", m_apiKey); + url.addQueryItem("track_id", track_id); + QNetworkReply* newReply = TomahawkApp::instance()->nam()->get(QNetworkRequest(url)); + newReply->setProperty("origData", oldReply->property("origData")); + newReply->setProperty("customData", oldReply->property("customData")); + newReply->setProperty("caller", oldReply->property("caller")); + connect(newReply, SIGNAL(finished()), SLOT(trackLyricsSlot())); +} + +void MusixMatchPlugin::trackLyricsSlot() +{ + qDebug() << Q_FUNC_INFO; + QNetworkReply* reply = qobject_cast( sender() ); + if (!reply) + { + emit info(QString(), Tomahawk::InfoSystem::InfoTrackLyrics, QVariant(), QVariant(), Tomahawk::InfoSystem::InfoCustomDataHash()); + return; + } + QDomDocument doc; + doc.setContent(reply->readAll()); + QDomNodeList domNodeList = doc.elementsByTagName("lyrics_body"); + if (domNodeList.isEmpty()) + { + emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(), reply->property("customData").value()); + emit finished(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); + return; + } + QString lyrics = domNodeList.at(0).toElement().text(); + qDebug() << "Emitting lyrics: " << lyrics; + emit info(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics, reply->property("origData"), QVariant(lyrics), reply->property("customData").value()); + emit finished(reply->property("caller").toString(), Tomahawk::InfoSystem::InfoTrackLyrics); +} \ No newline at end of file diff --git a/src/infosystem/infoplugins/musixmatchplugin.h b/src/infosystem/infoplugins/musixmatchplugin.h new file mode 100644 index 000000000..63301fa87 --- /dev/null +++ b/src/infosystem/infoplugins/musixmatchplugin.h @@ -0,0 +1,38 @@ +#ifndef MUSIXMATCHPLUGIN_H +#define MUSIXMATCHPLUGIN_H +#include "tomahawk/infosystem.h" + +class QNetworkReply; + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class MusixMatchPlugin : public InfoPlugin +{ + Q_OBJECT + +public: + MusixMatchPlugin(QObject *parent); + virtual ~MusixMatchPlugin(); + + void getInfo(const QString &caller, const InfoType type, const QVariant &data, InfoCustomDataHash customData); + +private: + bool isValidTrackData( const QString &caller, const QVariant& data, InfoCustomDataHash &customData ); + +public slots: + void trackSearchSlot(); + void trackLyricsSlot(); + +private: + QString m_apiKey; +}; + +} + +} + +#endif // MUSIXMATCHPLUGIN_H diff --git a/src/infosystem/infosystem.cpp b/src/infosystem/infosystem.cpp new file mode 100644 index 000000000..66b107d5e --- /dev/null +++ b/src/infosystem/infosystem.cpp @@ -0,0 +1,96 @@ +#include "tomahawk/infosystem.h" +#include "infoplugins/echonestplugin.h" +#include "infoplugins/musixmatchplugin.h" + +using namespace Tomahawk::InfoSystem; + +InfoSystem::InfoSystem(QObject *parent) + : QObject( parent ) +{ + qDebug() << Q_FUNC_INFO; + qRegisterMetaType > >("Tomahawk::InfoSystem::InfoGenericMap"); + qRegisterMetaType >("Tomahawk::InfoSystem::InfoCustomDataHash"); + qRegisterMetaType >("Tomahawk::InfoSystem::MusixMatchHash"); + InfoPluginPtr enptr(new EchoNestPlugin(this)); + m_plugins.append(enptr); + InfoPluginPtr mmptr(new MusixMatchPlugin(this)); + m_plugins.append(mmptr); +} + +void InfoSystem::registerInfoTypes(const InfoPluginPtr &plugin, const QSet< InfoType >& types) +{ + qDebug() << Q_FUNC_INFO; + Q_FOREACH(InfoType type, types) + m_infoMap[type].append(plugin); +} + +QLinkedList< InfoPluginPtr > InfoSystem::determineOrderedMatches(const InfoType type) const +{ + //Dummy function for now that returns the various items in the QSet; at some point this will + //probably need to support ordering based on the data source + QLinkedList< InfoPluginPtr > providers; + Q_FOREACH(InfoPluginPtr ptr, m_infoMap[type]) + providers << ptr; + return providers; +} + +void InfoSystem::getInfo(const QString &caller, const InfoType type, const QVariant& data, InfoCustomDataHash customData) +{ + qDebug() << Q_FUNC_INFO; + QLinkedList< InfoPluginPtr > providers = determineOrderedMatches(type); + if (providers.isEmpty()) + { + emit info(QString(), Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + return; + } + + InfoPluginPtr ptr = providers.first(); + if (!ptr) + { + emit info(QString(), Tomahawk::InfoSystem::InfoNoInfo, QVariant(), QVariant(), customData); + return; + } + + m_dataTracker[caller][type] = m_dataTracker[caller][type] + 1; + qDebug() << "current count in dataTracker for type" << type << "is" << m_dataTracker[caller][type]; + connect(ptr.data(), SIGNAL(info(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), + this, SLOT(infoSlot(QString, Tomahawk::InfoSystem::InfoType, QVariant, QVariant, Tomahawk::InfoSystem::InfoCustomDataHash)), Qt::UniqueConnection); + connect(ptr.data(), SIGNAL(finished(QString, Tomahawk::InfoSystem::InfoType)), + this, SLOT(finishedSlot(QString, Tomahawk::InfoSystem::InfoType)), Qt::UniqueConnection); + ptr.data()->getInfo(caller, type, data, customData); +} + +void InfoSystem::getInfo(const QString &caller, const InfoMap &input, InfoCustomDataHash customData) +{ + Q_FOREACH( InfoType type, input.keys() ) + getInfo(caller, type, input[type], customData); +} + +void InfoSystem::infoSlot(QString target, Tomahawk::InfoSystem::InfoType type, QVariant input, QVariant output, Tomahawk::InfoSystem::InfoCustomDataHash customData) +{ + qDebug() << Q_FUNC_INFO; + qDebug() << "current count in dataTracker is " << m_dataTracker[target][type]; + if (m_dataTracker[target][type] == 0) + { + qDebug() << "Caller was not waiting for that type of data!"; + return; + } + emit info(target, type, input, output, customData); +} + +void InfoSystem::finishedSlot(QString target, Tomahawk::InfoSystem::InfoType type) +{ + qDebug() << Q_FUNC_INFO; + m_dataTracker[target][type] = m_dataTracker[target][type] - 1; + qDebug() << "current count in dataTracker is " << m_dataTracker[target][type]; + Q_FOREACH(Tomahawk::InfoSystem::InfoType testtype, m_dataTracker[target].keys()) + { + if (m_dataTracker[target][testtype] != 0) + { + qDebug() << "found outstanding request of type" << testtype; + return; + } + } + qDebug() << "emitting finished with target" << target; + emit finished(target); +} \ No newline at end of file diff --git a/src/jabber/jabber.h b/src/jabber/jabber.h index 76b3a6913..1fa66e172 100644 --- a/src/jabber/jabber.h +++ b/src/jabber/jabber.h @@ -11,11 +11,8 @@ class Jabber : public QObject Q_OBJECT public: - Jabber(const QString &jid, - const QString password, - const QString server = "", - const int port=-1) - : p( jid, password, server, port ) + Jabber( const QString &jid, const QString password, const QString server = "", const int port=-1 ) + : p( jid, password, server, port ) { } @@ -24,6 +21,11 @@ public: // p.disconnect(); } + void setProxy( QNetworkProxy* proxy ) + { + p.setProxy( proxy ); + } + public slots: void start() diff --git a/src/jabber/jabber_p.cpp b/src/jabber/jabber_p.cpp index fb7e8c066..6d9e1e69d 100644 --- a/src/jabber/jabber_p.cpp +++ b/src/jabber/jabber_p.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include using namespace gloox; using namespace std; @@ -62,6 +64,40 @@ Jabber_p::~Jabber_p() } } + +void +Jabber_p::setProxy( QNetworkProxy* proxy ) +{ + qDebug() << Q_FUNC_INFO; + + if( !m_client || !proxy ) + { + qDebug() << "No client or no proxy"; + return; + } + + QNetworkProxy appProx = QNetworkProxy::applicationProxy(); + QNetworkProxy* prox = proxy->type() == QNetworkProxy::DefaultProxy ? &appProx : proxy; + + if( prox->type() == QNetworkProxy::NoProxy ) + { + qDebug() << "Setting proxy to none"; + m_client->setConnectionImpl( new gloox::ConnectionTCPClient( m_client.data(), m_client->logInstance(), m_client->server(), m_client->port() ) ); + } + else if( proxy->type() == QNetworkProxy::Socks5Proxy ) + { + qDebug() << "Setting proxy to SOCKS5"; + m_client->setConnectionImpl( new gloox::ConnectionSOCKS5Proxy( m_client.data(), + new gloox::ConnectionTCPClient( m_client->logInstance(), proxy->hostName().toStdString(), proxy->port() ), + m_client->logInstance(), m_client->server(), m_client->port() ) ); + } + else + { + qDebug() << "Proxy type unknown"; + } +} + + void Jabber_p::go() { @@ -79,8 +115,10 @@ Jabber_p::go() m_client->setPresence( Presence::Available, 1, "Tomahawk available" ); - // m_client->connect(); - // return; + // m_client->connect(); + // return; + + // Handle proxy if( m_client->connect( false ) ) { @@ -400,14 +438,21 @@ Jabber_p::handleRosterPresence( const RosterItem& item, const std::string& resou return; // ignore anyone not running tomahawk: - if( jid.full().find( "/tomahawk" ) == string::npos ) + // convert to QString to get proper regex support + QString res( jid.resource().c_str() ); + QRegExp regex( "tomahawk\\d+" ); + if( res != "tomahawk-tomahawk" && !res.contains( regex ) ) { + qDebug() << "not considering resource of " << res; // Disco them to check if they are tomahawk-capable + //qDebug() << "No tomahawk resource, DISCOing... " << jid.full().c_str(); //m_client->disco()->getDiscoInfo( jid, "", this, 0 ); return; } + qDebug() << "handling presence for resource of " << res; + //qDebug() << Q_FUNC_INFO << "jid: " << QString::fromStdString(item.jid()) // << " resource: " << QString::fromStdString(resource) // << " presencetype " << presence; diff --git a/src/jabber/jabber_p.h b/src/jabber/jabber_p.h index b975a17da..fd888b6cf 100644 --- a/src/jabber/jabber_p.h +++ b/src/jabber/jabber_p.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -64,6 +65,8 @@ public: explicit Jabber_p( const QString& jid, const QString& password, const QString& server = "", const int port = -1 ); virtual ~Jabber_p(); + void setProxy( QNetworkProxy* proxy ); + void disconnect(); /// GLOOX IMPLEMENTATION STUFF FOLLOWS diff --git a/src/playlist/playlistmanager.cpp b/src/playlist/playlistmanager.cpp index 77f01dba7..f46d8f525 100644 --- a/src/playlist/playlistmanager.cpp +++ b/src/playlist/playlistmanager.cpp @@ -21,7 +21,7 @@ PlaylistManager::PlaylistManager( QObject* parent ) , m_currentMode( 0 ) , m_superCollectionVisible( true ) { - m_widget->setMinimumWidth( 600 ); + m_widget->setMinimumWidth( 620 ); m_superCollectionViews << new CollectionView(); m_superCollectionViews.first()->setModel( m_superCollectionFlatModel ); diff --git a/src/proxydialog.ui b/src/proxydialog.ui new file mode 100644 index 000000000..12806644b --- /dev/null +++ b/src/proxydialog.ui @@ -0,0 +1,174 @@ + + + ProxyDialog + + + Qt::WindowModal + + + + 0 + 0 + 400 + 200 + + + + Proxy Settings + + + + + 30 + 160 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + 10 + 10 + 381 + 141 + + + + + 16 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + Host + + + + + + + Port + + + + + + + + + + + + + User + + + + + + + Password + + + + + + + QLineEdit::Password + + + + + + + Type + + + + + + + + + + buttonBox + accepted() + ProxyDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ProxyDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index e8cfde839..f3e18b5f0 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -1,5 +1,6 @@ #include "settingsdialog.h" #include "ui_settingsdialog.h" +#include "ui_proxydialog.h" #include #include @@ -17,7 +18,8 @@ #include -static QString md5( const QByteArray& src ) +static QString +md5( const QByteArray& src ) { QByteArray const digest = QCryptographicHash::hash( src, QCryptographicHash::Md5 ); return QString::fromLatin1( digest.toHex() ).rightJustified( 32, '0' ); @@ -27,6 +29,7 @@ static QString md5( const QByteArray& src ) SettingsDialog::SettingsDialog( QWidget *parent ) : QDialog( parent ) , ui( new Ui::SettingsDialog ) + , m_proxySettings( this ) , m_rejected( false ) , m_testLastFmQuery( 0 ) { @@ -39,9 +42,10 @@ SettingsDialog::SettingsDialog( QWidget *parent ) // JABBER ui->checkBoxJabberAutoConnect->setChecked( s->jabberAutoConnect() ); ui->jabberUsername->setText( s->jabberUsername() ); - ui->jabberPassword->setText(s->jabberPassword() ); - ui->jabberServer->setText( s->jabberServer() ); + ui->jabberPassword->setText( s->jabberPassword() ); + ui->jabberServer->setText( s->jabberServer() ); ui->jabberPort->setValue( s->jabberPort() ); + ui->proxyButton->setVisible( false ); if ( ui->jabberPort->text().toInt() != 5222 || !ui->jabberServer->text().isEmpty() ) { @@ -67,6 +71,7 @@ SettingsDialog::SettingsDialog( QWidget *parent ) connect( ui->pushButtonTestLastfmLogin, SIGNAL( clicked( bool) ), this, SLOT( testLastFmLogin() ) ); connect( ui->buttonBrowse, SIGNAL( clicked() ), SLOT( showPathSelector() ) ); + connect( ui->proxyButton, SIGNAL( clicked() ), SLOT( showProxySettings() ) ); connect( this, SIGNAL( rejected() ), SLOT( onRejected() ) ); } @@ -117,7 +122,6 @@ SettingsDialog::~SettingsDialog() { APP->reconnectJabber(); } - } else qDebug() << "Settings dialog cancelled, NOT saving prefs."; @@ -126,7 +130,8 @@ SettingsDialog::~SettingsDialog() } -void SettingsDialog::showPathSelector() +void +SettingsDialog::showPathSelector() { QString path = QFileDialog::getExistingDirectory( this, @@ -141,7 +146,8 @@ void SettingsDialog::showPathSelector() } -void SettingsDialog::doScan() +void +SettingsDialog::doScan() { // TODO this doesnt really belong here.. QString path = ui->lineEditMusicPath->text(); @@ -155,13 +161,15 @@ void SettingsDialog::doScan() } -void SettingsDialog::onRejected() +void +SettingsDialog::onRejected() { m_rejected = true; } -void SettingsDialog::changeEvent( QEvent *e ) +void +SettingsDialog::changeEvent( QEvent *e ) { QDialog::changeEvent( e ); switch ( e->type() ) @@ -176,7 +184,17 @@ void SettingsDialog::changeEvent( QEvent *e ) } -void SettingsDialog::testLastFmLogin() +void +SettingsDialog::showProxySettings() +{ + m_proxySettings.exec(); + if ( m_proxySettings.result() == QDialog::Accepted ) + m_proxySettings.saveSettings(); +} + + +void +SettingsDialog::testLastFmLogin() { #ifndef NO_LIBLASTFM ui->pushButtonTestLastfmLogin->setEnabled( false ); @@ -196,7 +214,8 @@ void SettingsDialog::testLastFmLogin() } -void SettingsDialog::onLastFmFinished() +void +SettingsDialog::onLastFmFinished() { #ifndef NO_LIBLASTFM lastfm::XmlQuery lfm = lastfm::XmlQuery( m_testLastFmQuery->readAll() ); @@ -233,3 +252,61 @@ void SettingsDialog::onLastFmFinished() #endif } + +ProxyDialog::ProxyDialog( QWidget *parent ) + : QDialog( parent ) + , ui( new Ui::ProxyDialog ) +{ + ui->setupUi( this ); + + // ugly, I know, but... + QHash enumMap; + int i = 0; + ui->typeBox->insertItem( i, "No Proxy", QNetworkProxy::NoProxy ); + enumMap[QNetworkProxy::NoProxy] = i++; + ui->typeBox->insertItem( i, "SOCKS 5", QNetworkProxy::Socks5Proxy ); + enumMap[QNetworkProxy::Socks5Proxy] = i++; + + TomahawkSettings* s = TomahawkApp::instance()->settings(); + + ui->typeBox->setCurrentIndex( enumMap[s->proxyType()] ); + ui->hostLineEdit->setText( s->proxyHost() ); + ui->portLineEdit->setText( QString::number( s->proxyPort() ) ); + ui->userLineEdit->setText( s->proxyUsername() ); + ui->passwordLineEdit->setText( s->proxyPassword() ); +} + + +void +ProxyDialog::saveSettings() +{ + qDebug() << Q_FUNC_INFO; + + //First set settings + TomahawkSettings* s = TomahawkApp::instance()->settings(); + s->setProxyHost( ui->hostLineEdit->text() ); + + bool ok; + qulonglong port = ui->portLineEdit->text().toULongLong( &ok ); + if( ok ) + s->setProxyPort( port ); + + s->setProxyUsername( ui->userLineEdit->text() ); + s->setProxyPassword( ui->passwordLineEdit->text() ); + s->setProxyType( ui->typeBox->itemData( ui->typeBox->currentIndex() ).toInt() ); + + // Now, if a proxy is defined, set QNAM + if( s->proxyType() == QNetworkProxy::NoProxy || s->proxyHost().isEmpty() ) + return; + + QNetworkProxy proxy( static_cast(s->proxyType()), s->proxyHost(), s->proxyPort(), s->proxyUsername(), s->proxyPassword() ); + QNetworkAccessManager* nam = TomahawkApp::instance()->nam(); + nam->setProxy( proxy ); + QNetworkProxy* globalProxy = TomahawkApp::instance()->proxy(); + QNetworkProxy* oldProxy = globalProxy; + globalProxy = new QNetworkProxy( proxy ); + if( oldProxy ) + delete oldProxy; + + QNetworkProxy::setApplicationProxy( proxy ); +} diff --git a/src/settingsdialog.h b/src/settingsdialog.h index 57a031110..3d71d82c5 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -8,8 +8,23 @@ class QNetworkReply; namespace Ui { class SettingsDialog; + class ProxyDialog; } +class ProxyDialog : public QDialog +{ +Q_OBJECT + +public: + explicit ProxyDialog( QWidget *parent = 0 ); + ~ProxyDialog() {}; + + void saveSettings(); + +private: + Ui::ProxyDialog *ui; +}; + class SettingsDialog : public QDialog { Q_OBJECT @@ -31,12 +46,15 @@ private slots: void showPathSelector(); void doScan(); + void showProxySettings(); + void testLastFmLogin(); void onLastFmFinished(); private: Ui::SettingsDialog *ui; + ProxyDialog m_proxySettings; bool m_rejected; QNetworkReply* m_testLastFmQuery; }; diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index 1bada64e1..820c61ba8 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -224,6 +224,43 @@ + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Proxy Settings... + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -234,7 +271,7 @@ 20 - 146 + 73 @@ -531,5 +568,21 @@ + + checkBoxJabberAdvanced + toggled(bool) + proxyButton + setVisible(bool) + + + 310 + 110 + + + 368 + 194 + + + diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index de8a3a18d..5ba604fdc 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -7,12 +7,14 @@ #include #include "tomahawk/collection.h" +#include "tomahawk/infosystem.h" #include "database/database.h" #include "database/databasecollection.h" #include "database/databasecommand_collectionstats.h" #include "database/databaseresolver.h" #include "jabber/jabber.h" #include "utils/tomahawkutils.h" +#include "xmppbot/xmppbot.h" #include "web/api_v1.h" #include "scriptresolver.h" @@ -101,6 +103,8 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) , m_zeroconf( 0 ) , m_settings( 0 ) , m_nam( 0 ) + , m_proxy( 0 ) + , m_infoSystem( 0 ) { qsrand( QTime( 0, 0, 0 ).secsTo( QTime::currentTime() ) ); @@ -157,6 +161,24 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] ) } #endif + // Set up proxy + if( m_settings->proxyType() != QNetworkProxy::NoProxy && !m_settings->proxyHost().isEmpty() ) + { + qDebug() << "Setting proxy to saved values"; + m_proxy = new QNetworkProxy( static_cast(m_settings->proxyType()), m_settings->proxyHost(), m_settings->proxyPort(), m_settings->proxyUsername(), m_settings->proxyPassword() ); + qDebug() << "Proxy type = " << QString::number( static_cast(m_proxy->type()) ); + qDebug() << "Proxy host = " << m_proxy->hostName(); + QNetworkAccessManager* nam = TomahawkApp::instance()->nam(); + nam->setProxy( *m_proxy ); + } + else + m_proxy = new QNetworkProxy( QNetworkProxy::NoProxy ); + + QNetworkProxy::setApplicationProxy( *m_proxy ); + + m_infoSystem = new Tomahawk::InfoSystem::InfoSystem( this ); + m_xmppBot = new XMPPBot( this ); + boost::function(result_ptr)> fac = boost::bind( &TomahawkApp::httpIODeviceFactory, this, _1 ); this->registerIODeviceFactory( "http", fac ); @@ -449,6 +471,7 @@ TomahawkApp::setupJabber() //const QString& jid, const QString& pass, const QStr connect( m_jabber.data(), SIGNAL( connected() ), SLOT( jabberConnected() ) ); connect( m_jabber.data(), SIGNAL( authError(int, const QString&) ), SLOT( jabberAuthError(int,const QString&) ) ); + m_jabber->setProxy( m_proxy ); m_jabber->start(); } diff --git a/src/tomahawksettings.cpp b/src/tomahawksettings.cpp index 41d5e8d11..d2c8441d0 100644 --- a/src/tomahawksettings.cpp +++ b/src/tomahawksettings.cpp @@ -21,8 +21,8 @@ TomahawkSettings::TomahawkSettings( QObject* parent ) else if( value( "configversion" ).toUInt() != SettingsDialog::VERSION ) { qDebug() << "Config version outdated, old:" << value( "configversion" ).toUInt() - << "new:" << SettingsDialog::VERSION - << "Doing upgrade, if any..."; + << "new:" << SettingsDialog::VERSION + << "Doing upgrade, if any..."; // insert upgrade code here as required setValue( "configversion", SettingsDialog::VERSION ); @@ -36,7 +36,8 @@ TomahawkSettings::~TomahawkSettings() } -QString TomahawkSettings::scannerPath() const +QString +TomahawkSettings::scannerPath() const { #ifndef TOMAHAWK_HEADLESS return value( "scannerpath", QDesktopServices::storageLocation( QDesktopServices::MusicLocation ) ).toString(); @@ -46,181 +47,337 @@ QString TomahawkSettings::scannerPath() const } -void TomahawkSettings::setScannerPath(const QString& path) +void +TomahawkSettings::setScannerPath( const QString& path ) { setValue( "scannerpath", path ); } -bool TomahawkSettings::hasScannerPath() const +bool +TomahawkSettings::hasScannerPath() const { return contains( "scannerpath" ); } -bool TomahawkSettings::httpEnabled() const +bool +TomahawkSettings::httpEnabled() const { return value( "network/http", true ).toBool(); } -void TomahawkSettings::setHttpEnabled(bool enable) +void +TomahawkSettings::setHttpEnabled( bool enable ) { setValue( "network/http", enable ); } -QByteArray TomahawkSettings::mainWindowGeometry() const +QString +TomahawkSettings::proxyHost() const +{ + return value( "network/proxy/host", QString() ).toString(); +} + + +void +TomahawkSettings::setProxyHost( const QString& host ) +{ + setValue( "network/proxy/host", host ); +} + + +qulonglong +TomahawkSettings::proxyPort() const +{ + return value( "network/proxy/port", 1080 ).toULongLong(); +} + + +void +TomahawkSettings::setProxyPort( const qulonglong port ) +{ + setValue( "network/proxy/port", port ); +} + + +QString +TomahawkSettings::proxyUsername() const +{ + return value( "network/proxy/username", QString() ).toString(); +} + + +void +TomahawkSettings::setProxyUsername( const QString& username ) +{ + setValue( "network/proxy/username", username ); +} + + +QString +TomahawkSettings::proxyPassword() const +{ + return value( "network/proxy/password", QString() ).toString(); +} + + +void +TomahawkSettings::setProxyPassword( const QString& password ) +{ + setValue( "network/proxy/password", password ); +} + + +int +TomahawkSettings::proxyType() const +{ + return value( "network/proxy/type", 0 ).toInt(); +} + + +void +TomahawkSettings::setProxyType( const int type ) +{ + setValue( "network/proxy/type", type ); +} + + +QByteArray +TomahawkSettings::mainWindowGeometry() const { return value( "ui/mainwindow/geometry" ).toByteArray(); } -void TomahawkSettings::setMainWindowGeometry(const QByteArray& geom) +void +TomahawkSettings::setMainWindowGeometry( const QByteArray& geom ) { setValue( "ui/mainwindow/geometry", geom ); } -QByteArray TomahawkSettings::mainWindowState() const +QByteArray +TomahawkSettings::mainWindowState() const { return value( "ui/mainwindow/state" ).toByteArray(); } -void TomahawkSettings::setMainWindowState(const QByteArray& state) +void +TomahawkSettings::setMainWindowState( const QByteArray& state ) { setValue( "ui/mainwindow/state", state ); } -QList TomahawkSettings::playlistColumnSizes() const +QList +TomahawkSettings::playlistColumnSizes() const { return value( "ui/playlist/columnSize" ).toList(); } -void TomahawkSettings::setPlaylistColumnSizes(const QList& cols) +void +TomahawkSettings::setPlaylistColumnSizes( const QList& cols ) { setValue( "ui/playlist/geometry", cols ); } -bool TomahawkSettings::jabberAutoConnect() const +bool +TomahawkSettings::jabberAutoConnect() const { return value( "jabber/autoconnect", true ).toBool(); } -void TomahawkSettings::setJabberAutoConnect(bool autoconnect) +void +TomahawkSettings::setJabberAutoConnect( bool autoconnect ) { setValue( "jabber/autoconnect", autoconnect ); } -int TomahawkSettings::jabberPort() const +int +TomahawkSettings::jabberPort() const { return value( "jabber/port", 5222 ).toInt(); } -void TomahawkSettings::setJabberPort(int port) +void +TomahawkSettings::setJabberPort( int port ) { setValue( "jabber/port", port ); } -QString TomahawkSettings::jabberServer() const +QString +TomahawkSettings::jabberServer() const { return value( "jabber/server" ).toString(); } -void TomahawkSettings::setJabberServer(const QString& server) +void +TomahawkSettings::setJabberServer( const QString& server ) { setValue( "jabber/server", server ); } -QString TomahawkSettings::jabberUsername() const +QString +TomahawkSettings::jabberUsername() const { return value( "jabber/username" ).toString(); } -void TomahawkSettings::setJabberUsername(const QString& username) +void +TomahawkSettings::setJabberUsername( const QString& username ) { setValue( "jabber/username", username ); } -QString TomahawkSettings::jabberPassword() const +QString +TomahawkSettings::jabberPassword() const { return value( "jabber/password" ).toString(); } -void TomahawkSettings::setJabberPassword(const QString& pw) +void +TomahawkSettings::setJabberPassword( const QString& pw ) { setValue( "jabber/password", pw ); } -bool TomahawkSettings::upnpEnabled() const +bool +TomahawkSettings::upnpEnabled() const { return value( "network/upnp", true ).toBool(); } -void TomahawkSettings::setUPnPEnabled(bool enable) +void +TomahawkSettings::setUPnPEnabled( bool enable ) { setValue( "network/upnp", enable ); } -QString TomahawkSettings::lastFmPassword() const +QString +TomahawkSettings::lastFmPassword() const { return value( "lastfm/password" ).toString(); } -void TomahawkSettings::setLastFmPassword(const QString& password) +void +TomahawkSettings::setLastFmPassword( const QString& password ) { setValue( "lastfm/password", password ); } -QByteArray TomahawkSettings::lastFmSessionKey() const +QByteArray +TomahawkSettings::lastFmSessionKey() const { return value( "lastfm/sessionkey" ).toByteArray(); } -void TomahawkSettings::setLastFmSessionKey(const QByteArray& key) +void +TomahawkSettings::setLastFmSessionKey( const QByteArray& key ) { setValue( "lastfm/sessionkey", key ); } -QString TomahawkSettings::lastFmUsername() const +QString +TomahawkSettings::lastFmUsername() const { return value( "lastfm/username" ).toString(); } -void TomahawkSettings::setLastFmUsername(const QString& username ) +void +TomahawkSettings::setLastFmUsername( const QString& username ) { setValue( "lastfm/username", username ); } -bool TomahawkSettings::scrobblingEnabled() const +bool +TomahawkSettings::scrobblingEnabled() const { return value( "lastfm/enablescrobbling", false ).toBool(); } -void TomahawkSettings::setScrobblingEnabled(bool enable) +void +TomahawkSettings::setScrobblingEnabled( bool enable ) { setValue( "lastfm/enablescrobbling", enable ); } + + +QString +TomahawkSettings::xmppBotServer() const +{ + return value( "xmppBot/server", QString() ).toString(); +} + + +void +TomahawkSettings::setXmppBotServer( const QString& server ) +{ + setValue( "xmppBot/server", server ); +} + + +QString +TomahawkSettings::xmppBotJid() const +{ + return value( "xmppBot/jid", QString() ).toString(); +} + + +void +TomahawkSettings::setXmppBotJid( const QString& component ) +{ + setValue( "xmppBot/jid", component ); +} + + +QString +TomahawkSettings::xmppBotPassword() const +{ + return value( "xmppBot/password", QString() ).toString(); +} + + +void +TomahawkSettings::setXmppBotPassword( const QString& password ) +{ + setValue( "xmppBot/password", password ); +} + + +int +TomahawkSettings::xmppBotPort() const +{ + return value( "xmppBot/port", -1 ).toInt(); +} + + +void +TomahawkSettings::setXmppBotPort( const int port ) +{ + setValue( "xmppBot/port", -1 ); +} diff --git a/src/tomahawksettings.h b/src/tomahawksettings.h index af865b864..fa7ac3b0a 100644 --- a/src/tomahawksettings.h +++ b/src/tomahawksettings.h @@ -50,7 +50,22 @@ public: bool upnpEnabled() const; /// true by default void setUPnPEnabled( bool enable ); - + + QString proxyHost() const; + void setProxyHost( const QString &host ); + + qulonglong proxyPort() const; + void setProxyPort( const qulonglong port ); + + QString proxyUsername() const; + void setProxyUsername( const QString &username ); + + QString proxyPassword() const; + void setProxyPassword( const QString &password ); + + int proxyType() const; + void setProxyType( const int type ); + /// Last.fm settings bool scrobblingEnabled() const; /// false by default void setScrobblingEnabled( bool enable ); @@ -63,6 +78,19 @@ public: QByteArray lastFmSessionKey() const; void setLastFmSessionKey( const QByteArray& key ); + + /// XMPP Component Settings + QString xmppBotServer() const; + void setXmppBotServer( const QString &server ); + + QString xmppBotJid() const; + void setXmppBotJid( const QString &component ); + + QString xmppBotPassword() const; + void setXmppBotPassword( const QString &password ); + + int xmppBotPort() const; + void setXmppBotPort( const int port ); }; #endif diff --git a/src/xmppbot/xmppbot.cpp b/src/xmppbot/xmppbot.cpp new file mode 100644 index 000000000..a71f16dd1 --- /dev/null +++ b/src/xmppbot/xmppbot.cpp @@ -0,0 +1,396 @@ +#include "xmppbot.h" + +#include "tomahawk/tomahawkapp.h" +#include "tomahawk/infosystem.h" +#include +#include