diff --git a/CMakeLists.txt b/CMakeLists.txt
index c63ef54c7..6b91c8371 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,5 @@
 PROJECT( tomahawk )
-CMAKE_MINIMUM_REQUIRED( VERSION 2.8 )
+CMAKE_MINIMUM_REQUIRED( VERSION 2.8.6 )
 SET( CMAKE_MODULE_PATH  "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules" )
 
 IF( ${CMAKE_VERSION} VERSION_GREATER 2.8.3 )
@@ -15,11 +15,13 @@ SET( TOMAHAWK_APPLICATION_NAME  "Tomahawk" )
 SET( TOMAHAWK_DESCRIPTION_SUMMARY "The social media player" )
 
 SET( TOMAHAWK_VERSION_MAJOR 0 )
-SET( TOMAHAWK_VERSION_MINOR 3 )
+SET( TOMAHAWK_VERSION_MINOR 4 )
 SET( TOMAHAWK_VERSION_PATCH 99 )
 
 #SET( TOMAHAWK_VERSION_RC 0 )
 
+# enforce proper symbol exporting on all platforms
+add_definitions( "-fvisibility=hidden" )
 
 # build options
 option(BUILD_GUI "Build Tomahawk with GUI" ON)
@@ -120,19 +122,6 @@ macro_log_feature(LIBJREEN_FOUND "Jreen" "Qt XMPP Library" "https://github.com/e
 macro_optional_find_package(QTweetLib)
 macro_log_feature(QTWEETLIB_FOUND "QTweetLib" "Qt Twitter Library" "https://github.com/minimoog/QTweetLib" FALSE "" "QTweetLib is needed for the Twitter SIP plugin.\n")
 
-
-IF( NOT QuaZip_FOUND )
-    add_subdirectory( ${CMAKE_SOURCE_DIR}/src/libtomahawk/thirdparty/quazip )
-    SET( QuaZip_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/libtomahawk/thirdparty/quazip )
-    SET( QuaZip_LIBRARY quazip )
-    SET( QuaZip_LIBRARIES ${QuaZip_LIBRARY} )
-    SET( QuaZip_FOUND true )
-    macro_log_feature(QuaZip_FOUND "QuaZip" "Provides support for extracting downloaded resolvers automatically. Building internal copy" "http://quazip.sourceforge.net/" FALSE "" "")
-
-    # copy headers to build/quazip so we can use proper includes inside the code
-    FILE( COPY ${CMAKE_SOURCE_DIR}/src/libtomahawk/thirdparty/quazip/quazip/ DESTINATION ${CMAKE_BINARY_DIR}/libtomahawk/thirdparty/quazip )
-ENDIF()
-
 # required
 #While we distribute our own liblastfm2, don't need to look for it
 #macro_optional_find_package(LibLastFm 0.3.3)
diff --git a/ChangeLog b/ChangeLog
index fa612aac7..d7433e89b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,39 @@
+Version 0.4.0:
+  * Added visual notification for database indexing job.
+  * Fixed icons not appearing in resolvers list.
+  * Fixed various UI glitches and stray error messages in stations.
+  * Fixed bug where album page would resolve bottom-to-top.
+  * Fixed bug where Footnotes would not update when changing selected album in Album View.
+  * Fixed dragging albums and artists from charts, album, and artist views.
+  * Fixed bug where filter text would be one step behind filter value.
+  * Fixed bug where resolvers would enable themselves after auto-updating.
+  * Fixed occasional crash when dropping tracks onto New Station item.
+  * Added jump-to-current-track support for search results page. 
+  * Fixed out of sync Show/Hide menu items on OS X when hidden with cmd-h.
+  * Fixed non-resolving tracks when dragging from album view.
+  * Fixed /Volumes directory not showing up on OS X.
+  * Fixed fetching album covers for albums with special characters.
+  * Show errors and continue gracefully when resolved audio is not available.
+  * Fixed various crashes on exit.
+  * Added basic command-line options for playback control.
+  * Bumped up web api timeouts to allow web clients to finish resolving.
+  * Added filename suggestion when exporting a playlist.
+  * Cleaned up highlighting of artist names in album view.
+  * Cleaned up alignment of playlist items.
+  * Fixed potential crash when searching.
+  * Added support for disc number.
+  * Added SoundCloudWall.com charts.
+  * Added ability to "lock on" to a user when listening along, to skip along.
+  * Fixed bug where loved tracks would be refreshed much too often.
+  * Fixed startup crash on OS X.
+  * Fixed some font size issues.
+  * Sped up Tomahawk startup by moving chart loading into a separate thread.
+  * Added support for parsing Grooveshark and Tinysong tracks and playlists.
+  * Reorganized sidebar to follow more logical item groupings.
+  * Added artist and album results to global searches.
+  * Fixed style and contrast issues when using GTK styles.
+  * Fixed paths to artwork when using MPRIS2 interface.
+ 
 Version 0.3.3:
     * Automatically load Super Collection tracks when no official release
       information is available.
diff --git a/README b/README
index 65df9b01f..8ada27b9c 100644
--- a/README
+++ b/README
@@ -39,6 +39,7 @@ Dependencies
  The following dependencies are optional, but recommended:
 
   Attica 0.2.0 - ftp://ftp.kde.org/pub/kde/stable/attica/
+  QuaZip 0.4.3 - http://quazip.sourceforge.net/
   Jreen 1.0.1 - https://github.com/euroelessar/jreen
   QTweetLib 0.3.0 - https://github.com/minimoog/QTweetLib
 
@@ -46,6 +47,5 @@ Dependencies
 
   MiniUPnP 1.6 - http://miniupnp.free.fr/
   liblastfm 0.4.0 - http://github.com/jonocole/liblastfm/
-  QuaZip 0.4.3 - http://quazip.sourceforge.net/
 
 Enjoy!
diff --git a/admin/mac/macdeploy.py b/admin/mac/macdeploy.py
index 2c2e0e221..f5ce32875 100755
--- a/admin/mac/macdeploy.py
+++ b/admin/mac/macdeploy.py
@@ -491,7 +491,7 @@ def FindVLCPlugin(name):
 FixBinary(binary)
 
 for plugin in VLC_PLUGINS:
-  FixVLCPlugin(FindVLCPlugin(plugin), '.')
+  FixVLCPlugin(FindVLCPlugin(plugin), '../Frameworks/vlc/plugins')
 
 for plugin in TOMAHAWK_PLUGINS:
   FixPlugin(plugin, '../MacOS')
diff --git a/data/images/grooveshark.png b/data/images/grooveshark.png
new file mode 100644
index 000000000..d7cd43b33
Binary files /dev/null and b/data/images/grooveshark.png differ
diff --git a/data/images/no-album-art-placeholder.png b/data/images/no-album-art-placeholder.png
index a117306e2..d7f2bb28c 100644
Binary files a/data/images/no-album-art-placeholder.png and b/data/images/no-album-art-placeholder.png differ
diff --git a/data/images/no-album-no-case.png b/data/images/no-album-no-case.png
index 1fbbbb8bf..963cdaa3d 100644
Binary files a/data/images/no-album-no-case.png and b/data/images/no-album-no-case.png differ
diff --git a/data/images/no-artist-image-placeholder.png b/data/images/no-artist-image-placeholder.png
index 4b0eba323..559a858d4 100644
Binary files a/data/images/no-artist-image-placeholder.png and b/data/images/no-artist-image-placeholder.png differ
diff --git a/data/images/process-stop.png b/data/images/process-stop.png
new file mode 100644
index 000000000..e8b995f74
Binary files /dev/null and b/data/images/process-stop.png differ
diff --git a/resources.qrc b/resources.qrc
index 134cccd5d..ad8118fc5 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -133,7 +133,9 @@
         <file>data/images/headphones-bigger.png</file>
         <file>data/images/no-album-no-case.png</file>
         <file>data/images/rdio.png</file>
+        <file>data/images/grooveshark.png</file>
         <file>data/images/lastfm-icon.png</file>
         <file>data/sql/dbmigrate-27_to_28.sql</file>
+        <file>data/images/process-stop.png</file>
     </qresource>
 </RCC>
diff --git a/src/AccountDelegate.cpp b/src/AccountDelegate.cpp
index 4af815b76..278b29497 100644
--- a/src/AccountDelegate.cpp
+++ b/src/AccountDelegate.cpp
@@ -34,10 +34,10 @@
 #define PADDING_BETWEEN_STARS 2
 #define STAR_SIZE 12
 
-#ifdef Q_WS_MAC
-#define TOPLEVEL_ACCOUNT_HEIGHT 72
+#ifdef Q_OS_MAC
+#define ROW_HEIGHT_MULTIPLIER 4.9
 #else
-#define TOPLEVEL_ACCOUNT_HEIGHT 68
+#define ROW_HEIGHT_MULTIPLIER 5.7
 #endif
 
 #define ICONSIZE 40
@@ -52,6 +52,7 @@ using namespace Accounts;
 
 AccountDelegate::AccountDelegate( QObject* parent )
     : QStyledItemDelegate ( parent )
+    , m_accountRowHeight( -1 )
 {
 
     m_defaultCover.load( RESPATH "images/sipplugin-online.png" );
@@ -75,20 +76,31 @@ AccountDelegate::AccountDelegate( QObject* parent )
 
 
 QSize
-AccountDelegate::sizeHint( const QStyleOptionViewItem&, const QModelIndex& index ) const
+AccountDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const
 {
     AccountModel::RowType rowType = static_cast< AccountModel::RowType >( index.data( AccountModel::RowTypeRole ).toInt() );
+    if ( m_accountRowHeight < 0 )
+    {
+        // Haven't calculated normal item height yet, do it once and save it
+        QStyleOptionViewItemV4 opt( option );
+        initStyleOption( &opt, index );
+        m_accountRowHeight = ROW_HEIGHT_MULTIPLIER * opt.fontMetrics.height();
+    }
+
     if ( rowType == AccountModel::TopLevelAccount || rowType == AccountModel::UniqueFactory || rowType == AccountModel::CustomAccount )
-        return QSize( 200, TOPLEVEL_ACCOUNT_HEIGHT );
+    {
+
+        return QSize( 200, m_accountRowHeight );
+    }
     else if ( rowType == AccountModel::TopLevelFactory )
     {
         // Make more space for each account we have to show.
         AccountFactory* fac = qobject_cast< AccountFactory* >( index.data( AccountModel::AccountData ).value< QObject* >() );
         if ( fac->isUnique() )
-            return QSize( 200, TOPLEVEL_ACCOUNT_HEIGHT );
+            return QSize( 200, m_accountRowHeight );
 
         const QList< Account* > accts = index.data( AccountModel::ChildrenOfFactoryRole ).value< QList< Tomahawk::Accounts::Account* > >();
-        const QSize s = QSize( 200, TOPLEVEL_ACCOUNT_HEIGHT + 12 * accts.size()-1 );
+        const QSize s = QSize( 200, m_accountRowHeight + 12 * accts.size()-1 );
 
         if ( s != m_sizeHints[ index ] )
             const_cast< AccountDelegate* >( this )->sizeHintChanged( index ); // FU KTHBBQ
@@ -200,7 +212,6 @@ AccountDelegate::paint ( QPainter* painter, const QStyleOptionViewItem& option,
         {
             painter->save();
             painter->setFont( installFont );
-            int oldRightEdge = rightEdge;
             rightEdge = drawAccountList( painter, opt, accts, rightEdge );
             painter->restore();
 
diff --git a/src/AccountDelegate.h b/src/AccountDelegate.h
index 6787489b4..01c7f6a9c 100644
--- a/src/AccountDelegate.h
+++ b/src/AccountDelegate.h
@@ -64,6 +64,7 @@ private:
     mutable QHash< QPersistentModelIndex, QRect > m_cachedStarRects;
     mutable QHash< QPersistentModelIndex, QRect > m_cachedConfigRects;
     mutable QHash< QPersistentModelIndex, QSize > m_sizeHints;
+    mutable int m_accountRowHeight;
 };
 
 }
diff --git a/src/AccountFactoryWrapper.cpp b/src/AccountFactoryWrapper.cpp
index f5f270b06..862239fa8 100644
--- a/src/AccountFactoryWrapper.cpp
+++ b/src/AccountFactoryWrapper.cpp
@@ -19,7 +19,8 @@
 #include "AccountFactoryWrapper.h"
 
 #include "accounts/Account.h"
-#include <accounts/AccountManager.h>
+#include "accounts/AccountManager.h"
+#include "guihelpers.h"
 #include "AccountFactoryWrapperDelegate.h"
 #include "delegateconfigwrapper.h"
 #include "ui_AccountFactoryWrapper.h"
@@ -29,7 +30,6 @@ AccountFactoryWrapper::AccountFactoryWrapper( AccountFactory* factory, QWidget*
     : QDialog( parent, Qt::Sheet )
     , m_factory( factory )
     , m_ui( new Ui_AccountFactoryWrapper )
-    , m_createAccount( false )
 {
     m_ui->setupUi( this );
 
@@ -92,40 +92,10 @@ AccountFactoryWrapper::load()
 void
 AccountFactoryWrapper::openAccountConfig( Account* account )
 {
-    if( account->configurationWidget() )
-    {
-#ifndef Q_WS_MAC
-        DelegateConfigWrapper dialog( account->configurationWidget(), QString("%1 Configuration" ).arg( account->accountFriendlyName() ), this );
-        QWeakPointer< DelegateConfigWrapper > watcher( &dialog );
-        int ret = dialog.exec();
-        if( !watcher.isNull() && ret == QDialog::Accepted )
-        {
-            // send changed config to resolver
-            account->saveConfig();
-        }
-#else
-        // on osx a sheet needs to be non-modal
-        DelegateConfigWrapper* dialog = new DelegateConfigWrapper( account->configurationWidget(), QString("%1 Configuration" ).arg( account->accountFriendlyName() ), this, Qt::Sheet );
-        dialog->setProperty( "accountplugin", QVariant::fromValue< QObject* >( account ) );
-        connect( dialog, SIGNAL( finished( int ) ), this, SLOT( accountConfigClosed( int ) ) );
-
-        dialog->show();
-#endif
-    }
+    TomahawkUtils::openAccountConfig( account, this, false );
 }
 
 
-void
-AccountFactoryWrapper::accountConfigClosed( int value )
-{
-    if( value == QDialog::Accepted )
-    {
-        DelegateConfigWrapper* dialog = qobject_cast< DelegateConfigWrapper* >( sender() );
-        Account* account = qobject_cast< Account* >( dialog->property( "accountplugin" ).value< QObject* >() );
-        account->saveConfig();
-    }
-}
-
 void
 AccountFactoryWrapper::removeAccount( Tomahawk::Accounts::Account* acct )
 {
@@ -139,9 +109,7 @@ AccountFactoryWrapper::buttonClicked( QAbstractButton* button )
 {
     if ( button == m_addButton )
     {
-        m_createAccount = true;
-        emit createAccount( m_factory );
-        return;
+        TomahawkUtils::createAccountFromFactory( m_factory, this );
     }
     else
         reject();
diff --git a/src/AccountFactoryWrapper.h b/src/AccountFactoryWrapper.h
index 02c8ec335..e69cf7166 100644
--- a/src/AccountFactoryWrapper.h
+++ b/src/AccountFactoryWrapper.h
@@ -42,14 +42,8 @@ public:
 
     explicit AccountFactoryWrapper( Tomahawk::Accounts::AccountFactory* factory, QWidget* parent = 0 );
 
-    bool doCreateAccount() const { return m_createAccount; }
-
-signals:
-    void createAccount( Tomahawk::Accounts::AccountFactory* factory );
-
 public slots:
     void openAccountConfig( Tomahawk::Accounts::Account* );
-    void accountConfigClosed( int value );
     void removeAccount( Tomahawk::Accounts::Account* );
 
 private slots:
@@ -60,7 +54,6 @@ private:
     Tomahawk::Accounts::AccountFactory* m_factory;
     Ui_AccountFactoryWrapper* m_ui;
     QPushButton* m_addButton;
-    bool m_createAccount;
 };
 
 #endif // ACCOUNTFACTORYWRAPPER_H
diff --git a/src/CMakeLists.osx.cmake b/src/CMakeLists.osx.cmake
index 9239931eb..f7a502f47 100644
--- a/src/CMakeLists.osx.cmake
+++ b/src/CMakeLists.osx.cmake
@@ -58,4 +58,7 @@ if (APPLE)
   FILE(COPY ${CMAKE_SOURCE_DIR}/admin/mac/sparkle_pub.pem
     DESTINATION "${CMAKE_BINARY_DIR}/tomahawk.app/Contents/Resources")
 
+  FILE(COPY /usr/bin/SetFile DESTINATION "${CMAKE_BINARY_DIR}/tomahawk.app/Contents/MacOS")
+  FILE(COPY /usr/bin/GetFileInfo DESTINATION "${CMAKE_BINARY_DIR}/tomahawk.app/Contents/MacOS")
+
 endif (APPLE)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ee8ac9b0b..8cc5dd1bf 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -70,63 +70,21 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui}
 
      breakpad/BreakPad.cpp
 
+     utils/guihelpers.cpp
+
      tomahawktrayicon.cpp
      audiocontrols.cpp
      settingsdialog.cpp
      diagnosticsdialog.cpp
      AccountDelegate.cpp
      settingslistdelegate.cpp
+     delegateconfigwrapper.cpp
      tomahawkwindow.cpp
      LoadXSPFDialog.cpp
      AccountFactoryWrapper.cpp
      AccountFactoryWrapperDelegate.cpp
 )
 
-SET( tomahawkHeaders ${tomahawkHeaders}
-     tomahawkapp.h
-
-     web/api_v1.h
-
-     musicscanner.h
-     scanmanager.h
-     ubuntuunityhack.h
-     shortcuthandler.h
-)
-
-IF(LIBLASTFM_FOUND)
-    SET(tomahawkHeaders ${tomahawkHeaders}
-        scrobbler.h
-    )
-ENDIF(LIBLASTFM_FOUND)
-
-SET( tomahawkHeadersGui ${tomahawkHeadersGui}
-     sourcetree/sourcesmodel.h
-     sourcetree/sourcesproxymodel.h
-     sourcetree/sourcetreeview.h
-     sourcetree/sourcedelegate.h
-     sourcetree/animationhelper.h
-     sourcetree/items/sourcetreeitem.h
-     sourcetree/items/sourceitem.h
-     sourcetree/items/playlistitems.h
-     sourcetree/items/categoryitems.h
-     sourcetree/items/genericpageitems.h
-     sourcetree/items/temporarypageitem.h
-     sourcetree/items/groupitem.h
-     sourcetree/items/historyitem.h
-
-     tomahawktrayicon.h
-     audiocontrols.h
-     settingsdialog.h
-     diagnosticsdialog.h
-     AccountDelegate.h
-     settingslistdelegate.h
-     delegateconfigwrapper.h
-     tomahawkwindow.h
-     LoadXSPFDialog.h
-     AccountFactoryWrapper.h
-     AccountFactoryWrapperDelegate.h
-)
-
 SET( tomahawkUI ${tomahawkUI}
      tomahawkwindow.ui
      diagnosticsdialog.ui
@@ -180,17 +138,11 @@ ENDIF( UNIX )
 IF( APPLE )
     INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/thirdparty/SPMediaKeyTap )
 
-    SET( tomahawkHeaders ${tomahawkHeaders} mac/tomahawkapp_mac.h mac/macshortcuthandler.h )
     SET( tomahawkSources ${tomahawkSources} mac/tomahawkapp_mac.mm mac/macshortcuthandler.cpp )
-
-    IF(HAVE_SPARKLE)
-        SET( tomahawkHeaders ${tomahawkHeaders} ${SPARKLE}/Headers )
-    ENDIF(HAVE_SPARKLE)
 ENDIF( APPLE )
 
 IF(GLOOX_FOUND)
   INCLUDE_DIRECTORIES( ${GLOOX_INCLUDE_DIR} )
-  SET( tomahawkHeaders ${tomahawkHeaders} xmppbot/xmppbot.h )
   SET( tomahawkSources ${tomahawkSources} xmppbot/xmppbot.cpp )
 ENDIF(GLOOX_FOUND)
 
@@ -206,17 +158,16 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
 
 include( ${CMAKE_SOURCE_DIR}/lang/translations.cmake )
 
-SET( final_src ${final_src} ${tomahawkMoc} ${tomahawkSources} ${tomahawkHeaders} ${trans_outfile})
+SET( final_src ${final_src} ${tomahawkMoc} ${tomahawkSources} ${trans_outfile})
 
 IF( BUILD_GUI )
-    LIST(APPEND tomahawkHeaders ${tomahawkHeadersGui})
     LIST(APPEND tomahawkSources ${tomahawkSourcesGui})
     qt4_wrap_ui( tomahawkUI_H ${tomahawkUI} )
 ENDIF()
 
 kde4_add_app_icon( tomahawkSources "${CMAKE_SOURCE_DIR}/data/icons/tomahawk-icon-*.png" )
 qt4_add_resources( RC_SRCS "../resources.qrc" )
-qt4_wrap_cpp( tomahawkMoc ${tomahawkHeaders} )
+
 SET( final_src ${final_src} ${tomahawkUI_H} ${tomahawkMoc} ${tomahawkSources} ${RC_SRCS} )
 
 IF( UNIX AND NOT APPLE )
@@ -224,13 +175,14 @@ IF( UNIX AND NOT APPLE )
 ENDIF( UNIX AND NOT APPLE )
 IF( APPLE )
     ADD_EXECUTABLE( tomahawk MACOSX_BUNDLE ${final_src} )
-    SET_TARGET_PROPERTIES(tomahawk PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_BINARY_DIR}/Info.plist"
-)
+    SET_TARGET_PROPERTIES(tomahawk PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_BINARY_DIR}/Info.plist")
 ENDIF( APPLE )
 IF( WIN32 )
     ADD_EXECUTABLE( tomahawk WIN32 ${final_src} )
 ENDIF( WIN32 )
 
+SET_TARGET_PROPERTIES(tomahawk PROPERTIES AUTOMOC TRUE)
+
 MESSAGE( STATUS "OS_SPECIFIC_LINK_LIBRARIES: ${OS_SPECIFIC_LINK_LIBRARIES}" )
 
 SET(LINK_LIBRARIES  "")
diff --git a/src/audiocontrols.cpp b/src/audiocontrols.cpp
index f536be7c6..f6211ca06 100644
--- a/src/audiocontrols.cpp
+++ b/src/audiocontrols.cpp
@@ -100,8 +100,6 @@ AudioControls::AudioControls( QWidget* parent )
     m_sliderTimeLine.setCurveShape( QTimeLine::LinearCurve );
     ui->seekSlider->setTimeLine( &m_sliderTimeLine );
 
-    m_defaultCover = QPixmap( RESPATH "images/no-album-no-case.png" ).scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
-
     connect( &m_phononTickCheckTimer, SIGNAL( timeout() ), SLOT( phononTickCheckTimeout() ) );
     connect( &m_sliderTimeLine,    SIGNAL( frameChanged( int ) ), ui->seekSlider, SLOT( setValue( int ) ) );
 
@@ -264,14 +262,14 @@ AudioControls::onAlbumCoverUpdated()
 void
 AudioControls::setAlbumCover()
 {
-    if ( !m_currentTrack->album()->cover().isNull() )
+    if ( !m_currentTrack->album()->cover( ui->coverImage->size() ).isNull() )
     {
         QPixmap cover;
-        cover.loadFromData( m_currentTrack->album()->cover() );
-        ui->coverImage->setPixmap( cover.scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) );
+        cover = m_currentTrack->album()->cover( ui->coverImage->size() );
+        ui->coverImage->setPixmap( cover );
     }
     else
-        ui->coverImage->setPixmap( m_defaultCover );
+        ui->coverImage->setPixmap( TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::ScaledCover, ui->coverImage->size() ) );
 }
 
 
@@ -279,10 +277,16 @@ void
 AudioControls::onSocialActionsLoaded()
 {
     Query* query = qobject_cast< Query* >( sender() );
-    if ( !query || query != m_currentTrack->toQuery().data() )
+    if ( !query )
         return;
 
-    setSocialActions();
+    query_ptr currentQuery = m_currentTrack->toQuery();
+    if ( query->artist() == currentQuery->artist() &&
+         query->track() == currentQuery->track() &&
+         query->album() == currentQuery->album() )
+    {
+        setSocialActions();
+    }
 }
 
 
diff --git a/src/audiocontrols.h b/src/audiocontrols.h
index 7d7de87c6..651f1dbf8 100644
--- a/src/audiocontrols.h
+++ b/src/audiocontrols.h
@@ -91,8 +91,6 @@ private:
 
     Ui::AudioControls *ui;
 
-    QPixmap m_defaultCover;
-
     Tomahawk::result_ptr m_currentTrack;
     Tomahawk::PlaylistInterface::RepeatMode m_repeatMode;
     bool m_shuffled;
diff --git a/src/delegateconfigwrapper.cpp b/src/delegateconfigwrapper.cpp
new file mode 100644
index 000000000..a7d8cb6a9
--- /dev/null
+++ b/src/delegateconfigwrapper.cpp
@@ -0,0 +1,114 @@
+/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.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 "delegateconfigwrapper.h"
+
+
+DelegateConfigWrapper::DelegateConfigWrapper( QWidget* conf, const QString& title, QWidget* parent, Qt::WindowFlags flags )
+    : QDialog( parent, flags )
+    , m_widget( conf )
+    , m_deleted( false )
+{
+    m_widget->setWindowFlags( Qt::Sheet );
+#ifdef Q_WS_MAC
+    m_widget->setVisible( true );
+#endif
+    setWindowTitle( title );
+    QVBoxLayout* v = new QVBoxLayout( this );
+    v->setContentsMargins( 0, 0, 0, 0 );
+    v->addWidget( m_widget );
+
+    m_buttons = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this );
+    m_okButton = m_buttons->button( QDialogButtonBox::Ok );
+    connect( m_buttons, SIGNAL( clicked( QAbstractButton*)  ), this, SLOT( closed( QAbstractButton* ) ) );
+    connect( this, SIGNAL( rejected() ), this, SLOT( rejected() ) );
+    v->addWidget( m_buttons );
+
+    setLayout( v );
+
+#ifdef Q_WS_MAC
+    setSizeGripEnabled( false );
+    setMinimumSize( sizeHint() );
+    setMaximumSize( sizeHint() ); // to remove the resize grip on osx this is the only way
+
+    if( conf->metaObject()->indexOfSignal( "sizeHintChanged()" ) > -1 )
+        connect( conf, SIGNAL( sizeHintChanged() ), this, SLOT( updateSizeHint() ) );
+#else
+    m_widget->setVisible( true );
+#endif
+
+}
+
+
+void
+DelegateConfigWrapper::setShowDelete( bool del )
+{
+    if ( del )
+        m_deleteButton = m_buttons->addButton( tr( "Delete Account" ), QDialogButtonBox::DestructiveRole );
+}
+
+
+void
+DelegateConfigWrapper::toggleOkButton( bool dataError )
+{
+    // if dataError is True we want to set the button enabled to false
+    m_okButton->setEnabled( !dataError );
+}
+
+
+void
+DelegateConfigWrapper::closed( QAbstractButton* b )
+{
+    // let the config widget live to see another day
+    layout()->removeWidget( m_widget );
+    m_widget->setParent( 0 );
+    m_widget->setVisible( false );
+
+    QDialogButtonBox* buttons = qobject_cast< QDialogButtonBox* >( sender() );
+    if ( buttons->standardButton( b ) == QDialogButtonBox::Ok )
+        done( QDialog::Accepted );
+    else if ( b == m_deleteButton )
+    {
+        m_deleted = true;
+        emit closedWithDelete();
+        reject();
+    }
+    else
+        done( QDialog::Rejected );
+}
+
+
+void
+DelegateConfigWrapper::rejected()
+{
+    layout()->removeWidget( m_widget );
+    m_widget->setParent( 0 );
+    m_widget->setVisible( false );
+}
+
+
+void
+DelegateConfigWrapper::updateSizeHint()
+{
+    hide();
+    setSizeGripEnabled( false );
+    setMinimumSize( sizeHint() );
+    setMaximumSize( sizeHint() );
+
+    show();
+}
+
diff --git a/src/delegateconfigwrapper.h b/src/delegateconfigwrapper.h
index 4a2d4bfd1..d934c481e 100644
--- a/src/delegateconfigwrapper.h
+++ b/src/delegateconfigwrapper.h
@@ -28,91 +28,23 @@ class DelegateConfigWrapper : public QDialog
 {
     Q_OBJECT
 public:
-    DelegateConfigWrapper( QWidget* conf, const QString& title, QWidget* parent, Qt::WindowFlags flags = 0 ) : QDialog( parent, flags ), m_widget( conf ), m_deleted( false )
-    {
-        m_widget->setWindowFlags( Qt::Sheet );
-#ifdef Q_WS_MAC
-        m_widget->setVisible( true );
-#endif
-        setWindowTitle( title );
-        QVBoxLayout* v = new QVBoxLayout( this );
-        v->setContentsMargins( 0, 0, 0, 0 );
-        v->addWidget( m_widget );
-
-        m_buttons = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this );
-        m_okButton = m_buttons->button( QDialogButtonBox::Ok );
-        connect( m_buttons, SIGNAL( clicked( QAbstractButton*)  ), this, SLOT( closed( QAbstractButton* ) ) );
-        connect( this, SIGNAL( rejected() ), this, SLOT( rejected() ) );
-        v->addWidget( m_buttons );
-
-        setLayout( v );
-
-#ifdef Q_WS_MAC
-        setSizeGripEnabled( false );
-        setMinimumSize( sizeHint() );
-        setMaximumSize( sizeHint() ); // to remove the resize grip on osx this is the only way
-
-        if( conf->metaObject()->indexOfSignal( "sizeHintChanged()" ) > -1 )
-            connect( conf, SIGNAL( sizeHintChanged() ), this, SLOT( updateSizeHint() ) );
-#else
-        m_widget->setVisible( true );
-#endif
-
-    }
+    DelegateConfigWrapper( QWidget* conf, const QString& title, QWidget* parent, Qt::WindowFlags flags = 0 );
 
     ~DelegateConfigWrapper() {}
 
-    void setShowDelete( bool del )
-    {
-        if ( del )
-            m_deleteButton = m_buttons->addButton( tr( "Delete Account" ), QDialogButtonBox::DestructiveRole );
-    }
+    void setShowDelete( bool del );
 
     bool deleted() const { return m_deleted; }
 
 public slots:
     void toggleOkButton( bool dataError )
-    {
-        // if dataError is True we want to set the button enabled to false
-        m_okButton->setEnabled( !dataError );
-    }
-    void closed( QAbstractButton* b )
-    {
-        // let the config widget live to see another day
-        layout()->removeWidget( m_widget );
-        m_widget->setParent( 0 );
-        m_widget->setVisible( false );
-
-        QDialogButtonBox* buttons = qobject_cast< QDialogButtonBox* >( sender() );
-        if( buttons->standardButton( b ) == QDialogButtonBox::Ok )
-            done( QDialog::Accepted );
-        else if ( b == m_deleteButton )
-        {
-            m_deleted = true;
-            emit closedWithDelete();
-            reject();
-        }
-        else
-            done( QDialog::Rejected );
-    }
+    ;
+    void closed( QAbstractButton* b );
 
     // we get a rejected() signal emitted if the user presses escape (and no clicked() signal )
-    void rejected()
-    {
-        layout()->removeWidget( m_widget );
-        m_widget->setParent( 0 );
-        m_widget->setVisible( false );
-    }
+    void rejected();
 
-    void updateSizeHint()
-    {
-        hide();
-        setSizeGripEnabled( false );
-        setMinimumSize( sizeHint() );
-        setMaximumSize( sizeHint() );
-
-        show();
-    }
+    void updateSizeHint();
 
 signals:
     void closedWithDelete();
diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp
index bdfbeb770..2ecbc6fe3 100644
--- a/src/libtomahawk/AtticaManager.cpp
+++ b/src/libtomahawk/AtticaManager.cpp
@@ -78,7 +78,10 @@ AtticaManager::loadPixmapsFromCache()
         }
 
         QPixmap* icon = new QPixmap( cacheDir.absoluteFilePath( file ) );
-        m_resolverStates[ info.baseName() ].pixmap = icon;
+        if ( !icon->isNull() )
+        {
+            m_resolverStates[ info.baseName() ].pixmap = icon;
+        }
     }
 }
 
@@ -95,14 +98,21 @@ AtticaManager::savePixmapsToCache()
 
     foreach( const QString& id, m_resolverStates.keys() )
     {
-        if ( !m_resolverStates[ id ].pixmap )
+        if ( !m_resolverStates[ id ].pixmap || !m_resolverStates[ id ].pixmapDirty )
             continue;
 
         const QString filename = cacheDir.absoluteFilePath( QString( "%1.png" ).arg( id ) );
-        if ( !m_resolverStates[ id ].pixmap->save( filename ) )
+        QFile f( filename );
+        if ( !f.open( QIODevice::WriteOnly ) )
         {
             tLog() << "Failed to open cache file for writing:" << filename;
-            continue;
+        }
+        else
+        {
+            if ( !m_resolverStates[ id ].pixmap->save( &f ) )
+            {
+                tLog() << "Failed to save pixmap into opened file for writing:" << filename;
+            }
         }
     }
 }
@@ -315,6 +325,7 @@ AtticaManager::resolverIconFetched()
     QPixmap* icon = new QPixmap;
     icon->loadFromData( data );
     m_resolverStates[ resolverId ].pixmap = icon;
+    m_resolverStates[ resolverId ].pixmapDirty = true;
 }
 
 
@@ -389,7 +400,7 @@ AtticaManager::upgradeResolver( const Content& resolver )
     emit resolverStateChanged( resolver.id() );
 
     uninstallResolver( resolver );
-    installResolver( resolver );
+    installResolver( resolver, false );
 }
 
 
@@ -447,6 +458,7 @@ AtticaManager::payloadFetched()
                 // Do the install / add to tomahawk
                 Tomahawk::Accounts::Account* resolver = Tomahawk::Accounts::ResolverAccountFactory::createFromPath( resolverPath, true );
                 Tomahawk::Accounts::AccountManager::instance()->addAccount( resolver );
+                TomahawkSettings::instance()->addAccount( resolver->accountId() );
             }
 
             m_resolverStates[ resolverId ].state = Installed;
diff --git a/src/libtomahawk/AtticaManager.h b/src/libtomahawk/AtticaManager.h
index 0b6fcfef6..09b441dd5 100644
--- a/src/libtomahawk/AtticaManager.h
+++ b/src/libtomahawk/AtticaManager.h
@@ -26,16 +26,12 @@
 #include <QPixmap>
 
 #include "dllmacro.h"
+#include "accounts/Account.h"
 
 #include <attica/provider.h>
 #include <attica/providermanager.h>
 #include <attica/content.h>
 
-namespace Tomahawk {
-namespace Accounts {
-class Account;
-}
-}
 
 class DLLEXPORT AtticaManager : public QObject
 {
@@ -56,9 +52,12 @@ public:
         ResolverState state;
         QPixmap* pixmap;
 
+        // internal
+        bool pixmapDirty;
+
         Resolver( const QString& v, const QString& path, int userR, ResolverState s )
-            : version( v ), scriptPath( path ), userRating( userR ), state( s ), pixmap( 0 ) {}
-        Resolver() : userRating( -1 ), state( Uninstalled ), pixmap( 0 ) {}
+            : version( v ), scriptPath( path ), userRating( userR ), state( s ), pixmap( 0 ), pixmapDirty( false ) {}
+        Resolver() : userRating( -1 ), state( Uninstalled ), pixmap( 0 ), pixmapDirty( false ) {}
     };
 
     typedef QHash< QString, AtticaManager::Resolver > StateHash;
@@ -137,6 +136,19 @@ private:
     static AtticaManager* s_instance;
 };
 
+class DLLEXPORT CustomAtticaAccount : public Tomahawk::Accounts::Account
+{
+    Q_OBJECT
+public:
+    virtual ~CustomAtticaAccount() {}
+
+    virtual Attica::Content atticaContent() const = 0;
+
+protected:
+    // No, you can't.
+    CustomAtticaAccount( const QString& id ) : Tomahawk::Accounts::Account( id ) {}
+};
+
 Q_DECLARE_METATYPE( Attica::Content );
 
 #endif // ATTICAMANAGER_H
diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt
index 9aa842e64..e0e73ce14 100644
--- a/src/libtomahawk/CMakeLists.txt
+++ b/src/libtomahawk/CMakeLists.txt
@@ -35,9 +35,12 @@ set( libGuiSources
     jobview/JobStatusView.cpp
     jobview/JobStatusModel.cpp
     jobview/JobStatusDelegate.cpp
+    jobview/JobStatusItem.cpp
     jobview/PipelineStatusItem.cpp
     jobview/TransferStatusItem.cpp
     jobview/LatchedStatusItem.cpp
+    jobview/ErrorStatusMessage.cpp
+    jobview/IndexingJobItem.cpp
 
     infobar/infobar.cpp
 
@@ -113,6 +116,7 @@ set( libGuiSources
     utils/proxystyle.cpp
     utils/tomahawkutilsgui.cpp
 
+    widgets/animatedcounterlabel.cpp
     widgets/checkdirtree.cpp
     widgets/querylabel.cpp
     widgets/imagebutton.cpp
@@ -145,137 +149,6 @@ IF(QCA2_FOUND)
     set( libGuiSources ${libGuiSources} utils/groovesharkparser.cpp )
 ENDIF(QCA2_FOUND)
 
-set( libGuiHeaders
-    actioncollection.h
-
-    contextmenu.h
-    dropjob.h
-    viewmanager.h
-    globalactionmanager.h
-    LatchManager.h
-    TomahawkSettingsGui.h
-
-    context/ContextPage.h
-    context/ContextWidget.h
-    context/pages/TopTracksContext.h
-    context/pages/RelatedArtistsContext.h
-    context/pages/WikipediaContext.h
-    context/pages/WebContext.h
-
-    infobar/infobar.h
-
-    playlist/topbar/topbar.h
-    playlist/topbar/clearbutton.h
-    playlist/topbar/searchlineedit.h
-    playlist/topbar/lineedit.h
-    playlist/topbar/lineedit_p.h
-    playlist/topbar/searchbutton.h
-
-    playlist/treemodel.h
-    playlist/treeproxymodel.h
-    playlist/treeproxymodelplaylistinterface.h
-    playlist/treeheader.h
-    playlist/treeitemdelegate.h
-    playlist/collectionproxymodel.h
-    playlist/collectionproxymodelplaylistinterface.h
-    playlist/collectionflatmodel.h
-    playlist/collectionview.h
-    playlist/playlistmodel.h
-    playlist/playlistproxymodel.h
-    playlist/playlistproxymodelplaylistinterface.h
-    playlist/playlistview.h
-    playlist/playlistitemdelegate.h
-    playlist/queueproxymodel.h
-    playlist/queueproxymodelplaylistinterface.h
-    playlist/queueview.h
-    playlist/trackmodel.h
-    playlist/trackmodelitem.h
-    playlist/trackproxymodel.h
-    playlist/trackproxymodelplaylistinterface.h
-    playlist/trackview.h
-    playlist/trackheader.h
-    playlist/treemodelitem.h
-    playlist/albumitem.h
-    playlist/albummodel.h
-    playlist/albumproxymodel.h
-    playlist/albumproxymodelplaylistinterface.h
-    playlist/albumitemdelegate.h
-    playlist/albumview.h
-    playlist/artistview.h
-    playlist/customplaylistview.h
-    playlist/ViewHeader.h
-
-    playlist/dynamic/DynamicPlaylist.h
-    playlist/dynamic/GeneratorInterface.h
-    playlist/dynamic/DynamicView.h
-    playlist/dynamic/DynamicModel.h
-    playlist/dynamic/echonest/EchonestGenerator.h
-    playlist/dynamic/echonest/EchonestControl.h
-    playlist/dynamic/echonest/EchonestSteerer.h
-    playlist/dynamic/widgets/DynamicWidget.h
-    playlist/dynamic/widgets/DynamicControlWrapper.h
-    playlist/dynamic/widgets/DynamicControlList.h
-    playlist/dynamic/widgets/ReadOrWriteWidget.h
-    playlist/dynamic/widgets/MiscControlWidgets.h
-    playlist/dynamic/widgets/CollapsibleControls.h
-    playlist/dynamic/widgets/DynamicSetupWidget.h
-    playlist/dynamic/widgets/LoadingSpinner.h
-
-    ExternalResolverGui.h
-    resolvers/scriptresolver.h
-    resolvers/qtscriptresolver.h
-
-    utils/widgetdragfilter.h
-    utils/xspfgenerator.h
-    utils/jspfloader.h
-    utils/spotifyparser.h
-    utils/m3uloader.h
-    utils/itunesparser.h
-    utils/rdioparser.h
-    utils/shortenedlinkparser.h
-    utils/dropjobnotifier.h
-
-    widgets/checkdirtree.h
-    widgets/querylabel.h
-    widgets/animatedcounterlabel.h
-    widgets/imagebutton.h
-    widgets/animatedsplitter.h
-    widgets/elidedlabel.h
-    widgets/newplaylistwidget.h
-    widgets/searchwidget.h
-    widgets/SeekSlider.h
-    widgets/playlisttypeselectordlg.h
-    widgets/welcomewidget.h
-    widgets/whatshotwidget.h
-    widgets/whatshotwidget_p.h
-    widgets/ChartDataLoader.h
-    widgets/RecentlyPlayedPlaylistsModel.h
-    widgets/RecentPlaylistsModel.h
-    widgets/OverlayButton.h
-    widgets/overlaywidget.h
-    widgets/HeaderLabel.h
-    widgets/HeaderWidget.h
-    widgets/combobox.h
-    widgets/ToggleButton.h
-    widgets/SocialPlaylistWidget.h
-    widgets/infowidgets/sourceinfowidget.h
-    widgets/infowidgets/ArtistInfoWidget.h
-    widgets/infowidgets/ArtistInfoWidget_p.h
-    widgets/infowidgets/AlbumInfoWidget.h
-    widgets/Breadcrumb.h
-    widgets/BreadcrumbButton.h
-
-    jobview/JobStatusView.h
-    jobview/JobStatusModel.h
-    jobview/JobStatusDelegate.h
-    jobview/JobStatusItem.h
-    jobview/PipelineStatusItem.h
-    jobview/TransferStatusItem.h
-    jobview/LatchedStatusItem.h
-
-    thirdparty/Qocoa/qsearchfield.h
-)
-
 IF(QCA2_FOUND)
     set( libGuiHeaders ${libGuiHeaders} utils/groovesharkparser.h )
 ENDIF(QCA2_FOUND)
@@ -291,6 +164,7 @@ set( libSources
     album.cpp
     albumplaylistinterface.cpp
     collection.cpp
+    functimeout.cpp
     playlist.cpp
     playlistplaylistinterface.cpp
     resolver.cpp
@@ -423,156 +297,6 @@ set( libSources
     thirdparty/kdsingleapplicationguard/kdlockedsharedmemorypointer.cpp
 )
 
-set( libHeaders
-    tomahawksettings.h
-    sourcelist.h
-    pipeline.h
-    functimeout.h
-
-    playlistinterface.h
-
-    aclsystem.h
-    collection.h
-    query.h
-    resolver.h
-    ExternalResolver.h
-    result.h
-    source.h
-    sourceplaylistinterface.h
-
-    artist.h
-    artistplaylistinterface.h
-    album.h
-    albumplaylistinterface.h
-    playlist.h
-    playlistplaylistinterface.h
-    viewpage.h
-
-    accounts/Account.h
-    accounts/AccountManager.h
-    accounts/AccountModel.h
-    accounts/AccountModelFilterProxy.h
-    accounts/ResolverAccount.h
-    accounts/LastFmAccount.h
-    accounts/LastFmConfig.h
-    accounts/SpotifyAccount.h
-    EchonestCatalogSynchronizer.h
-
-    sip/SipPlugin.h
-    sip/SipHandler.h
-    sip/sipinfo.h
-
-    audio/audioengine.h
-
-    database/database.h
-    database/fuzzyindex.h
-    database/databaseworker.h
-    database/databaseimpl.h
-    database/databaseresolver.h
-    database/databasecommand.h
-    database/databasecommandloggable.h
-    database/databasecommand_resolve.h
-    database/databasecommand_allartists.h
-    database/databasecommand_allalbums.h
-    database/databasecommand_alltracks.h
-    database/databasecommand_addfiles.h
-    database/databasecommand_deletefiles.h
-    database/databasecommand_dirmtimes.h
-    database/databasecommand_filemtimes.h
-    database/databasecommand_loadfiles.h
-    database/databasecommand_logplayback.h
-    database/databasecommand_addsource.h
-    database/databasecommand_sourceoffline.h
-    database/databasecommand_collectionstats.h
-    database/databasecommand_loadplaylistentries.h
-    database/databasecommand_modifyplaylist.h
-    database/databasecommand_playbackhistory.h
-    database/databasecommand_setplaylistrevision.h
-    database/databasecommand_loadallplaylists.h
-    database/databasecommand_loadallsortedplaylists.h
-    database/databasecommand_loadallsources.h
-    database/databasecommand_createplaylist.h
-    database/databasecommand_deleteplaylist.h
-    database/databasecommand_renameplaylist.h
-    database/databasecommand_loadops.h
-    database/databasecommand_updatesearchindex.h
-    database/databasecollection.h
-    database/localcollection.h
-    database/databasecommand_setdynamicplaylistrevision.h
-    database/databasecommand_createdynamicplaylist.h
-    database/databasecommand_loaddynamicplaylist.h
-    database/databasecommand_loaddynamicplaylistentries.h
-    database/databasecommand_deletedynamicplaylist.h
-    database/databasecommand_loadallautoplaylists.h
-    database/databasecommand_loadallstations.h
-    database/databasecommand_addclientauth.h
-    database/databasecommand_clientauthvalid.h
-    database/databasecommand_socialaction.h
-    database/databasecommand_loadsocialactions.h
-    database/databasecommand_genericselect.h
-    database/databasecommand_setcollectionattributes.h
-    database/databasecommand_collectionattributes.h
-    database/databasecommand_trackattributes.h
-    database/databasecommand_settrackattributes.h
-
-    infosystem/infosystem.h
-    infosystem/infosystem.h
-    infosystem/infosystemworker.h
-    infosystem/infosystemcache.h
-
-    infosystem/infoplugins/generic/echonestplugin.h
-    infosystem/infoplugins/generic/lastfmplugin.h
-    infosystem/infoplugins/generic/chartsplugin.h
-    infosystem/infoplugins/generic/spotifyPlugin.h
-    infosystem/infoplugins/generic/hypemPlugin.h
-    infosystem/infoplugins/generic/musixmatchplugin.h
-    infosystem/infoplugins/generic/musicbrainzPlugin.h
-    infosystem/infoplugins/generic/RoviPlugin.h
-
-    network/bufferiodevice.h
-    network/msgprocessor.h
-    network/remotecollection.h
-    network/streamconnection.h
-    network/dbsyncconnection.h
-    network/servent.h
-    network/connection.h
-    network/controlconnection.h
-    network/portfwdthread.h
-
-    playlist/PlaylistUpdaterInterface.h
-    playlist/dynamic/DynamicPlaylist.h
-    playlist/dynamic/GeneratorInterface.h
-    playlist/dynamic/GeneratorFactory.h
-    playlist/XspfUpdater.h
-    playlist/dynamic/database/DatabaseGenerator.h
-    playlist/dynamic/database/DatabaseControl.h
-    playlist/dynamic/DynamicControl.h
-
-    thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.h
-
-    utils/tomahawkutilsgui.h
-    utils/xspfloader.h
-    utils/qnr_iodevicestream.h
-)
-
-set( libHeaders_NoMOC
-    viewpage.h
-
-    accounts/Account.h
-
-    infosystem/infoplugins/unix/imageconverter.h
-
-    taghandlers/tag.h
-    taghandlers/apetag.h
-    taghandlers/asftag.h
-    taghandlers/id3v1tag.h
-    taghandlers/id3v2tag.h
-    taghandlers/mp4tag.h
-    taghandlers/oggtag.h
-
-    utils/tomahawkutils.h
-)
-
 set( libUI ${libUI}
     widgets/playlisttypeselectordlg.ui
     widgets/newplaylistwidget.ui
@@ -665,9 +389,7 @@ IF( APPLE )
       utils/tomahawkutils_mac.mm
       thirdparty/Qocoa/qsearchfield_mac.mm )
 
-    SET( libHeaders ${libHeaders}
-      infosystem/infoplugins/mac/adium.h
-      infosystem/infoplugins/mac/adiumplugin.h )
+    SET_SOURCE_FILES_PROPERTIES(utils/tomahawkutils_mac.mm PROPERTIES COMPILE_FLAGS "-fvisibility=default")
 
     SET( OS_SPECIFIC_LINK_LIBRARIES
         ${OS_SPECIFIC_LINK_LIBRARIES}
@@ -689,15 +411,14 @@ ENDIF(LIBLASTFM_FOUND)
 
 IF(BUILD_GUI)
     LIST(APPEND libSources ${libGuiSources} )
-    LIST(APPEND libHeaders ${libGuiHeaders} )
 ENDIF()
 
 qt4_wrap_ui( libUI_H ${libUI} )
-qt4_wrap_cpp( libMoc ${libHeaders} )
 
-SET( libSources ${libSources} ${libUI_H} ${libHeaders_NoMOC} )
+SET( libSources ${libSources} ${libUI_H} )
 
-ADD_LIBRARY( tomahawklib SHARED ${libSources} ${libMoc} )
+ADD_LIBRARY( tomahawklib SHARED ${libSources})
+set_target_properties(tomahawklib PROPERTIES AUTOMOC TRUE)
 
 IF(QCA2_FOUND)
     SET(LINK_LIBRARIES ${LINK_LIBRARIES} ${QCA2_LIBRARIES} )
diff --git a/src/libtomahawk/accounts/Account.cpp b/src/libtomahawk/accounts/Account.cpp
index 6fdaf8315..208cc7bac 100644
--- a/src/libtomahawk/accounts/Account.cpp
+++ b/src/libtomahawk/accounts/Account.cpp
@@ -55,6 +55,13 @@ Account::Account( const QString& accountId )
     loadFromConfig( accountId );
 }
 
+
+Account::~Account()
+{
+    sync();
+}
+
+
 QWidget*
 Account::configurationWidget()
 {
@@ -192,7 +199,7 @@ Account::types() const
         types |= SipType;
     if ( m_types.contains( "ResolverType" ) )
         types |= ResolverType;
-    if ( m_types.contains( "StatusPushTypeType" ) )
+    if ( m_types.contains( "StatusPushType" ) )
         types |= StatusPushType;
 
     return types;
diff --git a/src/libtomahawk/accounts/Account.h b/src/libtomahawk/accounts/Account.h
index d02bdceab..73a7dedee 100644
--- a/src/libtomahawk/accounts/Account.h
+++ b/src/libtomahawk/accounts/Account.h
@@ -73,7 +73,7 @@ public:
     enum ConnectionState { Disconnected, Connecting, Connected, Disconnecting };
 
     explicit Account( const QString &accountId );
-    virtual ~Account() {}
+    virtual ~Account();
 
     QString accountServiceName() const { QMutexLocker locker( &m_mutex ); return m_accountServiceName; } // e.g. "Twitter", "Last.fm"
     QString accountFriendlyName() const { QMutexLocker locker( &m_mutex ); return m_accountFriendlyName; } // e.g. screen name on the service, JID, etc.
diff --git a/src/libtomahawk/accounts/AccountManager.cpp b/src/libtomahawk/accounts/AccountManager.cpp
index 6c5715113..ae0b96f99 100644
--- a/src/libtomahawk/accounts/AccountManager.cpp
+++ b/src/libtomahawk/accounts/AccountManager.cpp
@@ -182,7 +182,7 @@ AccountManager::loadPluginFactory( const QString& path )
 void
 AccountManager::enableAccount( Account* account )
 {
-    if ( account->isAuthenticated() )
+    if ( account->enabled() )
         return;
 
     account->authenticate();
@@ -197,7 +197,7 @@ AccountManager::enableAccount( Account* account )
 void
 AccountManager::disableAccount( Account* account )
 {
-    if ( !account->isAuthenticated() )
+    if ( !account->enabled() )
         return;
 
     account->deauthenticate();
@@ -215,8 +215,6 @@ AccountManager::connectAll()
     {
         acc->authenticate();
         m_enabledAccounts << acc;
-//         if ( acc->types() & Accounts::SipType && acc->sipPlugin() )
-//             acc->sipPlugin()->connectPlugin();
 
     }
     m_connected = true;
@@ -409,4 +407,4 @@ AccountManager::onStateChanged( Account::ConnectionState state )
 
 };
 
-};
\ No newline at end of file
+};
diff --git a/src/libtomahawk/accounts/AccountModel.cpp b/src/libtomahawk/accounts/AccountModel.cpp
index 17c1a14b6..9c8b78f80 100644
--- a/src/libtomahawk/accounts/AccountModel.cpp
+++ b/src/libtomahawk/accounts/AccountModel.cpp
@@ -29,6 +29,7 @@
 using namespace Tomahawk;
 using namespace Accounts;
 
+#define ACCOUNTMODEL_DEBUG 0
 AccountModel::AccountModel( QObject* parent )
     : QAbstractListModel( parent )
 {
@@ -52,6 +53,11 @@ AccountModel::loadData()
     // Add all factories
     QList< AccountFactory* > factories = AccountManager::instance()->factories();
     QList< Account* > allAccounts = AccountManager::instance()->accounts();
+#if ACCOUNTMODEL_DEBUG
+    qDebug() << "All accounts:";
+    foreach ( Account* acct, allAccounts )
+        qDebug() << acct->accountFriendlyName() << "\t" << acct->accountId();
+#endif
     foreach ( AccountFactory* fac, factories )
     {
         if ( !fac->allowUserCreation() )
@@ -76,16 +82,28 @@ AccountModel::loadData()
         if ( AtticaManager::instance()->hasCustomAccountForAttica( content.id() ) )
         {
             Account* acct = AtticaManager::instance()->customAccountForAttica( content.id() );
-            m_accounts << new AccountModelNode( acct );
-            allAccounts.removeAll( acct );
+            Q_ASSERT( acct );
+            if ( acct )
+            {
+                m_accounts << new AccountModelNode( acct );
+                const int removed = allAccounts.removeAll( acct );
+#if ACCOUNTMODEL_DEBUG
+                qDebug() << "Removed custom account from misc accounts list, found:" << removed;
+                qDebug() << "All accounts after remove:";
+                foreach ( Account* acct, allAccounts )
+                    qDebug() << acct->accountFriendlyName() << "\t" << acct->accountId();    // All other accounts we haven't dealt with yet
+#endif
+            }
         } else
         {
             m_accounts << new AccountModelNode( content );
 
             foreach ( Account* acct, AccountManager::instance()->accounts( Accounts::ResolverType ) )
             {
+//                 qDebug() << "Found ResolverAccount" << acct->accountFriendlyName();
                 if ( AtticaResolverAccount* resolver = qobject_cast< AtticaResolverAccount* >( acct ) )
                 {
+//                     qDebug() << "Which is an attica resolver with id:" << resolver->atticaId();
                     if ( resolver->atticaId() == content.id() )
                     {
                         allAccounts.removeAll( acct );
@@ -95,9 +113,14 @@ AccountModel::loadData()
         }
     }
 
-    // All other accounts we haven't dealt with yet
+#if ACCOUNTMODEL_DEBUG
+    qDebug() << "All accounts left:";
+    foreach ( Account* acct, allAccounts )
+        qDebug() << acct->accountFriendlyName() << "\t" << acct->accountId();    // All other accounts we haven't dealt with yet
+#endif
    foreach ( Account* acct, allAccounts )
    {
+       qDebug() << "Resolver is left over:" << acct->accountFriendlyName();
        Q_ASSERT( !qobject_cast< AtticaResolverAccount* >( acct ) ); // This should be caught above in the attica list
 
        if ( qobject_cast< ResolverAccount* >( acct ) && !qobject_cast< AtticaResolverAccount* >( acct ) )
@@ -314,6 +337,12 @@ AccountModel::data( const QModelIndex& index, int role ) const
             Q_ASSERT( node->factory );
 
             Account* account = node->customAccount;
+            // This is sort of ugly. CustomAccounts are pure Account*, but we know that
+            // some might also be linked to attica resolvers (not always). If that is the case
+            // they have a Attica::Content set on the node, so we use that to display some
+            // extra metadata and rating
+            const bool hasAttica = !node->atticaContent.id().isEmpty();
+
             switch ( role )
             {
                 case Qt::DisplayRole:
@@ -324,9 +353,15 @@ AccountModel::data( const QModelIndex& index, int role ) const
                     return ShippedWithTomahawk;
                 case Qt::ToolTipRole:
                 case DescriptionRole:
-                    return node->factory->description();
+                    return hasAttica ? node->atticaContent.description() : node->factory->description();
                 case CanRateRole:
-                    return false;
+                    return hasAttica;
+                case AuthorRole:
+                    return hasAttica ? node->atticaContent.author() : QString();
+                case RatingRole:
+                    return hasAttica ? node->atticaContent.rating() / 20 : 0; // rating is out of 100
+                case DownloadCounterRole:
+                    return hasAttica ? node->atticaContent.downloads() : QVariant();
                 case RowTypeRole:
                     return CustomAccount;
                 case AccountData:
@@ -364,15 +399,24 @@ AccountModel::setData( const QModelIndex& index, const QVariant& value, int role
         switch ( node->type )
         {
             case AccountModelNode::UniqueFactoryType:
+            {
+                const Qt::CheckState state = static_cast< Qt::CheckState >( value.toInt() );
                 if ( node->accounts.isEmpty() )
                 {
+                    Q_ASSERT( state == Qt::Checked ); // How could we have a checked unique factory w/ no account??
                     // No account for this unique factory, create it
                     // Don't add it to node->accounts here, slot attached to accountmanager::accountcreated will do it for us
                     acct = node->factory->createAccount();
                     AccountManager::instance()->addAccount( acct );
                     TomahawkSettings::instance()->addAccount( acct->accountId() );
                 }
+                else
+                {
+                    Q_ASSERT( node->accounts.size() == 1 );
+                    acct = node->accounts.first();
+                }
                 break;
+            }
             case AccountModelNode::AtticaType:
             {
                 // This may or may not be installed. if it's not installed yet, install it, then go ahead and enable it
@@ -397,6 +441,12 @@ AccountModel::setData( const QModelIndex& index, const QVariant& value, int role
                 }
                 else
                 {
+                    if ( m_waitingForAtticaInstall.contains( resolver.id() ) )
+                    {
+                        // in progress, ignore
+                        return true;
+                    }
+
                     qDebug() << "Kicked off fetch+install, now waiting";
                     m_waitingForAtticaInstall.insert( resolver.id() );
 
diff --git a/src/libtomahawk/accounts/AccountModelNode.h b/src/libtomahawk/accounts/AccountModelNode.h
index 795e17b6f..fd6d2c933 100644
--- a/src/libtomahawk/accounts/AccountModelNode.h
+++ b/src/libtomahawk/accounts/AccountModelNode.h
@@ -22,6 +22,7 @@
 #include "Account.h"
 #include "AccountManager.h"
 #include "ResolverAccount.h"
+#include "AtticaManager.h"
 
 #include <attica/content.h>
 
@@ -86,7 +87,7 @@ struct AccountModelNode {
         {
             if ( AccountManager::instance()->factoryForAccount( acct ) == fac )
             {
-                qDebug() << "Found account for factory:" << acct->accountFriendlyName();
+//                 qDebug() << "Found account for factory:" << acct->accountFriendlyName();
                 accounts.append( acct );
             }
         }
@@ -97,7 +98,7 @@ struct AccountModelNode {
         init();
         atticaContent = cnt;
 
-        qDebug() << "Creating attica model node for resolver:" << cnt.id();
+//         qDebug() << "Creating attica model node for resolver:" << cnt.id();
 
         foreach ( Account* acct, AccountManager::instance()->accounts( Accounts::ResolverType ) )
         {
@@ -105,7 +106,7 @@ struct AccountModelNode {
             {
                 if ( resolver->atticaId() == atticaContent.id() )
                 {
-                    qDebug() << "found atticaaccount :" << resolver->accountFriendlyName();
+//                     qDebug() << "found atticaaccount :" << resolver->accountFriendlyName();
                     atticaAccount = resolver;
                     break;
                 }
@@ -124,6 +125,9 @@ struct AccountModelNode {
         init();
         customAccount = account;
         factory = AccountManager::instance()->factoryForAccount( account );
+
+        if ( CustomAtticaAccount* customAtticaAccount = qobject_cast< CustomAtticaAccount* >( account ) )
+            atticaContent = customAtticaAccount->atticaContent();
     }
 
     void init()
diff --git a/src/libtomahawk/accounts/LastFmAccount.cpp b/src/libtomahawk/accounts/LastFmAccount.cpp
index fae2f3132..fb4c97ec4 100644
--- a/src/libtomahawk/accounts/LastFmAccount.cpp
+++ b/src/libtomahawk/accounts/LastFmAccount.cpp
@@ -52,7 +52,7 @@ LastFmAccountFactory::icon() const
 
 
 LastFmAccount::LastFmAccount( const QString& accountId )
-    : Account( accountId )
+    : CustomAtticaAccount( accountId )
 {
     m_infoPlugin = QWeakPointer< LastFmPlugin >( new LastFmPlugin( this ) );
 
@@ -86,13 +86,19 @@ LastFmAccount::authenticate()
     const Attica::Content res = AtticaManager::instance()->resolverForId( "lastfm" );
     const AtticaManager::ResolverState state = AtticaManager::instance()->resolverState( res );
 
-    if ( state == AtticaManager::Installed )
+    qDebug() << "Last.FM account authenticating...";
+    if ( m_resolver.isNull() && state == AtticaManager::Installed )
     {
         hookupResolver();
-    } else
+    }
+    else if ( m_resolver.isNull() )
     {
         AtticaManager::instance()->installResolver( res, false );
     }
+    else
+    {
+        m_resolver.data()->start();
+    }
 
     emit connectionStateChanged( connectionState() );
 }
@@ -101,7 +107,7 @@ LastFmAccount::authenticate()
 void
 LastFmAccount::deauthenticate()
 {
-    if ( m_resolver.data()->running() )
+    if ( !m_resolver.isNull() && m_resolver.data()->running() )
         m_resolver.data()->stop();
 
     emit connectionStateChanged( connectionState() );
@@ -121,7 +127,7 @@ LastFmAccount::configurationWidget()
 Account::ConnectionState
 LastFmAccount::connectionState() const
 {
-    return m_authenticated ? Account::Connected : Account::Disconnected;
+    return (!m_resolver.isNull() && m_resolver.data()->running()) ? Account::Connected : Account::Disconnected;
 }
 
 
@@ -141,7 +147,7 @@ LastFmAccount::infoPlugin()
 bool
 LastFmAccount::isAuthenticated() const
 {
-    return m_authenticated;
+    return !m_resolver.isNull() && m_resolver.data()->running();
 }
 
 
@@ -169,7 +175,7 @@ LastFmAccount::password() const
 void
 LastFmAccount::setPassword( const QString& password )
 {
-    QVariantHash creds;
+    QVariantHash creds = credentials();
     creds[ "password" ] = password;
     setCredentials( creds );
 }
@@ -184,7 +190,7 @@ LastFmAccount::sessionKey() const
 void
 LastFmAccount::setSessionKey( const QString& sessionkey )
 {
-    QVariantHash creds;
+    QVariantHash creds = credentials();
     creds[ "sessionkey" ] = sessionkey;
     setCredentials( creds );
 }
@@ -200,7 +206,7 @@ LastFmAccount::username() const
 void
 LastFmAccount::setUsername( const QString& username )
 {
-    QVariantHash creds;
+    QVariantHash creds = credentials();
     creds[ "username" ] = username;
     setCredentials( creds );
 }
@@ -253,3 +259,10 @@ LastFmAccount::hookupResolver()
     m_resolver = QWeakPointer< ExternalResolverGui >( qobject_cast< ExternalResolverGui* >( Pipeline::instance()->addScriptResolver( data.scriptPath, enabled() ) ) );
     connect( m_resolver.data(), SIGNAL( changed() ), this, SLOT( resolverChanged() ) );
 }
+
+
+Attica::Content
+LastFmAccount::atticaContent() const
+{
+    return AtticaManager::instance()->resolverForId( "lastfm" );
+}
diff --git a/src/libtomahawk/accounts/LastFmAccount.h b/src/libtomahawk/accounts/LastFmAccount.h
index 015afce8e..3e7aaf651 100644
--- a/src/libtomahawk/accounts/LastFmAccount.h
+++ b/src/libtomahawk/accounts/LastFmAccount.h
@@ -20,6 +20,9 @@
 #define LASTFMACCOUNT_H
 
 #include "accounts/Account.h"
+#include "AtticaManager.h"
+
+#include <attica/content.h>
 
 #include <QObject>
 
@@ -58,7 +61,7 @@ private:
  * but the user can install the attica resolver on-demand. So we take care of both there.
  *
  */
-class LastFmAccount : public Account
+class LastFmAccount : public CustomAtticaAccount
 {
     Q_OBJECT
 public:
@@ -88,6 +91,8 @@ public:
     bool scrobble() const;
     void setScrobble( bool scrobble );
 
+    Attica::Content atticaContent() const;
+
 private slots:
     void resolverInstalled( const QString& resolverId );
 
@@ -95,7 +100,6 @@ private slots:
 private:
     void hookupResolver();
 
-    bool m_authenticated;
     QWeakPointer<Tomahawk::ExternalResolverGui> m_resolver;
     QWeakPointer<Tomahawk::InfoSystem::LastFmPlugin> m_infoPlugin;
     QWeakPointer<LastFmConfig> m_configWidget;
diff --git a/src/libtomahawk/accounts/ResolverAccount.cpp b/src/libtomahawk/accounts/ResolverAccount.cpp
index 501add13f..95acbe284 100644
--- a/src/libtomahawk/accounts/ResolverAccount.cpp
+++ b/src/libtomahawk/accounts/ResolverAccount.cpp
@@ -48,6 +48,7 @@ ResolverAccountFactory::createAccount( const QString& accountId )
 Account*
 ResolverAccountFactory::createFromPath( const QString& path, bool isAttica )
 {
+    qDebug() << "Creating ResolverAccount from path:" << path << "is attica" << isAttica;
     if ( isAttica )
     {
         QFileInfo info( path );
@@ -200,6 +201,8 @@ ResolverAccount::resolverChanged()
 AtticaResolverAccount::AtticaResolverAccount( const QString& accountId )
     : ResolverAccount( accountId )
 {
+    TomahawkSettings::instance()->setValue( QString( "accounts/%1/atticaresolver" ).arg( accountId ), true );
+
     m_atticaId = configuration().value( "atticaId" ).toString();
     loadIcon();
 }
@@ -209,9 +212,11 @@ AtticaResolverAccount::AtticaResolverAccount( const QString& accountId, const QS
     , m_atticaId( atticaId )
 {
     QVariantHash conf = configuration();
-    conf[ "atticaid" ] = atticaId;
+    conf[ "atticaId" ] = atticaId;
     setConfiguration( conf );
 
+    TomahawkSettings::instance()->setValue( QString( "accounts/%1/atticaresolver" ).arg( accountId ), true );
+
     loadIcon();
 }
 
diff --git a/src/libtomahawk/accounts/ResolverAccount.h b/src/libtomahawk/accounts/ResolverAccount.h
index 04da6d283..e02a7bfe1 100644
--- a/src/libtomahawk/accounts/ResolverAccount.h
+++ b/src/libtomahawk/accounts/ResolverAccount.h
@@ -35,7 +35,7 @@ public:
     ResolverAccountFactory() {}
     virtual ~ResolverAccountFactory() {}
 
-    virtual Account* createAccount(const QString& accountId = QString());
+    virtual Account* createAccount( const QString& accountId = QString() );
     virtual QString factoryId() const  { return "resolveraccount"; }
     virtual QString description() const { return QString(); }
     virtual QString prettyName() const { return QString(); } // Internal, not displayed
diff --git a/src/libtomahawk/accounts/SpotifyAccount.cpp b/src/libtomahawk/accounts/SpotifyAccount.cpp
index b806a3de5..e9b1dc8f1 100644
--- a/src/libtomahawk/accounts/SpotifyAccount.cpp
+++ b/src/libtomahawk/accounts/SpotifyAccount.cpp
@@ -22,26 +22,54 @@
 #include "PlaylistUpdaterInterface.h"
 #include "sourcelist.h"
 
+#include <QPixmap>
+
 using namespace Tomahawk;
 using namespace Accounts;
 
-SpotifyResolverAccount::SpotifyResolverAccount()
+static QPixmap* s_icon = 0;
+
+Account*
+SpotifyAccountFactory::createAccount( const QString& accountId )
 {
-    qDebug() << Q_FUNC_INFO;
-}
-SpotifyResolverAccount::~SpotifyResolverAccount()
-{
-    qDebug() << Q_FUNC_INFO;
+    return new SpotifyAccount( accountId );
 }
 
+QPixmap
+SpotifyAccountFactory::icon() const
+{
+    if ( !s_icon )
+        s_icon = new QPixmap( RESPATH "images/spotify-logo.png" );
+
+    return *s_icon;
+}
+
+
+SpotifyAccount::SpotifyAccount( const QString& accountId )
+    : ResolverAccount( accountId )
+{
+
+}
+
+
+QPixmap
+SpotifyAccount::icon() const
+{
+    if ( !s_icon )
+        s_icon = new QPixmap( RESPATH "images/spotify-logo.png" );
+
+    return *s_icon;
+}
+
+
 void
-SpotifyResolverAccount::addPlaylist(const QString &qid, const QString& title, QList< Tomahawk::query_ptr > tracks)
+SpotifyAccount::addPlaylist( const QString &qid, const QString& title, QList< Tomahawk::query_ptr > tracks )
 {
     Sync sync;
     sync.id_ = qid;
     int index =  m_syncPlaylists.indexOf( sync );
 
-    if( !m_syncPlaylists.contains( sync ) )
+    if(  !m_syncPlaylists.contains( sync ) )
     {
          qDebug() << Q_FUNC_INFO << "Adding playlist to sync" << qid;
          playlist_ptr pl;
@@ -56,11 +84,12 @@ SpotifyResolverAccount::addPlaylist(const QString &qid, const QString& title, QL
          sync.uuid = pl->guid();
          m_syncPlaylists.append( sync );
     }
-    else{
+    else
+    {
 
         qDebug() << Q_FUNC_INFO << "Found playlist";
 
-        if( index != -1 && !tracks.isEmpty())
+        if ( index != -1 && !tracks.isEmpty())
         {
 
             qDebug() << Q_FUNC_INFO << "Got pl" << m_syncPlaylists[ index ].playlist->guid();
@@ -71,11 +100,14 @@ SpotifyResolverAccount::addPlaylist(const QString &qid, const QString& title, QL
 
             qDebug() << Q_FUNC_INFO << "tracks" << currTracks;
 
-            QList< query_ptr > mergedTracks = TomahawkUtils::mergePlaylistChanges( currTracks, tracks );
-
-            QList<Tomahawk::plentry_ptr> el = m_syncPlaylists[ index ].playlist->entriesFromQueries( mergedTracks, true );
-            m_syncPlaylists[ index ].playlist->createNewRevision( uuid(), m_syncPlaylists[ index ].playlist->currentrevision(), el );
+            bool changed = false;
+            QList< query_ptr > mergedTracks = TomahawkUtils::mergePlaylistChanges( currTracks, tracks, changed );
 
+            if ( changed )
+            {
+                QList<Tomahawk::plentry_ptr> el = m_syncPlaylists[ index ].playlist->entriesFromQueries( mergedTracks, true );
+                m_syncPlaylists[ index ].playlist->createNewRevision( uuid(), m_syncPlaylists[ index ].playlist->currentrevision(), el );
+            }
         }
     }
 
@@ -84,9 +116,9 @@ SpotifyResolverAccount::addPlaylist(const QString &qid, const QString& title, QL
 
 
 
-bool operator==(SpotifyResolverAccount::Sync one, SpotifyResolverAccount::Sync two)
+bool operator==( SpotifyAccount::Sync one, SpotifyAccount::Sync two )
 {
-    if(one.id_ == two.id_)
+    if( one.id_ == two.id_ )
         return true;
     return false;
 }
diff --git a/src/libtomahawk/accounts/SpotifyAccount.h b/src/libtomahawk/accounts/SpotifyAccount.h
index db27dffd8..687315a96 100644
--- a/src/libtomahawk/accounts/SpotifyAccount.h
+++ b/src/libtomahawk/accounts/SpotifyAccount.h
@@ -23,6 +23,7 @@
 #include "utils/tomahawkutils.h"
 #include "sourcelist.h"
 #include "ResolverAccount.h"
+
 class QTimer;
 
 namespace Tomahawk {
@@ -31,24 +32,40 @@ class ExternalResolverGui;
 
 namespace Accounts {
 
-class SpotifyResolverAccount : public QObject // : public AccountFactory
+
+class SpotifyAccountFactory : public AccountFactory
 {
     Q_OBJECT
 public:
-    SpotifyResolverAccount();
-    virtual ~SpotifyResolverAccount();
+    SpotifyAccountFactory() {}
 
-    /*virtual Account* createAccount(const QString& accountId = QString());
-    virtual QString description() const { return tr( "Play and sync your playlists with Spotify" ); }
+    virtual Account* createAccount( const QString& accountId = QString() );
+    virtual QString description() const { return tr( "Play music from and sync your playlists with Spotify" ); }
     virtual QString factoryId() const { return "spotifyaccount"; }
     virtual QString prettyName() const { return "Spotify"; }
 
     virtual AccountTypes types() const { return AccountTypes( ResolverType ); }
     virtual bool allowUserCreation() const { return false; }
-    virtual QPixmap icon() const { return m_icon; }
+    virtual QPixmap icon() const;
     virtual bool isUnique() const { return true; }
-    */
-    void addPlaylist(const QString &qid, const QString& title, QList< Tomahawk::query_ptr > tracks);
+
+};
+
+class SpotifyAccount : public ResolverAccount
+{
+    Q_OBJECT
+public:
+    SpotifyAccount( const QString& accountId );
+    virtual ~SpotifyAccount() {}
+
+    virtual QPixmap icon() const;
+
+    virtual QWidget* aclWidget() { return 0; }
+    virtual InfoSystem::InfoPlugin* infoPlugin() { return 0; }
+    virtual SipPlugin* sipPlugin() { return 0; }
+
+    void addPlaylist( const QString &qid, const QString& title, QList< Tomahawk::query_ptr > tracks );
+
     struct Sync {
          QString id_;
          QString uuid;
diff --git a/src/libtomahawk/album.cpp b/src/libtomahawk/album.cpp
index fd56e8409..fbf362427 100644
--- a/src/libtomahawk/album.cpp
+++ b/src/libtomahawk/album.cpp
@@ -22,7 +22,6 @@
 #include "albumplaylistinterface.h"
 #include "database/database.h"
 #include "database/databaseimpl.h"
-#include "database/databasecommand_alltracks.h"
 #include "query.h"
 
 #include "utils/logger.h"
@@ -32,12 +31,16 @@ using namespace Tomahawk;
 
 Album::~Album()
 {
+    delete m_cover;
 }
 
 
 album_ptr
 Album::get( const Tomahawk::artist_ptr& artist, const QString& name, bool autoCreate )
 {
+    if ( !Database::instance() || !Database::instance()->impl() )
+        return album_ptr();
+
     int albid = Database::instance()->impl()->albumId( artist->id(), name, autoCreate );
     if ( albid < 1 && autoCreate )
         return album_ptr();
@@ -71,6 +74,7 @@ Album::Album( unsigned int id, const QString& name, const Tomahawk::artist_ptr&
     , m_id( id )
     , m_name( name )
     , m_artist( artist )
+    , m_cover( 0 )
     , m_infoLoaded( false )
 {
     connect( Tomahawk::InfoSystem::InfoSystem::instance(),
@@ -97,11 +101,14 @@ Album::artist() const
 }
 
 
-QByteArray
-Album::cover() const
+#ifndef ENABLE_HEADLESS
+QPixmap
+Album::cover( const QSize& size, bool forceLoad ) const
 {
     if ( !m_infoLoaded )
     {
+        if ( !forceLoad )
+            return QPixmap();
         m_uuid = uuid();
 
         Tomahawk::InfoSystem::InfoStringHash trackInfo;
@@ -117,8 +124,31 @@ Album::cover() const
         Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
     }
 
-    return m_cover;
+    if ( !m_cover && !m_coverBuffer.isEmpty() )
+    {
+        m_cover = new QPixmap();
+        m_cover->loadFromData( m_coverBuffer );
+    }
+
+    if ( m_cover && !m_cover->isNull() && !size.isEmpty() )
+    {
+        if ( m_coverCache.contains( size.width() ) )
+        {
+            return m_coverCache.value( size.width() );
+        }
+
+        QPixmap scaledCover;
+        scaledCover = m_cover->scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
+        m_coverCache.insert( size.width(), scaledCover );
+        return scaledCover;
+    }
+
+    if ( m_cover )
+        return *m_cover;
+    else
+        return QPixmap();
 }
+#endif
 
 
 void
@@ -137,7 +167,7 @@ Album::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVaria
         const QByteArray ba = returnedData["imgbytes"].toByteArray();
         if ( ba.length() )
         {
-            m_cover = ba;
+            m_coverBuffer = ba;
         }
     }
 
diff --git a/src/libtomahawk/album.h b/src/libtomahawk/album.h
index b929e9b49..741c7d65a 100644
--- a/src/libtomahawk/album.h
+++ b/src/libtomahawk/album.h
@@ -19,8 +19,13 @@
 #ifndef TOMAHAWKALBUM_H
 #define TOMAHAWKALBUM_H
 
+#include "config.h"
+
 #include <QtCore/QObject>
 #include <QtCore/QSharedPointer>
+#ifndef ENABLE_HEADLESS
+    #include <QtGui/QPixmap>
+#endif
 
 #include "typedefs.h"
 #include "playlistinterface.h"
@@ -44,7 +49,9 @@ public:
     unsigned int id() const { return m_id; }
     QString name() const { return m_name; }
     artist_ptr artist() const;
-    QByteArray cover() const;
+#ifndef ENABLE_HEADLESS
+    QPixmap cover( const QSize& size, bool forceLoad = true ) const;
+#endif
     bool infoLoaded() const { return m_infoLoaded; }
 
     Tomahawk::playlistinterface_ptr playlistInterface();
@@ -64,10 +71,13 @@ private:
     unsigned int m_id;
     QString m_name;
     artist_ptr m_artist;
-    QByteArray m_cover;
+    QByteArray m_coverBuffer;
+    mutable QPixmap* m_cover;
     bool m_infoLoaded;
     mutable QString m_uuid;
 
+    mutable QHash< int, QPixmap > m_coverCache;
+
     Tomahawk::playlistinterface_ptr m_playlistInterface;
 };
 
diff --git a/src/libtomahawk/albumplaylistinterface.h b/src/libtomahawk/albumplaylistinterface.h
index 3cc11a387..880e3c29e 100644
--- a/src/libtomahawk/albumplaylistinterface.h
+++ b/src/libtomahawk/albumplaylistinterface.h
@@ -68,7 +68,6 @@ signals:
     void nextTrackReady();
 
 private:
-    Q_DISABLE_COPY( AlbumPlaylistInterface )
     AlbumPlaylistInterface();
 
     QList< Tomahawk::query_ptr > m_queries;
diff --git a/src/libtomahawk/artist.cpp b/src/libtomahawk/artist.cpp
index 0c0ba332c..cdefc2505 100644
--- a/src/libtomahawk/artist.cpp
+++ b/src/libtomahawk/artist.cpp
@@ -31,12 +31,16 @@ using namespace Tomahawk;
 
 Artist::~Artist()
 {
+    delete m_cover;
 }
 
 
 artist_ptr
 Artist::get( const QString& name, bool autoCreate )
 {
+    if ( !Database::instance() || !Database::instance()->impl() )
+        return artist_ptr();
+
     int artid = Database::instance()->impl()->artistId( name, autoCreate );
     if ( artid < 1 && autoCreate )
         return artist_ptr();
@@ -69,6 +73,7 @@ Artist::Artist( unsigned int id, const QString& name )
     : QObject()
     , m_id( id )
     , m_name( name )
+    , m_cover( 0 )
     , m_infoLoaded( false )
 {
     m_sortname = DatabaseImpl::sortname( name, true );
@@ -89,11 +94,14 @@ Artist::onTracksAdded( const QList<Tomahawk::query_ptr>& tracks )
 }
 
 
-QByteArray
-Artist::cover() const
+#ifndef ENABLE_HEADLESS
+QPixmap
+Artist::cover( const QSize& size, bool forceLoad ) const
 {
     if ( !m_infoLoaded )
     {
+        if ( !forceLoad )
+            return QPixmap();
         m_uuid = uuid();
 
         Tomahawk::InfoSystem::InfoStringHash trackInfo;
@@ -108,8 +116,31 @@ Artist::cover() const
         Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
     }
 
-    return m_cover;
+    if ( !m_cover && !m_coverBuffer.isEmpty() )
+    {
+        m_cover = new QPixmap();
+        m_cover->loadFromData( m_coverBuffer );
+    }
+
+    if ( m_cover && !m_cover->isNull() && !size.isEmpty() )
+    {
+        if ( m_coverCache.contains( size.width() ) )
+        {
+            return m_coverCache.value( size.width() );
+        }
+
+        QPixmap scaledCover;
+        scaledCover = m_cover->scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
+        m_coverCache.insert( size.width(), scaledCover );
+        return scaledCover;
+    }
+
+    if ( m_cover )
+        return *m_cover;
+    else
+        return QPixmap();
 }
+#endif
 
 
 void
@@ -128,7 +159,7 @@ Artist::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVari
         const QByteArray ba = returnedData["imgbytes"].toByteArray();
         if ( ba.length() )
         {
-            m_cover = ba;
+            m_coverBuffer = ba;
         }
     }
 
diff --git a/src/libtomahawk/artist.h b/src/libtomahawk/artist.h
index 3e1442023..05efe48eb 100644
--- a/src/libtomahawk/artist.h
+++ b/src/libtomahawk/artist.h
@@ -19,8 +19,13 @@
 #ifndef TOMAHAWKARTIST_H
 #define TOMAHAWKARTIST_H
 
+#include "config.h"
+
 #include <QtCore/QObject>
 #include <QtCore/QSharedPointer>
+#ifndef ENABLE_HEADLESS
+    #include <QtGui/QPixmap>
+#endif
 
 #include "typedefs.h"
 #include "dllmacro.h"
@@ -43,7 +48,9 @@ public:
     unsigned int id() const { return m_id; }
     QString name() const { return m_name; }
     QString sortname() const { return m_sortname; }
-    QByteArray cover() const;
+#ifndef ENABLE_HEADLESS
+    QPixmap cover( const QSize& size, bool forceLoad = true ) const;
+#endif
     bool infoLoaded() const { return m_infoLoaded; }
 
     Tomahawk::playlistinterface_ptr playlistInterface();
@@ -63,10 +70,13 @@ private:
     unsigned int m_id;
     QString m_name;
     QString m_sortname;
-    QByteArray m_cover;
+    QByteArray m_coverBuffer;
+    mutable QPixmap* m_cover;
     bool m_infoLoaded;
     mutable QString m_uuid;
 
+    mutable QHash< int, QPixmap > m_coverCache;
+
     Tomahawk::playlistinterface_ptr m_playlistInterface;
 };
 
diff --git a/src/libtomahawk/artistplaylistinterface.cpp b/src/libtomahawk/artistplaylistinterface.cpp
index 4ab49bc79..01754c259 100644
--- a/src/libtomahawk/artistplaylistinterface.cpp
+++ b/src/libtomahawk/artistplaylistinterface.cpp
@@ -89,8 +89,8 @@ ArtistPlaylistInterface::tracks()
         cmd->setArtist( m_artist );
         cmd->setSortOrder( DatabaseCommand_AllTracks::Album );
 
-        connect( cmd, SIGNAL( tracks( QList<Tomahawk::query_ptr>, QVariant ) ),
-                        SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
+        connect( cmd,           SIGNAL( tracks( QList<Tomahawk::query_ptr>, QVariant ) ),
+                 m_artist.data(), SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
 
         Database::instance()->enqueue( QSharedPointer< DatabaseCommand >( cmd ) );
     }
diff --git a/src/libtomahawk/audio/audioengine.cpp b/src/libtomahawk/audio/audioengine.cpp
index 226d99a3f..f19f662f0 100644
--- a/src/libtomahawk/audio/audioengine.cpp
+++ b/src/libtomahawk/audio/audioengine.cpp
@@ -18,6 +18,8 @@
 
 #include "audioengine.h"
 
+#include "config.h"
+
 #include <QtCore/QUrl>
 #include <QtNetwork/QNetworkReply>
 
@@ -328,13 +330,15 @@ AudioEngine::sendWaitingNotificationSlot() const
 void
 AudioEngine::sendNowPlayingNotification()
 {
+#ifndef ENABLE_HEADLESS
     if ( m_currentTrack->album().isNull() || m_currentTrack->album()->infoLoaded() )
         onNowPlayingInfoReady();
     else
     {
         connect( m_currentTrack->album().data(), SIGNAL( updated() ), SLOT( onNowPlayingInfoReady() ), Qt::UniqueConnection );
-        m_currentTrack->album()->cover();
+        m_currentTrack->album()->cover( QSize( 0, 0 ) );
     }
+#endif
 }
 
 
@@ -357,9 +361,11 @@ AudioEngine::onNowPlayingInfoReady()
 
     if ( !m_currentTrack->album().isNull() )
     {
+#ifndef ENABLE_HEADLESS
         QImage cover;
-        cover.loadFromData( m_currentTrack->album()->cover() );
+        cover = m_currentTrack->album()->cover( QSize( 0, 0 ) ).toImage();
         playInfo["image"] = QVariant( cover );
+#endif
     }
 
     Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo(
@@ -584,10 +590,10 @@ AudioEngine::onStateChanged( Phonon::State newState, Phonon::State oldState )
 
     if ( newState == Phonon::ErrorState )
     {
+        stop();
+
         tLog() << "Phonon Error:" << m_mediaObject->errorString() << m_mediaObject->errorType();
         emit error( UnknownError );
-
-        stop();
         return;
     }
     if ( newState == Phonon::PlayingState )
diff --git a/src/libtomahawk/contextmenu.cpp b/src/libtomahawk/contextmenu.cpp
index 558b9cb29..de5122a05 100644
--- a/src/libtomahawk/contextmenu.cpp
+++ b/src/libtomahawk/contextmenu.cpp
@@ -88,6 +88,7 @@ ContextMenu::setQueries( const QList<Tomahawk::query_ptr>& queries )
         m_sigmap->setMapping( m_loveAction, ActionLove );
 
         connect( queries.first().data(), SIGNAL( socialActionsLoaded() ), SLOT( onSocialActionsLoaded() ) );
+        m_queries.first()->loadSocialActions();
         onSocialActionsLoaded();
     }
 
diff --git a/src/libtomahawk/database/databasecommand_allalbums.cpp b/src/libtomahawk/database/databasecommand_allalbums.cpp
index f0b731291..5de1f6be0 100644
--- a/src/libtomahawk/database/databasecommand_allalbums.cpp
+++ b/src/libtomahawk/database/databasecommand_allalbums.cpp
@@ -169,7 +169,6 @@ DatabaseCommand_AllAlbums::execForCollection( DatabaseImpl* dbi )
 void
 DatabaseCommand_AllAlbums::exec( DatabaseImpl* dbi )
 {
-    return;
     if ( !m_artist.isNull() )
     {
         execForArtist( dbi );
diff --git a/src/libtomahawk/database/databasecommand_resolve.cpp b/src/libtomahawk/database/databasecommand_resolve.cpp
index 1384f828e..38149e83d 100644
--- a/src/libtomahawk/database/databasecommand_resolve.cpp
+++ b/src/libtomahawk/database/databasecommand_resolve.cpp
@@ -20,6 +20,7 @@
 
 #include "artist.h"
 #include "album.h"
+#include "pipeline.h"
 #include "sourcelist.h"
 #include "utils/logger.h"
 
@@ -30,10 +31,14 @@ DatabaseCommand_Resolve::DatabaseCommand_Resolve( const query_ptr& query )
     : DatabaseCommand()
     , m_query( query )
 {
+    Q_ASSERT( Pipeline::instance()->isRunning() );
 }
 
+
 DatabaseCommand_Resolve::~DatabaseCommand_Resolve()
-{}
+{
+}
+
 
 void
 DatabaseCommand_Resolve::exec( DatabaseImpl* lib )
@@ -76,11 +81,9 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
     typedef QPair<int, float> scorepair_t;
 
     // STEP 1
-    QList< QPair<int, float> > artists = lib->searchTable( "artist", m_query->artist() );
-    QList< QPair<int, float> > tracks = lib->searchTable( "track", m_query->track() );
-    QList< QPair<int, float> > albums = lib->searchTable( "album", m_query->album() );
+    QList< QPair<int, float> > tracks = lib->search( m_query );
 
-    if ( artists.length() == 0 || tracks.length() == 0 )
+    if ( tracks.length() == 0 )
     {
         qDebug() << "No candidates found in first pass, aborting resolve" << m_query->artist() << m_query->track();
         emit results( m_query->id(), res );
@@ -90,13 +93,10 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
     // STEP 2
     TomahawkSqlQuery files_query = lib->newquery();
 
-    QStringList artsl, trksl;
-    for ( int k = 0; k < artists.count(); k++ )
-        artsl.append( QString::number( artists.at( k ).first ) );
+    QStringList trksl;
     for ( int k = 0; k < tracks.count(); k++ )
         trksl.append( QString::number( tracks.at( k ).first ) );
 
-    QString artsToken = QString( "file_join.artist IN (%1)" ).arg( artsl.join( "," ) );
     QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) );
 
     QString sql = QString( "SELECT "
@@ -119,8 +119,7 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
                             "artist.id = file_join.artist AND "
                             "track.id = file_join.track AND "
                             "file.id = file_join.file AND "
-                            "(%1 AND %2)" )
-         .arg( artsToken )
+                            "(%1)" )
          .arg( trksToken );
 
     files_query.prepare( sql );
@@ -196,27 +195,9 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
     typedef QPair<int, float> scorepair_t;
 
     // STEP 1
-    QList< QPair<int, float> > artistPairs = lib->searchTable( "artist", m_query->fullTextQuery(), 20 );
-    QList< QPair<int, float> > trackPairs = lib->searchTable( "track", m_query->fullTextQuery(), 20 );
-    QList< QPair<int, float> > albumPairs = lib->searchTable( "album", m_query->fullTextQuery(), 20 );
+    QList< QPair<int, float> > trackPairs = lib->search( m_query );
+    QList< QPair<int, float> > albumPairs = lib->searchAlbum( m_query, 20 );
 
-    foreach ( const scorepair_t& artistPair, artistPairs )
-    {
-        TomahawkSqlQuery query = lib->newquery();
-
-        QString sql = QString( "SELECT name FROM artist WHERE id = %1" ).arg( artistPair.first );
-        query.prepare( sql );
-        query.exec();
-
-        QList<Tomahawk::artist_ptr> artistList;
-        while ( query.next() )
-        {
-            Tomahawk::artist_ptr artist = Tomahawk::Artist::get( artistPair.first, query.value( 0 ).toString() );
-            artistList << artist;
-        }
-
-        emit artists( m_query->id(), artistList );
-    }
     foreach ( const scorepair_t& albumPair, albumPairs )
     {
         TomahawkSqlQuery query = lib->newquery();
@@ -235,10 +216,10 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
 
         emit albums( m_query->id(), albumList );
     }
-
-    if ( artistPairs.length() == 0 && trackPairs.length() == 0 && albumPairs.length() == 0 )
+    
+    if ( trackPairs.length() == 0 )
     {
-        qDebug() << "No candidates found in first pass, aborting resolve" << m_query->artist() << m_query->track();
+        qDebug() << "No candidates found in first pass, aborting resolve" << m_query->fullTextQuery();
         emit results( m_query->id(), res );
         return;
     }
@@ -246,18 +227,11 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
     // STEP 2
     TomahawkSqlQuery files_query = lib->newquery();
 
-    QStringList artsl, trksl, albsl;
-    for ( int k = 0; k < artistPairs.count(); k++ )
-        artsl.append( QString::number( artistPairs.at( k ).first ) );
+    QStringList trksl;
     for ( int k = 0; k < trackPairs.count(); k++ )
         trksl.append( QString::number( trackPairs.at( k ).first ) );
-    for ( int k = 0; k < albumPairs.count(); k++ )
-        albsl.append( QString::number( albumPairs.at( k ).first ) );
 
-    QString artsToken = QString( "file_join.artist IN (%1)" ).arg( artsl.join( "," ) );
     QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) );
-    QString albsToken = QString( "file_join.album IN (%1)" ).arg( albsl.join( "," ) );
-
     QString sql = QString( "SELECT "
                             "url, mtime, size, md5, mimetype, duration, bitrate, "  //0
                             "file_join.artist, file_join.album, file_join.track, "  //7
@@ -279,7 +253,7 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
                             "track.id = file_join.track AND "
                             "file.id = file_join.file AND "
                             "%1" )
-                        .arg( trackPairs.length() > 0 ? trksToken : QString( "0" ) );
+                        .arg( trksl.length() > 0 ? trksToken : QString( "0" ) );
 
     files_query.prepare( sql );
     files_query.exec();
diff --git a/src/libtomahawk/database/databasecommand_updatesearchindex.cpp b/src/libtomahawk/database/databasecommand_updatesearchindex.cpp
index 6fe5e18a4..5c3899ccd 100644
--- a/src/libtomahawk/database/databasecommand_updatesearchindex.cpp
+++ b/src/libtomahawk/database/databasecommand_updatesearchindex.cpp
@@ -21,32 +21,26 @@
 #include "databaseimpl.h"
 #include "tomahawksqlquery.h"
 #include "utils/logger.h"
+#include "jobview/IndexingJobItem.h"
+#include "jobview/JobStatusView.h"
+#include "jobview/JobStatusModel.h"
+
+#include <QSqlRecord>
 
 
 DatabaseCommand_UpdateSearchIndex::DatabaseCommand_UpdateSearchIndex()
     : DatabaseCommand()
+    , m_statusJob( new IndexingJobItem )
 {
     tLog() << Q_FUNC_INFO << "Updating index.";
+
+    JobStatusView::instance()->model()->addJob( m_statusJob );
 }
 
 
-void
-DatabaseCommand_UpdateSearchIndex::indexTable( DatabaseImpl* db, const QString& table )
+DatabaseCommand_UpdateSearchIndex::~DatabaseCommand_UpdateSearchIndex()
 {
-    qDebug() << Q_FUNC_INFO;
-
-    TomahawkSqlQuery query = db->newquery();
-    qDebug() << "Building index for" << table;
-    query.exec( QString( "SELECT id, name FROM %1" ).arg( table ) );
-
-    QMap< unsigned int, QString > fields;
-    while ( query.next() )
-    {
-        fields.insert( query.value( 0 ).toUInt(), query.value( 1 ).toString() );
-    }
-
-    db->m_fuzzyIndex->appendFields( table, fields );
-    qDebug() << "Building index for" << table << "finished.";
+    m_statusJob->done();
 }
 
 
@@ -55,9 +49,35 @@ DatabaseCommand_UpdateSearchIndex::exec( DatabaseImpl* db )
 {
     db->m_fuzzyIndex->beginIndexing();
 
-    indexTable( db, "artist" );
-    indexTable( db, "album" );
-    indexTable( db, "track" );
+    QMap< unsigned int, QMap< QString, QString > > data;
+    TomahawkSqlQuery q = db->newquery();
+
+    q.exec( "SELECT track.id, track.name, artist.name, artist.id FROM track, artist WHERE artist.id = track.artist" );
+    while ( q.next() )
+    {
+        QMap< QString, QString > track;
+        track.insert( "track", q.value( 1 ).toString() );
+        track.insert( "artist", q.value( 2 ).toString() );
+        track.insert( "artistid", q.value( 3 ).toString() );
+
+        data.insert( q.value( 0 ).toUInt(), track );
+    }
+
+    db->m_fuzzyIndex->appendFields( data );
+    data.clear();
+
+    q.exec( "SELECT album.id, album.name FROM album" );
+    while ( q.next() )
+    {
+        QMap< QString, QString > album;
+        album.insert( "album", q.value( 1 ).toString() );
+
+        data.insert( q.value( 0 ).toUInt(), album );
+    }
+
+    db->m_fuzzyIndex->appendFields( data );
+
+    qDebug() << "Building index finished.";
 
     db->m_fuzzyIndex->endIndexing();
 }
diff --git a/src/libtomahawk/database/databasecommand_updatesearchindex.h b/src/libtomahawk/database/databasecommand_updatesearchindex.h
index 91c2203d1..09aeba89c 100644
--- a/src/libtomahawk/database/databasecommand_updatesearchindex.h
+++ b/src/libtomahawk/database/databasecommand_updatesearchindex.h
@@ -22,23 +22,21 @@
 #include "databasecommand.h"
 #include "dllmacro.h"
 
+class IndexingJobItem;
+
 class DLLEXPORT DatabaseCommand_UpdateSearchIndex : public DatabaseCommand
 {
 Q_OBJECT
 public:
     explicit DatabaseCommand_UpdateSearchIndex();
+    virtual ~DatabaseCommand_UpdateSearchIndex();
 
     virtual QString commandname() const { return "updatesearchindex"; }
     virtual bool doesMutates() const { return true; }
     virtual void exec( DatabaseImpl* db );
 
-signals:
-    void indexUpdated();
-
 private:
-    void indexTable( DatabaseImpl* db, const QString& table );
-
-    QString table;
+    IndexingJobItem* m_statusJob;
 };
 
 #endif // DATABASECOMMAND_UPDATESEARCHINDEX_H
diff --git a/src/libtomahawk/database/databaseimpl.cpp b/src/libtomahawk/database/databaseimpl.cpp
index 847685457..54e3e993f 100644
--- a/src/libtomahawk/database/databaseimpl.cpp
+++ b/src/libtomahawk/database/databaseimpl.cpp
@@ -79,6 +79,9 @@ DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent )
     query.exec( "UPDATE source SET isonline = 'false'" );
 
     m_fuzzyIndex = new FuzzyIndex( *this, schemaUpdated );
+    if ( schemaUpdated )
+        QTimer::singleShot( 0, this, SLOT( updateIndex() ) );
+
     tDebug( LOGVERBOSE ) << "Loaded index:" << t.elapsed();
 
     if ( qApp->arguments().contains( "--dumpdb" ) )
@@ -405,13 +408,36 @@ DatabaseImpl::albumId( int artistid, const QString& name_orig, bool autoCreate )
 
 
 QList< QPair<int, float> >
-DatabaseImpl::searchTable( const QString& table, const QString& name, uint limit )
+DatabaseImpl::search( const Tomahawk::query_ptr& query, uint limit )
 {
     QList< QPair<int, float> > resultslist;
-    if ( table != "artist" && table != "track" && table != "album" )
+
+    QMap< int, float > resultsmap = m_fuzzyIndex->search( query );
+    foreach ( int i, resultsmap.keys() )
+    {
+        resultslist << QPair<int, float>( i, (float)resultsmap.value( i ) );
+    }
+    qSort( resultslist.begin(), resultslist.end(), DatabaseImpl::scorepairSorter );
+
+    if ( !limit )
         return resultslist;
 
-    QMap< int, float > resultsmap = m_fuzzyIndex->search( table, name );
+    QList< QPair<int, float> > resultscapped;
+    for ( int i = 0; i < (int)limit && i < resultsmap.count(); i++ )
+    {
+        resultscapped << resultslist.at( i );
+    }
+
+    return resultscapped;
+}
+
+
+QList< QPair<int, float> >
+DatabaseImpl::searchAlbum( const Tomahawk::query_ptr& query, uint limit )
+{
+    QList< QPair<int, float> > resultslist;
+
+    QMap< int, float > resultsmap = m_fuzzyIndex->searchAlbum( query );
     foreach ( int i, resultsmap.keys() )
     {
         resultslist << QPair<int, float>( i, (float)resultsmap.value( i ) );
@@ -696,3 +722,11 @@ DatabaseImpl::openDatabase( const QString& dbname )
 
     return schemaUpdated;
 }
+
+
+void
+DatabaseImpl::updateIndex()
+{
+    DatabaseCommand* cmd = new DatabaseCommand_UpdateSearchIndex();
+    Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
+}
diff --git a/src/libtomahawk/database/databaseimpl.h b/src/libtomahawk/database/databaseimpl.h
index 805dbab17..052ae68aa 100644
--- a/src/libtomahawk/database/databaseimpl.h
+++ b/src/libtomahawk/database/databaseimpl.h
@@ -56,7 +56,8 @@ public:
     int trackId( int artistid, const QString& name_orig, bool autoCreate );
     int albumId( int artistid, const QString& name_orig, bool autoCreate );
 
-    QList< QPair<int, float> > searchTable( const QString& table, const QString& name, uint limit = 0 );
+    QList< QPair<int, float> > search( const Tomahawk::query_ptr& query, uint limit = 0 );
+    QList< QPair<int, float> > searchAlbum( const Tomahawk::query_ptr& query, uint limit = 0 );
     QList< int > getTrackFids( int tid );
 
     static QString sortname( const QString& str, bool replaceArticle = false );
@@ -79,7 +80,8 @@ public:
 signals:
     void indexReady();
 
-public slots:
+private slots:
+    void updateIndex();
 
 private:
     QString cleanSql( const QString& sql );
diff --git a/src/libtomahawk/database/fuzzyindex.cpp b/src/libtomahawk/database/fuzzyindex.cpp
index b5d4b15a9..f9cf8802c 100644
--- a/src/libtomahawk/database/fuzzyindex.cpp
+++ b/src/libtomahawk/database/fuzzyindex.cpp
@@ -22,12 +22,14 @@
 #include <QTime>
 
 #include <CLucene.h>
+#include <CLucene/queryParser/MultiFieldQueryParser.h>
 
 #include "databaseimpl.h"
 #include "utils/tomahawkutils.h"
 #include "utils/logger.h"
 
 using namespace lucene::analysis;
+using namespace lucene::analysis::standard;
 using namespace lucene::document;
 using namespace lucene::store;
 using namespace lucene::index;
@@ -83,7 +85,7 @@ FuzzyIndex::beginIndexing()
         }
 
         qDebug() << "Creating new index writer.";
-        IndexWriter luceneWriter = IndexWriter( m_luceneDir, m_analyzer, true );
+        IndexWriter luceneWriter( m_luceneDir, m_analyzer, true );
     }
     catch( CLuceneError& error )
     {
@@ -102,38 +104,55 @@ FuzzyIndex::endIndexing()
 
 
 void
-FuzzyIndex::appendFields( const QString& table, const QMap< unsigned int, QString >& fields )
+FuzzyIndex::appendFields( const QMap< unsigned int, QMap< QString, QString > >& trackData )
 {
     try
     {
-        qDebug() << "Appending to index:" << fields.count();
+        tDebug() << "Appending to index:" << trackData.count();
         bool create = !IndexReader::indexExists( TomahawkUtils::appDataDir().absoluteFilePath( "tomahawk.lucene" ).toStdString().c_str() );
-        IndexWriter luceneWriter = IndexWriter( m_luceneDir, m_analyzer, create );
+        IndexWriter luceneWriter( m_luceneDir, m_analyzer, create );
         Document doc;
 
-        QMapIterator< unsigned int, QString > it( fields );
+        QMapIterator< unsigned int, QMap< QString, QString > > it( trackData );
         while ( it.hasNext() )
         {
             it.next();
             unsigned int id = it.key();
-            QString name = it.value();
+            QMap< QString, QString > values = it.value();
 
+            if ( values.contains( "track" ) )
             {
-                Field* field = _CLNEW Field( table.toStdWString().c_str(), DatabaseImpl::sortname( name ).toStdWString().c_str(),
-                                            Field::STORE_YES | Field::INDEX_UNTOKENIZED );
-                doc.add( *field );
-            }
+                doc.add( *( _CLNEW Field( _T( "fulltext" ), DatabaseImpl::sortname( QString( "%1 %2" ).arg( values.value( "artist" ) ).arg( values.value( "track" ) ) ).toStdWString().c_str(),
+                                          Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
 
-            {
-                Field* field = _CLNEW Field( _T( "id" ), QString::number( id ).toStdWString().c_str(),
-                Field::STORE_YES | Field::INDEX_NO );
-                doc.add( *field );
+                doc.add( *( _CLNEW Field( _T( "track" ), DatabaseImpl::sortname( values.value( "track" ) ).toStdWString().c_str(),
+                                          Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
+
+                doc.add( *( _CLNEW Field( _T( "artist" ), DatabaseImpl::sortname( values.value( "artist" ) ).toStdWString().c_str(),
+                                          Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
+
+                doc.add( *( _CLNEW Field( _T( "artistid" ), values.value( "artistid" ).toStdWString().c_str(),
+                                          Field::STORE_YES | Field::INDEX_NO ) ) );
+
+                doc.add( *( _CLNEW Field( _T( "trackid" ), QString::number( id ).toStdWString().c_str(),
+                                          Field::STORE_YES | Field::INDEX_NO ) ) );
             }
+            else if ( values.contains( "album" ) )
+            {
+                doc.add( *( _CLNEW Field( _T( "album" ), DatabaseImpl::sortname( values.value( "album" ) ).toStdWString().c_str(),
+                                          Field::STORE_NO | Field::INDEX_UNTOKENIZED ) ) );
+
+                doc.add( *( _CLNEW Field( _T( "albumid" ), QString::number( id ).toStdWString().c_str(),
+                                          Field::STORE_YES | Field::INDEX_NO ) ) );
+            }
+            else
+                Q_ASSERT( false );
 
             luceneWriter.addDocument( &doc );
             doc.clear();
         }
 
+        luceneWriter.optimize();
         luceneWriter.close();
     }
     catch( CLuceneError& error )
@@ -152,7 +171,7 @@ FuzzyIndex::loadLuceneIndex()
 
 
 QMap< int, float >
-FuzzyIndex::search( const QString& table, const QString& name )
+FuzzyIndex::search( const Tomahawk::query_ptr& query )
 {
     QMutexLocker lock( &m_mutex );
 
@@ -171,33 +190,112 @@ FuzzyIndex::search( const QString& table, const QString& name )
             m_luceneSearcher = _CLNEW IndexSearcher( m_luceneReader );
         }
 
-        if ( name.isEmpty() )
-            return resultsmap;
+        float minScore;
+        const TCHAR** fields = 0;
+        MultiFieldQueryParser parser( fields, m_analyzer );
+        BooleanQuery* qry = _CLNEW BooleanQuery();
 
-        SimpleAnalyzer analyzer;
-        QueryParser parser( table.toStdWString().c_str(), m_analyzer );
-        Hits* hits = 0;
+        if ( query->isFullTextQuery() )
+        {
+            QString escapedQuery = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->fullTextQuery() ).toStdWString().c_str() ) );
+            
+            Term* term = _CLNEW Term( _T( "track" ), escapedQuery.toStdWString().c_str() );
+            Query* fqry = _CLNEW FuzzyQuery( term );
+            qry->add( fqry, true, BooleanClause::SHOULD );
 
-        FuzzyQuery* qry = _CLNEW FuzzyQuery( _CLNEW Term( table.toStdWString().c_str(), DatabaseImpl::sortname( name ).toStdWString().c_str() ) );
-        hits = m_luceneSearcher->search( qry );
+            term = _CLNEW Term( _T( "artist" ), escapedQuery.toStdWString().c_str() );
+            fqry = _CLNEW FuzzyQuery( term );
+            qry->add( fqry, true, BooleanClause::SHOULD );
 
+            term = _CLNEW Term( _T( "fulltext" ), escapedQuery.toStdWString().c_str() );
+            fqry = _CLNEW FuzzyQuery( term );
+            qry->add( fqry, true, BooleanClause::SHOULD );
+
+            minScore = 0.00;
+        }
+        else
+        {
+            QString track = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->track() ).toStdWString().c_str() ) );
+            QString artist = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->artist() ).toStdWString().c_str() ) );
+//            QString album = QString::fromWCharArray( parser.escape( query->album().toStdWString().c_str() ) );
+
+            Term* term = _CLNEW Term( _T( "track" ), track.toStdWString().c_str() );
+            Query* fqry = _CLNEW FuzzyQuery( term );
+            qry->add( fqry, true, BooleanClause::MUST );
+
+            term = _CLNEW Term( _T( "artist" ), artist.toStdWString().c_str() );
+            fqry = _CLNEW FuzzyQuery( term );
+            qry->add( fqry, true, BooleanClause::MUST );
+
+            minScore = 0.05;
+        }
+
+        Hits* hits = m_luceneSearcher->search( qry );
         for ( uint i = 0; i < hits->length(); i++ )
         {
             Document* d = &hits->doc( i );
 
             float score = hits->score( i );
-            int id = QString::fromWCharArray( d->get( _T( "id" ) ) ).toInt();
-            QString result = QString::fromWCharArray( d->get( table.toStdWString().c_str() ) );
+            int id = QString::fromWCharArray( d->get( _T( "trackid" ) ) ).toInt();
 
-            if ( DatabaseImpl::sortname( result ) == DatabaseImpl::sortname( name ) )
-                score = 1.0;
-            else
-                score = qMin( score, (float)0.99 );
-
-            if ( score > 0.05 )
+            if ( score > minScore )
             {
                 resultsmap.insert( id, score );
-//                qDebug() << "Hitres:" << result << id << score << table << name;
+//                tDebug() << "Index hit:" << id << score << QString::fromWCharArray( ((Query*)qry)->toString() );
+            }
+        }
+
+        delete hits;
+        delete qry;
+    }
+    catch( CLuceneError& error )
+    {
+        tDebug() << "Caught CLucene error:" << error.what();
+        Q_ASSERT( false );
+    }
+
+    return resultsmap;
+}
+
+
+QMap< int, float >
+FuzzyIndex::searchAlbum( const Tomahawk::query_ptr& query )
+{
+    Q_ASSERT( query->isFullTextQuery() );
+
+    QMutexLocker lock( &m_mutex );
+
+    QMap< int, float > resultsmap;
+    try
+    {
+        if ( !m_luceneReader )
+        {
+            if ( !IndexReader::indexExists( TomahawkUtils::appDataDir().absoluteFilePath( "tomahawk.lucene" ).toStdString().c_str() ) )
+            {
+                qDebug() << Q_FUNC_INFO << "index didn't exist.";
+                return resultsmap;
+            }
+
+            m_luceneReader = IndexReader::open( m_luceneDir );
+            m_luceneSearcher = _CLNEW IndexSearcher( m_luceneReader );
+        }
+
+        QueryParser parser( _T( "album" ), m_analyzer );
+        QString escapedName = QString::fromWCharArray( parser.escape( DatabaseImpl::sortname( query->fullTextQuery() ).toStdWString().c_str() ) );
+
+        Query* qry = _CLNEW FuzzyQuery( _CLNEW Term( _T( "album" ), escapedName.toStdWString().c_str() ) );
+        Hits* hits = m_luceneSearcher->search( qry );
+        for ( uint i = 0; i < hits->length(); i++ )
+        {
+            Document* d = &hits->doc( i );
+
+            float score = hits->score( i );
+            int id = QString::fromWCharArray( d->get( _T( "albumid" ) ) ).toInt();
+
+            if ( score > 0.30 )
+            {
+                resultsmap.insert( id, score );
+//                tDebug() << "Index hit:" << id << score;
             }
         }
 
diff --git a/src/libtomahawk/database/fuzzyindex.h b/src/libtomahawk/database/fuzzyindex.h
index 3e93fbb23..2938a1f55 100644
--- a/src/libtomahawk/database/fuzzyindex.h
+++ b/src/libtomahawk/database/fuzzyindex.h
@@ -25,6 +25,8 @@
 #include <QString>
 #include <QMutex>
 
+#include "query.h"
+
 namespace lucene
 {
     namespace analysis
@@ -58,7 +60,7 @@ public:
 
     void beginIndexing();
     void endIndexing();
-    void appendFields( const QString& table, const QMap< unsigned int, QString >& fields );
+    void appendFields( const QMap< unsigned int, QMap< QString, QString > >& trackData );
     
 signals:
     void indexReady();
@@ -66,7 +68,8 @@ signals:
 public slots:
     void loadLuceneIndex();
 
-    QMap< int, float > search( const QString& table, const QString& name );
+    QMap< int, float > search( const Tomahawk::query_ptr& query );
+    QMap< int, float > searchAlbum( const Tomahawk::query_ptr& query );
 
 private:
     DatabaseImpl& m_db;
diff --git a/src/libtomahawk/dropjob.cpp b/src/libtomahawk/dropjob.cpp
index 6c0ed2f9f..45c9ece2d 100644
--- a/src/libtomahawk/dropjob.cpp
+++ b/src/libtomahawk/dropjob.cpp
@@ -35,6 +35,9 @@
 #include "utils/xspfloader.h"
 #include "jobview/JobStatusView.h"
 #include "jobview/JobStatusModel.h"
+#include "jobview/ErrorStatusMessage.h"
+#include "pipeline.h"
+
 #ifdef QCA2_FOUND
 #include "utils/groovesharkparser.h"
 #endif //QCA2_FOUND
@@ -43,7 +46,7 @@
 using namespace Tomahawk;
 
 bool DropJob::s_canParseSpotifyPlaylists = false;
-
+static QString s_dropJobInfoId = "dropjob";
 
 DropJob::DropJob( QObject *parent )
     : QObject( parent )
@@ -124,7 +127,7 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
         // Not the most elegant
         if ( url.contains( "spotify" ) && url.contains( "playlist" ) && s_canParseSpotifyPlaylists )
             return true;
-        
+
         if ( url.contains( "grooveshark.com" ) && url.contains( "playlist" ) )
             return true;
     }
@@ -571,7 +574,7 @@ DropJob::handleGroovesharkUrls ( const QString& urlsRaw )
 #ifdef QCA2_FOUND
     QStringList urls = urlsRaw.split( QRegExp( "\\s+" ), QString::SkipEmptyParts );
     tDebug() << "Got Grooveshark urls!" << urls;
-    
+
     if ( dropAction() == Default )
         setDropAction( Create );
 
@@ -691,6 +694,67 @@ DropJob::onTracksAdded( const QList<Tomahawk::query_ptr>& tracksList )
 }
 
 
+void
+DropJob::tracksFromDB( const QList< query_ptr >& tracks )
+{
+    // Tracks that we get from databasecommand_alltracks are resolved only against the database and explicitly marked
+    // as finished. if the source they resolve to is offline they will not resolve against any resolver.
+    // explicitly resolve them if they fall in that case first
+    foreach( const query_ptr& track, tracks )
+    {
+        if ( !track->playable() && !track->solved() && track->results().size() ) // we have offline results
+        {
+            track->setResolveFinished( false );
+            Pipeline::instance()->resolve( track );
+        }
+    }
+
+    album_ptr albumPtr;
+    artist_ptr artistPtr;
+    if ( Tomahawk::Album* album = qobject_cast< Tomahawk::Album* >( sender() ) )
+    {
+        foreach ( const album_ptr& ptr, m_albumsToKeep )
+            if ( ptr.data() == album )
+            {
+                albumPtr = ptr;
+                m_albumsToKeep.remove( ptr );
+            }
+    }
+    else if ( Tomahawk::Artist* artist = qobject_cast< Tomahawk::Artist* >( sender() ) )
+    {
+        foreach ( const artist_ptr& ptr, m_artistsToKeep )
+            if ( ptr.data() == artist )
+            {
+                artistPtr = ptr;
+                m_artistsToKeep.remove( ptr );
+            }
+    }
+
+    // If we have no tracks, this means no sources in our network have the give request (artist or album)
+    // Since we really do want to try to drop them, we ask the infosystem as well.
+    if ( tracks.isEmpty() )
+    {
+        if ( !albumPtr.isNull() && !albumPtr->artist().isNull() )
+        {
+            Q_ASSERT( artistPtr.isNull() );
+            --m_queryCount; // This query is done. New query is infosystem query
+            getAlbumFromInfoystem( albumPtr->artist()->name(), albumPtr->name() );
+        }
+        else if ( !artistPtr.isNull() )
+        {
+            Q_ASSERT( albumPtr.isNull() );
+            --m_queryCount;
+            getTopTen( artistPtr->name() );
+        }
+    }
+    else
+    {
+        onTracksAdded( tracks );
+    }
+
+}
+
+
 void
 DropJob::removeDuplicates()
 {
@@ -699,10 +763,18 @@ DropJob::removeDuplicates()
     {
         bool contains = false;
         foreach( const Tomahawk::query_ptr &tmpItem, list )
+        {
             if ( item->album() == tmpItem->album()
                  && item->artist() == tmpItem->artist()
                  && item->track() == tmpItem->track() )
+            {
+                if ( item->playable() && !tmpItem->playable() )
+                    list.replace( list.indexOf( tmpItem ), item );
+
                 contains = true;
+                break;
+            }
+        }
         if ( !contains )
             list.append( item );
     }
@@ -733,28 +805,31 @@ DropJob::removeRemoteSources()
 void
 DropJob::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
 {
-    if ( requestData.caller == "changeme" )
+    if ( requestData.caller == s_dropJobInfoId )
     {
-        Tomahawk::InfoSystem::InfoStringHash artistInfo;
+        const Tomahawk::InfoSystem::InfoStringHash info = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
 
-        artistInfo = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
+        const QString artist = info["artist"];
+        const QString album  = info["album"];
 
-        QString artist = artistInfo["artist"];
-
-        qDebug() << "Got requestData response for artist" << artist << output;
+        qDebug() << "Got requestData response for artist" << artist << "and album:" << album << output;
 
         QList< query_ptr > results;
 
         int i = 0;
         foreach ( const QVariant& title, output.toMap().value( "tracks" ).toList() )
         {
-            qDebug() << "got title" << title;
             results << Query::get( artist, title.toString(), QString(), uuid() );
 
             if ( ++i == 10 ) // Only getting top ten for now. Would make sense to make it configurable
                 break;
         }
 
+        if ( results.isEmpty() )
+        {
+            const QString which = album.isEmpty() ? "artist" : "album";
+            JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "No tracks found for given %1" ).arg( which ), 5 ) );
+        }
         onTracksAdded( results );
     }
 }
@@ -766,8 +841,10 @@ DropJob::getArtist( const QString &artist )
     artist_ptr artistPtr = Artist::get( artist );
     if ( artistPtr->playlistInterface()->tracks().isEmpty() )
     {
+        m_artistsToKeep.insert( artistPtr );
+
         connect( artistPtr.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
-                                     SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
+                                     SLOT( tracksFromDB( QList<Tomahawk::query_ptr> ) ) );
         m_queryCount++;
         return QList< query_ptr >();
     }
@@ -787,9 +864,14 @@ DropJob::getAlbum(const QString &artist, const QString &album)
 
     if ( albumPtr->playlistInterface()->tracks().isEmpty() )
     {
+        // For albums that don't exist until this moment, we are the main shared pointer holding on.
+        // fetching the tracks is asynchronous, so the resulting signal is queued. when we go out of scope we delete
+        // the artist_ptr which means we never get the signal delivered. so we hold on to the album pointer till we're done
+        m_albumsToKeep.insert( albumPtr );
+
         m_dropJob = new DropJobNotifier( QPixmap( RESPATH "images/album-icon.png" ), Album );
         connect( albumPtr.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
-                                    SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
+                                    SLOT( tracksFromDB( QList<Tomahawk::query_ptr> ) ) );
         JobStatusView::instance()->model()->addJob( m_dropJob );
 
         m_queryCount++;
@@ -811,7 +893,7 @@ DropJob::getTopTen( const QString &artist )
     artistInfo["artist"] = artist;
 
     Tomahawk::InfoSystem::InfoRequestData requestData;
-    requestData.caller = "changeme";
+    requestData.caller = s_dropJobInfoId;
     requestData.customData = QVariantMap();
 
     requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
@@ -821,3 +903,28 @@ DropJob::getTopTen( const QString &artist )
 
     m_queryCount++;
 }
+
+
+void
+DropJob::getAlbumFromInfoystem( const QString& artist, const QString& album )
+{
+    connect( Tomahawk::InfoSystem::InfoSystem::instance(),
+             SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
+             SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
+
+    Tomahawk::InfoSystem::InfoStringHash artistInfo;
+    artistInfo["artist"] = artist;
+    artistInfo["album"] = album;
+
+    Tomahawk::InfoSystem::InfoRequestData requestData;
+    requestData.caller = s_dropJobInfoId;
+    requestData.customData = QVariantMap();
+
+    requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
+
+    requestData.type = Tomahawk::InfoSystem::InfoAlbumSongs;
+    Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
+
+    m_queryCount++;
+}
+
diff --git a/src/libtomahawk/dropjob.h b/src/libtomahawk/dropjob.h
index 71ca32787..02e474a6c 100644
--- a/src/libtomahawk/dropjob.h
+++ b/src/libtomahawk/dropjob.h
@@ -23,6 +23,8 @@
 #include "query.h"
 
 #include "infosystem/infosystem.h"
+#include "utils/xspfloader.h"
+
 #include <QObject>
 #include <QStringList>
 #include <QMimeData>
@@ -120,9 +122,9 @@ private slots:
     void onTracksAdded( const QList<Tomahawk::query_ptr>& );
     void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
 
+    void tracksFromDB( const QList< Tomahawk::query_ptr >& );
 private:
     /// handle parsing mime data
-
     void handleAllUrls( const QString& urls );
     void handleTrackUrls( const QString& urls );
     QList< Tomahawk::query_ptr > tracksFromQueryList( const QMimeData* d );
@@ -135,6 +137,7 @@ private:
     QList< Tomahawk::query_ptr > getAlbum( const QString& artist, const QString& album );
 
     void getTopTen( const QString& artist );
+    void getAlbumFromInfoystem( const QString& artist, const QString& album );
 
     void removeDuplicates();
     void removeRemoteSources();
@@ -151,6 +154,8 @@ private:
     Tomahawk::DropJobNotifier* m_dropJob;
 
     QList< Tomahawk::query_ptr > m_resultList;
+    QSet< Tomahawk::album_ptr > m_albumsToKeep;
+    QSet< Tomahawk::artist_ptr > m_artistsToKeep;
 
     static bool s_canParseSpotifyPlaylists;
 };
diff --git a/src/libtomahawk/functimeout.cpp b/src/libtomahawk/functimeout.cpp
new file mode 100644
index 000000000..9f3d83f6e
--- /dev/null
+++ b/src/libtomahawk/functimeout.cpp
@@ -0,0 +1,47 @@
+/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@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 "functimeout.h"
+
+#include <QTimer>
+
+
+using namespace Tomahawk;
+
+
+FuncTimeout::FuncTimeout( int ms, boost::function< void() > func, QObject* besafe )
+    : m_func( func )
+    , m_watch( QWeakPointer< QObject >( besafe ) )
+{
+    //qDebug() << Q_FUNC_INFO;
+    QTimer::singleShot( ms, this, SLOT( exec() ) );
+}
+
+
+FuncTimeout::~FuncTimeout()
+{
+}
+
+
+void FuncTimeout::exec()
+{
+    if( !m_watch.isNull() )
+        m_func();
+    this->deleteLater();
+}
diff --git a/src/libtomahawk/functimeout.h b/src/libtomahawk/functimeout.h
index 060a2fe9f..e2b74dbfe 100644
--- a/src/libtomahawk/functimeout.h
+++ b/src/libtomahawk/functimeout.h
@@ -20,7 +20,6 @@
 #define FUNCTIMEOUT_H
 
 #include <QObject>
-#include <QTimer>
 #include <QWeakPointer>
 
 #include "boost/function.hpp"
@@ -43,26 +42,12 @@ class DLLEXPORT FuncTimeout : public QObject
 Q_OBJECT
 
 public:
-    FuncTimeout( int ms, boost::function<void()> func, QObject* besafe )
-        : m_func( func )
-        , m_watch( QWeakPointer< QObject >( besafe ) )
-    {
-        //qDebug() << Q_FUNC_INFO;
-        QTimer::singleShot( ms, this, SLOT( exec() ) );
-    };
+    FuncTimeout( int ms, boost::function<void()> func, QObject* besafe );
 
-    ~FuncTimeout()
-    {
-        //qDebug() << Q_FUNC_INFO;
-    };
+    ~FuncTimeout();
 
 public slots:
-    void exec()
-    {
-        if( !m_watch.isNull() )
-            m_func();
-        this->deleteLater();
-    };
+    void exec();
 
 private:
     boost::function<void()> m_func;
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp
index 6b19566d5..ef3ae6691 100644
--- a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp
+++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp
@@ -45,9 +45,9 @@ ChartsPlugin::ChartsPlugin()
     , m_chartsFetchJobs( 0 )
 {
     /// Add resources here
-    m_chartResources << "billboard" << "itunes" << "rdio" << "wearehunted" << "ex.fm" << "soundcloudwall.com";
+    m_chartResources << "billboard" << "itunes" << "rdio" << "wearehunted" << "ex.fm" << "soundcloudwall";
     /// If you add resource, update version aswell
-    m_chartVersion = "1.0";
+    m_chartVersion = "2.1";
     m_supportedGetTypes <<  InfoChart << InfoChartCapabilities;
 
 }
@@ -332,7 +332,9 @@ ChartsPlugin::chartTypes()
                 if( source == "itunes" ){
                     chartName = "iTunes";
                 }
-
+                if( source == "soundcloudwall" ){
+                    chartName = "SoundCloudWall";
+                }
                 if( source == "wearehunted" ){
                     chartName = "WeAreHunted";
                 }
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp
index b2d5b7a32..5a9ede354 100644
--- a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp
+++ b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp
@@ -451,8 +451,15 @@ LastFmPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSy
             QString artistName = criteria["artist"];
             QString albumName = criteria["album"];
 
-            QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=album.imageredirect&artist=%1&album=%2&autocorrect=1&size=large&api_key=7a90f6672a04b809ee309af169f34b8b";
-            QNetworkRequest req( imgurl.arg( artistName ).arg( albumName ) );
+            QUrl imgurl( "http://ws.audioscrobbler.com/2.0/" );
+            imgurl.addQueryItem( "method", "album.imageredirect" );
+            imgurl.addEncodedQueryItem( "artist", QUrl::toPercentEncoding( artistName, "", "+" ) );
+            imgurl.addEncodedQueryItem( "album", QUrl::toPercentEncoding( albumName, "", "+" ) );
+            imgurl.addQueryItem( "autocorrect", QString::number( 1 ) );
+            imgurl.addQueryItem( "size", "large" );
+            imgurl.addQueryItem( "api_key", "7a90f6672a04b809ee309af169f34b8b" );
+
+            QNetworkRequest req( imgurl );
             QNetworkReply* reply = TomahawkUtils::nam()->get( req );
             reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
 
@@ -464,8 +471,14 @@ LastFmPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSy
         {
             QString artistName = criteria["artist"];
 
-            QString imgurl = "http://ws.audioscrobbler.com/2.0/?method=artist.imageredirect&artist=%1&autocorrect=1&size=large&api_key=7a90f6672a04b809ee309af169f34b8b";
-            QNetworkRequest req( imgurl.arg( artistName ) );
+            QUrl imgurl( "http://ws.audioscrobbler.com/2.0/" );
+            imgurl.addQueryItem( "method", "artist.imageredirect" );
+            imgurl.addEncodedQueryItem( "artist", QUrl::toPercentEncoding( artistName, "", "+" ) );
+            imgurl.addQueryItem( "autocorrect", QString::number( 1 ) );
+            imgurl.addQueryItem( "size", "large" );
+            imgurl.addQueryItem( "api_key", "7a90f6672a04b809ee309af169f34b8b" );
+
+            QNetworkRequest req( imgurl );
             QNetworkReply* reply = TomahawkUtils::nam()->get( req );
             reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
 
@@ -693,14 +706,14 @@ LastFmPlugin::artistImagesReturned()
 void
 LastFmPlugin::settingsChanged()
 {
-    if ( !m_scrobbler && m_account->enabled() )
+    if ( !m_scrobbler && m_account->scrobble() )
     { // can simply create the scrobbler
-    lastfm::ws::Username = m_account->username();
-    m_pw = m_account->password();
+        lastfm::ws::Username = m_account->username();
+        m_pw = m_account->password();
 
         createScrobbler();
     }
-    else if ( m_scrobbler && !m_account->enabled() )
+    else if ( m_scrobbler && !m_account->scrobble() )
     {
         delete m_scrobbler;
         m_scrobbler = 0;
@@ -748,7 +761,7 @@ LastFmPlugin::onAuthenticated()
             m_account->setSessionKey( lastfm::ws::SessionKey.toLatin1() );
 
 //            qDebug() << "Got session key from last.fm";
-            if ( m_account->enabled() )
+            if ( m_account->scrobble() )
                 m_scrobbler = new lastfm::Audioscrobbler( "thk" );
         }
     }
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp
index f91476752..bcff0d24b 100644
--- a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp
+++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp
@@ -43,7 +43,6 @@ SpotifyPlugin::SpotifyPlugin()
     : InfoPlugin()
     , m_chartsFetchJobs( 0 )
 {
-
     m_supportedGetTypes << InfoChart << InfoChartCapabilities;
 
 }
@@ -71,7 +70,6 @@ SpotifyPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
 
     InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
 
-
     switch ( requestData.type )
     {
         case InfoChart:
@@ -87,6 +85,7 @@ SpotifyPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
         case InfoChartCapabilities:
             fetchChartCapabilities( requestData );
             break;
+
         default:
             dataError( requestData );
     }
@@ -110,7 +109,6 @@ SpotifyPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
         tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Hash did not contain required params!";
         dataError( requestData );
         return;
-
     }
     /// Set the criterias for current chart
     criteria["chart_id"] = hash["chart_id"];
@@ -118,6 +116,8 @@ SpotifyPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
 
     emit getCachedInfo( criteria, 86400000 /* Expire chart cache in 1 day */, requestData );
 }
+
+
 void
 SpotifyPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
@@ -132,12 +132,12 @@ SpotifyPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData req
     emit getCachedInfo( criteria, 604800000, requestData );
 }
 
+
 void
 SpotifyPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     switch ( requestData.type )
     {
-
         case InfoChart:
         {
             /// Fetch the chart, we need source and id
@@ -149,8 +149,6 @@ SpotifyPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, To
             reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
             connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) );
             return;
-
-
         }
         case InfoChartCapabilities:
         {
@@ -212,9 +210,8 @@ SpotifyPlugin::chartTypes()
         }
 
         QVariantMap charts;
-        foreach(QVariant geos, chartObj.value("Charts").toList().takeLast().toMap().value("geo").toList() )
+        foreach( QVariant geos, chartObj.value( "Charts" ).toList().takeLast().toMap().value( "geo" ).toList() )
         {
-
            const QString geo = geos.toMap().value( "name" ).toString();
            const QString geoId = geos.toMap().value( "id" ).toString();
            QString country;
@@ -225,7 +222,6 @@ SpotifyPlugin::chartTypes()
                country = geo;
            else
            {
-
                QLocale l( QString( "en_%1" ).arg( geo ) );
                country = Tomahawk::CountryUtils::fullCountryFromCode( geo );
 
@@ -240,7 +236,7 @@ SpotifyPlugin::chartTypes()
            }
 
            QList< InfoStringHash > chart_types;
-           foreach(QVariant types, chartObj.value("Charts").toList().takeFirst().toMap().value("types").toList() )
+           foreach( QVariant types, chartObj.value( "Charts" ).toList().takeFirst().toMap().value( "types" ).toList() )
            {
                QString type = types.toMap().value( "id" ).toString();
                QString label = types.toMap().value( "name" ).toString();
@@ -251,18 +247,15 @@ SpotifyPlugin::chartTypes()
                c[ "type" ] = type;
 
                chart_types.append( c );
-
            }
 
            charts.insert( country.toUtf8(), QVariant::fromValue<QList< InfoStringHash > >( chart_types ) );
-
         }
 
         QVariantMap defaultMap;
         defaultMap[ "spotify" ] = QStringList() << "United States" << "Top Albums";
         m_allChartsMap[ "defaults" ] = defaultMap;
         m_allChartsMap.insert( "Spotify", QVariant::fromValue<QVariantMap>( charts ) );
-
     }
     else
     {
@@ -281,13 +274,12 @@ SpotifyPlugin::chartTypes()
         }
         m_cachedRequests.clear();
     }
-
 }
 
+
 void
 SpotifyPlugin::chartReturned()
 {
-
     /// Chart request returned something! Woho
     QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
     QString url = reply->url().toString();
@@ -318,14 +310,13 @@ SpotifyPlugin::chartReturned()
         else
             setChartType( None );
 
-        foreach(QVariant result, res.value("toplist").toMap().value("result").toList() )
+        foreach( QVariant result, res.value( "toplist" ).toMap().value( "result" ).toList() )
         {
             QString title, artist;
             QVariantMap chartMap = result.toMap();
 
             if ( !chartMap.isEmpty() )
             {
-
                 title = chartMap.value( "title" ).toString();
                 artist = chartMap.value( "artist" ).toString();
 
@@ -341,7 +332,6 @@ SpotifyPlugin::chartReturned()
 
                 if( chartType() == Album )
                 {
-
                     InfoStringHash pair;
                     pair["artist"] = artist;
                     pair["album"] = title;
@@ -351,10 +341,8 @@ SpotifyPlugin::chartReturned()
 
                 if( chartType() == Artist )
                 {
-
                     top_artists << chartMap.value( "name" ).toString();
                     qDebug() << "SpotifyChart type is artist";
-
                 }
             }
         }
@@ -393,5 +381,4 @@ SpotifyPlugin::chartReturned()
     }
     else
         qDebug() << "Network error in fetching chart:" << reply->url().toString();
-
 }
diff --git a/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.cpp b/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.cpp
index 58a514d49..80f079b88 100644
--- a/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.cpp
+++ b/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.cpp
@@ -42,8 +42,6 @@ MprisPlugin::MprisPlugin()
     : InfoPlugin()
     , m_coverTempFile( 0 )
 {
-    qDebug() << Q_FUNC_INFO;
-
     // init
     m_playbackStatus = "Stopped";
 
@@ -54,79 +52,84 @@ MprisPlugin::MprisPlugin()
     new MprisPluginRootAdaptor( this );
     new MprisPluginPlayerAdaptor( this );
     QDBusConnection dbus = QDBusConnection::sessionBus();
-    dbus.registerObject("/org/mpris/MediaPlayer2", this);
-    dbus.registerService("org.mpris.MediaPlayer2.tomahawk");
+    dbus.registerObject( "/org/mpris/MediaPlayer2", this );
+    dbus.registerService( "org.mpris.MediaPlayer2.tomahawk" );
 
     // Listen to volume changes
     connect( AudioEngine::instance(), SIGNAL( volumeChanged( int ) ),
-            SLOT( onVolumeChanged( int ) ) );
+                                        SLOT( onVolumeChanged( int ) ) );
 
     // When the playlist changes, signals for several properties are sent
     connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::playlistinterface_ptr ) ),
-            SLOT( onPlaylistChanged( Tomahawk::playlistinterface_ptr ) ) );
+                                        SLOT( onPlaylistChanged( Tomahawk::playlistinterface_ptr ) ) );
 
     // When a track is added or removed, CanGoNext updated signal is sent
     Tomahawk::playlistinterface_ptr playlist = AudioEngine::instance()->playlist();
-    if( !playlist.isNull() )
+    if ( !playlist.isNull() )
+    {
         connect( playlist.data(), SIGNAL( trackCountChanged( unsigned int ) ),
-                SLOT( onTrackCountChanged( unsigned int ) ) );
+                                    SLOT( onTrackCountChanged( unsigned int ) ) );
+    }
 
     // Connect to AudioEngine's seeked signal
     connect( AudioEngine::instance(), SIGNAL( seeked( qint64 ) ),
-            SLOT( onSeeked( qint64 ) ) );
+                                        SLOT( onSeeked( qint64 ) ) );
 
     // Connect to the InfoSystem (we need to get album covers via getInfo)
 
     connect( Tomahawk::InfoSystem::InfoSystem::instance(),
-            SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
-            SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
-
-    connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) );
+             SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
+             SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) );
 
+    connect( Tomahawk::InfoSystem::InfoSystem::instance(),
+             SIGNAL( finished( QString ) ),
+             SLOT( infoSystemFinished( QString ) ) );
 }
 
 
 MprisPlugin::~MprisPlugin()
 {
-    qDebug() << Q_FUNC_INFO;
     delete m_coverTempFile;
 }
 
+
 // org.mpris.MediaPlayer2
 
 bool
 MprisPlugin::canQuit() const
 {
-    qDebug() << Q_FUNC_INFO;
     return true;
 }
 
+
 bool
 MprisPlugin::canRaise() const
 {
-    qDebug() << Q_FUNC_INFO;
     return false;
 }
 
+
 bool
 MprisPlugin::hasTrackList() const
 {
-    qDebug() << Q_FUNC_INFO;
     return false;
 }
 
+
 QString
 MprisPlugin::identity() const
 {
-    return QString("Tomahawk");
+    return QString( "Tomahawk" );
 }
 
+
 QString
 MprisPlugin::desktopEntry() const
 {
-    return QString("tomahawk");
+    return QString( "tomahawk" );
 }
 
+
 QStringList
 MprisPlugin::supportedUriSchemes() const
 {
@@ -135,23 +138,27 @@ MprisPlugin::supportedUriSchemes() const
     return uriSchemes;
 }
 
+
 QStringList
 MprisPlugin::supportedMimeTypes() const
 {
     return QStringList();
 }
 
+
 void
 MprisPlugin::Raise()
 {
 }
 
+
 void
 MprisPlugin::Quit()
 {
     QApplication::quit();
 }
 
+
 // org.mpris.MediaPlayer2.Player
 
 bool
@@ -160,24 +167,28 @@ MprisPlugin::canControl() const
     return true;
 }
 
+
 bool
 MprisPlugin::canGoNext() const
 {
     return AudioEngine::instance()->canGoNext();
 }
 
+
 bool
 MprisPlugin::canGoPrevious() const
 {
     return AudioEngine::instance()->canGoPrevious();
 }
 
+
 bool
 MprisPlugin::canPause() const
 {
     return AudioEngine::instance()->currentTrack();
 }
 
+
 bool
 MprisPlugin::canPlay() const
 {
@@ -186,6 +197,7 @@ MprisPlugin::canPlay() const
     return AudioEngine::instance()->currentTrack() || ( !p.isNull() && p->trackCount() );
 }
 
+
 bool
 MprisPlugin::canSeek() const
 {
@@ -196,6 +208,7 @@ MprisPlugin::canSeek() const
 
 }
 
+
 QString
 MprisPlugin::loopStatus() const
 {
@@ -215,39 +228,42 @@ MprisPlugin::loopStatus() const
             return "None";
             break;
         default:
-            return QString("None");
+            return "None";
             break;
     }
 
-    return QString("None");
+    return QString( "None" );
 }
 
+
 void
-MprisPlugin::setLoopStatus( const QString &value )
+MprisPlugin::setLoopStatus( const QString& value )
 {
     Tomahawk::playlistinterface_ptr p = AudioEngine::instance()->playlist();
     if ( p.isNull() )
         return;
-    if( value == "Track")
+    if ( value == "Track" )
         p->setRepeatMode( PlaylistInterface::RepeatOne );
-    else if( value == "Playlist" )
+    else if ( value == "Playlist" )
         p->setRepeatMode( PlaylistInterface::RepeatAll );
-    else if( value == "None" )
+    else if ( value == "None" )
         p->setRepeatMode( PlaylistInterface::NoRepeat );
 }
 
+
 double
 MprisPlugin::maximumRate() const
 {
     return 1.0;
 }
 
+
 QVariantMap
 MprisPlugin::metadata() const
 {
     QVariantMap metadataMap;
     Tomahawk::result_ptr track = AudioEngine::instance()->currentTrack();
-    if( track )
+    if ( track )
     {
         metadataMap.insert( "mpris:trackid", QString( "/track/" ) + track->id().replace( "-", "" ) );
         metadataMap.insert( "mpris:length", track->duration() );
@@ -256,9 +272,11 @@ MprisPlugin::metadata() const
         metadataMap.insert( "xesam:title", track->track() );
 
         // Only return art if tempfile exists, and if its name contains the same "artist_album_tomahawk_cover.png"
-        if( m_coverTempFile && m_coverTempFile->exists() &&
-                m_coverTempFile->fileName().contains( track->artist()->name() + "_" + track->album()->name() + "_tomahawk_cover.png" ) )
+        if ( m_coverTempFile && m_coverTempFile->exists() &&
+             m_coverTempFile->fileName().contains( track->artist()->name() + "_" + track->album()->name() + "_tomahawk_cover.png" ) )
+        {
             metadataMap.insert( "mpris:artUrl", QString( QUrl::fromLocalFile( QFileInfo( *m_coverTempFile ).absoluteFilePath() ).toEncoded() ) );
+        }
         else
         {
             // Need to fetch the album cover
@@ -280,18 +298,21 @@ MprisPlugin::metadata() const
     return metadataMap;
 }
 
+
 double
 MprisPlugin::minimumRate() const
 {
     return 1.0;
 }
 
+
 QString
 MprisPlugin::playbackStatus() const
 {
     return m_playbackStatus;
 }
 
+
 qlonglong
 MprisPlugin::position() const
 {
@@ -299,18 +320,21 @@ MprisPlugin::position() const
     return (qlonglong) ( AudioEngine::instance()->currentTime() * 1000 );
 }
 
+
 double
 MprisPlugin::rate() const
 {
     return 1.0;
 }
 
+
 void
 MprisPlugin::setRate( double value )
 {
     Q_UNUSED( value );
 }
 
+
 bool
 MprisPlugin::shuffle() const
 {
@@ -320,6 +344,7 @@ MprisPlugin::shuffle() const
     return p->shuffled();
 }
 
+
 void
 MprisPlugin::setShuffle( bool value )
 {
@@ -329,70 +354,76 @@ MprisPlugin::setShuffle( bool value )
     return p->setShuffled( value );
 }
 
+
 double
 MprisPlugin::volume() const
 {
     return AudioEngine::instance()->volume();
 }
 
+
 void
 MprisPlugin::setVolume( double value )
 {
     AudioEngine::instance()->setVolume( value );
 }
 
+
 void
 MprisPlugin::Next()
 {
     AudioEngine::instance()->next();
 }
 
+
 void
-MprisPlugin::OpenUri( const QString &Uri )
+MprisPlugin::OpenUri( const QString& Uri )
 {
-    if( Uri.contains( "tomahawk://" ) )
+    if ( Uri.contains( "tomahawk://" ) )
         GlobalActionManager::instance()->parseTomahawkLink( Uri );
-    else if( Uri.contains( "spotify:" ) )
+    else if ( Uri.contains( "spotify:" ) )
         GlobalActionManager::instance()->openSpotifyLink( Uri );
 }
 
+
 void
 MprisPlugin::Pause()
 {
     AudioEngine::instance()->pause();
 }
 
+
 void
 MprisPlugin::Play()
 {
     AudioEngine::instance()->play();
 }
 
+
 void
 MprisPlugin::PlayPause()
 {
     AudioEngine::instance()->playPause();
 }
 
+
 void
 MprisPlugin::Previous()
 {
     AudioEngine::instance()->previous();
 }
 
+
 void
 MprisPlugin::Seek( qlonglong Offset )
 {
-    qDebug() << Q_FUNC_INFO;
-
-    if( !canSeek() )
+    if ( !canSeek() )
         return;
 
     qlonglong seekTime = position() + Offset;
-    qDebug() << "seekTime: " << seekTime;
-    if( seekTime < 0 )
+    if ( seekTime < 0 )
         AudioEngine::instance()->seek( 0 );
-    else if( seekTime > AudioEngine::instance()->currentTrackTotalTime()*1000 )
+    else if ( seekTime > AudioEngine::instance()->currentTrackTotalTime()*1000 )
         Next();
     // seekTime is in microseconds, but we work internally in milliseconds
     else
@@ -400,50 +431,46 @@ MprisPlugin::Seek( qlonglong Offset )
 
 }
 
+
 void
-MprisPlugin::SetPosition( const QDBusObjectPath &TrackId, qlonglong Position )
+MprisPlugin::SetPosition( const QDBusObjectPath& TrackId, qlonglong Position )
 {
-    qDebug() << Q_FUNC_INFO;
-    if( !canSeek() )
+    if ( !canSeek() )
         return;
 
-    qDebug() << "path: " << TrackId.path();
-    qDebug() << "position: " << Position;
-
-    if( TrackId.path() != QString("/track/") + AudioEngine::instance()->currentTrack()->id().replace( "-", "" ) )
+    if ( TrackId.path() != QString( "/track/" ) + AudioEngine::instance()->currentTrack()->id().replace( "-", "" ) )
         return;
 
-    if( ( Position < 0) || ( Position > AudioEngine::instance()->currentTrackTotalTime()*1000 )  )
+    if ( ( Position < 0) || ( Position > AudioEngine::instance()->currentTrackTotalTime()*1000 )  )
         return;
 
-    qDebug() << "seeking to: " << Position/1000 << "ms";
-
     AudioEngine::instance()->seek( (qint64) (Position / 1000 ) );
 
 }
 
+
 void
 MprisPlugin::Stop()
 {
     AudioEngine::instance()->stop();
 }
 
+
 // InfoPlugin Methods
 
 void
 MprisPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
   Q_UNUSED( requestData );
-  qDebug() << Q_FUNC_INFO;
 
   return;
 }
 
+
 void
 MprisPlugin::pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input )
 {
     Q_UNUSED( caller );
-    qDebug() << Q_FUNC_INFO;
     bool isPlayingInfo = false;
 
     switch ( type )
@@ -469,11 +496,11 @@ MprisPlugin::pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVar
           break;
     }
 
-    if( isPlayingInfo )
-        notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "PlaybackStatus");
-
+    if ( isPlayingInfo )
+        notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "PlaybackStatus" );
 }
 
+
 void
 MprisPlugin::stateChanged( AudioState newState, AudioState oldState )
 {
@@ -481,12 +508,11 @@ MprisPlugin::stateChanged( AudioState newState, AudioState oldState )
     Q_UNUSED( oldState );
 }
 
+
 /** Audio state slots */
 void
-MprisPlugin::audioStarted( const QVariant &input )
+MprisPlugin::audioStarted( const QVariant& input )
 {
-    qDebug() << Q_FUNC_INFO;
-
     if ( !input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
         return;
 
@@ -495,64 +521,55 @@ MprisPlugin::audioStarted( const QVariant &input )
         return;
 
     m_playbackStatus = "Playing";
-    notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata");
-
-    //hash["artist"];
-    //hash["title"];
-    //QString nowPlaying = "";
-    //qDebug() << "nowPlaying: " << nowPlaying;
+    notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" );
 }
 
+
 void
-MprisPlugin::audioFinished( const QVariant &input )
+MprisPlugin::audioFinished( const QVariant& input )
 {
     Q_UNUSED( input );
-    //qDebug() << Q_FUNC_INFO;
 }
 
+
 void
 MprisPlugin::audioStopped()
 {
-    qDebug() << Q_FUNC_INFO;
     m_playbackStatus = "Stopped";
 }
 
+
 void
 MprisPlugin::audioPaused()
 {
-    qDebug() << Q_FUNC_INFO;
     m_playbackStatus = "Paused";
 }
 
+
 void
-MprisPlugin::audioResumed( const QVariant &input )
+MprisPlugin::audioResumed( const QVariant& input )
 {
-    qDebug() << Q_FUNC_INFO;
     audioStarted( input );
 }
 
+
 void
 MprisPlugin::onVolumeChanged( int volume )
 {
     Q_UNUSED( volume );
-    notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Volume");
+    notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Volume" );
 }
 
+
 void
 MprisPlugin::onPlaylistChanged( Tomahawk::playlistinterface_ptr playlist )
 {
-    qDebug() << Q_FUNC_INFO;
     disconnect( this, SLOT( onTrackCountChanged( unsigned int ) ) );
-    qDebug() << "disconnected";
-    if( !playlist.isNull() )
-        qDebug() << "playlist not null";
 
-    if( !playlist.isNull() )
+    if ( !playlist.isNull() )
         connect( playlist.data(), SIGNAL( trackCountChanged( unsigned int ) ),
             SLOT( onTrackCountChanged( unsigned int ) ) );
 
-    qDebug() << "connected new playlist";
-
     // Notify relevant changes
     notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "LoopStatus" );
     notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Shuffle" );
@@ -560,6 +577,7 @@ MprisPlugin::onPlaylistChanged( Tomahawk::playlistinterface_ptr playlist )
     onTrackCountChanged( 0 );
 }
 
+
 void
 MprisPlugin::onTrackCountChanged( unsigned int tracks )
 {
@@ -569,12 +587,14 @@ MprisPlugin::onTrackCountChanged( unsigned int tracks )
 
 }
 
- void
- MprisPlugin::onSeeked( qint64 ms )
- {
+
+void
+MprisPlugin::onSeeked( qint64 ms )
+{
     qlonglong us = (qlonglong) ( ms*1000 );
     emit Seeked( us );
- }
+}
+
 
 void
 MprisPlugin::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
@@ -603,7 +623,7 @@ MprisPlugin::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData,
         image.loadFromData( ba );
 
         // Pull out request data for album+artist
-        if( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
+        if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
         {
             qDebug() << "Cannot convert metadata input to album cover retrieval";
             return;
@@ -612,41 +632,33 @@ MprisPlugin::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData,
         Tomahawk::InfoSystem::InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>();
 
         // delete the old tempfile and make new one, to avoid caching of filename by mpris clients
-        if( m_coverTempFile )
+        if ( m_coverTempFile )
+        {
             delete m_coverTempFile;
-        m_coverTempFile = new QTemporaryFile( QDir::toNativeSeparators(
-                                                 QDir::tempPath() + "/" + hash["artist"] + "_" + hash["album"] + "_tomahawk_cover.png" ) );
-        if( !m_coverTempFile->open() )
+            m_coverTempFile = 0;
+        }
+
+        if ( image.isNull() )
+            return;
+
+        m_coverTempFile = new QTemporaryFile( QDir::toNativeSeparators( QDir::tempPath() + "/" + hash["artist"] + "_" + hash["album"] + "_tomahawk_cover.png" ) );
+        if ( !m_coverTempFile->open() )
         {
             qDebug() << "WARNING: could not write temporary file for cover art!";
         }
 
         // Finally, save the image to the new temp file
-        //if( image.save( QFileInfo( *m_coverTempFile ).absoluteFilePath(), "PNG" ) )
-        if( image.save( m_coverTempFile, "PNG") )
+        if ( image.save( m_coverTempFile, "PNG" ) )
         {
-            qDebug() << Q_FUNC_INFO << "Image saving successful, notifying";
-            qDebug() << "Saving to: " << QFileInfo( *m_coverTempFile ).absoluteFilePath();
+            qDebug() << "Saving cover image to:" << QFileInfo( *m_coverTempFile ).absoluteFilePath();
             m_coverTempFile->close();
             notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" );
         }
         else
         {
-            qDebug() << Q_FUNC_INFO << " failed to save image!";
+            tDebug() << Q_FUNC_INFO << "failed to save cover image!";
             m_coverTempFile->close();
         }
-
-
-
-        /*
-        if( m_coverTempFile->open() )
-        {
-            QTextStream out( m_coverTempFile );
-            out << ba;
-            m_coverTempFile->close();
-            notifyPropertyChanged( "org.mpris.MediaPlayer2.Player", "Metadata" );
-        }
-                */
     }
 }
 
@@ -657,14 +669,14 @@ MprisPlugin::infoSystemFinished( QString target )
     Q_UNUSED( target );
 }
 
+
 void
-MprisPlugin::notifyPropertyChanged( const QString& interface,
-                            const QString& propertyName )
+MprisPlugin::notifyPropertyChanged( const QString& interface, const QString& propertyName )
 {
     QDBusMessage signal = QDBusMessage::createSignal(
         "/org/mpris/MediaPlayer2",
         "org.freedesktop.DBus.Properties",
-        "PropertiesChanged");
+        "PropertiesChanged" );
     signal << interface;
     QVariantMap changedProps;
     changedProps.insert(propertyName, property(propertyName.toAscii()));
diff --git a/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.h b/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.h
index db87ca37c..f0f9864ff 100644
--- a/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.h
+++ b/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.h
@@ -87,7 +87,7 @@ public:
 
     Q_PROPERTY( QString LoopStatus READ loopStatus WRITE setLoopStatus )
     QString loopStatus() const;
-    void setLoopStatus( const QString &value );
+    void setLoopStatus( const QString& value );
 
     Q_PROPERTY( double MaximumRate READ maximumRate )
     double maximumRate() const;
@@ -129,16 +129,15 @@ public slots:
 
     // org.mpris.MediaPlayer2.Player
     void Next();
-    void OpenUri( const QString &Uri );
+    void OpenUri( const QString& Uri );
     void Pause();
     void Play();
     void PlayPause();
     void Previous();
     void Seek( qlonglong Offset );
-    void SetPosition( const QDBusObjectPath &TrackId, qlonglong Position );
+    void SetPosition( const QDBusObjectPath& TrackId, qlonglong Position );
     void Stop();
 
-
 protected slots:
     void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
     void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input );
@@ -160,16 +159,16 @@ private:
     // Get Info
 
     // Push Info
-    void audioStarted( const QVariant &input );
-    void audioFinished( const QVariant &input );
+    void audioStarted( const QVariant& input );
+    void audioFinished( const QVariant& input );
     void audioStopped();
     void audioPaused();
-    void audioResumed( const QVariant &input );
+    void audioResumed( const QVariant& input );
 
     // DBus
     void notifyPropertyChanged( const QString& interface, const QString& propertyName );
     QString m_playbackStatus;
-    QTemporaryFile *m_coverTempFile;
+    QTemporaryFile* m_coverTempFile;
 };
 
 };
diff --git a/src/libtomahawk/jobview/ErrorStatusMessage.cpp b/src/libtomahawk/jobview/ErrorStatusMessage.cpp
new file mode 100644
index 000000000..38f1082a1
--- /dev/null
+++ b/src/libtomahawk/jobview/ErrorStatusMessage.cpp
@@ -0,0 +1,56 @@
+/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 2012, Leo Franchi <lfranchi@kde.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 "ErrorStatusMessage.h"
+
+#include "utils/tomahawkutils.h"
+
+#include <QTimer>
+
+QPixmap* ErrorStatusMessage::s_pixmap = 0;
+
+ErrorStatusMessage::ErrorStatusMessage( const QString& message, int timeoutSecs )
+  : JobStatusItem()
+  , m_message( message )
+{
+    m_timer = new QTimer( this );
+    m_timer->setInterval( timeoutSecs * 1000 );
+    m_timer->setSingleShot( true );
+
+    connect( m_timer, SIGNAL( timeout() ), this, SIGNAL( finished() ) );
+
+    if ( !s_pixmap )
+        s_pixmap = new QPixmap( RESPATH "images/process-stop.png" );
+
+    m_timer->start();
+}
+
+
+QPixmap
+ErrorStatusMessage::icon() const
+{
+    Q_ASSERT( s_pixmap );
+    return *s_pixmap;
+}
+
+
+QString
+ErrorStatusMessage::mainText() const
+{
+    return m_message;
+}
diff --git a/src/libtomahawk/jobview/ErrorStatusMessage.h b/src/libtomahawk/jobview/ErrorStatusMessage.h
new file mode 100644
index 000000000..7bf4f2fe2
--- /dev/null
+++ b/src/libtomahawk/jobview/ErrorStatusMessage.h
@@ -0,0 +1,49 @@
+/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 2012, Leo Franchi <lfranchi@kde.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 ERRORSTATUSMESSAGE_H
+#define ERRORSTATUSMESSAGE_H
+
+#include "JobStatusItem.h"
+#include "dllmacro.h"
+
+#include <QPixmap>
+
+class QTimer;
+
+class DLLEXPORT ErrorStatusMessage : public JobStatusItem
+{
+    Q_OBJECT
+public:
+    explicit ErrorStatusMessage( const QString& errorMessage, int defaultTimeoutSecs = 8 );
+
+    QString type() const { return "errormessage"; }
+    QString rightColumnText() const { return QString(); }
+
+    QPixmap icon() const;
+    QString mainText() const;
+
+    bool allowMultiLine() const { return true; }
+private:
+    QString m_message;
+    QTimer* m_timer;
+
+    static QPixmap* s_pixmap;
+};
+
+#endif // ERRORSTATUSMESSAGE_H
diff --git a/src/libtomahawk/jobview/IndexingJobItem.cpp b/src/libtomahawk/jobview/IndexingJobItem.cpp
new file mode 100644
index 000000000..c9ec6928f
--- /dev/null
+++ b/src/libtomahawk/jobview/IndexingJobItem.cpp
@@ -0,0 +1,49 @@
+/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.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 "IndexingJobItem.h"
+
+#include "utils/tomahawkutils.h"
+
+#include <QPixmap>
+
+static QPixmap* s_indexIcon = 0;
+
+
+QString
+IndexingJobItem::mainText() const
+{
+    return tr( "Indexing database" );
+}
+
+QPixmap
+IndexingJobItem::icon() const
+{
+    if ( s_indexIcon == 0 )
+        s_indexIcon = new QPixmap( RESPATH "images/view-refresh.png" );
+
+    return *s_indexIcon;
+
+}
+
+
+void IndexingJobItem::done()
+{
+    emit finished();
+}
+
diff --git a/src/libtomahawk/jobview/IndexingJobItem.h b/src/libtomahawk/jobview/IndexingJobItem.h
new file mode 100644
index 000000000..61bf5a05f
--- /dev/null
+++ b/src/libtomahawk/jobview/IndexingJobItem.h
@@ -0,0 +1,39 @@
+/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.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 INDEXINGJOBITEM_H
+#define INDEXINGJOBITEM_H
+
+#include <jobview/JobStatusItem.h>
+
+
+class IndexingJobItem : public JobStatusItem
+{
+    Q_OBJECT
+public:
+    explicit IndexingJobItem() {}
+
+    void done();
+
+    virtual QString rightColumnText() const { return QString(); }
+    virtual QString mainText() const;
+    virtual QPixmap icon() const;
+    virtual QString type() const { return "indexerjob"; }
+};
+
+#endif // INDEXINGJOBITEM_H
diff --git a/src/libtomahawk/jobview/JobStatusDelegate.cpp b/src/libtomahawk/jobview/JobStatusDelegate.cpp
index c279687ee..9c686c15d 100644
--- a/src/libtomahawk/jobview/JobStatusDelegate.cpp
+++ b/src/libtomahawk/jobview/JobStatusDelegate.cpp
@@ -23,14 +23,16 @@
 
 #include <QPainter>
 #include <QApplication>
+#include <QListView>
 
 #define ROW_HEIGHT 20
 #define ICON_PADDING 1
 #define PADDING 2
 JobStatusDelegate::JobStatusDelegate( QObject* parent )
     : QStyledItemDelegate ( parent )
+    , m_parentView( qobject_cast< QListView* >( parent ) )
 {
-
+    Q_ASSERT( m_parentView );
 }
 
 JobStatusDelegate::~JobStatusDelegate()
@@ -45,6 +47,7 @@ JobStatusDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
     QStyleOptionViewItemV4 opt = option;
     initStyleOption( &opt, index );
     QFontMetrics fm( opt.font );
+    const bool allowMultiLine = index.data( JobStatusModel::AllowMultiLineRole ).toBool();
 
     opt.state &= ~QStyle::State_MouseOver;
     QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget );
@@ -52,7 +55,9 @@ JobStatusDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
 //     painter->drawLine( opt.rect.topLeft(), opt.rect.topRight() );
 
     painter->setRenderHint( QPainter::Antialiasing );
-    const QRect iconRect( ICON_PADDING, ICON_PADDING + opt.rect.y(), ROW_HEIGHT - 2*ICON_PADDING, ROW_HEIGHT - 2*ICON_PADDING );
+    QRect iconRect( ICON_PADDING, ICON_PADDING + opt.rect.y(), ROW_HEIGHT - 2*ICON_PADDING, ROW_HEIGHT - 2*ICON_PADDING );
+    if ( allowMultiLine )
+        iconRect.moveTop( opt.rect.top() + opt.rect.height() / 2 - iconRect.height() / 2);
     QPixmap p = index.data( Qt::DecorationRole ).value< QPixmap >();
     p = p.scaledToHeight( iconRect.height(), Qt::SmoothTransformation );
     painter->drawPixmap( iconRect, p );
@@ -71,15 +76,34 @@ JobStatusDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
 
     const int mainW = rightEdge - 3*PADDING - iconRect.right();
     QString mainText = index.data( Qt::DisplayRole ).toString();
-    mainText = fm.elidedText( mainText, Qt::ElideRight, mainW  );
-    painter->drawText( QRect( iconRect.right() + 2*PADDING, PADDING + opt.rect.y(), mainW, opt.rect.height() - 2*PADDING ), Qt::AlignLeft | Qt::AlignVCenter, mainText );
+    QTextOption to( Qt::AlignLeft | Qt::AlignVCenter );
+    if ( !allowMultiLine )
+        mainText = fm.elidedText( mainText, Qt::ElideRight, mainW  );
+    else
+        to.setWrapMode( QTextOption::WrapAtWordBoundaryOrAnywhere );
+    painter->drawText( QRect( iconRect.right() + 2*PADDING, PADDING + opt.rect.y(), mainW, opt.rect.height() - 2*PADDING ), mainText, to );
 }
 
 QSize
 JobStatusDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const
 {
-//     return QStyledItemDelegate::sizeHint( option, index );
-    const int w = QStyledItemDelegate::sizeHint ( option, index ).width();
-    return QSize( w, ROW_HEIGHT );
+    const bool allowMultiLine = index.data( JobStatusModel::AllowMultiLineRole ).toBool();
+
+    if ( !allowMultiLine )
+        return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), ROW_HEIGHT );
+    else if ( m_cachedMultiLineHeights.contains( index ) )
+        return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), m_cachedMultiLineHeights[ index ] );
+
+    // Don't elide, but stretch across as many rows as required
+    QStyleOptionViewItemV4 opt = option;
+    initStyleOption( &opt, index );
+
+    const QString text = index.data( Qt::DisplayRole ).toString();
+    const int leftEdge =  ICON_PADDING + ROW_HEIGHT + 2*PADDING;
+    const QRect rect = opt.fontMetrics.boundingRect( leftEdge, opt.rect.top(), m_parentView->width() - leftEdge, 200, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, text );
+
+    m_cachedMultiLineHeights.insert( index, rect.height() + 4*PADDING );
+
+    return QSize( QStyledItemDelegate::sizeHint ( option, index ).width(), rect.height() + 4*PADDING );
 }
 
diff --git a/src/libtomahawk/jobview/JobStatusDelegate.h b/src/libtomahawk/jobview/JobStatusDelegate.h
index fc2752350..08a355db4 100644
--- a/src/libtomahawk/jobview/JobStatusDelegate.h
+++ b/src/libtomahawk/jobview/JobStatusDelegate.h
@@ -22,6 +22,7 @@
 #include <QStyledItemDelegate>
 
 class QPainter;
+class QListView;
 
 class JobStatusDelegate : public QStyledItemDelegate
 {
@@ -33,6 +34,10 @@ public:
 
     virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
     virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const;
+
+private:
+    mutable QHash< QPersistentModelIndex, int > m_cachedMultiLineHeights;
+    QListView* m_parentView;
 };
 
 #endif // JOBSTATUSDELEGATE_H
diff --git a/src/libtomahawk/jobview/JobStatusItem.cpp b/src/libtomahawk/jobview/JobStatusItem.cpp
new file mode 100644
index 000000000..092f140d4
--- /dev/null
+++ b/src/libtomahawk/jobview/JobStatusItem.cpp
@@ -0,0 +1,42 @@
+/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.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 "JobStatusItem.h"
+
+
+JobStatusItem::JobStatusItem()
+    : QObject()
+{
+}
+
+
+JobStatusItem::~JobStatusItem()
+{
+}
+
+
+bool JobStatusItem::allowMultiLine() const
+{
+    return false;
+}
+
+bool JobStatusItem::collapseItem() const
+{
+    return false;
+}
diff --git a/src/libtomahawk/jobview/JobStatusItem.h b/src/libtomahawk/jobview/JobStatusItem.h
index 77f0dbb6e..a8190f269 100644
--- a/src/libtomahawk/jobview/JobStatusItem.h
+++ b/src/libtomahawk/jobview/JobStatusItem.h
@@ -20,7 +20,8 @@
 #define JOB_STATUS_ITEM
 
 #include <QObject>
-#include <QPixmap>
+
+class QPixmap;
 
 /**
  * Implement your own JobStatusItem if you want to add some sort of job status entry in the JobStatusView.
@@ -37,8 +38,8 @@ class JobStatusItem : public QObject
 {
     Q_OBJECT
 public:
-    explicit JobStatusItem() : QObject() {}
-    virtual ~JobStatusItem() {}
+    explicit JobStatusItem();
+    virtual ~JobStatusItem();
 
     virtual QString type() const = 0;
 
@@ -52,7 +53,8 @@ public:
      * instead of showing each individually. In this case, the right column from the item will be ignored
      * and a count will be shown instead.
      */
-    virtual bool collapseItem() const { return false; }
+    virtual bool collapseItem() const;
+    virtual bool allowMultiLine() const;
 
 signals:
     /// Ask for an update
diff --git a/src/libtomahawk/jobview/JobStatusModel.cpp b/src/libtomahawk/jobview/JobStatusModel.cpp
index a50c8a4e3..b22594a59 100644
--- a/src/libtomahawk/jobview/JobStatusModel.cpp
+++ b/src/libtomahawk/jobview/JobStatusModel.cpp
@@ -21,6 +21,7 @@
 #include "JobStatusItem.h"
 #include "utils/logger.h"
 
+#include <QPixmap>
 
 JobStatusModel::JobStatusModel( QObject* parent )
     : QAbstractListModel ( parent )
@@ -100,6 +101,8 @@ JobStatusModel::data( const QModelIndex& index, int role ) const
         else
             return item->rightColumnText();
     }
+    case AllowMultiLineRole:
+        return item->allowMultiLine();
     }
 
     return QVariant();
diff --git a/src/libtomahawk/jobview/JobStatusModel.h b/src/libtomahawk/jobview/JobStatusModel.h
index c3611cfd3..539189337 100644
--- a/src/libtomahawk/jobview/JobStatusModel.h
+++ b/src/libtomahawk/jobview/JobStatusModel.h
@@ -31,7 +31,8 @@ public:
     enum JobRoles {
         // DecorationRole is icon
         // DisplayRole is main col
-        RightColumnRole = Qt::UserRole + 1
+        RightColumnRole = Qt::UserRole + 1,
+        AllowMultiLineRole = Qt::UserRole + 2
     };
 
     explicit JobStatusModel( QObject* parent = 0 );
diff --git a/src/libtomahawk/jobview/JobStatusView.cpp b/src/libtomahawk/jobview/JobStatusView.cpp
index fa121a1f0..4b10f09e2 100644
--- a/src/libtomahawk/jobview/JobStatusView.cpp
+++ b/src/libtomahawk/jobview/JobStatusView.cpp
@@ -40,6 +40,7 @@ JobStatusView* JobStatusView::s_instance = 0;
 JobStatusView::JobStatusView( AnimatedSplitter* parent )
     : AnimatedWidget( parent )
     , m_parent( parent )
+    , m_cachedHeight( -1 )
 {
     s_instance = this;
 
@@ -56,9 +57,7 @@ JobStatusView::JobStatusView( AnimatedSplitter* parent )
 
     m_view->setFrameShape( QFrame::NoFrame );
     m_view->setAttribute( Qt::WA_MacShowFocusRect, 0 );
-
-//     new QTreeWidgetItem( m_tree );
-    m_view->setUniformItemSizes( true );
+    m_view->setUniformItemSizes( false );
 
 #ifndef Q_WS_WIN
     QFont f = font();
@@ -86,12 +85,14 @@ JobStatusView::setModel( JobStatusModel* m )
 
     connect( m_view->model(), SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( checkCount() ) );
     connect( m_view->model(), SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( checkCount() ) );
+    connect( m_view->model(), SIGNAL( modelReset() ), this, SLOT( checkCount() ) );
 }
 
 
 void
 JobStatusView::checkCount()
 {
+    m_cachedHeight = -1;
     if ( m_view->model()->rowCount() == 0 && !isHidden() )
         emit hideWidget();
     else
@@ -102,15 +103,21 @@ JobStatusView::checkCount()
 QSize
 JobStatusView::sizeHint() const
 {
+    if ( m_cachedHeight >= 0 )
+        return QSize( 0, m_cachedHeight );
+
     unsigned int y = 0;
-//     y += m_tree->header()->height();
     y += m_view->contentsMargins().top() + m_view->contentsMargins().bottom();
 
     if ( m_view->model()->rowCount() )
     {
-        unsigned int rowheight = m_view->sizeHintForRow( 0 );
-        y += rowheight * m_view->model()->rowCount() + 2;
+        for ( int i = 0; i < m_view->model()->rowCount(); i++ )
+        {
+            y += m_view->sizeHintForRow( i );
+        }
+        y += 2; // some padding
     }
 
+    m_cachedHeight = y;
     return QSize( 0, y );
 }
diff --git a/src/libtomahawk/jobview/JobStatusView.h b/src/libtomahawk/jobview/JobStatusView.h
index 1cbc5a7c2..5d07c16b7 100644
--- a/src/libtomahawk/jobview/JobStatusView.h
+++ b/src/libtomahawk/jobview/JobStatusView.h
@@ -56,6 +56,7 @@ private:
     QListView* m_view;
     JobStatusModel* m_model;
     AnimatedSplitter* m_parent;
+    mutable int m_cachedHeight;
 
     static JobStatusView* s_instance;
 };
diff --git a/src/libtomahawk/jobview/LatchedStatusItem.h b/src/libtomahawk/jobview/LatchedStatusItem.h
index 7f7cb3f8a..6841c3240 100644
--- a/src/libtomahawk/jobview/LatchedStatusItem.h
+++ b/src/libtomahawk/jobview/LatchedStatusItem.h
@@ -21,7 +21,9 @@
 
 #include "JobStatusItem.h"
 #include "typedefs.h"
+
 #include <QHash>
+#include <QPixmap>
 
 class LatchedStatusManager;
 
diff --git a/src/libtomahawk/jobview/PipelineStatusItem.h b/src/libtomahawk/jobview/PipelineStatusItem.h
index 5072c506f..04fe70bc1 100644
--- a/src/libtomahawk/jobview/PipelineStatusItem.h
+++ b/src/libtomahawk/jobview/PipelineStatusItem.h
@@ -22,6 +22,8 @@
 #include "jobview/JobStatusItem.h"
 #include "query.h"
 
+#include <QPixmap>
+
 class PipelineStatusItem : public JobStatusItem
 {
     Q_OBJECT
diff --git a/src/libtomahawk/jobview/TransferStatusItem.h b/src/libtomahawk/jobview/TransferStatusItem.h
index 2387580ec..1670343c2 100644
--- a/src/libtomahawk/jobview/TransferStatusItem.h
+++ b/src/libtomahawk/jobview/TransferStatusItem.h
@@ -21,6 +21,8 @@
 
 #include "JobStatusItem.h"
 
+#include <QPixmap>
+
 class StreamConnection;
 
 class TransferStatusManager : public QObject
diff --git a/src/libtomahawk/network/portfwdthread.cpp b/src/libtomahawk/network/portfwdthread.cpp
index 6006699bf..c0097d90b 100644
--- a/src/libtomahawk/network/portfwdthread.cpp
+++ b/src/libtomahawk/network/portfwdthread.cpp
@@ -43,7 +43,7 @@ PortFwdThread::~PortFwdThread()
 {
     qDebug() << Q_FUNC_INFO << "waiting for event loop to finish...";
     quit();
-    wait( 1000 );
+    wait( 6000 );
 
     delete m_portfwd;
 }
diff --git a/src/libtomahawk/pipeline.cpp b/src/libtomahawk/pipeline.cpp
index 2b25caa14..a1cc86970 100644
--- a/src/libtomahawk/pipeline.cpp
+++ b/src/libtomahawk/pipeline.cpp
@@ -28,6 +28,8 @@
 
 #include "utils/logger.h"
 
+#include "boost/bind.hpp"
+
 #define DEFAULT_CONCURRENT_QUERIES 4
 #define MAX_CONCURRENT_QUERIES 16
 #define CLEANUP_TIMEOUT 5 * 60 * 1000
@@ -61,6 +63,7 @@ Pipeline::Pipeline( QObject* parent )
 
 Pipeline::~Pipeline()
 {
+    tDebug() << Q_FUNC_INFO;
     m_running = false;
 
     // stop script resolvers
@@ -418,10 +421,11 @@ Pipeline::shunt( const query_ptr& q )
         r->resolve( q );
         emit resolving( q );
 
-        m_qidsTimeout.insert( q->id(), true );
-
         if ( r->timeout() > 0 )
+        {
+            m_qidsTimeout.insert( q->id(), true );
             new FuncTimeout( r->timeout(), boost::bind( &Pipeline::timeoutShunt, this, q ), this );
+        }
     }
     else
     {
diff --git a/src/libtomahawk/pipeline.h b/src/libtomahawk/pipeline.h
index 5fa88a2d7..50e33ab57 100644
--- a/src/libtomahawk/pipeline.h
+++ b/src/libtomahawk/pipeline.h
@@ -48,6 +48,8 @@ public:
     explicit Pipeline( QObject* parent = 0 );
     virtual ~Pipeline();
 
+    bool isRunning() const { return m_running; }
+
     unsigned int pendingQueryCount() const { return m_queries_pending.count(); }
     unsigned int activeQueryCount() const { return m_qidsState.count(); }
 
diff --git a/src/libtomahawk/playlist/XspfUpdater.cpp b/src/libtomahawk/playlist/XspfUpdater.cpp
index 796efb17e..a9d794334 100644
--- a/src/libtomahawk/playlist/XspfUpdater.cpp
+++ b/src/libtomahawk/playlist/XspfUpdater.cpp
@@ -71,7 +71,11 @@ XspfUpdater::playlistLoaded()
     foreach ( const plentry_ptr ple, playlist()->entries() )
         tracks << ple->query();
 
-    QList< query_ptr > mergedTracks = TomahawkUtils::mergePlaylistChanges( tracks, loader->entries() );
+    bool changed = false;
+    QList< query_ptr > mergedTracks = TomahawkUtils::mergePlaylistChanges( tracks, loader->entries(), changed );
+
+    if ( !changed )
+        return;
 
     QList<Tomahawk::plentry_ptr> el = playlist()->entriesFromQueries( mergedTracks, true );
     playlist()->createNewRevision( uuid(), playlist()->currentrevision(), el );
diff --git a/src/libtomahawk/playlist/albumitemdelegate.cpp b/src/libtomahawk/playlist/albumitemdelegate.cpp
index e28208248..b8c9072d2 100644
--- a/src/libtomahawk/playlist/albumitemdelegate.cpp
+++ b/src/libtomahawk/playlist/albumitemdelegate.cpp
@@ -40,7 +40,6 @@ AlbumItemDelegate::AlbumItemDelegate( QAbstractItemView* parent, AlbumProxyModel
     , m_view( parent )
     , m_model( proxy )
 {
-    m_defaultCover = QPixmap( RESPATH "images/no-album-art-placeholder.png" );
 }
 
 
@@ -89,21 +88,19 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
         painter->drawLine( shadowRect.bottomLeft() + QPoint( 0, 4 ), shadowRect.bottomRight() + QPoint( 0, 4 ) );
     }
 
+    QRect r = option.rect.adjusted( 6, 5, -6, -41 );
     QPixmap cover;
     if ( !item->album().isNull() )
     {
-        cover.loadFromData( item->album()->cover() );
+        cover = item->album()->cover( r.size() );
+        if ( cover.isNull() )
+            cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::CoverInCase, r.size() );
     }
     else if ( !item->artist().isNull() )
     {
-        cover.loadFromData( item->artist()->cover() );
+        cover = item->artist()->cover( r.size() );
     }
 
-    if ( cover.isNull() )
-        cover = m_defaultCover;
-
-    QRect r = option.rect.adjusted( 6, 5, -6, -41 );
-
     if ( option.state & QStyle::State_Selected )
     {
 #if defined(Q_WS_MAC) || defined(Q_WS_WIN)
@@ -123,17 +120,7 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
 #endif
     }
 
-    QPixmap scover;
-    if ( m_cache.contains( cover.cacheKey() ) )
-    {
-        scover = m_cache.value( cover.cacheKey() );
-    }
-    else
-    {
-        scover = cover.scaled( r.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
-        m_cache.insert( cover.cacheKey(), scover );
-    }
-    painter->drawPixmap( r, scover );
+    painter->drawPixmap( r, cover );
 
     painter->setPen( opt.palette.color( QPalette::Text ) );
     QTextOption to;
diff --git a/src/libtomahawk/playlist/albumitemdelegate.h b/src/libtomahawk/playlist/albumitemdelegate.h
index 86b31bdf5..7d29f6c81 100644
--- a/src/libtomahawk/playlist/albumitemdelegate.h
+++ b/src/libtomahawk/playlist/albumitemdelegate.h
@@ -49,12 +49,10 @@ private:
     QAbstractItemView* m_view;
     AlbumProxyModel* m_model;
 
-    mutable QHash< qint64, QPixmap > m_cache;
     mutable QHash< QPersistentModelIndex, QRect > m_artistNameRects;
     QPersistentModelIndex m_hoveringOver;
 
     QPixmap m_shadowPixmap;
-    QPixmap m_defaultCover;
 };
 
 #endif // ALBUMITEMDELEGATE_H
diff --git a/src/libtomahawk/playlist/albummodel.cpp b/src/libtomahawk/playlist/albummodel.cpp
index 9c552e070..08cc3f653 100644
--- a/src/libtomahawk/playlist/albummodel.cpp
+++ b/src/libtomahawk/playlist/albummodel.cpp
@@ -304,7 +304,18 @@ AlbumModel::addAlbums( const QList<Tomahawk::album_ptr>& albums )
     if ( m_overwriteOnAdd )
         clear();
 
-    if ( !albums.count() )
+    QList<Tomahawk::album_ptr> trimmedAlbums;
+    foreach ( const album_ptr& album, albums )
+    {
+        if ( !album.isNull() && album->name().length() )
+        {
+            if ( findItem( album ) || trimmedAlbums.contains( album ) )
+                continue;
+            trimmedAlbums << album;
+        }
+    }
+
+    if ( !trimmedAlbums.count() )
     {
         emit itemCountChanged( rowCount( QModelIndex() ) );
         return;
@@ -313,12 +324,12 @@ AlbumModel::addAlbums( const QList<Tomahawk::album_ptr>& albums )
     int c = rowCount( QModelIndex() );
     QPair< int, int > crows;
     crows.first = c;
-    crows.second = c + albums.count() - 1;
+    crows.second = c + trimmedAlbums.count() - 1;
 
     emit beginInsertRows( QModelIndex(), crows.first, crows.second );
 
     AlbumItem* albumitem;
-    foreach( const album_ptr& album, albums )
+    foreach( const album_ptr& album, trimmedAlbums )
     {
         albumitem = new AlbumItem( album, m_rootItem );
         albumitem->index = createIndex( m_rootItem->children.count() - 1, 0, albumitem );
@@ -339,7 +350,18 @@ AlbumModel::addArtists( const QList<Tomahawk::artist_ptr>& artists )
     if ( m_overwriteOnAdd )
         clear();
 
-    if ( !artists.count() )
+    QList<Tomahawk::artist_ptr> trimmedArtists;
+    foreach ( const artist_ptr& artist, artists )
+    {
+        if ( !artist.isNull() && artist->name().length() )
+        {
+            if ( findItem( artist ) || trimmedArtists.contains( artist ) )
+                continue;
+            trimmedArtists << artist;
+        }
+    }
+
+    if ( !trimmedArtists.count() )
     {
         emit itemCountChanged( rowCount( QModelIndex() ) );
         return;
@@ -348,12 +370,12 @@ AlbumModel::addArtists( const QList<Tomahawk::artist_ptr>& artists )
     int c = rowCount( QModelIndex() );
     QPair< int, int > crows;
     crows.first = c;
-    crows.second = c + artists.count() - 1;
+    crows.second = c + trimmedArtists.count() - 1;
 
     emit beginInsertRows( QModelIndex(), crows.first, crows.second );
 
     AlbumItem* albumitem;
-    foreach( const artist_ptr& artist, artists )
+    foreach ( const artist_ptr& artist, trimmedArtists )
     {
         albumitem = new AlbumItem( artist, m_rootItem );
         albumitem->index = createIndex( m_rootItem->children.count() - 1, 0, albumitem );
@@ -396,3 +418,35 @@ AlbumModel::onDataChanged()
     AlbumItem* p = (AlbumItem*)sender();
     emit dataChanged( p->index, p->index.sibling( p->index.row(), columnCount( QModelIndex() ) - 1 ) );
 }
+
+
+AlbumItem*
+AlbumModel::findItem( const artist_ptr& artist ) const
+{
+    for ( int i = 0; i < rowCount( QModelIndex() ); i++ )
+    {
+        AlbumItem* item = itemFromIndex( index( i, 0, QModelIndex() ) );
+        if ( !item->artist().isNull() && item->artist() == artist )
+        {
+            return item;
+        }
+    }
+    
+    return 0;
+}
+
+
+AlbumItem*
+AlbumModel::findItem( const album_ptr& album ) const
+{
+    for ( int i = 0; i < rowCount( QModelIndex() ); i++ )
+    {
+        AlbumItem* item = itemFromIndex( index( i, 0, QModelIndex() ) );
+        if ( !item->album().isNull() && item->album() == album )
+        {
+            return item;
+        }
+    }
+    
+    return 0;
+}
diff --git a/src/libtomahawk/playlist/albummodel.h b/src/libtomahawk/playlist/albummodel.h
index 01d8f0569..e163db437 100644
--- a/src/libtomahawk/playlist/albummodel.h
+++ b/src/libtomahawk/playlist/albummodel.h
@@ -71,6 +71,9 @@ public:
     virtual void setTitle( const QString& title ) { m_title = title; }
     virtual void setDescription( const QString& description ) { m_description = description; }
 
+    AlbumItem* findItem( const Tomahawk::artist_ptr& artist ) const;
+    AlbumItem* findItem( const Tomahawk::album_ptr& album ) const;
+
     AlbumItem* itemFromIndex( const QModelIndex& index ) const
     {
         if ( index.isValid() )
diff --git a/src/libtomahawk/playlist/albumview.cpp b/src/libtomahawk/playlist/albumview.cpp
index 6e4ba2ee9..a0a73dcb2 100644
--- a/src/libtomahawk/playlist/albumview.cpp
+++ b/src/libtomahawk/playlist/albumview.cpp
@@ -25,6 +25,7 @@
 #include <qmath.h>
 
 #include "audio/audioengine.h"
+#include "context/ContextWidget.h"
 #include "tomahawksettings.h"
 #include "artist.h"
 #include "albumitem.h"
@@ -113,6 +114,20 @@ AlbumView::setAlbumModel( AlbumModel* model )
 }
 
 
+void
+AlbumView::currentChanged( const QModelIndex& current, const QModelIndex& previous )
+{
+    QListView::currentChanged( current, previous );
+
+    AlbumItem* item = m_model->itemFromIndex( m_proxyModel->mapToSource( current ) );
+    if ( item )
+    {
+        if ( !item->album().isNull() )
+            ViewManager::instance()->context()->setAlbum( item->album() );
+    }
+}
+
+
 void
 AlbumView::onItemActivated( const QModelIndex& index )
 {
diff --git a/src/libtomahawk/playlist/albumview.h b/src/libtomahawk/playlist/albumview.h
index 61cc14d43..80f38981b 100644
--- a/src/libtomahawk/playlist/albumview.h
+++ b/src/libtomahawk/playlist/albumview.h
@@ -71,6 +71,9 @@ protected:
     void paintEvent( QPaintEvent* event );
     void resizeEvent( QResizeEvent* event );
 
+protected slots:
+    virtual void currentChanged( const QModelIndex& current, const QModelIndex& previous );
+
 private slots:
     void onItemCountChanged( unsigned int items );
 
diff --git a/src/libtomahawk/playlist/artistview.cpp b/src/libtomahawk/playlist/artistview.cpp
index 808b6c8c5..af00b8715 100644
--- a/src/libtomahawk/playlist/artistview.cpp
+++ b/src/libtomahawk/playlist/artistview.cpp
@@ -80,6 +80,11 @@ ArtistView::ArtistView( QWidget* parent )
     setFont( f );
     #endif
 
+    m_timer.setInterval( SCROLL_TIMEOUT );
+    connect( verticalScrollBar(), SIGNAL( rangeChanged( int, int ) ), SLOT( onViewChanged() ) );
+    connect( verticalScrollBar(), SIGNAL( valueChanged( int ) ), SLOT( onViewChanged() ) );
+    connect( &m_timer, SIGNAL( timeout() ), SLOT( onScrollTimeout() ) );
+
     connect( this, SIGNAL( doubleClicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) );
     connect( this, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( onCustomContextMenu( const QPoint& ) ) );
     connect( m_contextMenu, SIGNAL( triggered( int ) ), SLOT( onMenuTriggered( int ) ) );
@@ -128,7 +133,8 @@ ArtistView::setTreeModel( TreeModel* model )
     connect( m_proxyModel, SIGNAL( filteringFinished() ), m_loadingSpinner, SLOT( fadeOut() ) );
 
     connect( m_model, SIGNAL( itemCountChanged( unsigned int ) ), SLOT( onItemCountChanged( unsigned int ) ) );
-    connect( m_proxyModel, SIGNAL( filterChanged( QString ) ), SLOT( onFilterChanged( QString ) ) );
+    connect( m_proxyModel, SIGNAL( filteringFinished() ), SLOT( onFilterChangeFinished() ) );
+    connect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onViewChanged() ) );
 
     guid(); // this will set the guid on the header
 
@@ -145,6 +151,44 @@ ArtistView::setTreeModel( TreeModel* model )
 }
 
 
+void
+ArtistView::onViewChanged()
+{
+    if ( m_timer.isActive() )
+        m_timer.stop();
+
+    m_timer.start();
+}
+
+
+void
+ArtistView::onScrollTimeout()
+{
+    if ( m_timer.isActive() )
+        m_timer.stop();
+
+    QModelIndex left = indexAt( viewport()->rect().topLeft() );
+    while ( left.isValid() && left.parent().isValid() )
+        left = left.parent();
+
+    QModelIndex right = indexAt( viewport()->rect().bottomLeft() );
+    while ( right.isValid() && right.parent().isValid() )
+        right = right.parent();
+
+    int max = m_proxyModel->playlistInterface()->trackCount();
+    if ( right.isValid() )
+        max = right.row() + 1;
+
+    if ( !max )
+        return;
+
+    for ( int i = left.row(); i < max; i++ )
+    {
+        m_model->getCover( m_proxyModel->mapToSource( m_proxyModel->index( i, 0 ) ) );
+    }
+}
+
+
 void
 ArtistView::currentChanged( const QModelIndex& current, const QModelIndex& previous )
 {
@@ -236,7 +280,7 @@ ArtistView::onItemCountChanged( unsigned int items )
 
 
 void
-ArtistView::onFilterChanged( const QString& )
+ArtistView::onFilterChangeFinished()
 {
     if ( selectedIndexes().count() )
         scrollTo( selectedIndexes().at( 0 ), QAbstractItemView::PositionAtCenter );
diff --git a/src/libtomahawk/playlist/artistview.h b/src/libtomahawk/playlist/artistview.h
index 8167f9b73..30245534f 100644
--- a/src/libtomahawk/playlist/artistview.h
+++ b/src/libtomahawk/playlist/artistview.h
@@ -93,8 +93,10 @@ protected slots:
 
 private slots:
     void onItemCountChanged( unsigned int items );
-    void onFilterChanged( const QString& filter );
+    void onFilterChangeFinished();
     void onFilteringStarted();
+    void onViewChanged();
+    void onScrollTimeout();
 
     void onCustomContextMenu( const QPoint& pos );
     void onMenuTriggered( int action );
@@ -113,6 +115,7 @@ private:
     Tomahawk::ContextMenu* m_contextMenu;
 
     bool m_showModes;
+    QTimer m_timer;
     mutable QString m_guid;
 };
 
diff --git a/src/libtomahawk/playlist/customplaylistview.cpp b/src/libtomahawk/playlist/customplaylistview.cpp
index 1cb9517cd..b3b2c2ba5 100644
--- a/src/libtomahawk/playlist/customplaylistview.cpp
+++ b/src/libtomahawk/playlist/customplaylistview.cpp
@@ -42,7 +42,7 @@ CustomPlaylistView::CustomPlaylistView( CustomPlaylistView::PlaylistType type, c
 
     if ( m_type == SourceLovedTracks )
         connect( m_source.data(), SIGNAL( socialAttributesChanged( QString ) ), this, SLOT( socialAttributesChanged( QString ) ) );
-    else if ( m_type == AllLovedTracks )
+    else if ( m_type == TopLovedTracks )
     {
         connect( SourceList::instance()->getLocal().data(), SIGNAL( socialAttributesChanged( QString ) ), this, SLOT( socialAttributesChanged( QString ) ) );
         foreach ( const source_ptr& s, SourceList::instance()->sources( true ) )
@@ -86,12 +86,12 @@ CustomPlaylistView::generateTracks()
                            "GROUP BY track.id "
                            "ORDER BY counter DESC, social_attributes.timestamp DESC " ).arg( m_source->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_source->id() ) );
             break;
-        case AllLovedTracks:
+        case TopLovedTracks:
             sql = QString( "SELECT track.name, artist.name, source, COUNT(*) as counter "
                            "FROM social_attributes, track, artist "
-                           "WHERE social_attributes.id = track.id AND artist.id = track.artist AND social_attributes.k = 'Love' AND social_attributes.v = 'true'"
+                           "WHERE social_attributes.id = track.id AND artist.id = track.artist AND social_attributes.k = 'Love' AND social_attributes.v = 'true' "
                            "GROUP BY track.id "
-                           "ORDER BY counter DESC, social_attributes.timestamp DESC " );
+                           "ORDER BY counter DESC, social_attributes.timestamp DESC LIMIT 0, 50" );
             break;
     }
 
@@ -104,7 +104,11 @@ CustomPlaylistView::generateTracks()
 void
 CustomPlaylistView::tracksGenerated( QList< query_ptr > tracks )
 {
-    QList< query_ptr > newTracks = TomahawkUtils::mergePlaylistChanges( m_model->queries(), tracks );
+    bool changed = false;
+    QList< query_ptr > newTracks = TomahawkUtils::mergePlaylistChanges( m_model->queries(), tracks, changed);
+
+    if ( !changed )
+        return;
 
     m_model->clear();
     m_model->append( newTracks );
diff --git a/src/libtomahawk/playlist/customplaylistview.h b/src/libtomahawk/playlist/customplaylistview.h
index f03ede49b..692980e40 100644
--- a/src/libtomahawk/playlist/customplaylistview.h
+++ b/src/libtomahawk/playlist/customplaylistview.h
@@ -33,7 +33,7 @@ class DLLEXPORT CustomPlaylistView : public PlaylistView
 public:
     enum PlaylistType {
         SourceLovedTracks,
-        AllLovedTracks
+        TopLovedTracks
     };
 
     explicit CustomPlaylistView( PlaylistType type, const source_ptr& s, QWidget* parent = 0 );
diff --git a/src/libtomahawk/playlist/dynamic/DynamicModel.cpp b/src/libtomahawk/playlist/dynamic/DynamicModel.cpp
index fdae6f5ca..6584a6149 100644
--- a/src/libtomahawk/playlist/dynamic/DynamicModel.cpp
+++ b/src/libtomahawk/playlist/dynamic/DynamicModel.cpp
@@ -54,19 +54,22 @@ DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist, bool load
 {
     Q_UNUSED( loadEntries );
 
-    if( !m_playlist.isNull() ) {
+    if ( !m_playlist.isNull() )
+    {
         disconnect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) );
     }
+    const int oldCount = rowCount( QModelIndex() );
+
     m_playlist = playlist;
 
     m_deduper.clear();
-    if( m_playlist->mode() == OnDemand )
+    if ( m_playlist->mode() == OnDemand )
         setFilterUnresolvable( true );
 
     connect( m_playlist->generator().data(), SIGNAL( nextTrackGenerated( Tomahawk::query_ptr ) ), this, SLOT( newTrackGenerated( Tomahawk::query_ptr ) ) );
     PlaylistModel::loadPlaylist( m_playlist, m_playlist->mode() == Static );
 
-    if( m_playlist->mode() == OnDemand )
+    if ( m_playlist->mode() == OnDemand && oldCount != rowCount( QModelIndex() ) )
         emit trackCountChanged( rowCount( QModelIndex() ) );
 }
 
@@ -74,7 +77,7 @@ DynamicModel::loadPlaylist( const Tomahawk::dynplaylist_ptr& playlist, bool load
 QString
 DynamicModel::description() const
 {
-    if( !m_playlist.isNull() && !m_playlist->generator().isNull() )
+    if ( !m_playlist.isNull() && !m_playlist->generator().isNull() )
         return m_playlist->generator()->sentenceSummary();
     else
         return QString();
@@ -95,7 +98,8 @@ DynamicModel::startOnDemand()
 void
 DynamicModel::newTrackGenerated( const Tomahawk::query_ptr& query )
 {
-    if( m_onDemandRunning ) {
+    if ( m_onDemandRunning )
+    {
         bool isDuplicate = false;
         for ( int i = 0; i < m_deduper.size(); i++ )
         {
@@ -125,7 +129,7 @@ void
 DynamicModel::stopOnDemand( bool stopPlaying )
 {
     m_onDemandRunning = false;
-    if( stopPlaying )
+    if ( stopPlaying )
         AudioEngine::instance()->stop();
 
     disconnect( AudioEngine::instance(), SIGNAL( loading( Tomahawk::result_ptr ) ), this, SLOT( newTrackLoading() ) );
@@ -135,7 +139,7 @@ DynamicModel::stopOnDemand( bool stopPlaying )
 void
 DynamicModel::changeStation()
 {
-    if( m_onDemandRunning )
+    if ( m_onDemandRunning )
         m_changeOnNext = true;
     else // if we're not running, just start
         m_playlist->generator()->startOnDemand();
@@ -171,7 +175,7 @@ DynamicModel::trackResolveFinished( bool success )
     {
         qDebug() << "Got successful resolved track:" << q->track() << q->artist() << m_lastResolvedRow << m_currentAttempts;
 
-        if( m_currentAttempts > 0 ) {
+        if ( m_currentAttempts > 0 ) {
             qDebug() << "EMITTING AN ASK FOR COLLAPSE:" << m_lastResolvedRow << m_currentAttempts;
             emit collapseFromTo( m_lastResolvedRow, m_currentAttempts );
         }
@@ -188,11 +192,14 @@ void
 DynamicModel::newTrackLoading()
 {
     qDebug() << "Got NEW TRACK LOADING signal";
-    if( m_changeOnNext ) { // reset instead of getting the next one
+    if ( m_changeOnNext )
+    { // reset instead of getting the next one
         m_lastResolvedRow = rowCount( QModelIndex() );
         m_searchingForNext = true;
         m_playlist->generator()->startOnDemand();
-    } else if( m_onDemandRunning && m_currentAttempts == 0 && !m_searchingForNext ) { // if we're in dynamic mode and we're also currently idle
+    }
+    else if ( m_onDemandRunning && m_currentAttempts == 0 && !m_searchingForNext )
+    { // if we're in dynamic mode and we're also currently idle
         m_lastResolvedRow = rowCount( QModelIndex() );
         m_searchingForNext = true;
         qDebug() << "IDLE fetching new track!";
@@ -204,13 +211,17 @@ DynamicModel::newTrackLoading()
 void
 DynamicModel::tracksGenerated( const QList< query_ptr > entries, int limitResolvedTo )
 {
-    if( m_filterUnresolvable && m_playlist->mode() == OnDemand ) { // wait till we get them resolved (for previewing stations)
+    if ( m_filterUnresolvable && m_playlist->mode() == OnDemand )
+    { // wait till we get them resolved (for previewing stations)
         m_limitResolvedTo = limitResolvedTo;
         filterUnresolved( entries );
-    } else {
+    }
+    else
+    {
         addToPlaylist( entries, m_playlist->mode() == OnDemand ); // if ondemand, we're previewing, so clear old
 
-        if( m_playlist->mode() == OnDemand ) {
+        if ( m_playlist->mode() == OnDemand )
+        {
             m_lastResolvedRow = rowCount( QModelIndex() );
         }
     }
@@ -224,9 +235,9 @@ DynamicModel::filterUnresolved( const QList< query_ptr >& entries )
 {
     m_toResolveList = entries;
 
-    foreach( const query_ptr& q, entries ) {
+    foreach ( const query_ptr& q, entries )
         connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( filteringTrackResolved( bool ) ) );
-    }
+
     Pipeline::instance()->resolve( entries, true );
 }
 
@@ -240,7 +251,8 @@ DynamicModel::filteringTrackResolved( bool successful )
 
     // if meantime the user began the station, abort
     qDebug() << "Got filtering resolved finished for track, was it successful?:" << q->track() << q->artist() << successful << q->playable();
-    if( m_onDemandRunning ) {
+    if ( m_onDemandRunning )
+    {
         m_toResolveList.clear();
         m_resolvedList.clear();
 
@@ -248,8 +260,10 @@ DynamicModel::filteringTrackResolved( bool successful )
     }
 
     query_ptr realptr;
-    foreach( const query_ptr& qptr, m_toResolveList ) {
-        if( qptr.data() == q ) {
+    foreach ( const query_ptr& qptr, m_toResolveList )
+    {
+        if ( qptr.data() == q )
+        {
             realptr = qptr;
             break;
         }
@@ -259,25 +273,30 @@ DynamicModel::filteringTrackResolved( bool successful )
 
     m_toResolveList.removeAll( realptr );
 
-    if( realptr->playable() ) {
+    if ( realptr->playable() )
+    {
         m_resolvedList << realptr;
 
         // append and update internal lastResolvedRow
         addToPlaylist( QList< query_ptr >() << realptr, false );
-        if( m_playlist->mode() == OnDemand ) {
+        if ( m_playlist->mode() == OnDemand )
+        {
             m_lastResolvedRow = rowCount( QModelIndex() );
         }
 
-        if( m_toResolveList.isEmpty() || m_resolvedList.size() == m_limitResolvedTo ) { // done
+        if ( m_toResolveList.isEmpty() || m_resolvedList.size() == m_limitResolvedTo )
+        { // done
             m_toResolveList.clear();
             m_resolvedList.clear();
 
         }
-    } else {
+    }
+    else
+    {
         qDebug() << "Got unsuccessful resolve request for this track" << realptr->track() << realptr->artist();
     }
 
-    if( m_toResolveList.isEmpty() && rowCount( QModelIndex() ) == 0 ) // we failed
+    if ( m_toResolveList.isEmpty() && rowCount( QModelIndex() ) == 0 ) // we failed
         emit trackGenerationFailure( tr( "Could not find a playable track.\n\nPlease change the filters or try again." ) );
 }
 
@@ -285,16 +304,20 @@ DynamicModel::filteringTrackResolved( bool successful )
 void
 DynamicModel::addToPlaylist( const QList< query_ptr >& entries, bool clearFirst )
 {
-    if( clearFirst )
+    if ( clearFirst )
         clear();
 
     foreach ( const query_ptr& q, entries )
         m_deduper.append( QPair< QString, QString >( q->track(), q->artist() ) );
 
-    if( m_playlist->author()->isLocal() && m_playlist->mode() == Static ) {
+    if ( m_playlist->author()->isLocal() && m_playlist->mode() == Static )
+    {
         m_playlist->addEntries( entries, m_playlist->currentrevision() );
-    } else { // read-only, so add tracks only in the GUI, not to the playlist itself
-        foreach( const query_ptr& query, entries ) {
+    }
+    else
+    { // read-only, so add tracks only in the GUI, not to the playlist itself
+        foreach ( const query_ptr& query, entries )
+        {
             append( query );
         }
     }
@@ -310,12 +333,15 @@ DynamicModel::remove(const QModelIndex& idx, bool moreToCome)
         return;
 
     qDebug() << Q_FUNC_INFO << "DYNAMIC MODEL REMOVIN!" << moreToCome << ( idx == index( rowCount( QModelIndex() ) - 1, 0, QModelIndex() ) );
-    if( m_playlist->mode() == OnDemand ) {
-        if( !moreToCome && idx == index( rowCount( QModelIndex() ) - 1, 0, QModelIndex() ) ) { // if the user is manually removing the last one, re-add as we're a station
+    if ( m_playlist->mode() == OnDemand )
+    {
+        if ( !moreToCome && idx == index( rowCount( QModelIndex() ) - 1, 0, QModelIndex() ) )
+        { // if the user is manually removing the last one, re-add as we're a station
             newTrackLoading();
         }
         TrackModel::remove( idx );
-    } else
+    }
+    else
         PlaylistModel::remove( idx, moreToCome );
     // don't call onPlaylistChanged.
 
diff --git a/src/libtomahawk/playlist/dynamic/DynamicView.cpp b/src/libtomahawk/playlist/dynamic/DynamicView.cpp
index eda9a224b..014471187 100644
--- a/src/libtomahawk/playlist/dynamic/DynamicView.cpp
+++ b/src/libtomahawk/playlist/dynamic/DynamicView.cpp
@@ -1,6 +1,6 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
- *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
+ *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.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/libtomahawk/playlist/dynamic/DynamicView.h b/src/libtomahawk/playlist/dynamic/DynamicView.h
index 39826fe97..d39e60118 100644
--- a/src/libtomahawk/playlist/dynamic/DynamicView.h
+++ b/src/libtomahawk/playlist/dynamic/DynamicView.h
@@ -1,6 +1,6 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
- * 
- *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
+ *
+ *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.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
@@ -32,33 +32,33 @@ namespace Tomahawk
 
 class DynamicModel;
 
-    
+
 class DynamicView : public PlaylistView
 {
     Q_OBJECT
 public:
     explicit DynamicView( QWidget* parent = 0 );
     virtual ~DynamicView();
-    
+
     virtual void setDynamicModel( DynamicModel* model );
-    
+
     void setOnDemand( bool onDemand );
     void setReadOnly( bool readOnly );
-    
+
     void setDynamicWorking( bool working );
-    
+
     virtual void paintEvent( QPaintEvent* event );
-    
+
 public slots:
     void showMessageTimeout( const QString& title, const QString& body );
     void showMessage( const QString& message );
-    
+
     // collapse and animate the transition
     // there MUST be a row *after* startRow + num. that is, you can't collapse
     // entries unless there is at least one entry after the last collapsed row
     // optionally you can specify how  many rows are past the block of collapsed rows
     void collapseEntries( int startRow, int num, int numToKeep = 1 );
-    
+
 private slots:
     void onTrackCountChanged( unsigned int );
     void checkForOverflow();
@@ -70,7 +70,7 @@ private:
     DynamicModel* m_model;
     QString m_title;
     QString m_body;
-    
+
     bool m_onDemand;
     bool m_readOnly;
     bool m_checkOnCollapse;
@@ -88,7 +88,7 @@ private:
     QTimeLine m_fadeOutAnim;
     QTimeLine m_slideAnim;
 };
-    
+
 };
 
 
diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp
index cc3a7b28b..43073bf68 100644
--- a/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp
+++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp
@@ -181,6 +181,7 @@ Tomahawk::EchonestControl::updateWidgets()
         input->hide();
         m_match = QWeakPointer< QWidget >( match );
         m_input = QWeakPointer< QWidget >( input );
+        m_data.first = m_currentType;
     } else if( selectedType() == "Artist Description" ) {
         m_currentType = Echonest::DynamicPlaylist::Description;
 
@@ -199,6 +200,7 @@ Tomahawk::EchonestControl::updateWidgets()
         input->hide();
         m_match = QWeakPointer< QWidget >( match );
         m_input = QWeakPointer< QWidget >( input );
+        m_data.first = m_currentType;
     } else if( selectedType() == "User Radio" ) {
         m_currentType = Echonest::DynamicPlaylist::SourceCatalog;
 
@@ -246,6 +248,7 @@ Tomahawk::EchonestControl::updateWidgets()
         input->hide();
         m_match = QWeakPointer< QWidget >( match );
         m_input = QWeakPointer< QWidget >( input );
+        m_data.first = m_currentType;
     } else if( selectedType() == "Variety" ) {
         m_currentType = Echonest::DynamicPlaylist::Variety;
 
@@ -266,6 +269,7 @@ Tomahawk::EchonestControl::updateWidgets()
         input->hide();
         m_match = QWeakPointer< QWidget >( match );
         m_input = QWeakPointer< QWidget >( input );
+        m_data.first = m_currentType;
     } else if( selectedType() == "Adventurousness" ) {
         m_currentType = Echonest::DynamicPlaylist::Adventurousness;
 
@@ -287,6 +291,7 @@ Tomahawk::EchonestControl::updateWidgets()
         input->hide();
         m_match = QWeakPointer< QWidget >( match );
         m_input = QWeakPointer< QWidget >( input );
+        m_data.first = m_currentType;
     } else if( selectedType() == "Tempo" ) {
         m_currentType = Echonest::DynamicPlaylist::MinTempo;
 
diff --git a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp
index b4a07acba..22861c420 100644
--- a/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp
+++ b/src/libtomahawk/playlist/dynamic/widgets/DynamicWidget.cpp
@@ -118,32 +118,34 @@ DynamicWidget::loadDynamicPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
 {
     // special case: if we have launched multiple setRevision calls, and the number of controls is different, it means that we're getting an intermediate setRevision
     //  called after the user has already created more revisions. ignore in that case.
-    if( m_playlist.data() == playlist.data() && m_seqRevLaunched > 0
+    if ( m_playlist.data() == playlist.data() && m_seqRevLaunched > 0
         && m_controls->controls().size() != playlist->generator()->controls().size() // different number of controls
-        && qAbs( m_playlist->generator()->controls().size() - playlist->generator()->controls().size() ) < m_seqRevLaunched ) { // difference in controls has to be less than how many revisions we launched
+        && qAbs( m_playlist->generator()->controls().size() - playlist->generator()->controls().size() ) < m_seqRevLaunched )
+    { // difference in controls has to be less than how many revisions we launched
         return;
     }
     m_seqRevLaunched = 0;
 
     // if we're being told to load the same dynamic playlist over again, only do it if the controls have a different number
-    if( !m_playlist.isNull() && ( m_playlist.data() == playlist.data() ) // same playlist pointer
-        && m_playlist->generator()->controls().size() == playlist->generator()->controls().size() ) {
+    if ( !m_playlist.isNull() && ( m_playlist.data() == playlist.data() ) // same playlist pointer
+        && m_playlist->generator()->controls().size() == playlist->generator()->controls().size() )
+    {
         // we can skip our work. just let the dynamiccontrollist show the difference
         m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
 
         m_playlist = playlist;
 
-        if( !m_runningOnDemand ) {
+        if ( !m_runningOnDemand )
             m_model->loadPlaylist( m_playlist );
-        } else if( !m_controlsChanged ) { // if the controls changed, we already dealt with that and don't want to change station yet
+        else if ( !m_controlsChanged ) // if the controls changed, we already dealt with that and don't want to change station yet
             m_model->changeStation();
-        }
         m_controlsChanged = false;
 
         return;
     }
 
-    if( !m_playlist.isNull() ) {
+    if ( !m_playlist.isNull() )
+    {
         disconnect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
         disconnect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ), this, SLOT(onRevisionLoaded( Tomahawk::DynamicPlaylistRevision) ) );
         disconnect( m_playlist->generator().data(), SIGNAL( error( QString, QString ) ), this, SLOT( generatorError( QString, QString ) ) );
@@ -160,23 +162,22 @@ DynamicWidget::loadDynamicPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
     m_setup->setPlaylist( m_playlist );
 
 
-    if( !m_playlist->author()->isLocal() ) { // hide controls, as we show the description in the summary
+    if ( !m_playlist->author()->isLocal() )  // hide controls, as we show the description in the summary
             m_layout->removeWidget( m_controls );
-    } else if( m_layout->indexOf( m_controls ) == -1 ) {
+    else if ( m_layout->indexOf( m_controls ) == -1 )
         m_layout->insertWidget( 0, m_controls );
-    }
-
-    if( m_playlist->mode() == OnDemand && !m_playlist->generator()->controls().isEmpty() )
-        showPreview();
-
-    if( !m_playlist.isNull() )
-        m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
 
     connect( m_playlist->generator().data(), SIGNAL( generated( QList<Tomahawk::query_ptr> ) ), this, SLOT( tracksGenerated( QList<Tomahawk::query_ptr> ) ) );
     connect( m_playlist.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( onRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ) );
     connect( m_playlist->generator().data(), SIGNAL( error( QString, QString ) ), this, SLOT( generatorError( QString, QString ) ) );
     connect( m_playlist.data(), SIGNAL( deleted( Tomahawk::dynplaylist_ptr ) ), this, SLOT( onDeleted() ) );
     connect( m_playlist.data(), SIGNAL( changed() ), this, SLOT( onChanged() ) );
+
+    if ( m_playlist->mode() == OnDemand && !m_playlist->generator()->controls().isEmpty() )
+        showPreview();
+
+    if ( !m_playlist.isNull() )
+        m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
 }
 
 
@@ -226,12 +227,15 @@ DynamicWidget::resizeEvent(QResizeEvent* )
 void
 DynamicWidget::layoutFloatingWidgets()
 {
-    if( !m_runningOnDemand ) {
+    if ( !m_runningOnDemand )
+    {
         int x = ( width() / 2 ) - ( m_setup->size().width() / 2 );
         int y = height() - m_setup->size().height() - 40; // padding
 
         m_setup->move( x, y );
-    } else if( m_runningOnDemand && m_steering ) {
+    }
+    else if( m_runningOnDemand && m_steering )
+    {
         int x = ( width() / 2 ) - ( m_steering->size().width() / 2 );
         int y = height() - m_steering->size().height() - 40; // padding
 
@@ -243,13 +247,15 @@ DynamicWidget::layoutFloatingWidgets()
 void
 DynamicWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl )
 {
-    if( pl == m_view->proxyModel()->playlistInterface() ) { // same playlist
+    if ( pl == m_view->proxyModel()->playlistInterface() ) // same playlist
         m_activePlaylist = true;
-    } else {
+    else
+    {
         m_activePlaylist = false;
 
         // user started playing something somewhere else, so give it a rest
-        if( m_runningOnDemand ) {
+        if ( m_runningOnDemand )
+        {
             stopStation( false );
         }
     }
@@ -259,9 +265,8 @@ DynamicWidget::playlistChanged( Tomahawk::playlistinterface_ptr pl )
 void
 DynamicWidget::showEvent(QShowEvent* )
 {
-    if( !m_playlist.isNull() && !m_runningOnDemand ) {
+    if ( !m_playlist.isNull() && !m_runningOnDemand )
         m_setup->fadeIn();
-    }
 }
 
 
@@ -289,8 +294,9 @@ DynamicWidget::stationFailed( const QString& msg )
 void
 DynamicWidget::trackStarted()
 {
-    if( m_activePlaylist && !m_playlist.isNull() &&
-        m_playlist->mode() == OnDemand && !m_runningOnDemand ) {
+    if ( m_activePlaylist && !m_playlist.isNull() &&
+        m_playlist->mode() == OnDemand && !m_runningOnDemand )
+    {
 
         startStation();
     }
@@ -300,7 +306,7 @@ DynamicWidget::trackStarted()
 void
 DynamicWidget::tracksAdded()
 {
-    if( m_playlist->mode() == OnDemand && m_runningOnDemand && m_setup->isVisible() )
+    if ( m_playlist->mode() == OnDemand && m_runningOnDemand && m_setup->isVisible() )
         m_setup->fadeOut();
 }
 
@@ -325,7 +331,8 @@ DynamicWidget::startStation()
 
     m_setup->fadeOut();
     // show the steering controls
-    if( m_playlist->generator()->onDemandSteerable() ) {
+    if ( m_playlist->generator()->onDemandSteerable() )
+    {
         // position it horizontally centered, above the botton.
         m_steering = m_playlist->generator()->steeringWidget();
         Q_ASSERT( m_steering );
@@ -361,7 +368,7 @@ DynamicWidget::tracksGenerated( const QList< query_ptr >& queries )
     {
         m_resolveOnNextLoad = true;
     }
-    else if( m_playlist->mode() == OnDemand )
+    else if ( m_playlist->mode() == OnDemand )
     {
         limit = 5;
     }
@@ -380,7 +387,7 @@ DynamicWidget::controlsChanged( bool added )
     // when playing a station just ignore it till we're ready and get a controlChanged()
     m_controlsChanged = true;
 
-    if( !m_playlist->author()->isLocal() )
+    if ( !m_playlist->author()->isLocal() )
         return;
     m_playlist->createNewRevision();
     m_seqRevLaunched++;
@@ -396,7 +403,7 @@ void
 DynamicWidget::controlChanged( const Tomahawk::dyncontrol_ptr& control )
 {
     Q_UNUSED( control );
-    if( !m_playlist->author()->isLocal() )
+    if ( !m_playlist->author()->isLocal() )
         return;
     m_playlist->createNewRevision();
     m_seqRevLaunched++;
@@ -443,8 +450,7 @@ DynamicWidget::steeringChanged()
 void
 DynamicWidget::showPreview()
 {
-    if ( m_playlist->mode() == OnDemand &&
-        !m_runningOnDemand )
+    if ( m_playlist->mode() == OnDemand && !m_runningOnDemand )
     {
         // if this is a not running station, preview matching tracks
         m_model->clear();
@@ -456,12 +462,16 @@ DynamicWidget::showPreview()
 void
 DynamicWidget::generatorError( const QString& title, const QString& content )
 {
-    if( m_runningOnDemand ) {
-        stopStation( false );
-    }
     m_view->setDynamicWorking( false );
     m_loading->fadeOut();
-    m_view->showMessageTimeout( title, content );
+
+    if ( m_runningOnDemand )
+    {
+        stopStation( false );
+        m_view->showMessage( tr( "Station ran out of tracks!\n\nTry tweaking the filters for a new set of songs to play." ) );
+    }
+    else
+        m_view->showMessageTimeout( title, content );
 }
 
 
@@ -528,7 +538,7 @@ DynamicWidget::onDeleted()
 void
 DynamicWidget::onChanged()
 {
-    if( !m_playlist.isNull() &&
-        ViewManager::instance()->currentPage() == this )
-        emit nameChanged( m_playlist->title() );
+    if ( !m_playlist.isNull() &&
+         ViewManager::instance()->currentPage() == this )
+         emit nameChanged( m_playlist->title() );
 }
diff --git a/src/libtomahawk/playlist/treeitemdelegate.cpp b/src/libtomahawk/playlist/treeitemdelegate.cpp
index 90fbe4aa7..685a6389e 100644
--- a/src/libtomahawk/playlist/treeitemdelegate.cpp
+++ b/src/libtomahawk/playlist/treeitemdelegate.cpp
@@ -155,32 +155,18 @@ TreeItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
     QPixmap cover;
     if ( !item->album().isNull() )
     {
-        cover.loadFromData( item->album()->cover() );
+        cover = item->album()->cover( r.size(), false );
+        if ( cover.isNull() )
+            cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::ScaledCover, r.size() );
     }
     else if ( !item->artist().isNull() )
     {
-        cover.loadFromData( item->artist()->cover() );
+        cover = item->artist()->cover( r.size(), false );
+        if ( cover.isNull() )
+            cover = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::ScaledCover, r.size() );
     }
 
-    QPixmap scover;
-    if ( cover.isNull() )
-    {
-        if ( !item->artist().isNull() )
-            cover = m_defaultArtistImage;
-        else
-            cover = m_defaultAlbumCover;
-    }
-
-    if ( m_cache.contains( cover.cacheKey() ) )
-    {
-        scover = m_cache.value( cover.cacheKey() );
-    }
-    else
-    {
-        scover = cover.scaled( r.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
-        m_cache.insert( cover.cacheKey(), scover );
-    }
-    painter->drawPixmap( r, scover );
+    painter->drawPixmap( r, cover );
 
     QTextOption to;
     to.setAlignment( Qt::AlignVCenter );
diff --git a/src/libtomahawk/playlist/treeitemdelegate.h b/src/libtomahawk/playlist/treeitemdelegate.h
index 07fff9423..da84fcec9 100644
--- a/src/libtomahawk/playlist/treeitemdelegate.h
+++ b/src/libtomahawk/playlist/treeitemdelegate.h
@@ -43,8 +43,6 @@ private:
     ArtistView* m_view;
     TreeProxyModel* m_model;
 
-    mutable QHash< qint64, QPixmap > m_cache;
-
     QPixmap m_nowPlayingIcon;
     QPixmap m_defaultAlbumCover;
     QPixmap m_defaultArtistImage;
diff --git a/src/libtomahawk/playlist/treemodel.cpp b/src/libtomahawk/playlist/treemodel.cpp
index 24e3073e0..98100d65d 100644
--- a/src/libtomahawk/playlist/treemodel.cpp
+++ b/src/libtomahawk/playlist/treemodel.cpp
@@ -20,6 +20,7 @@
 
 #include <QMimeData>
 
+#include "pipeline.h"
 #include "source.h"
 #include "sourcelist.h"
 #include "audio/audioengine.h"
@@ -87,6 +88,18 @@ TreeModel::collection() const
 }
 
 
+void
+TreeModel::getCover( const QModelIndex& index )
+{
+    TreeModelItem* item = itemFromIndex( index );
+
+    if ( !item->artist().isNull() && !item->artist()->infoLoaded() )
+        item->artist()->cover( QSize( 0, 0 ) );
+    else if ( !item->album().isNull() && !item->album()->infoLoaded() )
+        item->album()->cover( QSize( 0, 0 ) );
+}
+
+
 void
 TreeModel::setCurrentItem( const QModelIndex& index )
 {
@@ -783,6 +796,8 @@ TreeModel::onAlbumsAdded( const QList<Tomahawk::album_ptr>& albums, const QModel
         albumitem = new TreeModelItem( album, parentItem );
         albumitem->index = createIndex( parentItem->children.count() - 1, 0, albumitem );
         connect( albumitem, SIGNAL( dataChanged() ), SLOT( onDataChanged() ) );
+        
+        getCover( albumitem->index );
     }
 
     emit endInsertRows();
@@ -915,10 +930,11 @@ TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QV
 
                 foreach ( const QString& trackName, tracks )
                 {
-                    query_ptr query = Query::get( inputInfo[ "artist" ], trackName, inputInfo[ "album" ], uuid() );
+                    query_ptr query = Query::get( inputInfo[ "artist" ], trackName, inputInfo[ "album" ] );
                     query->setAlbumPos( trackNo++ );
                     ql << query;
                 }
+                Pipeline::instance()->resolve( ql );
 
                 onTracksAdded( ql, idx );
             }
diff --git a/src/libtomahawk/playlist/treemodel.h b/src/libtomahawk/playlist/treemodel.h
index 53578bc8b..fb645cf28 100644
--- a/src/libtomahawk/playlist/treemodel.h
+++ b/src/libtomahawk/playlist/treemodel.h
@@ -98,6 +98,8 @@ public:
     void addAlbums( const Tomahawk::artist_ptr& artist, const QModelIndex& parent, bool autoRefetch = false );
     void addTracks( const Tomahawk::album_ptr& album, const QModelIndex& parent, bool autoRefetch = false );
 
+    void getCover( const QModelIndex& index );
+
     ColumnStyle columnStyle() const { return m_columnStyle; }
     void setColumnStyle( ColumnStyle style );
 
diff --git a/src/libtomahawk/query.cpp b/src/libtomahawk/query.cpp
index 8df9cd6f7..113ad7b8c 100644
--- a/src/libtomahawk/query.cpp
+++ b/src/libtomahawk/query.cpp
@@ -104,7 +104,9 @@ Query::Query( const QString& query, const QID& qid )
 
 Query::~Query()
 {
+    QMutexLocker lock( &m_mutex );
     m_ownRef.clear();
+    m_results.clear();
 }
 
 
@@ -459,7 +461,15 @@ Query::howSimilar( const Tomahawk::result_ptr& r )
 
     if ( isFullTextQuery() )
     {
+        const QString artistTrackname = DatabaseImpl::sortname( fullTextQuery() );
+        const QString rArtistTrackname  = DatabaseImpl::sortname( r->artist()->name() + " " + r->track() );
+
+        int atrdist = levenshtein( artistTrackname, rArtistTrackname );
+        int mlatr = qMax( artistTrackname.length(), rArtistTrackname.length() );
+        float dcatr = (float)( mlatr - atrdist ) / mlatr;
+
         float res = qMax( dcart, dcalb );
+        res = qMax( res, dcatr );
         return qMax( res, dctrk );
     }
     else
@@ -489,7 +499,7 @@ Query::loadSocialActions()
     query_ptr q = m_ownRef.toStrongRef();
 
     DatabaseCommand_LoadSocialActions* cmd = new DatabaseCommand_LoadSocialActions( q );
-    connect( cmd, SIGNAL( finished() ), SLOT( onSocialActionsLoaded() ));
+    connect( cmd, SIGNAL( finished() ), SLOT( onSocialActionsLoaded() ) );
     Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
 }
 
diff --git a/src/libtomahawk/resolver.cpp b/src/libtomahawk/resolver.cpp
index e69de29bb..89f9b8565 100644
--- a/src/libtomahawk/resolver.cpp
+++ b/src/libtomahawk/resolver.cpp
@@ -0,0 +1,19 @@
+/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@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 "resolver.h"
diff --git a/src/libtomahawk/resolvers/scriptresolver.cpp b/src/libtomahawk/resolvers/scriptresolver.cpp
index 5f5fb2193..a70ed5746 100644
--- a/src/libtomahawk/resolvers/scriptresolver.cpp
+++ b/src/libtomahawk/resolvers/scriptresolver.cpp
@@ -1,6 +1,7 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
  *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
+ *   Copyright 2010-2011, Leo Franchi            <lfranchi@kde.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
@@ -55,7 +56,6 @@ ScriptResolver::ScriptResolver( const QString& exe )
 
     // set the name to the binary, if we launch properly we'll get the name the resolver reports
     m_name = QFileInfo( filePath() ).baseName();
-    m_account = new Tomahawk::Accounts::SpotifyResolverAccount();
 }
 
 
@@ -283,9 +283,6 @@ ScriptResolver::handleMsg( const QByteArray& msg )
                 Tomahawk::query_ptr q = Tomahawk::Query::get( m.value( "artist" ).toString() , m.value( "track" ).toString() , QString(), uuid(), false );
                 tracks << q;
             }
-
-            if(m_account)
-                m_account->addPlaylist( qid, title, tracks);
         }
     }
 }
@@ -309,7 +306,7 @@ ScriptResolver::cmdExited( int code, QProcess::ExitStatus status )
         return;
     }
 
-    if ( m_num_restarts < 10 )
+    if ( m_num_restarts < 0 )
     {
         m_num_restarts++;
         tLog() << "*** Restart num" << m_num_restarts;
diff --git a/src/libtomahawk/resolvers/scriptresolver.h b/src/libtomahawk/resolvers/scriptresolver.h
index 037a29376..56fbff8d4 100644
--- a/src/libtomahawk/resolvers/scriptresolver.h
+++ b/src/libtomahawk/resolvers/scriptresolver.h
@@ -1,6 +1,7 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
  *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
+ *   Copyright 2010-2011, Leo Franchi            <lfranchi@kde.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
@@ -90,7 +91,6 @@ private:
 
     QJson::Parser m_parser;
     QJson::Serializer m_serializer;
-    Tomahawk::Accounts::SpotifyResolverAccount *m_account;
 };
 
 #endif // SCRIPTRESOLVER_H
diff --git a/src/libtomahawk/source.cpp b/src/libtomahawk/source.cpp
index 9d96792db..ebf795a08 100644
--- a/src/libtomahawk/source.cpp
+++ b/src/libtomahawk/source.cpp
@@ -350,6 +350,12 @@ Source::trackTimerFired()
 void
 Source::addCommand( const QSharedPointer<DatabaseCommand>& command )
 {
+    if ( QThread::currentThread() != thread() )
+    {
+        QMetaObject::invokeMethod( this, "addCommand", Qt::QueuedConnection, Q_ARG( const QSharedPointer<DatabaseCommand>, command ) );
+        return;
+    }
+
     m_cmds << command;
     if ( !command->singletonCmd() )
         m_lastCmdGuid = command->guid();
@@ -363,7 +369,6 @@ Source::executeCommands()
 {
     if ( QThread::currentThread() != thread() )
     {
-        tDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO;
         QMetaObject::invokeMethod( this, "executeCommands", Qt::QueuedConnection );
         return;
     }
diff --git a/src/libtomahawk/source.h b/src/libtomahawk/source.h
index f3361a720..396094c0e 100644
--- a/src/libtomahawk/source.h
+++ b/src/libtomahawk/source.h
@@ -129,9 +129,9 @@ private slots:
     void trackTimerFired();
 
     void executeCommands();
+    void addCommand( const QSharedPointer<DatabaseCommand>& command );
 
 private:
-    void addCommand( const QSharedPointer<DatabaseCommand>& command );
     void updateTracks();
     void reportSocialAttributesChanged( DatabaseCommand_SocialAction* action );
 
diff --git a/src/libtomahawk/thirdparty/Qocoa/qsearchfield_mac.mm b/src/libtomahawk/thirdparty/Qocoa/qsearchfield_mac.mm
index 120a8c291..efa05f631 100644
--- a/src/libtomahawk/thirdparty/Qocoa/qsearchfield_mac.mm
+++ b/src/libtomahawk/thirdparty/Qocoa/qsearchfield_mac.mm
@@ -22,6 +22,7 @@ THE SOFTWARE.
 */
 
 #include "qsearchfield.h"
+#include "moc_qsearchfield.cpp"
 
 #include "qocoa_mac.h"
 
diff --git a/src/libtomahawk/tomahawksettings.cpp b/src/libtomahawk/tomahawksettings.cpp
index aa9baa5eb..8b93906dd 100644
--- a/src/libtomahawk/tomahawksettings.cpp
+++ b/src/libtomahawk/tomahawksettings.cpp
@@ -46,19 +46,38 @@ TomahawkSettings::TomahawkSettings( QObject* parent )
 {
     s_instance = this;
 
-    if( !contains( "configversion") )
+    #ifdef Q_OS_LINUX
+        QFile file( fileName() );
+        file.setPermissions( file.permissions() & ~(QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup | QFile::ReadOther | QFile::WriteOther | QFile::ExeOther ) );
+    #endif
+
+    if ( !contains( "configversion" ) )
     {
         setValue( "configversion", TOMAHAWK_SETTINGS_VERSION );
         doInitialSetup();
     }
-    else if( value( "configversion" ).toUInt() != TOMAHAWK_SETTINGS_VERSION )
+    else if ( value( "configversion" ).toUInt() != TOMAHAWK_SETTINGS_VERSION )
     {
         qDebug() << "Config version outdated, old:" << value( "configversion" ).toUInt()
                  << "new:" << TOMAHAWK_SETTINGS_VERSION
-                 << "Doing upgrade, if any...";
+                 << "Doing upgrade, if any, and backing up";
 
+//         QString newname = QString( "%1.v%2" ).arg( dbname ).arg( version );
+        if ( format() == IniFormat ||
+             ( format() == NativeFormat
+#ifdef Q_OS_WIN
+               && false
+#endif
+             ) )
+        {
+            qDebug() << "Backing up old ini-style config file";
+            const QString path = fileName();
+            const QString newname = path + QString( ".v%1" ).arg( value( "configversion" ).toString() );
+            QFile::copy( path, newname );
+
+        }
         int current = value( "configversion" ).toUInt();
-        while( current < TOMAHAWK_SETTINGS_VERSION )
+        while ( current < TOMAHAWK_SETTINGS_VERSION )
         {
             doUpgrade( current, current + 1 );
 
@@ -67,7 +86,6 @@ TomahawkSettings::TomahawkSettings( QObject* parent )
         // insert upgrade code here as required
         setValue( "configversion", TOMAHAWK_SETTINGS_VERSION );
     }
-
 }
 
 
@@ -82,6 +100,16 @@ TomahawkSettings::doInitialSetup()
 {
     // by default we add a local network resolver
     addAccount( "sipzeroconf_autocreated" );
+
+    // Add a last.fm account for scrobbling and infosystem
+    const QString accountKey = QString( "lastfmaccount_%1" ).arg( QUuid::createUuid().toString().mid( 1, 8 ) );
+    addAccount( accountKey );
+
+    beginGroup( "accounts/" + accountKey );
+    setValue( "enabled", false );
+    setValue( "autoconnect", true );
+    setValue( "types", QStringList() << "ResolverType" << "StatusPushType" );
+    endGroup();
 }
 
 
@@ -90,22 +118,23 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion )
 {
     Q_UNUSED( newVersion );
 
-    if( oldVersion == 1 )
+    if ( oldVersion == 1 )
     {
         qDebug() << "Migrating config from verson 1 to 2: script resolver config name";
         if( contains( "script/resolvers" ) ) {
             setValue( "script/loadedresolvers", value( "script/resolvers" ) );
             remove( "script/resolvers" );
         }
-    } else if( oldVersion == 2 )
+    }
+    else if ( oldVersion == 2 )
     {
         qDebug() << "Migrating config from version 2 to 3: Converting jabber and twitter accounts to new SIP Factory approach";
         // migrate old accounts to new system. only jabber and twitter, and max one each. create a new plugin for each if needed
         // not pretty as we hardcode a plugin id and assume that we know how the config layout is, but hey, this is migration after all
-        if( contains( "jabber/username" ) && contains( "jabber/password" ) )
+        if ( contains( "jabber/username" ) && contains( "jabber/password" ) )
         {
             QString sipName = "sipjabber";
-            if( value( "jabber/username" ).toString().contains( "@gmail" ) )
+            if ( value( "jabber/username" ).toString().contains( "@gmail" ) )
                 sipName = "sipgoogle";
 
             setValue( QString( "%1_legacy/username" ).arg( sipName ), value( "jabber/username" ) );
@@ -122,7 +151,7 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion )
             remove( "jabber/server" );
             remove( "jabber/port" );
         }
-        if( contains( "twitter/ScreenName" ) && contains( "twitter/OAuthToken" ) )
+        if ( contains( "twitter/ScreenName" ) && contains( "twitter/OAuthToken" ) )
         {
             setValue( "siptwitter_legacy/ScreenName", value( "twitter/ScreenName" ) );
             setValue( "siptwitter_legacy/OAuthToken", value( "twitter/OAuthToken" ) );
@@ -143,7 +172,8 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion )
         }
         // create a zeroconf plugin too
         addSipPlugin( "sipzeroconf_legacy" );
-    } else if ( oldVersion == 3 )
+    }
+    else if ( oldVersion == 3 )
     {
         if ( contains( "script/atticaresolverstates" ) )
         {
@@ -182,11 +212,12 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion )
             tDebug() << "UPGRADING AND DELETING:" << resolverDir.absolutePath();
             TomahawkUtils::removeDirectory( resolverDir.absolutePath() );
         }
-    } else if ( oldVersion == 4 )
+    }
+    else if ( oldVersion == 4 || oldVersion == 5 )
     {
         // 0.3.0 contained a bug which prevent indexing local files. Force a reindex.
         QTimer::singleShot( 0, this, SLOT( updateIndex() ) );
-    } else if ( oldVersion == 5 )
+    } else if ( oldVersion == 6 )
     {
         // Migrate to accounts from sipplugins.
         // collect old connected and enabled sip plugins
@@ -215,10 +246,13 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion )
                 QVariantHash credentials;
                 credentials[ "username" ] = value( sipPlugin + "/username" );
                 credentials[ "password" ] = value( sipPlugin + "/password" );
-                credentials[ "port" ] = value( sipPlugin + "/port" );
-                credentials[ "server" ] = value( sipPlugin + "/server" );
+
+                QVariantHash configuration;
+                configuration[ "port" ] = value( sipPlugin + "/port" );
+                configuration[ "server" ] = value( sipPlugin + "/server" );
 
                 setValue( QString( "accounts/%1/credentials" ).arg( accountKey ), credentials );
+                setValue( QString( "accounts/%1/configuration" ).arg( accountKey ), configuration );
                 setValue( QString( "accounts/%1/accountfriendlyname" ).arg( accountKey ), value( sipPlugin + "/username" ) );
 
             }
@@ -271,6 +305,10 @@ TomahawkSettings::doUpgrade( int oldVersion, int newVersion )
 
         foreach ( const QString& resolver, allResolvers )
         {
+            // We handle last.fm resolvers differently.
+            if ( resolver.contains( "lastfm" ) )
+                continue;
+
             const QString accountKey = QString( "resolveraccount_%1" ).arg( QUuid::createUuid().toString().mid( 1, 8 ) );
             accounts << accountKey;
 
@@ -1011,6 +1049,7 @@ TomahawkSettings::setNowPlayingEnabled( bool enable )
     setValue( "adium/enablenowplaying", enable );
 }
 
+
 TomahawkSettings::PrivateListeningMode
 TomahawkSettings::privateListeningMode() const
 {
diff --git a/src/libtomahawk/tomahawksettings.h b/src/libtomahawk/tomahawksettings.h
index 52788b15c..1a1d67100 100644
--- a/src/libtomahawk/tomahawksettings.h
+++ b/src/libtomahawk/tomahawksettings.h
@@ -27,7 +27,7 @@
 
 #include "dllmacro.h"
 
-#define TOMAHAWK_SETTINGS_VERSION 6
+#define TOMAHAWK_SETTINGS_VERSION 7
 
 /**
  * Convenience wrapper around QSettings for tomahawk-specific config
diff --git a/src/libtomahawk/utils/dropjobnotifier.h b/src/libtomahawk/utils/dropjobnotifier.h
index 6119f0ad8..ff38faf9b 100644
--- a/src/libtomahawk/utils/dropjobnotifier.h
+++ b/src/libtomahawk/utils/dropjobnotifier.h
@@ -27,7 +27,8 @@
 
 #include <QObject>
 #include <QSet>
-#include <QtCore/QStringList>
+#include <QStringList>
+#include <QPixmap>
 
 class QNetworkReply;
 namespace Tomahawk
diff --git a/src/libtomahawk/utils/groovesharkparser.cpp b/src/libtomahawk/utils/groovesharkparser.cpp
index dcc28e620..d8353dfb9 100644
--- a/src/libtomahawk/utils/groovesharkparser.cpp
+++ b/src/libtomahawk/utils/groovesharkparser.cpp
@@ -27,6 +27,7 @@
 #include "dropjob.h"
 #include "jobview/JobStatusView.h"
 #include "jobview/JobStatusModel.h"
+#include "jobview/ErrorStatusMessage.h"
 #include "dropjobnotifier.h"
 #include "viewmanager.h"
 
@@ -38,6 +39,10 @@
 #include <QtNetwork/QNetworkAccessManager>
 #include <QtNetwork/QNetworkReply>
 
+#include <QWebPage>
+#include <QWebFrame>
+#include <QWebElement>
+
 using namespace Tomahawk;
 
 QPixmap* GroovesharkParser::s_pixmap = 0;
@@ -71,15 +76,17 @@ GroovesharkParser::~GroovesharkParser()
 void
 GroovesharkParser::lookupUrl( const QString& link )
 {
-    if( link.contains( "playlist" ) )
+    if ( link.contains( "playlist" ) )
     {
-        if( !m_createNewPlaylist )
+        if ( !m_createNewPlaylist )
             m_trackMode = true;
         else
             m_trackMode = false;
 
         lookupGroovesharkPlaylist( link );
     }
+    else if ( link.contains( "grooveshark.com/s/" ) || link.contains( "grooveshark.com/#/s/" ) )
+        lookupGroovesharkTrack( link );
     else
         return;
 
@@ -96,7 +103,7 @@ GroovesharkParser::lookupGroovesharkPlaylist( const QString& linkRaw )
         tDebug() << "no fragment, setting fragment to path";
         urlFragment = QUrl(linkRaw).path();
     }
-    
+
     tDebug() << urlFragment;
 
     int paramStartingPostition = urlFragment.indexOf( "?" );
@@ -107,22 +114,22 @@ GroovesharkParser::lookupGroovesharkPlaylist( const QString& linkRaw )
     bool ok;
 
     QStringList urlParts = urlFragment.split( "/", QString::SkipEmptyParts );
-    
+
     tDebug() << urlParts;
-    
+
     int playlistID = urlParts.at( 2 ).toInt( &ok, 10 );
     if (!ok)
     {
         tDebug() << "incorrect grooveshark url";
         return;
     }
-    
-    
-    
+
+
+
     m_title = urlParts.at( 1 );
-    
+
     tDebug() << "should get playlist " << playlistID;
-    
+
     DropJob::DropType type;
 
     type = DropJob::Playlist;
@@ -152,6 +159,53 @@ GroovesharkParser::lookupGroovesharkPlaylist( const QString& linkRaw )
     m_queries.insert( reply );
 }
 
+
+void
+GroovesharkParser::lookupGroovesharkTrack( const QString& track )
+{
+    tLog() << "Parsing Grooveshark Track Page:" << track;
+
+    QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( QUrl( track ) ) );
+    connect( reply, SIGNAL( finished() ), this, SLOT( trackPageFetchFinished() ) );
+
+    m_browseJob = new DropJobNotifier( pixmap(), "Grooveshark", DropJob::Track, reply );
+    JobStatusView::instance()->model()->addJob( m_browseJob );
+
+    m_queries << reply;
+}
+
+
+void
+GroovesharkParser::trackPageFetchFinished()
+{
+    QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() );
+    Q_ASSERT( r );
+
+    m_queries.remove( r );
+    r->deleteLater();
+
+    QWebPage page;
+    page.settings()->setAttribute( QWebSettings::JavascriptEnabled, false );
+    page.settings()->setAttribute( QWebSettings::PluginsEnabled, false );
+    page.settings()->setAttribute( QWebSettings::JavaEnabled, false );
+    page.settings()->setAttribute( QWebSettings::AutoLoadImages, false );
+    page.mainFrame()->setHtml( QString::fromUtf8( r->readAll() ) );
+    QWebElement title = page.mainFrame()->findFirstElement("span[itemprop='name']");
+    QWebElement artist = page.mainFrame()->findFirstElement("noscript span[itemprop='byArtist']");
+    QWebElement album = page.mainFrame()->findFirstElement("noscript span[itemprop='inAlbum']");
+
+    if ( !title.toPlainText().isEmpty() && !artist.toPlainText().isEmpty() )
+    {
+        tDebug() << "Got track info from grooveshark, enough to create a query:" << title.toPlainText() << artist.toPlainText() << album.toPlainText();
+
+        Tomahawk::query_ptr q = Tomahawk::Query::get( artist.toPlainText(), title.toPlainText(), album.toPlainText(), uuid(), true );
+        m_tracks << q;
+    }
+
+    checkTrackFinished();
+}
+
+
 void
 GroovesharkParser::groovesharkLookupFinished()
 {
@@ -178,7 +232,7 @@ GroovesharkParser::groovesharkLookupFinished()
         foreach (const QVariant& var, list)
         {
             QVariantMap trackResult = var.toMap();
-            
+
             QString title, artist, album;
 
             title = trackResult.value( "SongName", QString() ).toString();
@@ -194,10 +248,11 @@ GroovesharkParser::groovesharkLookupFinished()
             Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album, uuid(), m_trackMode );
             m_tracks << q;
         }
-        
+
 
     } else
     {
+        JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Error fetching Grooveshark information from the network!" ) ) );
         tLog() << "Error in network request to grooveshark for track decoding:" << r->errorString();
     }
 
@@ -229,7 +284,7 @@ GroovesharkParser::checkPlaylistFinished()
             return;
         }
 
-        
+
         emit tracks( m_tracks );
 
         deleteLater();
diff --git a/src/libtomahawk/utils/groovesharkparser.h b/src/libtomahawk/utils/groovesharkparser.h
index a387ed928..af04dac3d 100644
--- a/src/libtomahawk/utils/groovesharkparser.h
+++ b/src/libtomahawk/utils/groovesharkparser.h
@@ -42,7 +42,7 @@ class QNetworkReply;
 
 namespace Tomahawk
 {
-    
+
 class DropJobNotifier;
 
 class DLLEXPORT GroovesharkParser : public QObject
@@ -58,6 +58,7 @@ signals:
 
 private slots:
     void groovesharkLookupFinished();
+    void trackPageFetchFinished();
 
     void playlistCreated();
 private:
@@ -65,6 +66,8 @@ private:
 
     void lookupUrl( const QString& url );
     void lookupGroovesharkPlaylist( const QString& playlist );
+    void lookupGroovesharkTrack( const QString& track );
+
     void checkTrackFinished();
     void checkPlaylistFinished();
     int  m_limit;
@@ -79,7 +82,7 @@ private:
     QCA::SymmetricKey m_apiKey;
 
     static QPixmap* s_pixmap;
-    
+
 };
 
 }
diff --git a/src/libtomahawk/utils/itunesparser.cpp b/src/libtomahawk/utils/itunesparser.cpp
index 1afb1cfaf..f4f97060d 100644
--- a/src/libtomahawk/utils/itunesparser.cpp
+++ b/src/libtomahawk/utils/itunesparser.cpp
@@ -25,6 +25,7 @@
 #include "sourcelist.h"
 #include "jobview/JobStatusView.h"
 #include "jobview/JobStatusModel.h"
+#include "jobview/ErrorStatusMessage.h"
 
 #include <qjson/parser.h>
 
@@ -166,6 +167,7 @@ ItunesParser::itunesResponseLookupFinished()
 
     } else
     {
+        JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Error fetching iTunes information from the network!" ) ) );
         tLog() << "Error in network request to Itunes for track decoding:" << r->errorString();
     }
 
diff --git a/src/libtomahawk/utils/rdioparser.cpp b/src/libtomahawk/utils/rdioparser.cpp
index a0621f23c..3d8416177 100644
--- a/src/libtomahawk/utils/rdioparser.cpp
+++ b/src/libtomahawk/utils/rdioparser.cpp
@@ -26,6 +26,7 @@
 #include "dropjob.h"
 #include "jobview/JobStatusView.h"
 #include "jobview/JobStatusModel.h"
+#include "jobview/ErrorStatusMessage.h"
 #include "dropjobnotifier.h"
 #include "viewmanager.h"
 #include "sourcelist.h"
@@ -189,6 +190,7 @@ RdioParser::rdioReturned()
 
     } else
     {
+        JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Error fetching Rdio information from the network!" ) ) );
         tLog() << "Error in network request to Rdio for track decoding:" << r->errorString();
     }
 
diff --git a/src/libtomahawk/utils/shortenedlinkparser.cpp b/src/libtomahawk/utils/shortenedlinkparser.cpp
index 6d89aba69..e28b1a1d3 100644
--- a/src/libtomahawk/utils/shortenedlinkparser.cpp
+++ b/src/libtomahawk/utils/shortenedlinkparser.cpp
@@ -21,7 +21,11 @@
 
 #include "utils/logger.h"
 #include "utils/tomahawkutils.h"
+#include "dropjobnotifier.h"
 #include "query.h"
+#include "jobview/ErrorStatusMessage.h"
+#include "jobview/JobStatusModel.h"
+#include "jobview/JobStatusView.h"
 
 #include <qjson/parser.h>
 
@@ -30,6 +34,7 @@
 
 using namespace Tomahawk;
 
+QPixmap* ShortenedLinkParser::s_pixmap = 0;
 
 ShortenedLinkParser::ShortenedLinkParser ( const QStringList& urls, QObject* parent )
     : QObject( parent )
@@ -58,6 +63,8 @@ ShortenedLinkParser::handlesUrl( const QString& url )
              url.contains( "itun.es" ) ||
              url.contains( "tinyurl.com" ) ||
              url.contains( "tinysong.com" ) ||
+             url.contains( "grooveshark.com/s/~/" ) || // These redirect to the 'real' grooveshark track url
+             url.contains( "grooveshark.com/#/s/~/" ) ||
              url.contains( "rd.io" ) );
 }
 
@@ -65,11 +72,18 @@ void
 ShortenedLinkParser::lookupUrl ( const QString& url )
 {
     tDebug() << "Looking up..." << url;
+    QString cleaned = url;
+    if ( cleaned.contains( "/#/s/" ) )
+        cleaned.replace( "/#", "" );
 
-    QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( QUrl( url ) ) );
+    QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( QUrl( cleaned ) ) );
     connect( reply, SIGNAL( finished() ), this, SLOT( lookupFinished() ) );
 
     m_queries.insert( reply );
+
+    m_expandJob = new DropJobNotifier( pixmap(), "shortened", DropJob::Track, reply );
+    JobStatusView::instance()->model()->addJob( m_expandJob );
+
 }
 
 void
@@ -78,6 +92,9 @@ ShortenedLinkParser::lookupFinished()
     QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() );
     Q_ASSERT( r );
 
+    if ( r->error() != QNetworkReply::NoError )
+        JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Network error parsing shortened link!" ) ) );
+
     QVariant redir = r->attribute( QNetworkRequest::RedirectionTargetAttribute );
     if ( redir.isValid() && !redir.toUrl().isEmpty() )
     {
@@ -89,7 +106,7 @@ ShortenedLinkParser::lookupFinished()
     else
     {
         tLog() << "Got a redirected url:" << r->url().toString();
-        m_links << r->url().toString();   
+        m_links << r->url().toString();
         m_queries.remove( r );
         r->deleteLater();
         checkFinished();
@@ -108,3 +125,13 @@ ShortenedLinkParser::checkFinished()
         deleteLater();
     }
 }
+
+
+QPixmap
+ShortenedLinkParser::pixmap()
+{
+    if ( !s_pixmap )
+        s_pixmap = new QPixmap( RESPATH "images/add.png" );
+
+    return *s_pixmap;
+}
diff --git a/src/libtomahawk/utils/shortenedlinkparser.h b/src/libtomahawk/utils/shortenedlinkparser.h
index 4fd358749..8c503acd3 100644
--- a/src/libtomahawk/utils/shortenedlinkparser.h
+++ b/src/libtomahawk/utils/shortenedlinkparser.h
@@ -26,12 +26,15 @@
 #include <QObject>
 #include <QSet>
 #include <QStringList>
+#include <QPixmap>
 
 class QNetworkReply;
 
 namespace Tomahawk
 {
 
+class DropJobNotifier;
+
 /**
  * Small class to parse whitelisted shortened links into the redirected urls
  *
@@ -58,8 +61,13 @@ private:
     void lookupUrl( const QString& url );
     void checkFinished();
 
+    static QPixmap pixmap();
+
     QStringList m_links;
     QSet< QNetworkReply* > m_queries;
+    DropJobNotifier* m_expandJob;
+
+    static QPixmap* s_pixmap;
 };
 
 }
diff --git a/src/libtomahawk/utils/spotifyparser.cpp b/src/libtomahawk/utils/spotifyparser.cpp
index cba486f2d..49c86081a 100644
--- a/src/libtomahawk/utils/spotifyparser.cpp
+++ b/src/libtomahawk/utils/spotifyparser.cpp
@@ -26,6 +26,7 @@
 #include "dropjob.h"
 #include "jobview/JobStatusView.h"
 #include "jobview/JobStatusModel.h"
+#include "jobview/ErrorStatusMessage.h"
 #include "dropjobnotifier.h"
 #include "viewmanager.h"
 
@@ -220,6 +221,7 @@ SpotifyParser::spotifyBrowseFinished()
 
     } else
     {
+        JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( tr( "Error fetching Spotify information from the network!" ) ) );
         tLog() << "Error in network request to Spotify for track decoding:" << r->errorString();
     }
 
diff --git a/src/libtomahawk/utils/tomahawkutils.cpp b/src/libtomahawk/utils/tomahawkutils.cpp
index 367164955..640b075a5 100644
--- a/src/libtomahawk/utils/tomahawkutils.cpp
+++ b/src/libtomahawk/utils/tomahawkutils.cpp
@@ -1,6 +1,7 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
  *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
+ *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.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
@@ -533,10 +534,12 @@ newerVersion( const QString& oldVersion, const QString& newVersion )
 
 
 QList< Tomahawk::query_ptr >
-mergePlaylistChanges( const QList< Tomahawk::query_ptr >& orig, const QList< Tomahawk::query_ptr >& newTracks )
+mergePlaylistChanges( const QList< Tomahawk::query_ptr >& orig, const QList< Tomahawk::query_ptr >& newTracks, bool& changed )
 {
     int sameCount = 0;
     QList< Tomahawk::query_ptr > tosave = newTracks;
+    changed = false;
+
     foreach ( const Tomahawk::query_ptr& newquery, newTracks )
     {
         foreach ( const Tomahawk::query_ptr& oldq, orig )
@@ -558,6 +561,7 @@ mergePlaylistChanges( const QList< Tomahawk::query_ptr >& orig, const QList< Tom
     if ( orig.size() == newTracks.size() && sameCount == orig.size() )
         return orig;
 
+    changed = true;
     return tosave;
 }
 
diff --git a/src/libtomahawk/utils/tomahawkutils.h b/src/libtomahawk/utils/tomahawkutils.h
index fc2dcf4a1..8b3252a98 100644
--- a/src/libtomahawk/utils/tomahawkutils.h
+++ b/src/libtomahawk/utils/tomahawkutils.h
@@ -1,6 +1,7 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
  *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
+ *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.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
@@ -42,6 +43,18 @@ namespace TomahawkUtils
         MediaTypeTrack
     };
 
+    enum ImageType
+    {
+        DefaultAlbumCover,
+        DefaultArtistImage
+    };
+    enum ImageMode
+    {
+        NoDefaultCover,
+        CoverInCase,
+        ScaledCover
+    };
+
     class DLLEXPORT NetworkProxyFactory : public QNetworkProxyFactory
     {
     public:
@@ -98,7 +111,7 @@ namespace TomahawkUtils
      *
      * \return true if some changes were made, false if the new tracks are the same as the current tracks in \param orig
      */
-    DLLEXPORT QList< Tomahawk::query_ptr > mergePlaylistChanges( const QList< Tomahawk::query_ptr >& orig, const QList< Tomahawk::query_ptr >& newTracks );
+    DLLEXPORT QList< Tomahawk::query_ptr > mergePlaylistChanges( const QList< Tomahawk::query_ptr >& orig, const QList< Tomahawk::query_ptr >& newTracks, bool& changed );
 
     DLLEXPORT void crash();
 }
diff --git a/src/libtomahawk/utils/tomahawkutilsgui.cpp b/src/libtomahawk/utils/tomahawkutilsgui.cpp
index 4d1d419d5..168e5915c 100644
--- a/src/libtomahawk/utils/tomahawkutilsgui.cpp
+++ b/src/libtomahawk/utils/tomahawkutilsgui.cpp
@@ -1,6 +1,7 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
  *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
+ *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.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
@@ -19,6 +20,7 @@
 #include "config.h"
 #include "tomahawkutilsgui.h"
 
+#include "logger.h"
 #include <QtGui/QLayout>
 #include <QtGui/QPainter>
 #include <QtGui/QPixmap>
@@ -36,6 +38,7 @@
     #include <windowsx.h>
 #endif
 
+
 namespace TomahawkUtils
 {
 static int s_headerHeight = 0;
@@ -83,15 +86,15 @@ createDragPixmap( MediaType type, int itemCount )
     QPixmap pixmap;
     switch ( type )
     {
-    case MediaTypeArtist:
-        pixmap = QPixmap( ":/data/images/artist-icon.png" ).scaledToWidth( size, Qt::SmoothTransformation );
-        break;
-    case MediaTypeAlbum:
-        pixmap = QPixmap( ":/data/images/album-icon.png" ).scaledToWidth( size, Qt::SmoothTransformation );
-        break;
-    case MediaTypeTrack:
-        pixmap = QPixmap( QString( ":/data/images/track-icon-%2x%2.png" ).arg( size ) );
-        break;
+        case MediaTypeArtist:
+            pixmap = QPixmap( ":/data/images/artist-icon.png" ).scaledToWidth( size, Qt::SmoothTransformation );
+            break;
+        case MediaTypeAlbum:
+            pixmap = QPixmap( ":/data/images/album-icon.png" ).scaledToWidth( size, Qt::SmoothTransformation );
+            break;
+        case MediaTypeTrack:
+            pixmap = QPixmap( QString( ":/data/images/track-icon-%2x%2.png" ).arg( size ) );
+            break;
     }
 
     int x = 0;
@@ -303,4 +306,60 @@ alphaBlend( const QColor& colorFrom, const QColor& colorTo, float opacity )
     return QColor( r, g, b );
 }
 
+
+QPixmap
+defaultPixmap( ImageType type, ImageMode mode, const QSize& size )
+{
+    QPixmap pixmap;
+    QHash< int, QPixmap > subsubcache;
+    QHash< int, QHash< int, QPixmap > > subcache;
+    static QHash< int, QHash< int, QHash< int, QPixmap > > > cache;
+
+    if ( cache.contains( type ) )
+    {
+        subcache = cache.value( type );
+
+        if ( subcache.contains( mode ) )
+        {
+            subsubcache = subcache.value( mode );
+
+            if ( subsubcache.contains( size.width() ) )
+                return subsubcache.value( size.width() );
+        }
+    }
+
+    switch ( type )
+    {
+        case DefaultAlbumCover:
+            if ( mode == CoverInCase )
+                pixmap = QPixmap( RESPATH "images/no-album-art-placeholder.png" );
+            else
+                pixmap = QPixmap( RESPATH "images/no-album-no-case.png" );
+            break;
+
+        case DefaultArtistImage:
+                pixmap = QPixmap( RESPATH "images/no-artist-image-placeholder.png" );
+            break;
+
+        default:
+            break;
+    }
+
+    if ( pixmap.isNull() )
+    {
+        Q_ASSERT( false );
+        return QPixmap();
+    }
+
+    if ( !size.isNull() )
+        pixmap = pixmap.scaled( size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
+
+    subsubcache.insert( size.width(), pixmap );
+    subcache.insert( mode, subsubcache );
+    cache.insert( type, subcache );
+
+    return pixmap;
+}
+
+
 } // ns
diff --git a/src/libtomahawk/utils/tomahawkutilsgui.h b/src/libtomahawk/utils/tomahawkutilsgui.h
index 6f8f08674..1790aeb9b 100644
--- a/src/libtomahawk/utils/tomahawkutilsgui.h
+++ b/src/libtomahawk/utils/tomahawkutilsgui.h
@@ -1,6 +1,7 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
  *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
+ *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.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
@@ -19,6 +20,8 @@
 #ifndef TOMAHAWKUTILSGUI_H
 #define TOMAHAWKUTILSGUI_H
 
+#include <QSize>
+
 #include "tomahawkutils.h"
 #include "dllmacro.h"
 
@@ -46,6 +49,9 @@ namespace TomahawkUtils
 
     DLLEXPORT int headerHeight();
     DLLEXPORT void setHeaderHeight( int height );
+
+    DLLEXPORT QPixmap defaultPixmap( ImageType type, ImageMode mode, const QSize& size = QSize( 0, 0 ) );
+
 }
 
 #endif // TOMAHAWKUTILSGUI_H
diff --git a/src/libtomahawk/utils/xspfloader.cpp b/src/libtomahawk/utils/xspfloader.cpp
index 570213c41..29fdbfe67 100644
--- a/src/libtomahawk/utils/xspfloader.cpp
+++ b/src/libtomahawk/utils/xspfloader.cpp
@@ -1,7 +1,7 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
  *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
- *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
+ *   Copyright 2011-2012, Leo Franchi            <lfranchi@kde.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
@@ -26,6 +26,12 @@
 #include "utils/tomahawkutils.h"
 #include "utils/logger.h"
 
+#ifndef ENABLE_HEADLESS
+#include "jobview/JobStatusView.h"
+#include "jobview/JobStatusModel.h"
+#include "jobview/ErrorStatusMessage.h"
+#endif
+
 #include "sourcelist.h"
 #include "playlist.h"
 #include <XspfUpdater.h>
@@ -33,6 +39,22 @@
 
 using namespace Tomahawk;
 
+QString
+XSPFLoader::errorToString( XSPFErrorCode error )
+{
+    switch ( error )
+    {
+    case ParseError:
+        return tr( "Failed to parse contents of XSPF playlist" );
+    case InvalidTrackError:
+        return tr( "Some playlist entries were found without artist and track name, they will be omitted");
+    case FetchError:
+        return tr( "Failed to fetch the desired playlist from the network, or the desired file does not exist" );
+    default:
+        return QString();
+    }
+}
+
 XSPFLoader::XSPFLoader( bool autoCreate, bool autoUpdate, QObject *parent )
     : QObject( parent )
     , m_autoCreate( autoCreate )
@@ -98,6 +120,9 @@ void
 XSPFLoader::reportError()
 {
     emit error( FetchError );
+#ifndef ENABLE_HEADLESS
+    JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( errorToString( FetchError) ) );
+#endif
     deleteLater();
 }
 
diff --git a/src/libtomahawk/utils/xspfloader.h b/src/libtomahawk/utils/xspfloader.h
index 055e76dbf..eee77de96 100644
--- a/src/libtomahawk/utils/xspfloader.h
+++ b/src/libtomahawk/utils/xspfloader.h
@@ -1,7 +1,7 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
  *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
- *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
+ *   Copyright 2011-2012, Leo Franchi            <lfranchi@kde.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
@@ -49,6 +49,8 @@ public:
     void setOverrideTitle( const QString& newTitle );
     void setAutoResolveTracks( bool autoResolve ) { m_autoResolve = autoResolve; }
 
+    static QString errorToString( XSPFErrorCode error );
+
 signals:
     void error( XSPFLoader::XSPFErrorCode error );
     void ok( const Tomahawk::playlist_ptr& );
diff --git a/src/libtomahawk/viewmanager.cpp b/src/libtomahawk/viewmanager.cpp
index 0816d8203..ef8008773 100644
--- a/src/libtomahawk/viewmanager.cpp
+++ b/src/libtomahawk/viewmanager.cpp
@@ -126,6 +126,10 @@ ViewManager::ViewManager( QObject* parent )
 ViewManager::~ViewManager()
 {
     saveCurrentPlaylistSettings();
+    delete m_whatsHotWidget;
+    delete m_welcomeWidget;
+    delete m_topLovedWidget;
+    delete m_contextWidget;
     delete m_widget;
 }
 
@@ -371,7 +375,8 @@ ViewManager::showSuperCollection()
         }
     }
 
-    m_superCollectionModel->setTitle( tr( "All available tracks" ) );
+    m_superCollectionModel->setTitle( tr( "SuperCollection" ) );
+    m_superCollectionModel->setDescription( tr( "Combined libraries of all your online friends" ) );
     m_superAlbumModel->setTitle( tr( "All available albums" ) );
 
     ViewPage* shown = 0;
@@ -432,7 +437,7 @@ Tomahawk::ViewPage*
 ViewManager::showTopLovedPage()
 {
     if ( !m_topLovedWidget )
-        m_topLovedWidget = new CustomPlaylistView( CustomPlaylistView::AllLovedTracks, source_ptr(), m_widget );
+        m_topLovedWidget = new CustomPlaylistView( CustomPlaylistView::TopLovedTracks, source_ptr(), m_widget );
 
     return show( m_topLovedWidget );
 }
diff --git a/src/libtomahawk/viewmanager.h b/src/libtomahawk/viewmanager.h
index adce43cc6..4dc2148b8 100644
--- a/src/libtomahawk/viewmanager.h
+++ b/src/libtomahawk/viewmanager.h
@@ -133,6 +133,7 @@ signals:
     void hideQueueRequested();
 
     void tomahawkLoaded();
+
 public slots:
     Tomahawk::ViewPage* showSuperCollection();
     Tomahawk::ViewPage* showWelcomePage();
diff --git a/src/libtomahawk/widgets/animatedcounterlabel.cpp b/src/libtomahawk/widgets/animatedcounterlabel.cpp
new file mode 100644
index 000000000..f3b46f516
--- /dev/null
+++ b/src/libtomahawk/widgets/animatedcounterlabel.cpp
@@ -0,0 +1,106 @@
+/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@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 "animatedcounterlabel.h"
+
+
+AnimatedCounterLabel::AnimatedCounterLabel(QWidget* parent, Qt::WindowFlags f)
+    : QLabel( parent, f )
+    , m_displayed( 0 )
+    , m_val( 0 )
+    , m_oldval( 0 )
+    , m_format( "%L1" )
+{
+    connect( &m_timer, SIGNAL( frameChanged( int ) ), SLOT( frame( int ) ) );
+    connect( &m_timer, SIGNAL( finished() ), SLOT( showDiff() ) );
+}
+
+
+void AnimatedCounterLabel::setFormat(const QString& f)
+{
+    m_format = f;
+    setText( m_format.arg( m_displayed ) );
+}
+
+
+void AnimatedCounterLabel::setVisible(bool b)
+{
+    QLabel::setVisible( b );
+    if ( !m_diff.isNull() )
+        m_diff.data()->setVisible( b );
+}
+
+
+void AnimatedCounterLabel::frame(int f)
+{
+    m_displayed = f;
+    QLabel::setText( m_format.arg( m_displayed ) );
+    QLabel::update();
+}
+
+
+void AnimatedCounterLabel::setVal(unsigned int v)
+{
+    if( v == m_val )
+        return;
+
+    m_oldval = m_val;
+    m_val = v;
+    m_timer.stop();
+    unsigned int dur = 1000;
+    unsigned int r = abs( v - m_oldval );
+
+    if( r > 1000 )        dur = 1500;
+    else if( r > 10000 )  dur = 2000;
+    else if( r > 25000 )  dur = 2250;
+    else if( r > 50000 )  dur = 2750;
+    else if( r > 100000 ) dur = 3000;
+    else if( r > 500000 ) dur = 5000;
+
+    m_timer.setDuration( dur );
+    m_timer.setFrameRange( m_displayed, v );
+    m_timer.setEasingCurve( QEasingCurve( QEasingCurve::OutCubic ) );
+    m_timer.start();
+}
+
+
+void AnimatedCounterLabel::showDiff()
+{
+    int differ = m_val - m_oldval;
+    m_diff = new QLabel( QString("%1 %L2" ).arg( differ > 0 ? "+" : "" )
+    .arg( (int)m_val - (int)m_oldval ),
+                                 this->parentWidget() );
+
+    m_diff.data()->setStyleSheet( "font-size:9px; color:grey;" );
+    m_diff.data()->move( QPoint( this->pos().x(), this->pos().y() ) );
+    QPropertyAnimation* a = new QPropertyAnimation( m_diff.data(), "pos" );
+    a->setEasingCurve( QEasingCurve( QEasingCurve::InQuad ) );
+    a->setStartValue( m_diff.data()->pos() + QPoint( 0, -10 ) );
+    a->setEndValue( QPoint( m_diff.data()->pos().x(), m_diff.data()->pos().y() - 25 ) );
+    a->setDuration( 1000 );
+    //        qDebug() << "ANIMATING DIFF:" << a->startValue() << a->endValue();
+
+    connect( a, SIGNAL( finished() ), m_diff.data(), SLOT( hide() ) );
+    connect( a, SIGNAL( finished() ), m_diff.data(), SLOT( deleteLater() ) );
+    connect( a, SIGNAL( finished() ), a, SLOT( deleteLater() ) );
+
+    m_diff.data()->show();
+    m_diff.data()->setVisible( this->isVisible() );
+    a->start();
+}
diff --git a/src/libtomahawk/widgets/animatedcounterlabel.h b/src/libtomahawk/widgets/animatedcounterlabel.h
index 39e102140..a934b31df 100644
--- a/src/libtomahawk/widgets/animatedcounterlabel.h
+++ b/src/libtomahawk/widgets/animatedcounterlabel.h
@@ -32,86 +32,15 @@ class DLLEXPORT AnimatedCounterLabel : public QLabel
 Q_OBJECT
 
 public:
-    explicit AnimatedCounterLabel( QWidget* parent = 0, Qt::WindowFlags f = 0 )
-        : QLabel( parent, f )
-        , m_displayed( 0 )
-        , m_val( 0 )
-        , m_oldval( 0 )
-        , m_format( "%L1" )
-    {
-        connect( &m_timer, SIGNAL( frameChanged( int ) ), SLOT( frame( int ) ) );
-        connect( &m_timer, SIGNAL( finished() ), SLOT( showDiff() ) );
-    }
+    explicit AnimatedCounterLabel( QWidget* parent = 0, Qt::WindowFlags f = 0 );
 
-    void setFormat( const QString& f )
-    {
-        m_format = f;
-        setText( m_format.arg( m_displayed ) );
-    }
+    void setFormat( const QString& f );
 
 public slots:
-    void setVisible( bool b )
-    {
-        QLabel::setVisible( b );
-        if ( !m_diff.isNull() )
-            m_diff.data()->setVisible( b );
-    }
-
-    void frame( int f )
-    {
-        m_displayed = f;
-        QLabel::setText( m_format.arg( m_displayed ) );
-        QLabel::update();
-    }
-
-    void setVal( unsigned int v )
-    {
-        if( v == m_val )
-            return;
-
-        m_oldval = m_val;
-        m_val = v;
-        m_timer.stop();
-        unsigned int dur = 1000;
-        unsigned int r = abs( v - m_oldval );
-
-        if( r > 1000 )        dur = 1500;
-        else if( r > 10000 )  dur = 2000;
-        else if( r > 25000 )  dur = 2250;
-        else if( r > 50000 )  dur = 2750;
-        else if( r > 100000 ) dur = 3000;
-        else if( r > 500000 ) dur = 5000;
-
-        m_timer.setDuration( dur );
-        m_timer.setFrameRange( m_displayed, v );
-        m_timer.setEasingCurve( QEasingCurve( QEasingCurve::OutCubic ) );
-        m_timer.start();
-    }
-
-    void showDiff()
-    {
-        int differ = m_val - m_oldval;
-        m_diff = new QLabel( QString("%1 %L2" ).arg( differ > 0 ? "+" : "" )
-                                                     .arg( (int)m_val - (int)m_oldval ),
-                                                     this->parentWidget() );
-
-        m_diff.data()->setStyleSheet( "font-size:9px; color:grey;" );
-        m_diff.data()->move( QPoint( this->pos().x(), this->pos().y() ) );
-        QPropertyAnimation* a = new QPropertyAnimation( m_diff.data(), "pos" );
-        a->setEasingCurve( QEasingCurve( QEasingCurve::InQuad ) );
-        a->setStartValue( m_diff.data()->pos() + QPoint( 0, -10 ) );
-        a->setEndValue( QPoint( m_diff.data()->pos().x(), m_diff.data()->pos().y() - 25 ) );
-        a->setDuration( 1000 );
-        //        qDebug() << "ANIMATING DIFF:" << a->startValue() << a->endValue();
-
-        connect( a, SIGNAL( finished() ), m_diff.data(), SLOT( hide() ) );
-        connect( a, SIGNAL( finished() ), m_diff.data(), SLOT( deleteLater() ) );
-        connect( a, SIGNAL( finished() ), a, SLOT( deleteLater() ) );
-
-        m_diff.data()->show();
-        m_diff.data()->setVisible( this->isVisible() );
-        a->start();
-    }
+    void setVisible( bool b );
+    void frame( int f );
+    void setVal( unsigned int v );
+    void showDiff();
 
 private:
     QTimeLine m_timer;
diff --git a/src/libtomahawk/widgets/checkdirtree.cpp b/src/libtomahawk/widgets/checkdirtree.cpp
index ee4bdc86f..2f5df909f 100644
--- a/src/libtomahawk/widgets/checkdirtree.cpp
+++ b/src/libtomahawk/widgets/checkdirtree.cpp
@@ -22,6 +22,7 @@
 #include "utils/logger.h"
 #include "tomahawksettings.h"
 
+#include <QCoreApplication>
 #include <QProcess>
 
 static QString s_macVolumePath = "/Volumes";
@@ -30,40 +31,49 @@ CheckDirModel::CheckDirModel( QWidget* parent )
     : QFileSystemModel( parent )
     , m_shownVolumes( false )
 {
-#ifdef Q_WS_MAC
+#ifdef Q_OS_MAC
+    m_setFilePath = QString( "%1/SetFile" )        .arg( QCoreApplication::applicationDirPath() );
+    m_getFileInfoPath = QString( "%1/GetFileInfo" ).arg( QCoreApplication::applicationDirPath() );
+
     QProcess* checkVolumeVisible = new QProcess( this );
     connect( checkVolumeVisible, SIGNAL( readyReadStandardOutput() ), this, SLOT( getFileInfoResult() ) );
-    checkVolumeVisible->start( "GetFileInfo", QStringList() <<  "-aV" << s_macVolumePath );
+    qDebug() << "Running GetFileInfo:" << m_getFileInfoPath << "-aV" << s_macVolumePath;
+    checkVolumeVisible->start( m_getFileInfoPath, QStringList() <<  "-aV" << s_macVolumePath );
 #endif
 }
 
 CheckDirModel::~CheckDirModel()
 {
-#ifdef Q_WS_MAC
+#ifdef Q_OS_MAC
     // reset to previous state
     if ( m_shownVolumes )
-        QProcess::startDetached( QString( "SetFile -a V %1" ).arg( s_macVolumePath ) );
+        QProcess::startDetached( QString( "%1 -a V %2" ).arg( m_setFilePath).arg( s_macVolumePath ) );
 #endif
 }
 
 void
 CheckDirModel::getFileInfoResult()
 {
-#ifdef Q_WS_MAC
+#ifdef Q_OS_MAC
     QProcess* p = qobject_cast< QProcess* >( sender() );
     Q_ASSERT( p );
 
     QByteArray res = p->readAll().trimmed();
+    qDebug() << "Got output from GetFileInfo:" << res;
     // 1 means /Volumes is hidden, so we show it while the dialog is visible
     if ( res == "1" )
     {
         // Remove the hidden flag for the /Volumnes folder so all mount points are visible in the default (Q)FileSystemModel
-        QProcess* p = new QProcess( this );
-        connect( p, SIGNAL( finished( int, QProcess::ExitStatus ) ), this, SLOT( volumeShowFinished() ) );
-        p->start( QString( "SetFile -a v %1" ).arg( s_macVolumePath ) );
+        QProcess* showProcess = new QProcess( this );
+        qDebug() << "Running SetFile:" << QString( "%1 -a v %2" ).arg( m_setFilePath ).arg( s_macVolumePath );
+        showProcess->start( QString( "%1 -a v %2" ).arg( m_setFilePath ).arg( s_macVolumePath ) );
+        connect( showProcess, SIGNAL( readyReadStandardError() ), this, SLOT( processErrorOutput() ) );
         m_shownVolumes = true;
+
+        QTimer::singleShot( 500, this, SLOT( volumeShowFinished() ) );
     }
 
+    p->terminate();
     p->deleteLater();
 #endif
 }
@@ -74,6 +84,16 @@ CheckDirModel::volumeShowFinished()
     reset();
 }
 
+
+void
+CheckDirModel::processErrorOutput()
+{
+    QProcess* p = qobject_cast< QProcess* >( sender() );
+    Q_ASSERT( p );
+    qDebug() << "Got ERROR OUTPUT from subprocess in CheckDirModel:" << p->readAll();
+}
+
+
 Qt::ItemFlags
 CheckDirModel::flags( const QModelIndex& index ) const
 {
diff --git a/src/libtomahawk/widgets/checkdirtree.h b/src/libtomahawk/widgets/checkdirtree.h
index 3c8119534..4b1871d2f 100644
--- a/src/libtomahawk/widgets/checkdirtree.h
+++ b/src/libtomahawk/widgets/checkdirtree.h
@@ -46,11 +46,13 @@ signals:
 private slots:
     void getFileInfoResult();
     void volumeShowFinished();
-
+    void processErrorOutput();
 private:
     QHash<QPersistentModelIndex, Qt::CheckState> m_checkTable;
 
     bool m_shownVolumes;
+    QString m_setFilePath;
+    QString m_getFileInfoPath;
 };
 
 
diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp
index 62b048704..af76743c7 100644
--- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp
+++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp
@@ -61,20 +61,20 @@ AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, ModelMode st
     ui->tracksView->setTreeModel( m_tracksModel );
     ui->tracksView->setRootIsDecorated( false );
 
-    m_pixmap = QPixmap( RESPATH "images/no-album-art-placeholder.png" ).scaledToWidth( 48, Qt::SmoothTransformation );
+    m_pixmap = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultAlbumCover, TomahawkUtils::ScaledCover, QSize( 48, 48 ) );
 
     m_button = new OverlayButton( ui->tracksView );
     m_button->setCheckable( true );
     m_button->setChecked( m_tracksModel->mode() == InfoSystemMode );
     if ( m_button->isChecked() )
-        m_button->setText( tr( "Click to show Super Collection Tracks" ) );
+        m_button->setText( tr( "Click to show SuperCollection Tracks" ) );
     else
         m_button->setText( tr( "Click to show Official Tracks" ) );
 
     m_buttonAlbums = new OverlayButton( ui->albumsView );
     m_buttonAlbums->setCheckable( true );
     m_buttonAlbums->setChecked( true );
-    m_buttonAlbums->setText( tr( "Click to show Super Collection Albums" ) );
+    m_buttonAlbums->setText( tr( "Click to show SuperCollection Albums" ) );
     m_buttonAlbums->show();
 
     connect( m_button, SIGNAL( clicked() ), SLOT( onModeToggle() ) );
@@ -113,7 +113,7 @@ AlbumInfoWidget::setMode( ModelMode mode )
         onModeToggle();
 
     if ( mode == InfoSystemMode )
-        m_button->setText( tr( "Click to show Super Collection Tracks" ) );
+        m_button->setText( tr( "Click to show SuperCollection Tracks" ) );
     else
         m_button->setText( tr( "Click to show Official Tracks" ) );
 }
@@ -131,7 +131,7 @@ void
 AlbumInfoWidget::onAlbumsModeToggle()
 {
     if ( m_buttonAlbums->isChecked() )
-        m_buttonAlbums->setText( tr( "Click to show Super Collection Albums" ) );
+        m_buttonAlbums->setText( tr( "Click to show SuperCollection Albums" ) );
     else
         m_buttonAlbums->setText( tr( "Click to show Official Albums" ) );
 
@@ -243,10 +243,10 @@ AlbumInfoWidget::loadAlbums( bool autoRefetch )
 void
 AlbumInfoWidget::onAlbumCoverUpdated()
 {
-    if ( m_album->cover().isNull() )
+    if ( m_album->cover( QSize( 0, 0 ) ).isNull() )
         return;
 
-    m_pixmap.loadFromData( m_album->cover() );
+    m_pixmap = m_album->cover( QSize( 0, 0 ) );
     emit pixmapChanged( m_pixmap );
 }
 
diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp
index 178713c04..bde6dd54e 100644
--- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp
+++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp
@@ -36,6 +36,8 @@
 #include "widgets/OverlayButton.h"
 #include "widgets/overlaywidget.h"
 
+#include "pipeline.h"
+
 using namespace Tomahawk;
 
 
@@ -77,10 +79,10 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget*
     ui->topHits->setTrackModel( m_topHitsModel );
     ui->topHits->setSortingEnabled( false );
 
-    m_pixmap = QPixmap( RESPATH "images/no-album-no-case.png" ).scaledToWidth( 48, Qt::SmoothTransformation );
+    m_pixmap = TomahawkUtils::defaultPixmap( TomahawkUtils::DefaultArtistImage, TomahawkUtils::ScaledCover, QSize( 48, 48 ) );
 
     m_button = new OverlayButton( ui->albums );
-    m_button->setText( tr( "Click to show Super Collection Albums" ) );
+    m_button->setText( tr( "Click to show SuperCollection Albums" ) );
     m_button->setCheckable( true );
     m_button->setChecked( true );
 
@@ -119,7 +121,7 @@ ArtistInfoWidget::setMode( ModelMode mode )
         onModeToggle();
 
     if ( mode == InfoSystemMode )
-        m_button->setText( tr( "Click to show Super Collection Albums" ) );
+        m_button->setText( tr( "Click to show SuperCollection Albums" ) );
     else
         m_button->setText( tr( "Click to show Official Albums" ) );
 }
@@ -260,7 +262,8 @@ ArtistInfoWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestD
             int i = 0;
             foreach ( const QString& track, tracks )
             {
-                queries << Query::get( m_artist->name(), track, QString(), uuid() );
+                queries << Query::get( m_artist->name(), track, QString() );
+                Pipeline::instance()->resolve( queries );
 
                 if ( ++i == 15 )
                     break;
@@ -289,10 +292,10 @@ ArtistInfoWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestD
 void
 ArtistInfoWidget::onArtistImageUpdated()
 {
-    if ( m_artist->cover().isNull() )
+    if ( m_artist->cover( QSize( 0, 0 ) ).isNull() )
         return;
 
-    m_pixmap.loadFromData( m_artist->cover() );
+    m_pixmap = m_artist->cover( QSize( 0, 0 ) );
     emit pixmapChanged( m_pixmap );
 }
 
diff --git a/src/libtomahawk/widgets/searchwidget.cpp b/src/libtomahawk/widgets/searchwidget.cpp
index c7a547333..0ccbda4ab 100644
--- a/src/libtomahawk/widgets/searchwidget.cpp
+++ b/src/libtomahawk/widgets/searchwidget.cpp
@@ -1,6 +1,7 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
  *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
+ *   Copyright 2012       Leo Franchi            <lfranchi@kde.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
@@ -112,9 +113,26 @@ SearchWidget::changeEvent( QEvent* e )
 }
 
 
+Tomahawk::playlistinterface_ptr
+SearchWidget::playlistInterface() const
+{
+    return ui->resultsView->playlistInterface();
+}
+
+
+bool
+SearchWidget::jumpToCurrentTrack()
+{
+    return ui->resultsView->jumpToCurrentTrack();
+}
+
+
 void
 SearchWidget::onResultsFound( const QList<Tomahawk::result_ptr>& results )
 {
+    QList<Tomahawk::artist_ptr> artists;
+    QList<Tomahawk::album_ptr> albums;
+
     foreach( const Tomahawk::result_ptr& result, results )
     {
         if ( !result->collection().isNull() && !result->isOnline() )
@@ -128,7 +146,13 @@ SearchWidget::onResultsFound( const QList<Tomahawk::result_ptr>& results )
         q->addResults( rl );
 
         m_resultsModel->append( q );
+        
+        artists << result->artist();
+        albums << result->album();
     }
+
+    m_artistsModel->addArtists( artists );
+    m_albumsModel->addAlbums( albums );
 }
 
 
diff --git a/src/libtomahawk/widgets/searchwidget.h b/src/libtomahawk/widgets/searchwidget.h
index b16f3611f..2da9eeb0d 100644
--- a/src/libtomahawk/widgets/searchwidget.h
+++ b/src/libtomahawk/widgets/searchwidget.h
@@ -1,6 +1,7 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
  *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
+ *   Copyright 2012       Leo Franchi            <lfranchi@kde.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
@@ -46,7 +47,7 @@ public:
     ~SearchWidget();
 
     virtual QWidget* widget() { return this; }
-    virtual Tomahawk::playlistinterface_ptr playlistInterface() const { return Tomahawk::playlistinterface_ptr(); }
+    virtual Tomahawk::playlistinterface_ptr playlistInterface() const;
     virtual bool isTemporaryPage() const { return true; }
 
     virtual QString title() const { return QString( tr( "Search: %1" ) ).arg( m_search ); }
@@ -55,7 +56,7 @@ public:
 
     virtual bool showStatsBar() const { return false; }
 
-    virtual bool jumpToCurrentTrack() { return false; }
+    virtual bool jumpToCurrentTrack();
 
 protected:
     void changeEvent( QEvent* e );
diff --git a/src/libtomahawk/widgets/whatshotwidget.cpp b/src/libtomahawk/widgets/whatshotwidget.cpp
index bedad456a..c4c9ecfb4 100644
--- a/src/libtomahawk/widgets/whatshotwidget.cpp
+++ b/src/libtomahawk/widgets/whatshotwidget.cpp
@@ -108,6 +108,8 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent )
 
 WhatsHotWidget::~WhatsHotWidget()
 {
+    qDeleteAll( m_workers );
+    m_workers.clear();
     m_workerThread->exit(0);
     m_playlistInterface.clear();
     delete ui;
@@ -485,6 +487,7 @@ WhatsHotWidget::chartArtistsLoaded( ChartDataLoader* loader, const QList< artist
         }
     }
 
+    m_workers.remove( loader );
     loader->deleteLater();
 }
 
@@ -502,6 +505,7 @@ WhatsHotWidget::chartTracksLoaded( ChartDataLoader* loader, const QList< query_p
         m_trackModels[ chartId ]->append( tracks );
     }
 
+    m_workers.remove( loader );
     loader->deleteLater();
 }
 
@@ -515,5 +519,6 @@ WhatsHotWidget::chartAlbumsLoaded( ChartDataLoader* loader, const QList< album_p
     if ( m_albumModels.contains( chartId ) )
         m_albumModels[ chartId ]->addAlbums( albums );
 
+    m_workers.remove( loader );
     loader->deleteLater();
 }
diff --git a/src/libtomahawk/widgets/whatshotwidget.h b/src/libtomahawk/widgets/whatshotwidget.h
index 6e6801059..55e30e283 100644
--- a/src/libtomahawk/widgets/whatshotwidget.h
+++ b/src/libtomahawk/widgets/whatshotwidget.h
@@ -50,6 +50,7 @@ namespace Tomahawk
 {
     class ChartDataLoader;
     class ChartsPlaylistInterface;
+    class ChartDataLoader;
 }
 
 /**
@@ -111,6 +112,7 @@ private:
     // {Artist,Album,Track}::get() calls are all synchronous db calls
     // and we don't want to lock up out UI in case the db is busy (e.g. on startup)
     QThread* m_workerThread;
+    QSet< Tomahawk::ChartDataLoader* > m_workers;
 
     // Cache our model data
     QHash< QString, AlbumModel* > m_albumModels;
diff --git a/src/main.cpp b/src/main.cpp
index 4ad4ca4d4..f25a34a58 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -124,7 +124,7 @@ main( int argc, char *argv[] )
 #endif
 
 #ifndef ENABLE_HEADLESS
-   // new BreakPad( QDir::tempPath(), TomahawkSettings::instance()->crashReporterEnabled() );
+    new BreakPad( QDir::tempPath(), TomahawkSettings::instance()->crashReporterEnabled() );
 #endif
 
     KDSingleApplicationGuard guard( &a, KDSingleApplicationGuard::AutoKillOtherInstances );
diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp
index ddd954efa..62e03e36f 100644
--- a/src/settingsdialog.cpp
+++ b/src/settingsdialog.cpp
@@ -20,8 +20,6 @@
 #include "settingsdialog.h"
 #include "config.h"
 
-#include "utils/tomahawkutilsgui.h"
-
 #include <QDesktopServices>
 #include <QFileDialog>
 #include <QMessageBox>
@@ -38,6 +36,8 @@
 #include "pipeline.h"
 #include "resolver.h"
 #include "ExternalResolverGui.h"
+#include "utils/tomahawkutilsgui.h"
+#include "guihelpers.h"
 #include "scanmanager.h"
 #include "settingslistdelegate.h"
 #include "AccountDelegate.h"
@@ -123,7 +123,7 @@ SettingsDialog::SettingsDialog( QWidget *parent )
     ui->accountsFilterCombo->addItem( tr( "All" ), Accounts::NoType );
     ui->accountsFilterCombo->addItem( accountTypeToString( SipType ), SipType );
     ui->accountsFilterCombo->addItem( accountTypeToString( ResolverType ), ResolverType );
-    ui->accountsFilterCombo->addItem( accountTypeToString( InfoType ), InfoType );
+    ui->accountsFilterCombo->addItem( accountTypeToString( StatusPushType ), StatusPushType );
 
     connect( ui->accountsFilterCombo, SIGNAL( activated( int ) ), this, SLOT( accountsFilterChanged( int ) ) );
 
@@ -234,7 +234,7 @@ SettingsDialog::createIcons()
     QFontMetrics fm( font() );
     QListWidgetItem *accountsButton = new QListWidgetItem( ui->listWidget );
     accountsButton->setIcon( QIcon( RESPATH "images/account-settings.png" ) );
-    accountsButton->setText( tr( "Accounts" ) );
+    accountsButton->setText( tr( "Services" ) );
     accountsButton->setTextAlignment( Qt::AlignHCenter );
     accountsButton->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled );
     maxlen = fm.width( accountsButton->text() );
@@ -343,61 +343,6 @@ SettingsDialog::accountsFilterChanged( int )
 }
 
 
-void
-SettingsDialog::openAccountConfig( Account* account, bool showDelete )
-{
-    if( account->configurationWidget() )
-    {
-#ifndef Q_OS_MAC
-        DelegateConfigWrapper dialog( account->configurationWidget(), QString("%1 Configuration" ).arg( account->accountFriendlyName() ), this );
-        dialog.setShowDelete( showDelete );
-        QWeakPointer< DelegateConfigWrapper > watcher( &dialog );
-        int ret = dialog.exec();
-        if ( !watcher.isNull() && dialog.deleted() )
-        {
-            AccountManager::instance()->removeAccount( account );
-        }
-        else if( !watcher.isNull() && ret == QDialog::Accepted )
-        {
-            // send changed config to resolver
-            account->saveConfig();
-        }
-#else
-        // on osx a sheet needs to be non-modal
-        DelegateConfigWrapper* dialog = new DelegateConfigWrapper( account->configurationWidget(), QString("%1 Configuration" ).arg( account->accountFriendlyName() ), this, Qt::Sheet );
-        dialog->setShowDelete( showDelete );
-        dialog->setProperty( "accountplugin", QVariant::fromValue< QObject* >( account ) );
-        connect( dialog, SIGNAL( finished( int ) ), this, SLOT( accountConfigClosed( int ) ) );
-        connect( dialog, SIGNAL( closedWithDelete() ), this, SLOT( accountConfigDelete() ) );
-
-        dialog->show();
-#endif
-    }
-}
-
-
-void
-SettingsDialog::accountConfigClosed( int value )
-{
-    if( value == QDialog::Accepted )
-    {
-        DelegateConfigWrapper* dialog = qobject_cast< DelegateConfigWrapper* >( sender() );
-        Account* account = qobject_cast< Account* >( dialog->property( "accountplugin" ).value< QObject* >() );
-        account->saveConfig();
-    }
-}
-
-
-void
-SettingsDialog::accountConfigDelete()
-{
-    DelegateConfigWrapper* dialog = qobject_cast< DelegateConfigWrapper* >( sender() );
-    Account* account = qobject_cast< Account* >( dialog->property( "accountplugin" ).value< QObject* >() );
-    Q_ASSERT( account );
-    AccountManager::instance()->removeAccount( account );
-}
-
-
 void
 SettingsDialog::openAccountFactoryConfig( AccountFactory* factory )
 {
@@ -421,14 +366,10 @@ SettingsDialog::openAccountFactoryConfig( AccountFactory* factory )
     AccountFactoryWrapper dialog( factory, this );
     QWeakPointer< AccountFactoryWrapper > watcher( &dialog );
 
-    int ret = dialog.exec();
-    if ( !watcher.isNull() && dialog.doCreateAccount() )
-        createAccountFromFactory( factory );
+    dialog.exec();
 #else
     // on osx a sheet needs to be non-modal
     AccountFactoryWrapper* dialog = new AccountFactoryWrapper( factory, this );
-    connect( dialog, SIGNAL( createAccount( Tomahawk::Accounts::AccountFactory* ) ), this, SLOT( createAccountFromFactory( Tomahawk::Accounts::AccountFactory* ) ) );
-
     dialog->show();
 #endif
 }
@@ -437,84 +378,14 @@ SettingsDialog::openAccountFactoryConfig( AccountFactory* factory )
 void
 SettingsDialog::createAccountFromFactory( AccountFactory* factory )
 {
-#ifdef Q_WS_MAC
-    // On mac we need to close the dialog we came from before showing another dialog
-    Q_ASSERT( sender() && qobject_cast< AccountFactoryWrapper* >( sender() ) );
-    AccountFactoryWrapper* dialog = qobject_cast< AccountFactoryWrapper* >( sender() );
-    dialog->accept();
-#endif
-
-    //if exited with OK, create it, if not, delete it immediately!
-    Account* account = factory->createAccount();
-    bool added = false;
-    if( account->configurationWidget() )
-    {
-#ifdef Q_WS_MAC
-        // on osx a sheet needs to be non-modal
-        DelegateConfigWrapper* dialog = new DelegateConfigWrapper( account->configurationWidget(), QString("%1 Config" ).arg( account->accountFriendlyName() ), this, Qt::Sheet );
-        dialog->setProperty( "accountplugin", QVariant::fromValue< QObject* >( account ) );
-        connect( dialog, SIGNAL( finished( int ) ), this, SLOT( accountCreateConfigClosed( int ) ) );
-
-        if( account->configurationWidget()->metaObject()->indexOfSignal( "dataError(bool)" ) > -1 )
-            connect( account->configurationWidget(), SIGNAL( dataError( bool ) ), dialog, SLOT( toggleOkButton( bool ) ), Qt::UniqueConnection );
-
-        dialog->show();
-#else
-        DelegateConfigWrapper dialog( account->configurationWidget(), QString("%1 Config" ).arg( account->accountFriendlyName() ), this );
-        QWeakPointer< DelegateConfigWrapper > watcher( &dialog );
-
-        if( account->configurationWidget()->metaObject()->indexOfSignal( "dataError(bool)" ) > -1 )
-            connect( account->configurationWidget(), SIGNAL( dataError( bool ) ), &dialog, SLOT( toggleOkButton( bool ) ), Qt::UniqueConnection );
-
-        int ret = dialog.exec();
-        if( !watcher.isNull() && ret == QDialog::Accepted ) // send changed config to account
-            added = true;
-        else // canceled, delete it
-            added = false;
-
-        handleAccountAdded( account, added );
-#endif
-    }
-    else
-    {
-        // no config, so just add it
-        added = true;
-        handleAccountAdded( account, added );
-    }
+    TomahawkUtils::createAccountFromFactory( factory, this );
 }
 
 
 void
-SettingsDialog::accountCreateConfigClosed( int finished )
+SettingsDialog::openAccountConfig( Account* account, bool showDelete )
 {
-    DelegateConfigWrapper* dialog = qobject_cast< DelegateConfigWrapper* >( sender() );
-    Account* account = qobject_cast< Account* >( dialog->property( "accountplugin" ).value< QObject* >() );
-    Q_ASSERT( account );
-
-    bool added = ( finished == QDialog::Accepted );
-
-    handleAccountAdded( account, added );
-}
-
-
-void
-SettingsDialog::handleAccountAdded( Account* account, bool added )
-{
-    if ( added )
-    {
-        account->setEnabled( true );
-        account->setAutoConnect( true );
-        account->saveConfig();
-
-        TomahawkSettings::instance()->addAccount( account->accountId() );
-        AccountManager::instance()->addAccount( account );
-        AccountManager::instance()->hookupAndEnable( account );
-    }
-    else
-    {
-        // user pressed cancel
-        delete account;
-    }
+    TomahawkUtils::openAccountConfig( account, this, showDelete );
 }
 
 
diff --git a/src/settingsdialog.h b/src/settingsdialog.h
index ca146efb3..cb02666bd 100644
--- a/src/settingsdialog.h
+++ b/src/settingsdialog.h
@@ -89,12 +89,8 @@ private slots:
     void accountsFilterChanged( int );
 
     void createAccountFromFactory( Tomahawk::Accounts::AccountFactory* );
-
     void openAccountConfig( Tomahawk::Accounts::Account*, bool showDelete = false );
     void openAccountFactoryConfig( Tomahawk::Accounts::AccountFactory* );
-    void accountConfigClosed( int value );
-    void accountConfigDelete();
-    void accountCreateConfigClosed( int value );
 
     void installFromFile();
     void scrollTo( const QModelIndex& );
@@ -108,7 +104,6 @@ private slots:
 
 private:
     void createIcons();
-    void handleAccountAdded( Tomahawk::Accounts::Account* p, bool added );
 
     Ui_StackedSettingsDialog* ui;
 
diff --git a/src/sourcetree/items/categoryitems.cpp b/src/sourcetree/items/categoryitems.cpp
index cd9a89db9..4be8e182f 100644
--- a/src/sourcetree/items/categoryitems.cpp
+++ b/src/sourcetree/items/categoryitems.cpp
@@ -120,6 +120,7 @@ CategoryAddItem::flags() const
         case SourcesModel::PlaylistsCategory:
             return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled;
         case SourcesModel::StationsCategory:
+            return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled;
         default:
             return Qt::ItemIsEnabled;
             break;
diff --git a/src/sourcetree/items/groupitem.cpp b/src/sourcetree/items/groupitem.cpp
index 01804c0ba..20d0a20f1 100644
--- a/src/sourcetree/items/groupitem.cpp
+++ b/src/sourcetree/items/groupitem.cpp
@@ -28,12 +28,10 @@ using namespace Tomahawk;
 
 
 GroupItem::GroupItem( SourcesModel* model, SourceTreeItem* parent, const QString& text, int peerSortValue )
-    : SourceTreeItem( model, parent, SourcesModel::Group )
+    : SourceTreeItem( model, parent, SourcesModel::Group, peerSortValue )
     , m_text( text )
-    , m_peerSortValue( peerSortValue )
+    , m_defaultExpanded( true )
 {
-    // expand by default
-    QTimer::singleShot( 0, this, SLOT( requestExpanding() ) );
 }
 
 
@@ -56,6 +54,18 @@ GroupItem::requestExpanding()
 }
 
 
+void
+GroupItem::checkExpandedState()
+{
+    if ( m_defaultExpanded )
+    {
+        // only default expand once
+        m_defaultExpanded = false;
+        requestExpanding();
+    }
+}
+
+
 QString
 GroupItem::text() const
 {
diff --git a/src/sourcetree/items/groupitem.h b/src/sourcetree/items/groupitem.h
index 5f173ac0b..823f991a7 100644
--- a/src/sourcetree/items/groupitem.h
+++ b/src/sourcetree/items/groupitem.h
@@ -36,9 +36,11 @@ public:
     virtual QString text() const;
     virtual bool willAcceptDrag( const QMimeData* data ) const { Q_UNUSED( data ); return false; }
     virtual QIcon icon() const { return QIcon(); }
-    virtual int peerSortValue() const { return m_peerSortValue; }
     virtual bool isBeingPlayed() const { return false; }
 
+    void checkExpandedState();
+    void setDefaultExpanded( bool b ) { m_defaultExpanded = b; }
+
 public slots:
     virtual void activate();
 
@@ -50,7 +52,7 @@ private slots:
 
 private:
     QString m_text;
-    int m_peerSortValue;
+    bool m_defaultExpanded;
 };
 
 #endif
diff --git a/src/sourcetree/items/sourceitem.cpp b/src/sourcetree/items/sourceitem.cpp
index f4084b38e..82a977f7c 100644
--- a/src/sourcetree/items/sourceitem.cpp
+++ b/src/sourcetree/items/sourceitem.cpp
@@ -124,7 +124,7 @@ SourceItem::source() const
 QString
 SourceItem::text() const
 {
-    return m_source.isNull() ? tr( "Super Collection" ) : m_source->friendlyName();
+    return m_source.isNull() ? tr( "SuperCollection" ) : m_source->friendlyName();
 }
 
 
@@ -486,7 +486,7 @@ ViewPage*
 SourceItem::lovedTracksClicked()
 {
     if ( !m_lovedTracksPage )
-        m_lovedTracksPage = new CustomPlaylistView( m_source.isNull() ? CustomPlaylistView::AllLovedTracks : CustomPlaylistView::SourceLovedTracks, m_source, ViewManager::instance()->widget() );
+        m_lovedTracksPage = new CustomPlaylistView( m_source.isNull() ? CustomPlaylistView::TopLovedTracks : CustomPlaylistView::SourceLovedTracks, m_source, ViewManager::instance()->widget() );
 
     ViewManager::instance()->show( m_lovedTracksPage );
     return m_lovedTracksPage;
diff --git a/src/sourcetree/items/sourcetreeitem.cpp b/src/sourcetree/items/sourcetreeitem.cpp
index 01a0224f8..644331472 100644
--- a/src/sourcetree/items/sourcetreeitem.cpp
+++ b/src/sourcetree/items/sourcetreeitem.cpp
@@ -23,11 +23,12 @@
 using namespace Tomahawk;
 
 
-SourceTreeItem::SourceTreeItem( SourcesModel* model, SourceTreeItem* parent, SourcesModel::RowType thisType, int index )
+SourceTreeItem::SourceTreeItem( SourcesModel* model, SourceTreeItem* parent, SourcesModel::RowType thisType, int peerSortValue, int index )
     : QObject()
     , m_type( thisType )
     , m_parent( parent )
     , m_model( model )
+    , m_peerSortValue( peerSortValue )
 {
     connect( this, SIGNAL( beginChildRowsAdded( int, int ) ), m_model, SLOT( onItemRowsAddedBegin( int, int ) ) );
     connect( this, SIGNAL( beginChildRowsRemoved( int, int ) ), m_model, SLOT( onItemRowsRemovedBegin( int, int ) ) );
diff --git a/src/sourcetree/items/sourcetreeitem.h b/src/sourcetree/items/sourcetreeitem.h
index 7e174b789..adde55609 100644
--- a/src/sourcetree/items/sourcetreeitem.h
+++ b/src/sourcetree/items/sourcetreeitem.h
@@ -43,7 +43,7 @@ public:
     Q_DECLARE_FLAGS( DropTypes, DropType )
 
     SourceTreeItem() : m_type( SourcesModel::Invalid ), m_parent( 0 ), m_model( 0 ) {}
-    SourceTreeItem( SourcesModel* model, SourceTreeItem* parent, SourcesModel::RowType thisType, int index = -1 ); // if index is -1, append at end of parent's child list
+    SourceTreeItem( SourcesModel* model, SourceTreeItem* parent, SourcesModel::RowType thisType, int peerSortValue = 0, int index = -1 ); // if index is -1, append at end of parent's child list
     virtual ~SourceTreeItem();
 
     // generic info used by the tree model
@@ -63,7 +63,7 @@ public:
     virtual bool willAcceptDrag( const QMimeData* ) const { return false; }
     virtual bool dropMimeData( const QMimeData*, Qt::DropAction ) { return false; }
     virtual bool setData( const QVariant&, bool ) { return false; }
-    virtual int peerSortValue() const { return 0; } // How to sort relative to peers in the tree.
+    virtual int peerSortValue() const { return m_peerSortValue; } // How to sort relative to peers in the tree.
     virtual int IDValue() const { return 0; }
     virtual DropTypes supportedDropTypes( const QMimeData* mimeData ) const { Q_UNUSED( mimeData ); return DropTypesNone; }
     virtual void setDropType( DropType type ) { m_dropType = type; }
@@ -101,6 +101,7 @@ private:
     SourceTreeItem* m_parent;
     QList< SourceTreeItem* > m_children;
     SourcesModel* m_model;
+    int m_peerSortValue;
 
     DropType m_dropType;
 };
diff --git a/src/sourcetree/sourcedelegate.cpp b/src/sourcetree/sourcedelegate.cpp
index 61362ae6f..6cd00ce65 100644
--- a/src/sourcetree/sourcedelegate.cpp
+++ b/src/sourcetree/sourcedelegate.cpp
@@ -82,7 +82,7 @@ SourceDelegate::~SourceDelegate()
 QSize
 SourceDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const
 {
-    SourceTreeItem *item = index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >();
+    SourceTreeItem* item = index.data( SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >();
     SourcesModel::RowType type = static_cast< SourcesModel::RowType >( index.data( SourcesModel::SourceTreeItemTypeRole ).toInt() );
 
     if ( type == SourcesModel::Collection )
@@ -93,6 +93,10 @@ SourceDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex&
     {
         return QSize( option.rect.width(), 6 );
     }
+    else if ( type == SourcesModel::Group && index.row() > 0 )
+    {
+        return QSize( option.rect.width(), 24 );
+    }
     else if ( m_expandedMap.contains( index ) )
     {
         if ( !m_expandedMap.value( index )->initialized() )
@@ -128,7 +132,7 @@ SourceDelegate::paintDecorations( QPainter* painter, const QStyleOptionViewItem&
     if ( playable && playing && item->isBeingPlayed() )
     {
         const int iconW = option.rect.height() - 4;
-        QRect iconRect = QRect( option.rect.x() - iconW - 4, option.rect.y() + 2, iconW, iconW );
+        QRect iconRect = QRect( 4, option.rect.y() + 2, iconW, iconW );
         QPixmap speaker = option.state & QStyle::State_Selected ? m_nowPlayingSpeaker : m_nowPlayingSpeakerDark;
         speaker = speaker.scaledToHeight( iconW, Qt::SmoothTransformation );
         painter->drawPixmap( iconRect, speaker );
@@ -191,7 +195,7 @@ SourceDelegate::paintCollection( QPainter* painter, const QStyleOptionViewItem&
 
     painter->setFont( normal );
     textRect = option.rect.adjusted( iconRect.width() + 8, option.rect.height() / 2, -figWidth - 24, -6 );
-    
+
     bool privacyOn = TomahawkSettings::instance()->privateListeningMode() == TomahawkSettings::FullyPrivate;
     if ( !colItem->source().isNull() && colItem->source()->isLocal() && privacyOn )
     {
@@ -238,9 +242,11 @@ SourceDelegate::paintCollection( QPainter* painter, const QStyleOptionViewItem&
         }
     }
 
-    text = painter->fontMetrics().elidedText( desc, Qt::ElideRight, textRect.width() );
+    textRect.adjust( 0, 0, 0, 2 );
+    text = painter->fontMetrics().elidedText( desc, Qt::ElideRight, textRect.width() - 4 );
     QTextOption to( Qt::AlignVCenter );
-    painter->drawText( textRect.adjusted( 0, 0, 0, 2 ), text, to );
+    to.setWrapMode( QTextOption::NoWrap );
+    painter->drawText( textRect, text, to );
 
     if ( status )
     {
@@ -305,7 +311,7 @@ SourceDelegate::paintGroup( QPainter* painter, const QStyleOptionViewItem& optio
     font.setBold( true );
     painter->setFont( font );
 
-    QTextOption to( Qt::AlignVCenter );
+    QTextOption to( Qt::AlignBottom );
 
     painter->setPen( option.palette.color( QPalette::Base ) );
     painter->setBrush( option.palette.color( QPalette::Base ) );
@@ -325,7 +331,7 @@ SourceDelegate::paintGroup( QPainter* painter, const QStyleOptionViewItem& optio
 
         font.setPixelSize( font.pixelSize() - 1 );
         painter->setFont( font );
-        QTextOption to( Qt::AlignVCenter | Qt::AlignRight );
+        QTextOption to( Qt::AlignBottom | Qt::AlignRight );
 
         // draw close icon
         painter->setPen( Qt::white );
@@ -400,8 +406,6 @@ SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co
     if ( type != SourcesModel::Group && type != SourcesModel::Category && type != SourcesModel::Divider )
         QApplication::style()->drawControl( QStyle::CE_ItemViewItem, &o3, painter );
 
-    paintDecorations( painter, o3, index );
-
     if ( type == SourcesModel::Collection )
     {
         paintCollection( painter, o, index );
@@ -511,6 +515,9 @@ SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co
     else
     {
         o.state &= ~QStyle::State_MouseOver;
+        if ( !index.parent().parent().isValid() )
+            o.rect.adjust( 7, 0, 0, 0 );
+
         QStyledItemDelegate::paint( painter, o, index );
 
         if ( type == SourcesModel::TemporaryPage )
@@ -532,6 +539,8 @@ SourceDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, co
         }
     }
 
+    paintDecorations( painter, o3, index );
+
     painter->restore();
 }
 
@@ -618,6 +627,12 @@ SourceDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QSt
         }
     }
 
+    // We emit our own clicked() signal instead of relying on QTreeView's, because that is fired whether or not a delegate accepts
+    // a mouse press event. Since we want to swallow click events when they are on headphones other action items, here wemake sure we only
+    // emit if we really want to
+    if ( event->type() == QEvent::MouseButtonRelease )
+        emit clicked( index );
+
     return QStyledItemDelegate::editorEvent ( event, model, option, index );
 }
 
diff --git a/src/sourcetree/sourcedelegate.h b/src/sourcetree/sourcedelegate.h
index 1f6cea627..d7a1d3ef6 100644
--- a/src/sourcetree/sourcedelegate.h
+++ b/src/sourcetree/sourcedelegate.h
@@ -42,6 +42,7 @@ public:
     SourceTreeItem::DropType hoveredDropType() const;
 
 signals:
+    void clicked( const QModelIndex& idx );
     void latchOn( const Tomahawk::source_ptr& idx );
     void latchOff( const Tomahawk::source_ptr& idx );
     void toggleRealtimeLatch( const Tomahawk::source_ptr& idx, bool realtime );
diff --git a/src/sourcetree/sourcesmodel.cpp b/src/sourcetree/sourcesmodel.cpp
index dfe877d96..b4dca6e55 100644
--- a/src/sourcetree/sourcesmodel.cpp
+++ b/src/sourcetree/sourcesmodel.cpp
@@ -39,6 +39,7 @@
 #include "globalactionmanager.h"
 #include "dropjob.h"
 #include "items/playlistitems.h"
+#include "playlist/artistview.h"
 #include "playlist/playlistview.h"
 #include "playlist/dynamic/widgets/DynamicWidget.h"
 
@@ -53,8 +54,6 @@ SourcesModel::SourcesModel( QObject* parent )
     m_rootItem = new SourceTreeItem( this, 0, Invalid );
 
     appendGroups();
-    appendItem( source_ptr() );
-
     onSourcesAdded( SourceList::instance()->sources() );
 
     connect( SourceList::instance(), SIGNAL( sourceAdded( Tomahawk::source_ptr ) ), SLOT( onSourceAdded( Tomahawk::source_ptr ) ) );
@@ -265,28 +264,34 @@ SourcesModel::appendGroups()
 {
     beginInsertRows( QModelIndex(), rowCount(), rowCount() + 2 );
 
-    new SourceTreeItem( this, m_rootItem, SourcesModel::Divider, 0 );
-    new HistoryItem( this, m_rootItem, tr( "History" ), 5 );
-    GroupItem* browse = new GroupItem( this, m_rootItem, tr( "Browse" ), 10 );
+    GroupItem* browse = new GroupItem( this, m_rootItem, tr( "Browse" ), 0 );
+    new HistoryItem( this, m_rootItem, tr( "Search History" ), 1 );
+//    new SourceTreeItem( this, m_rootItem, SourcesModel::Divider, 2 );
+    m_myMusicGroup = new GroupItem( this, m_rootItem, tr( "My Music" ), 3 );
 
     // super collection
+    GenericPageItem* sc = new GenericPageItem( this, browse, tr( "SuperCollection" ), QIcon( RESPATH "images/supercollection.png" ),
+                                                  boost::bind( &ViewManager::showSuperCollection, ViewManager::instance() ),
+                                                  boost::bind( &ViewManager::superCollectionView, ViewManager::instance() ) );
+    sc->setSortValue( 1 );
+
+    // browse section
     GenericPageItem* loved = new GenericPageItem( this, browse, tr( "Top Loved Tracks" ), QIcon( RESPATH "images/loved_playlist.png" ),
                                                   boost::bind( &ViewManager::showTopLovedPage, ViewManager::instance() ),
                                                   boost::bind( &ViewManager::topLovedWidget, ViewManager::instance() ) );
-    loved->setSortValue( -250 );
+    loved->setSortValue( 2 );
 
-    // add misc children of root node
     GenericPageItem* recent = new GenericPageItem( this, browse, tr( "Dashboard" ), QIcon( RESPATH "images/dashboard.png" ),
                                                    boost::bind( &ViewManager::showWelcomePage, ViewManager::instance() ),
                                                    boost::bind( &ViewManager::welcomeWidget, ViewManager::instance() ) );
-    recent->setSortValue( -300 );
+    recent->setSortValue( 0 );
 
     GenericPageItem* hot = new GenericPageItem( this, browse, tr( "Charts" ), QIcon( RESPATH "images/charts.png" ),
                                                 boost::bind( &ViewManager::showWhatsHotPage, ViewManager::instance() ),
                                                 boost::bind( &ViewManager::whatsHotWidget, ViewManager::instance() ) );
-    hot->setSortValue( -300 );
+    hot->setSortValue( 3 );
 
-    m_collectionsGroup = new GroupItem( this, m_rootItem, tr( "Friends" ), 15 );
+    m_collectionsGroup = new GroupItem( this, m_rootItem, tr( "Friends" ), 4 );
 
     endInsertRows();
 }
@@ -295,10 +300,10 @@ SourcesModel::appendGroups()
 void
 SourcesModel::appendItem( const Tomahawk::source_ptr& source )
 {
-    SourceTreeItem* parent;
+    GroupItem* parent;
     if ( !source.isNull() && source->isLocal() )
     {
-        parent = m_rootItem;
+        parent = m_myMusicGroup;
     }
     else
     {
@@ -309,6 +314,8 @@ SourcesModel::appendItem( const Tomahawk::source_ptr& source )
     beginInsertRows( idx, rowCount( idx ), rowCount( idx ) );
     new SourceItem( this, parent, source );
     endInsertRows();
+    
+    parent->checkExpandedState();
 }
 
 
diff --git a/src/sourcetree/sourcesmodel.h b/src/sourcetree/sourcesmodel.h
index 969067396..694e4f8b6 100644
--- a/src/sourcetree/sourcesmodel.h
+++ b/src/sourcetree/sourcesmodel.h
@@ -139,6 +139,7 @@ private:
 
     SourceTreeItem* m_rootItem;
     GroupItem* m_collectionsGroup;
+    GroupItem* m_myMusicGroup;
 
     QList< Tomahawk::source_ptr > m_sourcesWithViewPage;
     QHash< Tomahawk::source_ptr, SourceTreeItem* > m_sourcesWithViewPageItems;
diff --git a/src/sourcetree/sourcesproxymodel.cpp b/src/sourcetree/sourcesproxymodel.cpp
index a0db5e2ea..de36f87e5 100644
--- a/src/sourcetree/sourcesproxymodel.cpp
+++ b/src/sourcetree/sourcesproxymodel.cpp
@@ -66,7 +66,7 @@ SourcesProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourcePar
 
     if ( item && item->type() != SourcesModel::Divider && item->parent()->parent() == 0 && !item->children().count() )
         return false;
-
+   
     if ( !m_filtered )
         return true;
 
diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp
index 330e8aeee..ced9bda00 100644
--- a/src/sourcetree/sourcetreeview.cpp
+++ b/src/sourcetree/sourcetreeview.cpp
@@ -87,6 +87,7 @@ SourceTreeView::SourceTreeView( QWidget* parent )
     connect( m_delegate, SIGNAL( latchOn( Tomahawk::source_ptr ) ), SLOT( latchOnOrCatchUp( Tomahawk::source_ptr ) ) );
     connect( m_delegate, SIGNAL( latchOff( Tomahawk::source_ptr ) ), SLOT( latchOff( Tomahawk::source_ptr ) ) );
     connect( m_delegate, SIGNAL( toggleRealtimeLatch( Tomahawk::source_ptr, bool ) ), m_latchManager, SLOT( latchModeChangeRequest( Tomahawk::source_ptr,bool ) ) );
+    connect( m_delegate, SIGNAL( clicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) );
 
     setItemDelegate( m_delegate );
 
@@ -104,7 +105,6 @@ SourceTreeView::SourceTreeView( QWidget* parent )
     header()->setStretchLastSection( false );
     header()->setResizeMode( 0, QHeaderView::Stretch );
 
-    connect( this, SIGNAL( clicked( QModelIndex ) ), SLOT( onItemActivated( QModelIndex ) ) );
     connect( this, SIGNAL( expanded( QModelIndex ) ), SLOT( onItemExpanded( QModelIndex ) ) );
 //     connect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( onSelectionChanged() ) );
 
@@ -256,14 +256,14 @@ SourceTreeView::selectRequest( const QPersistentModelIndex& idx )
 
 
 void
-SourceTreeView::expandRequest( const QPersistentModelIndex &idx )
+SourceTreeView::expandRequest( const QPersistentModelIndex& idx )
 {
     expand( idx );
 }
 
 
 void
-SourceTreeView::toggleExpandRequest( const QPersistentModelIndex &idx )
+SourceTreeView::toggleExpandRequest( const QPersistentModelIndex& idx )
 {
     if ( isExpanded( idx ) )
         collapse( idx );
@@ -431,11 +431,11 @@ SourceTreeView::latchModeToggled( bool checked )
     qDebug() << Q_FUNC_INFO;
     if ( !m_contextMenuIndex.isValid() )
         return;
-    
+
     SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt();
     if( type != SourcesModel::Collection )
         return;
-    
+
     const SourceItem* item = itemFromIndex< SourceItem >( m_contextMenuIndex );
     const source_ptr source = item->source();
     emit latchModeChangeRequest( source, checked );
diff --git a/src/stackedsettingsdialog.ui b/src/stackedsettingsdialog.ui
index dacee0c24..6d08a1a2b 100644
--- a/src/stackedsettingsdialog.ui
+++ b/src/stackedsettingsdialog.ui
@@ -85,7 +85,7 @@
      <item>
       <widget class="QStackedWidget" name="stackedWidget">
        <property name="currentIndex">
-        <number>2</number>
+        <number>0</number>
        </property>
        <widget class="QWidget" name="accountsPage">
         <layout class="QVBoxLayout" name="verticalLayout_11">
@@ -95,7 +95,7 @@
          <item>
           <widget class="QGroupBox" name="groupBox_4">
            <property name="title">
-            <string>Internet Sources</string>
+            <string>Internet Services</string>
            </property>
            <layout class="QVBoxLayout" name="verticalLayout_8">
             <property name="margin">
@@ -136,7 +136,11 @@
              </layout>
             </item>
             <item>
-             <widget class="QListView" name="accountsView"/>
+             <widget class="QListView" name="accountsView">
+              <property name="alternatingRowColors">
+               <bool>true</bool>
+              </property>
+             </widget>
             </item>
            </layout>
           </widget>
diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp
index 900d56182..669a3383d 100644
--- a/src/tomahawkapp.cpp
+++ b/src/tomahawkapp.cpp
@@ -61,7 +61,6 @@
 #include "utils/logger.h"
 #include "utils/tomahawkutilsgui.h"
 
-#include <lastfm/ws.h>
 #include "config.h"
 
 #ifndef ENABLE_HEADLESS
@@ -170,7 +169,7 @@ TomahawkApp::init()
     m_scanManager = QWeakPointer<ScanManager>( new ScanManager( this ) );
 
     // init pipeline and resolver factories
-    new Pipeline( this );
+    new Pipeline();
 
 #ifndef ENABLE_HEADLESS
     Pipeline::instance()->addExternalResolverFactory( boost::bind( &QtScriptResolver::factory, _1 ) );
@@ -300,6 +299,8 @@ TomahawkApp::~TomahawkApp()
 {
     tLog() << "Shutting down Tomahawk...";
 
+    Pipeline::instance()->stop();
+
     if ( !m_servent.isNull() )
         delete m_servent.data();
     if ( !m_scanManager.isNull() )
@@ -308,15 +309,8 @@ TomahawkApp::~TomahawkApp()
     if ( !m_audioEngine.isNull() )
         delete m_audioEngine.data();
 
-    if ( !m_infoSystem.isNull() )
-        delete m_infoSystem.data();
-
-    //FIXME: delete GeneratorFactory::registerFactory( "echonest", new EchonestFactory ); ?
-
     delete Tomahawk::Accounts::AccountManager::instance();
 
-    Pipeline::instance()->stop();
-
 #ifndef ENABLE_HEADLESS
     delete m_mainwindow;
     delete AtticaManager::instance();
@@ -327,6 +321,9 @@ TomahawkApp::~TomahawkApp()
 
     delete Pipeline::instance();
 
+    if ( !m_infoSystem.isNull() )
+        delete m_infoSystem.data();
+
     tLog() << "Finished shutdown.";
 }
 
@@ -352,6 +349,13 @@ TomahawkApp::printHelp()
     echo( "  --testdb       Use a test database instead of real collection\n" );
     echo( "  --noupnp       Disable UPnP\n" );
     echo( "  --nosip        Disable SIP\n" );
+    echo( "\nPlayback Controls:\n" );
+    echo( "  --playpause    Toggle playing/paused state\n" );
+    echo( "  --play         Start/resume playback\n" );
+    echo( "  --pause        Pause playback\n" );
+    echo( "  --stop         Stop playback\n" );
+    echo( "  --next         Advances to the next track (if available)\n" );
+    echo( "  --prev         Returns to the previous track (if available)\n" );
     echo( "\nurl is a tomahawk:// command or alternatively a url that Tomahawk can recognize.\n" );
     echo( "For more documentation, see http://wiki.tomahawk-player.org/mediawiki/index.php/Tomahawk://_Links\n" );
 }
@@ -465,7 +469,6 @@ TomahawkApp::initHTTP()
 
     tLog() << "Starting HTTPd on" << m_session.listenInterface().toString() << m_session.port();
     m_session.start();
-
 }
 
 
@@ -512,6 +515,7 @@ TomahawkApp::initServent()
     }
 }
 
+
 // Called after Servent emits ready()
 void
 TomahawkApp::initSIP()
@@ -540,10 +544,7 @@ TomahawkApp::spotifyApiCheckFinished()
     QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() );
     Q_ASSERT( reply );
 
-    if ( reply->error() )
-        DropJob::setCanParseSpotifyPlaylists( false );
-    else
-        DropJob::setCanParseSpotifyPlaylists( true );
+    DropJob::setCanParseSpotifyPlaylists( !reply->error() );
 #endif
 }
 
@@ -603,5 +604,19 @@ TomahawkApp::instanceStarted( KDSingleApplicationGuard::Instance instance )
         return;
 
     QString arg1 = instance.arguments[ 1 ];
-    loadUrl( arg1 );
+    if ( loadUrl( arg1 ) )
+        return;
+
+    if ( instance.arguments.contains( "--next" ) )
+        AudioEngine::instance()->next();
+    else if ( instance.arguments.contains( "--prev" ) )
+        AudioEngine::instance()->previous();
+    else if ( instance.arguments.contains( "--playpause" ) )
+        AudioEngine::instance()->playPause();
+    else if ( instance.arguments.contains( "--play" ) )
+        AudioEngine::instance()->play();
+    else if ( instance.arguments.contains( "--pause" ) )
+        AudioEngine::instance()->pause();
+    else if ( instance.arguments.contains( "--stop" ) )
+        AudioEngine::instance()->stop();
 }
diff --git a/src/tomahawktrayicon.cpp b/src/tomahawktrayicon.cpp
index 6a58a4e6f..b8d927ec2 100644
--- a/src/tomahawktrayicon.cpp
+++ b/src/tomahawktrayicon.cpp
@@ -58,6 +58,8 @@ TomahawkTrayIcon::TomahawkTrayIcon( QObject* parent )
     m_showWindowAction = m_contextMenu->addAction( tr( "Hide Tomahawk Window" ) );
     m_showWindowAction->setData( true );
     connect( m_showWindowAction, SIGNAL( triggered() ), this, SLOT( showWindow() ) );
+
+    connect( m_contextMenu, SIGNAL( aboutToShow() ), this, SLOT( menuAboutToShow() ) );
 #endif
 
     m_contextMenu->addSeparator();
@@ -118,6 +120,16 @@ TomahawkTrayIcon::showWindow()
 }
 
 
+void
+TomahawkTrayIcon::menuAboutToShow()
+{
+    // When using Cmd-H on mac to hide a window, it is an OS-level hide that is different from QWidget::hide().
+    // Qt returns isVisible() == true for windows that are hidden with Cmd-H, which is weird. isActiveWindow() returns
+    // the proper information though.
+    setShowHideWindow( APP->mainWindow()->isActiveWindow() );
+}
+
+
 void
 TomahawkTrayIcon::setResult( const Tomahawk::result_ptr& result )
 {
@@ -188,6 +200,12 @@ TomahawkTrayIcon::onActivated( QSystemTrayIcon::ActivationReason reason )
         }
         break;
 
+        case QSystemTrayIcon::MiddleClick:
+        {
+            AudioEngine::instance()->playPause();
+        }
+        break;
+
         default:
             break;
     }
diff --git a/src/tomahawktrayicon.h b/src/tomahawktrayicon.h
index 3e5de54e1..be984ae43 100644
--- a/src/tomahawktrayicon.h
+++ b/src/tomahawktrayicon.h
@@ -46,6 +46,7 @@ private slots:
     void enablePlay();
     void enablePause();
 
+    void menuAboutToShow();
 private:
     void refreshToolTip();
     ~TomahawkTrayIcon();
diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp
index 54b9fefb3..8d8c086b8 100644
--- a/src/tomahawkwindow.cpp
+++ b/src/tomahawkwindow.cpp
@@ -1,7 +1,7 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
  *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
- *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
+ *   Copyright 2010-2012, Leo Franchi <lfranchi@kde.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
@@ -58,6 +58,8 @@
 #include "tomahawksettings.h"
 #include "sourcelist.h"
 #include "jobview/JobStatusView.h"
+#include "jobview/JobStatusModel.h"
+#include "jobview/ErrorStatusMessage.h"
 #include "tomahawktrayicon.h"
 #include "scanmanager.h"
 #include "tomahawkapp.h"
@@ -80,6 +82,7 @@ TomahawkWindow::TomahawkWindow( QWidget* parent )
     , m_searchWidget( 0 )
     , m_audioControls( new AudioControls( this ) )
     , m_trayIcon( new TomahawkTrayIcon( this ) )
+    , m_audioRetryCounter( 0 )
 {
     setWindowIcon( QIcon( RESPATH "icons/tomahawk-icon-128x128.png" ) );
 
@@ -570,11 +573,17 @@ TomahawkWindow::onXSPFError( XSPFLoader::XSPFErrorCode error )
 void
 TomahawkWindow::onAudioEngineError( AudioEngine::AudioErrorCode /* error */ )
 {
+    QString msg;
 #ifdef Q_WS_X11
-    QMessageBox::warning( this, tr( "Playback Error" ), tr( "Sorry, there is a problem accessing your audio device. Make sure you have a suitable Phonon backend and required plugins installed." ), QMessageBox::Ok );
+    msg = tr( "Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed." );
 #else
-    QMessageBox::warning( this, tr( "Playback Error" ), tr( "Sorry, there is a problem accessing your audio device." ), QMessageBox::Ok );
+    msg = tr( "Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped." );
 #endif
+    JobStatusView::instance()->model()->addJob( new ErrorStatusMessage( msg, 15 ) );
+
+    if ( m_audioRetryCounter < 3 )
+        AudioEngine::instance()->play();
+    m_audioRetryCounter++;
 }
 
 
@@ -654,6 +663,7 @@ TomahawkWindow::playlistCreateDialogFinished( int ret )
 void
 TomahawkWindow::audioStarted()
 {
+    m_audioRetryCounter = 0;
     ui->actionPlay->setText( tr( "Pause" ) );
 }
 
diff --git a/src/tomahawkwindow.h b/src/tomahawkwindow.h
index 9c8401832..adc3d98f0 100644
--- a/src/tomahawkwindow.h
+++ b/src/tomahawkwindow.h
@@ -1,7 +1,7 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
  *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
- *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
+ *   Copyright 2010-2012, Leo Franchi <lfranchi@kde.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
@@ -145,6 +145,7 @@ private:
 
     Tomahawk::result_ptr m_currentTrack;
     QString m_windowTitle;
+    int m_audioRetryCounter;
 };
 
 #endif // TOMAHAWKWINDOW_H
diff --git a/src/utils/guihelpers.cpp b/src/utils/guihelpers.cpp
new file mode 100644
index 000000000..e7345e4f2
--- /dev/null
+++ b/src/utils/guihelpers.cpp
@@ -0,0 +1,172 @@
+/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.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 "guihelpers.h"
+
+#include <QObject>
+
+#include "accounts/Account.h"
+#include "accounts/AccountManager.h"
+#include "delegateconfigwrapper.h"
+
+namespace TomahawkUtils
+{
+
+void
+handleAccountAdded( Tomahawk::Accounts::Account* account, bool added )
+{
+    if ( added )
+    {
+        account->setEnabled( true );
+        account->setAutoConnect( true );
+        account->saveConfig();
+
+        TomahawkSettings::instance()->addAccount( account->accountId() );
+        Tomahawk::Accounts::AccountManager::instance()->addAccount( account );
+        Tomahawk::Accounts::AccountManager::instance()->hookupAndEnable( account );
+    }
+    else
+    {
+        // user pressed cancel
+        delete account;
+    }
+}
+
+
+class UtilsObject : public QObject
+{
+    Q_OBJECT
+public:
+    UtilsObject( DelegateConfigWrapper* w ) : QObject( w ), m_w( w ) {}
+
+public slots:
+    void
+    accountCreateConfigClosed( int ret )
+    {
+        Tomahawk::Accounts::Account* account = qobject_cast< Tomahawk::Accounts::Account* >( m_w->property( "accountplugin" ).value< QObject* >() );
+        Q_ASSERT( account );
+
+        bool added = ( ret == QDialog::Accepted );
+
+        handleAccountAdded( account, added );
+    }
+
+    void
+    accountConfigClosed( int ret )
+    {
+        if( ret == QDialog::Accepted )
+        {
+            Tomahawk::Accounts::Account* account = qobject_cast< Tomahawk::Accounts::Account* >( m_w->property( "accountplugin" ).value< QObject* >() );
+            account->saveConfig();
+        }
+    }
+
+    void
+    accountConfigDelete()
+    {
+        Tomahawk::Accounts::Account* account = qobject_cast< Tomahawk::Accounts::Account* >( m_w->property( "accountplugin" ).value< QObject* >() );
+        Q_ASSERT( account );
+        Tomahawk::Accounts::AccountManager::instance()->removeAccount( account );
+    }
+private:
+    DelegateConfigWrapper* m_w;
+
+};
+
+
+void
+createAccountFromFactory( Tomahawk::Accounts::AccountFactory* factory, QWidget* parent )
+{
+    //if exited with OK, create it, if not, delete it immediately!
+    Tomahawk::Accounts::Account* account = factory->createAccount();
+    bool added = false;
+    if( account->configurationWidget() )
+    {
+        #ifdef Q_WS_MAC
+        // on osx a sheet needs to be non-modal
+        DelegateConfigWrapper* dialog = new DelegateConfigWrapper( account->configurationWidget(), QString("%1 Config" ).arg( account->accountFriendlyName() ), parent, Qt::Sheet );
+        dialog->setProperty( "accountplugin", QVariant::fromValue< QObject* >( account ) );
+
+        UtilsObject* obj = new UtilsObject( dialog );
+        QObject::connect( dialog, SIGNAL( finished( int ) ), obj, SLOT( accountCreateConfigClosed( int ) ) );
+
+        if( account->configurationWidget()->metaObject()->indexOfSignal( "dataError(bool)" ) > -1 )
+            QObject::connect( account->configurationWidget(), SIGNAL( dataError( bool ) ), dialog, SLOT( toggleOkButton( bool ) ), Qt::UniqueConnection );
+
+        dialog->show();
+        #else
+        DelegateConfigWrapper dialog( account->configurationWidget(), QString("%1 Config" ).arg( account->accountFriendlyName() ), parent );
+        QWeakPointer< DelegateConfigWrapper > watcher( &dialog );
+
+        if( account->configurationWidget()->metaObject()->indexOfSignal( "dataError(bool)" ) > -1 )
+            QObject::connect( account->configurationWidget(), SIGNAL( dataError( bool ) ), &dialog, SLOT( toggleOkButton( bool ) ), Qt::UniqueConnection );
+
+        int ret = dialog.exec();
+        if( !watcher.isNull() && ret == QDialog::Accepted ) // send changed config to account
+            added = true;
+        else // canceled, delete it
+            added = false;
+
+        handleAccountAdded( account, added );
+        #endif
+    }
+    else
+    {
+        // no config, so just add it
+        added = true;
+        handleAccountAdded( account, added );
+    }
+}
+
+void
+openAccountConfig( Tomahawk::Accounts::Account* account, QWidget* parent, bool showDelete )
+{
+    if( account->configurationWidget() )
+    {
+        #ifndef Q_OS_MAC
+        DelegateConfigWrapper dialog( account->configurationWidget(), QString("%1 Configuration" ).arg( account->accountFriendlyName() ), parent );
+        dialog.setShowDelete( showDelete );
+        QWeakPointer< DelegateConfigWrapper > watcher( &dialog );
+        int ret = dialog.exec();
+        if ( !watcher.isNull() && dialog.deleted() )
+        {
+            Tomahawk::Accounts::AccountManager::instance()->removeAccount( account );
+        }
+        else if( !watcher.isNull() && ret == QDialog::Accepted )
+        {
+            // send changed config to resolver
+            account->saveConfig();
+        }
+        #else
+        // on osx a sheet needs to be non-modal
+        DelegateConfigWrapper* dialog = new DelegateConfigWrapper( account->configurationWidget(), QString("%1 Configuration" ).arg( account->accountFriendlyName() ), parent, Qt::Sheet );
+        dialog->setShowDelete( showDelete );
+        dialog->setProperty( "accountplugin", QVariant::fromValue< QObject* >( account ) );
+        UtilsObject* obj = new UtilsObject( dialog );
+
+        QObject::connect( dialog, SIGNAL( finished( int ) ),    obj, SLOT( accountConfigClosed( int ) ) );
+        QObject::connect( dialog, SIGNAL( closedWithDelete() ), obj, SLOT( accountConfigDelete() ) );
+
+        dialog->show();
+        #endif
+    }
+}
+
+} // namespace TomahawkUtils
+
+#include "guihelpers.moc"
\ No newline at end of file
diff --git a/src/utils/guihelpers.h b/src/utils/guihelpers.h
new file mode 100644
index 000000000..f17ccd43c
--- /dev/null
+++ b/src/utils/guihelpers.h
@@ -0,0 +1,36 @@
+/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.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 TOMAHAWK_GUI_HELPERS_H
+#define TOMAHAWK_GUI_HELPERS_H
+
+class QWidget;
+namespace Tomahawk {
+    namespace Accounts {
+        class AccountFactory;
+        class Account;
+    }
+}
+
+namespace TomahawkUtils
+{
+    void createAccountFromFactory( Tomahawk::Accounts::AccountFactory*, QWidget* parent );
+    void openAccountConfig( Tomahawk::Accounts::Account*, QWidget* parent, bool showDelete = false );
+}
+
+#endif
\ No newline at end of file
diff --git a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m
index 8ba4adb8f..851ea0385 100644
--- a/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m
+++ b/thirdparty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m
@@ -88,11 +88,11 @@
 }
 -(void)printBacktrace;
 {
-  int x;
-  for(x = 3; x < frameCount; x++) {
-    if(frameStrings[x] == NULL) { break; }
-    printf("%s\n", frameStrings[x]);
-  }
+        int x;
+        for(x = 3; x < frameCount; x++) {
+		if(frameStrings[x] == NULL) { break; }
+		printf("%s\n", frameStrings[x]);
+	}
 }
 @end
 
diff --git a/thirdparty/SPMediaKeyTap/SPMediaKeyTap.h b/thirdparty/SPMediaKeyTap/SPMediaKeyTap.h
index f33ad7040..aa974d238 100644
--- a/thirdparty/SPMediaKeyTap/SPMediaKeyTap.h
+++ b/thirdparty/SPMediaKeyTap/SPMediaKeyTap.h
@@ -31,4 +31,13 @@
 -(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event;
 @end
 
-extern NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey;
\ No newline at end of file
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey;
+extern NSString *kIgnoreMediaKeysDefaultsKey;
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/thirdparty/SPMediaKeyTap/SPMediaKeyTap.m b/thirdparty/SPMediaKeyTap/SPMediaKeyTap.m
index a349f5922..665edc27f 100644
--- a/thirdparty/SPMediaKeyTap/SPMediaKeyTap.m
+++ b/thirdparty/SPMediaKeyTap/SPMediaKeyTap.m
@@ -28,6 +28,9 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
 	[self startWatchingAppSwitching];
 	singleton = self;
 	_mediaKeyAppList = [NSMutableArray new];
+    _tapThreadRL=nil;
+    _eventPort=nil;
+    _eventPortSource=nil;
 	return self;
 }
 -(void)dealloc;
@@ -58,6 +61,9 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
 }
 
 -(void)startWatchingMediaKeys;{
+    // Prevent having multiple mediaKeys threads
+    [self stopWatchingMediaKeys];
+
 	[self setShouldInterceptMediaKeyEvents:YES];
 	
 	// Add an event tap to intercept the system defined media key events
@@ -78,6 +84,22 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
 -(void)stopWatchingMediaKeys;
 {
 	// TODO<nevyn>: Shut down thread, remove event tap port and source
+
+    if(_tapThreadRL){
+        CFRunLoopStop(_tapThreadRL);
+        _tapThreadRL=nil;
+    }
+
+    if(_eventPort){
+        CFMachPortInvalidate(_eventPort);
+        CFRelease(_eventPort);
+        _eventPort=nil;
+    }
+
+    if(_eventPortSource){
+        CFRelease(_eventPortSource);
+        _eventPortSource=nil;
+    }
 }
 
 #pragma mark -
@@ -90,7 +112,9 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
 	return NO;
 #else
 	// XXX(nevyn): MediaKey event tap doesn't work on 10.4, feel free to figure out why if you have the energy.
-	return floor(NSAppKitVersionNumber) >= 949/*NSAppKitVersionNumber10_5*/;
+	return
+		![[NSUserDefaults standardUserDefaults] boolForKey:kIgnoreMediaKeysDefaultsKey]
+		&& floor(NSAppKitVersionNumber) >= 949/*NSAppKitVersionNumber10_5*/;
 #endif
 }
 
@@ -108,6 +132,14 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
 		@"com.apple.Aperture",
 		@"com.plexsquared.Plex",
 		@"com.soundcloud.desktop",
+		@"org.niltsh.MPlayerX",
+		@"com.ilabs.PandorasHelper",
+		@"com.mahasoftware.pandabar",
+		@"com.bitcartel.pandorajam",
+		@"org.clementine-player.clementine",
+		@"fm.last.Last.fm",
+		@"com.beatport.BeatportPro",
+		@"com.Timenut.SongKey",
 		@"com.macromedia.fireworks", // the tap messes up their mouse input
 		nil
 	];
@@ -213,6 +245,8 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
 #pragma mark Task switching callbacks
 
 NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey = @"SPApplicationsNeedingMediaKeys";
+NSString *kIgnoreMediaKeysDefaultsKey = @"SPIgnoreMediaKeys";
+
 
 
 -(void)mediaKeyAppListChanged;
diff --git a/thirdparty/liblastfm2/src/CMakeLists.txt b/thirdparty/liblastfm2/src/CMakeLists.txt
index f379776e0..56c203c14 100644
--- a/thirdparty/liblastfm2/src/CMakeLists.txt
+++ b/thirdparty/liblastfm2/src/CMakeLists.txt
@@ -87,6 +87,7 @@ endif(WIN32)
 qt4_wrap_cpp(MOC_SOURCES ${MOC_HEADERS})
 
 IF( WIN32 )
+    add_definitions( -DLASTFM_LIB )
     add_library(tomahawk_lastfm2 SHARED
     ${SOURCES}
     ${MOC_SOURCES}
diff --git a/thirdparty/liblastfm2/src/global.h b/thirdparty/liblastfm2/src/global.h
index d297ae702..8a513d802 100644
--- a/thirdparty/liblastfm2/src/global.h
+++ b/thirdparty/liblastfm2/src/global.h
@@ -30,25 +30,21 @@
 
 #include <QtGlobal>
 
-#ifdef Q_CC_MSVC
-    #ifdef LASTFM_LIB
-        #define LASTFM_DLLEXPORT __declspec(dllexport)
-    #else
-        #define LASTFM_DLLEXPORT __declspec(dllimport)
-    #endif
-	#ifdef LASTFM_FINGERPRINT_LIB
-        #define LASTFM_FINGERPRINT_DLLEXPORT __declspec(dllexport)
-    #else
-        #define LASTFM_FINGERPRINT_DLLEXPORT __declspec(dllimport)
-    #endif
-#elif __GNUC__ >= 4
-    #define LASTFM_DLLEXPORT __attribute__ ((visibility("default")))
-	#define LASTFM_FINGERPRINT_DLLEXPORT __attribute__ ((visibility("default")))
+#ifndef LASTFM_LIB_STATIC
+#ifdef LASTFM_LIB
+    #define LASTFM_DLLEXPORT Q_DECL_EXPORT
 #else
-    #define LASTFM_DLLEXPORT
-	#define LASTFM_FINGERPRINT_DLLEXPORT
+    #define LASTFM_DLLEXPORT Q_DECL_IMPORT
 #endif
-
+#ifdef LASTFM_FINGERPRINT_LIB
+    #define LASTFM_FINGERPRINT_DLLEXPORT Q_DECL_EXPORT
+#else
+    #define LASTFM_FINGERPRINT_DLLEXPORT Q_DECL_IMPORT
+#endif
+#else // LASTFM_LIB_STATIC
+    #define LASTFM_DLLEXPORT
+    #define LASTFM_FINGERPRINT_DLLEXPORT
+#endif // LASTFM_LIB_STATIC
 
 
 #include <QMetaEnum>