From 4c5855c95d5bfe6bef998a13bc15bf369e11eaa8 Mon Sep 17 00:00:00 2001
From: Jeff Mitchell <tomahawk@jefferai.org>
Date: Mon, 9 Apr 2012 20:46:12 -0400
Subject: [PATCH] Add twitter info plugin. Still needs some work -- when it
 should delete itself (for instance if you disable the plugin and re-enable)
 it doesn't seem to, leading to twitter complaining about duplicate statuses.
 Also get a message about creating children for parent in different thread.

Along the way, made lastfm plugin correctly switch to the right thread
for the info plugin, fix a couple bugs (such as loving and unloving
using the same type), and so on.

Need to fix up xmpp info plugin to use the correct thread as well.

Also, right now tweets indiscriminately when you love, should turn that
off before merging to master.
---
 src/accounts/lastfm/LastFmAccount.cpp         |  17 +-
 src/accounts/lastfm/lastfmplugin.cpp          |  40 +++--
 src/accounts/lastfm/lastfmplugin.h            |   2 +-
 src/accounts/twitter/CMakeLists.txt           |   2 +
 src/accounts/twitter/twitteraccount.cpp       |  30 +++-
 src/accounts/twitter/twitteraccount.h         |   6 +-
 src/accounts/twitter/twitterinfoplugin.cpp    | 161 ++++++++++++++++++
 src/accounts/twitter/twitterinfoplugin.h      |  79 +++++++++
 src/accounts/xmpp/XmppInfoPlugin.cpp          |   1 +
 src/accounts/xmpp/XmppInfoPlugin.h            |   1 +
 src/accounts/xmpp/sip/xmppsip.cpp             |   1 -
 src/accounts/xmpp/xmppaccount.h               |   2 +-
 src/libtomahawk/accounts/Account.cpp          |   2 +
 src/libtomahawk/accounts/AccountManager.cpp   |   3 -
 src/libtomahawk/globalactionmanager.cpp       |   5 +-
 src/libtomahawk/infosystem/infosystem.cpp     |  19 ++-
 src/libtomahawk/infosystem/infosystem.h       |   2 +
 .../infosystem/infosystemcache.cpp            |   8 +-
 .../infosystem/infosystemworker.cpp           |   1 +
 src/libtomahawk/query.cpp                     |   4 +-
 20 files changed, 347 insertions(+), 39 deletions(-)
 create mode 100644 src/accounts/twitter/twitterinfoplugin.cpp
 create mode 100644 src/accounts/twitter/twitterinfoplugin.h

diff --git a/src/accounts/lastfm/LastFmAccount.cpp b/src/accounts/lastfm/LastFmAccount.cpp
index 7e4ac519f..f302fb8bf 100644
--- a/src/accounts/lastfm/LastFmAccount.cpp
+++ b/src/accounts/lastfm/LastFmAccount.cpp
@@ -70,12 +70,20 @@ LastFmAccount::LastFmAccount( const QString& accountId )
     {
         hookupResolver();
     }
+
+
+    if ( infoPlugin() && Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() )
+    {
+        infoPlugin()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
+        Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() );
+    }
 }
 
 
 LastFmAccount::~LastFmAccount()
 {
-    delete m_infoPlugin.data();
+    if ( m_infoPlugin )
+        m_infoPlugin.data()->deleteLater();
     delete m_resolver.data();
 }
 
@@ -158,7 +166,9 @@ LastFmAccount::icon() const
 InfoPlugin*
 LastFmAccount::infoPlugin()
 {
-    return m_infoPlugin.data();
+    if ( m_infoPlugin )
+        return m_infoPlugin.data();
+    return 0;
 }
 
 bool
@@ -178,7 +188,8 @@ LastFmAccount::saveConfig()
         setScrobble( m_configWidget.data()->scrobble() );
     }
 
-    m_infoPlugin.data()->settingsChanged();
+    if ( m_infoPlugin )
+        QTimer::singleShot( 0, m_infoPlugin.data(), SLOT( settingsChanged() ) );
 }
 
 
diff --git a/src/accounts/lastfm/lastfmplugin.cpp b/src/accounts/lastfm/lastfmplugin.cpp
index 7e824fa53..cbec482ca 100644
--- a/src/accounts/lastfm/lastfmplugin.cpp
+++ b/src/accounts/lastfm/lastfmplugin.cpp
@@ -53,10 +53,10 @@ LastFmPlugin::LastFmPlugin( LastFmAccount* account )
 
     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 +707,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 +743,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 +756,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 +781,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 +800,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" );
     }
diff --git a/src/accounts/lastfm/lastfmplugin.h b/src/accounts/lastfm/lastfmplugin.h
index 76d490a37..c071ed7d5 100644
--- a/src/accounts/lastfm/lastfmplugin.h
+++ b/src/accounts/lastfm/lastfmplugin.h
@@ -79,7 +79,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;
diff --git a/src/accounts/twitter/CMakeLists.txt b/src/accounts/twitter/CMakeLists.txt
index 253bd4e03..62bcf83b6 100644
--- a/src/accounts/twitter/CMakeLists.txt
+++ b/src/accounts/twitter/CMakeLists.txt
@@ -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
diff --git a/src/accounts/twitter/twitteraccount.cpp b/src/accounts/twitter/twitteraccount.cpp
index 92eaab161..ced1cccfc 100644
--- a/src/accounts/twitter/twitteraccount.cpp
+++ b/src/accounts/twitter/twitteraccount.cpp
@@ -21,6 +21,7 @@
 #include "twitteraccount.h"
 #include "twitterconfigwidget.h"
 #include "accounts/twitter/tomahawkoauthtwitter.h"
+#include "libtomahawk/infosystem/infosystem.h"
 
 #include "sip/SipPlugin.h"
 
@@ -99,6 +100,19 @@ TwitterAccount::sipPlugin()
 }
 
 
+Tomahawk::InfoSystem::InfoPlugin*
+TwitterAccount::infoPlugin()
+{
+    if ( m_twitterInfoPlugin.isNull() )
+    {
+        m_twitterInfoPlugin = QWeakPointer< Tomahawk::InfoSystem::TwitterInfoPlugin >( new Tomahawk::InfoSystem::TwitterInfoPlugin( this ) );
+
+        return m_twitterInfoPlugin.data();
+    }
+    return m_twitterInfoPlugin.data();
+}
+
+
 void
 TwitterAccount::authenticate()
 {
@@ -122,10 +136,14 @@ TwitterAccount::authenticate()
 void
 TwitterAccount::deauthenticate()
 {
-    if ( sipPlugin() )
+    if ( m_twitterSipPlugin )
         sipPlugin()->disconnectPlugin();
 
+    if ( m_twitterInfoPlugin )
+        m_twitterInfoPlugin.data()->deleteLater();
+
     m_isAuthenticated = false;
+    
     emit nowDeauthenticated();
 }
 
@@ -139,7 +157,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() )
@@ -170,10 +188,18 @@ TwitterAccount::connectAuthVerifyReply( const QTweetUser &user )
 
         sipPlugin()->connectPlugin();
 
+        if ( infoPlugin() && Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() )
+        {
+            infoPlugin()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() );
+            Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() );
+        }
+
         m_isAuthenticated = true;
         emit nowAuthenticated( m_twitterAuth, user );
     }
 }
+
+
 QPixmap
 TwitterAccount::icon() const {
     return QPixmap( ":/twitter-icon.png" );
diff --git a/src/accounts/twitter/twitteraccount.h b/src/accounts/twitter/twitteraccount.h
index 16dcf6242..fb8a08981 100644
--- a/src/accounts/twitter/twitteraccount.h
+++ b/src/accounts/twitter/twitteraccount.h
@@ -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::InfoPlugin* infoPlugin();
     SipPlugin* sipPlugin();
 
     QWidget* configurationWidget() { return m_configWidget.data(); }
@@ -92,6 +93,7 @@ private:
     QWeakPointer< TomahawkOAuthTwitter > m_twitterAuth;
     QWeakPointer< TwitterConfigWidget > m_configWidget;
     QWeakPointer< TwitterSipPlugin > m_twitterSipPlugin;
+    QWeakPointer< Tomahawk::InfoSystem::TwitterInfoPlugin > m_twitterInfoPlugin;
 
     // for settings access
     friend class TwitterConfigWidget;
diff --git a/src/accounts/twitter/twitterinfoplugin.cpp b/src/accounts/twitter/twitterinfoplugin.cpp
new file mode 100644
index 000000000..ba12660e5
--- /dev/null
+++ b/src/accounts/twitter/twitterinfoplugin.cpp
@@ -0,0 +1,161 @@
+/* === 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;
+
+    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()
+{
+}
+
+
+bool
+TwitterInfoPlugin::refreshTwitterAuth()
+{
+    tDebug() << Q_FUNC_INFO << " begin";
+    if( !m_twitterAuth.isNull() )
+        delete m_twitterAuth.data();
+
+    Q_ASSERT( TomahawkUtils::nam() != 0 );
+    tDebug() << Q_FUNC_INFO << " with nam " << TomahawkUtils::nam();
+    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";
+        deleteLater();
+        return;
+    }
+    else
+    {
+        tDebug() << "TwitterInfoPlugin successfully authenticated to Twitter";
+        return;
+    }
+}
+
+
+void
+TwitterInfoPlugin::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData )
+{
+    tDebug() << Q_FUNC_INFO;
+    if ( !isValid() )
+    {
+        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%3 and loving it! %4" )
+                        .arg( info[ "title" ] )
+                        .arg( info[ "artist" ] )
+                        .arg( info[ "album" ].isEmpty() ? QString() : QString( " %1" ).arg( tr( "on \"%1\"" ).arg( info[ "album" ] ) ) )
+                        .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 &) ) );
+    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();
+}
+
+}
+
+}
\ No newline at end of file
diff --git a/src/accounts/twitter/twitterinfoplugin.h b/src/accounts/twitter/twitterinfoplugin.h
new file mode 100644
index 000000000..e3da557a0
--- /dev/null
+++ b/src/accounts/twitter/twitterinfoplugin.h
@@ -0,0 +1,79 @@
+/* === 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 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;
diff --git a/src/accounts/xmpp/XmppInfoPlugin.cpp b/src/accounts/xmpp/XmppInfoPlugin.cpp
index 728ba4cf5..e13dd64eb 100644
--- a/src/accounts/xmpp/XmppInfoPlugin.cpp
+++ b/src/accounts/xmpp/XmppInfoPlugin.cpp
@@ -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
diff --git a/src/accounts/xmpp/XmppInfoPlugin.h b/src/accounts/xmpp/XmppInfoPlugin.h
index 223167524..fbe050966 100644
--- a/src/accounts/xmpp/XmppInfoPlugin.h
+++ b/src/accounts/xmpp/XmppInfoPlugin.h
@@ -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
diff --git a/src/accounts/xmpp/sip/xmppsip.cpp b/src/accounts/xmpp/sip/xmppsip.cpp
index 10fb265b4..da1c105bf 100644
--- a/src/accounts/xmpp/sip/xmppsip.cpp
+++ b/src/accounts/xmpp/sip/xmppsip.cpp
@@ -165,7 +165,6 @@ XmppSipPlugin::XmppSipPlugin( Account *account )
 
 XmppSipPlugin::~XmppSipPlugin()
 {
-    delete m_infoPlugin;
     delete m_avatarManager;
     delete m_roster;
 #ifndef ENABLE_HEADLESS
diff --git a/src/accounts/xmpp/xmppaccount.h b/src/accounts/xmpp/xmppaccount.h
index 8529d9e11..bae5fbc12 100644
--- a/src/accounts/xmpp/xmppaccount.h
+++ b/src/accounts/xmpp/xmppaccount.h
@@ -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() );
 };
 
diff --git a/src/libtomahawk/accounts/Account.cpp b/src/libtomahawk/accounts/Account.cpp
index e8a293c2e..b43196f33 100644
--- a/src/libtomahawk/accounts/Account.cpp
+++ b/src/libtomahawk/accounts/Account.cpp
@@ -37,6 +37,8 @@ accountTypeToString( AccountType type )
         case InfoType:
         case StatusPushType:
             return QObject::tr( "Status Updaters" );
+        case NoType:
+            return QString();
     }
 
     return QString();
diff --git a/src/libtomahawk/accounts/AccountManager.cpp b/src/libtomahawk/accounts/AccountManager.cpp
index eb1f7b43d..507e9fe53 100644
--- a/src/libtomahawk/accounts/AccountManager.cpp
+++ b/src/libtomahawk/accounts/AccountManager.cpp
@@ -297,9 +297,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 );
 }
 
diff --git a/src/libtomahawk/globalactionmanager.cpp b/src/libtomahawk/globalactionmanager.cpp
index ffde78745..33a97aacc 100644
--- a/src/libtomahawk/globalactionmanager.cpp
+++ b/src/libtomahawk/globalactionmanager.cpp
@@ -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 ) ) );
@@ -901,7 +902,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
diff --git a/src/libtomahawk/infosystem/infosystem.cpp b/src/libtomahawk/infosystem/infosystem.cpp
index c1f6c020b..5cafa1032 100644
--- a/src/libtomahawk/infosystem/infosystem.cpp
+++ b/src/libtomahawk/infosystem/infosystem.cpp
@@ -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();
@@ -218,10 +218,27 @@ InfoSystem::addInfoPlugin( InfoPlugin* plugin )
         QMetaObject::invokeMethod( this, "addInfoPlugin", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPlugin*, plugin ) );
         return;
     }
+
+    if ( plugin->thread() != m_infoSystemWorkerThreadController->worker()->thread() )
+    {
+        tDebug() << Q_FUNC_INFO << "The object must be moved to the worker thread first, see InfoSystem::workerThread()";
+        return;
+    }
+    
     QMetaObject::invokeMethod( m_infoSystemWorkerThreadController->worker(), "addInfoPlugin", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPlugin*, plugin ) );
 }
 
 
+QWeakPointer< QThread >
+InfoSystem::workerThread() const
+{
+    if ( m_infoSystemWorkerThreadController->isRunning() && m_infoSystemWorkerThreadController->worker() )
+        return QWeakPointer< QThread >( m_infoSystemWorkerThreadController->worker()->thread() );
+
+    return QWeakPointer< QThread >();
+}
+
+
 InfoSystemCacheThread::InfoSystemCacheThread( QObject *parent )
     : QThread( parent )
 {
diff --git a/src/libtomahawk/infosystem/infosystem.h b/src/libtomahawk/infosystem/infosystem.h
index 9f0d8a811..f7bab5884 100644
--- a/src/libtomahawk/infosystem/infosystem.h
+++ b/src/libtomahawk/infosystem/infosystem.h
@@ -282,6 +282,8 @@ 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 );
diff --git a/src/libtomahawk/infosystem/infosystemcache.cpp b/src/libtomahawk/infosystem/infosystemcache.cpp
index ac626ecd0..5a2503046 100644
--- a/src/libtomahawk/infosystem/infosystemcache.cpp
+++ b/src/libtomahawk/infosystem/infosystemcache.cpp
@@ -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 );
diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp
index be7fb4f2b..ce0b35e6a 100644
--- a/src/libtomahawk/infosystem/infosystemworker.cpp
+++ b/src/libtomahawk/infosystem/infosystemworker.cpp
@@ -106,6 +106,7 @@ InfoSystemWorker::init( Tomahawk::InfoSystem::InfoSystemCache* cache )
 void
 InfoSystemWorker::addInfoPlugin( InfoPlugin* plugin )
 {
+    tDebug() << Q_FUNC_INFO << plugin;
     InfoPluginPtr weakptr( plugin );
     m_plugins.append( weakptr );
     registerInfoTypes( weakptr, weakptr.data()->supportedGetTypes(), weakptr.data()->supportedPushTypes() );
diff --git a/src/libtomahawk/query.cpp b/src/libtomahawk/query.cpp
index 0702cad40..2b9cabc86 100644
--- a/src/libtomahawk/query.cpp
+++ b/src/libtomahawk/query.cpp
@@ -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 );