1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-03-23 09:19:41 +01:00

Merge branch 'twitterinfo'

This commit is contained in:
Jeff Mitchell 2012-04-11 15:25:25 -04:00
commit 556c0b0767
36 changed files with 718 additions and 201 deletions

View File

@ -54,8 +54,6 @@ LastFmAccountFactory::icon() const
LastFmAccount::LastFmAccount( const QString& accountId )
: CustomAtticaAccount( accountId )
{
m_infoPlugin = QWeakPointer< LastFmPlugin >( new LastFmPlugin( this ) );
setAccountFriendlyName( "Last.Fm" );
m_icon.load( RESPATH "images/lastfm-icon.png" );
@ -70,12 +68,22 @@ LastFmAccount::LastFmAccount( const QString& accountId )
{
hookupResolver();
}
if ( infoPlugin() && Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() )
{
infoPlugin().data()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() );
QMetaObject::invokeMethod( infoPlugin().data(), "init", Qt::QueuedConnection );
}
}
LastFmAccount::~LastFmAccount()
{
delete m_infoPlugin.data();
if ( m_infoPlugin )
Tomahawk::InfoSystem::InfoSystem::instance()->removeInfoPlugin( infoPlugin() );
delete m_resolver.data();
}
@ -155,10 +163,13 @@ LastFmAccount::icon() const
}
InfoPlugin*
InfoPluginPtr
LastFmAccount::infoPlugin()
{
return m_infoPlugin.data();
if ( m_infoPlugin.isNull() )
m_infoPlugin = QWeakPointer< LastFmPlugin >( new LastFmPlugin( this ) );
return InfoPluginPtr( m_infoPlugin.data() );
}
bool
@ -178,7 +189,8 @@ LastFmAccount::saveConfig()
setScrobble( m_configWidget.data()->scrobble() );
}
m_infoPlugin.data()->settingsChanged();
if ( m_infoPlugin )
QTimer::singleShot( 0, m_infoPlugin.data(), SLOT( settingsChanged() ) );
}

View File

@ -72,7 +72,7 @@ public:
virtual void authenticate();
virtual SipPlugin* sipPlugin() { return 0; }
virtual Tomahawk::InfoSystem::InfoPlugin* infoPlugin();
virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin();
virtual bool isAuthenticated() const;

View File

@ -46,17 +46,27 @@ LastFmPlugin::LastFmPlugin( LastFmAccount* account )
{
m_supportedGetTypes << InfoAlbumCoverArt << InfoArtistImages << InfoArtistSimilars << InfoArtistSongs << InfoChart << InfoChartCapabilities;
m_supportedPushTypes << InfoSubmitScrobble << InfoSubmitNowPlaying << InfoLove << InfoUnLove;
}
void
LastFmPlugin::init()
{
if ( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() && thread() != Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() )
{
tDebug() << "Failure: move to the worker thread before running init";
return;
}
// Flush session key cache
// TODO WHY FLUSH
// m_account->setSessionKey( QByteArray() );
lastfm::ws::ApiKey = "7194b85b6d1f424fe1668173a78c0c4a";
lastfm::ws::SharedSecret = "ba80f1df6d27ae63e9cb1d33ccf2052f";
lastfm::ws::Username = m_account->username();
lastfm::ws::Username = m_account.data()->username();
lastfm::setNetworkAccessManager( TomahawkUtils::nam() );
m_pw = m_account->password();
m_pw = m_account.data()->password();
//HACK work around a bug in liblastfm---it doesn't create its config dir, so when it
// tries to write the track cache, it fails silently. until we have a fixed version, do this
@ -707,23 +717,26 @@ LastFmPlugin::artistImagesReturned()
void
LastFmPlugin::settingsChanged()
{
if ( !m_scrobbler && m_account->scrobble() )
if ( m_account.isNull() )
return;
if ( !m_scrobbler && m_account.data()->scrobble() )
{ // can simply create the scrobbler
lastfm::ws::Username = m_account->username();
m_pw = m_account->password();
lastfm::ws::Username = m_account.data()->username();
m_pw = m_account.data()->password();
createScrobbler();
}
else if ( m_scrobbler && !m_account->scrobble() )
else if ( m_scrobbler && !m_account.data()->scrobble() )
{
delete m_scrobbler;
m_scrobbler = 0;
}
else if ( m_account->username() != lastfm::ws::Username ||
m_account->password() != m_pw )
else if ( m_account.data()->username() != lastfm::ws::Username ||
m_account.data()->password() != m_pw )
{
lastfm::ws::Username = m_account->username();
m_pw = m_account->password();
lastfm::ws::Username = m_account.data()->username();
m_pw = m_account.data()->password();
// credentials have changed, have to re-create scrobbler for them to take effect
if ( m_scrobbler )
{
@ -740,12 +753,12 @@ void
LastFmPlugin::onAuthenticated()
{
QNetworkReply* authJob = dynamic_cast<QNetworkReply*>( sender() );
if ( !authJob )
if ( !authJob || m_account.isNull() )
{
tLog() << Q_FUNC_INFO << "Help! No longer got a last.fm auth job!";
return;
}
if ( authJob->error() == QNetworkReply::NoError )
{
lastfm::XmlQuery lfm = lastfm::XmlQuery( authJob->readAll() );
@ -753,16 +766,16 @@ LastFmPlugin::onAuthenticated()
if ( lfm.children( "error" ).size() > 0 )
{
tLog() << "Error from authenticating with Last.fm service:" << lfm.text();
m_account->setSessionKey( QByteArray() );
m_account.data()->setSessionKey( QByteArray() );
}
else
{
lastfm::ws::SessionKey = lfm[ "session" ][ "key" ].text();
m_account->setSessionKey( lastfm::ws::SessionKey.toLatin1() );
m_account.data()->setSessionKey( lastfm::ws::SessionKey.toLatin1() );
// qDebug() << "Got session key from last.fm";
if ( m_account->scrobble() )
if ( m_account.data()->scrobble() )
m_scrobbler = new lastfm::Audioscrobbler( "thk" );
}
}
@ -778,7 +791,10 @@ LastFmPlugin::onAuthenticated()
void
LastFmPlugin::createScrobbler()
{
if ( m_account->sessionKey().isEmpty() ) // no session key, so get one
if ( m_account.isNull() )
return;
if ( m_account.data()->sessionKey().isEmpty() ) // no session key, so get one
{
qDebug() << "LastFmPlugin::createScrobbler Session key is empty";
QString authToken = TomahawkUtils::md5( ( lastfm::ws::Username.toLower() + TomahawkUtils::md5( m_pw.toUtf8() ) ).toUtf8() );
@ -794,7 +810,7 @@ LastFmPlugin::createScrobbler()
else
{
qDebug() << "LastFmPlugin::createScrobbler Already have session key";
lastfm::ws::SessionKey = m_account->sessionKey();
lastfm::ws::SessionKey = m_account.data()->sessionKey();
m_scrobbler = new lastfm::Audioscrobbler( "thk" );
}

View File

@ -49,6 +49,7 @@ public:
virtual ~LastFmPlugin();
public slots:
void init();
void settingsChanged();
void onAuthenticated();
@ -79,7 +80,7 @@ private:
void dataError( Tomahawk::InfoSystem::InfoRequestData requestData );
Accounts::LastFmAccount* m_account;
QWeakPointer< Accounts::LastFmAccount > m_account;
QList<lastfm::Track> parseTrackList( QNetworkReply * reply );
lastfm::MutableTrack m_track;

View File

@ -66,7 +66,7 @@ public:
virtual QPixmap icon() const;
virtual QWidget* aclWidget() { return 0; }
virtual InfoSystem::InfoPlugin* infoPlugin() { return 0; }
virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin() { return Tomahawk::InfoSystem::InfoPluginPtr(); }
virtual SipPlugin* sipPlugin() { return 0; }
void addPlaylist( const QString &qid, const QString& title, QList< Tomahawk::query_ptr > tracks );

View File

@ -8,6 +8,7 @@ add_definitions( -DACCOUNTDLLEXPORT_PRO )
set( twitterAccountSources
twitteraccount.cpp
twitterinfoplugin.cpp
twitterconfigwidget.cpp
tomahawkoauthtwitter.cpp
sip/twittersip.cpp
@ -15,6 +16,7 @@ set( twitterAccountSources
set( twitterAccountHeaders
twitteraccount.h
twitterinfoplugin.h
twitterconfigwidget.h
tomahawkoauthtwitter.h
sip/twittersip.h

View File

@ -21,6 +21,7 @@
#include "twitteraccount.h"
#include "twitterconfigwidget.h"
#include "accounts/twitter/tomahawkoauthtwitter.h"
#include "libtomahawk/infosystem/infosystem.h"
#include "sip/SipPlugin.h"
@ -47,9 +48,10 @@ TwitterAccountFactory::createAccount( const QString& accountId )
TwitterAccount::TwitterAccount( const QString &accountId )
: Account( accountId )
, m_isAuthenticated( false )
, m_isAuthenticating( false )
{
setAccountServiceName( "Twitter" );
setTypes( AccountTypes( InfoType | SipType ) );
setTypes( AccountTypes( StatusPushType | SipType ) );
qDebug() << "Got cached peers:" << configuration() << configuration()[ "cachedpeers" ];
@ -99,19 +101,57 @@ TwitterAccount::sipPlugin()
}
Tomahawk::InfoSystem::InfoPluginPtr
TwitterAccount::infoPlugin()
{
if ( m_twitterInfoPlugin.isNull() )
m_twitterInfoPlugin = QWeakPointer< Tomahawk::InfoSystem::TwitterInfoPlugin >( new Tomahawk::InfoSystem::TwitterInfoPlugin( this ) );
return Tomahawk::InfoSystem::InfoPluginPtr( m_twitterInfoPlugin.data() );
}
void
TwitterAccount::authenticate()
{
// Since we need to have a chance for deletion (via the infosystem) to work on the info plugin, we put this on the event loop
tDebug() << Q_FUNC_INFO;
QTimer::singleShot( 0, this, SLOT( authenticateSlot() ) );
}
void
TwitterAccount::authenticateSlot()
{
tDebug() << Q_FUNC_INFO;
if ( m_twitterInfoPlugin.isNull() )
{
if ( infoPlugin() && Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() )
{
infoPlugin().data()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() );
QMetaObject::invokeMethod( infoPlugin().data(), "init", Qt::QueuedConnection );
}
}
if ( m_isAuthenticating )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Already authenticating";
return;
}
tDebug() << Q_FUNC_INFO << "credentials: " << credentials().keys();
if ( credentials()[ "oauthtoken" ].toString().isEmpty() || credentials()[ "oauthtokensecret" ].toString().isEmpty() )
{
qDebug() << "TwitterSipPlugin has empty Twitter credentials; not connecting";
tDebug() << Q_FUNC_INFO << "TwitterSipPlugin has empty Twitter credentials; not connecting";
return;
}
if ( refreshTwitterAuth() )
{
m_isAuthenticating = true;
tDebug() << Q_FUNC_INFO << "Verifying credentials";
QTweetAccountVerifyCredentials *credVerifier = new QTweetAccountVerifyCredentials( m_twitterAuth.data(), this );
connect( credVerifier, SIGNAL( parsedUser( const QTweetUser & ) ), SLOT( connectAuthVerifyReply( const QTweetUser & ) ) );
credVerifier->verify();
@ -122,10 +162,17 @@ TwitterAccount::authenticate()
void
TwitterAccount::deauthenticate()
{
if ( sipPlugin() )
tDebug() << Q_FUNC_INFO;
if ( m_twitterSipPlugin )
sipPlugin()->disconnectPlugin();
if ( m_twitterInfoPlugin )
Tomahawk::InfoSystem::InfoSystem::instance()->removeInfoPlugin( m_twitterInfoPlugin.data() );
m_isAuthenticated = false;
m_isAuthenticating = false;
emit nowDeauthenticated();
}
@ -139,7 +186,7 @@ TwitterAccount::refreshTwitterAuth()
delete m_twitterAuth.data();
Q_ASSERT( TomahawkUtils::nam() != 0 );
qDebug() << Q_FUNC_INFO << " with nam " << TomahawkUtils::nam();
tDebug() << Q_FUNC_INFO << " with nam " << TomahawkUtils::nam();
m_twitterAuth = QWeakPointer< TomahawkOAuthTwitter >( new TomahawkOAuthTwitter( TomahawkUtils::nam(), this ) );
if( m_twitterAuth.isNull() )
@ -155,6 +202,7 @@ TwitterAccount::refreshTwitterAuth()
void
TwitterAccount::connectAuthVerifyReply( const QTweetUser &user )
{
m_isAuthenticating = false;
if ( user.id() == 0 )
{
qDebug() << "TwitterAccount could not authenticate to Twitter";
@ -174,6 +222,8 @@ TwitterAccount::connectAuthVerifyReply( const QTweetUser &user )
emit nowAuthenticated( m_twitterAuth, user );
}
}
QPixmap
TwitterAccount::icon() const {
return QPixmap( ":/twitter-icon.png" );

View File

@ -25,6 +25,7 @@
#include "tomahawkoauthtwitter.h"
#include "sip/twittersip.h"
#include "twitterinfoplugin.h"
#include "accounts/accountdllmacro.h"
#include "accounts/Account.h"
@ -49,7 +50,7 @@ public:
QString factoryId() const { return "twitteraccount"; }
QString description() const { return tr( "Connect to your Twitter followers." ); }
QPixmap icon() const { return QPixmap( ":/twitter-icon.png" ); }
AccountTypes types() const { return AccountTypes( SipType ); };
AccountTypes types() const { return AccountTypes( SipType | StatusPushType ); };
Account* createAccount( const QString& pluginId = QString() );
};
@ -69,7 +70,7 @@ public:
ConnectionState connectionState() const;
Tomahawk::InfoSystem::InfoPlugin* infoPlugin() { return 0; }
Tomahawk::InfoSystem::InfoPluginPtr infoPlugin();
SipPlugin* sipPlugin();
QWidget* configurationWidget() { return m_configWidget.data(); }
@ -83,15 +84,18 @@ signals:
void nowDeauthenticated();
private slots:
void authenticateSlot();
void configDialogAuthedSignalSlot( bool authed );
void connectAuthVerifyReply( const QTweetUser &user );
private:
QIcon m_icon;
bool m_isAuthenticated;
bool m_isAuthenticating;
QWeakPointer< TomahawkOAuthTwitter > m_twitterAuth;
QWeakPointer< TwitterConfigWidget > m_configWidget;
QWeakPointer< TwitterSipPlugin > m_twitterSipPlugin;
QWeakPointer< Tomahawk::InfoSystem::TwitterInfoPlugin > m_twitterInfoPlugin;
// for settings access
friend class TwitterConfigWidget;

View File

@ -0,0 +1,174 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2012, Dominik Schmidt <domme@tomahawk-player.org>
* Copyright 2012, Jeff Mitchell <jeff@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "twitterinfoplugin.h"
#include "accounts/twitter/twitteraccount.h"
#include <QTweetLib/qtweetaccountverifycredentials.h>
#include <QTweetLib/qtweetstatusupdate.h>
#include "globalactionmanager.h"
#include "utils/logger.h"
namespace Tomahawk
{
namespace InfoSystem
{
TwitterInfoPlugin::TwitterInfoPlugin( Tomahawk::Accounts::TwitterAccount* account )
: m_account( account )
{
m_supportedPushTypes << InfoLove;
}
void
TwitterInfoPlugin::init()
{
if ( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() && thread() != Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() )
{
tDebug() << "Failure: move to the worker thread before running init";
return;
}
QVariantHash credentials = m_account->credentials();
if ( credentials[ "oauthtoken" ].toString().isEmpty() || credentials[ "oauthtokensecret" ].toString().isEmpty() )
{
tDebug() << "TwitterInfoPlugin has empty Twitter credentials; not connecting";
return;
}
if ( refreshTwitterAuth() )
{
QTweetAccountVerifyCredentials *credVerifier = new QTweetAccountVerifyCredentials( m_twitterAuth.data(), this );
connect( credVerifier, SIGNAL( parsedUser( const QTweetUser & ) ), SLOT( connectAuthVerifyReply( const QTweetUser & ) ) );
credVerifier->verify();
}
}
TwitterInfoPlugin::~TwitterInfoPlugin()
{
tDebug() << Q_FUNC_INFO;
}
bool
TwitterInfoPlugin::refreshTwitterAuth()
{
tDebug() << Q_FUNC_INFO << " begin" << this;
if( !m_twitterAuth.isNull() )
delete m_twitterAuth.data();
Q_ASSERT( TomahawkUtils::nam() != 0 );
tDebug() << Q_FUNC_INFO << " with nam " << TomahawkUtils::nam() << this;
m_twitterAuth = QWeakPointer< TomahawkOAuthTwitter >( new TomahawkOAuthTwitter( TomahawkUtils::nam(), this ) );
if( m_twitterAuth.isNull() )
return false;
m_twitterAuth.data()->setOAuthToken( m_account->credentials()[ "oauthtoken" ].toString().toLatin1() );
m_twitterAuth.data()->setOAuthTokenSecret( m_account->credentials()[ "oauthtokensecret" ].toString().toLatin1() );
return true;
}
void
TwitterInfoPlugin::connectAuthVerifyReply( const QTweetUser &user )
{
if ( user.id() == 0 )
{
tDebug() << "TwitterInfoPlugin could not authenticate to Twitter" << this;
deleteLater();
return;
}
else
{
tDebug() << "TwitterInfoPlugin successfully authenticated to Twitter" << this;
return;
}
}
void
TwitterInfoPlugin::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData )
{
tDebug() << Q_FUNC_INFO;
if ( !isValid() )
{
tDebug() << Q_FUNC_INFO << "Plugin not valid, deleting and returning";
deleteLater();
return;
}
Tomahawk::InfoSystem::PushInfoPair pushInfoPair = pushData.infoPair;
if ( !pushInfoPair.second.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
{
tDebug() << Q_FUNC_INFO << "Cannot convert input into an info string hash";
return;
}
Tomahawk::InfoSystem::InfoStringHash info = pushInfoPair.second.value< Tomahawk::InfoSystem::InfoStringHash >();
QString msg = tr( "Listening to \"%1\" by %2 and loving it! %3" )
.arg( info[ "title" ] )
.arg( info[ "artist" ] )
.arg( pushInfoPair.first.contains( "shorturl" ) ?
pushInfoPair.first[ "shorturl" ].toUrl().toString() :
GlobalActionManager::instance()->openLink( info[ "title" ], info[ "artist" ], info[ "album" ] ).toString() );
QTweetStatusUpdate *statUpdate = new QTweetStatusUpdate( m_twitterAuth.data(), this );
connect( statUpdate, SIGNAL( postedStatus(const QTweetStatus &) ), SLOT( postLovedStatusUpdateReply(const QTweetStatus &) ) );
connect( statUpdate, SIGNAL( error(QTweetNetBase::ErrorCode, const QString&) ), SLOT( postLovedStatusUpdateError(QTweetNetBase::ErrorCode, const QString &) ) );
tDebug() << Q_FUNC_INFO << "Posting message: " << msg;
statUpdate->post( msg );
}
void
TwitterInfoPlugin::postLovedStatusUpdateReply( const QTweetStatus& status )
{
if ( status.id() == 0 )
tDebug() << Q_FUNC_INFO << "Failed to post loved status";
else
tDebug() << Q_FUNC_INFO << "Successfully posted loved status";
}
void
TwitterInfoPlugin::postLovedStatusUpdateError( QTweetNetBase::ErrorCode code, const QString& errorMsg )
{
tDebug() << Q_FUNC_INFO << "Error posting love message, error code is " << code << ", error message is " << errorMsg;
}
bool
TwitterInfoPlugin::isValid() const
{
return !m_twitterAuth.isNull();
}
}
}

View File

@ -0,0 +1,80 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2012, Dominik Schmidt <domme@tomahawk-player.org>
* Copyright 2012, Jeff Mitchell <jeff@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TWITTERINFOPLUGIN_H
#define TWITTERINFOPLUGIN_H
#include "infosystem/infosystem.h"
#include "accounts/twitter/tomahawkoauthtwitter.h"
#include <QTweetLib/qtweetuser.h>
#include <QTweetLib/qtweetstatus.h>
#include <QTweetLib/qtweetnetbase.h>
namespace Tomahawk {
namespace Accounts {
class TwitterAccount;
}
namespace InfoSystem {
class TwitterInfoPlugin : public InfoPlugin
{
Q_OBJECT
public:
TwitterInfoPlugin( Tomahawk::Accounts::TwitterAccount* account );
virtual ~TwitterInfoPlugin();
public slots:
void init();
void notInCacheSlot( const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
{
Q_UNUSED( criteria );
Q_UNUSED( requestData );
}
protected slots:
void pushInfo( Tomahawk::InfoSystem::InfoPushData pushData );
void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
{
Q_UNUSED( requestData );
}
private slots:
void connectAuthVerifyReply( const QTweetUser &user );
void postLovedStatusUpdateReply( const QTweetStatus& status );
void postLovedStatusUpdateError( QTweetNetBase::ErrorCode code, const QString& errorMsg );
private:
bool refreshTwitterAuth();
bool isValid() const;
Tomahawk::Accounts::TwitterAccount* m_account;
QWeakPointer< TomahawkOAuthTwitter > m_twitterAuth;
};
}
}
#endif // TWITTERINFOPLUGIN_H
struct A;

View File

@ -1,6 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2012, Dominik Schmidt <domme@tomahawk-player.org>
* Copyright 2012, Jeff Mitchell <jeff@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -23,31 +24,18 @@
#include "sip/xmppsip.h"
#include "utils/logger.h"
#include <jreen/tune.h>
#include <jreen/pubsubmanager.h>
#include <jreen/jid.h>
#include <jreen/client.h>
// remove now playing status after PAUSE_TIMEOUT seconds
static const int PAUSE_TIMEOUT = 60;
static const int PAUSE_TIMEOUT = 10;
Tomahawk::InfoSystem::XmppInfoPlugin::XmppInfoPlugin(XmppSipPlugin* sipPlugin)
Tomahawk::InfoSystem::XmppInfoPlugin::XmppInfoPlugin( XmppSipPlugin* sipPlugin )
: m_sipPlugin( sipPlugin )
, m_pubSubManager( 0 )
, m_pauseTimer( this )
{
Q_ASSERT( sipPlugin->m_client );
m_supportedPushTypes << InfoNowPlaying << InfoNowPaused << InfoNowResumed << InfoNowStopped;
m_pubSubManager = new Jreen::PubSub::Manager( sipPlugin->m_client );
m_pubSubManager->addEntityType< Jreen::Tune >();
// Clear status
Jreen::Tune::Ptr tune( new Jreen::Tune() );
m_pubSubManager->publishItems(QList<Jreen::Payload::Ptr>() << tune, Jreen::JID());
m_pauseTimer.setSingleShot( true );
connect( &m_pauseTimer, SIGNAL( timeout() ),
this, SLOT( audioStopped() ) );
@ -56,24 +44,28 @@ Tomahawk::InfoSystem::XmppInfoPlugin::XmppInfoPlugin(XmppSipPlugin* sipPlugin)
Tomahawk::InfoSystem::XmppInfoPlugin::~XmppInfoPlugin()
{
//Note: the next two lines don't currently work, because the deletion wipes out internally posted events, need to talk to euro about a fix
Jreen::Tune::Ptr tune( new Jreen::Tune() );
m_pubSubManager->publishItems(QList<Jreen::Payload::Ptr>() << tune, Jreen::JID());
m_pubSubManager->deleteLater();
}
void
Tomahawk::InfoSystem::XmppInfoPlugin::init()
{
if ( QThread::currentThread() != Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() )
{
QMetaObject::invokeMethod( this, "init", Qt::QueuedConnection );
return;
}
if ( m_sipPlugin.isNull() )
return;
connect( this, SIGNAL( publishTune( QUrl, Tomahawk::InfoSystem::InfoStringHash ) ), m_sipPlugin.data(), SLOT( publishTune( QUrl, Tomahawk::InfoSystem::InfoStringHash ) ), Qt::QueuedConnection );
}
void
Tomahawk::InfoSystem::XmppInfoPlugin::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData )
{
tDebug() << Q_FUNC_INFO << m_sipPlugin->m_client->jid().full();
if( m_sipPlugin->m_account->configuration().value("publishtracks").toBool() == false )
{
tDebug() << Q_FUNC_INFO << m_sipPlugin->m_client->jid().full() << "Not publishing now playing info (disabled in account config)";
return;
}
switch ( pushData.type )
{
case InfoNowPlaying:
@ -108,8 +100,7 @@ Tomahawk::InfoSystem::XmppInfoPlugin::audioStarted( const Tomahawk::InfoSystem::
QVariantMap map = pushInfoPair.second.toMap();
if ( map.contains( "private" ) && map[ "private" ] == TomahawkSettings::FullyPrivate )
{
Jreen::Tune::Ptr tune( new Jreen::Tune() );
m_pubSubManager->publishItems( QList<Jreen::Payload::Ptr>() << tune, Jreen::JID() );
emit publishTune( QUrl(), Tomahawk::InfoSystem::InfoStringHash() );
return;
}
@ -120,42 +111,25 @@ Tomahawk::InfoSystem::XmppInfoPlugin::audioStarted( const Tomahawk::InfoSystem::
}
Tomahawk::InfoSystem::InfoStringHash info = map[ "trackinfo" ].value< Tomahawk::InfoSystem::InfoStringHash >();
tDebug() << Q_FUNC_INFO << m_sipPlugin->m_client->jid().full() << info;
Jreen::Tune::Ptr tune( new Jreen::Tune() );
tune->setTitle( info.value( "title" ) );
tune->setArtist( info.value( "artist" ) );
tune->setLength( info.value("duration").toInt() );
tune->setTrack( info.value("albumpos") );
QUrl url;
if ( pushInfoPair.first.contains( "shorturl" ) )
tune->setUri( pushInfoPair.first[ "shorturl" ].toUrl() );
url = pushInfoPair.first[ "shorturl" ].toUrl();
else
tune->setUri( GlobalActionManager::instance()->openLink( info.value( "title" ), info.value( "artist" ), info.value( "album" ) ) );
url = GlobalActionManager::instance()->openLink( info.value( "title" ), info.value( "artist" ), info.value( "album" ) );
tDebug() << Q_FUNC_INFO << "Setting URI of " << tune->uri().toString();
//TODO: provide a rating once available in Tomahawk
tune->setRating( 10 );
//TODO: it would be nice to set Spotify, Dilandau etc here, but not the jabber ids of friends
tune->setSource( "Tomahawk" );
m_pubSubManager->publishItems( QList<Jreen::Payload::Ptr>() << tune, Jreen::JID() );
emit publishTune( url, info );
}
void
Tomahawk::InfoSystem::XmppInfoPlugin::audioPaused()
{
tDebug() << Q_FUNC_INFO << m_sipPlugin->m_client->jid().full();
}
void
Tomahawk::InfoSystem::XmppInfoPlugin::audioStopped()
{
tDebug() << Q_FUNC_INFO << m_sipPlugin->m_client->jid().full();
Jreen::Tune::Ptr tune( new Jreen::Tune() );
m_pubSubManager->publishItems(QList<Jreen::Payload::Ptr>() << tune, Jreen::JID());
emit publishTune( QUrl(), Tomahawk::InfoSystem::InfoStringHash() );
}

View File

@ -1,6 +1,7 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2012, Dominik Schmidt <domme@tomahawk-player.org>
* Copyright 2012, Jeff Mitchell <jeff@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -23,12 +24,6 @@
#include <QTimer>
namespace Jreen {
namespace PubSub {
class Manager;
}
}
class XmppSipPlugin;
namespace Tomahawk {
@ -43,7 +38,11 @@ namespace Tomahawk {
XmppInfoPlugin(XmppSipPlugin* parent);
virtual ~XmppInfoPlugin();
signals:
void publishTune( QUrl url, Tomahawk::InfoSystem::InfoStringHash trackInfo );
public slots:
void init();
void notInCacheSlot( const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData );
protected slots:
@ -56,8 +55,7 @@ namespace Tomahawk {
void audioPaused();
private:
XmppSipPlugin* m_sipPlugin;
Jreen::PubSub::Manager* m_pubSubManager;
QWeakPointer< XmppSipPlugin > m_sipPlugin;
QTimer m_pauseTimer;
};

View File

@ -35,6 +35,7 @@
#include <jreen/softwareversion.h>
#include <jreen/iqreply.h>
#include <jreen/logger.h>
#include <jreen/tune.h>
#include <qjson/parser.h>
#include <qjson/serializer.h>
@ -86,11 +87,11 @@ JreenMessageHandler(QtMsgType type, const char *msg)
XmppSipPlugin::XmppSipPlugin( Account *account )
: SipPlugin( account )
, m_infoPlugin( 0 )
, m_state( Account::Disconnected )
#ifndef ENABLE_HEADLESS
, m_menu( 0 )
, m_xmlConsole( 0 )
, m_pubSubManager( 0 )
#endif
{
Jreen::Logger::addHandler( JreenMessageHandler );
@ -161,11 +162,22 @@ XmppSipPlugin::XmppSipPlugin( Account *account )
#ifndef ENABLE_HEADLESS
connect(m_avatarManager, SIGNAL(newAvatar(QString)), SLOT(onNewAvatar(QString)));
#endif
m_pubSubManager = new Jreen::PubSub::Manager( m_client );
m_pubSubManager->addEntityType< Jreen::Tune >();
// Clear status
Jreen::Tune::Ptr tune( new Jreen::Tune() );
m_pubSubManager->publishItems(QList<Jreen::Payload::Ptr>() << tune, Jreen::JID());
}
XmppSipPlugin::~XmppSipPlugin()
{
delete m_infoPlugin;
//Note: the next two lines don't currently work, because the deletion wipes out internally posted events, need to talk to euro about a fix
Jreen::Tune::Ptr tune( new Jreen::Tune() );
m_pubSubManager->publishItems(QList<Jreen::Payload::Ptr>() << tune, Jreen::JID());
delete m_pubSubManager;
delete m_avatarManager;
delete m_roster;
#ifndef ENABLE_HEADLESS
@ -175,10 +187,13 @@ XmppSipPlugin::~XmppSipPlugin()
}
InfoSystem::InfoPlugin*
InfoSystem::InfoPluginPtr
XmppSipPlugin::infoPlugin()
{
return m_infoPlugin;
if ( m_infoPlugin.isNull() )
m_infoPlugin = QWeakPointer< Tomahawk::InfoSystem::XmppInfoPlugin >( new Tomahawk::InfoSystem::XmppInfoPlugin( this ) );
return InfoSystem::InfoPluginPtr( m_infoPlugin.data() );
}
@ -241,6 +256,8 @@ XmppSipPlugin::disconnectPlugin()
m_peers.clear();
publishTune( QUrl(), Tomahawk::InfoSystem::InfoStringHash() );
m_client->disconnectFromServer( true );
m_state = Account::Disconnecting;
emit stateChanged( m_state );
@ -272,10 +289,11 @@ XmppSipPlugin::onConnect()
m_roster->load();
// load XmppInfoPlugin
if( !m_infoPlugin )
if ( infoPlugin() && Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() )
{
m_infoPlugin = new Tomahawk::InfoSystem::XmppInfoPlugin( this );
InfoSystem::InfoSystem::instance()->addInfoPlugin( m_infoPlugin );
infoPlugin().data()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() );
QMetaObject::invokeMethod( infoPlugin().data(), "init", Qt::QueuedConnection );
}
//FIXME: this implementation is totally broken atm, so it's disabled to avoid harm :P
@ -340,6 +358,9 @@ XmppSipPlugin::onDisconnect( Jreen::Client::DisconnectReason reason )
{
handlePeerStatus(peer, Jreen::Presence::Unavailable);
}
if ( !m_infoPlugin.isNull() )
Tomahawk::InfoSystem::InfoSystem::instance()->removeInfoPlugin( infoPlugin() );
}
@ -509,6 +530,41 @@ XmppSipPlugin::showAddFriendDialog()
}
void
XmppSipPlugin::publishTune( const QUrl& url, const InfoSystem::InfoStringHash& trackInfo )
{
if( m_account->configuration().value("publishtracks").toBool() == false )
{
tDebug() << Q_FUNC_INFO << m_client->jid().full() << "Not publishing now playing info (disabled in account config)";
return;
}
if ( trackInfo.isEmpty() )
{
Jreen::Tune::Ptr tune( new Jreen::Tune() );
m_pubSubManager->publishItems(QList<Jreen::Payload::Ptr>() << tune, Jreen::JID());
}
Jreen::Tune::Ptr tune( new Jreen::Tune() );
tune->setTitle( trackInfo.value( "title" ) );
tune->setArtist( trackInfo.value( "artist" ) );
tune->setLength( trackInfo.value("duration").toInt() );
tune->setTrack( trackInfo.value("albumpos") );
//TODO: provide a rating once available in Tomahawk
tune->setRating( 10 );
//TODO: it would be nice to set Spotify, Dilandau etc here, but not the jabber ids of friends
tune->setSource( "Tomahawk" );
tune->setUri( url );
tDebug() << Q_FUNC_INFO << "Setting URI of " << tune->uri().toString();
m_pubSubManager->publishItems( QList<Jreen::Payload::Ptr>() << tune, Jreen::JID() );
}
QString
XmppSipPlugin::defaultSuffix() const
{

View File

@ -42,6 +42,7 @@
#include <jreen/abstractroster.h>
#include <jreen/connection.h>
#include <jreen/mucroom.h>
#include <jreen/pubsubmanager.h>
#ifndef ENABLE_HEADLESS
#include <QtGui/QMessageBox>
@ -67,7 +68,7 @@ public:
//FIXME: Make this more correct
virtual bool isValid() const { return true; }
Tomahawk::InfoSystem::InfoPlugin* infoPlugin();
Tomahawk::InfoSystem::InfoPluginPtr infoPlugin();
#ifndef ENABLE_HEADLESS
virtual QMenu* menu();
@ -92,6 +93,7 @@ public slots:
void broadcastMsg( const QString &msg );
virtual void addContact( const QString &jid, const QString& msg = QString() );
void showAddFriendDialog();
void publishTune( const QUrl &url, const Tomahawk::InfoSystem::InfoStringHash &trackInfo );
protected:
virtual QString defaultSuffix() const;
@ -131,7 +133,7 @@ private:
int m_currentPort;
QString m_currentResource;
Tomahawk::InfoSystem::InfoPlugin* m_infoPlugin;
QWeakPointer< Tomahawk::InfoSystem::XmppInfoPlugin> m_infoPlugin;
Tomahawk::Accounts::Account::ConnectionState m_state;
// sort out
@ -147,6 +149,7 @@ private:
#endif
enum IqContext { NoContext, RequestDisco, RequestedDisco, SipMessageSent, RequestedVCard, RequestVersion, RequestedVersion };
AvatarManager *m_avatarManager;
Jreen::PubSub::Manager* m_pubSubManager;
};
#endif

View File

@ -91,13 +91,13 @@ XmppAccount::saveConfig()
}
InfoSystem::InfoPlugin*
InfoSystem::InfoPluginPtr
XmppAccount::infoPlugin()
{
if( !m_xmppSipPlugin.isNull() )
return m_xmppSipPlugin.data()->infoPlugin();
return 0;
return InfoSystem::InfoPluginPtr();
}

View File

@ -51,7 +51,7 @@ public:
QString description() const { return tr( "Log on to your Jabber/XMPP account to connect to your friends" ); }
QString factoryId() const { return "xmppaccount"; }
QPixmap icon() const { return QPixmap( ":/xmpp-icon.png" ); }
AccountTypes types() const { return AccountTypes( SipType ); };
AccountTypes types() const { return AccountTypes( SipType | StatusPushType ); };
Account* createAccount( const QString& pluginId = QString() );
};
@ -69,7 +69,7 @@ public:
void deauthenticate();
bool isAuthenticated() const;
Tomahawk::InfoSystem::InfoPlugin* infoPlugin();
Tomahawk::InfoSystem::InfoPluginPtr infoPlugin();
SipPlugin* sipPlugin();

View File

@ -64,7 +64,7 @@ public:
bool isAuthenticated() const;
ConnectionState connectionState() const;
Tomahawk::InfoSystem::InfoPlugin* infoPlugin() { return 0; }
virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin() { return Tomahawk::InfoSystem::InfoPluginPtr(); }
SipPlugin* sipPlugin();
QWidget* configurationWidget() { return 0; }

View File

@ -37,6 +37,8 @@ accountTypeToString( AccountType type )
case InfoType:
case StatusPushType:
return QObject::tr( "Status Updaters" );
case NoType:
return QString();
}
return QString();

View File

@ -31,16 +31,13 @@
#include "dllmacro.h"
#include "tomahawksettings.h"
#include "libtomahawk/infosystem/infosystem.h"
class SipPlugin;
namespace Tomahawk
{
namespace InfoSystem
{
class InfoPlugin;
}
namespace Accounts
{
@ -100,7 +97,7 @@ public:
virtual QString errorMessage() const { QMutexLocker locker( &m_mutex ); return m_cachedError; }
virtual Tomahawk::InfoSystem::InfoPlugin* infoPlugin() = 0;
virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin() = 0;
virtual SipPlugin* sipPlugin() = 0;
AccountTypes types() const;

View File

@ -51,14 +51,7 @@ AccountManager::AccountManager( QObject *parent )
{
s_instance = this;
connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( onSettingsChanged() ) );
loadPluginFactories( findPluginFactories() );
// We include the resolver factory manually, not in a plugin
ResolverAccountFactory* f = new ResolverAccountFactory();
m_accountFactories[ f->factoryId() ] = f;
registerAccountFactoryForFilesystem( f );
QTimer::singleShot( 0, this, SLOT( init() ) );
}
@ -72,6 +65,29 @@ AccountManager::~AccountManager()
}
void
AccountManager::init()
{
if ( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().isNull() )
{
//We need the info system worker to be alive so that we can move info plugins into its thread
QTimer::singleShot( 0, this, SLOT( init() ) );
return;
}
connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( onSettingsChanged() ) );
loadPluginFactories( findPluginFactories() );
// We include the resolver factory manually, not in a plugin
ResolverAccountFactory* f = new ResolverAccountFactory();
m_accountFactories[ f->factoryId() ] = f;
registerAccountFactoryForFilesystem( f );
emit ready();
}
QStringList
AccountManager::findPluginFactories()
{
@ -180,6 +196,7 @@ AccountManager::loadPluginFactory( const QString& path )
void
AccountManager::enableAccount( Account* account )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
if ( account->enabled() )
return;
@ -195,6 +212,7 @@ AccountManager::enableAccount( Account* account )
void
AccountManager::disableAccount( Account* account )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
if ( !account->enabled() )
return;
@ -209,6 +227,7 @@ AccountManager::disableAccount( Account* account )
void
AccountManager::connectAll()
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
foreach( Account* acc, m_accounts )
{
acc->authenticate();
@ -222,6 +241,7 @@ AccountManager::connectAll()
void
AccountManager::disconnectAll()
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
foreach( Account* acc, m_enabledAccounts )
acc->deauthenticate();
@ -234,6 +254,7 @@ AccountManager::disconnectAll()
void
AccountManager::toggleAccountsConnected()
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
if ( m_connected )
disconnectAll();
else
@ -297,9 +318,6 @@ AccountManager::addAccount( Account* account )
if ( account->types() & Accounts::StatusPushType )
m_accountsByAccountType[ Accounts::StatusPushType ].append( account );
if ( account->infoPlugin() )
InfoSystem::InfoSystem::instance()->addInfoPlugin( account->infoPlugin() );
emit added( account );
}
@ -370,6 +388,7 @@ AccountManager::hookupAccount( Account* account ) const
void
AccountManager::hookupAndEnable( Account* account, bool startup )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
SipPlugin* p = account->sipPlugin();
if ( p )
SipHandler::instance()->hookUpPlugin( p );

View File

@ -43,7 +43,7 @@ public:
explicit AccountManager( QObject *parent );
virtual ~AccountManager();
void loadFromConfig();
void initSIP();
@ -84,6 +84,8 @@ public slots:
void toggleAccountsConnected();
signals:
void ready();
void added( Tomahawk::Accounts::Account* );
void removed( Tomahawk::Accounts::Account* );
@ -94,6 +96,7 @@ signals:
void stateChanged( Account* p, Accounts::Account::ConnectionState state );
private slots:
void init();
void onStateChanged( Tomahawk::Accounts::Account::ConnectionState state );
void onError( int code, const QString& msg );

View File

@ -80,7 +80,7 @@ public:
// Not relevant
virtual QPixmap icon() const { return QPixmap(); }
virtual SipPlugin* sipPlugin() { return 0; }
virtual Tomahawk::InfoSystem::InfoPlugin* infoPlugin() { return 0; }
virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin() { return Tomahawk::InfoSystem::InfoPluginPtr(); }
virtual QWidget* aclWidget() { return 0; }
private slots:

View File

@ -327,8 +327,8 @@ AudioEngine::sendNowPlayingNotification( const Tomahawk::InfoSystem::InfoType ty
onNowPlayingInfoReady( type );
else
{
_detail::Closure* closure = NewClosure( m_currentTrack->album().data(), SIGNAL( updated() ), const_cast< AudioEngine* >( this ), SLOT( onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType ) ), type );
m_currentTrack->album()->cover( QSize( 0, 0 ) );
NewClosure( m_currentTrack->album().data(), SIGNAL( updated() ), const_cast< AudioEngine* >( this ), SLOT( onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType ) ), type );
m_currentTrack->album()->cover( QSize( 0, 0 ), true );
}
#endif
}
@ -337,14 +337,11 @@ AudioEngine::sendNowPlayingNotification( const Tomahawk::InfoSystem::InfoType ty
void
AudioEngine::onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType type )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << type;
if ( m_currentTrack.isNull() ||
m_currentTrack->track().isNull() ||
m_currentTrack->artist().isNull() )
return;
if ( !m_currentTrack->album().isNull() && sender() && m_currentTrack->album().data() != sender() )
return;
QVariantMap playInfo;
@ -353,26 +350,29 @@ AudioEngine::onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType type )
#ifndef ENABLE_HEADLESS
QImage cover;
cover = m_currentTrack->album()->cover( QSize( 0, 0 ) ).toImage();
playInfo["cover"] = cover;
QTemporaryFile coverTempFile( QDir::toNativeSeparators( QDir::tempPath() + "/" + m_currentTrack->artist()->name() + "_" + m_currentTrack->album()->name() + "_tomahawk_cover.png" ) );
if ( !coverTempFile.open() )
if ( !cover.isNull() )
{
tDebug() << "WARNING: could not write temporary file for cover art!";
}
playInfo["cover"] = cover;
// Finally, save the image to the new temp file
if ( cover.save( &coverTempFile, "PNG" ) )
{
tDebug( LOGVERBOSE ) << "Saving cover image to:" << QFileInfo( coverTempFile ).absoluteFilePath();
coverTempFile.close();
playInfo["coveruri"] = QFileInfo( coverTempFile ).absoluteFilePath();
QTemporaryFile* coverTempFile = new QTemporaryFile( QDir::toNativeSeparators( QDir::tempPath() + "/" + m_currentTrack->artist()->name() + "_" + m_currentTrack->album()->name() + "_tomahawk_cover.png" ) );
if ( !coverTempFile->open() )
tDebug() << Q_FUNC_INFO << "WARNING: could not write temporary file for cover art!";
else
{
// Finally, save the image to the new temp file
coverTempFile->setAutoRemove( false );
if ( cover.save( coverTempFile, "PNG" ) )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Saving cover image to:" << QFileInfo( *coverTempFile ).absoluteFilePath();
playInfo["coveruri"] = QFileInfo( *coverTempFile ).absoluteFilePath();
}
else
tDebug() << Q_FUNC_INFO << "failed to save cover image!";
}
delete coverTempFile;
}
else
{
tDebug() << Q_FUNC_INFO << "failed to save cover image!";
coverTempFile.close();
}
tDebug() << Q_FUNC_INFO << "Cover from album is null!";
#endif
}
@ -388,6 +388,7 @@ AudioEngine::onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType type )
Tomahawk::InfoSystem::InfoPushData pushData ( s_aeInfoIdentifier, type, playInfo, Tomahawk::InfoSystem::PushShortUrlFlag );
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "pushing data with type " << type;
Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( pushData );
}

View File

@ -125,6 +125,7 @@ GlobalActionManager::openLink( const QString& title, const QString& artist, cons
void
GlobalActionManager::shortenLink( const QUrl& url, const QVariant& callbackObj )
{
tDebug() << Q_FUNC_INFO << "callbackObj is valid: " << ( callbackObj.isValid() ? "true" : "false" );
if ( QThread::currentThread() != thread() )
{
qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO;
@ -136,7 +137,7 @@ GlobalActionManager::shortenLink( const QUrl& url, const QVariant& callbackObj )
request.setUrl( url );
QNetworkReply *reply = TomahawkUtils::nam()->get( request );
if ( !callbackObj.isValid() )
if ( callbackObj.isValid() )
reply->setProperty( "callbackobj", callbackObj );
connect( reply, SIGNAL( finished() ), SLOT( shortenLinkRequestFinished() ) );
connect( reply, SIGNAL( error( QNetworkReply::NetworkError ) ), SLOT( shortenLinkRequestError( QNetworkReply::NetworkError ) ) );
@ -1013,7 +1014,7 @@ GlobalActionManager::shortenLinkRequestFinished()
}
QVariant callbackObj;
if ( reply->property( "callbackobj" ).canConvert< QVariant >() && reply->property( "callbackobj" ).isValid() )
if ( reply->property( "callbackobj" ).isValid() )
callbackObj = reply->property( "callbackobj" );
// Check for the redirect attribute, as this should be the shortened link

View File

@ -49,9 +49,9 @@
using namespace Tomahawk::InfoSystem;
FdoNotifyPlugin::FdoNotifyPlugin()
: InfoPlugin()
, m_nowPlayingId( 0 )
{
qDebug() << Q_FUNC_INFO;
m_supportedPushTypes << InfoNotifyUser << InfoNowPlaying << InfoTrackUnresolved << InfoNowStopped;
@ -119,6 +119,7 @@ FdoNotifyPlugin::notifyUser( const QString &messageText )
void
FdoNotifyPlugin::nowPlaying( const QVariant &input )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
if ( !input.canConvert< QVariantMap >() )
return;
@ -135,23 +136,29 @@ FdoNotifyPlugin::nowPlaying( const QVariant &input )
.arg( hash[ "title" ] )
.arg( hash[ "artist" ] )
.arg( hash[ "album" ].isEmpty() ? QString() : QString( " %1" ).arg( tr( "on \"%1\"" ).arg( hash[ "album" ] ) ) );
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "sending message" << messageText;
QDBusMessage message = QDBusMessage::createMethodCall( "org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", "Notify" );
QList<QVariant> arguments;
arguments << QString( "Tomahawk" ); //app_name
arguments << quint32( 0 ); //notification_id
arguments << m_nowPlayingId; //notification_id
arguments << QString(); //app_icon
arguments << QString( "Tomahawk" ); //summary
arguments << messageText; //body
arguments << QStringList(); //actions
QVariantMap dict;
dict["desktop-entry"] = QString( "tomahawk" );
if ( map.contains( "cover" ) && map[ "cover" ].canConvert< QImage >() )
dict[ "image_data" ] = ImageConverter::variantForImage( map[ "cover" ].value< QImage >() );
if ( map.contains( "coveruri" ) && map[ "coveruri" ].canConvert< QString >() )
dict[ "image_data" ] = ImageConverter::variantForImage( QImage( map[ "coveruri" ].toString(), "PNG" ) );
else
dict[ "image_data" ] = ImageConverter::variantForImage( QImage( RESPATH "icons/tomahawk-icon-128x128.png" ) );
arguments << dict; //hints
arguments << qint32( -1 ); //expire_timeout
message.setArguments( arguments );
QDBusConnection::sessionBus().send( message );
const QDBusMessage &reply = QDBusConnection::sessionBus().call( message );
const QVariantList &list = reply.arguments();
if ( list.count() > 0 )
m_nowPlayingId = list.at( 0 ).toInt();
}

View File

@ -54,6 +54,8 @@ private:
void notifyUser( const QString &messageText );
void nowPlaying( const QVariant &input );
quint32 m_nowPlayingId;
};
}

View File

@ -137,7 +137,7 @@ InfoSystem::init()
bool
InfoSystem::getInfo( const InfoRequestData &requestData )
{
qDebug() << Q_FUNC_INFO;
//qDebug() << Q_FUNC_INFO;
if ( !m_inited || !m_infoSystemWorkerThreadController->worker() )
{
init();
@ -210,15 +210,66 @@ InfoSystem::pushInfo( const QString &caller, const InfoTypeMap &input, const Pus
void
InfoSystem::addInfoPlugin( InfoPlugin* plugin )
InfoSystem::addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin )
{
// Init is not complete (waiting for worker thread to start and create worker object) so keep trying till then
if ( !m_inited || !m_infoSystemWorkerThreadController->worker() )
{
QMetaObject::invokeMethod( this, "addInfoPlugin", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPluginPtr, plugin ) );
return;
}
if ( plugin.isNull() )
{
tDebug() << Q_FUNC_INFO << "Given plugin is null!";
return;
}
if ( plugin.data()->thread() != m_infoSystemWorkerThreadController->worker()->thread() )
{
tDebug() << Q_FUNC_INFO << "The object must be moved to the worker thread first, see InfoSystem::workerThread()";
return;
}
tDebug() << Q_FUNC_INFO << plugin.data();
QMetaObject::invokeMethod( m_infoSystemWorkerThreadController->worker(), "addInfoPlugin", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPluginPtr, plugin ) );
}
void
InfoSystem::removeInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin )
{
// Init is not complete (waiting for worker th read to start and create worker object) so keep trying till then
if ( !m_inited || !m_infoSystemWorkerThreadController->worker() )
{
QMetaObject::invokeMethod( this, "addInfoPlugin", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPlugin*, plugin ) );
QMetaObject::invokeMethod( this, "removeInfoPlugin", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPluginPtr, plugin ) );
return;
}
QMetaObject::invokeMethod( m_infoSystemWorkerThreadController->worker(), "addInfoPlugin", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPlugin*, plugin ) );
if ( plugin.isNull() )
{
tDebug() << Q_FUNC_INFO << "Given plugin is null!";
return;
}
if ( plugin.data()->thread() != m_infoSystemWorkerThreadController->worker()->thread() )
{
tDebug() << Q_FUNC_INFO << "The object must be moved to the worker thread first, see InfoSystem::workerThread()";
return;
}
tDebug() << Q_FUNC_INFO << plugin.data();
QMetaObject::invokeMethod( m_infoSystemWorkerThreadController->worker(), "removeInfoPlugin", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPluginPtr, plugin ) );
}
QWeakPointer< QThread >
InfoSystem::workerThread() const
{
if ( m_infoSystemWorkerThreadController->isRunning() && m_infoSystemWorkerThreadController->worker() )
return QWeakPointer< QThread >( m_infoSystemWorkerThreadController->worker()->thread() );
return QWeakPointer< QThread >();
}

View File

@ -282,9 +282,12 @@ public:
bool pushInfo( InfoPushData pushData );
bool pushInfo( const QString &caller, const InfoTypeMap &input, const PushInfoFlags pushFlags );
QWeakPointer< QThread > workerThread() const;
public slots:
// InfoSystem takes ownership of InfoPlugins
void addInfoPlugin( Tomahawk::InfoSystem::InfoPlugin* plugin );
void addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin );
void removeInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin );
signals:
void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
@ -307,7 +310,6 @@ private:
}
inline uint qHash( Tomahawk::InfoSystem::InfoStringHash hash )
{
QCryptographicHash md5( QCryptographicHash::Md5 );
@ -329,6 +331,7 @@ inline uint qHash( Tomahawk::InfoSystem::InfoStringHash hash )
return returnval;
}
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoRequestData );
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoPushData );
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoStringHash );
@ -337,6 +340,7 @@ Q_DECLARE_METATYPE( Tomahawk::InfoSystem::PushInfoFlags );
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoType );
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoSystemCache* );
Q_DECLARE_METATYPE( QList< Tomahawk::InfoSystem::InfoStringHash > );
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoPluginPtr );
Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoPlugin* );
#endif // TOMAHAWK_INFOSYSTEM_H

View File

@ -159,7 +159,7 @@ InfoSystemCache::getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash criteri
if ( !fileLocationHash.isEmpty() )
{
//We already know of some values, so no need to re-read the directory again as it's already happened
qDebug() << Q_FUNC_INFO << "notInCache -- filelocationhash empty";
//qDebug() << Q_FUNC_INFO << "notInCache -- filelocationhash empty";
notInCache( sendingObj, criteria, requestData );
return;
}
@ -169,7 +169,7 @@ InfoSystemCache::getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash criteri
if ( !dir.exists() )
{
//Dir doesn't exist so clearly not in cache
qDebug() << Q_FUNC_INFO << "notInCache -- dir doesn't exist";
//qDebug() << Q_FUNC_INFO << "notInCache -- dir doesn't exist";
notInCache( sendingObj, criteria, requestData );
return;
}
@ -186,7 +186,7 @@ InfoSystemCache::getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash criteri
if ( !fileLocationHash.contains( criteriaHashVal ) )
{
//Still didn't find it? It's really not in the cache then
qDebug() << Q_FUNC_INFO << "notInCache -- filelocationhash doesn't contain criteria val";
//qDebug() << Q_FUNC_INFO << "notInCache -- filelocationhash doesn't contain criteria val";
notInCache( sendingObj, criteria, requestData );
return;
}
@ -250,7 +250,7 @@ InfoSystemCache::notInCache( QObject *receiver, Tomahawk::InfoSystem::InfoString
void
InfoSystemCache::updateCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 maxAge, Tomahawk::InfoSystem::InfoType type, QVariant output )
{
qDebug() << Q_FUNC_INFO;
//qDebug() << Q_FUNC_INFO;
const QString criteriaHashVal = criteriaMd5( criteria );
const QString criteriaHashValWithType = criteriaMd5( criteria, type );
const QString cacheDir = m_cacheBaseDir + QString::number( (int)type );

View File

@ -82,36 +82,51 @@ InfoSystemWorker::init( Tomahawk::InfoSystem::InfoSystemCache* cache )
m_shortLinksWaiting = 0;
m_cache = cache;
#ifndef ENABLE_HEADLESS
addInfoPlugin( new EchoNestPlugin() );
addInfoPlugin( new MusixMatchPlugin() );
addInfoPlugin( new MusicBrainzPlugin() );
addInfoPlugin( new ChartsPlugin() );
addInfoPlugin( new RoviPlugin() );
addInfoPlugin( new SpotifyPlugin() );
addInfoPlugin( new hypemPlugin() );
addInfoPlugin( InfoPluginPtr( new EchoNestPlugin() ) );
addInfoPlugin( InfoPluginPtr( new MusixMatchPlugin() ) );
addInfoPlugin( InfoPluginPtr( new MusicBrainzPlugin() ) );
addInfoPlugin( InfoPluginPtr( new ChartsPlugin() ) );
addInfoPlugin( InfoPluginPtr( new RoviPlugin() ) );
addInfoPlugin( InfoPluginPtr( new SpotifyPlugin() ) );
addInfoPlugin( InfoPluginPtr( new hypemPlugin() ) );
#endif
#ifdef Q_WS_MAC
addInfoPlugin( new AdiumPlugin() );
addInfoPlugin( InfoPluginPtr( new AdiumPlugin() ) );
#endif
#ifndef ENABLE_HEADLESS
#ifdef Q_WS_X11
addInfoPlugin( new FdoNotifyPlugin() );
addInfoPlugin( new MprisPlugin() );
addInfoPlugin( InfoPluginPtr( new FdoNotifyPlugin() ) );
addInfoPlugin( InfoPluginPtr( new MprisPlugin() ) );
#endif
#endif
}
void
InfoSystemWorker::addInfoPlugin( InfoPlugin* plugin )
InfoSystemWorker::addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin )
{
InfoPluginPtr weakptr( plugin );
m_plugins.append( weakptr );
registerInfoTypes( weakptr, weakptr.data()->supportedGetTypes(), weakptr.data()->supportedPushTypes() );
tDebug() << Q_FUNC_INFO << plugin;
foreach ( InfoPluginPtr ptr, m_plugins )
{
if ( ptr == plugin )
{
tDebug() << Q_FUNC_INFO << "This plugin is already added to the infosystem.";
return;
}
}
if ( plugin.isNull() )
{
tDebug() << Q_FUNC_INFO << "passed-in plugin is null";
return;
}
m_plugins.append( plugin );
registerInfoTypes( plugin, plugin.data()->supportedGetTypes(), plugin.data()->supportedPushTypes() );
connect(
plugin,
plugin.data(),
SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
this,
SLOT( infoSlot( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
@ -119,14 +134,14 @@ InfoSystemWorker::addInfoPlugin( InfoPlugin* plugin )
);
connect(
plugin,
plugin.data(),
SIGNAL( getCachedInfo( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ),
m_cache,
SLOT( getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ),
Qt::QueuedConnection
);
connect(
plugin,
plugin.data(),
SIGNAL( updateCache( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ),
m_cache,
SLOT( updateCacheSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ),
@ -135,6 +150,32 @@ InfoSystemWorker::addInfoPlugin( InfoPlugin* plugin )
}
void
InfoSystemWorker::removeInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin )
{
tDebug() << Q_FUNC_INFO << plugin;
if ( plugin.isNull() )
{
tDebug() << Q_FUNC_INFO << "passed-in plugin is null";
return;
}
foreach ( InfoPluginPtr ptr, m_plugins )
{
if ( ptr == plugin )
break;
tDebug() << Q_FUNC_INFO << "This plugin does not exist in the infosystem.";
return;
}
m_plugins.removeOne( plugin );
deregisterInfoTypes( plugin, plugin.data()->supportedGetTypes(), plugin.data()->supportedPushTypes() );
delete plugin.data();
}
void
InfoSystemWorker::registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType >& getTypes, const QSet< InfoType >& pushTypes )
{
@ -145,6 +186,16 @@ InfoSystemWorker::registerInfoTypes( const InfoPluginPtr &plugin, const QSet< In
}
void
InfoSystemWorker::deregisterInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType >& getTypes, const QSet< InfoType >& pushTypes )
{
Q_FOREACH( InfoType type, getTypes )
m_infoGetMap[type].removeOne( plugin );
Q_FOREACH( InfoType type, pushTypes )
m_infoPushMap[type].removeOne( plugin );
}
QList< InfoPluginPtr >
InfoSystemWorker::determineOrderedMatches( const InfoType type ) const
{
@ -234,6 +285,8 @@ InfoSystemWorker::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData )
}
}
tDebug() << Q_FUNC_INFO << "number of matching plugins: " << m_infoPushMap[ pushData.type ].size();
Q_FOREACH( InfoPluginPtr ptr, m_infoPushMap[ pushData.type ] )
{
if( ptr )

View File

@ -49,8 +49,6 @@ public:
InfoSystemWorker();
~InfoSystemWorker();
void registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType > &getTypes, const QSet< InfoType > &pushTypes );
signals:
void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
void finished( QString target );
@ -64,7 +62,8 @@ public slots:
void infoSlot( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
void addInfoPlugin( Tomahawk::InfoSystem::InfoPlugin* plugin );
void addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin );
void removeInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin );
void getShortUrl( Tomahawk::InfoSystem::InfoPushData data );
void shortLinkReady( QUrl longUrl, QUrl shortUrl, QVariant callbackObj );
@ -73,6 +72,8 @@ private slots:
void checkTimeoutsTimerFired();
private:
void registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType > &getTypes, const QSet< InfoType > &pushTypes );
void deregisterInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType > &getTypes, const QSet< InfoType > &pushTypes );
void checkFinished( const Tomahawk::InfoSystem::InfoRequestData &target );
QList< InfoPluginPtr > determineOrderedMatches( const InfoType type ) const;

View File

@ -588,9 +588,9 @@ Query::setLoved( bool loved )
trackInfo["album"] = album();
Tomahawk::InfoSystem::InfoPushData pushData ( id(),
Tomahawk::InfoSystem::InfoLove,
( loved ? Tomahawk::InfoSystem::InfoLove : Tomahawk::InfoSystem::InfoUnLove ),
QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( trackInfo ),
Tomahawk::InfoSystem::PushNoFlag );
Tomahawk::InfoSystem::PushShortUrlFlag );
Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( pushData );

View File

@ -174,7 +174,6 @@ MusicScanner::scan()
SLOT( commitBatch( QVariantList, QVariantList ) ), Qt::DirectConnection );
m_dirListerThreadController = new QThread( this );
m_dirListerThreadController->setPriority( QThread::IdlePriority );
m_dirLister = QWeakPointer< DirLister >( new DirLister( m_dirs ) );
m_dirLister.data()->moveToThread( m_dirListerThreadController );
@ -186,7 +185,7 @@ MusicScanner::scan()
connect( m_dirLister.data(), SIGNAL( finished() ),
SLOT( listerFinished() ), Qt::QueuedConnection );
m_dirListerThreadController->start();
m_dirListerThreadController->start( QThread::IdlePriority );
QMetaObject::invokeMethod( m_dirLister.data(), "go" );
}

View File

@ -196,7 +196,6 @@ ScanManager::runDirScan()
{
m_scanTimer->stop();
m_musicScannerThreadController = new QThread( this );
m_musicScannerThreadController->setPriority( QThread::IdlePriority );
m_scanner = QWeakPointer< MusicScanner >( new MusicScanner( paths ) );
m_scanner.data()->moveToThread( m_musicScannerThreadController );
connect( m_scanner.data(), SIGNAL( finished() ), SLOT( scannerFinished() ) );

View File

@ -35,6 +35,8 @@
#include "collection.h"
#include "infosystem/infosystem.h"
#include "accounts/AccountManager.h"
#include "accounts/spotify/SpotifyAccount.h"
#include "accounts/lastfm/LastFmAccount.h"
#include "database/database.h"
#include "database/databasecollection.h"
#include "database/databasecommand_collectionstats.h"
@ -60,8 +62,6 @@
#include "utils/jspfloader.h"
#include "utils/logger.h"
#include "utils/tomahawkutilsgui.h"
#include "accounts/lastfm/LastFmAccount.h"
#include "accounts/spotify/SpotifyAccount.h"
#include "config.h"
@ -227,16 +227,8 @@ TomahawkApp::init()
tDebug() << "Init AccountManager.";
m_accountManager = QWeakPointer< Tomahawk::Accounts::AccountManager >( new Tomahawk::Accounts::AccountManager( this ) );
Tomahawk::Accounts::LastFmAccountFactory* lastfmFactory = new Tomahawk::Accounts::LastFmAccountFactory();
m_accountManager.data()->addAccountFactory( lastfmFactory );
Tomahawk::Accounts::SpotifyAccountFactory* spotifyFactory = new Tomahawk::Accounts::SpotifyAccountFactory;
m_accountManager.data()->addAccountFactory( spotifyFactory );
m_accountManager.data()->registerAccountFactoryForFilesystem( spotifyFactory );
Tomahawk::Accounts::AccountManager::instance()->loadFromConfig();
connect( m_accountManager.data(), SIGNAL( ready() ), SLOT( accountManagerReady() ) );
Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() );
#ifndef ENABLE_HEADLESS
EchonestGenerator::setupCatalogs();
@ -447,6 +439,7 @@ TomahawkApp::registerMetaTypes()
qRegisterMetaType< Tomahawk::InfoSystem::InfoRequestData >( "Tomahawk::InfoSystem::InfoRequestData" );
qRegisterMetaType< Tomahawk::InfoSystem::InfoPushData >( "Tomahawk::InfoSystem::InfoPushData" );
qRegisterMetaType< Tomahawk::InfoSystem::InfoSystemCache* >( "Tomahawk::InfoSystem::InfoSystemCache*" );
qRegisterMetaType< Tomahawk::InfoSystem::InfoPluginPtr >( "Tomahawk::InfoSystem::InfoPluginPtr" );
qRegisterMetaType< Tomahawk::InfoSystem::InfoPlugin* >( "Tomahawk::InfoSystem::InfoPlugin*" );
qRegisterMetaType< QList< Tomahawk::InfoSystem::InfoStringHash > >("QList< Tomahawk::InfoSystem::InfoStringHash > ");
@ -598,6 +591,20 @@ TomahawkApp::spotifyApiCheckFinished()
}
void
TomahawkApp::accountManagerReady()
{
Tomahawk::Accounts::LastFmAccountFactory* lastfmFactory = new Tomahawk::Accounts::LastFmAccountFactory();
m_accountManager.data()->addAccountFactory( lastfmFactory );
Tomahawk::Accounts::SpotifyAccountFactory* spotifyFactory = new Tomahawk::Accounts::SpotifyAccountFactory;
m_accountManager.data()->addAccountFactory( spotifyFactory );
m_accountManager.data()->registerAccountFactoryForFilesystem( spotifyFactory );
Tomahawk::Accounts::AccountManager::instance()->loadFromConfig();
}
void
TomahawkApp::activate()
{

View File

@ -111,6 +111,7 @@ private slots:
void initHTTP();
void spotifyApiCheckFinished();
void accountManagerReady();
private:
void registerMetaTypes();