diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7472b2971..5bd4a8d59 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -127,8 +127,16 @@ ENDIF()
 #macro_log_feature(LIBLASTFM_FOUND "LastFm" "Qt library for the Last.fm webservices" "https://github.com/mxcl/liblastfm" FALSE "" "liblastfm is needed for scrobbling tracks to Last.fm and fetching cover artwork")
 set(LIBLASTFM_FOUND true)
 
+#### submodules start
 
 # this installs headers and such and should really be handled in a separate package by packagers
+IF( INTERNAL_JREEN OR INTERNAL_QTWEETLIB )
+    IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.gitmodules)
+        EXECUTE_PROCESS(COMMAND git submodule init WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} )
+        EXECUTE_PROCESS(COMMAND git submodule update WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} )
+    ENDIF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.gitmodules)
+ENDIF()
+
 IF( INTERNAL_JREEN )
     ADD_SUBDIRECTORY( ${THIRDPARTY_DIR}/jreen )
     SET( LIBJREEN_INCLUDE_DIR  ${CMAKE_CURRENT_BINARY_DIR}/thirdparty/jreen/include )
@@ -141,7 +149,6 @@ ELSE( INTERNAL_JREEN )
 ENDIF( INTERNAL_JREEN )
 macro_log_feature(LIBJREEN_FOUND "Jreen" "Qt XMPP Library" "https://github.com/euroelessar/jreen" FALSE "" "Jreen is needed for the Jabber SIP plugin. \n\n     Use -DINTERNAL_JREEN=ON to build the git submodule inside Tomahawk \n     Be aware this installs a full jreen with headers and everything!\n")
 
-# this installs headers and such and should really be handled in a separate package by packagers
 IF( INTERNAL_QTWEETLIB )
     ADD_SUBDIRECTORY( ${THIRDPARTY_DIR}/qtweetlib )
     # copy headers to build/QTweetLib so we can use proper includes inside the code
@@ -155,6 +162,7 @@ ELSE( INTERNAL_QTWEETLIB )
     macro_optional_find_package(QTweetLib)
 ENDIF( INTERNAL_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\n     Use -DINTERNAL_QTWEETLIB=ON to build the git submodule inside Tomahawk \n")
+#### submodules end
 
 ### libportfwd
 SET( LIBPORTFWD_INCLUDE_DIR ${THIRDPARTY_DIR}/libportfwd/include )
diff --git a/CMakeModules/FindCLucene.cmake b/CMakeModules/FindCLucene.cmake
index eb0eafc3e..83c569a38 100644
--- a/CMakeModules/FindCLucene.cmake
+++ b/CMakeModules/FindCLucene.cmake
@@ -12,7 +12,15 @@
 INCLUDE(CheckSymbolExists)
 INCLUDE(FindLibraryWithDebug)
 
+# try to locate a patched unstable version (for comp's sake *sigh*) first
+FIND_PACKAGE(CLuceneUnstable)
+IF(CLUCENEUNSTABLE_FOUND)
+    SET(CLucene_FOUND TRUE)
+    SET(CLUCENE_INCLUDE_DIR ${CLUCENE_UNSTABLE_INCLUDE_DIRS})
+    SET(CLUCENE_LIBRARIES ${CLUCENE_UNSTABLE_LIBS})
 
+    #MESSAGE(FATAL_ERROR NARF)
+ELSE(CLUCENEUNSTABLE_FOUND)
 IF(CLucene_FIND_VERSION)
   SET(CLUCENE_MIN_VERSION ${CLucene_FIND_VERSION})
 ELSEIF()
@@ -99,6 +107,7 @@ ENDIF (CLUCENE_LIBRARY_DIR)
 IF(CLUCENE_INCLUDE_DIR AND CLUCENE_LIBRARIES AND CLUCENE_LIBRARY_DIR AND CLUCENE_GOOD_VERSION)
   SET(CLucene_FOUND TRUE)
 ENDIF(CLUCENE_INCLUDE_DIR AND CLUCENE_LIBRARIES AND CLUCENE_LIBRARY_DIR AND CLUCENE_GOOD_VERSION)
+ENDIF(CLUCENEUNSTABLE_FOUND)
 
 IF(CLucene_FOUND)
   IF(NOT CLucene_FIND_QUIETLY)
diff --git a/CMakeModules/FindCLuceneUnstable.cmake b/CMakeModules/FindCLuceneUnstable.cmake
new file mode 100644
index 000000000..62643aa86
--- /dev/null
+++ b/CMakeModules/FindCLuceneUnstable.cmake
@@ -0,0 +1,37 @@
+# - Try to find clucene-unstable
+#  This is a workaround for distros, that want to ship a recent enough clucene but don't want to replace the old version
+#
+#  CLUCENEUNSTABLE_FOUND - system has clucene-unstable
+#  CLUCENE_UNSTABLE_INCLUDE_DIR - the clucene-unstable include directories
+#  CLUCENE_UNSTABLE_LIBS - link these to use clucene-unstable
+#
+# (c) Dominik Schmidt <dev@dominik-schmidt.de>
+#
+
+# Include dir
+find_path(CLUCENE_UNSTABLE_INCLUDE_DIR
+  NAMES CLucene.h
+  PATH_SUFFIXES clucene-unstable
+  PATHS ${KDE4_INCLUDE_DIR}
+)
+
+# Finally the library itself
+find_library(CLUCENE_UNSTABLE_SHARED_LIB
+  NAMES clucene-unstable-shared
+  PATHS ${KDE4_LIB_DIR}
+)
+
+find_library(CLUCENE_UNSTABLE_CORE_LIB
+  NAMES clucene-unstable-core
+  PATHS ${KDE4_LIB_DIR}
+)
+
+
+SET( CLUCENE_UNSTABLE_LIBS  ${CLUCENE_UNSTABLE_SHARED_LIB} ${CLUCENE_UNSTABLE_CORE_LIB} )
+SET( CLUCENE_UNSTABLE_INCLUDE_DIRS ${CLUCENE_UNSTABLE_INCLUDE_DIR})
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(CLuceneUnstable DEFAULT_MSG CLUCENE_UNSTABLE_LIBS CLUCENE_UNSTABLE_INCLUDE_DIRS)
+
+
+MARK_AS_ADVANCED(CLUCENE_UNSTABLE_LIBS CLUCENE_UNSTABLE_INCLUDE_DIRS)
+
diff --git a/ChangeLog b/ChangeLog
index 096ff0f30..15f431f6f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,12 +1,72 @@
 Version 0.3.0:
+    * Fixed bug where we would download http:// tracks twice.
+    * Make artist names in the album view clickable.
+    * Don't start playing if a tomahawk:// link was clicked while Tomahawk
+      is paused.
+    * Make artist name clickable in header of Album pages.
+    * Fixed adding social actions such as loved tracks from other sources.
+    * Added a drop shadow to cover images, and put CD placeholder in jewel
+      case.
+    * Added shuffle and repeat support to tree view.
+    * Draw a speaker next to the currently playing playlist.
+    * Refresh station previews whenever a filter is changed.
+    * Support and show official releases on album and track pages.
+    * Filter out duplicates from station previews and upcoming tracks.
+    * Resolve lists top-down.
+    * Added YouTube resolver.
+    * Fixed bug where going offline then online would not re-connect to many
+      peers.
+    * Added support for auto-updating live xspf playlists. 
+    * Don't show an age of 41 for tracks that have no age information.
+    * Show config UI for resolvers that have them as so on as you add the resolver.
+    * Add support for Echo Nest Personal Catalogs and User Radio. Synchronize
+      your catalog with The Echo Nest and enable personal recommendations
+      from you and your friends.
+    * Added support for Grooveshark resolver (requires Grooveshark Anywhere).
+    * Fixed re-resolving when resolvers or sources go offline.
+    * Correctly sort recently played tracks on the Dashboard.
+    * Show a Lion full-screen toggle button if running on Lion.
+    * Display list of who is currently listening along to you.
+    * Show headphone icon in source item to allow users to listen along; paint
+      headphones red on a source if the user is listening along to it.
+    * Added new job status view in the bottom of the source list that displays
+      current jobs such as resolving, parsing playlists, and loading from
+      database.
+    * Parse and convert a Spotify playlist url when dropped anywhere on the
+      sidebar
+    * Convert resolvers to use asynchronous calls to avoid blocking Tomahawk's
+      UI, greatly increasing responsiveness of Tomahawk while resolving.
+    * Fixed no playlists overlay not disappearing when playlists were added.
+    * Add support for parsing itunes track, artist and album links.
+    * Fixed crash when syncing playlists with peers.
+    * Add support for browsing, downloading and rating resolvers
+      from inside Tomahawk directly.
+    * Support multi-folder selection and scanning.
+    * Actually remove deleted files from the collection.
+    * Fixed handling of special characters in tomahawk:// links
+    * Improve sidebar performance by caching pixmaps and shrinking them.
+    * Send updated playlists to peers when tracks are moved/copied.
+    * Remove splitter handles in sidebar
     * Fixed Tomahawk preventing system shutdown / logut.
     * Ignore leading 'The' when sorting artists.
     * Added Charts page, which shows various sources' top hits & artists.
     * The Collection tree-views can now be filtered.
+    * Fixed crash when pressing enter in an empty playlist.
     * Moved the song queue below to the left, below the sidebar.
     * Added Footnotes, a contextual view that you can slide it.
     * Show recently added playlists in dashboard rather than recently opened
       playlists.
+    * Fixed seek slider and give it some smooth animation between ticks.
+    * Fixed Twitter issue where it would repeatedly send DMs to friends.
+    * Add a new drag and drop menu when dropping items onto playlists,
+      allowing users to drop the dragged tracks, the whole album, or
+      the whole artists's tracks.
+    * Bring Tomahawk window to the front when clicking a Tomahawk link.
+    * Fixed crash in source list when initially syncing with remote sources.
+    * Open temporary artist, album, and search playlists as temporary items
+      in the sidebar.
+    * Fixed sorting of playlists and items in the artist view.
+    * Allow dragging and dropping albums and artists to playlists.
     * Added MPRIS 2.1 support.
 
 Version 0.2.3:
diff --git a/README b/README
index d035b51a2..1c1b8701f 100644
--- a/README
+++ b/README
@@ -34,7 +34,7 @@ Dependencies
   TagLib 1.6.2 - http://developer.kde.org/~wheeler/taglib.html
   Boost 1.3 - http://www.boost.org/
   CLucene 0.9.23 (0.9.21 will fail) - http://clucene.sourceforge.net/download.shtml
-  libechonest 1.1.9 - http://projects.kde.org/projects/playground/libs/libechonest/
+  libechonest 1.2.0 - http://projects.kde.org/projects/playground/libs/libechonest/
 
  The following dependencies are optional, but recommended:
 
diff --git a/data/images/add.png b/data/images/add.png
index 14e3c7e84..59fa7fb1d 100644
Binary files a/data/images/add.png and b/data/images/add.png differ
diff --git a/data/images/album-shadow.png b/data/images/album-shadow.png
deleted file mode 100644
index a053655e2..000000000
Binary files a/data/images/album-shadow.png and /dev/null differ
diff --git a/data/images/back.png b/data/images/back.png
index 699eed76c..0c18ccf38 100644
Binary files a/data/images/back.png and b/data/images/back.png differ
diff --git a/data/images/charts.png b/data/images/charts.png
index f0e2f9472..6e56456a2 100644
Binary files a/data/images/charts.png and b/data/images/charts.png differ
diff --git a/data/images/close.png b/data/images/close.png
index 36da18e7d..5e3eaffc2 100644
Binary files a/data/images/close.png and b/data/images/close.png differ
diff --git a/data/images/collapse.png b/data/images/collapse.png
index 66a7e8618..b92bc558f 100644
Binary files a/data/images/collapse.png and b/data/images/collapse.png differ
diff --git a/data/images/cover-shadow.png b/data/images/cover-shadow.png
deleted file mode 100644
index e362118c6..000000000
Binary files a/data/images/cover-shadow.png and /dev/null differ
diff --git a/data/images/downloading.png b/data/images/downloading.png
index dd983f655..1856dec22 100644
Binary files a/data/images/downloading.png and b/data/images/downloading.png differ
diff --git a/data/images/drop-album.png b/data/images/drop-album.png
index bd4ff88e5..5e0568ea5 100644
Binary files a/data/images/drop-album.png and b/data/images/drop-album.png differ
diff --git a/data/images/drop-all-songs.png b/data/images/drop-all-songs.png
index 5e743fa5b..d8d511399 100644
Binary files a/data/images/drop-all-songs.png and b/data/images/drop-all-songs.png differ
diff --git a/data/images/drop-local-songs.png b/data/images/drop-local-songs.png
index 587cbffe5..61c34f22e 100644
Binary files a/data/images/drop-local-songs.png and b/data/images/drop-local-songs.png differ
diff --git a/data/images/drop-song.png b/data/images/drop-song.png
index 582ded327..7e25ab572 100644
Binary files a/data/images/drop-song.png and b/data/images/drop-song.png differ
diff --git a/data/images/drop-top-songs.png b/data/images/drop-top-songs.png
index e12f91052..22fff4da6 100644
Binary files a/data/images/drop-top-songs.png and b/data/images/drop-top-songs.png differ
diff --git a/data/images/filter.png b/data/images/filter.png
index ed48da97b..d2f1c2e35 100644
Binary files a/data/images/filter.png and b/data/images/filter.png differ
diff --git a/data/images/forward.png b/data/images/forward.png
index 72ad320ef..9b49c6687 100644
Binary files a/data/images/forward.png and b/data/images/forward.png differ
diff --git a/data/images/loved_playlist.png b/data/images/loved_playlist.png
index 7e2f4fd64..79c488e8e 100644
Binary files a/data/images/loved_playlist.png and b/data/images/loved_playlist.png differ
diff --git a/data/images/no-album-art-placeholder.png b/data/images/no-album-art-placeholder.png
index 3db3a8dd3..a117306e2 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
new file mode 100644
index 000000000..1fbbbb8bf
Binary files /dev/null and b/data/images/no-album-no-case.png differ
diff --git a/data/images/now-playing-speaker-dark.png b/data/images/now-playing-speaker-dark.png
index 6aeb9dae6..fd0fec99b 100644
Binary files a/data/images/now-playing-speaker-dark.png and b/data/images/now-playing-speaker-dark.png differ
diff --git a/data/images/open.png b/data/images/open.png
index 32542f05b..96bd0f982 100644
Binary files a/data/images/open.png and b/data/images/open.png differ
diff --git a/data/images/rdio.png b/data/images/rdio.png
new file mode 100644
index 000000000..1cb29a309
Binary files /dev/null and b/data/images/rdio.png differ
diff --git a/data/images/recently-played.png b/data/images/recently-played.png
index d82e7374e..956f761f2 100644
Binary files a/data/images/recently-played.png and b/data/images/recently-played.png differ
diff --git a/data/images/star-hover.png b/data/images/star-hover.png
index 89efe2aef..fcf452a46 100644
Binary files a/data/images/star-hover.png and b/data/images/star-hover.png differ
diff --git a/data/images/starred.png b/data/images/starred.png
index a93e9f6da..247c1308d 100644
Binary files a/data/images/starred.png and b/data/images/starred.png differ
diff --git a/data/images/station.png b/data/images/station.png
index d08a96397..9680fe8a1 100644
Binary files a/data/images/station.png and b/data/images/station.png differ
diff --git a/data/images/track-placeholder.png b/data/images/track-placeholder.png
index fbc578124..2976d36d7 100644
Binary files a/data/images/track-placeholder.png and b/data/images/track-placeholder.png differ
diff --git a/data/images/uploading.png b/data/images/uploading.png
index 9b4455138..45cea76a7 100644
Binary files a/data/images/uploading.png and b/data/images/uploading.png differ
diff --git a/data/images/user-avatar.png b/data/images/user-avatar.png
index a82081b31..75059d551 100644
Binary files a/data/images/user-avatar.png and b/data/images/user-avatar.png differ
diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts
index 9a4aebce4..558d60af2 100644
--- a/lang/tomahawk_de.ts
+++ b/lang/tomahawk_de.ts
@@ -6,13 +6,13 @@
     <message>
         <location filename="src/libtomahawk/playlist/albummodel.cpp" line="145"/>
         <source>Album</source>
-        <translation type="unfinished">Album</translation>
+        <translation>Album</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/albummodel.cpp" line="247"/>
         <location filename="src/libtomahawk/playlist/albummodel.cpp" line="269"/>
         <source>All albums from %1</source>
-        <translation type="unfinished">Alle Alben von %1</translation>
+        <translation>Alle Alben von %1</translation>
     </message>
 </context>
 <context>
@@ -20,27 +20,27 @@
     <message>
         <location filename="src/audiocontrols.ui" line="32"/>
         <source>Form</source>
-        <translation type="unfinished"></translation>
+        <translation></translation>
     </message>
     <message>
         <location filename="src/audiocontrols.ui" line="78"/>
         <source>Prev</source>
-        <translation type="unfinished"></translation>
+        <translation>Zurück</translation>
     </message>
     <message>
         <location filename="src/audiocontrols.ui" line="85"/>
         <source>Play</source>
-        <translation type="unfinished"></translation>
+        <translation>Abspielen</translation>
     </message>
     <message>
         <location filename="src/audiocontrols.ui" line="92"/>
         <source>Pause</source>
-        <translation type="unfinished"></translation>
+        <translation>Pause</translation>
     </message>
     <message>
         <location filename="src/audiocontrols.ui" line="99"/>
         <source>Next</source>
-        <translation type="unfinished"></translation>
+        <translation>Weiter</translation>
     </message>
     <message>
         <location filename="src/audiocontrols.ui" line="161"/>
@@ -50,47 +50,47 @@
     <message>
         <location filename="src/audiocontrols.ui" line="213"/>
         <source>Artist</source>
-        <translation type="unfinished"></translation>
+        <translation>Künstler</translation>
     </message>
     <message>
         <location filename="src/audiocontrols.ui" line="235"/>
         <source>Album</source>
-        <translation type="unfinished"></translation>
+        <translation>Album</translation>
     </message>
     <message>
         <location filename="src/audiocontrols.ui" line="262"/>
         <source>Owner</source>
-        <translation type="unfinished"></translation>
+        <translation>Eigentümer</translation>
     </message>
     <message>
         <location filename="src/audiocontrols.ui" line="295"/>
         <source>Time</source>
-        <translation type="unfinished"></translation>
+        <translation>Zeit</translation>
     </message>
     <message>
         <location filename="src/audiocontrols.ui" line="321"/>
         <source>Time Left</source>
-        <translation type="unfinished"></translation>
+        <translation>Zeit verbleibend</translation>
     </message>
     <message>
         <location filename="src/audiocontrols.ui" line="396"/>
         <source>Shuffle</source>
-        <translation type="unfinished"></translation>
+        <translation>Zufall</translation>
     </message>
     <message>
         <location filename="src/audiocontrols.ui" line="409"/>
         <source>Repeat</source>
-        <translation type="unfinished"></translation>
+        <translation>Wiederholen</translation>
     </message>
     <message>
         <location filename="src/audiocontrols.ui" line="447"/>
         <source>Low</source>
-        <translation type="unfinished"></translation>
+        <translation>Niedrig</translation>
     </message>
     <message>
         <location filename="src/audiocontrols.ui" line="479"/>
         <source>High</source>
-        <translation type="unfinished"></translation>
+        <translation>Hoch</translation>
     </message>
 </context>
 <context>
@@ -98,7 +98,7 @@
     <message>
         <location filename="src/libtomahawk/playlist/topbar/clearbutton.cpp" line="38"/>
         <source>Clear</source>
-        <translation type="unfinished">Leeren</translation>
+        <translation>Leeren</translation>
     </message>
 </context>
 <context>
@@ -106,12 +106,12 @@
     <message>
         <location filename="src/libtomahawk/playlist/collectionflatmodel.cpp" line="86"/>
         <source>Your Collection</source>
-        <translation type="unfinished">Deine Sammlung</translation>
+        <translation>Deine Sammlung</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/collectionflatmodel.cpp" line="88"/>
         <source>Collection of %1</source>
-        <translation type="unfinished">Deine Sammlung von %1</translation>
+        <translation>Deine Sammlung von %1</translation>
     </message>
 </context>
 <context>
@@ -160,7 +160,7 @@
     <message>
         <location filename="src/libtomahawk/playlist/infobar/infobar.ui" line="26"/>
         <source>InfoBar</source>
-        <translation type="unfinished"></translation>
+        <translation>Infoleiste</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/infobar/infobar.ui" line="54"/>
@@ -195,12 +195,12 @@
     <message>
         <location filename="src/sip/jabber/jabber_p.cpp" line="641"/>
         <source>Authorize User</source>
-        <translation type="unfinished">Benutzer authorisieren</translation>
+        <translation>Benutzer authorisieren</translation>
     </message>
     <message>
         <location filename="src/sip/jabber/jabber_p.cpp" line="642"/>
         <source>Do you want to grant &lt;b&gt;%1&lt;/b&gt; access to your Collection?</source>
-        <translation type="unfinished">Willst du &lt;b&gt;%1&lt;/b&gt; wirklich den Zugriff auf deine Sammlung erlauben?</translation>
+        <translation>Willst du &lt;b&gt;%1&lt;/b&gt; wirklich den Zugriff auf deine Sammlung erlauben?</translation>
     </message>
 </context>
 <context>
@@ -236,7 +236,7 @@
     <message>
         <location filename="src/libtomahawk/widgets/welcomewidget.cpp" line="203"/>
         <source>%1 tracks</source>
-        <translation type="unfinished">%1 Stücke</translation>
+        <translation>%1 Stücke</translation>
     </message>
 </context>
 <context>
@@ -244,12 +244,12 @@
     <message>
         <location filename="src/libtomahawk/playlist/playlistmanager.cpp" line="363"/>
         <source>All available tracks</source>
-        <translation type="unfinished">Alle verfügbaren Stücke</translation>
+        <translation>Alle verfügbaren Stücke</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/playlistmanager.cpp" line="364"/>
         <source>All available albums</source>
-        <translation type="unfinished">Alle verfügbaren Alben</translation>
+        <translation>Alle verfügbaren Alben</translation>
     </message>
 </context>
 <context>
@@ -257,12 +257,12 @@
     <message>
         <location filename="src/libtomahawk/playlist/playlistmodel.cpp" line="86"/>
         <source>A playlist by %1</source>
-        <translation type="unfinished">Eine Playliste von %1</translation>
+        <translation>Eine Playliste von %1</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/playlistmodel.cpp" line="86"/>
         <source>you</source>
-        <translation type="unfinished">dir</translation>
+        <translation>dir</translation>
     </message>
 </context>
 <context>
@@ -290,7 +290,7 @@
     <message>
         <location filename="src/libtomahawk/playlist/playlistview.cpp" line="143"/>
         <source>This playlist is currently empty. Add some tracks to it and enjoy the music!</source>
-        <translation>Die Playliste ist derzeit leer. Füge einige Stücke hinzu und genieße die Musik!</translation>
+        <translation>Diese Playliste ist derzeit leer. Füge einige Stücke hinzu und genieße die Musik!</translation>
     </message>
 </context>
 <context>
@@ -298,32 +298,32 @@
     <message>
         <location filename="src/proxydialog.ui" line="17"/>
         <source>Proxy Settings</source>
-        <translation type="unfinished">Proxy-Einstellungen</translation>
+        <translation>Proxy-Einstellungen</translation>
     </message>
     <message>
         <location filename="src/proxydialog.ui" line="91"/>
         <source>Host</source>
-        <translation type="unfinished">Rechnername</translation>
+        <translation>Rechnername</translation>
     </message>
     <message>
         <location filename="src/proxydialog.ui" line="98"/>
         <source>Port</source>
-        <translation type="unfinished">Port</translation>
+        <translation>Port</translation>
     </message>
     <message>
         <location filename="src/proxydialog.ui" line="111"/>
         <source>User</source>
-        <translation type="unfinished">Benutzer</translation>
+        <translation>Benutzer</translation>
     </message>
     <message>
         <location filename="src/proxydialog.ui" line="118"/>
         <source>Password</source>
-        <translation type="unfinished">Passwort</translation>
+        <translation>Passwort</translation>
     </message>
     <message>
         <location filename="src/proxydialog.ui" line="132"/>
         <source>Type</source>
-        <translation type="unfinished">Typ</translation>
+        <translation>Typ</translation>
     </message>
 </context>
 <context>
@@ -332,12 +332,12 @@
         <location filename="src/libtomahawk/playlist/queueview.cpp" line="51"/>
         <location filename="src/libtomahawk/playlist/queueview.cpp" line="90"/>
         <source>Click to show queue</source>
-        <translation type="unfinished">Klicke hier, um die Warteschlange anzuzeigen</translation>
+        <translation>Klicke hier, um die Warteschlange anzuzeigen</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/queueview.cpp" line="75"/>
         <source>Click to hide queue</source>
-        <translation type="unfinished">Klicke hier, um die Warteschlange auszublenden</translation>
+        <translation>Klicke hier, um die Warteschlange auszublenden</translation>
     </message>
 </context>
 <context>
@@ -345,7 +345,7 @@
     <message>
         <location filename="src/libtomahawk/playlist/topbar/searchlineedit.cpp" line="56"/>
         <source>Search</source>
-        <translation type="unfinished">Suchen</translation>
+        <translation>Suchen</translation>
     </message>
 </context>
 <context>
@@ -354,7 +354,7 @@
         <location filename="src/settingsdialog.ui" line="20"/>
         <source>Music Player Settings</source>
         <translatorcomment>Übersetzung eher dürftig</translatorcomment>
-        <translation type="unfinished">Einstellungen für das Musikabspielprogramm</translation>
+        <translation>Einstellungen für das Musikabspielprogramm</translation>
     </message>
     <message>
         <location filename="src/settingsdialog.ui" line="30"/>
@@ -375,7 +375,7 @@
     <message>
         <location filename="src/settingsdialog.ui" line="132"/>
         <source>Advanced Jabber Settings</source>
-        <translation type="unfinished">Erweiterte Einstellungen für Jabber</translation>
+        <translation>Erweiterte Einstellungen für Jabber</translation>
     </message>
     <message>
         <location filename="src/settingsdialog.ui" line="152"/>
@@ -565,7 +565,7 @@
     <message>
         <location filename="src/sourcetree/sourcetreeitem.cpp" line="48"/>
         <source>Super Collection</source>
-        <translation type="unfinished">Komplettsammlung</translation>
+        <translation>Komplettsammlung</translation>
     </message>
 </context>
 <context>
@@ -573,7 +573,7 @@
     <message>
         <location filename="src/sourcetree/sourcetreeitemwidget.ui" line="20"/>
         <source>Form</source>
-        <translation type="unfinished"></translation>
+        <translation>Form</translation>
     </message>
     <message>
         <location filename="src/sourcetree/sourcetreeitemwidget.ui" line="69"/>
@@ -586,67 +586,67 @@
     <message>
         <location filename="src/sourcetree/sourcetreeitemwidget.ui" line="147"/>
         <source>Off</source>
-        <translation type="unfinished"></translation>
+        <translation>Aus</translation>
     </message>
     <message>
         <location filename="src/sourcetree/sourcetreeitemwidget.ui" line="175"/>
         <source>Info</source>
-        <translation type="unfinished"></translation>
+        <translation>Info</translation>
     </message>
     <message>
         <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="57"/>
         <source>Super Collection</source>
-        <translation type="unfinished">Komplettsammlung</translation>
+        <translation>Komplettsammlung</translation>
     </message>
     <message>
         <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="58"/>
         <source>All available tracks</source>
-        <translation type="unfinished">Alle verfügbaren Stücke</translation>
+        <translation>Alle verfügbaren Stücke</translation>
     </message>
     <message>
         <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="85"/>
         <source>Idle</source>
-        <translation type="unfinished"></translation>
+        <translation>Warte</translation>
     </message>
     <message>
         <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="128"/>
         <source>%L1 tracks</source>
-        <translation type="unfinished">%L1 Stücke</translation>
+        <translation>%L1 Stücke</translation>
     </message>
     <message>
         <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="139"/>
         <source>Checking</source>
-        <translation type="unfinished">Teste</translation>
+        <translation>Überprüfe</translation>
     </message>
     <message>
         <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="142"/>
         <source>Fetching</source>
-        <translation type="unfinished">Hole</translation>
+        <translation>Sammle</translation>
     </message>
     <message>
         <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="145"/>
         <source>Parsing</source>
-        <translation type="unfinished">Parse</translation>
+        <translation>Parse</translation>
     </message>
     <message>
         <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="148"/>
         <source>Saving</source>
-        <translation type="unfinished">Speichere</translation>
+        <translation>Speichere</translation>
     </message>
     <message>
         <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="151"/>
         <source>Synced</source>
-        <translation type="unfinished">Synchronisiert</translation>
+        <translation>Synchronisiert</translation>
     </message>
     <message>
         <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="154"/>
         <source>Scanning (%L1 tracks)</source>
-        <translation type="unfinished">Durchsuche (%L1 Stücke)</translation>
+        <translation>Durchsuche (%L1 Stücke)</translation>
     </message>
     <message>
         <location filename="src/sourcetree/sourcetreeitemwidget.cpp" line="183"/>
         <source>Offline</source>
-        <translation type="unfinished">Nicht Verbunden</translation>
+        <translation>Nicht verbunden</translation>
     </message>
 </context>
 <context>
@@ -672,7 +672,7 @@
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/widgets/DynamicControlList.cpp" line="73"/>
         <source>Click to collapse</source>
-        <translation type="unfinished">Klicken zum Zusammenfalten</translation>
+        <translation>Klicken um einzuklappen</translation>
     </message>
 </context>
 <context>
@@ -727,7 +727,7 @@ Bitte ändere den Filter oder versuche es erneut.</translation>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="240"/>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="259"/>
         <source>is</source>
-        <translation type="unfinished">ist</translation>
+        <translation>ist</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="178"/>
@@ -737,7 +737,7 @@ Bitte ändere den Filter oder versuche es erneut.</translation>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="222"/>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="226"/>
         <source>Less</source>
-        <translation type="unfinished">Kleiner</translation>
+        <translation>Kleiner</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="178"/>
@@ -747,178 +747,180 @@ Bitte ändere den Filter oder versuche es erneut.</translation>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="222"/>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="226"/>
         <source>More</source>
-        <translation type="unfinished">Größer</translation>
+        <translation>Größer</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="197"/>
         <source>0 BPM</source>
-        <translation type="unfinished"></translation>
+        <translation>0 BPM</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="197"/>
         <source>500 BPM</source>
-        <translation type="unfinished"></translation>
+        <translation>500 BPM</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="201"/>
         <source>0 secs</source>
-        <translation type="unfinished">0 s</translation>
+        <translation>0 sec</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="201"/>
         <source>3600 secs</source>
-        <translation type="unfinished">3600 s</translation>
+        <translation>3600 sec</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="205"/>
         <source>-100 dB</source>
-        <translation type="unfinished"></translation>
+        <translation>-100 dB</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="205"/>
         <source>100 dB</source>
-        <translation type="unfinished"></translation>
+        <translation>100 dB</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="242"/>
         <source>Major</source>
-        <translation type="unfinished">Dur</translation>
+        <translation>Dur</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="243"/>
         <source>Minor</source>
-        <translation type="unfinished">Moll</translation>
+        <translation>Moll</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="261"/>
         <source>C</source>
-        <translation type="unfinished">C</translation>
+        <translation>C</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="262"/>
         <source>C Sharp</source>
-        <translation type="unfinished">Cis</translation>
+        <translation>Cis</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="263"/>
         <source>D</source>
-        <translation type="unfinished">D</translation>
+        <translation>D</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="264"/>
         <source>E Flat</source>
-        <translation type="unfinished">Es</translation>
+        <translation>Es</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="265"/>
         <source>E</source>
-        <translation type="unfinished">E</translation>
+        <translation>E</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="266"/>
         <source>F</source>
-        <translation type="unfinished">F</translation>
+        <translation>F</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="267"/>
         <source>F Sharp</source>
-        <translation type="unfinished">Fis</translation>
+        <translation>Fis</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="268"/>
         <source>G</source>
-        <translation type="unfinished">G</translation>
+        <translation>G</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="269"/>
         <source>A Flat</source>
-        <translation type="unfinished">As</translation>
+        <translation>As</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="270"/>
         <source>A</source>
-        <translation type="unfinished">A</translation>
+        <translation>A</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="271"/>
         <source>B Flat</source>
-        <translatorcomment>stimmt das?</translatorcomment>
-        <translation type="unfinished">B</translation>
+        <translation>B</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="272"/>
         <source>B</source>
-        <translation type="unfinished">H</translation>
+        <translation>H</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="289"/>
         <source>Ascending</source>
-        <translation type="unfinished">Aufsteigend</translation>
+        <translation>Aufsteigend</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="290"/>
         <source>Descending</source>
-        <translation type="unfinished">Absteigend</translation>
+        <translation>Absteigend</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="293"/>
         <source>Tempo</source>
-        <translation type="unfinished">Tempo</translation>
+        <translation>Tempo</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="294"/>
         <source>Duration</source>
-        <translation type="unfinished">Dauer</translation>
+        <translation>Dauer</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="295"/>
         <source>Loudness</source>
-        <translation type="unfinished">Lautstärke</translation>
+        <translation>Lautstärke</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="296"/>
         <source>Artist Familiarity</source>
-        <translation type="unfinished"></translation>
+    <translatorcomment>nicht sicher ob das passt</translatorcomment>
+        <translation>Ähnlichkeit</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="297"/>
         <source>Artist Hotttnesss</source>
+	<translatorcomment>keine ahnung</translatorcomment>
         <translation type="unfinished"></translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="298"/>
         <source>Song Hotttnesss</source>
+	<translatorcomment>keine ahnung</translatorcomment>
         <translation type="unfinished"></translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="299"/>
         <source>Latitude</source>
-        <translation type="unfinished">Breitengrad</translation>
+        <translation>Breitengrad</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="300"/>
         <source>Longitude</source>
-        <translation type="unfinished">Längengrad</translation>
+        <translation>Längengrad</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="301"/>
         <source>Mode</source>
-        <translation type="unfinished">Modus</translation>
+        <translation>Modus</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="302"/>
         <source>Key</source>
-        <translation type="unfinished">Schlüssel</translation>
+        <translation>Schlüssel</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="303"/>
         <source>Energy</source>
-        <translation type="unfinished">Energie</translation>
+        <translation>Energie</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp" line="304"/>
         <source>Danceability</source>
-        <translation type="unfinished">Tanzbarkeit</translation>
+        <translation>Tanzbarkeit</translation>
     </message>
 </context>
 <context>
@@ -926,97 +928,100 @@ Bitte ändere den Filter oder versuche es erneut.</translation>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="54"/>
         <source>Steer this station:</source>
-        <translation type="unfinished">Steuere diese Station:</translation>
+        <translation>Steuere diese Station:</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="60"/>
         <source>Takes effect on track change</source>
-        <translation type="unfinished">Wird nach dem Wechsel eines Stückes aktiv</translation>
+        <translation>Wird nach dem Wechsel eines Stückes aktiv</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="68"/>
         <source>Much less</source>
-        <translation type="unfinished">Viel Weniger</translation>
+        <translation>Viel weniger</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="69"/>
         <source>Less</source>
-        <translation type="unfinished">Weniger</translation>
+        <translation>Weniger</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="70"/>
         <source>A bit less</source>
-        <translation type="unfinished">Etwas Weniger</translation>
+        <translation>Etwas weniger</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="71"/>
         <source>Keep at current</source>
-        <translation type="unfinished">So belassen</translation>
+        <translation>So belassen</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="72"/>
         <source>A bit more</source>
-        <translation type="unfinished">Etwas Mehr</translation>
+        <translation>Etwas mehr</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="73"/>
         <source>More</source>
-        <translation type="unfinished">Mehr</translation>
+        <translation>Mehr</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="74"/>
         <source>Much more</source>
-        <translation type="unfinished">Viel Mehr</translation>
+        <translation>Viel mehr</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="77"/>
         <source>Tempo</source>
-        <translation type="unfinished">Tempo</translation>
+        <translation>Tempo</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="78"/>
         <source>Loudness</source>
-        <translation type="unfinished">Lautstärke</translation>
+        <translation>Lautstärke</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="79"/>
         <source>Danceability</source>
-        <translation type="unfinished">Tanzbarkeit</translation>
+        <translation>Tanzbarkeit</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="80"/>
         <source>Energy</source>
-        <translation type="unfinished">Energie</translation>
+        <translation>Energie</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="81"/>
         <source>Song Hotttnesss</source>
+	<translatorcomment>keine ahnung</translatorcomment>
         <translation type="unfinished"></translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="82"/>
         <source>Artist Hotttnesss</source>
+	<translatorcomment>keine ahnung</translatorcomment>
         <translation type="unfinished"></translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="83"/>
         <source>Artist Familiarity</source>
-        <translation type="unfinished"></translation>
+	<translatorcomment>nicht sicher ob das stimmt</translatorcomment>
+        <translation>Ähnlichkeit Künstler</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="84"/>
         <source>By Description</source>
-        <translation type="unfinished">Von der Beschreibung</translation>
+        <translation>Von der Beschreibung</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="92"/>
         <source>Enter a description</source>
-        <translation type="unfinished">Gib eine Beschreibung ein</translation>
+        <translation>Gib eine Beschreibung ein</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/dynamic/echonest/EchonestSteerer.cpp" line="99"/>
         <source>Reset all steering commands</source>
-        <translation type="unfinished">Setze alle Steuerkommandos zurück</translation>
+        <translation>Setze alle Steuerkommandos zurück</translation>
     </message>
 </context>
 <context>
@@ -1025,27 +1030,27 @@ Bitte ändere den Filter oder versuche es erneut.</translation>
         <location filename="src/libtomahawk/source.cpp" line="176"/>
         <location filename="src/libtomahawk/source.cpp" line="211"/>
         <source>Scanning (%L1 tracks)</source>
-        <translation type="unfinished">Scanne (%L1 Stücke)</translation>
+        <translation>Scanne (%L1 Stücke)</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/source.cpp" line="196"/>
         <source>Checking</source>
-        <translation type="unfinished">Teste</translation>
+        <translation>Überprüfe</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/source.cpp" line="199"/>
         <source>Fetching</source>
-        <translation type="unfinished">Hole</translation>
+        <translation>Sammle</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/source.cpp" line="202"/>
         <source>Parsing</source>
-        <translation type="unfinished">Parse</translation>
+        <translation>Parse</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/source.cpp" line="205"/>
         <source>Saving</source>
-        <translation type="unfinished">Speichere</translation>
+        <translation>Speichere</translation>
     </message>
 </context>
 <context>
@@ -1053,37 +1058,37 @@ Bitte ändere den Filter oder versuche es erneut.</translation>
     <message>
         <location filename="src/tomahawktrayicon.cpp" line="42"/>
         <source>Play</source>
-        <translation type="unfinished">Abspielen</translation>
+        <translation>Abspielen</translation>
     </message>
     <message>
         <location filename="src/tomahawktrayicon.cpp" line="43"/>
         <source>Pause</source>
-        <translation type="unfinished">Pause</translation>
+        <translation>Pause</translation>
     </message>
     <message>
         <location filename="src/tomahawktrayicon.cpp" line="44"/>
         <source>Stop</source>
-        <translation type="unfinished">Anhalten</translation>
+        <translation>Anhalten</translation>
     </message>
     <message>
         <location filename="src/tomahawktrayicon.cpp" line="46"/>
         <source>Previous Track</source>
-        <translation type="unfinished">Vorheriges Stück</translation>
+        <translation>Vorheriges Stück</translation>
     </message>
     <message>
         <location filename="src/tomahawktrayicon.cpp" line="47"/>
         <source>Next Track</source>
-        <translation type="unfinished">Nächstes Stück</translation>
+        <translation>Nächstes Stück</translation>
     </message>
     <message>
         <location filename="src/tomahawktrayicon.cpp" line="49"/>
         <source>Quit</source>
-        <translation type="unfinished">Verlassen</translation>
+        <translation>Verlassen</translation>
     </message>
     <message>
         <location filename="src/tomahawktrayicon.cpp" line="97"/>
         <source>Currently not playing.</source>
-        <translation type="unfinished">Derzeit wird nichts gespielt.</translation>
+        <translation>Derzeit wird nichts gespielt.</translation>
     </message>
 </context>
 <context>
@@ -1142,7 +1147,7 @@ Bitte ändere den Filter oder versuche es erneut.</translation>
     <message>
         <location filename="src/tomahawkwindow.ui" line="111"/>
         <source>Re&amp;scan Collection...</source>
-        <translation type="unfinished">Sammlung neu&amp;laden…</translation>
+        <translation>Sammlung neu&amp;laden…</translation>
     </message>
     <message>
         <location filename="src/tomahawkwindow.ui" line="116"/>
@@ -1188,7 +1193,7 @@ Bitte ändere den Filter oder versuche es erneut.</translation>
         <location filename="src/tomahawkwindow.cpp" line="150"/>
         <location filename="src/tomahawkwindow.cpp" line="166"/>
         <source>Check for updates...</source>
-        <translation type="unfinished">Suche nach Updates…</translation>
+        <translation>Suche nach Updates…</translation>
     </message>
     <message>
         <location filename="src/tomahawkwindow.cpp" line="170"/>
@@ -1203,7 +1208,7 @@ Bitte ändere den Filter oder versuche es erneut.</translation>
     <message>
         <location filename="src/tomahawkwindow.cpp" line="172"/>
         <source>Home</source>
-        <translation type="unfinished"></translation>
+        <translation>Anfang</translation>
     </message>
     <message>
         <location filename="src/tomahawkwindow.cpp" line="332"/>
@@ -1245,7 +1250,7 @@ Bitte ändere den Filter oder versuche es erneut.</translation>
     <message>
         <location filename="src/tomahawkwindow.cpp" line="506"/>
         <source>&lt;h2&gt;&lt;b&gt;Tomahawk %1&lt;/h2&gt;Copyright 2010, 2011&lt;br/&gt;Christian Muehlhaeuser &amp;lt;muesli@tomahawk-player.org&amp;gt;&lt;br/&gt;&lt;br/&gt;Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Harald Sitter and Steve Robertson</source>
-        <translation type="unfinished"></translation>
+        <translation>&lt;h2&gt;&lt;b&gt;Tomahawk %1&lt;/h2&gt;Copyright 2010, 2011&lt;br/&gt;Christian Muehlhaeuser &amp;lt;muesli@tomahawk-player.org&amp;gt;&lt;br/&gt;&lt;br/&gt;Danke an: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Harald Sitter und Steve Robertson</translation>
     </message>
 </context>
 <context>
@@ -1253,47 +1258,47 @@ Bitte ändere den Filter oder versuche es erneut.</translation>
     <message>
         <location filename="src/libtomahawk/playlist/topbar/topbar.ui" line="26"/>
         <source>Form</source>
-        <translation type="unfinished"></translation>
+        <translation>Form</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/topbar/topbar.ui" line="66"/>
         <source>0 Sources</source>
-        <translation type="unfinished"></translation>
+        <translation>0 Quellen</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/topbar/topbar.ui" line="89"/>
         <source>0 Tracks</source>
-        <translation type="unfinished"></translation>
+        <translation>0 Titel</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/topbar/topbar.ui" line="112"/>
         <source>0 Artists</source>
-        <translation type="unfinished"></translation>
+        <translation>0 Künstler</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/topbar/topbar.ui" line="135"/>
         <source>0 Shown</source>
-        <translation type="unfinished"></translation>
+        <translation>0 angezeigt</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/topbar/topbar.cpp" line="45"/>
         <source>Tracks</source>
-        <translation type="unfinished">Stücke</translation>
+        <translation>Stücke</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/topbar/topbar.cpp" line="46"/>
         <source>Artists</source>
-        <translation type="unfinished">Künstler</translation>
+        <translation>Künstler</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/topbar/topbar.cpp" line="182"/>
         <source>Sources</source>
-        <translation type="unfinished">Quellen</translation>
+        <translation>Quellen</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/topbar/topbar.cpp" line="234"/>
         <source>Shown</source>
-        <translation type="unfinished">Angezeigt</translation>
+        <translation>Angezeigt</translation>
     </message>
 </context>
 <context>
@@ -1301,47 +1306,47 @@ Bitte ändere den Filter oder versuche es erneut.</translation>
     <message>
         <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/>
         <source>Artist</source>
-        <translation type="unfinished">Künstler</translation>
+        <translation>Künstler</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/>
         <source>Track</source>
-        <translation type="unfinished">Titel</translation>
+        <translation>Titel</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/>
         <source>Album</source>
-        <translation type="unfinished">Album</translation>
+        <translation>Album</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/>
         <source>Duration</source>
-        <translation type="unfinished">Spieldauer</translation>
+        <translation>Spieldauer</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/>
         <source>Bitrate</source>
-        <translation type="unfinished">Bitrate</translation>
+        <translation>Bitrate</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/>
         <source>Age</source>
-        <translation type="unfinished">Alter</translation>
+        <translation>Alter</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/>
         <source>Year</source>
-        <translation type="unfinished">Jahr</translation>
+        <translation>Jahr</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/>
         <source>Size</source>
-        <translation type="unfinished">Größe</translation>
+        <translation>Größe</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/playlist/trackmodel.cpp" line="210"/>
         <source>Origin</source>
-        <translation type="unfinished">Quelle</translation>
+        <translation>Quelle</translation>
     </message>
 </context>
 <context>
@@ -1349,7 +1354,7 @@ Bitte ändere den Filter oder versuche es erneut.</translation>
     <message>
         <location filename="src/libtomahawk/playlist/trackview.cpp" line="325"/>
         <source>Sorry, your filter &apos;%1&apos; did not match any results.</source>
-        <translation type="unfinished">Entschuldige, dein Filter &apos;%1&apos; erzeugte keine Ergebnisse.</translation>
+        <translation>Entschuldige, dein Filter &apos;%1&apos; erzeugte keine Ergebnisse.</translation>
     </message>
 </context>
 <context>
@@ -1375,59 +1380,61 @@ Bitte ändere den Filter oder versuche es erneut.</translation>
     <message>
         <location filename="src/sip/twitter/twitterconfigwidget.ui" line="25"/>
         <source>Authenticating with Twitter allows you to discover and play music from your Twitter friends running Tomahawk.</source>
-        <translation type="unfinished"></translation>
+        <translation>Die Authentifizierung mit Twitter ermöglicht es dir Musik von den Twitter-Freunden zu hören, die Tomahawk verwenden</translation>
     </message>
     <message>
         <location filename="src/sip/twitter/twitterconfigwidget.ui" line="35"/>
         <source>This feature works best when you have set a static host name in the &quot;Network&quot; settings tab under Advanced Settings, but may work even if you do not. Tomahawk uses Direct Messages and this will only work when both Twitter users have followed each other.</source>
-        <translation type="unfinished"></translation>
+        <translation>Dies funktioniert am Besten, wenn du einen statischen Hostnamen im &quot;Netzwerk&quot;-Tab unter Erweiterte Netzwerkeinstellungen eingetragen hast. Es kann aber auch ohne funktionieren. Tomahawk verwendet Direktnachrichten, was voraussetzt, dass sich beide Twitternutzer gegenseitig folgen.</translation>
     </message>
     <message>
         <location filename="src/sip/twitter/twitterconfigwidget.ui" line="47"/>
         <source>Status: No saved credentials</source>
-        <translation type="unfinished"></translation>
+        <translation>Status: Keine gespeicherten Zugangsdaten</translation>
     </message>
     <message>
         <location filename="src/sip/twitter/twitterconfigwidget.ui" line="57"/>
         <source>Authenticate with Twitter</source>
-        <translation type="unfinished"></translation>
+        <translation>Mit Twitter authentifizieren</translation>
     </message>
     <message>
         <location filename="src/sip/twitter/twitterconfigwidget.ui" line="68"/>
         <source>Here&apos;s how it works: just press one of the buttons below to tweet &quot;Got Tomahawk?&quot; and some necessary information.  Then be (very) patient. Twitter is an asynchronous protocol so it can take a bit!
 
 If connections to peers seem to have been lost, just press the appropriate button again to re-post a tweet for resynchronization.</source>
-        <translation type="unfinished"></translation>
+        <translation>So geht&apos;s: klicke auf einen der Buttons unten um &quot;Got Tomahawk?&quot; und einige notwendige Informationen zu twittern.  Dann heisst es (sehr) geduldig sein. Twitter ist ein asynchrones Protokoll, also kann es etwas dauern!
+
+Sollte die Verbindung zu anderen Gegenstellen verloren gehen, klicke einfach erneut auf den entsprechenden Button um einen Tweet zur Resynchronisierung zu senden.</translation>
     </message>
     <message>
         <location filename="src/sip/twitter/twitterconfigwidget.ui" line="104"/>
         <source>Select the kind of tweet you would like, then press the button to post it:</source>
-        <translation type="unfinished"></translation>
+        <translation>Wähle die gewünschte Tweet-Form aus und klicke auf den Button um ihn zu senden:</translation>
     </message>
     <message>
         <location filename="src/sip/twitter/twitterconfigwidget.ui" line="120"/>
         <source>Global Tweet</source>
-        <translation type="unfinished"></translation>
+        <translation>Globaler Tweet</translation>
     </message>
     <message>
         <location filename="src/sip/twitter/twitterconfigwidget.ui" line="125"/>
         <source>@Mention</source>
-        <translation type="unfinished"></translation>
+        <translation>@Erwähnung</translation>
     </message>
     <message>
         <location filename="src/sip/twitter/twitterconfigwidget.ui" line="130"/>
         <source>Direct Message</source>
-        <translation type="unfinished"></translation>
+        <translation>Direktnachricht</translation>
     </message>
     <message>
         <location filename="src/sip/twitter/twitterconfigwidget.ui" line="150"/>
         <source>e.g. @tomahawkplayer</source>
-        <translation type="unfinished"></translation>
+        <translation>z.B. @tomahawkplayer</translation>
     </message>
     <message>
         <location filename="src/sip/twitter/twitterconfigwidget.ui" line="163"/>
         <source>Tweet!</source>
-        <translation type="unfinished"></translation>
+        <translation>Twittern!</translation>
     </message>
 </context>
 <context>
@@ -1458,27 +1465,27 @@ If connections to peers seem to have been lost, just press the appropriate butto
     <message>
         <location filename="src/libtomahawk/utils/xspfloader.cpp" line="118"/>
         <source>New Playlist</source>
-        <translation type="unfinished">Neue Playliste</translation>
+        <translation>Neue Playliste</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/utils/xspfloader.cpp" line="143"/>
         <source>Failed to save tracks</source>
-        <translation type="unfinished">Konnte Stücke nicht abspeichern</translation>
+        <translation>Konnte Stücke nicht abspeichern</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/utils/xspfloader.cpp" line="143"/>
         <source>Some tracks in the playlist do not contain an artist and a title. They will be ignored.</source>
-        <translation type="unfinished">Einige Stücke in der Playliste enthalten weder Künstler noch Titel. Diese werden ignoriert.</translation>
+        <translation>Einige Stücke in der Playliste enthalten weder Künstler noch Titel. Diese werden ignoriert.</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/utils/xspfloader.cpp" line="164"/>
         <source>XSPF Error</source>
-        <translation type="unfinished">XSPF-Fehler</translation>
+        <translation>XSPF-Fehler</translation>
     </message>
     <message>
         <location filename="src/libtomahawk/utils/xspfloader.cpp" line="164"/>
         <source>This is not a valid XSPF playlist.</source>
-        <translation type="unfinished">Dies ist keine valide XSPF-Playliste.</translation>
+        <translation>Dies ist keine gültige XSPF-Playliste.</translation>
     </message>
 </context>
-</TS>
+</TS>
\ No newline at end of file
diff --git a/resources.qrc b/resources.qrc
index 02ee5f9ab..369d4955d 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -4,7 +4,6 @@
         <file>data/images/avatar-dude.png</file>
         <file>data/images/back-pressed.png</file>
         <file>data/images/back-rest.png</file>
-        <file>data/images/cover-shadow.png</file>
         <file>data/images/filter.png</file>
         <file>data/images/loved.png</file>
         <file>data/images/not-loved.png</file>
@@ -112,7 +111,7 @@
         <file>data/sql/dbmigrate-23_to_24.sql</file>
         <file>data/sql/dbmigrate-24_to_25.sql</file>
         <file>data/sql/dbmigrate-25_to_26.sql</file>
-	<file>data/sql/dbmigrate-26_to_27.sql</file>
+        <file>data/sql/dbmigrate-26_to_27.sql</file>
         <file>data/js/tomahawk.js</file>
         <file>data/images/avatar_frame.png</file>
         <file>data/images/drop-all-songs.png</file>
@@ -120,7 +119,7 @@
         <file>data/images/drop-top-songs.png</file>
         <file>data/images/drop-song.png</file>
         <file>data/images/drop-album.png</file>
-	<file>data/images/spotify-logo.png</file>
+        <file>data/images/spotify-logo.png</file>
         <file>data/images/itunes.png</file>
         <file>data/images/uploading.png</file>
         <file>data/images/downloading.png</file>
@@ -128,5 +127,7 @@
         <file>data/images/headphones-off.png</file>
         <file>data/images/headphones-sidebar.png</file>
         <file>data/images/headphones-bigger.png</file>
+        <file>data/images/no-album-no-case.png</file>
+        <file>data/images/rdio.png</file>
     </qresource>
 </RCC>
diff --git a/src/CMakeLists.linux.txt b/src/CMakeLists.linux.txt
index 355144407..ae108e4d5 100644
--- a/src/CMakeLists.linux.txt
+++ b/src/CMakeLists.linux.txt
@@ -5,6 +5,6 @@ FOREACH( _file ${_icons} )
  INSTALL( FILES ${_file} RENAME tomahawk.png DESTINATION share/icons/hicolor/${_res}/apps )
 ENDFOREACH( _file )
 
-INSTALL( FILES ${CMAKE_SOURCE_DIR}/data/icons/tomahawk-icon.svg RENAME tomahawk.svg  DESTINATION share/icons/hicolor/scalable )
+INSTALL( FILES ${CMAKE_SOURCE_DIR}/data/icons/tomahawk-icon.svg RENAME tomahawk.svg  DESTINATION share/icons/hicolor/scalable/apps )
 
 INSTALL( FILES ${CMAKE_SOURCE_DIR}/admin/unix/tomahawk.desktop DESTINATION share/applications )
diff --git a/src/GetNewStuffDelegate.cpp b/src/GetNewStuffDelegate.cpp
index 5329e0591..1e1c5d381 100644
--- a/src/GetNewStuffDelegate.cpp
+++ b/src/GetNewStuffDelegate.cpp
@@ -40,6 +40,7 @@
 GetNewStuffDelegate::GetNewStuffDelegate( QObject* parent )
     : QStyledItemDelegate ( parent )
     , m_widestTextWidth( 0 )
+    , m_hoveringOver( -1 )
 {
     m_defaultCover.load( RESPATH "images/sipplugin-online.png" );
     m_ratingStarPositive.load( RESPATH "images/starred.png" );
@@ -190,8 +191,10 @@ GetNewStuffDelegate::paint( QPainter* painter, const QStyleOptionViewItem& optio
         if ( i == 1 )
             m_cachedStarRects[ QPair<int, int>(index.row(), index.column()) ] = r;
 
-        QPixmap pm;
-        if ( m_hoveringOver > -1 )
+        const bool userHasRated = index.data( GetNewStuffModel::UserHasRatedRole ).toBool();
+        if ( !userHasRated && // Show on-hover animation if the user hasn't rated it yet, and is hovering over it
+             m_hoveringOver > -1 &&
+             m_hoveringItem == index )
         {
             if ( i <= m_hoveringOver ) // positive star
                 painter->drawPixmap( r, m_onHoverStar );
@@ -200,8 +203,13 @@ GetNewStuffDelegate::paint( QPainter* painter, const QStyleOptionViewItem& optio
         }
         else
         {
-            if ( i <= rating ) // positive star
-                painter->drawPixmap( r, m_ratingStarPositive );
+            if ( i <= rating ) // positive or rated star
+            {
+                if ( userHasRated )
+                    painter->drawPixmap( r, m_onHoverStar );
+                else
+                    painter->drawPixmap( r, m_ratingStarPositive );
+            }
             else
                 painter->drawPixmap( r, m_ratingStarNegative );
         }
@@ -258,7 +266,6 @@ bool
 GetNewStuffDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index )
 {
     Q_UNUSED( option );
-    m_hoveringOver = -1;
 
     if ( event->type() != QEvent::MouseButtonRelease &&
          event->type() != QEvent::MouseMove )
@@ -287,25 +294,30 @@ GetNewStuffDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, cons
 
         if ( fullStars.contains( me->pos() ) )
         {
-            tDebug() << "A star was pressed...which one?";
-
             const int eachStar = starsWidth / 5;
             const int clickOffset = me->pos().x() - fullStars.x();
             const int whichStar = (clickOffset / eachStar) + 1;
 
             if ( event->type() == QEvent::MouseButtonRelease )
             {
-                tDebug() << "Clicked on:" << whichStar;
                 model->setData( index, whichStar, GetNewStuffModel::RatingRole );
             }
             else if ( event->type() == QEvent::MouseMove )
             {
                 // 0-indexed
                 m_hoveringOver = whichStar;
+                m_hoveringItem = index;
             }
 
             return true;
         }
     }
+
+    if ( m_hoveringOver > -1 )
+    {
+        emit update( m_hoveringItem );
+        m_hoveringOver = -1;
+        m_hoveringItem = QPersistentModelIndex();
+    }
     return false;
 }
diff --git a/src/GetNewStuffDelegate.h b/src/GetNewStuffDelegate.h
index e2b71afaa..570210226 100644
--- a/src/GetNewStuffDelegate.h
+++ b/src/GetNewStuffDelegate.h
@@ -31,6 +31,9 @@ public:
     virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
     virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const;
 
+signals:
+    void update( const QModelIndex& idx );
+
 protected:
     virtual bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index );
 
@@ -39,6 +42,7 @@ private:
 
     int m_widestTextWidth;
     int m_hoveringOver;
+    QPersistentModelIndex m_hoveringItem;
     mutable QHash< QPair<int, int>, QRect > m_cachedButtonRects;
     mutable QHash< QPair<int, int>, QRect > m_cachedStarRects;
 };
diff --git a/src/GetNewStuffDialog.cpp b/src/GetNewStuffDialog.cpp
index a0102c648..3a5997b26 100644
--- a/src/GetNewStuffDialog.cpp
+++ b/src/GetNewStuffDialog.cpp
@@ -30,7 +30,9 @@ GetNewStuffDialog::GetNewStuffDialog( QWidget *parent, Qt::WindowFlags f )
     ui->setupUi( this );
 
     ui->listView->setModel( m_model );
-    ui->listView->setItemDelegate( new GetNewStuffDelegate( ui->listView ) );
+    GetNewStuffDelegate* del = new GetNewStuffDelegate( ui->listView );
+    connect( del, SIGNAL( update( QModelIndex ) ), ui->listView, SLOT( update( QModelIndex ) ) );
+    ui->listView->setItemDelegate( del );
     ui->listView->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
 
     ui->listView->setMouseTracking( true );
diff --git a/src/GetNewStuffModel.cpp b/src/GetNewStuffModel.cpp
index cb2aa99cc..405ca968b 100644
--- a/src/GetNewStuffModel.cpp
+++ b/src/GetNewStuffModel.cpp
@@ -94,6 +94,8 @@ GetNewStuffModel::data( const QModelIndex& index, int role ) const
             return resolver.author();
         case StateRole:
             return (int)AtticaManager::instance()->resolverState( resolver );
+        case UserHasRatedRole:
+            return AtticaManager::instance()->userHasRated( resolver );
     }
     return QVariant();
 }
diff --git a/src/GetNewStuffModel.h b/src/GetNewStuffModel.h
index 60e2e9104..378057e1e 100644
--- a/src/GetNewStuffModel.h
+++ b/src/GetNewStuffModel.h
@@ -38,7 +38,8 @@ public:
         DescriptionRole = Qt::UserRole + 5,
         TypeRole = Qt::UserRole + 6, // Category in attica-speak. What sort of item this is (resolver, etc).
         AuthorRole = Qt::UserRole + 7,
-        StateRole = Qt::UserRole + 8
+        StateRole = Qt::UserRole + 8,
+        UserHasRatedRole = Qt::UserRole + 9
     };
 
     enum Types {
diff --git a/src/audiocontrols.cpp b/src/audiocontrols.cpp
index 0139b3145..42f8d7ab0 100644
--- a/src/audiocontrols.cpp
+++ b/src/audiocontrols.cpp
@@ -128,7 +128,7 @@ AudioControls::AudioControls( QWidget* parent )
     connect( AudioEngine::instance(), SIGNAL( timerMilliSeconds( qint64 ) ), SLOT( onPlaybackTimer( qint64 ) ) );
     connect( AudioEngine::instance(), SIGNAL( volumeChanged( int ) ), SLOT( onVolumeChanged( int ) ) );
 
-    m_defaultCover = QPixmap( RESPATH "images/no-album-art-placeholder.png" )
+    m_defaultCover = QPixmap( RESPATH "images/no-album-no-case.png" )
                      .scaled( ui->coverImage->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
 
     connect( Tomahawk::InfoSystem::InfoSystem::instance(),
diff --git a/src/libtomahawk/AtticaManager.cpp b/src/libtomahawk/AtticaManager.cpp
index 55b5e11b3..5303fcd17 100644
--- a/src/libtomahawk/AtticaManager.cpp
+++ b/src/libtomahawk/AtticaManager.cpp
@@ -45,7 +45,6 @@ AtticaManager::AtticaManager( QObject* parent )
 
     // resolvers
     m_manager.addProviderFile( QUrl( "http://bakery.tomahawk-player.org:10480/resolvers/providers.xml" ) );
-    QTimer::singleShot( 0, this, SLOT( loadPixmapsFromCache() ) );
 }
 
 
@@ -62,6 +61,8 @@ AtticaManager::loadPixmapsFromCache()
     if ( !cacheDir.cd( "atticacache" ) ) // doesn't exist, no cache
         return;
 
+    qDebug() << "Loading resolvers from cache dir:" << cacheDir.absolutePath();
+    qDebug() << "Currently we know about these resolvers:" << m_resolverStates.keys();
     foreach ( const QString& file, cacheDir.entryList( QStringList() << "*.png", QDir::Files | QDir::NoSymLinks ) )
     {
         // load all the pixmaps
@@ -204,6 +205,8 @@ AtticaManager::resolversList( BaseJob* j )
     m_resolverStates = TomahawkSettings::instance()->atticaResolverStates();
 
     // load icon cache from disk, and fetch any we are missing
+    loadPixmapsFromCache();
+
     foreach ( Content resolver, m_resolvers )
     {
         if ( !m_resolverStates.contains( resolver.id() ) )
diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt
index d8d0aa5f1..77d798ff0 100644
--- a/src/libtomahawk/CMakeLists.txt
+++ b/src/libtomahawk/CMakeLists.txt
@@ -113,8 +113,11 @@ set( libSources
     infosystem/infoplugins/generic/echonestplugin.cpp
     infosystem/infoplugins/generic/lastfmplugin.cpp
     infosystem/infoplugins/generic/chartsplugin.cpp
+    infosystem/infoplugins/generic/spotifyPlugin.cpp
+    infosystem/infoplugins/generic/hypemPlugin.cpp
     infosystem/infoplugins/generic/musixmatchplugin.cpp
     infosystem/infoplugins/generic/musicbrainzPlugin.cpp
+    infosystem/infoplugins/generic/RoviPlugin.cpp
 
     playlist/treemodel.cpp
     playlist/treeproxymodel.cpp
@@ -224,11 +227,8 @@ set( libSources
     widgets/infowidgets/sourceinfowidget.cpp
     widgets/infowidgets/ArtistInfoWidget.cpp
     widgets/infowidgets/AlbumInfoWidget.cpp
-    widgets/kbreadcrumbselectionmodel.cpp
-    widgets/breadcrumbbar.cpp
-    widgets/breadcrumbbuttonbase.cpp
-    widgets/headerbreadcrumb.cpp
-    widgets/siblingcrumbbutton.cpp
+    widgets/Breadcrumb.cpp
+    widgets/BreadcrumbButton.cpp
 
     jobview/JobStatusView.cpp
     jobview/JobStatusModel.cpp
@@ -345,8 +345,11 @@ set( libHeaders
     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
@@ -451,13 +454,10 @@ set( libHeaders
     widgets/SocialPlaylistWidget.h
     widgets/infowidgets/sourceinfowidget.h
     widgets/infowidgets/ArtistInfoWidget.h
+    widgets/infowidgets/ArtistInfoWidget_p.h
     widgets/infowidgets/AlbumInfoWidget.h
-    widgets/kbreadcrumbselectionmodel.h
-    widgets/kbreadcrumbselectionmodel_p.h
-    widgets/breadcrumbbar.h
-    widgets/breadcrumbbuttonbase.h
-    widgets/headerbreadcrumb.h
-    widgets/siblingcrumbbutton.h
+    widgets/Breadcrumb.h
+    widgets/BreadcrumbButton.h
 
     jobview/JobStatusView.h
     jobview/JobStatusModel.h
@@ -507,7 +507,6 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/.
     ${LIBECHONEST_INCLUDE_DIR}
     ${LIBECHONEST_INCLUDE_DIR}/..
     ${CLUCENE_INCLUDE_DIR}
-    ${CLUCENE_LIBRARY_DIR}
     ${PHONON_INCLUDES}
     ${CMAKE_BINARY_DIR}/thirdparty/liblastfm2/src
 
diff --git a/src/libtomahawk/audio/audioengine.cpp b/src/libtomahawk/audio/audioengine.cpp
index 2398749f2..c92be1f37 100644
--- a/src/libtomahawk/audio/audioengine.cpp
+++ b/src/libtomahawk/audio/audioengine.cpp
@@ -424,6 +424,9 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result )
             tLog() << "Starting new song:" << m_currentTrack->url();
             emit loading( m_currentTrack );
 
+            if ( QNetworkReply* qnr_io = qobject_cast< QNetworkReply* >( io.data() ) )
+                connect( qnr_io, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( ioStreamError( QNetworkReply::NetworkError ) ) );
+
             if ( !isHttpResult( m_currentTrack->url() ) && !isLocalResult( m_currentTrack->url() ) )
             {
                 if ( QNetworkReply* qnr_io = qobject_cast< QNetworkReply* >( io.data() ) )
@@ -573,6 +576,16 @@ AudioEngine::playItem( Tomahawk::PlaylistInterface* playlist, const Tomahawk::re
     }
 }
 
+void
+AudioEngine::ioStreamError( QNetworkReply::NetworkError error )
+{
+    if ( error != QNetworkReply::NoError )
+    {
+        if ( canGoNext() )
+            loadNextTrack();
+    }
+}
+
 
 void
 AudioEngine::playlistNextTrackReady()
@@ -682,7 +695,10 @@ AudioEngine::setPlaylist( PlaylistInterface* playlist )
     }
 
     if ( !playlist )
+    {
+        m_playlist.clear();
         return;
+    }
 
     m_playlist = playlist->getSharedPointer();
 
diff --git a/src/libtomahawk/audio/audioengine.h b/src/libtomahawk/audio/audioengine.h
index 038579d9c..f888ce42c 100644
--- a/src/libtomahawk/audio/audioengine.h
+++ b/src/libtomahawk/audio/audioengine.h
@@ -21,6 +21,7 @@
 
 #include <QObject>
 #include <QTimer>
+#include <QNetworkReply>
 
 #include <phonon/MediaObject>
 #include <phonon/AudioOutput>
@@ -134,6 +135,7 @@ private slots:
 
     void setCurrentTrack( const Tomahawk::result_ptr& result );
 
+    void ioStreamError( QNetworkReply::NetworkError );
 private:
     void setState( AudioState state );
 
diff --git a/src/libtomahawk/context/ContextWidget.cpp b/src/libtomahawk/context/ContextWidget.cpp
index 747dbac87..f2006e31c 100644
--- a/src/libtomahawk/context/ContextWidget.cpp
+++ b/src/libtomahawk/context/ContextWidget.cpp
@@ -276,10 +276,14 @@ ContextWidget::onAnimationFinished()
         m_scene->setSceneRect( ui->contextView->viewport()->rect() );
         layoutViews( false );
         setQuery( m_query, true );
+
+        ui->toggleButton->setText( tr( "Hide Footnotes" ) );
     }
     else
     {
         setFixedHeight( m_minHeight );
+
+        ui->toggleButton->setText( tr( "Show Footnotes" ) );
     }
 }
 
diff --git a/src/libtomahawk/context/ContextWidget.ui b/src/libtomahawk/context/ContextWidget.ui
index 336fbb413..77a9f2fe8 100644
--- a/src/libtomahawk/context/ContextWidget.ui
+++ b/src/libtomahawk/context/ContextWidget.ui
@@ -35,7 +35,7 @@
       </sizepolicy>
      </property>
      <property name="text">
-      <string>Footnotes</string>
+      <string>Show Footnotes</string>
      </property>
      <property name="alignment">
       <set>Qt::AlignCenter</set>
diff --git a/src/libtomahawk/database/databasecommand_loadsocialactions.cpp b/src/libtomahawk/database/databasecommand_loadsocialactions.cpp
index cf0dfa7e7..8f2b51e2f 100644
--- a/src/libtomahawk/database/databasecommand_loadsocialactions.cpp
+++ b/src/libtomahawk/database/databasecommand_loadsocialactions.cpp
@@ -50,7 +50,7 @@ DatabaseCommand_LoadSocialActions::exec( DatabaseImpl* dbi )
         return;
 
     QString whereToken;
-    whereToken = QString( "WHERE id IS %1"  ).arg( trkid );
+    whereToken = QString( "WHERE id IS %1" ).arg( trkid );
 
     QString sql = QString(
             "SELECT k, v, timestamp, source "
@@ -66,7 +66,7 @@ DatabaseCommand_LoadSocialActions::exec( DatabaseImpl* dbi )
         Tomahawk::SocialAction action;
         action.action    = query.value( 0 );  // action
         action.value     = query.value( 1 );  // comment
-        action.timestamp = query.value( 2 );    // timestamp
+        action.timestamp = query.value( 2 );  // timestamp
         action.source    = query.value( 3 );  // source
 
         allSocialActions.append( action );
diff --git a/src/libtomahawk/database/databasecommand_socialaction.cpp b/src/libtomahawk/database/databasecommand_socialaction.cpp
index 0f30c3ca4..2b137b9da 100644
--- a/src/libtomahawk/database/databasecommand_socialaction.cpp
+++ b/src/libtomahawk/database/databasecommand_socialaction.cpp
@@ -49,22 +49,17 @@ DatabaseCommand_SocialAction::exec( DatabaseImpl* dbi )
 
     TomahawkSqlQuery query = dbi->newquery();
 
-
     QVariant srcid = source()->isLocal() ? QVariant( QVariant::Int ) : source()->id();
 
-    int trkid = -2;
-    if ( !m_result.isNull() && !m_artist.isNull() && !m_track.isEmpty() )
-    {
-        bool autoCreate = true;
-        int artid = dbi->artistId( m_artist, autoCreate );
-        if ( artid < 1 )
-            return;
+    if ( m_artist.isNull() || m_track.isEmpty() )
+        return;
 
-        autoCreate = true; // artistId overwrites autoCreate (reference)
-        trkid = dbi->trackId( artid, m_track, autoCreate );
-        if ( trkid < 1 )
-            return;
-    }
+    int artid = dbi->artistId( m_artist, true );
+    if ( artid < 1 )
+        return;
+    int trkid = dbi->trackId( artid, m_track, true );
+    if ( trkid < 1 )
+        return;
 
     // update if it already exists
     TomahawkSqlQuery find = dbi->newquery();
@@ -80,12 +75,13 @@ DatabaseCommand_SocialAction::exec( DatabaseImpl* dbi )
                                .arg( trkid )
                                .arg( source()->isLocal() ? "IS NULL" : QString( "=%1" ).arg( source()->id() ) )
                                .arg( m_action ) );
-    } else
+    }
+    else
     {
-        query.prepare( "INSERT  INTO social_attributes(id, source, k, v, timestamp) "
+        query.prepare( "INSERT INTO social_attributes(id, source, k, v, timestamp) "
                        "VALUES (?, ?, ?, ?, ?)" );
 
-        query.bindValue( 0, trkid >= -1 ? trkid : QVariant() );
+        query.bindValue( 0, trkid );
         query.bindValue( 1, srcid );
         query.bindValue( 2, m_action );
         query.bindValue( 3, m_comment );
diff --git a/src/libtomahawk/dropjob.cpp b/src/libtomahawk/dropjob.cpp
index 48f9d212b..12432e682 100644
--- a/src/libtomahawk/dropjob.cpp
+++ b/src/libtomahawk/dropjob.cpp
@@ -112,7 +112,8 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
         if ( url.contains( "spotify" ) && url.contains( "track" ) )
             return true;
 
-        if ( url.contains( "rdio.com" ) && url.contains( "track" ) )
+        if ( url.contains( "rdio.com" ) && ( ( ( url.contains( "track" ) && url.contains( "artist" ) && url.contains( "album" ) )
+                                               || url.contains( "playlists" )  ) ) )
             return true;
     }
 
@@ -122,6 +123,8 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
             return true;
         if ( url.contains( "spotify" ) && url.contains( "album" ) )
             return true;
+        if ( url.contains( "rdio.com" ) && ( url.contains( "artist" ) && url.contains( "album" ) && !url.contains( "track" ) )  )
+            return true;
     }
 
     if ( acceptedType.testFlag( Artist ) )
@@ -130,6 +133,8 @@ DropJob::acceptsMimeData( const QMimeData* data, DropJob::DropTypes acceptedType
             return true;
         if ( url.contains( "spotify" ) && url.contains( "artist" ) )
             return true;
+        if ( url.contains( "rdio.com" ) && ( url.contains( "artist" ) && !url.contains( "album" ) && !url.contains( "track" ) )  )
+            return true;
     }
 
     // We whitelist t.co and bit.ly (and j.mp) since they do some link checking. Often playable (e.g. spotify..) links hide behind them,
@@ -152,6 +157,16 @@ DropJob::isDropType( DropJob::DropType desired, const QMimeData* data )
         // Not the most elegant
         if ( url.contains( "spotify" ) && url.contains( "playlist" ) && s_canParseSpotifyPlaylists )
             return true;
+
+        if ( url.contains( "rdio.com" ) && url.contains( "people" ) && url.contains( "playlist" ) )
+            return true;
+
+        // we don't know about these.. gotta say yes for now
+        if ( url.contains( "bit.ly" ) ||
+             url.contains( "j.mp" ) ||
+             url.contains( "t.co" ) ||
+             url.contains( "rd.io" ) )
+            return true;
     }
 
     return false;
@@ -443,6 +458,25 @@ DropJob::handleSpotifyUrls( const QString& urlsRaw )
     m_queryCount++;
 }
 
+void
+DropJob::handleRdioUrls( const QString& urlsRaw )
+{
+    QStringList urls = urlsRaw.split( QRegExp( "\\s+" ), QString::SkipEmptyParts );
+    qDebug() << "Got Rdio urls!!" << urls;
+
+    if ( dropAction() == Default )
+        setDropAction( Create );
+
+    RdioParser* rdio = new RdioParser( this );
+    connect( rdio, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( onTracksAdded( QList< Tomahawk::query_ptr > ) ) );
+
+    rdio->setCreatePlaylist( dropAction() == Create  );
+    rdio->parse( urls );
+
+    m_queryCount++;
+}
+
+
 void
 DropJob::handleAllUrls( const QString& urls )
 {
@@ -452,6 +486,8 @@ DropJob::handleAllUrls( const QString& urls )
               && ( urls.contains( "playlist" ) || urls.contains( "artist" ) || urls.contains( "album" ) || urls.contains( "track" ) )
               && s_canParseSpotifyPlaylists )
         handleSpotifyUrls( urls );
+    else if ( urls.contains( "rdio.com" ) )
+        handleRdioUrls( urls );
     else
         handleTrackUrls ( urls );
 }
diff --git a/src/libtomahawk/dropjob.h b/src/libtomahawk/dropjob.h
index 0142ec5bb..369899e11 100644
--- a/src/libtomahawk/dropjob.h
+++ b/src/libtomahawk/dropjob.h
@@ -102,7 +102,9 @@ public:
     void setGetWholeAlbums( bool getWholeAlbums );
     void tracksFromMimeData( const QMimeData* data, bool allowDuplicates = false, bool onlyLocal = false, bool top10 = false );
     void handleXspfs( const QString& files );
+
     void handleSpotifyUrls( const QString& urls );
+    void handleRdioUrls( const QString& urls );
 
     static bool canParseSpotifyPlaylists() { return s_canParseSpotifyPlaylists; }
     static void setCanParseSpotifyPlaylists( bool parseable ) { s_canParseSpotifyPlaylists = parseable; }
diff --git a/src/libtomahawk/globalactionmanager.cpp b/src/libtomahawk/globalactionmanager.cpp
index 1e969d2a1..b0bbe57fd 100644
--- a/src/libtomahawk/globalactionmanager.cpp
+++ b/src/libtomahawk/globalactionmanager.cpp
@@ -50,6 +50,7 @@
 #include "utils/spotifyparser.h"
 #include "utils/shortenedlinkparser.h"
 #include "utils/rdioparser.h"
+#include "widgets/searchwidget.h"
 
 GlobalActionManager* GlobalActionManager::s_instance = 0;
 
@@ -269,6 +270,8 @@ GlobalActionManager::parseTomahawkLink( const QString& urlIn )
             return handlePlayCommand( u );
         } else if( cmdType == "open" ) {
             return handleOpenCommand( u );
+        } else if( cmdType == "view" ) {
+            return handleViewCommand( u );
         } else {
             tLog() << "Tomahawk link not supported, command not known!" << cmdType << u.path();
             return false;
@@ -357,7 +360,7 @@ GlobalActionManager::handleOpenTrack ( const query_ptr& q )
     ViewManager::instance()->queue()->model()->append( q );
     ViewManager::instance()->showQueue();
 
-    if( !AudioEngine::instance()->isPlaying() ) {
+    if( !AudioEngine::instance()->isPlaying() && !AudioEngine::instance()->isPaused() ) {
         connect( q.data(), SIGNAL( resolvingFinished( bool ) ), this, SLOT( waitingForResolved( bool ) ) );
         m_waitingToPlay = q;
     }
@@ -498,23 +501,71 @@ bool
 GlobalActionManager::handleSearchCommand( const QUrl& url )
 {
     // open the super collection and set this as the search filter
-    QStringList query;
-    if( url.hasQueryItem( "artist" ) )
-        query << url.queryItemValue( "artist" );
-    if( url.hasQueryItem( "album" ) )
-        query << url.queryItemValue( "album" );
-    if( url.hasQueryItem( "title" ) )
-        query << url.queryItemValue( "title" );
-    QString queryStr = query.join( " " );
+    QString queryStr;
+    if ( url.hasQueryItem( "query" ) )
+        queryStr = url.queryItemValue( "query" );
+    else
+    {
+        QStringList query;
+        if( url.hasQueryItem( "artist" ) )
+            query << url.queryItemValue( "artist" );
+        if( url.hasQueryItem( "album" ) )
+            query << url.queryItemValue( "album" );
+        if( url.hasQueryItem( "title" ) )
+            query << url.queryItemValue( "title" );
+        queryStr = query.join( " " );
+    }
 
-    if( queryStr.isEmpty() )
+    if( queryStr.trimmed().isEmpty() )
         return false;
 
-    ViewManager::instance()->showSuperCollection();
-//    ViewManager::instance()->topbar()->setFilter( queryStr );
+    ViewManager::instance()->show( new SearchWidget( queryStr.trimmed() ) );
+
     return true;
 }
 
+bool
+GlobalActionManager::handleViewCommand( const QUrl& url )
+{
+    QStringList parts = url.path().split( "/" ).mid( 1 ); // get the rest of the command
+    if( parts.isEmpty() ) {
+        tLog() << "No specific view command:" << url.toString();
+        return false;
+    }
+
+    if ( parts[ 0 ] == "artist" )
+    {
+        const QString artist = url.queryItemValue( "name" );
+        if ( artist.isEmpty() )
+        {
+            tLog() << "Not artist supplied for view/artist command.";
+            return false;
+        }
+        artist_ptr artistPtr = Artist::get( artist );
+        if ( !artistPtr.isNull() )
+            ViewManager::instance()->show( artistPtr );
+
+        return true;
+    }
+    else if ( parts[ 0 ] == "album" )
+    {
+        const QString artist = url.queryItemValue( "artist" );
+        const QString album = url.queryItemValue( "name" );
+        if ( artist.isEmpty() || album.isEmpty() )
+        {
+            tLog() << "Not artist or album supplied for view/artist command:" << url;
+            return false;
+        }
+        album_ptr albumPtr = Album::get( Artist::get( artist, false ), album, false );
+        if ( !albumPtr.isNull() )
+            ViewManager::instance()->show( albumPtr );
+
+        return true;
+    }
+
+    return false;
+}
+
 
 bool
 GlobalActionManager::handleAutoPlaylistCommand( const QUrl& url )
diff --git a/src/libtomahawk/globalactionmanager.h b/src/libtomahawk/globalactionmanager.h
index e3d85eb14..3a0ad3685 100644
--- a/src/libtomahawk/globalactionmanager.h
+++ b/src/libtomahawk/globalactionmanager.h
@@ -95,6 +95,7 @@ private:
     bool handlePlayCommand(const QUrl& url );
     bool handleBookmarkCommand(const QUrl& url );
     bool handleOpenCommand(const QUrl& url );
+    bool handleViewCommand(const QUrl& url );
     bool doQueueAdd( const QStringList& parts, const QList< QPair< QString, QString > >& queryItems );
 
     bool playSpotify( const QUrl& url );
diff --git a/src/libtomahawk/infobar/infobar.cpp b/src/libtomahawk/infobar/infobar.cpp
index c441292cf..4296b75ae 100644
--- a/src/libtomahawk/infobar/infobar.cpp
+++ b/src/libtomahawk/infobar/infobar.cpp
@@ -27,6 +27,7 @@
 #include "utils/tomahawkutils.h"
 #include "utils/logger.h"
 #include <QCheckBox>
+#include <widgets/querylabel.h>
 
 #define ANIMATION_TIME 400
 #define IMAGE_HEIGHT 64
@@ -37,6 +38,7 @@ using namespace Tomahawk;
 InfoBar::InfoBar( QWidget* parent )
     : QWidget( parent )
     , ui( new Ui::InfoBar )
+    , m_queryLabel( 0 )
 {
     ui->setupUi( this );
     TomahawkUtils::unmarginLayout( layout() );
@@ -71,6 +73,14 @@ InfoBar::InfoBar( QWidget* parent )
     ui->longDescriptionLabel->setText( QString() );
     ui->imageLabel->setText( QString() );
 
+    m_queryLabel = new QueryLabel( this );
+    m_queryLabel->setType( QueryLabel::Artist );
+    m_queryLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
+    m_queryLabel->setTextPen( palette().brightText().color() );
+    m_queryLabel->setFont( boldFont );
+    m_queryLabel->hide();
+    connect( m_queryLabel, SIGNAL( clickedArtist() ), this, SLOT( artistClicked() ) );
+
     m_autoUpdate = new QCheckBox( this );
     m_autoUpdate->setText( tr( "Automatically update" ) );
     m_autoUpdate->setLayoutDirection( Qt::RightToLeft );
@@ -95,6 +105,8 @@ InfoBar::InfoBar( QWidget* parent )
     setPalette( p );
     setAutoFillBackground( true );
 
+    setMinimumHeight( geometry().height() );
+    setMaximumHeight( geometry().height() );
     connect( ViewManager::instance(), SIGNAL( filterAvailable( bool ) ), SLOT( setFilterAvailable( bool ) ) );
     connect( ViewManager::instance(), SIGNAL( autoUpdateAvailable( bool ) ), SLOT( setAutoUpdateAvailable( bool ) ) );
 }
@@ -116,9 +128,49 @@ InfoBar::setCaption( const QString& s )
 void
 InfoBar::setDescription( const QString& s )
 {
+    if ( m_queryLabel->isVisible() )
+    {
+        ui->verticalLayout->removeWidget( m_queryLabel );
+        m_queryLabel->hide();
+
+        ui->verticalLayout->addWidget( ui->descriptionLabel );
+        ui->verticalLayout->setContentsMargins( 0, 0, 0, 0 );
+        ui->descriptionLabel->show();
+    }
     ui->descriptionLabel->setText( s );
 }
 
+void
+InfoBar::setDescription( const artist_ptr& artist )
+{
+    m_queryLabel->setQuery( Query::get( artist->name(), QString(), QString() ) );
+    m_queryLabel->setExtraContentsMargins( 4, 0, 0, 0 );
+
+    if ( !m_queryLabel->isVisible() )
+    {
+        ui->verticalLayout->removeWidget( ui->descriptionLabel );
+        ui->descriptionLabel->hide();
+
+        m_queryLabel->show();
+        ui->verticalLayout->addWidget( m_queryLabel );
+        ui->verticalLayout->setContentsMargins( 0, 0, 0, 15 );
+    }
+
+}
+
+void
+InfoBar::setDescription( const album_ptr&  )
+{
+    // TODO
+}
+
+void
+InfoBar::artistClicked()
+{
+    if ( m_queryLabel && !m_queryLabel->query().isNull() )
+        ViewManager::instance()->show( Artist::get( m_queryLabel->artist() ) );
+}
+
 
 void
 InfoBar::setLongDescription( const QString& s )
diff --git a/src/libtomahawk/infobar/infobar.h b/src/libtomahawk/infobar/infobar.h
index a718a32a3..f16339d52 100644
--- a/src/libtomahawk/infobar/infobar.h
+++ b/src/libtomahawk/infobar/infobar.h
@@ -22,7 +22,9 @@
 #include <QWidget>
 
 #include "dllmacro.h"
+#include "artist.h"
 
+class QueryLabel;
 class QCheckBox;
 class QTimeLine;
 class QSearchField;
@@ -43,7 +45,12 @@ public:
 
 public slots:
     void setCaption( const QString& s );
+
     void setDescription( const QString& s );
+    // If you want a querylabel instead of an ElidedLabel
+    void setDescription( const Tomahawk::artist_ptr& artist );
+    void setDescription( const Tomahawk::album_ptr& album_ptr );
+
     void setLongDescription( const QString& s );
     void setPixmap( const QPixmap& p );
 
@@ -60,12 +67,14 @@ protected:
 
 private slots:
     void onFilterEdited();
+    void artistClicked();
 
 private:
     Ui::InfoBar* ui;
 
     QSearchField* m_searchWidget;
     QCheckBox* m_autoUpdate;
+    QueryLabel* m_queryLabel;
 };
 
 #endif // INFOBAR_H
diff --git a/src/libtomahawk/infobar/infobar.ui b/src/libtomahawk/infobar/infobar.ui
index 7379ef909..a0f3a9fe9 100644
--- a/src/libtomahawk/infobar/infobar.ui
+++ b/src/libtomahawk/infobar/infobar.ui
@@ -65,10 +65,13 @@
        <property name="sizeConstraint">
         <enum>QLayout::SetDefaultConstraint</enum>
        </property>
+       <property name="bottomMargin">
+        <number>0</number>
+       </property>
        <item>
         <widget class="ElidedLabel" name="captionLabel">
          <property name="sizePolicy">
-          <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+          <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp
new file mode 100644
index 000000000..c5810440e
--- /dev/null
+++ b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.cpp
@@ -0,0 +1,207 @@
+/* === 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 "RoviPlugin.h"
+
+#include "utils/logger.h"
+
+#include <QDateTime>
+#include <QNetworkReply>
+#include <parser.h>
+
+using namespace Tomahawk::InfoSystem;
+
+static QString
+md5( const QByteArray& src )
+{
+    QByteArray const digest = QCryptographicHash::hash( src, QCryptographicHash::Md5 );
+    return QString::fromLatin1( digest.toHex() ).rightJustified( 32, '0' );
+}
+
+RoviPlugin::RoviPlugin()
+    : InfoPlugin()
+{
+    m_supportedGetTypes << InfoAlbumSongs;
+
+    /*
+     *    Your API Key is 7jxr9zggt45h6rg2n4ss3mrj
+     *    Your secret is XUnYutaAW6
+     */
+    m_apiKey = "7jxr9zggt45h6rg2n4ss3mrj";
+    m_secret = "XUnYutaAW6";
+}
+
+RoviPlugin::~RoviPlugin()
+{
+
+}
+
+
+
+void
+RoviPlugin::namChangedSlot( QNetworkAccessManager* nam )
+{
+    if ( !nam )
+        return;
+
+    m_nam = nam;
+}
+
+
+void
+RoviPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
+{
+    if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
+    {
+        emit info( requestData, QVariant() );
+        return;
+    }
+    InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
+    if ( !hash.contains( "artist" ) || !hash.contains( "album" ) )
+    {
+        emit info( requestData, QVariant() );
+        return;
+    }
+
+    Tomahawk::InfoSystem::InfoStringHash criteria;
+    criteria["artist"] = hash["artist"];
+    criteria["album"] = hash["album"];
+
+    emit getCachedInfo( criteria, 0, requestData );
+}
+
+void
+RoviPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
+{
+    switch ( requestData.type )
+    {
+        case InfoAlbumSongs:
+        {
+            QUrl baseUrl = QUrl( "http://api.rovicorp.com/search/v2/music/search" );
+            baseUrl.addQueryItem( "query", QString( "%1 %2" ).arg( criteria[ "artist" ] ).arg( criteria[ "album" ] ) );
+            baseUrl.addQueryItem( "entitytype", "album" );
+            baseUrl.addQueryItem( "include", "album:tracks" );
+
+            QNetworkReply* reply = makeRequest( baseUrl );
+
+            reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
+            connect( reply, SIGNAL( finished() ), this, SLOT( albumLookupFinished() ) );
+            connect( reply, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( albumLookupError( QNetworkReply::NetworkError ) ) );
+            break;
+        }
+        default:
+        {
+            Q_ASSERT( false );
+            break;
+        }
+    }
+}
+
+void
+RoviPlugin::albumLookupError( QNetworkReply::NetworkError error )
+{
+    if ( error == QNetworkReply::NoError )
+        return;
+
+    QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
+    Q_ASSERT( reply );
+
+    Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
+
+    emit info( requestData, QVariant() );
+
+}
+
+void
+RoviPlugin::albumLookupFinished()
+{
+    QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
+    Q_ASSERT( reply );
+
+    if ( reply->error() != QNetworkReply::NoError )
+        return;
+
+    Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
+
+    QJson::Parser p;
+    bool ok;
+    QVariantMap response = p.parse( reply, &ok ).toMap();
+
+    if ( !ok || response.isEmpty() || !response.contains( "searchResponse" ) )
+    {
+        tLog() << "Error parsing JSON from Rovi!" << p.errorString() << response;
+        emit info( requestData, QVariant() );
+        return;
+    }
+
+    QVariantList resultList = response[ "searchResponse" ].toMap().value( "results" ).toList();
+    if ( resultList.size() == 0 )
+    {
+        emit info( requestData, QVariant() );
+        return;
+    }
+
+    QVariantMap results = resultList.first().toMap();
+    QVariantList tracks = results[ "album" ].toMap()[ "tracks" ].toList();
+
+    if ( tracks.isEmpty() )
+    {
+        tLog() << "Error parsing JSON from Rovi!" << p.errorString() << response;
+        emit info( requestData, QVariant() );
+    }
+
+
+    QStringList trackNameList;
+    foreach ( const QVariant& track, tracks )
+    {
+        const QVariantMap trackData = track.toMap();
+        if ( trackData.contains( "title" ) )
+            trackNameList << trackData[ "title" ].toString();
+    }
+
+    QVariantMap returnedData;
+    returnedData["tracks"] = trackNameList;
+
+    emit info( requestData, returnedData );
+
+    Tomahawk::InfoSystem::InfoStringHash criteria;
+    criteria["artist"] = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>()["artist"];
+    criteria["album"] = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>()["album"];
+
+    emit updateCache( criteria, 0, requestData.type, returnedData );
+}
+
+
+QNetworkReply*
+RoviPlugin::makeRequest( QUrl url )
+{
+    url.addQueryItem( "apikey", m_apiKey );
+    url.addEncodedQueryItem( "sig", generateSig() );
+
+    qDebug() << "Rovi request url:" << url.toString();
+    return m_nam->get( QNetworkRequest( url ) );
+}
+
+
+QByteArray
+RoviPlugin::generateSig() const
+{
+    QByteArray raw = m_apiKey + m_secret + QString::number( QDateTime::currentMSecsSinceEpoch() / 1000 ).toLatin1();
+    return md5( raw ).toLatin1();
+
+}
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.h
new file mode 100644
index 000000000..21c81c5a9
--- /dev/null
+++ b/src/libtomahawk/infosystem/infoplugins/generic/RoviPlugin.h
@@ -0,0 +1,67 @@
+/* === 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 ROVIPLUGIN_H
+#define ROVIPLUGIN_H
+
+#include "infosystem/infosystem.h"
+
+#include <QNetworkReply>
+
+class QNetworkAccessManager;
+
+namespace Tomahawk
+{
+
+namespace InfoSystem
+{
+
+class RoviPlugin : public InfoPlugin
+{
+    Q_OBJECT
+public:
+    RoviPlugin();
+    virtual ~RoviPlugin();
+
+protected:
+    virtual void namChangedSlot( QNetworkAccessManager* nam );
+    virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData );
+
+    virtual void pushInfo( QString, Tomahawk::InfoSystem::InfoType, QVariant )
+    {}
+
+    virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
+
+private slots:
+    void albumLookupFinished();
+    void albumLookupError( QNetworkReply::NetworkError );
+private:
+    QNetworkReply* makeRequest( QUrl url );
+    QByteArray generateSig() const;
+
+    QNetworkAccessManager* m_nam;
+
+    QByteArray m_apiKey;
+    QByteArray m_secret;
+};
+
+}
+
+}
+
+#endif // ROVIPLUGIN_H
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp
index 50bd25124..a0b3f9475 100644
--- a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp
+++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp
@@ -49,7 +49,7 @@ ChartsPlugin::ChartsPlugin()
 
 
     /// Add resources here
-    m_chartResources << "billboard" << "itunes";
+    m_chartResources << "billboard" << "itunes" << "rdio" << "wearehunted";
     m_supportedGetTypes <<  InfoChart << InfoChartCapabilities;
 
 }
@@ -73,9 +73,10 @@ ChartsPlugin::namChangedSlot( QNetworkAccessManager *nam )
     m_nam = QWeakPointer< QNetworkAccessManager >( nam );
 
     /// Then get each chart from resource
-    /// We need to fetch them before they are asked for
+    /// We want to prepopulate the breadcrumb to fetch them before they are asked for
 
-    if( !m_chartResources.isEmpty() && m_nam ){
+    if ( !m_chartResources.isEmpty() && m_nam && m_allChartsMap.isEmpty() )
+    {
 
         tDebug() << "ChartsPlugin: InfoChart fetching possible resources";
         foreach ( QVariant resource, m_chartResources )
@@ -93,21 +94,21 @@ ChartsPlugin::namChangedSlot( QNetworkAccessManager *nam )
 
 
 void
-ChartsPlugin::dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+ChartsPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
-    emit info( requestId, requestData, QVariant() );
+    emit info( requestData, QVariant() );
     return;
 }
 
 
 void
-ChartsPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+ChartsPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     qDebug() << Q_FUNC_INFO << requestData.caller;
     qDebug() << Q_FUNC_INFO << requestData.customData;
 
     InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
-    bool foundSource;
+    bool foundSource = false;
 
     switch ( requestData.type )
     {
@@ -116,7 +117,7 @@ ChartsPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData req
             /// We need something to check if the request is actually ment to go to this plugin
             if ( !hash.contains( "chart_source" ) )
             {
-                dataError( requestId, requestData );
+                dataError( requestData );
                 break;
             }
             else
@@ -131,19 +132,19 @@ ChartsPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData req
 
                 if( !foundSource )
                 {
-                    dataError( requestId, requestData );
+                    dataError( requestData );
                     break;
                 }
 
             }
-            fetchChart( requestId, requestData );
+            fetchChart( requestData );
             break;
 
         case InfoChartCapabilities:
-            fetchChartCapabilities( requestId, requestData );
+            fetchChartCapabilities( requestData );
             break;
         default:
-            dataError( requestId, requestData );
+            dataError( requestData );
     }
 }
 
@@ -158,12 +159,12 @@ ChartsPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoTy
 
 
 void
-ChartsPlugin::fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+ChartsPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
 
     if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
     {
-        dataError( requestId, requestData );
+        dataError( requestData );
         return;
     }
 
@@ -173,7 +174,7 @@ ChartsPlugin::fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData
     /// Each request needs to contain both a id and source
     if ( !hash.contains( "chart_id" ) && !hash.contains( "chart_source" ) )
     {
-        dataError( requestId, requestData );
+        dataError( requestData );
         return;
 
     }
@@ -181,29 +182,30 @@ ChartsPlugin::fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData
     criteria["chart_id"] = hash["chart_id"];
     criteria["chart_source"] = hash["chart_source"];
 
-    emit getCachedInfo( requestId, criteria, 0, requestData );
+    emit getCachedInfo( criteria, 0, requestData );
 }
 
 void
-ChartsPlugin::fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+ChartsPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
     {
-        dataError( requestId, requestData );
+        dataError( requestData );
         return;
     }
 
     Tomahawk::InfoSystem::InfoStringHash criteria;
-    emit getCachedInfo( requestId, criteria, 0, requestData );
+
+    emit getCachedInfo( criteria, 0, requestData );
 }
 
 void
-ChartsPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
+ChartsPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     if ( !m_nam.data() )
     {
         tLog() << "Have a null QNAM, uh oh";
-        emit info( requestId, requestData, QVariant() );
+        emit info( requestData, QVariant() );
         return;
     }
 
@@ -217,7 +219,6 @@ ChartsPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria,
             qDebug() << Q_FUNC_INFO << "Getting chart url" << url;
 
             QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) );
-            reply->setProperty( "requestId", requestId );
             reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
 
             connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) );
@@ -230,22 +231,18 @@ ChartsPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria,
             if ( m_chartsFetchJobs > 0 )
             {
                 qDebug() << Q_FUNC_INFO << "InfoChartCapabilities still fetching!";
-                m_cachedRequests.append( QPair< uint, InfoRequestData >( requestId, requestData ) );
+                m_cachedRequests.append( requestData );
                 return;
             }
 
-            emit info(
-                requestId,
-                requestData,
-                m_allChartsMap
-            );
+            emit info( requestData, m_allChartsMap );
             return;
         }
 
         default:
         {
             tLog() << Q_FUNC_INFO << "Couldn't figure out what to do with this type of request after cache miss";
-            emit info( requestId, requestData, QVariant() );
+            emit info( requestData, QVariant() );
             return;
         }
     }
@@ -279,96 +276,152 @@ ChartsPlugin::chartTypes()
         // We'll populate charts with the data from the server
         QVariantMap charts;
         QString chartName;
-        if ( source == "itunes" )
+        QStringList defaultChain;
+        if ( source == "wearehunted" || source == "itunes" )
         {
+            // Some charts can have an extra param, itunes has geo, WAH has emerging/mainstream
             // Itunes has geographic-area based charts. So we build a breadcrumb of
             // ITunes - Country - Albums - Top Chart Type
             //                  - Tracks - Top Chart Type
-            QHash< QString, QVariantMap > countries;
+            // WeAreHunted has Mainstream/Emerging
+            // WeAreHunted - Type - Artists - Chart Type
+            //                    - Tracks  - Chart Type
+            QHash< QString, QVariantMap > extraType;
             foreach( const QVariant& chartObj, chartObjs.values() )
             {
-                const QVariantMap chart = chartObj.toMap();
-                const QString id = chart.value( "id" ).toString();
-                const QString geo = chart.value( "geo" ).toString();
-                QString name = chart.value( "name" ).toString();
-                const QString type = chart.value( "type" ).toString();
+                if( !chartObj.toMap().isEmpty() ){
+                    const QVariantMap chart = chartObj.toMap();
+                    const QString id = chart.value( "id" ).toString();
+                    const QString geo = chart.value( "geo" ).toString();
+                    QString name = chart.value( "genre" ).toString();
+                    const QString type = chart.value( "type" ).toString();
+                    const bool isDefault = ( chart.contains( "default" ) && chart[ "default" ].toInt() == 1 );
 
-                QString country;
-                if ( !m_cachedCountries.contains( geo ) )
-                {
-                    QLocale l( QString( "en_%1" ).arg( geo ) );
-                    country = Tomahawk::CountryUtils::fullCountryFromCode( geo );
+                    QString extra;
+                    if( !geo.isEmpty() ){
 
-                    for ( int i = 1; i < country.size(); i++ )
-                    {
-                        if ( country.at( i ).isUpper() )
+                        if ( !m_cachedCountries.contains( geo ) )
                         {
-                            country.insert( i, " " );
-                            i++;
+                            QLocale l( QString( "en_%1" ).arg( geo ) );
+                            extra = Tomahawk::CountryUtils::fullCountryFromCode( geo );
+
+                            for ( int i = 1; i < extra.size(); i++ )
+                            {
+                                if ( extra.at( i ).isUpper() )
+                                {
+                                    extra.insert( i, " " );
+                                    i++;
+                                }
+                            }
+                            m_cachedCountries[ geo ] = extra;
                         }
+                        else
+                        {
+                            extra = m_cachedCountries[ geo ];
+                        }
+                    }else extra = chart.value( "extra" ).toString();
+
+                    if ( name.isEmpty() ) // not a specific chart, an all chart
+                        name = tr( "Top Overall" );
+
+                    InfoStringHash c;
+                    c[ "id" ] = id;
+                    c[ "label" ] = name;
+                    c[ "type" ] = "album";
+                    if ( isDefault )
+                        c[ "default" ] = "true";
+
+                    QList<InfoStringHash> extraTypeData = extraType[ extra ][ type ].value< QList< InfoStringHash > >();
+                    extraTypeData.append( c );
+
+                    extraType[ extra ].insert( type, QVariant::fromValue< QList< InfoStringHash > >( extraTypeData ) );
+                    if ( isDefault )
+                    {
+                        defaultChain.clear();
+                        defaultChain.append( extra );
+                        defaultChain.append( type );
+                        defaultChain.append( name );
                     }
-                    m_cachedCountries[ geo ] = country;
                 }
-                else
+                foreach( const QString& c, extraType.keys() )
                 {
-                    country = m_cachedCountries[ geo ];
+                    charts[ c ] = extraType[ c ];
+//                     qDebug() << "extraType has types:" << c;
+                }
+                if( source == "itunes" ){
+                    chartName = "iTunes";
+                }
+                if( source == "wearehunted" ){
+                    chartName = "WeAreHunted";
                 }
-
-                if ( name.startsWith( "iTunes Store:" ) ) // truncate
-                    name = name.mid( 13 );
-
-                InfoStringHash c;
-                c[ "id" ] = id;
-                c[ "label" ] = name;
-                c[ "type" ] = "album";
-                QList<InfoStringHash> countryTypeData = countries[ country ][ type ].value< QList< InfoStringHash > >();
-                countryTypeData.append( c );
-
-                countries[ country ].insert( type, QVariant::fromValue< QList< InfoStringHash > >( countryTypeData ) );
             }
 
-            foreach( const QString& c, countries.keys() )
-            {
-                charts[ c ] = countries[ c ];
-//                 qDebug() << "Country has types:" << countries[ c ];
-            }
-            chartName = "iTunes";
-        } else
+        }else
         {
             // We'll just build:
             // [Source] - Album - Chart Type
             // [Source] - Track - Chart Type
             QList< InfoStringHash > albumCharts;
             QList< InfoStringHash > trackCharts;
+            QList< InfoStringHash > artistCharts;
+
             foreach( const QVariant& chartObj, chartObjs.values() )
             {
-                const QVariantMap chart = chartObj.toMap();
-                const QString type = chart.value( "type" ).toString();
-                InfoStringHash c;
-                c[ "id" ] = chart.value( "id" ).toString();
-                c[ "label" ] = chart.value( "name" ).toString();
-                if ( type == "Album" )
-                {
-                    c[ "type" ] = "album";
-                    albumCharts.append( c );
-                }
-                else if ( type == "Track" )
-                {
-                    c[ "type" ] = "tracks";
-                    trackCharts.append( c );
-                }
-            }
-            charts.insert( tr( "Albums" ), QVariant::fromValue< QList< InfoStringHash > >( albumCharts ) );
-            charts.insert( tr( "Tracks" ), QVariant::fromValue< QList< InfoStringHash > >( trackCharts ) );
+                if( !chartObj.toMap().isEmpty() ){
+                    const QVariantMap chart = chartObj.toMap();
+                    const QString type = chart.value( "type" ).toString();
+                    const bool isDefault = ( chart.contains( "default" ) && chart[ "default" ].toInt() == 1 );
 
-            /// @note For displaying purposes, upper the first letter
-            /// @note Remeber to lower it when fetching this!
-            chartName = source;
-            chartName[0] = chartName[0].toUpper();
+                    InfoStringHash c;
+                    c[ "id" ] = chart.value( "id" ).toString();
+                    c[ "label" ] = chart.value( "name" ).toString();
+                    if ( isDefault )
+                        c[ "default" ] = "true";
+
+                    if ( type == "Album" )
+                    {
+                        c[ "type" ] = "album";
+                        albumCharts.append( c );
+                    }
+                    else if ( type == "Track" )
+                    {
+                        c[ "type" ] = "tracks";
+                        trackCharts.append( c );
+
+                    }else if ( type == "Artist" )
+                    {
+                        c[ "type" ] = "artists";
+                        artistCharts.append( c );
+
+                    }
+
+                    if ( isDefault )
+                    {
+                        defaultChain.clear();
+                        defaultChain.append( type + "s" ); //UGLY but it's plural to the user, see below
+                        defaultChain.append( c[ "label" ] );
+                    }
+                }
+                if( !artistCharts.isEmpty() )
+                    charts.insert( tr( "Artists" ), QVariant::fromValue< QList< InfoStringHash > >( artistCharts ) );
+                if( !albumCharts.isEmpty() )
+                    charts.insert( tr( "Albums" ), QVariant::fromValue< QList< InfoStringHash > >( albumCharts ) );
+                if( !trackCharts.isEmpty() )
+                    charts.insert( tr( "Tracks" ), QVariant::fromValue< QList< InfoStringHash > >( trackCharts ) );
+
+                /// @note For displaying purposes, upper the first letter
+                /// @note Remeber to lower it when fetching this!
+                chartName = source;
+                chartName[0] = chartName[0].toUpper();
+            }
         }
 
         /// Add the possible charts and its types to breadcrumb
 //         qDebug() << "ADDING CHART TYPE TO CHARTS:" << chartName;
+        QVariantMap defaultMap = m_allChartsMap.value( "defaults" ).value< QVariantMap >();
+        defaultMap[ source ] = defaultChain;
+        m_allChartsMap[ "defaults" ] = defaultMap;
+        m_allChartsMap[ "defaultSource" ] = "itunes";
         m_allChartsMap.insert( chartName , QVariant::fromValue< QVariantMap >( charts ) );
 
     }
@@ -380,10 +433,9 @@ ChartsPlugin::chartTypes()
     m_chartsFetchJobs--;
     if ( !m_cachedRequests.isEmpty() && m_chartsFetchJobs == 0 )
     {
-        QPair< uint, InfoRequestData > request;
-        foreach ( request, m_cachedRequests )
+        foreach ( InfoRequestData request, m_cachedRequests )
         {
-            emit info( request.first, request.second, m_allChartsMap );
+            emit info( request, m_allChartsMap );
         }
         m_cachedRequests.clear();
     }
@@ -414,6 +466,7 @@ ChartsPlugin::chartReturned()
         QVariantList chartResponse = res.value( "list" ).toList();
         QList< InfoStringHash > top_tracks;
         QList< InfoStringHash > top_albums;
+        QStringList top_artists;
 
         /// Deside what type, we need to handle it differently
         /// @todo: We allready know the type, append it to breadcrumb hash
@@ -422,6 +475,8 @@ ChartsPlugin::chartReturned()
             setChartType( Album );
         else if( res.value( "type" ).toString() == "Track" )
             setChartType( Track );
+        else if( res.value( "type" ).toString() == "Artist" )
+            setChartType( Artist );
         else
             setChartType( None );
 
@@ -477,10 +532,29 @@ ChartsPlugin::chartReturned()
                         top_tracks << pair;
 
                     }
+                }else if( chartType() == Artist )
+                {
+                    if ( artist.isEmpty() ) // don't have enough...
+                    {
+                        tLog() << "Didn't get an artist from charts, not enough to build a query on. Aborting" << artist;
+
+                    }
+                    else
+                    {
+                        top_artists << artist;
+                    }
+
                 }
             }
         }
 
+        if( chartType() == Artist )
+        {
+            tDebug() << "ChartsPlugin:" << "\tgot " << top_artists.size() << " artists";
+            returnedData["artists"] = QVariant::fromValue( top_artists );
+            returnedData["type"] = "artists";
+        }
+
         if( chartType() == Track )
         {
             tDebug() << "ChartsPlugin:" << "\tgot " << top_tracks.size() << " tracks";
@@ -498,11 +572,7 @@ ChartsPlugin::chartReturned()
         Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
 
 
-        emit info(
-            reply->property( "requestId" ).toUInt(),
-            requestData,
-            returnedData
-        );
+        emit info( requestData, returnedData );
         // TODO update cache
     }
     else
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h
index 3d35b39cf..c44b88ccf 100644
--- a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h
+++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h
@@ -56,15 +56,15 @@ public slots:
     void namChangedSlot( QNetworkAccessManager *nam );
 
 protected slots:
-    virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
-    virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData );
+    virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
+    virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData );
 
     virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data );
 
 private:
-    void fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
-    void fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
-    void dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
+    void fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData );
+    void fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData );
+    void dataError( Tomahawk::InfoSystem::InfoRequestData requestData );
 
     QVariantList m_chartResources;
     QList<InfoStringHash> m_charts;
@@ -73,7 +73,7 @@ private:
     QVariantMap m_allChartsMap;
 
     uint m_chartsFetchJobs;
-    QList< QPair< uint, InfoRequestData > > m_cachedRequests;
+    QList< InfoRequestData > m_cachedRequests;
 
     QHash< QString, QString > m_cachedCountries;
 
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.cpp
index a975dbe08..3d4b1e02b 100644
--- a/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.cpp
+++ b/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.cpp
@@ -68,37 +68,37 @@ EchoNestPlugin::namChangedSlot( QNetworkAccessManager *nam )
 }
 
 void
-EchoNestPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+EchoNestPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     switch ( requestData.type )
     {
         case Tomahawk::InfoSystem::InfoArtistBiography:
-            return getArtistBiography( requestId, requestData );
+            return getArtistBiography( requestData );
         case Tomahawk::InfoSystem::InfoArtistFamiliarity:
-            return getArtistFamiliarity( requestId, requestData );
+            return getArtistFamiliarity( requestData );
         case Tomahawk::InfoSystem::InfoArtistHotttness:
-            return getArtistHotttnesss( requestId, requestData );
+            return getArtistHotttnesss( requestData );
         case Tomahawk::InfoSystem::InfoArtistTerms:
-            return getArtistTerms( requestId, requestData );
+            return getArtistTerms( requestData );
         case Tomahawk::InfoSystem::InfoTrackEnergy:
-            return getSongProfile( requestId, requestData, "energy" );
+            return getSongProfile( requestData, "energy" );
         case Tomahawk::InfoSystem::InfoMiscTopTerms:
-            return getMiscTopTerms( requestId, requestData );
+            return getMiscTopTerms( requestData );
         default:
         {
-            emit info( requestId, requestData, QVariant() );
+            emit info( requestData, QVariant() );
             return;
         }
     }
 }
 
 void
-EchoNestPlugin::getSongProfile( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData, const QString &item )
+EchoNestPlugin::getSongProfile( const Tomahawk::InfoSystem::InfoRequestData &requestData, const QString &item )
 {
     //WARNING: Totally not implemented yet
     Q_UNUSED( item );
 
-    if( !isValidTrackData( requestId, requestData ) )
+    if( !isValidTrackData( requestData ) )
         return;
 
 //     Track track( input.toString() );
@@ -110,67 +110,62 @@ EchoNestPlugin::getSongProfile( uint requestId, const Tomahawk::InfoSystem::Info
 }
 
 void
-EchoNestPlugin::getArtistBiography( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData )
+EchoNestPlugin::getArtistBiography( const Tomahawk::InfoSystem::InfoRequestData &requestData )
 {
-    if( !isValidArtistData( requestId, requestData ) )
+    if( !isValidArtistData( requestData ) )
         return;
 
     Echonest::Artist artist( requestData.input.toString() );
     QNetworkReply *reply = artist.fetchBiographies();
     reply->setProperty( "artist", QVariant::fromValue< Echonest::Artist >( artist ) );
-    reply->setProperty( "requestId", requestId );
     reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
     connect( reply, SIGNAL( finished() ), SLOT( getArtistBiographySlot() ) );
 }
 
 void
-EchoNestPlugin::getArtistFamiliarity( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData )
+EchoNestPlugin::getArtistFamiliarity( const Tomahawk::InfoSystem::InfoRequestData &requestData )
 {
-    if( !isValidArtistData( requestId, requestData ) )
+    if( !isValidArtistData( requestData ) )
         return;
 
     qDebug() << "Fetching artist familiarity!" << requestData.input;
     Echonest::Artist artist( requestData.input.toString() );
     QNetworkReply* reply = artist.fetchFamiliarity();
     reply->setProperty( "artist", QVariant::fromValue< Echonest::Artist >( artist ) );
-    reply->setProperty( "requestId", requestId );
     reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
     connect( reply, SIGNAL( finished() ), SLOT( getArtistFamiliaritySlot() ) );
 }
 
 void
-EchoNestPlugin::getArtistHotttnesss( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData )
+EchoNestPlugin::getArtistHotttnesss( const Tomahawk::InfoSystem::InfoRequestData &requestData )
 {
-    if( !isValidArtistData( requestId, requestData ) )
+    if( !isValidArtistData( requestData ) )
         return;
 
     Echonest::Artist artist( requestData.input.toString() );
     QNetworkReply* reply = artist.fetchHotttnesss();
     reply->setProperty( "artist", QVariant::fromValue< Echonest::Artist >( artist ) );
-    reply->setProperty( "requestId", requestId );
     reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
     connect( reply, SIGNAL( finished() ), SLOT( getArtistHotttnesssSlot() ) );
 }
 
 void
-EchoNestPlugin::getArtistTerms( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData )
+EchoNestPlugin::getArtistTerms( const Tomahawk::InfoSystem::InfoRequestData &requestData )
 {
-    if( !isValidArtistData( requestId, requestData ) )
+    if( !isValidArtistData( requestData ) )
         return;
 
     Echonest::Artist artist( requestData.input.toString() );
     QNetworkReply* reply = artist.fetchTerms( Echonest::Artist::Weight );
     reply->setProperty( "artist", QVariant::fromValue< Echonest::Artist >( artist ) );
-    reply->setProperty( "requestId", requestId );
     reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
     connect( reply, SIGNAL( finished() ), SLOT( getArtistTermsSlot() ) );
 }
 
 void
-EchoNestPlugin::getMiscTopTerms( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData )
+EchoNestPlugin::getMiscTopTerms( const Tomahawk::InfoSystem::InfoRequestData &requestData )
 {
     QNetworkReply* reply = Echonest::Artist::topTerms( 20 );
-    reply->setProperty( "requestId", requestId );
     reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
     connect( reply, SIGNAL( finished() ), SLOT( getMiscTopSlot() ) );
 }
@@ -195,9 +190,7 @@ EchoNestPlugin::getArtistBiographySlot()
         biographyMap[ biography.site() ] = siteData;
     }
     Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
-    emit info( reply->property( "requestId" ).toUInt(),
-               requestData,
-               biographyMap );
+    emit info( requestData, biographyMap );
     reply->deleteLater();
 }
 
@@ -208,9 +201,7 @@ EchoNestPlugin::getArtistFamiliaritySlot()
     Echonest::Artist artist = artistFromReply( reply );
     qreal familiarity = artist.familiarity();
     Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
-    emit info( reply->property( "requestId" ).toUInt(),
-               requestData,
-               familiarity );
+    emit info( requestData, familiarity );
     reply->deleteLater();
 }
 
@@ -221,9 +212,7 @@ EchoNestPlugin::getArtistHotttnesssSlot()
     Echonest::Artist artist = artistFromReply( reply );
     qreal hotttnesss = artist.hotttnesss();
     Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
-    emit info( reply->property( "requestId" ).toUInt(),
-               requestData,
-               hotttnesss );
+    emit info( requestData, hotttnesss );
     reply->deleteLater();
 }
 
@@ -241,9 +230,7 @@ EchoNestPlugin::getArtistTermsSlot()
         termsMap[ term.name() ] = termHash;
     }
     Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
-    emit info( reply->property( "requestId" ).toUInt(),
-               requestData,
-               termsMap );
+    emit info( requestData, termsMap );
     reply->deleteLater();
 }
 
@@ -260,46 +247,44 @@ EchoNestPlugin::getMiscTopSlot()
         termsMap[ term.name() ] = termHash;
     }
     Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
-    emit info( reply->property( "requestId" ).toUInt(),
-               requestData,
-               termsMap );
+    emit info( requestData, termsMap );
     reply->deleteLater();
 }
 
 bool
-EchoNestPlugin::isValidArtistData( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData )
+EchoNestPlugin::isValidArtistData( const Tomahawk::InfoSystem::InfoRequestData &requestData )
 {
     if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QString >() )
     {
-        emit info( requestId, requestData, QVariant() );
+        emit info( requestData, QVariant() );
         return false;
     }
     QString artistName = requestData.input.toString();
     if ( artistName.isEmpty() )
     {
-        emit info( requestId, requestData, QVariant() );
+        emit info( requestData, QVariant() );
         return false;
     }
     return true;
 }
 
 bool
-EchoNestPlugin::isValidTrackData( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData )
+EchoNestPlugin::isValidTrackData( const Tomahawk::InfoSystem::InfoRequestData &requestData )
 {
     if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QString >() )
     {
-        emit info( requestId, requestData, QVariant() );
+        emit info( requestData, QVariant() );
         return false;
     }
     QString trackName = requestData.input.toString();
     if ( trackName.isEmpty() )
     {
-        emit info( requestId, requestData, QVariant() );
+        emit info( requestData, QVariant() );
         return false;
     }
     if ( !requestData.customData.contains( "artistName" ) || requestData.customData[ "artistName" ].toString().isEmpty() )
     {
-        emit info( requestId, requestData, QVariant() );
+        emit info( requestData, QVariant() );
         return false;
     }
     return true;
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.h
index e6e0825ab..bd59193c8 100644
--- a/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.h
+++ b/src/libtomahawk/infosystem/infoplugins/generic/echonestplugin.h
@@ -46,7 +46,7 @@ public:
     virtual ~EchoNestPlugin();
 
 protected slots:
-    virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
+    virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
 
     virtual void pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant data )
     {
@@ -55,9 +55,8 @@ protected slots:
         Q_UNUSED( data );
     }
 
-    virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
+    virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
     {
-        Q_UNUSED( requestId );
         Q_UNUSED( criteria );
         Q_UNUSED( requestData );
     }
@@ -66,15 +65,15 @@ public slots:
     void namChangedSlot( QNetworkAccessManager *nam );
 
 private:
-    void getSongProfile( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData, const QString &item = QString() );
-    void getArtistBiography( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData );
-    void getArtistFamiliarity( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData );
-    void getArtistHotttnesss( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData );
-    void getArtistTerms( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData );
-    void getMiscTopTerms( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData );
+    void getSongProfile( const Tomahawk::InfoSystem::InfoRequestData &requestData, const QString &item = QString() );
+    void getArtistBiography( const Tomahawk::InfoSystem::InfoRequestData &requestData );
+    void getArtistFamiliarity( const Tomahawk::InfoSystem::InfoRequestData &requestData );
+    void getArtistHotttnesss( const Tomahawk::InfoSystem::InfoRequestData &requestData );
+    void getArtistTerms( const Tomahawk::InfoSystem::InfoRequestData &requestData );
+    void getMiscTopTerms( const Tomahawk::InfoSystem::InfoRequestData &requestData );
 
-    bool isValidArtistData( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData );
-    bool isValidTrackData( uint requestId, const Tomahawk::InfoSystem::InfoRequestData &requestData );
+    bool isValidArtistData( const Tomahawk::InfoSystem::InfoRequestData &requestData );
+    bool isValidTrackData( const Tomahawk::InfoSystem::InfoRequestData &requestData );
     Echonest::Artist artistFromReply( QNetworkReply* );
 
 private slots:
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp
new file mode 100644
index 000000000..dd6a4b7b1
--- /dev/null
+++ b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.cpp
@@ -0,0 +1,420 @@
+/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com>
+ *
+ *   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 "hypemPlugin.h"
+
+#include <QDir>
+#include <QSettings>
+#include <QCryptographicHash>
+#include <QNetworkConfiguration>
+#include <QNetworkReply>
+#include <QDomElement>
+
+#include "album.h"
+#include "typedefs.h"
+#include "audio/audioengine.h"
+#include "tomahawksettings.h"
+#include "utils/tomahawkutils.h"
+#include "utils/logger.h"
+
+#define HYPEM_URL "http://hypem.com/playlist/"
+#define HYPEM_END_URL "json/1/data.js"
+#include <qjson/parser.h>
+#include <qjson/serializer.h>
+
+using namespace Tomahawk::InfoSystem;
+
+
+hypemPlugin::hypemPlugin()
+    : InfoPlugin()
+    , m_chartsFetchJobs( 0 )
+{
+
+    m_supportedGetTypes << InfoChart << InfoChartCapabilities;
+    m_types << "Artists" << "Tracks" << "Recent by Tag";
+
+    m_trackTypes    << "Last 3 Days"
+                    << "Last Week"
+                    << "No Remixes"
+                    << "On Twitter";
+
+    m_byTagTypes    << "Dance"
+                    << "Experimental"
+                    << "Electronic"
+                    << "Funk"
+                    << "Hip-hop"
+                    << "Indie"
+                    << "Instrumental"
+                    << "Post-punk"
+                    << "Rock"
+                    << "Singer-songwriter"
+                    << "Alternative"
+                    << "Pop"
+                    << "Female"
+                    << "Vocalist"
+                    << "Folk"
+                    << "Electro"
+                    << "Lo-fi"
+                    << "Psychedelic"
+                    << "Rap"
+                    << "British"
+                    << "Ambient"
+                    << "Dubstep"
+                    << "House"
+                    << "Chillwave"
+                    << "Dreampop"
+                    << "Shoegaze"
+                    << "Chillout"
+                    << "Soul"
+                    << "French"
+                    << "Acoustic"
+                    << "Canadian"
+                    << "60s"
+                    << "80s"
+                    << "Techno"
+                    << "Punk"
+                    << "New wave";
+    chartTypes();
+
+}
+
+
+
+
+hypemPlugin::~hypemPlugin()
+{
+    qDebug() << Q_FUNC_INFO;
+}
+
+
+void
+hypemPlugin::namChangedSlot( QNetworkAccessManager *nam )
+{
+    tDebug() << "hypemPlugin: namChangedSLot";
+    qDebug() << Q_FUNC_INFO;
+    if( !nam )
+        return;
+
+    m_nam = QWeakPointer< QNetworkAccessManager >( nam );
+
+
+}
+
+
+void
+hypemPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData )
+{
+    emit info( requestData, QVariant() );
+    return;
+}
+
+
+void
+hypemPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
+{
+    qDebug() << Q_FUNC_INFO << requestData.caller;
+    qDebug() << Q_FUNC_INFO << requestData.customData;
+
+    InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
+
+
+    switch ( requestData.type )
+    {
+
+        case InfoChart:
+            if ( !hash.contains( "chart_source" ) || hash["chart_source"].toLower() != "hype machine" )
+            {
+                dataError( requestData );
+                break;
+            }
+            qDebug() << Q_FUNC_INFO << "InfoCHart req for" << hash["chart_source"];
+            fetchChart( requestData );
+            break;
+
+        case InfoChartCapabilities:
+            fetchChartCapabilities( requestData );
+            break;
+        default:
+            dataError( requestData );
+    }
+}
+
+
+void
+hypemPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input )
+{
+    Q_UNUSED( caller )
+    Q_UNUSED( type)
+    Q_UNUSED( input )
+}
+
+
+void
+hypemPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
+{
+
+    if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
+    {
+        dataError( requestData );
+        return;
+    }
+
+    InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
+    Tomahawk::InfoSystem::InfoStringHash criteria;
+
+    /// Each request needs to contain both a id and source
+    if ( !hash.contains( "chart_id" ) && !hash.contains( "chart_source" ) )
+    {
+        dataError( requestData );
+        return;
+
+    }
+    /// Set the criterias for current chart
+    criteria["chart_id"] = hash["chart_id"];
+    criteria["chart_source"] = hash["chart_source"];
+
+    emit getCachedInfo( criteria, 0, requestData );
+}
+
+void
+hypemPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData )
+{
+    if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
+    {
+        dataError( requestData );
+        return;
+    }
+
+    Tomahawk::InfoSystem::InfoStringHash criteria;
+    emit getCachedInfo( criteria, 0, requestData );
+}
+
+void
+hypemPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
+{
+    if ( !m_nam.data() )
+    {
+        tLog() << "Have a null QNAM, uh oh";
+        emit info( requestData, QVariant() );
+        return;
+    }
+
+
+    switch ( requestData.type )
+    {
+        case InfoChart:
+        {
+            /// Fetch the chart, we need source and id
+
+            QUrl url = QUrl( QString( HYPEM_URL "%1/%2" ).arg( criteria["chart_id"].toLower() ).arg(HYPEM_END_URL) );
+            qDebug() << Q_FUNC_INFO << "Getting chart url" << url;
+
+            QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) );
+            reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
+            connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) );
+            return;
+
+
+        }
+
+        case InfoChartCapabilities:
+        {
+            if ( m_chartsFetchJobs > 0 )
+            {
+                qDebug() << Q_FUNC_INFO << "InfoChartCapabilities still fetching!";
+                m_cachedRequests.append( requestData );
+                return;
+            }
+
+            emit info( requestData, m_allChartsMap );
+            return;
+        }
+
+        default:
+        {
+            tLog() << Q_FUNC_INFO << "Couldn't figure out what to do with this type of request after cache miss";
+            emit info( requestData, QVariant() );
+            return;
+        }
+    }
+}
+
+
+void
+hypemPlugin::chartTypes()
+{
+    /// Get possible chart type for specifichypemPlugin: InfoChart types returned chart source
+    tDebug() << Q_FUNC_INFO << "Got hypem types";
+
+    QVariantMap charts;
+
+    foreach(QVariant types, m_types )
+    {
+        QList< InfoStringHash > chart_types;
+        QList< InfoStringHash > pop_charts;
+        InfoStringHash c;
+
+        if(types.toString() != "Artists")
+        {
+
+            if(types.toString() == "Tracks")
+            {
+
+                foreach(QVariant trackType, m_trackTypes)
+                {
+                    QString typeId;
+                    if(trackType.toString() == "Last 3 Days")
+                        typeId = "popular/3day";
+
+                    if(trackType.toString() == "Last Week")
+                        typeId = "popular/lastweek";
+
+                    if(trackType.toString() == "No Remixes")
+                        typeId = "popular/noremix";
+
+                    if(trackType.toString() == "On Twitter")
+                        typeId = "popular/twitter";
+
+                    c[ "id" ] = typeId;
+                    c[ "label" ] = trackType.toString();
+                    c[ "type" ] = "tracks";
+                    pop_charts.append( c );
+                }
+
+                chart_types.append( pop_charts );
+
+            }
+            else if(types.toString() == "Recent by Tag")
+            {
+                foreach(QVariant tagTypes, m_byTagTypes)
+                {
+
+                    c[ "id" ] = "tags/" + tagTypes.toString().toLower();
+                    c[ "label" ] = tagTypes.toString();
+                    c[ "type" ] = tagTypes.toString();
+                    chart_types.append( c );
+                }
+
+            }
+
+        }else
+        {
+            InfoStringHash c;
+            c[ "id" ] = "popular/artists";
+            c[ "label" ] = "Most Recent";
+            c[ "type" ] = "artists";
+            chart_types.append( c );
+        }
+
+        charts.insert( types.toString(), QVariant::fromValue<QList< InfoStringHash > >( chart_types ) );
+    }
+
+
+    m_allChartsMap.insert( "Hype Machine", QVariant::fromValue<QVariantMap>( charts ) );
+    qDebug() << "hypemPlugin:Chartstype: " << m_allChartsMap;
+
+
+}
+
+void
+hypemPlugin::chartReturned()
+{
+
+    /// Chart request returned something! Woho
+    QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
+    QString url = reply->url().toString();
+    QVariantMap returnedData;
+    if ( reply->error() == QNetworkReply::NoError )
+    {
+        QJson::Parser p;
+        bool ok;
+        QVariantMap res = p.parse( reply, &ok ).toMap();
+
+        if ( !ok )
+        {
+            tLog() << "Failed to parse json from chart lookup:" << p.errorString() << "On line" << p.errorLine();
+            return;
+        }
+
+        /// SO we have a result, parse it!
+        QList< InfoStringHash > top_tracks;
+        QStringList top_artists;
+
+        if( url.contains( "artists" ) )
+            setChartType( Artist );
+        else
+            setChartType( Track );
+
+        foreach(QVariant result, res )
+        {
+            QString title, artist;
+            QVariantMap chartMap = result.toMap();
+
+            if ( !chartMap.isEmpty() )
+            {
+
+                title = chartMap.value( "title" ).toString();
+                artist = chartMap.value( "artist" ).toString();
+
+                if( chartType() == Track )
+                {
+                    InfoStringHash pair;
+                    pair["artist"] = artist;
+                    pair["track"] = title;
+                    top_tracks << pair;
+
+                    qDebug() << "HypemChart type is track";
+                }
+
+
+                if( chartType() == Artist )
+                {
+
+                    top_artists << artist;
+                    qDebug() << "HypemChart type is artist";
+
+                }
+            }
+        }
+
+        if( chartType() == Track )
+        {
+            tDebug() << "HypemPlugin:" << "\tgot " << top_tracks.size() << " tracks";
+            returnedData["tracks"] = QVariant::fromValue( top_tracks );
+            returnedData["type"] = "tracks";
+        }
+
+
+
+        if( chartType() == Artist )
+        {
+            tDebug() << "HypemPlugin:" << "\tgot " << top_artists.size() << " artists";
+            returnedData["artists"] = top_artists;
+            returnedData["type"] = "artists";
+        }
+
+        Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
+
+
+        emit info( requestData, returnedData );
+        // TODO update cache
+    }
+    else
+        qDebug() << "Network error in fetching chart:" << reply->url().toString();
+
+}
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.h
new file mode 100644
index 000000000..a13c0299d
--- /dev/null
+++ b/src/libtomahawk/infosystem/infoplugins/generic/hypemPlugin.h
@@ -0,0 +1,93 @@
+/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com>
+ *
+ *   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 hypemPlugin_H
+#define hypemPlugin_H
+
+#include "infosystem/infosystem.h"
+#include "infosystem/infosystemworker.h"
+#include <QNetworkReply>
+#include <QObject>
+
+class QNetworkReply;
+
+namespace Tomahawk
+{
+
+namespace InfoSystem
+{
+
+class hypemPlugin : public InfoPlugin
+{
+    Q_OBJECT
+
+public:
+    hypemPlugin();
+    virtual ~hypemPlugin();
+
+    enum ChartType {
+        None =      0x00,
+        Track =     0x01,
+        Album =     0x02,
+        Artist =    0x04
+
+    };
+ void setChartType( ChartType type ) { m_chartType = type; }
+ ChartType chartType() const { return m_chartType; }
+
+public slots:
+    void chartReturned();
+    void chartTypes();
+    void namChangedSlot( QNetworkAccessManager *nam );
+
+protected slots:
+    virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
+    virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData );
+
+    virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data );
+
+private:
+    void fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData );
+    void fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData );
+    void dataError( Tomahawk::InfoSystem::InfoRequestData requestData );
+
+    QVariantList m_chartResources;
+    QList<InfoStringHash> m_charts;
+
+
+    ChartType m_chartType;
+    QVariantMap m_allChartsMap;
+    QVariantList m_types;
+    QVariantList m_popularTypes;
+    QVariantList m_trackTypes;
+    QVariantList m_byTagTypes;
+
+
+    uint m_chartsFetchJobs;
+    QList< InfoRequestData > m_cachedRequests;
+
+    QHash< QString, QString > m_cachedCountries;
+
+    QWeakPointer< QNetworkAccessManager > m_nam;
+};
+
+}
+
+}
+
+#endif // hypemPlugin_H
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp
index ae99903ec..ed7681642 100644
--- a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp
+++ b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.cpp
@@ -123,43 +123,43 @@ LastFmPlugin::namChangedSlot( QNetworkAccessManager *nam )
 
 
 void
-LastFmPlugin::dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+LastFmPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
-    emit info( requestId, requestData, QVariant() );
+    emit info( requestData, QVariant() );
     return;
 }
 
 
 void
-LastFmPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+LastFmPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     switch ( requestData.type )
     {
         case InfoArtistImages:
-            fetchArtistImages( requestId, requestData );
+            fetchArtistImages( requestData );
             break;
 
         case InfoAlbumCoverArt:
-            fetchCoverArt( requestId, requestData );
+            fetchCoverArt( requestData );
             break;
 
         case InfoArtistSimilars:
-            fetchSimilarArtists( requestId, requestData );
+            fetchSimilarArtists( requestData );
             break;
 
         case InfoArtistSongs:
-            fetchTopTracks( requestId, requestData );
+            fetchTopTracks( requestData );
             break;
 
         case InfoChart:
-            fetchChart( requestId, requestData );
+            fetchChart( requestData );
             break;
 
         case InfoChartCapabilities:
-            fetchChartCapabilities( requestId, requestData );
+            fetchChartCapabilities( requestData );
             break;
         default:
-            dataError( requestId, requestData );
+            dataError( requestData );
     }
 }
 
@@ -267,95 +267,95 @@ LastFmPlugin::sendLoveSong( const InfoType type, QVariant input )
 
 
 void
-LastFmPlugin::fetchSimilarArtists( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+LastFmPlugin::fetchSimilarArtists( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
     {
-        dataError( requestId, requestData );
+        dataError( requestData );
         return;
     }
     InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
     if ( !hash.contains( "artist" ) )
     {
-        dataError( requestId, requestData );
+        dataError( requestData );
         return;
     }
 
     Tomahawk::InfoSystem::InfoStringHash criteria;
     criteria["artist"] = hash["artist"];
 
-    emit getCachedInfo( requestId, criteria, 2419200000, requestData );
+    emit getCachedInfo( criteria, 2419200000, requestData );
 }
 
 
 void
-LastFmPlugin::fetchTopTracks( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+LastFmPlugin::fetchTopTracks( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
     {
-        dataError( requestId, requestData );
+        dataError( requestData );
         return;
     }
     InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
     if ( !hash.contains( "artist" ) )
     {
-        dataError( requestId, requestData );
+        dataError( requestData );
         return;
     }
 
     Tomahawk::InfoSystem::InfoStringHash criteria;
     criteria["artist"] = hash["artist"];
 
-    emit getCachedInfo( requestId, criteria, 2419200000, requestData );
+    emit getCachedInfo( criteria, 2419200000, requestData );
 }
 
 void
-LastFmPlugin::fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+LastFmPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
     {
-        dataError( requestId, requestData );
+        dataError( requestData );
         return;
     }
     InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
     Tomahawk::InfoSystem::InfoStringHash criteria;
     if ( !hash.contains( "chart_id" ) )
     {
-        dataError( requestId, requestData );
+        dataError( requestData );
         return;
     } else {
         criteria["chart_id"] = hash["chart_id"];
     }
 
-    emit getCachedInfo( requestId, criteria, 0, requestData );
+    emit getCachedInfo( criteria, 0, requestData );
 }
 
 void
-LastFmPlugin::fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+LastFmPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
     {
-        dataError( requestId, requestData );
+        dataError( requestData );
         return;
     }
     InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
     Tomahawk::InfoSystem::InfoStringHash criteria;
 
-    emit getCachedInfo( requestId, criteria, 0, requestData );
+    emit getCachedInfo( criteria, 0, requestData );
 }
 
 void
-LastFmPlugin::fetchCoverArt( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+LastFmPlugin::fetchCoverArt( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
     {
-        dataError( requestId, requestData );
+        dataError( requestData );
         return;
     }
     InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
     if ( !hash.contains( "artist" ) || !hash.contains( "album" ) )
     {
-        dataError( requestId, requestData );
+        dataError( requestData );
         return;
     }
 
@@ -363,39 +363,39 @@ LastFmPlugin::fetchCoverArt( uint requestId, Tomahawk::InfoSystem::InfoRequestDa
     criteria["artist"] = hash["artist"];
     criteria["album"] = hash["album"];
 
-    emit getCachedInfo( requestId, criteria, 2419200000, requestData );
+    emit getCachedInfo( criteria, 2419200000, requestData );
 }
 
 
 void
-LastFmPlugin::fetchArtistImages( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+LastFmPlugin::fetchArtistImages( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
     {
-        dataError( requestId, requestData );
+        dataError( requestData );
         return;
     }
     InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
     if ( !hash.contains( "artist" ) )
     {
-        dataError( requestId, requestData );
+        dataError( requestData );
         return;
     }
 
     Tomahawk::InfoSystem::InfoStringHash criteria;
     criteria["artist"] = hash["artist"];
 
-    emit getCachedInfo( requestId, criteria, 2419200000, requestData );
+    emit getCachedInfo( criteria, 2419200000, requestData );
 }
 
 
 void
-LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
+LastFmPlugin::notInCacheSlot( QHash<QString, QString> criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     if ( !lastfm::nam() )
     {
         tLog() << "Have a null QNAM, uh oh";
-        emit info( requestId, requestData, QVariant() );
+        emit info( requestData, QVariant() );
         return;
     }
 
@@ -407,14 +407,14 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria,
              /// We need something to check if the request is actually ment to go to this plugin
             if ( !hash.contains( "chart_source" ) )
             {
-                dataError( requestId, requestData );
+                dataError( requestData );
                 break;
             }
             else
             {
                 if( "last.fm" != hash["chart_source"] )
                 {
-                    dataError( requestId, requestData );
+                    dataError( requestData );
                     break;
                 }
 
@@ -425,7 +425,6 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria,
             args["method"] = criteria["chart_id"];
             args["limit"] = "100";
             QNetworkReply* reply = lastfm::ws::get(args);
-            reply->setProperty( "requestId", requestId );
             reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
 
             connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) );
@@ -466,11 +465,7 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria,
             result.insert( "Last.fm", QVariant::fromValue<QVariantMap>( charts ) );
 
             tDebug() << "LASTFM RETURNING CHART LIST!";
-            emit info(
-                requestId,
-                requestData,
-                result
-            );
+            emit info( requestData, result );
             return;
         }
 
@@ -478,7 +473,6 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria,
         {
             lastfm::Artist a( criteria["artist"] );
             QNetworkReply* reply = a.getSimilar();
-            reply->setProperty( "requestId", requestId );
             reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
 
             connect( reply, SIGNAL( finished() ), SLOT( similarArtistsReturned() ) );
@@ -489,7 +483,6 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria,
         {
             lastfm::Artist a( criteria["artist"] );
             QNetworkReply* reply = a.getTopTracks();
-            reply->setProperty( "requestId", requestId );
             reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
 
             connect( reply, SIGNAL( finished() ), SLOT( topTracksReturned() ) );
@@ -504,7 +497,6 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria,
             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 ) );
             QNetworkReply* reply = lastfm::nam()->get( req );
-            reply->setProperty( "requestId", requestId );
             reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
 
             connect( reply, SIGNAL( finished() ), SLOT( coverArtReturned() ) );
@@ -518,7 +510,6 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria,
             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 ) );
             QNetworkReply* reply = lastfm::nam()->get( req );
-            reply->setProperty( "requestId", requestId );
             reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
 
             connect( reply, SIGNAL( finished() ), SLOT( artistImagesReturned() ) );
@@ -528,7 +519,7 @@ LastFmPlugin::notInCacheSlot( uint requestId, QHash<QString, QString> criteria,
         default:
         {
             tLog() << "Couldn't figure out what to do with this type of request after cache miss";
-            emit info( requestId, requestData, QVariant() );
+            emit info( requestData, QVariant() );
             return;
         }
     }
@@ -553,11 +544,7 @@ LastFmPlugin::similarArtistsReturned()
 
     Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
 
-    emit info(
-        reply->property( "requestId" ).toUInt(),
-        requestData,
-        returnedData
-    );
+    emit info( requestData, returnedData );
 
     Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>();
     Tomahawk::InfoSystem::InfoStringHash criteria;
@@ -609,11 +596,7 @@ LastFmPlugin::chartReturned()
 
     Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
 
-    emit info(
-        reply->property( "requestId" ).toUInt(),
-        requestData,
-        returnedData
-    );
+    emit info( requestData, returnedData );
     // TODO update cache
 }
 
@@ -629,11 +612,7 @@ LastFmPlugin::topTracksReturned()
 
     Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
 
-    emit info(
-        reply->property( "requestId" ).toUInt(),
-        requestData,
-        returnedData
-    );
+    emit info( requestData, returnedData );
 
     Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>();
     Tomahawk::InfoSystem::InfoStringHash criteria;
@@ -653,7 +632,7 @@ LastFmPlugin::coverArtReturned()
         if ( ba.isNull() || !ba.length() )
         {
             tLog() << Q_FUNC_INFO << "Uh oh, null byte array";
-            emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+            emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
             return;
         }
         foreach ( const QUrl& url, m_badUrls )
@@ -668,11 +647,7 @@ LastFmPlugin::coverArtReturned()
 
         Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
 
-        emit info(
-            reply->property( "requestId" ).toUInt(),
-            requestData,
-            returnedData
-        );
+        emit info( requestData, returnedData );
 
         Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>();
         Tomahawk::InfoSystem::InfoStringHash criteria;
@@ -685,13 +660,12 @@ LastFmPlugin::coverArtReturned()
         if ( !lastfm::nam() )
         {
             tLog() << Q_FUNC_INFO << "Uh oh, nam is null";
-            emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+            emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
             return;
         }
         // Follow HTTP redirect
         QNetworkRequest req( redir );
         QNetworkReply* newReply = lastfm::nam()->get( req );
-        newReply->setProperty( "requestId", reply->property( "requestId" ) );
         newReply->setProperty( "requestData", reply->property( "requestData" ) );
         connect( newReply, SIGNAL( finished() ), SLOT( coverArtReturned() ) );
     }
@@ -711,7 +685,7 @@ LastFmPlugin::artistImagesReturned()
         if ( ba.isNull() || !ba.length() )
         {
             tLog() << Q_FUNC_INFO << "Uh oh, null byte array";
-            emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+            emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
             return;
         }
         foreach ( const QUrl& url, m_badUrls )
@@ -725,7 +699,7 @@ LastFmPlugin::artistImagesReturned()
 
         Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
 
-        emit info( reply->property( "requestId" ).toUInt(), requestData, returnedData );
+        emit info( requestData, returnedData );
 
         Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>();
         Tomahawk::InfoSystem::InfoStringHash criteria;
@@ -737,13 +711,12 @@ LastFmPlugin::artistImagesReturned()
         if ( !lastfm::nam() )
         {
             tLog() << Q_FUNC_INFO << "Uh oh, nam is null";
-            emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+            emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
             return;
         }
         // Follow HTTP redirect
         QNetworkRequest req( redir );
         QNetworkReply* newReply = lastfm::nam()->get( req );
-        newReply->setProperty( "requestId", reply->property( "requestId" ) );
         newReply->setProperty( "requestData", reply->property( "requestData" ) );
         connect( newReply, SIGNAL( finished() ), SLOT( artistImagesReturned() ) );
     }
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h
index 90f117882..fbd372e02 100644
--- a/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h
+++ b/src/libtomahawk/infosystem/infoplugins/generic/lastfmplugin.h
@@ -56,25 +56,25 @@ public slots:
     void namChangedSlot( QNetworkAccessManager *nam );
 
 protected slots:
-    virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
-    virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData );
+    virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
+    virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData );
 
     virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data );
 
 private:
-    void fetchCoverArt( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
-    void fetchArtistImages( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
-    void fetchSimilarArtists( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
-    void fetchTopTracks( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
-    void fetchChart( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
-    void fetchChartCapabilities( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
+    void fetchCoverArt( Tomahawk::InfoSystem::InfoRequestData requestData );
+    void fetchArtistImages( Tomahawk::InfoSystem::InfoRequestData requestData );
+    void fetchSimilarArtists( Tomahawk::InfoSystem::InfoRequestData requestData );
+    void fetchTopTracks( Tomahawk::InfoSystem::InfoRequestData requestData );
+    void fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData );
+    void fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData );
 
     void createScrobbler();
     void nowPlaying( const QVariant &input );
     void scrobble();
     void sendLoveSong( const InfoType type, QVariant input );
 
-    void dataError( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
+    void dataError( Tomahawk::InfoSystem::InfoRequestData requestData );
 
     QList<lastfm::Track> parseTrackList( QNetworkReply * reply );
 
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp
index 388eda463..8c0b3c2f4 100644
--- a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp
+++ b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.cpp
@@ -50,31 +50,64 @@ MusicBrainzPlugin::namChangedSlot( QNetworkAccessManager *nam )
     m_nam = QWeakPointer< QNetworkAccessManager >( nam );
 }
 
-
 void
-MusicBrainzPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+MusicBrainzPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
     {
-        emit info( requestId, requestData, QVariant() );
+        emit info( requestData, QVariant() );
         return;
     }
     InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
     if ( !hash.contains( "artist" ) )
     {
-        emit info( requestId, requestData, QVariant() );
+        emit info( requestData, QVariant() );
         return;
     }
 
+    switch ( requestData.type )
+    {
+        case InfoArtistReleases:
+        {
+
+            Tomahawk::InfoSystem::InfoStringHash criteria;
+            criteria["artist"] = hash["artist"];
+
+            emit getCachedInfo( criteria, 2419200000, requestData );
+            break;
+        }
+
+        case InfoAlbumSongs:
+        {
+
+            Tomahawk::InfoSystem::InfoStringHash criteria;
+            criteria["artist"] = hash["artist"];
+            criteria["album"] = hash["album"];
+
+            emit getCachedInfo( criteria, 2419200000, requestData );
+
+            break;
+        }
+
+        default:
+        {
+            Q_ASSERT( false );
+            break;
+        }
+    }
+}
+
+void
+MusicBrainzPlugin::notInCacheSlot( InfoStringHash criteria, InfoRequestData requestData )
+{
     switch ( requestData.type )
     {
         case InfoArtistReleases:
         {
             QString requestString( "http://musicbrainz.org/ws/2/artist" );
             QUrl url( requestString );
-            url.addQueryItem( "query", hash["artist"] );
+            url.addQueryItem( "query", criteria["artist"] );
             QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) );
-            reply->setProperty( "requestId", requestId );
             reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
 
             connect( reply, SIGNAL( finished() ), SLOT( artistSearchSlot() ) );
@@ -85,9 +118,8 @@ MusicBrainzPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestDat
         {
             QString requestString( "http://musicbrainz.org/ws/2/artist" );
             QUrl url( requestString );
-            url.addQueryItem( "query", hash["artist"] );
+            url.addQueryItem( "query", criteria["artist"] );
             QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) );
-            reply->setProperty( "requestId", requestId );
             reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
 
             connect( reply, SIGNAL( finished() ), SLOT( albumSearchSlot() ) );
@@ -104,24 +136,24 @@ MusicBrainzPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestDat
 
 
 bool
-MusicBrainzPlugin::isValidTrackData( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+MusicBrainzPlugin::isValidTrackData( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QVariantMap >() )
     {
-        emit info( requestId, requestData, QVariant() );
+        emit info( requestData, QVariant() );
         qDebug() << Q_FUNC_INFO << "Data null, invalid, or can't convert";
         return false;
     }
     QVariantMap hash = requestData.input.value< QVariantMap >();
     if ( hash[ "trackName" ].toString().isEmpty() )
     {
-        emit info( requestId, requestData, QVariant() );
+        emit info( requestData, QVariant() );
         qDebug() << Q_FUNC_INFO << "Track name is empty";
         return false;
     }
     if ( hash[ "artistName" ].toString().isEmpty() )
     {
-        emit info( requestId, requestData, QVariant() );
+        emit info( requestData, QVariant() );
         qDebug() << Q_FUNC_INFO << "No artist name found";
         return false;
     }
@@ -141,7 +173,7 @@ MusicBrainzPlugin::artistSearchSlot()
     QDomNodeList domNodeList = doc.elementsByTagName( "artist" );
     if ( domNodeList.isEmpty() )
     {
-        emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+        emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
         return;
     }
 
@@ -151,7 +183,6 @@ MusicBrainzPlugin::artistSearchSlot()
     url.addQueryItem( "artist", artist_id );
 
     QNetworkReply* newReply = m_nam.data()->get( QNetworkRequest( url ) );
-    newReply->setProperty( "requestId", oldReply->property( "requestId" ) );
     newReply->setProperty( "requestData", oldReply->property( "requestData" ) );
     connect( newReply, SIGNAL( finished() ), SLOT( albumFoundSlot() ) );
 }
@@ -169,7 +200,7 @@ MusicBrainzPlugin::albumSearchSlot()
     QDomNodeList domNodeList = doc.elementsByTagName( "artist" );
     if ( domNodeList.isEmpty() )
     {
-        emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+        emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
         return;
     }
 
@@ -179,7 +210,6 @@ MusicBrainzPlugin::albumSearchSlot()
     url.addQueryItem( "artist", artist_id );
 
     QNetworkReply* newReply = m_nam.data()->get( QNetworkRequest( url ) );
-    newReply->setProperty( "requestId", oldReply->property( "requestId" ) );
     newReply->setProperty( "requestData", oldReply->property( "requestData" ) );
     connect( newReply, SIGNAL( finished() ), SLOT( tracksSearchSlot() ) );
 }
@@ -197,7 +227,7 @@ MusicBrainzPlugin::tracksSearchSlot()
     QDomNodeList domNodeList = doc.elementsByTagName( "release" );
     if ( domNodeList.isEmpty() )
     {
-        emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+        emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
         return;
     }
 
@@ -214,7 +244,7 @@ MusicBrainzPlugin::tracksSearchSlot()
 
     if ( element.isNull() )
     {
-        emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+        emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
         return;
     }
 
@@ -223,7 +253,6 @@ MusicBrainzPlugin::tracksSearchSlot()
     QUrl url( requestString );
 
     QNetworkReply* newReply = m_nam.data()->get( QNetworkRequest( url ) );
-    newReply->setProperty( "requestId", oldReply->property( "requestId" ) );
     newReply->setProperty( "requestData", oldReply->property( "requestData" ) );
     connect( newReply, SIGNAL( finished() ), SLOT( tracksFoundSlot() ) );
 }
@@ -241,7 +270,7 @@ MusicBrainzPlugin::albumFoundSlot()
     QDomNodeList domNodeList = doc.elementsByTagName( "title" );
     if ( domNodeList.isEmpty() )
     {
-        emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+        emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
         return;
     }
 
@@ -256,7 +285,7 @@ MusicBrainzPlugin::albumFoundSlot()
     Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
     QVariantMap returnedData;
     returnedData["albums"] = albums;
-    emit info( reply->property( "requestId" ).toUInt(), requestData, returnedData );
+    emit info( requestData, returnedData );
 
     Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>();
     Tomahawk::InfoSystem::InfoStringHash criteria;
@@ -277,7 +306,7 @@ MusicBrainzPlugin::tracksFoundSlot()
     QDomNodeList domNodeList = doc.elementsByTagName( "recording" );
     if ( domNodeList.isEmpty() )
     {
-        emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+        emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
         return;
     }
 
@@ -297,7 +326,7 @@ MusicBrainzPlugin::tracksFoundSlot()
     Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
     QVariantMap returnedData;
     returnedData["tracks"] = tracks;
-    emit info( reply->property( "requestId" ).toUInt(), requestData, returnedData );
+    emit info( requestData, returnedData );
 
     Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>();
     Tomahawk::InfoSystem::InfoStringHash criteria;
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h
index 8e943bae3..444b30809 100644
--- a/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h
+++ b/src/libtomahawk/infosystem/infoplugins/generic/musicbrainzPlugin.h
@@ -42,7 +42,8 @@ public slots:
     void namChangedSlot( QNetworkAccessManager *nam );
 
 protected slots:
-    virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
+    virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
+    virtual void notInCacheSlot( InfoStringHash criteria, InfoRequestData requestData );
 
     virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data )
     {
@@ -51,12 +52,6 @@ protected slots:
         Q_UNUSED( data );
     }
 
-virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
-    {
-        Q_UNUSED( requestId );
-        Q_UNUSED( criteria );
-        Q_UNUSED( requestData );
-    }
 
 private slots:
     void artistSearchSlot();
@@ -67,7 +62,7 @@ private slots:
     void tracksFoundSlot();
 
 private:
-    bool isValidTrackData( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
+    bool isValidTrackData( Tomahawk::InfoSystem::InfoRequestData requestData );
 
     QWeakPointer< QNetworkAccessManager > m_nam;
 };
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.cpp
index ca2b96858..05811765a 100644
--- a/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.cpp
+++ b/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.cpp
@@ -53,17 +53,17 @@ MusixMatchPlugin::namChangedSlot( QNetworkAccessManager *nam )
 }
 
 void
-MusixMatchPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+MusixMatchPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     qDebug() << Q_FUNC_INFO;
-    if( !isValidTrackData( requestId, requestData ) || !requestData.input.canConvert< QVariantMap >() || m_nam.isNull() || requestData.type != Tomahawk::InfoSystem::InfoTrackLyrics )
+    if( !isValidTrackData( requestData ) || !requestData.input.canConvert< QVariantMap >() || m_nam.isNull() || requestData.type != Tomahawk::InfoSystem::InfoTrackLyrics )
         return;
     QVariantMap hash = requestData.input.value< QVariantMap >();
     QString artist = hash["artistName"].toString();
     QString track = hash["trackName"].toString();
     if( artist.isEmpty() || track.isEmpty() )
     {
-        emit info( requestId, requestData, QVariant() );
+        emit info( requestData, QVariant() );
         return;
     }
     qDebug() << "artist is " << artist << ", track is " << track;
@@ -73,32 +73,31 @@ MusixMatchPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData
     url.addQueryItem( "q_artist", artist );
     url.addQueryItem( "q_track", track );
     QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) );
-    reply->setProperty( "requestId", requestId );
     reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
 
     connect( reply, SIGNAL( finished() ), SLOT( trackSearchSlot() ) );
 }
 
 bool
-MusixMatchPlugin::isValidTrackData( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+MusixMatchPlugin::isValidTrackData( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     qDebug() << Q_FUNC_INFO;
     if ( requestData.input.isNull() || !requestData.input.isValid() || !requestData.input.canConvert< QVariantMap >() )
     {
-        emit info( requestId, requestData, QVariant() );
+        emit info( requestData, QVariant() );
         qDebug() << "MusixMatchPlugin::isValidTrackData: Data null, invalid, or can't convert";
         return false;
     }
     QVariantMap hash = requestData.input.value< QVariantMap >();
     if ( hash[ "trackName" ].toString().isEmpty() )
     {
-        emit info( requestId, requestData, QVariant() );
+        emit info( requestData, QVariant() );
         qDebug() << "MusixMatchPlugin::isValidTrackData: Track name is empty";
         return false;
     }
     if ( hash[ "artistName" ].toString().isEmpty() )
     {
-        emit info( requestId, requestData, QVariant() );
+        emit info( requestData, QVariant() );
         qDebug() << "MusixMatchPlugin::isValidTrackData: No artist name found";
         return false;
     }
@@ -119,7 +118,7 @@ MusixMatchPlugin::trackSearchSlot()
     QDomNodeList domNodeList = doc.elementsByTagName("track_id");
     if ( domNodeList.isEmpty() )
     {
-        emit info( oldReply->property( "requestId" ).toUInt(), oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+        emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
         return;
     }
     QString track_id = domNodeList.at(0).toElement().text();
@@ -128,7 +127,6 @@ MusixMatchPlugin::trackSearchSlot()
     url.addQueryItem( "apikey", m_apiKey );
     url.addQueryItem( "track_id", track_id );
     QNetworkReply* newReply = m_nam.data()->get( QNetworkRequest( url ) );
-    newReply->setProperty( "requestId", oldReply->property( "requestId" ) );
     newReply->setProperty( "requestData", oldReply->property( "requestData" ) );
     connect( newReply, SIGNAL( finished() ), SLOT( trackLyricsSlot() ) );
 }
@@ -146,10 +144,10 @@ MusixMatchPlugin::trackLyricsSlot()
     QDomNodeList domNodeList = doc.elementsByTagName( "lyrics_body" );
     if ( domNodeList.isEmpty() )
     {
-        emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+        emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
         return;
     }
     QString lyrics = domNodeList.at(0).toElement().text();
     qDebug() << "Emitting lyrics: " << lyrics;
-    emit info( reply->property( "requestId" ).toUInt(), reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant( lyrics ) );
+    emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant( lyrics ) );
 }
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.h
index a6ef3fbe3..40b1cc536 100644
--- a/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.h
+++ b/src/libtomahawk/infosystem/infoplugins/generic/musixmatchplugin.h
@@ -45,7 +45,7 @@ public slots:
     void namChangedSlot( QNetworkAccessManager *nam );
     
 protected slots:
-    virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
+    virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
 
     virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data )
     {
@@ -54,15 +54,14 @@ protected slots:
         Q_UNUSED( data );
     }
 
-virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
+virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
     {
-        Q_UNUSED( requestId );
         Q_UNUSED( criteria );
         Q_UNUSED( requestData );
     }
     
 private:
-    bool isValidTrackData( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
+    bool isValidTrackData( Tomahawk::InfoSystem::InfoRequestData requestData );
     
     QString m_apiKey;
 
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp
new file mode 100644
index 000000000..245ceb593
--- /dev/null
+++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.cpp
@@ -0,0 +1,410 @@
+/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com>
+ *
+ *   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 "spotifyPlugin.h"
+
+#include <QDir>
+#include <QSettings>
+#include <QCryptographicHash>
+#include <QNetworkConfiguration>
+#include <QNetworkReply>
+#include <QDomElement>
+
+#include "album.h"
+#include "typedefs.h"
+#include "audio/audioengine.h"
+#include "tomahawksettings.h"
+#include "utils/tomahawkutils.h"
+#include "utils/logger.h"
+#include "chartsplugin_data_p.h"
+
+#define SPOTIFY_API_URL "http://spotikea.tomahawk-player.org:10380/"
+#include <qjson/parser.h>
+#include <qjson/serializer.h>
+
+using namespace Tomahawk::InfoSystem;
+
+
+SpotifyPlugin::SpotifyPlugin()
+    : InfoPlugin()
+    , m_chartsFetchJobs( 0 )
+{
+
+    m_supportedGetTypes << InfoChart << InfoChartCapabilities;
+
+}
+
+
+SpotifyPlugin::~SpotifyPlugin()
+{
+    qDebug() << Q_FUNC_INFO;
+}
+
+
+void
+SpotifyPlugin::namChangedSlot( QNetworkAccessManager *nam )
+{
+    tDebug() << "SpotifyPlugin: namChangedSLot";
+    qDebug() << Q_FUNC_INFO;
+    if( !nam )
+        return;
+
+    m_nam = QWeakPointer< QNetworkAccessManager >( nam );
+
+    // we never need to re-fetch
+    if ( !m_allChartsMap.isEmpty() )
+        return;
+
+    /// We need to fetch possible types before they are asked for
+    tDebug() << "SpotifyPlugin: InfoChart fetching possible resources";
+
+    QUrl url = QUrl( QString( SPOTIFY_API_URL "toplist/charts" )  );
+    QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) );
+    tDebug() << Q_FUNC_INFO << "fetching:" << url;
+    connect( reply, SIGNAL( finished() ), SLOT( chartTypes() ) );
+    m_chartsFetchJobs++;
+
+}
+
+
+void
+SpotifyPlugin::dataError( Tomahawk::InfoSystem::InfoRequestData requestData )
+{
+    emit info( requestData, QVariant() );
+    return;
+}
+
+
+void
+SpotifyPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
+{
+    qDebug() << Q_FUNC_INFO << requestData.caller;
+    qDebug() << Q_FUNC_INFO << requestData.customData;
+
+    InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
+
+
+    switch ( requestData.type )
+    {
+        case InfoChart:
+            if ( !hash.contains( "chart_source" ) || hash["chart_source"] != "spotify" )
+            {
+                dataError( requestData );
+                break;
+            }
+            qDebug() << Q_FUNC_INFO << "InfoCHart req for" << hash["chart_source"];
+            fetchChart( requestData );
+            break;
+
+        case InfoChartCapabilities:
+            fetchChartCapabilities( requestData );
+            break;
+        default:
+            dataError( requestData );
+    }
+}
+
+
+void
+SpotifyPlugin::pushInfo( const QString caller, const Tomahawk::InfoSystem::InfoType type, const QVariant input )
+{
+    Q_UNUSED( caller )
+    Q_UNUSED( type)
+    Q_UNUSED( input )
+}
+
+void
+SpotifyPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData )
+{
+    if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
+    {
+        dataError( requestData );
+        return;
+    }
+    InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
+    Tomahawk::InfoSystem::InfoStringHash criteria;
+    if ( !hash.contains( "chart_id" ) )
+    {
+        dataError( requestData );
+        return;
+    } else {
+        criteria["chart_id"] = hash["chart_id"];
+    }
+
+    emit getCachedInfo( criteria, 604800000 /* Expire chart cache in 1 week */, requestData );
+}
+void
+SpotifyPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData )
+{
+    if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() )
+    {
+        dataError( requestData );
+        return;
+    }
+
+    Tomahawk::InfoSystem::InfoStringHash criteria;
+    emit getCachedInfo( criteria, 0, requestData );
+}
+
+void
+SpotifyPlugin::notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
+{
+    if ( !m_nam.data() )
+    {
+        tLog() << Q_FUNC_INFO << "Have a null QNAM, uh oh";
+        emit info( requestData, QVariant() );
+        return;
+    }
+
+
+    switch ( requestData.type )
+    {
+
+        case InfoChart:
+        {
+            /// Fetch the chart, we need source and id
+            QUrl url = QUrl( QString( SPOTIFY_API_URL "toplist/%1/" ).arg( criteria["chart_id"] ) );
+            qDebug() << Q_FUNC_INFO << "Getting chart url" << url;
+
+            QNetworkReply* reply = m_nam.data()->get( QNetworkRequest( url ) );
+            reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
+            connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) );
+            return;
+
+
+        }
+        case InfoChartCapabilities:
+        {
+            if ( m_chartsFetchJobs > 0 )
+            {
+                qDebug() << Q_FUNC_INFO << "InfoChartCapabilities still fetching!";
+                m_cachedRequests.append( requestData );
+                return;
+            }
+
+            emit info( requestData, m_allChartsMap );
+            return;
+        }
+
+        default:
+        {
+            tLog() << Q_FUNC_INFO << "Couldn't figure out what to do with this type of request after cache miss";
+            emit info( requestData, QVariant() );
+            return;
+        }
+    }
+}
+
+
+void
+SpotifyPlugin::chartTypes()
+{
+    /// Get possible chart type for specificSpotifyPlugin: InfoChart types returned chart source
+    tDebug() << Q_FUNC_INFO << "Got spotifychart type result";
+    QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
+
+    if ( reply->error() == QNetworkReply::NoError )
+    {
+        QJson::Parser p;
+        bool ok;
+        const QVariantMap res = p.parse( reply, &ok ).toMap();
+        const QVariantMap chartObj = res;
+
+        if ( !ok )
+        {
+            tLog() << Q_FUNC_INFO << "Failed to parse resources" << p.errorString() << "On line" << p.errorLine();
+
+            return;
+        }
+
+        QVariantMap charts;
+        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;
+
+           if( geo == "For me" )
+              continue; /// country = geo; Lets use this later, when we can get the spotify username from tomahawk
+           else if( geo == "Everywhere" )
+               country = geo;
+           else
+           {
+
+               QLocale l( QString( "en_%1" ).arg( geo ) );
+               country = Tomahawk::CountryUtils::fullCountryFromCode( geo );
+
+               for ( int i = 1; i < country.size(); i++ )
+               {
+                   if ( country.at( i ).isUpper() )
+                   {
+                       country.insert( i, " " );
+                       i++;
+                   }
+               }
+           }
+
+           QList< InfoStringHash > chart_types;
+           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();
+
+               InfoStringHash c;
+               c[ "id" ] = type + "/" + geoId;
+               c[ "label" ] = label;
+               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
+    {
+        tLog() << Q_FUNC_INFO << "Error fetching charts:" << reply->errorString();
+    }
+
+    m_chartsFetchJobs--;
+    if ( !m_cachedRequests.isEmpty() && m_chartsFetchJobs == 0 )
+    {
+        foreach ( InfoRequestData request, m_cachedRequests )
+        {
+            emit info( request, m_allChartsMap );
+        }
+        m_cachedRequests.clear();
+    }
+
+}
+
+void
+SpotifyPlugin::chartReturned()
+{
+
+    /// Chart request returned something! Woho
+    QNetworkReply* reply = qobject_cast<QNetworkReply*>( sender() );
+    QString url = reply->url().toString();
+    QVariantMap returnedData;
+    if ( reply->error() == QNetworkReply::NoError )
+    {
+        QJson::Parser p;
+        bool ok;
+        QVariantMap res = p.parse( reply, &ok ).toMap();
+
+        if ( !ok )
+        {
+            tLog() << "Failed to parse json from chart lookup:" << p.errorString() << "On line" << p.errorLine();
+            return;
+        }
+
+        /// SO we have a result, parse it!
+        QList< InfoStringHash > top_tracks;
+        QList< InfoStringHash > top_albums;
+        QStringList top_artists;
+
+        if( url.contains( "albums" ) )
+            setChartType( Album );
+        else if( url.contains( "tracks" ) )
+            setChartType( Track );
+        else if( url.contains( "artists" ) )
+            setChartType( Artist );
+        else
+            setChartType( None );
+
+        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();
+
+                if( chartType() == Track )
+                {
+                    InfoStringHash pair;
+                    pair["artist"] = artist;
+                    pair["track"] = title;
+                    top_tracks << pair;
+
+                    qDebug() << "SpotifyChart type is track";
+                }
+
+                if( chartType() == Album )
+                {
+
+                    InfoStringHash pair;
+                    pair["artist"] = artist;
+                    pair["album"] = title;
+                    top_albums << pair;
+                    qDebug() << "SpotifyChart type is album";
+                }
+
+                if( chartType() == Artist )
+                {
+
+                    top_artists << chartMap.value( "name" ).toString();
+                    qDebug() << "SpotifyChart type is artist";
+
+                }
+            }
+        }
+
+        if( chartType() == Track )
+        {
+            tDebug() << "ChartsPlugin:" << "\tgot " << top_tracks.size() << " tracks";
+            returnedData["tracks"] = QVariant::fromValue( top_tracks );
+            returnedData["type"] = "tracks";
+        }
+
+        if( chartType() == Album )
+        {
+            tDebug() << "ChartsPlugin:" << "\tgot " << top_albums.size() << " albums";
+            returnedData["albums"] = QVariant::fromValue( top_albums );
+            returnedData["type"] = "albums";
+        }
+
+        if( chartType() == Artist )
+        {
+            tDebug() << "ChartsPlugin:" << "\tgot " << top_artists.size() << " artists";
+            returnedData["artists"] = top_artists;
+            returnedData["type"] = "artists";
+        }
+
+        Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
+
+
+        emit info( requestData, returnedData );
+
+    }
+    else
+        qDebug() << "Network error in fetching chart:" << reply->url().toString();
+
+}
diff --git a/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h
new file mode 100644
index 000000000..4a27d826c
--- /dev/null
+++ b/src/libtomahawk/infosystem/infoplugins/generic/spotifyPlugin.h
@@ -0,0 +1,80 @@
+/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com>
+ *
+ *   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 SpotifyPlugin_H
+#define SpotifyPlugin_H
+
+#include "infosystem/infosystem.h"
+#include "infosystem/infosystemworker.h"
+#include <QNetworkReply>
+#include <QObject>
+
+class QNetworkReply;
+
+namespace Tomahawk
+{
+
+namespace InfoSystem
+{
+
+class SpotifyPlugin : public InfoPlugin
+{
+    Q_OBJECT
+
+public:
+    SpotifyPlugin();
+    virtual ~SpotifyPlugin();
+
+    enum ChartType {
+        None =      0x00,
+        Track =     0x01,
+        Album =     0x02,
+        Artist =    0x04
+
+    };
+ void setChartType( ChartType type ) { m_chartType = type; }
+ ChartType chartType() const { return m_chartType; }
+
+public slots:
+    void chartReturned();
+    void chartTypes();
+    void namChangedSlot( QNetworkAccessManager *nam );
+
+protected slots:
+    virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
+    virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData );
+    virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data );
+
+private:
+    void fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData );
+    void fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData );
+    void dataError( Tomahawk::InfoSystem::InfoRequestData requestData );
+
+
+    ChartType m_chartType;
+    QVariantMap m_allChartsMap;
+    uint m_chartsFetchJobs;
+    QList< InfoRequestData > m_cachedRequests;
+    QWeakPointer< QNetworkAccessManager > m_nam;
+};
+
+}
+
+}
+
+#endif // SpotifyPlugin_H
diff --git a/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.h b/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.h
index c602d80fc..5f12a510f 100644
--- a/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.h
+++ b/src/libtomahawk/infosystem/infoplugins/mac/adiumplugin.h
@@ -41,9 +41,8 @@ public:
     virtual ~AdiumPlugin();
 
 protected slots:
-    virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+    virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
     {
-        Q_UNUSED( requestId );
         Q_UNUSED( requestData );
     }
     
@@ -52,9 +51,8 @@ protected slots:
 public slots:
     void namChangedSlot( QNetworkAccessManager* nam );
 
-    virtual void notInCacheSlot( uint requestId, const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
+    virtual void notInCacheSlot( const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
     {
-        Q_UNUSED( requestId );
         Q_UNUSED( criteria );
         Q_UNUSED( requestData );
     }
diff --git a/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h b/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h
index ed5dfabf8..d5fbc4759 100644
--- a/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h
+++ b/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h
@@ -38,17 +38,15 @@ public:
     virtual void namChangedSlot( QNetworkAccessManager* ) {}
 
 protected slots:
-    virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+    virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
     {
-        Q_UNUSED( requestId );
         Q_UNUSED( requestData );
     }
 
     virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant pushData );
 
-    virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
+    virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
     {
-        Q_UNUSED( requestId );
         Q_UNUSED( criteria );
         Q_UNUSED( requestData );
     }
diff --git a/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.cpp b/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.cpp
index 3e1fa3466..7725b68f2 100644
--- a/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.cpp
+++ b/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.cpp
@@ -432,9 +432,8 @@ MprisPlugin::Stop()
 // InfoPlugin Methods
 
 void
-MprisPlugin::getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData )
+MprisPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
-  Q_UNUSED( requestId );
   Q_UNUSED( requestData );
   qDebug() << Q_FUNC_INFO;
 
diff --git a/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.h b/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.h
index c708e2a23..81d8d1507 100644
--- a/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.h
+++ b/src/libtomahawk/infosystem/infoplugins/unix/mprisplugin.h
@@ -119,9 +119,8 @@ public:
 public slots:
     void namChangedSlot( QNetworkAccessManager* /*nam*/ ) {} // unused
 
-    virtual void notInCacheSlot( uint requestId, const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
+    virtual void notInCacheSlot( const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
     {
-        Q_UNUSED( requestId );
         Q_UNUSED( criteria );
         Q_UNUSED( requestData );
     }
@@ -143,7 +142,7 @@ public slots:
 
 
 protected slots:
-    void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData );
+    void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
     void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input );
 
 private slots:
diff --git a/src/libtomahawk/infosystem/infosystem.cpp b/src/libtomahawk/infosystem/infosystem.cpp
index 5fa2325bb..505aa2119 100644
--- a/src/libtomahawk/infosystem/infosystem.cpp
+++ b/src/libtomahawk/infosystem/infosystem.cpp
@@ -74,12 +74,16 @@ InfoSystem::InfoSystem( QObject *parent )
 
     connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( newNam() ) );
 
-    connect( m_cache.data(), SIGNAL( info( uint, Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
-             m_worker.data(), SLOT( infoSlot( uint, Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection );
+    connect( m_cache.data(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
+             m_worker.data(), SLOT( infoSlot( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection );
 
     connect( m_worker.data(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
              this,       SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), Qt::UniqueConnection );
+
     connect( m_worker.data(), SIGNAL( finished( QString ) ), this, SIGNAL( finished( QString ) ), Qt::UniqueConnection );
+
+    connect( m_worker.data(), SIGNAL( finished( QString, Tomahawk::InfoSystem::InfoType ) ),
+             this, SIGNAL( finished( QString, Tomahawk::InfoSystem::InfoType ) ), Qt::UniqueConnection );
 }
 
 InfoSystem::~InfoSystem()
@@ -120,10 +124,10 @@ InfoSystem::newNam() const
 
 
 void
-InfoSystem::getInfo( const InfoRequestData &requestData, uint timeoutMillis, bool allSources )
+InfoSystem::getInfo( const InfoRequestData &requestData )
 {
     qDebug() << Q_FUNC_INFO;
-    QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ), Q_ARG( uint, timeoutMillis ), Q_ARG( bool, allSources ) );
+    QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) );
 }
 
 
@@ -133,11 +137,13 @@ InfoSystem::getInfo( const QString &caller, const QVariantMap &customData, const
     InfoRequestData requestData;
     requestData.caller = caller;
     requestData.customData = customData;
+    requestData.allSources = allSources;
     Q_FOREACH( InfoType type, inputMap.keys() )
     {
         requestData.type = type;
         requestData.input = inputMap[ type ];
-        QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ), Q_ARG( uint, ( timeoutMap.contains( type ) ? timeoutMap[ type ] : 0 ) ), Q_ARG( bool, allSources ) );
+        requestData.timeoutMillis = timeoutMap.contains( type ) ? timeoutMap[ type ] : 10000;
+        QMetaObject::invokeMethod( m_worker.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) );
     }
 }
 
diff --git a/src/libtomahawk/infosystem/infosystem.h b/src/libtomahawk/infosystem/infosystem.h
index ebef34a42..01be362e5 100644
--- a/src/libtomahawk/infosystem/infosystem.h
+++ b/src/libtomahawk/infosystem/infosystem.h
@@ -32,6 +32,7 @@
 #include <QtCore/QStringList>
 
 #include "dllmacro.h"
+#include "utils/tomahawkutils.h"
 
 class QNetworkAccessManager;
 
@@ -43,28 +44,29 @@ class InfoSystemCache;
 class InfoSystemWorker;
 
 enum InfoType { // as items are saved in cache, mark them here to not change them
-    InfoTrackID = 0,
-    InfoTrackArtist = 1,
-    InfoTrackAlbum = 2,
-    InfoTrackGenre = 3,
-    InfoTrackComposer = 4,
-    InfoTrackDate = 5,
-    InfoTrackNumber = 6,
-    InfoTrackDiscNumber = 7,
-    InfoTrackBitRate = 8,
-    InfoTrackLength = 9,
-    InfoTrackSampleRate = 10,
-    InfoTrackFileSize = 11,
-    InfoTrackBPM = 12,
-    InfoTrackReplayGain = 13,
-    InfoTrackReplayPeakGain = 14,
-    InfoTrackLyrics = 15,
-    InfoTrackLocation = 16,
-    InfoTrackProfile = 17,
-    InfoTrackEnergy = 18,
-    InfoTrackDanceability = 19,
-    InfoTrackTempo = 20,
-    InfoTrackLoudness = 21,
+    InfoNoInfo = 0, //WARNING: *ALWAYS* keep this first!
+    InfoTrackID = 1,
+    InfoTrackArtist = 2,
+    InfoTrackAlbum = 3,
+    InfoTrackGenre = 4,
+    InfoTrackComposer = 5,
+    InfoTrackDate = 6,
+    InfoTrackNumber = 7,
+    InfoTrackDiscNumber = 8,
+    InfoTrackBitRate = 9,
+    InfoTrackLength = 10,
+    InfoTrackSampleRate = 11,
+    InfoTrackFileSize = 12,
+    InfoTrackBPM = 13,
+    InfoTrackReplayGain = 14,
+    InfoTrackReplayPeakGain = 15,
+    InfoTrackLyrics = 16,
+    InfoTrackLocation = 17,
+    InfoTrackProfile = 18,
+    InfoTrackEnergy = 19,
+    InfoTrackDanceability = 20,
+    InfoTrackTempo = 21,
+    InfoTrackLoudness = 22,
 
     InfoArtistID = 25,
     InfoArtistName = 26,
@@ -120,14 +122,40 @@ enum InfoType { // as items are saved in cache, mark them here to not change the
 
     InfoNotifyUser = 100,
 
-    InfoNoInfo = 101 //WARNING: *ALWAYS* keep this last!
+    InfoLastInfo = 101 //WARNING: *ALWAYS* keep this last!
 };
 
 struct InfoRequestData {
+    quint64 requestId;
+    quint64 internalId; //do not assign to this; it may get overwritten by the InfoSystem
     QString caller;
     Tomahawk::InfoSystem::InfoType type;
     QVariant input;
     QVariantMap customData;
+    uint timeoutMillis;
+    bool allSources;
+    
+    InfoRequestData()
+        : requestId( TomahawkUtils::infosystemRequestId() )
+        , internalId( TomahawkUtils::infosystemRequestId() )
+        , caller( QString() )
+        , type( Tomahawk::InfoSystem::InfoNoInfo )
+        , input( QVariant() )
+        , customData( QVariantMap() )
+        , timeoutMillis( 10000 )
+        , allSources( false )
+        {}
+    
+    InfoRequestData( const quint64 rId, const QString &callr, const Tomahawk::InfoSystem::InfoType typ, const QVariant &inputvar, const QVariantMap &custom )
+        : requestId( rId )
+        , internalId( TomahawkUtils::infosystemRequestId() )
+        , caller( callr )
+        , type( typ )
+        , input( inputvar )
+        , customData( custom )
+        , timeoutMillis( 10000 )
+        , allSources( false )
+        {}
 };
 
 typedef QMap< InfoType, QVariant > InfoTypeMap;
@@ -147,15 +175,15 @@ public:
     QSet< InfoType > supportedPushTypes() const { return m_supportedPushTypes; }
 
 signals:
-    void getCachedInfo( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData );
-    void info( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
+    void getCachedInfo( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData );
+    void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
 
     void updateCache( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 maxAge, Tomahawk::InfoSystem::InfoType type, QVariant output );
 
 protected slots:
-    virtual void getInfo( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData ) = 0;
+    virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) = 0;
     virtual void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant data ) = 0;
-    virtual void notInCacheSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) = 0;
+    virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) = 0;
 
     virtual void namChangedSlot( QNetworkAccessManager *nam ) = 0;
 
@@ -212,7 +240,7 @@ public:
     InfoSystem( QObject *parent );
     ~InfoSystem();
 
-    void getInfo( const InfoRequestData &requestData, uint timeoutMillis = 0, bool allSources = false );
+    void getInfo( const InfoRequestData &requestData );
     //WARNING: if changing timeoutMillis above, also change in below function in .cpp file
     void getInfo( const QString &caller, const QVariantMap &customData, const InfoTypeMap &inputMap, const InfoTimeoutMap &timeoutMap = InfoTimeoutMap(), bool allSources = false );
     void pushInfo( const QString &caller, const InfoType type, const QVariant &input );
@@ -221,6 +249,7 @@ public:
 signals:
     void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
     void finished( QString target );
+    void finished( QString target, Tomahawk::InfoSystem::InfoType type );
 
 public slots:
     void newNam() const;
diff --git a/src/libtomahawk/infosystem/infosystemcache.cpp b/src/libtomahawk/infosystem/infosystemcache.cpp
index 7ded3a9a0..7ee11a4cf 100644
--- a/src/libtomahawk/infosystem/infosystemcache.cpp
+++ b/src/libtomahawk/infosystem/infosystemcache.cpp
@@ -75,7 +75,7 @@ InfoSystemCache::doUpgrade( uint oldVersion, uint newVersion )
     {
         qDebug() << Q_FUNC_INFO << "Wiping cache";
 
-        for ( int i = 0; i <= InfoNoInfo; i++ )
+        for ( int i = InfoNoInfo; i <= InfoLastInfo; i++ )
         {
             InfoType type = (InfoType)(i);
             const QString cacheDirName = m_cacheBaseDir + QString::number( (int)type );
@@ -96,7 +96,7 @@ InfoSystemCache::pruneTimerFired()
     qDebug() << Q_FUNC_INFO << "Pruning infosystemcache";
     qlonglong currentMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch();
 
-    for ( int i = 0; i <= InfoNoInfo; i++ )
+    for ( int i = InfoNoInfo; i <= InfoLastInfo; i++ )
     {
         InfoType type = (InfoType)(i);
         QHash< QString, QString > fileLocationHash = m_fileLocationCache[type];
@@ -121,7 +121,7 @@ InfoSystemCache::pruneTimerFired()
 
 
 void
-InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData )
+InfoSystemCache::getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData )
 {
     QObject* sendingObj = sender();
     const QString criteriaHashVal = criteriaMd5( criteria );
@@ -133,7 +133,7 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt
         {
             //We already know of some values, so no need to re-read the directory again as it's already happened
             qDebug() << Q_FUNC_INFO << "notInCache -- filelocationhash empty";
-            notInCache( sendingObj, requestId, criteria, requestData );
+            notInCache( sendingObj, criteria, requestData );
             return;
         }
 
@@ -143,7 +143,7 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt
         {
             //Dir doesn't exist so clearly not in cache
             qDebug() << Q_FUNC_INFO << "notInCache -- dir doesn't exist";
-            notInCache( sendingObj, requestId, criteria, requestData );
+            notInCache( sendingObj, criteria, requestData );
             return;
         }
 
@@ -160,7 +160,7 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt
         {
             //Still didn't find it? It's really not in the cache then
             qDebug() << Q_FUNC_INFO << "notInCache -- filelocationhash doesn't contain criteria val";
-            notInCache( sendingObj, requestId, criteria, requestData );
+            notInCache( sendingObj, criteria, requestData );
             return;
         }
     }
@@ -180,7 +180,7 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt
         m_dataCache.remove( criteriaHashValWithType );
 
         qDebug() << Q_FUNC_INFO << "notInCache -- file was stale";
-        notInCache( sendingObj, requestId, criteria, requestData );
+        notInCache( sendingObj, criteria, requestData );
         return;
     }
     else if ( newMaxAge > 0 )
@@ -190,7 +190,7 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt
         if ( !QFile::rename( file.canonicalFilePath(), newFilePath ) )
         {
             qDebug() << Q_FUNC_INFO << "notInCache -- failed to move old cache file to new location";
-            notInCache( sendingObj, requestId, criteria, requestData );
+            notInCache( sendingObj, criteria, requestData );
             return;
         }
 
@@ -204,25 +204,26 @@ InfoSystemCache::getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoSt
         QVariant output = cachedSettings.value( "data" );
         m_dataCache.insert( criteriaHashValWithType, new QVariant( output ) );
 
-        emit info( requestId, requestData, output );
+        emit info( requestData, output );
     }
     else
     {
-        emit info( requestId, requestData, QVariant( *( m_dataCache[ criteriaHashValWithType ] ) ) );
+        emit info( requestData, QVariant( *( m_dataCache[ criteriaHashValWithType ] ) ) );
     }
 }
 
 
 void
-InfoSystemCache::notInCache( QObject *receiver, uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
+InfoSystemCache::notInCache( QObject *receiver, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData )
 {
-    QMetaObject::invokeMethod( receiver, "notInCacheSlot", Q_ARG( uint, requestId ), Q_ARG( Tomahawk::InfoSystem::InfoStringHash, criteria ), Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) );
+    QMetaObject::invokeMethod( receiver, "notInCacheSlot", Q_ARG( Tomahawk::InfoSystem::InfoStringHash, criteria ), Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) );
 }
 
 
 void
 InfoSystemCache::updateCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 maxAge, Tomahawk::InfoSystem::InfoType type, QVariant output )
 {
+    qDebug() << Q_FUNC_INFO;
     const QString criteriaHashVal = criteriaMd5( criteria );
     const QString criteriaHashValWithType = criteriaMd5( criteria, type );
     const QString cacheDir = m_cacheBaseDir + QString::number( (int)type );
@@ -283,7 +284,7 @@ InfoSystemCache::criteriaMd5( const Tomahawk::InfoSystem::InfoStringHash &criter
         md5.addData( key.toUtf8() );
         md5.addData( criteria[key].toUtf8() );
     }
-    if ( type != Tomahawk::InfoSystem::InfoNoInfo )
+    if ( type != Tomahawk::InfoSystem::InfoNoInfo && type != Tomahawk::InfoSystem::InfoLastInfo )
         md5.addData( QString::number( (int)type ).toUtf8() );
     return md5.result().toHex();
 }
diff --git a/src/libtomahawk/infosystem/infosystemcache.h b/src/libtomahawk/infosystem/infosystemcache.h
index 5a013d663..8910a6209 100644
--- a/src/libtomahawk/infosystem/infosystemcache.h
+++ b/src/libtomahawk/infosystem/infosystemcache.h
@@ -43,17 +43,17 @@ public:
     virtual ~InfoSystemCache();
 
 signals:
-    void info( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
+    void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
 
 public slots:
-    void getCachedInfoSlot( uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData );
+    void getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 newMaxAge, Tomahawk::InfoSystem::InfoRequestData requestData );
     void updateCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 maxAge, Tomahawk::InfoSystem::InfoType type, QVariant output );
 
 private slots:
     void pruneTimerFired();
 
 private:
-    void notInCache( QObject *receiver, uint requestId, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData );
+    void notInCache( QObject *receiver, Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData );
     void doUpgrade( uint oldVersion, uint newVersion );
     const QString criteriaMd5( const Tomahawk::InfoSystem::InfoStringHash &criteria, Tomahawk::InfoSystem::InfoType type = Tomahawk::InfoSystem::InfoNoInfo ) const;
     
diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp
index 9ae11951c..b58394130 100644
--- a/src/libtomahawk/infosystem/infosystemworker.cpp
+++ b/src/libtomahawk/infosystem/infosystemworker.cpp
@@ -26,8 +26,10 @@
 #include "infoplugins/generic/echonestplugin.h"
 #include "infoplugins/generic/musixmatchplugin.h"
 #include "infoplugins/generic/chartsplugin.h"
+#include "infoplugins/generic/spotifyPlugin.h"
 #include "infoplugins/generic/lastfmplugin.h"
 #include "infoplugins/generic/musicbrainzPlugin.h"
+#include "infoplugins/generic/hypemPlugin.h"
 #include "utils/tomahawkutils.h"
 #include "utils/logger.h"
 
@@ -40,6 +42,7 @@
 #endif
 
 #include "lastfm/NetworkAccessManager"
+#include "infoplugins/generic/RoviPlugin.h"
 
 namespace Tomahawk
 {
@@ -49,7 +52,6 @@ namespace InfoSystem
 
 InfoSystemWorker::InfoSystemWorker()
     : QObject()
-    , m_nextRequest( 0 )
 {
 //    qDebug() << Q_FUNC_INFO;
 
@@ -92,6 +94,15 @@ InfoSystemWorker::init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache> cac
     InfoPluginPtr sptr( new ChartsPlugin() );
     m_plugins.append( sptr );
     registerInfoTypes( sptr, sptr.data()->supportedGetTypes(), sptr.data()->supportedPushTypes() );
+    InfoPluginPtr roviptr( new RoviPlugin() );
+    m_plugins.append( roviptr );
+    registerInfoTypes( roviptr, roviptr.data()->supportedGetTypes(), roviptr.data()->supportedPushTypes() );
+    InfoPluginPtr spotptr( new SpotifyPlugin() );
+    m_plugins.append( spotptr );
+    registerInfoTypes( spotptr, spotptr.data()->supportedGetTypes(), spotptr.data()->supportedPushTypes() );
+    InfoPluginPtr hypeptr( new hypemPlugin() );
+    m_plugins.append( hypeptr );
+    registerInfoTypes( hypeptr, hypeptr.data()->supportedGetTypes(), hypeptr.data()->supportedPushTypes() );
 
 
     #ifdef Q_WS_MAC
@@ -105,24 +116,24 @@ InfoSystemWorker::init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache> cac
     registerInfoTypes( fdonotifyptr, fdonotifyptr.data()->supportedGetTypes(), fdonotifyptr.data()->supportedPushTypes() );
     InfoPluginPtr mprisptr( new MprisPlugin() );
     m_plugins.append( mprisptr );
-    registerInfoTypes( mprisptr, mprisptr.data()->supportedGetTypes(), mprisptr.data()->supportedPushTypes() );    
+    registerInfoTypes( mprisptr, mprisptr.data()->supportedGetTypes(), mprisptr.data()->supportedPushTypes() );
     #endif
 
     Q_FOREACH( InfoPluginPtr plugin, m_plugins )
     {
         connect(
                 plugin.data(),
-                SIGNAL( info( uint, Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
+                SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
                 this,
-                SLOT( infoSlot( uint, Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
+                SLOT( infoSlot( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
                 Qt::UniqueConnection
             );
 
         connect(
                 plugin.data(),
-                SIGNAL( getCachedInfo( uint, Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ),
+                SIGNAL( getCachedInfo( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ),
                 cache.data(),
-                SLOT( getCachedInfoSlot( uint, Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) )
+                SLOT( getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) )
             );
         connect(
                 plugin.data(),
@@ -165,19 +176,19 @@ InfoSystemWorker::determineOrderedMatches( const InfoType type ) const
 
 
 void
-InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, uint timeoutMillis, bool allSources )
+InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
 {
-//    qDebug() << Q_FUNC_INFO;
+    //qDebug() << Q_FUNC_INFO << "type is " << requestData.type << " and allSources = " << (allSources ? "true" : "false" );
 
     QList< InfoPluginPtr > providers = determineOrderedMatches( requestData.type );
     if ( providers.isEmpty() )
     {
         emit info( requestData, QVariant() );
-        checkFinished( requestData.caller );
+        checkFinished( requestData );
         return;
     }
 
-    if ( !allSources )
+    if ( !requestData.allSources )
         providers = QList< InfoPluginPtr >( providers.mid( 0, 1 ) );
 
     bool foundOne = false;
@@ -187,12 +198,22 @@ InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, ui
             continue;
 
         foundOne = true;
-        uint requestId = ++m_nextRequest;
+
+        if ( requestData.allSources || m_savedRequestMap.contains( requestData.requestId ) )
+        {
+            if ( m_savedRequestMap.contains( requestData.requestId ) )
+                tDebug() << Q_FUNC_INFO << "Warning: reassigning requestId because it already exists";
+            requestData.internalId = TomahawkUtils::infosystemRequestId();
+        }
+        else
+            requestData.internalId = requestData.requestId;
+
+        quint64 requestId = requestData.internalId;
         m_requestSatisfiedMap[ requestId ] = false;
-        if ( timeoutMillis != 0 )
+        if ( requestData.timeoutMillis != 0 )
         {
             qint64 currMs = QDateTime::currentMSecsSinceEpoch();
-            m_timeRequestMapper.insert( currMs + timeoutMillis, requestId );
+            m_timeRequestMapper.insert( currMs + requestData.timeoutMillis, requestId );
         }
     //    qDebug() << "Assigning request with requestId" << requestId << "and type" << requestData.type;
         m_dataTracker[ requestData.caller ][ requestData.type ] = m_dataTracker[ requestData.caller ][ requestData.type ] + 1;
@@ -205,19 +226,19 @@ InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, ui
         data->customData = requestData.customData;
         m_savedRequestMap[ requestId ] = data;
 
-        QMetaObject::invokeMethod( ptr.data(), "getInfo", Qt::QueuedConnection, Q_ARG( uint, requestId ), Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) );
+        QMetaObject::invokeMethod( ptr.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) );
     }
 
     if ( !foundOne )
     {
         emit info( requestData, QVariant() );
-        checkFinished( requestData.caller );
+        checkFinished( requestData );
     }
 }
 
 
 void
-InfoSystemWorker::pushInfo( QString caller, InfoType type, QVariant input )
+InfoSystemWorker::pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input )
 {
 //    qDebug() << Q_FUNC_INFO;
 
@@ -230,10 +251,12 @@ InfoSystemWorker::pushInfo( QString caller, InfoType type, QVariant input )
 
 
 void
-InfoSystemWorker::infoSlot( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
+InfoSystemWorker::infoSlot( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
 {
 //    qDebug() << Q_FUNC_INFO << "with requestId" << requestId;
 
+    quint64 requestId = requestData.internalId;
+
     if ( m_dataTracker[ requestData.caller ][ requestData.type ] == 0 )
     {
 //        qDebug() << Q_FUNC_INFO << "Caller was not waiting for that type of data!";
@@ -252,23 +275,23 @@ InfoSystemWorker::infoSlot( uint requestId, Tomahawk::InfoSystem::InfoRequestDat
 //    qDebug() << "Current count in dataTracker for target" << requestData.caller << "and type" << requestData.type << "is" << m_dataTracker[ requestData.caller ][ requestData.type ];
     delete m_savedRequestMap[ requestId ];
     m_savedRequestMap.remove( requestId );
-    checkFinished( requestData.caller );
+    checkFinished( requestData );
 }
 
 
 void
-InfoSystemWorker::checkFinished( const QString &target )
+InfoSystemWorker::checkFinished( const Tomahawk::InfoSystem::InfoRequestData &requestData )
 {
-    Q_FOREACH( InfoType testtype, m_dataTracker[ target ].keys() )
+    if ( m_dataTracker[ requestData.caller ][ requestData.type ] == 0 )
+        emit finished( requestData.caller, requestData.type );
+    
+    Q_FOREACH( InfoType testtype, m_dataTracker[ requestData.caller ].keys() )
     {
-        if ( m_dataTracker[ target ][ testtype ] != 0)
-        {
-//            qDebug() << "Found outstanding request of type" << testtype;
+        if ( m_dataTracker[ requestData.caller ][ testtype ] != 0 )
             return;
-        }
     }
 //    qDebug() << "Emitting finished with target" << target;
-    emit finished( target );
+    emit finished( requestData.caller );
 }
 
 
@@ -278,7 +301,7 @@ InfoSystemWorker::checkTimeoutsTimerFired()
     qint64 currTime = QDateTime::currentMSecsSinceEpoch();
     Q_FOREACH( qint64 time, m_timeRequestMapper.keys() )
     {
-        Q_FOREACH( uint requestId, m_timeRequestMapper.values( time ) )
+        Q_FOREACH( quint64 requestId, m_timeRequestMapper.values( time ) )
         {
             if ( time < currTime )
             {
@@ -313,7 +336,7 @@ InfoSystemWorker::checkTimeoutsTimerFired()
                 if ( !m_timeRequestMapper.count( time ) )
                     m_timeRequestMapper.remove( time );
 
-                checkFinished( returnData.caller );
+                checkFinished( returnData );
             }
             else
             {
@@ -338,18 +361,31 @@ InfoSystemWorker::nam() const
 void
 InfoSystemWorker::newNam()
 {
-//    qDebug() << Q_FUNC_INFO << " begin";
-
     QNetworkAccessManager *oldNam = TomahawkUtils::nam();
     if ( oldNam && oldNam->thread() == thread() )
     {
-//        qDebug() << Q_FUNC_INFO << "Using old nam as it's the same thread (GUI) as me";
-        m_nam = QWeakPointer< QNetworkAccessManager >( oldNam );
-        emit namChanged( m_nam.data() );
+        if ( m_nam.data() != oldNam )
+        {
+            m_nam = QWeakPointer< QNetworkAccessManager >( oldNam );
+            emit namChanged( m_nam.data() );
+        }
         return;
     }
 
-//    qDebug() << Q_FUNC_INFO << "No nam exists, or it's a different thread, creating a new one";
+    if
+        (
+            oldNam &&
+            !m_nam.isNull() &&
+            oldNam->configuration() == m_nam.data()->configuration() &&
+            oldNam->networkAccessible() == m_nam.data()->networkAccessible()
+        )
+    {
+        TomahawkUtils::NetworkProxyFactory fac1 = *( dynamic_cast< TomahawkUtils::NetworkProxyFactory * >( oldNam->proxyFactory() ) );
+        TomahawkUtils::NetworkProxyFactory fac2 = *( dynamic_cast< TomahawkUtils::NetworkProxyFactory * >( m_nam.data()->proxyFactory() ) );
+        if ( fac1 == fac2 )
+            return;
+    }
+
     QNetworkAccessManager* newNam;
 #ifdef LIBLASTFM_FOUND
     newNam = new lastfm::NetworkAccessManager( this );
diff --git a/src/libtomahawk/infosystem/infosystemworker.h b/src/libtomahawk/infosystem/infosystemworker.h
index 347861dd9..3bcdd1b62 100644
--- a/src/libtomahawk/infosystem/infosystemworker.h
+++ b/src/libtomahawk/infosystem/infosystemworker.h
@@ -52,15 +52,16 @@ public:
 signals:
     void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
     void finished( QString target );
+    void finished( QString target, Tomahawk::InfoSystem::InfoType type );
     
     void namChanged( QNetworkAccessManager* );
 
 public slots:
     void init( QWeakPointer< Tomahawk::InfoSystem::InfoSystemCache > cache );
-    void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData, uint timeoutMillis, bool allSources );
+    void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData );
     void pushInfo( QString caller, Tomahawk::InfoSystem::InfoType type, QVariant input );
 
-    void infoSlot( uint requestId, Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
+    void infoSlot( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output );
     
     void newNam();
 
@@ -69,11 +70,11 @@ private slots:
     
 private:
 
-    void checkFinished( const QString &target );
+    void checkFinished( const Tomahawk::InfoSystem::InfoRequestData &target );
     QList< InfoPluginPtr > determineOrderedMatches( const InfoType type ) const;
     
     QHash< QString, QHash< InfoType, int > > m_dataTracker;
-    QMultiMap< qint64, uint > m_timeRequestMapper;
+    QMultiMap< qint64, quint64 > m_timeRequestMapper;
     QHash< uint, bool > m_requestSatisfiedMap;
     QHash< uint, InfoRequestData* > m_savedRequestMap;
     
@@ -85,8 +86,6 @@ private:
 
     QWeakPointer< QNetworkAccessManager> m_nam;
 
-    uint m_nextRequest;
-
     QTimer m_checkTimeoutsTimer;
 };
 
diff --git a/src/libtomahawk/network/portfwdthread.cpp b/src/libtomahawk/network/portfwdthread.cpp
index 78af1c27c..b0cb98e12 100644
--- a/src/libtomahawk/network/portfwdthread.cpp
+++ b/src/libtomahawk/network/portfwdthread.cpp
@@ -81,7 +81,7 @@ PortFwdThread::work()
             qDebug() << "Trying to setup portfwd on" << tryport;
             if ( m_portfwd->add( tryport, m_port ) )
             {
-                QString pubip = QString( m_portfwd->external_ip().c_str() );
+                QString pubip = QString( m_portfwd->external_ip().c_str() ).trimmed();
                 m_externalAddress = QHostAddress( pubip );
                 m_externalPort = tryport;
                 tDebug() << "External servent address detected as" << pubip << ":" << m_externalPort;
diff --git a/src/libtomahawk/playlist/albumitemdelegate.cpp b/src/libtomahawk/playlist/albumitemdelegate.cpp
index 4758e3c1f..377c0eddd 100644
--- a/src/libtomahawk/playlist/albumitemdelegate.cpp
+++ b/src/libtomahawk/playlist/albumitemdelegate.cpp
@@ -31,6 +31,8 @@
 
 #include "playlist/albumitem.h"
 #include "playlist/albumproxymodel.h"
+#include <QMouseEvent>
+#include <viewmanager.h>
 
 
 AlbumItemDelegate::AlbumItemDelegate( QAbstractItemView* parent, AlbumProxyModel* proxy )
@@ -38,7 +40,6 @@ AlbumItemDelegate::AlbumItemDelegate( QAbstractItemView* parent, AlbumProxyModel
     , m_view( parent )
     , m_model( proxy )
 {
-    m_shadowPixmap = QPixmap( RESPATH "images/cover-shadow.png" );
     m_defaultCover = QPixmap( RESPATH "images/no-album-art-placeholder.png" );
 }
 
@@ -63,9 +64,30 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
     qApp->style()->drawControl( QStyle::CE_ItemViewItem, &opt, painter );
 
     painter->save();
+    painter->setRenderHint( QPainter::Antialiasing );
 
-//    painter->setRenderHint( QPainter::Antialiasing );
-//    painter->drawPixmap( option.rect.adjusted( 4, 4, -4, -38 ), m_shadowPixmap );
+    if ( !( option.state & QStyle::State_Selected ) )
+    {
+        QRect shadowRect = option.rect.adjusted( 5, 4, -5, -40 );
+        painter->setPen( QColor( 90, 90, 90 ) );
+        painter->drawRoundedRect( shadowRect, 0.5, 0.5 );
+
+        QPen shadowPen( QColor( 30, 30, 30 ) );
+        shadowPen.setWidth( 0.4 );
+        painter->drawLine( shadowRect.bottomLeft() + QPoint( -1, 2 ), shadowRect.bottomRight() + QPoint( 1, 2 ) );
+
+        shadowPen.setColor( QColor( 160, 160, 160 ) );
+        painter->setPen( shadowPen );
+        painter->drawLine( shadowRect.topLeft() + QPoint( -1, 2 ), shadowRect.bottomLeft() + QPoint( -1, 2 ) );
+        painter->drawLine( shadowRect.topRight() + QPoint( 2, 2 ), shadowRect.bottomRight() + QPoint( 2, 2 ) );
+        painter->drawLine( shadowRect.bottomLeft() + QPoint( 0, 3 ), shadowRect.bottomRight() + QPoint( 0, 3 ) );
+
+        shadowPen.setColor( QColor( 180, 180, 180 ) );
+        painter->setPen( shadowPen );
+        painter->drawLine( shadowRect.topLeft() + QPoint( -2, 3 ), shadowRect.bottomLeft() + QPoint( -2, 1 ) );
+        painter->drawLine( shadowRect.topRight() + QPoint( 3, 3 ), shadowRect.bottomRight() + QPoint( 3, 1 ) );
+        painter->drawLine( shadowRect.bottomLeft() + QPoint( 0, 4 ), shadowRect.bottomRight() + QPoint( 0, 4 ) );
+    }
 
     QPixmap cover = item->cover.isNull() ? m_defaultCover : item->cover;
     QRect r = option.rect.adjusted( 6, 5, -6, -41 );
@@ -117,7 +139,7 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
         oneLiner = true;
     else
         oneLiner = ( textRect.height() / 2 < painter->fontMetrics().boundingRect( item->album()->name() ).height() ||
-                      textRect.height() / 2 < painter->fontMetrics().boundingRect( item->album()->artist()->name() ).height() );
+                     textRect.height() / 2 < painter->fontMetrics().boundingRect( item->album()->artist()->name() ).height() );
 
     if ( oneLiner )
     {
@@ -127,15 +149,95 @@ AlbumItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option,
     }
     else
     {
+        painter->setFont( boldFont );
         to.setAlignment( Qt::AlignHCenter | Qt::AlignTop );
         text = painter->fontMetrics().elidedText( item->album()->name(), Qt::ElideRight, textRect.width() - 3 );
         painter->drawText( textRect, text, to );
 
-        painter->setFont( boldFont );
+        // If the user is hovering over an artist rect, draw a background so she knows it's clickable
+        QRect r = textRect;
+        r.setTop( r.bottom() - painter->fontMetrics().height() );
+        r.adjust( 4, 0, -4, -1 );
+        if ( m_hoveringOver == index )
+            TomahawkUtils::drawQueryBackground( painter, opt.palette, r, 1.5 );
+
+#ifdef Q_WS_MAC
+        painter->setPen( opt.palette.color( QPalette::Dark ).darker( 200 ) );
+#else
+        painter->setPen( opt.palette.color( QPalette::Dark ) );
+#endif
         to.setAlignment( Qt::AlignHCenter | Qt::AlignBottom );
         text = painter->fontMetrics().elidedText( item->album()->artist()->name(), Qt::ElideRight, textRect.width() - 3 );
         painter->drawText( textRect, text, to );
+        // Calculate rect of artist on-hover button click area
+
+        m_artistNameRects[ index ] = r;
     }
 
     painter->restore();
 }
+
+bool
+AlbumItemDelegate::editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index )
+{
+    Q_UNUSED( option );
+
+    if ( event->type() != QEvent::MouseButtonRelease &&
+         event->type() != QEvent::MouseMove &&
+         event->type() != QEvent::MouseButtonPress &&
+         event->type() != QEvent::Leave )
+        return false;
+
+    if ( m_artistNameRects.contains( index ) )
+    {
+        QMouseEvent* ev = static_cast< QMouseEvent* >( event );
+        QRect artistNameRect = m_artistNameRects[ index ];
+        if ( artistNameRect.contains( ev->pos() ) )
+        {
+            if ( event->type() == QEvent::MouseMove )
+            {
+                if ( m_hoveringOver != index )
+                {
+                    QModelIndex old = m_hoveringOver;
+                    m_hoveringOver = index;
+                    emit updateIndex( old );
+                    emit updateIndex( index );
+                }
+
+                event->accept();
+                return true;
+            }
+            else if ( event->type() == QEvent::MouseButtonRelease )
+            {
+                AlbumItem* item = m_model->sourceModel()->itemFromIndex( m_model->mapToSource( index ) );
+                if ( !item || item->album().isNull() || item->album()->artist().isNull() )
+                    return false;
+
+                ViewManager::instance()->show( item->album()->artist() );
+
+                event->accept();
+                return true;
+            } else if ( event->type() == QEvent::MouseButtonPress )
+            {
+                // Stop the whole album from having a down click action as we just want the artist name to be clicked
+                event->accept();
+                return true;
+            }
+        }
+    }
+
+    whitespaceMouseEvent();
+
+    return false;
+}
+
+void
+AlbumItemDelegate::whitespaceMouseEvent()
+{
+    if ( m_hoveringOver.isValid() )
+    {
+        QModelIndex old = m_hoveringOver;
+        m_hoveringOver = QPersistentModelIndex();
+        emit updateIndex( old );
+    }
+}
diff --git a/src/libtomahawk/playlist/albumitemdelegate.h b/src/libtomahawk/playlist/albumitemdelegate.h
index a5bb3a27a..86b31bdf5 100644
--- a/src/libtomahawk/playlist/albumitemdelegate.h
+++ b/src/libtomahawk/playlist/albumitemdelegate.h
@@ -23,6 +23,7 @@
 
 #include "dllmacro.h"
 
+class QEvent;
 class AlbumProxyModel;
 
 class DLLEXPORT AlbumItemDelegate : public QStyledItemDelegate
@@ -32,17 +33,26 @@ Q_OBJECT
 public:
     AlbumItemDelegate( QAbstractItemView* parent = 0, AlbumProxyModel* proxy = 0 );
 
+    void whitespaceMouseEvent();
+
 protected:
     void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
     QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const;
 
+    bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index );
 //    QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
 
+signals:
+    void updateIndex( const QModelIndex& idx );
+
 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;
 };
diff --git a/src/libtomahawk/playlist/albummodel.cpp b/src/libtomahawk/playlist/albummodel.cpp
index 7c629777d..5662d5036 100644
--- a/src/libtomahawk/playlist/albummodel.cpp
+++ b/src/libtomahawk/playlist/albummodel.cpp
@@ -254,6 +254,8 @@ AlbumModel::addCollection( const collection_ptr& collection, bool overwrite )
     Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
 
     m_title = tr( "All albums from %1" ).arg( collection->source()->friendlyName() );
+
+    emit loadingStarted();
 }
 
 
@@ -280,6 +282,8 @@ AlbumModel::addFilteredCollection( const collection_ptr& collection, unsigned in
         m_title = tr( "All albums from %1" ).arg( collection->source()->friendlyName() );
     else
         m_title = tr( "All albums" );
+
+    emit loadingStarted();
 }
 
 
@@ -292,6 +296,8 @@ AlbumModel::addAlbums( const QList<Tomahawk::album_ptr>& albums )
     if ( m_overwriteOnAdd )
         clear();
 
+    emit loadingFinished();
+
     int c = rowCount( QModelIndex() );
     QPair< int, int > crows;
     crows.first = c;
diff --git a/src/libtomahawk/playlist/albummodel.h b/src/libtomahawk/playlist/albummodel.h
index 596bf5f42..6a73124ac 100644
--- a/src/libtomahawk/playlist/albummodel.h
+++ b/src/libtomahawk/playlist/albummodel.h
@@ -94,6 +94,8 @@ signals:
 
     void trackCountChanged( unsigned int tracks );
 
+    void loadingStarted();
+    void loadingFinished();
 private slots:
     void onDataChanged();
 
diff --git a/src/libtomahawk/playlist/albumview.cpp b/src/libtomahawk/playlist/albumview.cpp
index 45099d6e8..d0a117a95 100644
--- a/src/libtomahawk/playlist/albumview.cpp
+++ b/src/libtomahawk/playlist/albumview.cpp
@@ -22,6 +22,7 @@
 #include <QKeyEvent>
 #include <QPainter>
 #include <QScrollBar>
+#include <qmath.h>
 
 #include "audio/audioengine.h"
 #include "tomahawksettings.h"
@@ -31,6 +32,7 @@
 #include "albummodel.h"
 #include "viewmanager.h"
 #include "utils/logger.h"
+#include "dynamic/widgets/LoadingSpinner.h"
 
 #define SCROLL_TIMEOUT 280
 
@@ -41,13 +43,16 @@ AlbumView::AlbumView( QWidget* parent )
     : QListView( parent )
     , m_model( 0 )
     , m_proxyModel( 0 )
-//    , m_delegate( 0 )
+    , m_delegate( 0 )
+    , m_loadingSpinner( new LoadingSpinner( this ) )
 {
     setDragEnabled( true );
     setDropIndicatorShown( false );
     setDragDropOverwriteMode( false );
     setUniformItemSizes( true );
-    setSpacing( 20 );
+    setSpacing( 16 );
+    setContentsMargins( 0, 0, 0, 0 );
+    setMouseTracking( true );
 
     setResizeMode( Adjust );
     setViewMode( IconMode );
@@ -75,7 +80,9 @@ void
 AlbumView::setProxyModel( AlbumProxyModel* model )
 {
     m_proxyModel = model;
-    setItemDelegate( new AlbumItemDelegate( this, m_proxyModel ) );
+    m_delegate = new AlbumItemDelegate( this, m_proxyModel );
+    connect( m_delegate, SIGNAL( updateIndex( QModelIndex ) ), this, SLOT( update( QModelIndex ) ) );
+    setItemDelegate( m_delegate );
 
     QListView::setModel( m_proxyModel );
 }
@@ -104,6 +111,9 @@ AlbumView::setAlbumModel( AlbumModel* model )
     connect( m_proxyModel, SIGNAL( filterChanged( QString ) ), SLOT( onFilterChanged( QString ) ) );
     connect( m_proxyModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onViewChanged() ) );
 
+    connect( m_model, SIGNAL( loadingStarted() ), m_loadingSpinner, SLOT( fadeIn() ) );
+    connect( m_model, SIGNAL( loadingFinished() ), m_loadingSpinner, SLOT( fadeOut() ) );
+
     setAcceptDrops( false );
     onViewChanged(); // Fetch covers if albums were added to model before model was attached to view
 }
@@ -179,6 +189,30 @@ AlbumView::paintEvent( QPaintEvent* event )
 }
 
 
+void
+AlbumView::resizeEvent( QResizeEvent* event )
+{
+    QListView::resizeEvent( event );
+
+#ifdef Q_WS_X11
+    int scrollbar = !verticalScrollBar()->isVisible() ? verticalScrollBar()->rect().width() : 0;
+#else
+    int scrollbar = verticalScrollBar()->rect().width();
+#endif
+    int rectWidth = contentsRect().width() - scrollbar - 16 - 3;
+    QSize itemSize = m_proxyModel->data( QModelIndex(), Qt::SizeHintRole ).toSize();
+
+    int itemsPerRow = qFloor( rectWidth / ( itemSize.width() + 16 ) );
+    int rightSpacing = rectWidth - ( itemsPerRow * ( itemSize.width() + 16 ) );
+    int newSpacing = 16 + floor( rightSpacing / ( itemsPerRow + 1 ) );
+
+    if ( itemsPerRow < 1 )
+        setSpacing( 16 );
+    else
+        setSpacing( newSpacing );
+}
+
+
 void
 AlbumView::onFilterChanged( const QString& )
 {
diff --git a/src/libtomahawk/playlist/albumview.h b/src/libtomahawk/playlist/albumview.h
index 52dfecee3..b16f9e898 100644
--- a/src/libtomahawk/playlist/albumview.h
+++ b/src/libtomahawk/playlist/albumview.h
@@ -28,6 +28,8 @@
 #include "albumproxymodel.h"
 
 class AlbumModel;
+class LoadingSpinner;
+class AlbumItemDelegate;
 
 class DLLEXPORT AlbumView : public QListView, public Tomahawk::ViewPage
 {
@@ -63,6 +65,7 @@ protected:
     virtual void startDrag( Qt::DropActions supportedActions );
 
     void paintEvent( QPaintEvent* event );
+    void resizeEvent( QResizeEvent* event );
 
 private slots:
     void onFilterChanged( const QString& filter );
@@ -73,7 +76,8 @@ private slots:
 private:
     AlbumModel* m_model;
     AlbumProxyModel* m_proxyModel;
-//    PlaylistItemDelegate* m_delegate;
+    AlbumItemDelegate* m_delegate;
+    LoadingSpinner* m_loadingSpinner;
 
     QTimer m_timer;
 };
diff --git a/src/libtomahawk/playlist/artistview.cpp b/src/libtomahawk/playlist/artistview.cpp
index 61851b692..59ce86097 100644
--- a/src/libtomahawk/playlist/artistview.cpp
+++ b/src/libtomahawk/playlist/artistview.cpp
@@ -158,7 +158,7 @@ ArtistView::onItemActivated( const QModelIndex& index )
         if ( !item->artist().isNull() )
             ViewManager::instance()->show( item->artist() );
         else if ( !item->album().isNull() )
-            ViewManager::instance()->show( item->album() );
+            ViewManager::instance()->show( item->album(), m_model->mode() );
         else if ( !item->result().isNull() && item->result()->isOnline() )
         {
             m_model->setCurrentItem( item->index );
diff --git a/src/libtomahawk/playlist/customplaylistview.cpp b/src/libtomahawk/playlist/customplaylistview.cpp
index 218b6ffb4..7273dcee3 100644
--- a/src/libtomahawk/playlist/customplaylistview.cpp
+++ b/src/libtomahawk/playlist/customplaylistview.cpp
@@ -52,8 +52,11 @@ CustomPlaylistView::CustomPlaylistView( CustomPlaylistView::PlaylistType type, c
     }
 }
 
+
 CustomPlaylistView::~CustomPlaylistView()
-{}
+{
+}
+
 
 bool
 CustomPlaylistView::isBeingPlayed() const
@@ -61,6 +64,7 @@ CustomPlaylistView::isBeingPlayed() const
     return AudioEngine::instance()->currentTrackPlaylist() == playlistInterface();
 }
 
+
 bool
 CustomPlaylistView::jumpToCurrentTrack()
 {
@@ -80,7 +84,7 @@ CustomPlaylistView::generateTracks()
                            "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' AND social_attributes.source %1 "
                            "GROUP BY track.id "
-                           "ORDER BY counter DESC, social_attributes.timestamp DESC " ).arg( m_source->isLocal() ? "IS NULL" : QString( "=%1" ).arg( m_source->id() ) );
+                           "ORDER BY counter DESC, social_attributes.timestamp DESC " ).arg( m_source->isLocal() ? "IS NULL" : QString( "= %1" ).arg( m_source->id() ) );
             break;
         case AllLovedTracks:
             sql = QString( "SELECT track.name, artist.name, source, COUNT(*) as counter "
@@ -96,6 +100,7 @@ CustomPlaylistView::generateTracks()
     Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
 }
 
+
 void
 CustomPlaylistView::tracksGenerated( QList< query_ptr > tracks )
 {
@@ -103,6 +108,7 @@ CustomPlaylistView::tracksGenerated( QList< query_ptr > tracks )
         m_model->append( q );
 }
 
+
 QString
 CustomPlaylistView::title() const
 {
@@ -132,18 +138,21 @@ CustomPlaylistView::description() const
     }
 }
 
+
 QString
 CustomPlaylistView::longDescription() const
 {
     return QString();
 }
 
+
 QPixmap
 CustomPlaylistView::pixmap() const
 {
     return QPixmap( RESPATH "images/loved_playlist.png" );
 }
 
+
 void
 CustomPlaylistView::reload()
 {
@@ -153,7 +162,7 @@ CustomPlaylistView::reload()
 
 
 void
-CustomPlaylistView::sourceAdded( const source_ptr& s)
+CustomPlaylistView::sourceAdded( const source_ptr& s )
 {
     connect( s.data(), SIGNAL( socialAttributesChanged() ), this, SLOT( reload() ) );
 }
diff --git a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp
index d7f12f7f3..19a74a2eb 100644
--- a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.cpp
+++ b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.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 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,9 @@ LoadingSpinner::~LoadingSpinner()
 void
 LoadingSpinner::fadeIn()
 {
+    if ( isVisible() )
+        return;
+
     show();
 
     m_anim->start();
diff --git a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h
index 5720df34c..556dcd11c 100644
--- a/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.h
+++ b/src/libtomahawk/playlist/dynamic/widgets/LoadingSpinner.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 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
@@ -54,4 +54,4 @@ private:
 
 #endif
 
-class QPaintEvent;
\ No newline at end of file
+class QPaintEvent;
diff --git a/src/libtomahawk/playlist/playlistmodel.cpp b/src/libtomahawk/playlist/playlistmodel.cpp
index c8a2b61dc..14bf12bae 100644
--- a/src/libtomahawk/playlist/playlistmodel.cpp
+++ b/src/libtomahawk/playlist/playlistmodel.cpp
@@ -384,7 +384,7 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r
     // so check if the drag originated in this playlist to determine whether or not to copy
 #ifdef Q_WS_MAC
     if ( !data->hasFormat( "application/tomahawk.playlist.id" ) ||
-        data->data( "application/tomahawk.playlist.id" ) != m_playlist->guid() )
+         ( !m_playlist.isNull() && data->data( "application/tomahawk.playlist.id" ) != m_playlist->guid() ) )
         dj->setDropAction( DropJob::Append );
 #else
     if ( action & Qt::CopyAction )
diff --git a/src/libtomahawk/playlist/trackview.cpp b/src/libtomahawk/playlist/trackview.cpp
index ca7e89e25..4d1ceabc4 100644
--- a/src/libtomahawk/playlist/trackview.cpp
+++ b/src/libtomahawk/playlist/trackview.cpp
@@ -556,7 +556,8 @@ TrackView::mousePressEvent( QMouseEvent* event )
                 }
                 else
                 {
-                    //TODO
+                    artist_ptr artist = Artist::get( item->query()->artist() );
+                    ViewManager::instance()->show( Album::get( artist, item->query()->album() ) );
                 }
                 break;
             }
diff --git a/src/libtomahawk/playlist/treeitemdelegate.cpp b/src/libtomahawk/playlist/treeitemdelegate.cpp
index 1328b15cd..dee8934c9 100644
--- a/src/libtomahawk/playlist/treeitemdelegate.cpp
+++ b/src/libtomahawk/playlist/treeitemdelegate.cpp
@@ -40,7 +40,7 @@ TreeItemDelegate::TreeItemDelegate( ArtistView* parent, TreeProxyModel* proxy )
     , m_model( proxy )
 {
     m_nowPlayingIcon = QPixmap( RESPATH "images/now-playing-speaker.png" );
-    m_defaultAlbumCover = QPixmap( RESPATH "images/no-album-art-placeholder.png" );
+    m_defaultAlbumCover = QPixmap( RESPATH "images/no-album-no-case.png" );
     m_defaultArtistImage = QPixmap( RESPATH "images/no-artist-image-placeholder.png" );
 }
 
diff --git a/src/libtomahawk/playlist/treemodel.cpp b/src/libtomahawk/playlist/treemodel.cpp
index 8dd336ea5..a9c4a0b1d 100644
--- a/src/libtomahawk/playlist/treemodel.cpp
+++ b/src/libtomahawk/playlist/treemodel.cpp
@@ -38,7 +38,7 @@ TreeModel::TreeModel( QObject* parent )
     , m_rootItem( new TreeModelItem( 0, this ) )
     , m_infoId( uuid() )
     , m_columnStyle( AllColumns )
-    , m_mode( Database )
+    , m_mode( DatabaseMode )
 {
     setIcon( QPixmap( RESPATH "images/music-icon.png" ) );
 
@@ -576,7 +576,7 @@ TreeModel::addAlbums( const artist_ptr& artist, const QModelIndex& parent )
 {
     emit loadingStarted();
 
-    if ( m_mode == Database )
+    if ( m_mode == DatabaseMode )
     {
         DatabaseCommand_AllAlbums* cmd = new DatabaseCommand_AllAlbums( m_collection, artist );
         cmd->setData( parent.row() );
@@ -586,7 +586,7 @@ TreeModel::addAlbums( const artist_ptr& artist, const QModelIndex& parent )
 
         Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
     }
-    else if ( m_mode == InfoSystem )
+    else if ( m_mode == InfoSystemMode )
     {
         Tomahawk::InfoSystem::InfoStringHash artistInfo;
         artistInfo["artist"] = artist->name();
@@ -612,7 +612,7 @@ TreeModel::addTracks( const album_ptr& album, const QModelIndex& parent )
     rows << parent.row();
     rows << parent.parent().row();
 
-    if ( m_mode == Database )
+    if ( m_mode == DatabaseMode )
     {
         DatabaseCommand_AllTracks* cmd = new DatabaseCommand_AllTracks( m_collection );
         cmd->setAlbum( album.data() );
@@ -623,17 +623,20 @@ TreeModel::addTracks( const album_ptr& album, const QModelIndex& parent )
 
         Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
     }
-    else if ( m_mode == InfoSystem )
+    else if ( m_mode == InfoSystemMode )
     {
         Tomahawk::InfoSystem::InfoStringHash artistInfo;
         artistInfo["artist"] = album->artist()->name();
         artistInfo["album"] = album->name();
 
+        m_receivedInfoData.remove( artistInfo );
         Tomahawk::InfoSystem::InfoRequestData requestData;
         requestData.caller = m_infoId;
         requestData.customData["rows"] = QVariant( rows );
         requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
         requestData.type = Tomahawk::InfoSystem::InfoAlbumSongs;
+        requestData.timeoutMillis = 0;
+        requestData.allSources = true;
         Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
     }
     else
@@ -826,7 +829,7 @@ TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QV
             QStringList albums = returnedData[ "albums" ].toStringList();
             QList<album_ptr> al;
 
-            InfoSystem::InfoStringHash inputInfo;
+            Tomahawk::InfoSystem::InfoStringHash inputInfo;
             inputInfo = requestData.input.value< InfoSystem::InfoStringHash >();
             artist_ptr artist = Artist::get( inputInfo[ "artist" ], false );
 
@@ -845,12 +848,23 @@ TreeModel::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QV
 
         case Tomahawk::InfoSystem::InfoAlbumSongs:
         {
+            if ( m_receivedInfoData.contains( requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >() ) )
+                break;
+
+            emit loadingFinished();
+
             QVariantMap returnedData = output.value< QVariantMap >();
+            if ( returnedData.isEmpty() )
+                break;
+
+            m_receivedInfoData.insert( requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >() );
+
+
             QStringList tracks = returnedData[ "tracks" ].toStringList();
             QList<query_ptr> ql;
 
-            InfoSystem::InfoStringHash inputInfo;
-            inputInfo = requestData.input.value< InfoSystem::InfoStringHash >();
+            Tomahawk::InfoSystem::InfoStringHash inputInfo;
+            inputInfo = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
 
             foreach ( const QString& trackName, tracks )
             {
diff --git a/src/libtomahawk/playlist/treemodel.h b/src/libtomahawk/playlist/treemodel.h
index af4aa6bf0..3973b396f 100644
--- a/src/libtomahawk/playlist/treemodel.h
+++ b/src/libtomahawk/playlist/treemodel.h
@@ -33,6 +33,7 @@
 #include "infosystem/infosystem.h"
 
 #include "dllmacro.h"
+#include "typedefs.h"
 
 class QMetaData;
 
@@ -55,9 +56,6 @@ public:
     enum ColumnStyle
     { AllColumns = 0, TrackOnly };
 
-    enum ModelMode
-    { Database = 0, InfoSystem };
-
     explicit TreeModel( QObject* parent = 0 );
     virtual ~TreeModel();
 
@@ -73,8 +71,8 @@ public:
     virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const;
     virtual int columnCount( const QModelIndex& parent = QModelIndex() ) const;
 
-    virtual ModelMode mode() const { return m_mode; }
-    virtual void setMode( ModelMode mode ) { m_mode = mode; }
+    virtual Tomahawk::ModelMode mode() const { return m_mode; }
+    virtual void setMode( Tomahawk::ModelMode mode ) { m_mode = mode; }
 
     virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const;
     virtual QVariant headerData( int section, Qt::Orientation orientation, int role ) const;
@@ -165,12 +163,13 @@ private:
     QString m_description;
     QPixmap m_icon;
     ColumnStyle m_columnStyle;
-    ModelMode m_mode;
+    Tomahawk::ModelMode m_mode;
 
     QList<Tomahawk::artist_ptr> m_artistsFilter;
 
     Tomahawk::collection_ptr m_collection;
     QHash<qlonglong, QPersistentModelIndex> m_coverHash;
+    QSet<Tomahawk::InfoSystem::InfoStringHash> m_receivedInfoData;
 };
 
 #endif // ALBUMMODEL_H
diff --git a/src/libtomahawk/playlist/treeproxymodel.cpp b/src/libtomahawk/playlist/treeproxymodel.cpp
index 5dc0e0cfa..206bbd1b7 100644
--- a/src/libtomahawk/playlist/treeproxymodel.cpp
+++ b/src/libtomahawk/playlist/treeproxymodel.cpp
@@ -195,7 +195,7 @@ TreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent
     TreeModelItem* pi = sourceModel()->itemFromIndex( sourceModel()->index( sourceRow, 0, sourceParent ) );
     Q_ASSERT( pi );
 
-    if ( m_model->mode() == TreeModel::Database && !pi->result().isNull() )
+    if ( m_model->mode() == Tomahawk::DatabaseMode && !pi->result().isNull() )
     {
         QList< Tomahawk::result_ptr > rl = m_cache.values( sourceParent );
         foreach ( const Tomahawk::result_ptr& result, rl )
@@ -329,14 +329,34 @@ TreeProxyModel::siblingItem( int itemsAway )
 Tomahawk::result_ptr
 TreeProxyModel::siblingItem( int itemsAway, bool readOnly )
 {
-    qDebug() << Q_FUNC_INFO;
-
     QModelIndex idx = currentIndex();
+    if ( !idx.isValid() )
+        return Tomahawk::result_ptr();
+
+    if ( m_shuffled )
+        idx = index( qrand() % rowCount( idx.parent() ), 0, idx.parent() );
+    else if ( m_repeatMode == PlaylistInterface::RepeatOne )
+        idx = index( idx.row(), 0, idx.parent() );
+    else
+        idx = index( idx.row() + ( itemsAway > 0 ? 1 : -1 ), 0, idx.parent() );
+
+    if ( !idx.isValid() && m_repeatMode == PlaylistInterface::RepeatAll )
+    {
+        if ( itemsAway > 0 )
+        {
+            // reset to first item
+            idx = index( 0, 0, currentIndex().parent() );
+        }
+        else
+        {
+            // reset to last item
+            idx = index( rowCount( currentIndex().parent() ) - 1, 0, currentIndex().parent() );
+        }
+    }
 
     // Try to find the next available PlaylistItem (with results)
     if ( idx.isValid() ) do
     {
-        idx = index( idx.row() + ( itemsAway > 0 ? 1 : -1 ), 0, idx.parent() );
         if ( !idx.isValid() )
             break;
 
diff --git a/src/libtomahawk/resolvers/qtscriptresolver.cpp b/src/libtomahawk/resolvers/qtscriptresolver.cpp
index f0b760d82..0d4bd5978 100644
--- a/src/libtomahawk/resolvers/qtscriptresolver.cpp
+++ b/src/libtomahawk/resolvers/qtscriptresolver.cpp
@@ -31,6 +31,7 @@
 #include <network/servent.h>
 #include <QNetworkRequest>
 #include <QNetworkReply>
+#include <QMessageBox>
 
 // FIXME: bloody hack, remove this for 0.3
 // this one adds new functionality to old resolvers
@@ -186,9 +187,9 @@ ScriptEngine::javaScriptConsoleMessage( const QString& message, int lineNumber,
     tLog() << "JAVASCRIPT:" << m_scriptPath << message << lineNumber << sourceID;
     /// I guess there is somereason for a assert in here, maybe fatal js errors, but
     /// undefined is not so fatal
-    if(sourceID != "undefined")
-        Q_ASSERT( false );
-
+#ifdef QT_DEBUG
+    QMessageBox::critical( 0, "Script Resolver Error", QString( "%1 %2 %3 %4" ).arg( m_scriptPath ).arg( message ).arg( lineNumber ).arg( sourceID ) );
+#endif
 }
 
 
diff --git a/src/libtomahawk/typedefs.h b/src/libtomahawk/typedefs.h
index b3b98232e..b972009ba 100644
--- a/src/libtomahawk/typedefs.h
+++ b/src/libtomahawk/typedefs.h
@@ -63,6 +63,12 @@ namespace Tomahawk
         Static
     };
 
+    enum ModelMode
+    {
+        DatabaseMode = 0,
+        InfoSystemMode
+    };
+
 }; // ns
 
 typedef int AudioErrorCode;
diff --git a/src/libtomahawk/utils/dropjobnotifier.cpp b/src/libtomahawk/utils/dropjobnotifier.cpp
index c104ada55..219f51215 100644
--- a/src/libtomahawk/utils/dropjobnotifier.cpp
+++ b/src/libtomahawk/utils/dropjobnotifier.cpp
@@ -51,8 +51,8 @@ DropJobNotifier::DropJobNotifier( QPixmap servicePixmap, QString service, DropJo
 
 DropJobNotifier::DropJobNotifier( QPixmap pixmap, DropJob::DropType type )
     : JobStatusItem()
-    , m_pixmap( pixmap )
     , m_job( 0 )
+    , m_pixmap( pixmap )
 {
     init( type );
 }
diff --git a/src/libtomahawk/utils/rdioparser.cpp b/src/libtomahawk/utils/rdioparser.cpp
index 8be1a40d2..7e7d2e28f 100644
--- a/src/libtomahawk/utils/rdioparser.cpp
+++ b/src/libtomahawk/utils/rdioparser.cpp
@@ -1,6 +1,7 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
  *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
+ *   Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com>
  *
  *   Tomahawk is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -18,15 +19,39 @@
 
 #include "rdioparser.h"
 
+#include "shortenedlinkparser.h"
+#include "config.h"
+#include "utils/tomahawkutils.h"
+#include "utils/logger.h"
+#include "dropjob.h"
+#include "jobview/JobStatusView.h"
+#include "jobview/JobStatusModel.h"
+#include "dropjobnotifier.h"
+#include "viewmanager.h"
+#include "sourcelist.h"
+
+#include <qjson/parser.h>
+#include <QDateTime>
+#include <QtNetwork/QNetworkAccessManager>
+#include <QtNetwork/QNetworkReply>
 #include <QUrl>
 #include <QStringList>
-#include "shortenedlinkparser.h"
+
+#include <QtCore/QCryptographicHash>
 
 using namespace Tomahawk;
 
+QPixmap* RdioParser::s_pixmap = 0;
+
+#ifdef QCA2_FOUND
+QCA::Initializer RdioParser::m_qcaInit = QCA::Initializer();
+#endif
+
 RdioParser::RdioParser( QObject* parent )
     : QObject( parent )
     , m_count( 0 )
+    , m_browseJob( 0 )
+    , m_createPlaylist( false )
 {
 }
 
@@ -63,50 +88,276 @@ RdioParser::parseUrl( const QString& url )
         return;
     }
 
-    query_ptr query;
-    m_count++;
-
-    if ( url.contains( "artist" ) && url.contains( "album" ) )
+    if ( url.contains( "artist" ) && url.contains( "album" ) && url.contains( "track" ) )
+        parseTrack( url );
+    else
     {
-        // this is a "full" url, no redirection needed
-        QString realUrl = QUrl::fromUserInput( url ).toString().replace( "_", " " );
-
-        QString artist, trk, album;
-        QString matchStr = "/%1/([^/]*)/";
-        QRegExp r( QString( matchStr ).arg( "artist" ) );
-
-        int loc = r.indexIn( realUrl );
-        if ( loc >= 0 )
-            artist = r.cap( 1 );
-
-        r = QRegExp( QString( matchStr ).arg( "album" ) );
-        loc = r.indexIn( realUrl );
-        if ( loc >= 0 )
-            album = r.cap( 1 );
-
-        r = QRegExp( QString( matchStr ).arg( "track" ) );
-        loc = r.indexIn( realUrl );
-        if ( loc >= 0 )
-            trk = r.cap( 1 );
-
-        if ( !trk.isEmpty() && !artist.isEmpty() )
+        DropJob::DropType type = DropJob::None;
+        if ( url.contains( "artist" ) && url.contains( "album" ) )
+            type = DropJob::Album;
+        else if ( url.contains( "artist" ) )
+            type = DropJob::Artist;
+        else if ( url.contains( "people" ) && url.contains( "playlist" ) )
+            type = DropJob::Playlist;
+        else
         {
-            query = Query::get( artist, trk, album, uuid(), true );
+            tLog() << "Got Rdio URL I can't parse!" << url;
+            return;
         }
+
+        // artist, album, or playlist link requre fetching
+        fetchObjectsFromUrl( url, type );
     }
 
-    if ( m_multi )
-    {
-        if ( !query.isNull() )
-            m_queries << query;
-
-        if ( m_count == m_total )
-            emit tracks( m_queries );
-    }
-    if ( !m_multi && !query.isNull() )
-        emit track( query );
 }
 
+void
+RdioParser::fetchObjectsFromUrl( const QString& url, DropJob::DropType type )
+{
+    QList< QPair< QByteArray, QByteArray > > params;
+    params.append( QPair<QByteArray, QByteArray>( "extras", "tracks" ) );
+
+    QString cleanedUrl = url;
+    cleanedUrl.replace("#/", "");
+
+    QByteArray data;
+    QNetworkRequest request = generateRequest( "getObjectFromUrl", cleanedUrl, params, &data );
+
+    request.setHeader( QNetworkRequest::ContentTypeHeader, QLatin1String( "application/x-www-form-urlencoded" ) );
+    QNetworkReply* reply = TomahawkUtils::nam()->post( request, data  );
+    connect( reply, SIGNAL( finished() ), this, SLOT( rdioReturned() ) );
+
+    m_browseJob = new DropJobNotifier( pixmap(), QString( "Rdio" ), type, reply );
+    JobStatusView::instance()->model()->addJob( m_browseJob );
+
+    m_reqQueries.insert( reply );
+}
+
+void
+RdioParser::rdioReturned()
+{
+
+    QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() );
+    Q_ASSERT( r );
+    m_reqQueries.remove( r );
+    m_count++;
+    r->deleteLater();
+
+    if ( r->error() == QNetworkReply::NoError )
+    {
+        QJson::Parser p;
+        bool ok;
+        QVariantMap res = p.parse( r, &ok ).toMap();
+        QVariantMap result = res.value( "result" ).toMap();
+
+        if ( !ok || result.isEmpty() )
+        {
+            tLog() << "Failed to parse json from Rdio browse item :" << p.errorString() << "On line" << p.errorLine() << "With data:" << res;
+
+            return;
+        }
+
+        QVariantList tracks = result.value( "tracks" ).toList();
+        if ( tracks.isEmpty() )
+        {
+            tLog() << "Got no tracks in result, ignoring!" << result;
+            return;
+        }
+
+        // Playlists will have these
+        m_title = result[ "name" ].toString();
+        m_creator = result[ "owner" ].toString();
+
+        foreach( QVariant track, tracks )
+        {
+            QVariantMap rdioResult = track.toMap();
+            QString title, artist, album;
+
+            title = rdioResult.value( "name", QString() ).toString();
+            artist = rdioResult.value( "artist", QString() ).toString();
+            album = rdioResult.value( "album", QString() ).toString();
+
+            if ( title.isEmpty() && artist.isEmpty() ) // don't have enough...
+            {
+                tLog() << "Didn't get an artist and track name from Rdio, not enough to build a query on. Aborting" << title << artist << album;
+                return;
+            }
+
+            Tomahawk::query_ptr q = Tomahawk::Query::get( artist, title, album, uuid(), !m_createPlaylist );
+            m_tracks << q;
+        }
+
+    } else
+    {
+        tLog() << "Error in network request to Rdio for track decoding:" << r->errorString();
+    }
+
+
+    checkFinished();
+
+
+}
+
+void
+RdioParser::parseTrack( const QString& origUrl )
+{
+    QString url = origUrl;
+    QString artist, trk, album, playlist;
+    QString realUrl = url.replace( "_", " " );
+    QString matchStr = "/%1/([^/]*)/";
+    QString matchPlStr = "/%1/(?:[^/]*)/([^/]*)/";
+
+    QRegExp r( QString( matchStr ).arg( "artist" ) );
+
+    int loc = r.indexIn( realUrl );
+    if ( loc >= 0 )
+        artist = r.cap( 1 );
+
+    r = QRegExp( QString( matchStr ).arg( "album" ) );
+    loc = r.indexIn( realUrl );
+    if ( loc >= 0 )
+        album = r.cap( 1 );
+
+    r = QRegExp( QString( matchStr ).arg( "track" ) );
+    loc = r.indexIn( realUrl );
+    if ( loc >= 0 )
+        trk = r.cap( 1 );
+
+    r = QRegExp( QString( matchPlStr ).arg( "playlists" ) );
+    loc = r.indexIn( realUrl );
+    if ( loc >= 0 )
+        playlist = r.cap( 1 );
+
+    if ( trk.isEmpty() || artist.isEmpty() )
+    {
+        tLog() << "Parsed Rdio track url but it's missing artist or track!" << url;
+        return;
+    }
+
+    query_ptr q = Query::get( artist, trk, album, uuid(), !m_createPlaylist );
+    m_count++;
+    m_tracks << q;
+
+    checkFinished();
+}
+
+
+QNetworkRequest
+RdioParser::generateRequest( const QString& method, const QString& url, const QList< QPair< QByteArray, QByteArray > >& extraParams, QByteArray* data )
+{
+    QUrl fetchUrl( "http://api.rdio.com/1/" );
+    QUrl toSignUrl = fetchUrl;
+
+    QPair<QByteArray, QByteArray> param;
+    foreach( param, extraParams )
+    {
+        toSignUrl.addEncodedQueryItem( param.first, param.second );
+    }
+    toSignUrl.addQueryItem( "method", method );
+    toSignUrl.addEncodedQueryItem("oauth_consumer_key", "gk8zmyzj5xztt8aj48csaart" );
+    QString nonce;
+    for ( int i = 0; i < 8; i++ )
+        nonce += QString::number( qrand() % 10 );
+    toSignUrl.addQueryItem("oauth_nonce", nonce );
+    toSignUrl.addEncodedQueryItem("oauth_signature_method", "HMAC-SHA1");
+    toSignUrl.addQueryItem("oauth_timestamp", QString::number(QDateTime::currentMSecsSinceEpoch() / 1000 ) );
+    toSignUrl.addEncodedQueryItem("oauth_version",  "1.0");
+    toSignUrl.addEncodedQueryItem( "url", QUrl::toPercentEncoding( url ) );
+    int size = toSignUrl.encodedQueryItems().size();
+    for( int i = 0; i < size; i++ ) {
+        const QPair< QByteArray, QByteArray > item = toSignUrl.encodedQueryItems().at( i );
+        data->append( item.first + "=" + item.second + "&" );
+    }
+    data->truncate( data->size() - 1 ); // remove extra &
+
+    QByteArray toSign = "POST&" + QUrl::toPercentEncoding( fetchUrl.toEncoded() ) + '&' + QUrl::toPercentEncoding( *data );
+    qDebug() << "Rdio" << toSign;
+
+    toSignUrl.addEncodedQueryItem( "oauth_signature", QUrl::toPercentEncoding( hmacSha1("yt35kakDyW&", toSign ) ) );
+
+    data->clear();
+    size = toSignUrl.encodedQueryItems().size();
+    for( int i = 0; i < size; i++ ) {
+        const QPair< QByteArray, QByteArray > item = toSignUrl.encodedQueryItems().at( i );
+        data->append( item.first + "=" + item.second + "&" );
+    }
+    data->truncate( data->size() - 1 ); // remove extra &
+
+    qDebug() << "POST data:" << *data;
+
+    QNetworkRequest request = QNetworkRequest( fetchUrl );
+    request.setHeader( QNetworkRequest::ContentTypeHeader, QLatin1String( "application/x-www-form-urlencoded" ) );
+
+    return request;
+}
+
+
+QByteArray
+RdioParser::hmacSha1(QByteArray key, QByteArray baseString)
+{
+#ifdef QCA2_FOUND
+    QCA::MessageAuthenticationCode hmacsha1( "hmac(sha1)", QCA::SecureArray() );
+    QCA::SymmetricKey keyObject( key );
+    hmacsha1.setup( keyObject );
+
+    hmacsha1.update( QCA::SecureArray( baseString ) );
+    QCA::SecureArray resultArray = hmacsha1.final();
+
+    QByteArray result = resultArray.toByteArray().toBase64();
+    return result;
+#else
+    tLog() << "Tomahawk compiled without QCA support, cannot generate HMAC signature";
+    return QByteArray();
+#endif
+}
+
+void
+RdioParser::checkFinished()
+{
+    tDebug() << "Checking for Rdio batch playlist job finished" << m_reqQueries.isEmpty();
+    if ( m_reqQueries.isEmpty() ) // we're done
+    {
+        if ( m_browseJob )
+            m_browseJob->setFinished();
+
+        if ( m_tracks.isEmpty() )
+            return;
+
+        if( m_createPlaylist )
+        {
+            m_playlist = Playlist::create( SourceList::instance()->getLocal(),
+                                           uuid(),
+                                           m_title,
+                                           "",
+                                           m_creator,
+                                           false,
+                                           m_tracks );
+
+            connect( m_playlist.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( playlistCreated() ) );
+
+            return;
+        }
+        else
+        {
+            if ( !m_multi )
+                emit track( m_tracks.first() );
+            else if ( m_multi && m_count == m_total )
+                emit tracks( m_tracks );
+
+            m_tracks.clear();
+        }
+
+        deleteLater();
+    }
+}
+
+void
+RdioParser::playlistCreated( Tomahawk::PlaylistRevision )
+{
+    ViewManager::instance()->show( m_playlist );
+}
+
+
 void
 RdioParser::expandedLinks( const QStringList& urls )
 {
@@ -117,3 +368,13 @@ RdioParser::expandedLinks( const QStringList& urls )
     }
 }
 
+QPixmap
+RdioParser::pixmap() const
+{
+    if ( !s_pixmap )
+        s_pixmap = new QPixmap( RESPATH "images/rdio.png" );
+
+
+    return *s_pixmap;
+}
+
diff --git a/src/libtomahawk/utils/rdioparser.h b/src/libtomahawk/utils/rdioparser.h
index 9bcf53a1e..3302b4715 100644
--- a/src/libtomahawk/utils/rdioparser.h
+++ b/src/libtomahawk/utils/rdioparser.h
@@ -1,6 +1,7 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
  *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
+ *   Copyright 2010-2011, Hugo Lindström <hugolm84@gmail.com>
  *
  *   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,15 +20,28 @@
 #ifndef RDIOPARSER_H
 #define RDIOPARSER_H
 
+#include "jobview/JobStatusItem.h"
+#include "query.h"
+#include "config.h"
+#include "dropjob.h"
+#include "typedefs.h"
+#include "playlist.h"
+
 #include <QtCore/QObject>
 #include <QStringList>
+#include <QSet>
 
-#include "query.h"
+#include <QNetworkRequest>
+
+#ifdef QCA2_FOUND
+#include <QtCrypto>
+#endif
 
 class QNetworkReply;
 namespace Tomahawk
 {
 
+class DropJobNotifier;
 /**
  * Small class to parse spotify links into query_ptrs
  *
@@ -38,25 +52,50 @@ class RdioParser : public QObject
 {
     Q_OBJECT
 public:
+
     explicit RdioParser( QObject* parent = 0 );
     virtual ~RdioParser();
 
     void parse( const QString& url );
     void parse( const QStringList& urls );
 
+    void setCreatePlaylist( bool createPlaylist ) { m_createPlaylist = createPlaylist; }
+
 signals:
     void track( const Tomahawk::query_ptr& track );
     void tracks( const QList< Tomahawk::query_ptr > tracks );
 
 private slots:
     void expandedLinks( const QStringList& );
+    void rdioReturned();
 
+    void playlistCreated( Tomahawk::PlaylistRevision );
 private:
+    void parseTrack( const QString& url );
+    void fetchObjectsFromUrl( const QString& url, DropJob::DropType type );
+
+    QByteArray hmacSha1(QByteArray key, QByteArray baseString);
+    QNetworkRequest generateRequest( const QString& method, const QString& url, const QList< QPair< QByteArray, QByteArray > >& extraParams, QByteArray* postData );
+    QPixmap pixmap() const;
+    void checkFinished();
     void parseUrl( const QString& url );
 
     bool m_multi;
     int m_count, m_total;
-    QList< query_ptr > m_queries;
+    QSet< QNetworkReply* > m_reqQueries;
+    DropJobNotifier* m_browseJob;
+
+    QString m_title, m_creator;
+    playlist_ptr m_playlist;
+
+    static QPixmap* s_pixmap;
+
+    bool m_createPlaylist;
+    QList< query_ptr > m_tracks;
+
+#ifdef QCA2_FOUND
+    static QCA::Initializer m_qcaInit;
+#endif
 };
 
 }
diff --git a/src/libtomahawk/utils/tomahawkutils.cpp b/src/libtomahawk/utils/tomahawkutils.cpp
index e6a2ae830..3797557da 100644
--- a/src/libtomahawk/utils/tomahawkutils.cpp
+++ b/src/libtomahawk/utils/tomahawkutils.cpp
@@ -24,6 +24,7 @@
 #include <QtGui/QColor>
 #include <QtCore/QDateTime>
 #include <QtCore/QDir>
+#include <QtCore/QMutex>
 #include <QtGui/QLayout>
 #include <QtGui/QPainter>
 #include <QtGui/QPixmap>
@@ -64,6 +65,8 @@ namespace TomahawkUtils
 
 
 static int s_headerHeight = 0;
+static quint64 s_infosystemRequestId = 0;
+static QMutex s_infosystemRequestIdMutex;
 
 #ifdef Q_WS_MAC
 QString
@@ -431,6 +434,14 @@ drawBackgroundAndNumbers( QPainter* painter, const QString& text, const QRect& f
     painter->drawText( figRect.adjusted( -5, 0, 6, 0 ), text, to );
 }
 
+void
+drawQueryBackground( QPainter* p, const QPalette& palette, const QRect& r, qreal lightnessFactor )
+{
+    p->setPen( palette.mid().color().lighter( lightnessFactor * 100 ) );
+    p->setBrush( palette.highlight().color().lighter( lightnessFactor * 100 ) );
+    p->drawRoundedRect( r, 4.0, 4.0 );
+}
+
 
 void
 unmarginLayout( QLayout* layout )
@@ -502,6 +513,15 @@ NetworkProxyFactory::setProxy( const QNetworkProxy& proxy )
 }
 
 
+bool NetworkProxyFactory::operator==( const NetworkProxyFactory& other )
+{
+    if ( m_noProxyHosts != other.m_noProxyHosts or m_proxy != other.m_proxy )
+        return false;
+
+    return true;
+}
+
+
 NetworkProxyFactory*
 proxyFactory()
 {
@@ -690,4 +710,13 @@ removeDirectory( const QString& dir )
 }
 
 
+quint64 infosystemRequestId()
+{
+    QMutexLocker locker( &s_infosystemRequestIdMutex );
+    quint64 result = s_infosystemRequestId;
+    s_infosystemRequestId++;
+    return result;
+}
+
+
 } // ns
diff --git a/src/libtomahawk/utils/tomahawkutils.h b/src/libtomahawk/utils/tomahawkutils.h
index 30656d01c..924045a1d 100644
--- a/src/libtomahawk/utils/tomahawkutils.h
+++ b/src/libtomahawk/utils/tomahawkutils.h
@@ -26,6 +26,7 @@
 #include <QtNetwork/QNetworkProxy>
 #include <QtCore/QStringList>
 #include <QtCore/QRect>
+#include <QPalette>
 
 #define RESPATH ":/data/"
 
@@ -64,6 +65,8 @@ namespace TomahawkUtils
         void setProxy( const QNetworkProxy &proxy );
         QNetworkProxy proxy() { return m_proxy; }
 
+        bool operator==( const NetworkProxyFactory &other );
+
     private:
         QStringList m_noProxyHosts;
         QNetworkProxy m_proxy;
@@ -85,6 +88,7 @@ namespace TomahawkUtils
     DLLEXPORT QPixmap createDragPixmap( MediaType type, int itemCount = 1 );
 
     DLLEXPORT void drawBackgroundAndNumbers( QPainter* p, const QString& text, const QRect& rect );
+    DLLEXPORT void drawQueryBackground( QPainter* p, const QPalette& palette, const QRect& r, qreal lightnessFactor = 1 );
 
     DLLEXPORT void unmarginLayout( QLayout* layout );
 
@@ -106,6 +110,8 @@ namespace TomahawkUtils
     DLLEXPORT void setHeaderHeight( int height );
 
     DLLEXPORT bool removeDirectory( const QString& dir );
+
+    DLLEXPORT quint64 infosystemRequestId();
 }
 
 #endif // TOMAHAWKUTILS_H
diff --git a/src/libtomahawk/viewmanager.cpp b/src/libtomahawk/viewmanager.cpp
index 594abc40f..b7cd72b08 100644
--- a/src/libtomahawk/viewmanager.cpp
+++ b/src/libtomahawk/viewmanager.cpp
@@ -217,12 +217,12 @@ ViewManager::show( const Tomahawk::artist_ptr& artist )
 
 
 Tomahawk::ViewPage*
-ViewManager::show( const Tomahawk::album_ptr& album )
+ViewManager::show( const Tomahawk::album_ptr& album, Tomahawk::ModelMode initialMode )
 {
     AlbumInfoWidget* swidget;
     if ( !m_albumViews.contains( album ) || m_albumViews.value( album ).isNull() )
     {
-        swidget = new AlbumInfoWidget( album );
+        swidget = new AlbumInfoWidget( album, initialMode );
         m_albumViews.insert( album, swidget );
     }
     else
@@ -555,6 +555,12 @@ ViewManager::setPage( ViewPage* page, bool trackHistory )
         if( obj->metaObject()->indexOfSignal( "descriptionChanged(QString)" ) > -1 )
             connect( obj, SIGNAL( descriptionChanged( QString ) ), m_infobar, SLOT( setDescription( QString ) ), Qt::UniqueConnection );
 
+        if( obj->metaObject()->indexOfSignal( "descriptionChanged(Tomahawk::artist_ptr)" ) > -1 )
+            connect( obj, SIGNAL( descriptionChanged( Tomahawk::artist_ptr ) ), m_infobar, SLOT( setDescription( Tomahawk::artist_ptr ) ), Qt::UniqueConnection );
+
+        if( obj->metaObject()->indexOfSignal( "descriptionChanged(Tomahawk::album_ptr)" ) > -1 )
+            connect( obj, SIGNAL( descriptionChanged( Tomahawk::album_ptr ) ), m_infobar, SLOT( setDescription( Tomahawk::album_ptr ) ), Qt::UniqueConnection );
+
         if( obj->metaObject()->indexOfSignal( "longDescriptionChanged(QString)" ) > -1 )
             connect( obj, SIGNAL( longDescriptionChanged( QString ) ), m_infobar, SLOT( setLongDescription( QString ) ), Qt::UniqueConnection );
 
@@ -672,7 +678,19 @@ ViewManager::updateView()
 
     m_infobar->setVisible( currentPage()->showInfoBar() );
     m_infobar->setCaption( currentPage()->title() );
-    m_infobar->setDescription( currentPage()->description() );
+    switch( currentPage()->descriptionType() )
+    {
+        case ViewPage::TextType:
+            m_infobar->setDescription( currentPage()->description() );
+            break;
+        case ViewPage::ArtistType:
+            m_infobar->setDescription( currentPage()->descriptionArtist() );
+            break;
+        case ViewPage::AlbumType:
+            m_infobar->setDescription( currentPage()->descriptionAlbum() );
+            break;
+
+    }
     m_infobar->setLongDescription( currentPage()->longDescription() );
     m_infobar->setPixmap( currentPage()->pixmap() );
 
diff --git a/src/libtomahawk/viewmanager.h b/src/libtomahawk/viewmanager.h
index 38820b63b..3c2bed9a6 100644
--- a/src/libtomahawk/viewmanager.h
+++ b/src/libtomahawk/viewmanager.h
@@ -139,7 +139,7 @@ public slots:
     Tomahawk::ViewPage* show( const Tomahawk::playlist_ptr& playlist );
     Tomahawk::ViewPage* show( const Tomahawk::dynplaylist_ptr& playlist );
     Tomahawk::ViewPage* show( const Tomahawk::artist_ptr& artist );
-    Tomahawk::ViewPage* show( const Tomahawk::album_ptr& album );
+    Tomahawk::ViewPage* show( const Tomahawk::album_ptr& album, Tomahawk::ModelMode withInitialMode = Tomahawk::InfoSystemMode );
     Tomahawk::ViewPage* show( const Tomahawk::collection_ptr& collection );
     Tomahawk::ViewPage* show( const Tomahawk::source_ptr& source );
 
diff --git a/src/libtomahawk/viewpage.h b/src/libtomahawk/viewpage.h
index 6e32bcef9..2810830d1 100644
--- a/src/libtomahawk/viewpage.h
+++ b/src/libtomahawk/viewpage.h
@@ -23,6 +23,8 @@
 
 #include "typedefs.h"
 #include "playlistinterface.h"
+#include "artist.h"
+#include "album.h"
 #include "utils/tomahawkutils.h"
 
 #include "dllmacro.h"
@@ -33,6 +35,12 @@ namespace Tomahawk
 class DLLEXPORT ViewPage
 {
 public:
+    enum DescriptionType {
+        TextType = 0,
+        ArtistType = 1,
+        AlbumType = 2
+    };
+
     ViewPage() {}
     virtual ~ViewPage() {}
 
@@ -40,7 +48,12 @@ public:
     virtual Tomahawk::PlaylistInterface* playlistInterface() const = 0;
 
     virtual QString title() const = 0;
+
+    virtual DescriptionType descriptionType() { return TextType; }
     virtual QString description() const = 0;
+    virtual Tomahawk::artist_ptr descriptionArtist() const { return Tomahawk::artist_ptr(); }
+    virtual Tomahawk::album_ptr descriptionAlbum() const { return Tomahawk::album_ptr(); }
+
     virtual QString longDescription() const { return QString(); }
     virtual QPixmap pixmap() const { return QPixmap( RESPATH "icons/tomahawk-icon-128x128.png" ); }
 
@@ -62,6 +75,8 @@ public:
     /** subclasses implementing ViewPage can emit the following signals:
      * nameChanged( const QString& )
      * descriptionChanged( const QString& )
+     * descriptionChanged( const Tomahawk::artist_ptr& artist )
+     * descriptionChanged( const Tomahawk::album_ptr& album )
      * longDescriptionChanged( const QString& )
      * pixmapChanged( const QPixmap& )
      * destroyed( QWidget* widget );
diff --git a/src/libtomahawk/widgets/Breadcrumb.cpp b/src/libtomahawk/widgets/Breadcrumb.cpp
new file mode 100644
index 000000000..8409283ff
--- /dev/null
+++ b/src/libtomahawk/widgets/Breadcrumb.cpp
@@ -0,0 +1,171 @@
+/* === 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 "Breadcrumb.h"
+
+#include "BreadcrumbButton.h"
+#include "utils/stylehelper.h"
+#include "utils/logger.h"
+
+#include <QStylePainter>
+#include <QPushButton>
+#include <QHBoxLayout>
+#include <QPropertyAnimation>
+#include <QDebug>
+
+using namespace Tomahawk;
+
+Breadcrumb::Breadcrumb( QWidget* parent, Qt::WindowFlags f )
+    : QWidget( parent, f )
+    , m_model( 0 )
+
+    , m_buttonlayout( new QHBoxLayout( this ) )
+{
+    m_buttonlayout->setSpacing( 0 );
+    m_buttonlayout->setMargin( 0 );
+    m_buttonlayout->setAlignment( Qt::AlignLeft );
+
+    setAutoFillBackground( true );
+    setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
+
+    setLayoutDirection( Qt::LeftToRight );
+    setLayout( m_buttonlayout );
+    show();
+}
+
+Breadcrumb::~Breadcrumb()
+{
+
+}
+
+void
+Breadcrumb::setModel( QAbstractItemModel* model )
+{
+    foreach ( BreadcrumbButton* b, m_buttons )
+        b->deleteLater();;
+    m_buttons.clear();
+
+    m_model = model;
+    updateButtons( QModelIndex() );
+}
+
+void
+Breadcrumb::setRootIcon( const QPixmap& pm )
+{
+    m_rootIcon = pm;
+
+    QPushButton* button = new QPushButton( QIcon( m_rootIcon ), "", this );
+    button->setFlat( true );
+    button->setStyleSheet( "QPushButton{ background-color: transparent; border: none; width:16px; height:16px;}" );
+    m_buttonlayout->insertWidget( 0, button );
+    m_buttonlayout->insertSpacing( 0,5 );
+    m_buttonlayout->insertSpacing( 2,5 );
+}
+
+
+void
+Breadcrumb::paintEvent( QPaintEvent* )
+{
+    QStylePainter p( this );
+    StyleHelper::horizontalHeader( &p, rect() );
+}
+
+// updateFrom is the item that has changed---all children must be recomputed
+// if invalid, redo the whole breadcrumb
+void
+Breadcrumb::updateButtons( const QModelIndex& updateFrom )
+{
+    qDebug() << "Updating buttons:" << updateFrom.data();
+    int cur = 0;
+    QModelIndex idx = updateFrom;
+    for ( int i = 0; i < m_buttons.count(); i++ )
+    {
+        qDebug() << "Checking if this breadcrumb item changed:" << m_buttons[ i ]->currentIndex().data() << updateFrom.data() << ( m_buttons[ i ]->currentIndex() != updateFrom);
+        if ( m_buttons[ i ]->currentIndex() == updateFrom )
+        {
+            cur = i;
+            break;
+        }
+    }
+
+    // We set the parent index, so go up one
+    idx = idx.parent();
+
+    // Ok, changed all indices that are at cur or past it. lets update them
+    // When we get to the "end" of the tree, the leaf node is the chart itself
+    qDebug() << "DONE and beginning iteration:" << idx.data();
+    while ( m_model->rowCount( idx ) > 0 )
+    {
+        qDebug() << "CHANGED AND iterating:" << idx.data();
+        BreadcrumbButton* btn = 0;
+        if ( m_buttons.size() <= cur )
+        {
+            // We have to create a new button, doesn't exist yet
+            btn = new BreadcrumbButton( this, m_model );
+            connect( btn, SIGNAL( currentIndexChanged( QModelIndex ) ), this, SLOT( breadcrumbComboChanged( QModelIndex ) ) );
+
+            m_buttonlayout->addWidget( btn );
+            btn->show();
+
+            // Animate all buttons except the first
+            if ( m_buttons.count() > 0 && isVisible() )
+            {
+                QPropertyAnimation* animation = new QPropertyAnimation( btn, "pos" );
+                animation->setDuration( 300 );
+                animation->setStartValue( m_buttons.last()->pos());
+                animation->setEndValue( btn->pos() );
+                animation->start( QAbstractAnimation::DeleteWhenStopped );
+            }
+
+            m_buttons.append( btn );
+        }
+        else
+        {
+            // Got a button already, we just want to change the contents
+            btn = m_buttons[ cur ];
+        }
+
+        // The children of idx are what populates this combobox.
+        // It takes care of setting the default/user-populated value.
+        btn->setParentIndex( idx );
+
+        // Repeat with children
+        idx = btn->currentIndex();
+
+        cur++;
+    }
+
+    // extra buttons to delete! (cur is 0-indexed)
+    while ( m_buttons.size() > cur )
+    {
+        BreadcrumbButton* b = m_buttons.takeLast();
+        m_buttonlayout->removeWidget( b );
+        b->deleteLater();
+    }
+
+    // Now we're at the leaf, lets activate the chart
+    emit activateIndex( idx );
+}
+
+void
+Breadcrumb::breadcrumbComboChanged( const QModelIndex& childSelected )
+{
+    // Some breadcrumb buttons' combobox changed. lets update the child breadcrumbs
+    tDebug() << "Combo changed:" << childSelected.data();
+    updateButtons( childSelected );
+}
diff --git a/src/libtomahawk/widgets/Breadcrumb.h b/src/libtomahawk/widgets/Breadcrumb.h
new file mode 100644
index 000000000..74adabf1a
--- /dev/null
+++ b/src/libtomahawk/widgets/Breadcrumb.h
@@ -0,0 +1,83 @@
+/* === 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_BREADCRUMB_H
+#define TOMAHAWK_BREADCRUMB_H
+
+#include <QWidget>
+#include <QModelIndex>
+
+class QHBoxLayout;
+class QAbstractItemModel;
+
+namespace Tomahawk {
+
+class BreadcrumbButton;
+
+/**
+ * A breadcrumb widget for Tomahawk's needs.
+ *
+ * This breadcrumb operates on a QAIM. It uses a KBreadcrumbSelectionModel to manage the visible
+ *  breadcrumb selection.
+ * This breadcrumb always expands fully to the deepest child.
+ * Selections are remembered when switching to/from in parent nodes
+ * Items that have a DefaultRole set will automatically select the default unless the user has
+ *  made a previous selection, which is saved in the UserSelection role
+ */
+class Breadcrumb : public QWidget
+{
+    Q_OBJECT
+public:
+    enum ExtraRoles {
+        DefaultRole = Qt::UserRole + 1,
+        UserSelectedRole = Qt::UserRole + 2,
+        ChartIdRole = Qt::UserRole + 3
+    };
+
+    explicit Breadcrumb( QWidget* parent = 0, Qt::WindowFlags f = 0 );
+    virtual ~Breadcrumb();
+
+    void setModel( QAbstractItemModel* model );
+    QAbstractItemModel* model() const { return m_model; }
+
+    void setRootIcon( const QPixmap& pm );
+
+protected:
+    virtual void paintEvent( QPaintEvent* );
+
+signals:
+    void activateIndex( const QModelIndex& idx );
+
+private slots:
+    void breadcrumbComboChanged( const QModelIndex& );
+
+private:
+    // Takes an index in the selection model to update from (updates from that to all children)
+    void updateButtons( const QModelIndex& fromIndex );
+
+    QAbstractItemModel* m_model;
+    QPixmap m_rootIcon;
+
+    QHBoxLayout* m_buttonlayout;
+    QList<BreadcrumbButton*> m_buttons;
+};
+
+}
+
+#endif // TOMAHAWK_BREADCRUMB_H
+
diff --git a/src/libtomahawk/widgets/siblingcrumbbutton.cpp b/src/libtomahawk/widgets/BreadcrumbButton.cpp
similarity index 51%
rename from src/libtomahawk/widgets/siblingcrumbbutton.cpp
rename to src/libtomahawk/widgets/BreadcrumbButton.cpp
index f11704aac..4a94b469a 100644
--- a/src/libtomahawk/widgets/siblingcrumbbutton.cpp
+++ b/src/libtomahawk/widgets/BreadcrumbButton.cpp
@@ -1,6 +1,7 @@
 /* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  *
  *   Copyright 2011, Casey Link <unnamedrambler@gmail.com>
+ *   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
@@ -16,76 +17,45 @@
  *   along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "siblingcrumbbutton.h"
+#include "BreadcrumbButton.h"
 
+#include "Breadcrumb.h"
 #include "combobox.h"
 #include "utils/stylehelper.h"
 #include "utils/tomahawkutils.h"
 
-#include <QTimer>
-#include <QDebug>
+#include <QPaintEvent>
 #include <QPainter>
-#include <QStyle>
-#include <QStyleOption>
+#include <QHBoxLayout>
 
-BreadcrumbButtonBase* SiblingCrumbButtonFactory::newButton(QModelIndex index, BreadcrumbBar *parent)
-{
-    return new SiblingCrumbButton(index, parent);
-}
+using namespace Tomahawk;
 
-SiblingCrumbButton::SiblingCrumbButton(
-    QModelIndex index, BreadcrumbBar *parent)
-    : BreadcrumbButtonBase(parent),
-      m_index(index), m_combo( new ComboBox(this) )
+BreadcrumbButton::BreadcrumbButton( Breadcrumb* parent, QAbstractItemModel* model )
+    : QWidget( parent )
+    , m_breadcrumb( parent )
+    , m_model( model )
+    , m_combo( new ComboBox( this ) )
 {
+    setLayout( new QHBoxLayout );
+    layout()->setContentsMargins( 0, 0, 0, 0 );
+    layout()->setSpacing( 0 );
+    layout()->addWidget( m_combo );
+
     setFixedHeight( TomahawkUtils::headerHeight() );
     m_combo->setSizeAdjustPolicy( QComboBox::AdjustToContents );
-    setIndex(index);
-    connect(m_combo, SIGNAL(activated(int)), SLOT(comboboxActivated(int)));
+
+    setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Expanding );
+
+    connect( m_combo, SIGNAL( activated( int ) ), SLOT( comboboxActivated( int ) ) );
 }
 
-void SiblingCrumbButton::setIndex( QModelIndex index )
+void
+BreadcrumbButton::paintEvent( QPaintEvent* )
 {
-    if ( !(m_index == index && text() == index.data().toString()) )
-    {
-        m_index = index;
-        setText( index.data().toString() );
-    }
-    fillCombo();
-}
-
-QModelIndex SiblingCrumbButton::index() const
-{
-    return m_index;
-}
-
-void SiblingCrumbButton::setActive( bool active )
-{
-    Q_UNUSED( active );
-    if ( active )
-        QTimer::singleShot( 0, this, SLOT( activateSelf() ) );
-}
-
-bool SiblingCrumbButton::isActive() const
-{
-    return false;
-}
-
-QSize SiblingCrumbButton::sizeHint() const
-{
-    // our width = width of combo + 20px for right-arrow and spacing
-    const int padding = hasChildren() ? 20 : 5;
-    return m_combo->sizeHint() + QSize( padding, 0 );
-}
-
-void SiblingCrumbButton::paintEvent( QPaintEvent *event )
-{
-    Q_UNUSED( event );
-
     QPainter p( this );
     QStyleOption opt;
     opt.initFrom( this );
-    QRect r = opt.rect;
+    QRect r = rect();
 
     StyleHelper::horizontalHeader( &p, r ); // draw the background
 
@@ -97,13 +67,12 @@ void SiblingCrumbButton::paintEvent( QPaintEvent *event )
     int rightSpacing = 10;
     int left = !reverse ? r.right()-rightSpacing - menuButtonWidth : r.left();
     int right = !reverse ? r.right()-rightSpacing : r.left() + menuButtonWidth;
-    int height = sizeHint().height();
+    int height = r.height();
     QRect arrowRect( ( left + right ) / 2 + ( reverse ? 6 : -6 ), 0, height, height );
 
     QStyleOption arrowOpt = opt;
     arrowOpt.rect = arrowRect;
 
-
     QLine l1( left, 0, right, height/2 );
     QLine l2( left, height, right, height/2 );
 
@@ -125,16 +94,38 @@ void SiblingCrumbButton::paintEvent( QPaintEvent *event )
     p.drawLine( l2 );
 }
 
-void SiblingCrumbButton::fillCombo()
+QSize
+BreadcrumbButton::sizeHint() const
 {
+    // our width = width of combo + 20px for right-arrow and spacing
+    const int padding = hasChildren() ? 20 : 8;
+    return m_combo->sizeHint() + QSize( padding, 0 );
+}
+
+
+void
+BreadcrumbButton::setParentIndex( const QModelIndex& idx )
+{
+    m_parentIndex = idx;
+    // Populate listview with list of children.
+    // Then try to find a default
     QStringList list;
-    int count = breadcrumbBar()->model()->rowCount(m_index.parent());
-    for(int i = 0; i < count; ++i) {
-        QModelIndex sibling = m_index.sibling(i,0);
-        if( sibling.isValid() )
-            list << sibling.data().toString();
+    int count = m_model->rowCount( m_parentIndex );
+    int defaultIndex = -1, userSelected = -1;
+    for ( int i = 0; i < count; ++i )
+    {
+        QModelIndex idx = m_model->index( i, 0, m_parentIndex );
+        if ( idx.isValid() )
+        {
+            list << idx.data().toString();
+            if ( idx.data( Breadcrumb::DefaultRole ).toBool() )
+                defaultIndex = i;
+            if ( idx.data( Breadcrumb::UserSelectedRole ).toBool() )
+                userSelected = i;
+        }
     }
 
+
     if ( m_combo->count() && list.count() )
     {
         // Check if it's the same, Don't change if it is, as it'll cause flickering
@@ -146,31 +137,42 @@ void SiblingCrumbButton::fillCombo()
             return;
     }
 
+    m_combo->hide();
     m_combo->clear();
-    m_combo->addItems(list);
-    m_combo->setCurrentIndex( m_combo->findText(text()));
+    m_combo->addItems( list );
+
+    if ( userSelected > -1 )
+        m_combo->setCurrentIndex( userSelected );
+    else if ( defaultIndex > -1 )
+        m_combo->setCurrentIndex( defaultIndex );
+
+    m_curIndex = m_model->index( m_combo->currentIndex(), 0, m_parentIndex );
+
+    m_combo->show();
     m_combo->adjustSize();
 }
 
-void SiblingCrumbButton::comboboxActivated(int i)
+void
+BreadcrumbButton::comboboxActivated( int idx )
 {
-    QModelIndex activated = m_index.sibling(i,0);
-    int count = breadcrumbBar()->model()->rowCount(activated);
-    if( count > 0 ) {
-//         qDebug() << "activated crumb with children:" << activated.child(0,0).data().toString();
-        breadcrumbBar()->currentChangedTriggered(activated.child(0,0));
-    } else {
-        // if it has no children, then emit itself
-        breadcrumbBar()->currentChangedTriggered(activated);
-    }
+    m_model->setData( m_curIndex, false, Breadcrumb::UserSelectedRole );
+
+    QModelIndex selected = m_model->index( idx, 0, m_parentIndex );
+    m_curIndex = selected;
+    m_model->setData( selected, true, Breadcrumb::UserSelectedRole );
+
+    emit currentIndexChanged( selected );
 }
 
-void SiblingCrumbButton::activateSelf()
+
+bool
+BreadcrumbButton::hasChildren() const
 {
-    comboboxActivated(m_index.row());
+    return m_model->rowCount( m_model->index( m_combo->currentIndex(), 0, m_parentIndex ) ) > 0;
 }
 
-bool SiblingCrumbButton::hasChildren() const
+QModelIndex
+BreadcrumbButton::currentIndex() const
 {
-    return m_index.model()->hasChildren(m_index);
+    return m_model->index( m_combo->currentIndex(), 0, m_parentIndex );
 }
diff --git a/src/libtomahawk/widgets/BreadcrumbButton.h b/src/libtomahawk/widgets/BreadcrumbButton.h
new file mode 100644
index 000000000..d7efaa96d
--- /dev/null
+++ b/src/libtomahawk/widgets/BreadcrumbButton.h
@@ -0,0 +1,69 @@
+/* === 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_BREADCRUMBBUTTON_H
+#define TOMAHAWK_BREADCRUMBBUTTON_H
+
+#include <QWidget>
+#include <QModelIndex>
+
+class ComboBox;
+class QPaintEvent;
+
+namespace Tomahawk {
+
+class Breadcrumb;
+class BreadcrumbModel;
+
+class BreadcrumbButton : public QWidget
+{
+    Q_OBJECT
+public:
+    explicit BreadcrumbButton( Breadcrumb* parent, QAbstractItemModel* model );
+
+    void setParentIndex( const QModelIndex& idx );
+
+    // Which index is currently visible. This is the first, default or last selected
+    // calculated immediately after loading, or what the user has selected if he has made
+    // a manua;l selection
+    QModelIndex currentIndex() const;
+protected:
+    virtual void paintEvent( QPaintEvent* );
+    virtual QSize sizeHint() const;
+
+signals:
+    // Some combobox value is changed
+    void currentIndexChanged( const QModelIndex& childSelected );
+
+private slots:
+    void comboboxActivated( int );
+
+private:
+    bool hasChildren() const;
+
+    Breadcrumb* m_breadcrumb;
+    QAbstractItemModel* m_model;
+
+    QPersistentModelIndex m_parentIndex;
+    QPersistentModelIndex m_curIndex;
+    ComboBox* m_combo;
+};
+
+}
+
+#endif // TOMAHAWK_BREADCRUMBBUTTON_H
diff --git a/src/libtomahawk/widgets/breadcrumbbar.cpp b/src/libtomahawk/widgets/breadcrumbbar.cpp
deleted file mode 100644
index dea514c0e..000000000
--- a/src/libtomahawk/widgets/breadcrumbbar.cpp
+++ /dev/null
@@ -1,345 +0,0 @@
-/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
- *
- *   Copyright 2011, Casey Link <unnamedrambler@gmail.com>
- *   Copyright (C) 2004-2011 Glenn Van Loon, glenn@startupmanager.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 "breadcrumbbar.h"
-#include "breadcrumbbuttonbase.h"
-
-#include <QAbstractItemModel>
-#include <QAbstractProxyModel>
-#include <QHBoxLayout>
-#include <QItemSelectionModel>
-#include <QLabel>
-#include <QPropertyAnimation>
-#include <QResizeEvent>
-#include <QPushButton>
-
-#include "utils/logger.h"
-
-
-BreadcrumbBar::BreadcrumbBar( BreadcrumbButtonFactory *buttonFactory, QWidget *parent )
-    : QWidget( parent )
-    , m_buttonFactory( buttonFactory )
-    , m_model( 0 )
-    , m_selectionModel( 0 )
-    , m_layout( new QHBoxLayout( this ) )
-    , m_useAnimation( false )
-
-{
-    m_layout->setSpacing( 0 );
-    m_layout->setMargin( 0 );
-    m_layout->setAlignment( Qt::AlignLeft );
-
-    setAutoFillBackground( false );
-    setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
-
-    setLayoutDirection( Qt::LeftToRight );
-    setLayout( m_layout );
-    setMinimumWidth( 100 );
-    show();
-}
-
-
-BreadcrumbBar::BreadcrumbBar( QWidget *parent )
-    : QWidget( parent )
-    , m_buttonFactory( 0 )
-    , m_model( 0 )
-    , m_selectionModel( 0 )
-    , m_layout( new QHBoxLayout( this ) )
-    , m_useAnimation( false )
-
-{
-    m_layout->setSpacing( 0 );
-    m_layout->setMargin( 0 );
-    m_layout->setAlignment( Qt::AlignLeft );
-
-    setAutoFillBackground( false );
-    setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
-
-    setLayoutDirection( Qt::LeftToRight );
-    setLayout( m_layout );
-    setMinimumWidth( 100 );
-    show();
-}
-
-
-BreadcrumbBar::~BreadcrumbBar()
-{
-}
-
-
-void BreadcrumbBar::setButtonFactory(BreadcrumbButtonFactory *buttonFactory)
-{
-    tDebug( LOGVERBOSE ) << "Breadcrumbbar:: got me some button factory!";
-    m_buttonFactory = buttonFactory;
-}
-
-
-BreadcrumbButtonFactory* BreadcrumbBar::buttonFactory() const
-{
-    return m_buttonFactory;
-}
-
-
-void BreadcrumbBar::appendButton(BreadcrumbButtonBase *widget, int stretch)
-{
-    m_layout->insertWidget(m_layout->count(), widget, stretch);
-    if( !m_useAnimation )
-        return; //we're done here.
-
-    // A nifty trick to force the widget to calculate its position and geometry
-    //widget->setAttribute(Qt::WA_DontShowOnScreen);
-    widget->show();
-    //widget->setAttribute(Qt::WA_DontShowOnScreen, false);
-
-    if( m_navButtons.size() > 0 ) {
-        QWidget* neighbor = m_layout->itemAt(m_layout->count()-2)->widget();
-        QPropertyAnimation *animation = new QPropertyAnimation(widget,"pos");
-        animation->setDuration(300);
-        animation->setStartValue(neighbor->pos());
-        animation->setEndValue(widget->pos());
-        animation->start(QAbstractAnimation::DeleteWhenStopped);
-    }
-}
-
-
-void BreadcrumbBar::deleteAnimationFinished()
-{
-    QPropertyAnimation *anim = qobject_cast<QPropertyAnimation*>(sender());
-
-    if( !anim )
-        return;
-    QObject *obj = anim->targetObject();
-    obj->deleteLater();
-    anim->deleteLater();
-}
-
-
-void BreadcrumbBar::deleteButton(BreadcrumbButtonBase *widget)
-{
-    widget->hide();
-    widget->deleteLater();
-    return; // all done here
-
-    // Don't animate on delete. We expand a child recursively until it has no more children---this makes
-    // the deleting and creating animations overlap.
-
-//     int index = m_layout->indexOf(widget);
-//     if( index != 0 && m_navButtons.size() > 0 ) {
-//         QWidget* neighbor = m_layout->itemAt(index-1)->widget();
-//         QPropertyAnimation *animation = new QPropertyAnimation(widget,"pos");
-//         m_layout->removeWidget(widget);
-//         connect(animation, SIGNAL(finished()), SLOT(deleteAnimationFinished()));
-//         animation->setDuration(300);
-//         animation->setStartValue(widget->pos());
-//         animation->setEndValue(neighbor->pos());
-//         animation->start();
-//     } else {
-//         widget->hide();
-//         widget->deleteLater();
-//     }
-}
-
-
-void BreadcrumbBar::updateButtons()
-{
-    tDebug( LOGVERBOSE ) << "Breadcrumbbar:: updateButtons" << m_buttonFactory << m_selectionModel ;
-    if ( m_selectionModel )
-        tDebug( LOGVERBOSE ) <<"Breadcrumbbar:: update buttoms current index"<< m_selectionModel->currentIndex().isValid();
-    if ( !m_buttonFactory || !m_selectionModel || !m_selectionModel->currentIndex().isValid() )
-    {
-        tDebug( LOGVERBOSE ) << "Breadcrumb:: updatebuttons failed!";
-        return;
-    }
-
-    QLinkedList<BreadcrumbButtonBase*>::iterator it = m_navButtons.begin();
-    QLinkedList<BreadcrumbButtonBase*>::const_iterator const itEnd = m_navButtons.end();
-    bool createButton = false;
-
-    QModelIndex index = m_selectionModel->currentIndex();
-    QList<QModelIndex> indexes;
-    while (index.parent().isValid())
-    {
-        indexes.prepend(index);
-        index = index.parent();
-    }
-    tDebug( LOGVERBOSE ) << "BreadcrumbBar::updateButtons:: " << index.data().toString();
-    indexes.prepend(index);
-
-    int count = indexes.size(), i = 0;
-    foreach (index, indexes)
-    {
-        createButton = (it == itEnd);
-        bool isLastButton = (++i == count);
-
-        QString const dirName = index.data().toString();
-        BreadcrumbButtonBase *button = 0;
-        if (createButton)
-        {
-            button = m_buttonFactory->newButton(index, this);
-            appendButton(button);
-            button->setActive(isLastButton);
-            m_navButtons.append(button);
-        }
-        else
-        {
-            button = *it;
-            button->setIndex(index);
-            button->setActive(isLastButton);
-            ++it;
-        }
-    }
-
-    QLinkedList<BreadcrumbButtonBase*>::Iterator itBegin = it;
-    while (it != itEnd)
-    {
-        deleteButton(*it);
-        ++it;
-    }
-    m_navButtons.erase(itBegin, m_navButtons.end());
-
-    collapseButtons();
-
-    adjustSize();
-}
-
-
-void BreadcrumbBar::collapseButtons()
-{
-    foreach (BreadcrumbButtonBase *button, m_navButtons) {
-        button->show();
-    }
-
-    const int desired_width = size().width();
-    int current_width = sizeHint().width();
-
-    QLinkedList<BreadcrumbButtonBase*>::iterator it = m_navButtons.begin();
-    QLinkedList<BreadcrumbButtonBase*>::const_iterator const itEnd = m_navButtons.end();
-    it = m_navButtons.begin();
-    while( current_width > desired_width && it != itEnd ) {
-        (*it)->hide();
-        ++it;
-        current_width = sizeHint().width();
-    }
-}
-
-
-void BreadcrumbBar::clearButtons()
-{
-    foreach (BreadcrumbButtonBase *button, m_navButtons)
-    {
-        button->hide();
-        button->deleteLater();
-    }
-    m_navButtons.clear();
-}
-
-
-void BreadcrumbBar::currentIndexChanged()
-{
-    updateButtons();
-}
-
-
-void BreadcrumbBar::setRootIcon(const QIcon &icon)
-{
-
-    m_rootIcon = icon;
-    QPushButton* button = new QPushButton(icon, "", this);
-    button->setFlat(true);
-    button->setStyleSheet( "QPushButton{ background-color: transparent; border: none; width:16px; height:16px;}" );
-    m_layout->insertWidget(0, button);
-    m_layout->insertSpacing(0,5);
-    m_layout->insertSpacing(2,5);
-    connect(button, SIGNAL(clicked()), this, SIGNAL(rootClicked()));
-}
-
-
-void BreadcrumbBar::setRootText(const QString &text)
-{
-    //TODO: implement this
-    m_rootText = text;
-    /*QLabel *label= new QLabel(this);
-    label->setPixmap(icon.pixmap(16,16));
-    m_layout->insertWidget(0, label);
-    m_layout->insertSpacing(0,5);
-    m_layout->insertSpacing(2,5);*/
-}
-
-
-void BreadcrumbBar::setUseAnimation(bool use)
-{
-    m_useAnimation = use;
-}
-
-
-bool BreadcrumbBar::useAnimation() const
-{
-    return m_useAnimation;
-}
-
-
-void BreadcrumbBar::setModel(QAbstractItemModel *model)
-{
-    m_model = model;
-    updateButtons();
-}
-
-
-QAbstractItemModel* BreadcrumbBar::model()
-{
-    return m_model;
-}
-
-
-void BreadcrumbBar::setSelectionModel(QItemSelectionModel *selectionModel)
-{
-    m_selectionModel = selectionModel;
-    m_selectionModel->setCurrentIndex(m_model->index(0,0), QItemSelectionModel::SelectCurrent);
-    connect(m_selectionModel,
-        SIGNAL(currentChanged(QModelIndex const&, QModelIndex const&)),
-        this, SLOT(currentIndexChanged()));
-    updateButtons();
-}
-
-
-QItemSelectionModel* BreadcrumbBar::selectionModel()
-{
-    return m_selectionModel;
-}
-
-
-QModelIndex BreadcrumbBar::currentIndex()
-{
-    return m_selectionModel->currentIndex();
-}
-
-
-void BreadcrumbBar::currentChangedTriggered(QModelIndex const& index)
-{
-    Q_ASSERT(m_selectionModel);
-    m_selectionModel->setCurrentIndex( index, QItemSelectionModel::SelectCurrent);
-    emit currentIndexChanged(index);
-}
-
-
-void BreadcrumbBar::resizeEvent ( QResizeEvent * event )
-{
-    Q_UNUSED( event );
-    collapseButtons();
-}
diff --git a/src/libtomahawk/widgets/breadcrumbbar.h b/src/libtomahawk/widgets/breadcrumbbar.h
deleted file mode 100644
index 0ebe68df0..000000000
--- a/src/libtomahawk/widgets/breadcrumbbar.h
+++ /dev/null
@@ -1,198 +0,0 @@
-/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
- *
- *   Copyright 2011, Casey Link <unnamedrambler@gmail.com>
- *   Copyright (C) 2004-2011 Glenn Van Loon, glenn@startupmanager.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 BREADCRUMBBAR_H
-#define BREADCRUMBBAR_H
-
-#include "breadcrumbbuttonbase.h"
-
-#include <QModelIndex>
-#include <QWidget>
-#include <QLinkedList>
-#include <QIcon>
-
-class QAbstractItemModel;
-class QItemSelectionModel;
-class QAbstractItemModel;
-class QAbstractProxyModel;
-class QHBoxLayout;
-class QItemSelectionModel;
-class QResizeEvent;
-
-/**
- * \brief A breadcrumb view for a QAbstractItemModel
- *
- * This a lean Breadcrumb navigation bar that supports the following features:
- *	- a QAbstractItemModel data source for MV goodness
- *	- client provided crumb button widgets (to use your own style and behavior)
- *	- client provided crumb animation [optional]
- *	- client provided root item (icon or text) [optional]
- *
- */
-class BreadcrumbBar : public QWidget
-{
-    Q_OBJECT
-
-public:
-    /**
-     *	\brief breadcrumb bar constructor
-     *	\param buttonFactory the button factory to instantiate bread crumbs from
-     */
-    BreadcrumbBar(BreadcrumbButtonFactory *buttonFactory, QWidget *parent = 0);
-    /**
-     *	\brief breadcrumb bar constructor
-     *	You must set the button factory using BreadcrumbBar::setButtonFactory
-     */
-    BreadcrumbBar(QWidget *parent = 0);
-
-    virtual ~BreadcrumbBar();
-
-    /**
-     * \brief sets the button factory to use to create buttons
-     * \param buttonFactory the button factory
-     */
-    void setButtonFactory(BreadcrumbButtonFactory *buttonFactory);
-    BreadcrumbButtonFactory* buttonFactory() const;
-
-    /**
-     * \brief Set the icon that should be displayed at the root
-     * \param icon the icon
-     */
-    void setRootIcon(const QIcon &icon);
-    /**
-     * \brief Set the text that should be displayed at the root
-     * \param test the text
-     */
-    void setRootText(const QString &text);
-
-    /**
-     * \brief Set whether the crumb animation should be used (default: false)
-     */
-    void setUseAnimation(bool use);
-    bool useAnimation() const;
-
-    /**
-     *	\brief set the item model to use as a data source
-     *	\param model the item model
-     *
-     *	Although the item model can be of any structure, the breadcrumb bar
-     *	works best when the model's structure is a tree.
-     */
-    void setModel(QAbstractItemModel *model);
-    QAbstractItemModel *model();
-
-    /**
-     *	\brief set the selection model used to determine the crumb items
-     *	\param selectionModel the selection model
-     *
-     *  The selection model is just as important as your model. In fact your
-     *  model can be of any structure (even not-tree-like) as long as your
-     *  selection model understands what it means for an item to be a crumb,
-     *  and knows how to select it.
-     *
-     *  If you have a standard tree model, you probably should use
-     *  KBreadCrumbSelectionModel which encapsulates the concept of "When an
-     *  item is selected, make a selection which includes its parent and
-     *  ancestor items until the top is reached".
-     *
-     *  \sa See Stephen Kelley's blog post here http://steveire.wordpress.com/2010/04/21/breadcrumbs-for-your-view
-     */
-    void setSelectionModel(QItemSelectionModel *selectionModel);
-    QItemSelectionModel *selectionModel();
-
-    /**
-     *	\brief get the index of the currently active item
-     *	\return the active index
-     */
-    QModelIndex currentIndex();
-
-    /**
-     *	\brief used by crumbs to notify that the current index has changed
-     *	\param index the new current index
-     */
-    void currentChangedTriggered(QModelIndex const& index);
-
-signals:
-    void rootClicked();
-    void currentIndexChanged(QModelIndex);
-
-protected:
-    /**
-     *	\brief append a crumb widget
-     *	\param button the crumb button to add
-     *	\param stretch widget stretch factor
-     *	Respects the useAnimation() setting.
-     */
-    void appendButton(BreadcrumbButtonBase *button, int stretch = 0);
-
-    /**
-     *	\brief deletes a crumb from the bar
-     *	\param button the crumb button to delete
-     *	Respects the useAnimation() setting.
-     */
-    void deleteButton(BreadcrumbButtonBase *button);
-
-    /**
-     * \brief collapses crumbs when there isnt enough room for all of them
-     * Starts hiding from the left until we can fit all the buttons.
-     */
-    void collapseButtons();
-    /**
-     * \brief reimpl from QWidget
-     */
-    void resizeEvent ( QResizeEvent * event );
-
-
-protected slots:
-
-    /**
-     * \brief The current index has changed in the selection model
-     * When the selection model changes, we get notified here
-     */
-    void currentIndexChanged();
-
-    /**
-     *	\brief Recreate the button bar from the selection model
-     */
-    void updateButtons();
-    /**
-     *	\brief clear breadcrumb buttons
-     */
-    void clearButtons();
-
-    /**
-     * Called when the delete animation finishes so we can delete the button
-     * object.
-     */
-    void deleteAnimationFinished();
-
-
-private:
-    BreadcrumbButtonFactory *m_buttonFactory; /*!< Factory used to create new crumbs */
-    QAbstractItemModel *m_model; /*!< The source model */
-    QItemSelectionModel *m_selectionModel; /*!< The selection model */
-    QHBoxLayout *m_layout; /*!< The layout holding out crumb buttons */
-    QLinkedList<BreadcrumbButtonBase*> m_navButtons; /*< Our list of crumbs! */
-    QIcon m_rootIcon; /*!< The root icon */
-    QString m_rootText; /*!< The root text */
-    bool m_useAnimation; /*<!< Whether we should animate the transition or not */
-};
-
-
-#endif // BREADCRUMBBAR_H
diff --git a/src/libtomahawk/widgets/breadcrumbbuttonbase.cpp b/src/libtomahawk/widgets/breadcrumbbuttonbase.cpp
deleted file mode 100644
index 1c5fa8583..000000000
--- a/src/libtomahawk/widgets/breadcrumbbuttonbase.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
- *
- *   Copyright 2011, Casey Link <unnamedrambler@gmail.com>
- *   Copyright (C) 2004-2011 Glenn Van Loon, glenn@startupmanager.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 "breadcrumbbuttonbase.h"
-#include "breadcrumbbar.h"
-
-BreadcrumbButtonBase::BreadcrumbButtonBase(BreadcrumbBar *parent)
-    : QPushButton(parent),  m_breadcrumbBar(parent)
-{
-    setFocusPolicy(Qt::NoFocus);
-    setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
-    setMinimumHeight(parent->minimumHeight());
-}
-
-BreadcrumbButtonBase::~BreadcrumbButtonBase()
-{
-}
-
-BreadcrumbBar* BreadcrumbButtonBase::breadcrumbBar() const
-{
-    return m_breadcrumbBar;
-}
-
diff --git a/src/libtomahawk/widgets/breadcrumbbuttonbase.h b/src/libtomahawk/widgets/breadcrumbbuttonbase.h
deleted file mode 100644
index 7625b6bae..000000000
--- a/src/libtomahawk/widgets/breadcrumbbuttonbase.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
- *
- *   Copyright 2011, Casey Link <unnamedrambler@gmail.com>
- *   Copyright (C) 2004-2011 Glenn Van Loon, glenn@startupmanager.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 BREADCRUMBBUTTONBASE_P_H
-#define BREADCRUMBBUTTONBASE_P_H
-
-#include <QPushButton>
-#include <QModelIndex>
-
-class BreadcrumbBar;
-class BreadcrumbButtonBase;
-
-/**
- * \brief an abstract factory class to create crumb buttons
- * Subclass this class and make it return bread crumb buttons of your type.
- */
-class BreadcrumbButtonFactory {
-
-    public:
-        BreadcrumbButtonFactory(){}
-
-        virtual ~BreadcrumbButtonFactory(){}
-
-        /**
-         * \brief instantiates a new bread crumb button
-         * \param index the initial index this crumb should hold
-         * \param parent the breadcrumb bar this button will belong to
-         * \returns a new bread crumb button allocated on the heap.
-         *
-         * This method can be as simple as:
-         * \code
-         *      return new MyBreadCrumbButton(index, parent);
-         * \endcode
-         *
-         */
-        virtual BreadcrumbButtonBase* newButton(QModelIndex index, BreadcrumbBar *parent) = 0;
-
-};
-
-/**
- *	\brief The button base class for the BreadcrumbBar
- * Re-implement this to provide your own crumb buttons. Don't forget to supply
- * a BreadcrumbButtonFactory as well.
- */
-class BreadcrumbButtonBase : public QPushButton
-{
-    Q_OBJECT
-
-public:
-    explicit BreadcrumbButtonBase(BreadcrumbBar *parent);
-    virtual ~BreadcrumbButtonBase();
-
-    /**
-     *	\brief retrieve the breadcrumb bar that this button belongs to
-     *	\return the parent breadcrumb bar
-     */
-    BreadcrumbBar* breadcrumbBar() const;
-
-    /**
-     *	\brief set the model item that this button represents
-     *	\param index the index of the model items to display
-     */
-    virtual void setIndex(QModelIndex index) = 0;
-    virtual QModelIndex index() const = 0;
-
-    /**
-     *	\brief sets whether this button is active or not
-     *	\param active true for active, false for inactive
-     *	You could, for example, make the active button bold.
-     */
-    virtual void setActive(bool active) = 0;
-    virtual bool isActive() const = 0;
-
-private:
-    BreadcrumbBar *m_breadcrumbBar;
-};
-
-#endif // BREADCRUMBBUTTONBASE_P_H
diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp
index 975e2f605..2f0f8415a 100644
--- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp
+++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.cpp
@@ -39,7 +39,7 @@ static QString s_aiInfoIdentifier = QString( "AlbumInfoWidget" );
 using namespace Tomahawk;
 
 
-AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* parent )
+AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, ModelMode startingMode, QWidget* parent )
     : QWidget( parent )
     , ui( new Ui::AlbumInfoWidget )
 {
@@ -58,16 +58,19 @@ AlbumInfoWidget::AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* par
     ui->albumsView->setAlbumModel( m_albumsModel );
 
     m_tracksModel = new TreeModel( ui->tracksView );
-    m_tracksModel->setMode( TreeModel::InfoSystem );
+    m_tracksModel->setMode( startingMode );
     ui->tracksView->setTreeModel( m_tracksModel );
     ui->tracksView->setRootIsDecorated( false );
 
     m_pixmap = QPixmap( RESPATH "images/no-album-art-placeholder.png" ).scaledToWidth( 48, Qt::SmoothTransformation );
 
     m_button = new OverlayButton( ui->tracksView );
-    m_button->setText( tr( "Click to show Super Collection Tracks" ) );
     m_button->setCheckable( true );
-    m_button->setChecked( true );
+    m_button->setChecked( m_tracksModel->mode() == InfoSystemMode );
+    if ( m_button->isChecked() )
+        m_button->setText( tr( "Click to show Super Collection Tracks" ) );
+    else
+        m_button->setText( tr( "Click to show Official Tracks" ) );
 
     connect( m_button, SIGNAL( clicked() ), SLOT( onModeToggle() ) );
     connect( m_tracksModel, SIGNAL( loadingStarted() ), SLOT( onLoadingStarted() ) );
@@ -88,11 +91,24 @@ AlbumInfoWidget::~AlbumInfoWidget()
     delete ui;
 }
 
+PlaylistInterface*
+AlbumInfoWidget::playlistInterface() const
+{
+    return ui->tracksView->playlistInterface();
+}
+
+void
+AlbumInfoWidget::setMode( ModelMode mode )
+{
+    if ( m_tracksModel->mode() != mode )
+        onModeToggle();
+}
+
 
 void
 AlbumInfoWidget::onModeToggle()
 {
-    m_tracksModel->setMode( m_button->isChecked() ? TreeModel::InfoSystem : TreeModel::Database );
+    m_tracksModel->setMode( m_button->isChecked() ? InfoSystemMode : DatabaseMode );
     m_tracksModel->clear();
     m_tracksModel->addTracks( m_album, QModelIndex() );
 
@@ -130,6 +146,23 @@ AlbumInfoWidget::isBeingPlayed() const
     return false;
 }
 
+artist_ptr AlbumInfoWidget::descriptionArtist() const
+{
+    if ( !m_album.isNull() && !m_album->artist().isNull() )
+        return m_album->artist();
+
+    return artist_ptr();
+}
+
+ViewPage::DescriptionType
+AlbumInfoWidget::descriptionType()
+{
+    if ( !m_album.isNull() && !m_album->artist().isNull() )
+        return ViewPage::ArtistType;
+
+    return ViewPage::TextType;
+}
+
 
 void
 AlbumInfoWidget::load( const album_ptr& album )
@@ -137,6 +170,7 @@ AlbumInfoWidget::load( const album_ptr& album )
     m_album = album;
     m_title = album->name();
     m_description = album->artist()->name();
+
     ui->albumsLabel->setText( tr( "Other Albums by %1" ).arg( album->artist()->name() ) );
 
     m_tracksModel->addTracks( album, QModelIndex() );
diff --git a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h
index bb5c521d0..9542ab679 100644
--- a/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h
+++ b/src/libtomahawk/widgets/infowidgets/AlbumInfoWidget.h
@@ -35,6 +35,7 @@
 #include "infosystem/infosystem.h"
 
 #include "dllmacro.h"
+#include "typedefs.h"
 
 class AlbumModel;
 class TreeModel;
@@ -50,9 +51,29 @@ class DLLEXPORT AlbumInfoWidget : public QWidget, public Tomahawk::ViewPage
 Q_OBJECT
 
 public:
-    AlbumInfoWidget( const Tomahawk::album_ptr& album, QWidget* parent = 0 );
+    AlbumInfoWidget( const Tomahawk::album_ptr& album, Tomahawk::ModelMode startingMode = Tomahawk::InfoSystemMode, QWidget* parent = 0 );
     ~AlbumInfoWidget();
 
+    virtual QWidget* widget() { return this; }
+    virtual Tomahawk::PlaylistInterface* playlistInterface() const;
+
+    virtual QString title() const { return m_title; }
+    virtual DescriptionType descriptionType();
+    virtual QString description() const { return m_description; }
+    virtual Tomahawk::artist_ptr descriptionArtist() const;
+    virtual QString longDescription() const { return m_longDescription; }
+    virtual QPixmap pixmap() const { if ( m_pixmap.isNull() ) return Tomahawk::ViewPage::pixmap(); else return m_pixmap; }
+
+    void setMode( Tomahawk::ModelMode mode );
+
+    virtual bool isTemporaryPage() const { return true; }
+    virtual bool showStatsBar() const { return false; }
+
+    virtual bool jumpToCurrentTrack() { return false; }
+    virtual bool isBeingPlayed() const;
+
+public slots:
+
     /** \brief Loads information for a given album.
      *  \param album The album that you want to load information for.
      *
@@ -63,23 +84,9 @@ public:
      */
     void load( const Tomahawk::album_ptr& album );
 
-    virtual QWidget* widget() { return this; }
-    virtual Tomahawk::PlaylistInterface* playlistInterface() const { return 0; }
-
-    virtual QString title() const { return m_title; }
-    virtual QString description() const { return m_description; }
-    virtual QString longDescription() const { return m_longDescription; }
-    virtual QPixmap pixmap() const { if ( m_pixmap.isNull() ) return Tomahawk::ViewPage::pixmap(); else return m_pixmap; }
-
-    virtual bool isTemporaryPage() const { return true; }
-    virtual bool showStatsBar() const { return false; }
-
-    virtual bool jumpToCurrentTrack() { return false; }
-    virtual bool isBeingPlayed() const;
-
 signals:
     void longDescriptionChanged( const QString& description );
-    void descriptionChanged( const QString& description );
+    void descriptionChanged( const Tomahawk::artist_ptr& artist );
     void pixmapChanged( const QPixmap& pixmap );
 
 protected:
diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp
index 32a32b083..3ead1a03c 100644
--- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp
+++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.cpp
@@ -17,12 +17,13 @@
  */
 
 #include "ArtistInfoWidget.h"
+#include "ArtistInfoWidget_p.h"
 #include "ui_ArtistInfoWidget.h"
 
 #include "audio/audioengine.h"
-#include "viewmanager.h"
 #include "playlist/treemodel.h"
 #include "playlist/playlistmodel.h"
+#include "playlist/treeproxymodel.h"
 
 #include "database/databasecommand_alltracks.h"
 #include "database/databasecommand_allalbums.h"
@@ -45,6 +46,8 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget*
 {
     ui->setupUi( this );
 
+    m_plInterface = new MetaPlaylistInterface( this );
+
     ui->albums->setFrameShape( QFrame::NoFrame );
     ui->albums->setAttribute( Qt::WA_MacShowFocusRect, 0 );
     ui->relatedArtists->setFrameShape( QFrame::NoFrame );
@@ -59,7 +62,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget*
     TomahawkUtils::unmarginLayout( ui->albumHeader->layout() );
 
     m_albumsModel = new TreeModel( ui->albums );
-    m_albumsModel->setMode( TreeModel::InfoSystem );
+    m_albumsModel->setMode( InfoSystemMode );
     ui->albums->setTreeModel( m_albumsModel );
 
     m_relatedModel = new TreeModel( ui->relatedArtists );
@@ -70,7 +73,7 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget*
     m_topHitsModel->setStyle( TrackModel::Short );
     ui->topHits->setTrackModel( m_topHitsModel );
 
-    m_pixmap = QPixmap( RESPATH "images/no-album-art-placeholder.png" ).scaledToWidth( 48, Qt::SmoothTransformation );
+    m_pixmap = QPixmap( RESPATH "images/no-album-no-case.png" ).scaledToWidth( 48, Qt::SmoothTransformation );
 
     m_button = new OverlayButton( ui->albums );
     m_button->setText( tr( "Click to show All Releases" ) );
@@ -93,14 +96,21 @@ ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget*
 
 ArtistInfoWidget::~ArtistInfoWidget()
 {
+    delete m_plInterface;
     delete ui;
 }
 
+PlaylistInterface*
+ArtistInfoWidget::playlistInterface() const
+{
+    return m_plInterface;
+}
+
 
 void
 ArtistInfoWidget::onModeToggle()
 {
-    m_albumsModel->setMode( m_button->isChecked() ? TreeModel::InfoSystem : TreeModel::Database );
+    m_albumsModel->setMode( m_button->isChecked() ? InfoSystemMode : DatabaseMode );
     m_albumsModel->clear();
     m_albumsModel->addAlbums( m_artist, QModelIndex() );
 
@@ -178,12 +188,15 @@ ArtistInfoWidget::load( const artist_ptr& artist )
     requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
 
     requestData.type = Tomahawk::InfoSystem::InfoArtistImages;
+    requestData.requestId = TomahawkUtils::infosystemRequestId();
     Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
 
     requestData.type = Tomahawk::InfoSystem::InfoArtistSimilars;
+    requestData.requestId = TomahawkUtils::infosystemRequestId();
     Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
 
     requestData.type = Tomahawk::InfoSystem::InfoArtistSongs;
+    requestData.requestId = TomahawkUtils::infosystemRequestId();
     Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
 }
 
diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h
index 65ef84f29..754578bef 100644
--- a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h
+++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget.h
@@ -47,6 +47,8 @@ namespace Ui
     class ArtistInfoWidget;
 }
 
+class MetaPlaylistInterface;
+
 class DLLEXPORT ArtistInfoWidget : public QWidget, public Tomahawk::ViewPage
 {
 Q_OBJECT
@@ -66,7 +68,7 @@ public:
     void load( const Tomahawk::artist_ptr& artist );
 
     virtual QWidget* widget() { return this; }
-    virtual Tomahawk::PlaylistInterface* playlistInterface() const { return 0; }
+    virtual Tomahawk::PlaylistInterface* playlistInterface() const;
 
     virtual QString title() const { return m_title; }
     virtual QString description() const { return m_description; }
@@ -103,6 +105,7 @@ private:
     TreeModel* m_relatedModel;
     TreeModel* m_albumsModel;
     PlaylistModel* m_topHitsModel;
+    MetaPlaylistInterface* m_plInterface;
 
     OverlayButton* m_button;
 
@@ -111,6 +114,8 @@ private:
     QString m_longDescription;
     QString m_infoId;
     QPixmap m_pixmap;
+
+    friend class MetaPlaylistInterface;
 };
 
 #endif // ARTISTINFOWIDGET_H
diff --git a/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h
new file mode 100644
index 000000000..4a3b6b50a
--- /dev/null
+++ b/src/libtomahawk/widgets/infowidgets/ArtistInfoWidget_p.h
@@ -0,0 +1,106 @@
+/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
+ *
+ *   Copyright 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 ARTISTINFOWIDGET_P_H
+#define ARTISTINFOWIDGET_P_H
+
+#include "ArtistInfoWidget.h"
+#include "ui_ArtistInfoWidget.h"
+#include "playlistinterface.h"
+#include "treeproxymodel.h"
+#include "result.h"
+
+#include <QObject>
+
+class MetaPlaylistInterface : public QObject, public Tomahawk::PlaylistInterface
+{
+    Q_OBJECT
+public:
+    explicit MetaPlaylistInterface( ArtistInfoWidget* w )
+        : PlaylistInterface( this )
+        , m_w( w )
+    {
+        connect( m_w->ui->albums->proxyModel(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ),
+                 SLOT( anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ) );
+        connect( m_w->ui->relatedArtists->proxyModel(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ),
+                 SLOT( anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ) );
+        connect( m_w->ui->topHits->proxyModel(), SIGNAL( repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ),
+                 SLOT( anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode ) ) );
+
+        connect( m_w->ui->albums->proxyModel(), SIGNAL( shuffleModeChanged( bool ) ),
+                 SLOT( anyShuffleChanged( bool ) ) );
+        connect( m_w->ui->relatedArtists->proxyModel(), SIGNAL( shuffleModeChanged( bool ) ),
+                 SLOT( anyShuffleChanged( bool ) ) );
+        connect( m_w->ui->topHits->proxyModel(), SIGNAL( shuffleModeChanged( bool ) ),
+                 SLOT( anyShuffleChanged( bool ) ) );
+    }
+    virtual ~MetaPlaylistInterface() {}
+
+
+    // Any one is fine, we keep them all synched
+    virtual RepeatMode repeatMode() const { return m_w->ui->albums->proxyModel()->repeatMode(); }
+
+    virtual bool shuffled() const { return m_w->ui->albums->proxyModel()->shuffled(); }
+
+    // Do nothing
+    virtual Tomahawk::result_ptr currentItem() const { return Tomahawk::result_ptr(); }
+    virtual Tomahawk::result_ptr siblingItem( int ) { return Tomahawk::result_ptr(); }
+    virtual int trackCount() const { return 0; }
+    virtual QList< Tomahawk::query_ptr > tracks() { return QList< Tomahawk::query_ptr >(); }
+    virtual int unfilteredTrackCount() const { return 0; }
+
+public slots:
+    virtual void setRepeatMode( RepeatMode mode )
+    {
+        m_w->ui->albums->proxyModel()->setRepeatMode( mode );
+        m_w->ui->relatedArtists->proxyModel()->setRepeatMode( mode );
+        m_w->ui->topHits->proxyModel()->setRepeatMode( mode );
+    }
+
+    virtual void setShuffled( bool enabled )
+    {
+        m_w->ui->albums->proxyModel()->setShuffled( enabled );
+        m_w->ui->relatedArtists->proxyModel()->setShuffled( enabled );
+        m_w->ui->topHits->proxyModel()->setShuffled( enabled );
+    }
+
+signals:
+    void repeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode );
+    void shuffleModeChanged( bool enabled );
+
+    void trackCountChanged( unsigned int tracks );
+    void sourceTrackCountChanged( unsigned int tracks );
+    void nextTrackReady();
+
+private slots:
+    void anyRepeatModeChanged( Tomahawk::PlaylistInterface::RepeatMode mode )
+    {
+        emit repeatModeChanged( mode );
+    }
+
+    void anyShuffleChanged( bool enabled )
+    {
+        emit shuffleModeChanged( enabled );
+    }
+
+private:
+    ArtistInfoWidget* m_w;
+
+};
+
+#endif
diff --git a/src/libtomahawk/widgets/kbreadcrumbselectionmodel.cpp b/src/libtomahawk/widgets/kbreadcrumbselectionmodel.cpp
deleted file mode 100644
index 92af04c96..000000000
--- a/src/libtomahawk/widgets/kbreadcrumbselectionmodel.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
-    Copyright (C) 2010 Klarälvdalens Datakonsult AB,
-        a KDAB Group company, info@kdab.net,
-        author Stephen Kelly <stephen@kdab.com>
-
-    This library is free software; you can redistribute it and/or modify it
-    under the terms of the GNU Library General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or (at your
-    option) any later version.
-
-    This library 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 Library General Public
-    License for more details.
-
-    You should have received a copy of the GNU Library General Public License
-    along with this library; see the file COPYING.LIB.  If not, write to the
-    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-    02110-1301, USA.
-*/
-
-
-#include "kbreadcrumbselectionmodel.h"
-#include "kbreadcrumbselectionmodel_p.h"
-
-#include <QDebug>
-
-KBreadcrumbSelectionModel::KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, QObject* parent)
-  : QItemSelectionModel(const_cast<QAbstractItemModel *>(selectionModel->model()), parent),
-    d_ptr(new KBreadcrumbSelectionModelPrivate(this, selectionModel, MakeBreadcrumbSelectionInSelf))
-{
-  d_ptr->init();
-}
-
-KBreadcrumbSelectionModel::KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, BreadcrumbTarget direction, QObject* parent)
-  : QItemSelectionModel(const_cast<QAbstractItemModel *>(selectionModel->model()), parent),
-    d_ptr(new KBreadcrumbSelectionModelPrivate(this, selectionModel, direction))
-{
-  if ( direction != MakeBreadcrumbSelectionInSelf)
-    connect(selectionModel, SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),
-            this, SLOT(sourceSelectionChanged(const QItemSelection&,const QItemSelection&)));
-
-  d_ptr->init();
-}
-
-KBreadcrumbSelectionModel::~KBreadcrumbSelectionModel()
-{
-  delete d_ptr;
-}
-
-bool KBreadcrumbSelectionModel::isActualSelectionIncluded() const
-{
-  Q_D(const KBreadcrumbSelectionModel);
-  return d->m_includeActualSelection;
-}
-
-void KBreadcrumbSelectionModel::setActualSelectionIncluded(bool includeActualSelection)
-{
-  Q_D(KBreadcrumbSelectionModel);
-  d->m_includeActualSelection = includeActualSelection;
-}
-
-int KBreadcrumbSelectionModel::breadcrumbLength() const
-{
-  Q_D(const KBreadcrumbSelectionModel);
-  return d->m_selectionDepth;
-}
-
-void KBreadcrumbSelectionModel::setBreadcrumbLength(int breadcrumbLength)
-{
-  Q_D(KBreadcrumbSelectionModel);
-  d->m_selectionDepth = breadcrumbLength;
-}
-
-QItemSelection KBreadcrumbSelectionModelPrivate::getBreadcrumbSelection(const QModelIndex& index)
-{
-  QItemSelection breadcrumbSelection;
-
-  if (m_includeActualSelection)
-    breadcrumbSelection.append(QItemSelectionRange(index));
-
-  QModelIndex parent = index.parent();
-  int sumBreadcrumbs = 0;
-  bool includeAll = m_selectionDepth < 0;
-  while (parent.isValid() && (includeAll || sumBreadcrumbs < m_selectionDepth)) {
-    breadcrumbSelection.append(QItemSelectionRange(parent));
-    parent = parent.parent();
-  }
-  return breadcrumbSelection;
-}
-
-QItemSelection KBreadcrumbSelectionModelPrivate::getBreadcrumbSelection(const QItemSelection& selection)
-{
-  QItemSelection breadcrumbSelection;
-
-  if (m_includeActualSelection)
-    breadcrumbSelection = selection;
-
-  QItemSelection::const_iterator it = selection.constBegin();
-  const QItemSelection::const_iterator end = selection.constEnd();
-
-  for ( ; it != end; ++it)
-  {
-    QModelIndex parent = it->parent();
-
-    if (breadcrumbSelection.contains(parent))
-      continue;
-
-    int sumBreadcrumbs = 0;
-    bool includeAll = m_selectionDepth < 0;
-
-    while (parent.isValid() && (includeAll || sumBreadcrumbs < m_selectionDepth))
-    {
-      breadcrumbSelection.append(QItemSelectionRange(parent));
-      parent = parent.parent();
-
-      if (breadcrumbSelection.contains(parent))
-        break;
-
-      ++sumBreadcrumbs;
-    }
-  }
-  return breadcrumbSelection;
-}
-
-void KBreadcrumbSelectionModelPrivate::sourceSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
-{
-  Q_Q(KBreadcrumbSelectionModel);
-  QItemSelection deselectedCrumbs = getBreadcrumbSelection(deselected);
-  QItemSelection selectedCrumbs = getBreadcrumbSelection(selected);
-
-  QItemSelection removed = deselectedCrumbs;
-  foreach(const QItemSelectionRange &range, selectedCrumbs)
-  {
-    removed.removeAll(range);
-  }
-
-  QItemSelection added = selectedCrumbs;
-  foreach(const QItemSelectionRange &range, deselectedCrumbs)
-  {
-    added.removeAll(range);
-  }
-
-  if (!removed.isEmpty())
-  {
-    q->QItemSelectionModel::select(removed, QItemSelectionModel::Deselect);
-  }
-  if (!added.isEmpty())
-  {
-    q->QItemSelectionModel::select(added, QItemSelectionModel::Select);
-  }
-}
-
-void KBreadcrumbSelectionModel::select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
-{
-  Q_D(KBreadcrumbSelectionModel);
-  // When an item is removed, the current index is set to the top index in the model.
-  // That causes a selectionChanged signal with a selection which we do not want.
-  if ( d->m_ignoreCurrentChanged )
-  {
-    d->m_ignoreCurrentChanged = false;
-    return;
-  }
-  if ( d->m_direction == MakeBreadcrumbSelectionInOther )
-  {
-    d->m_selectionModel->select(d->getBreadcrumbSelection(index), command);
-    QItemSelectionModel::select(index, command);
-  } else {
-    d->m_selectionModel->select(index, command);
-    QItemSelectionModel::select(d->getBreadcrumbSelection(index), command);
-  }
-}
-
-void KBreadcrumbSelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command)
-{
-  Q_D(KBreadcrumbSelectionModel);
-  QItemSelection bcc = d->getBreadcrumbSelection(selection);
-  if ( d->m_direction == MakeBreadcrumbSelectionInOther )
-  {
-    d->m_selectionModel->select(selection, command);
-    QItemSelectionModel::select(bcc, command);
-  } else {
-    d->m_selectionModel->select(bcc, command);
-    QItemSelectionModel::select(selection, command);
-  }
-}
-
-void KBreadcrumbSelectionModelPrivate::init()
-{
-  Q_Q(KBreadcrumbSelectionModel);
-  q->connect(m_selectionModel->model(), SIGNAL(layoutChanged()), SLOT(syncBreadcrumbs()));
-  q->connect(m_selectionModel->model(), SIGNAL(modelReset()), SLOT(syncBreadcrumbs()));
-  q->connect(m_selectionModel->model(), SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)), SLOT(syncBreadcrumbs()));
-  // Don't need to handle insert & remove because they can't change the breadcrumbs on their own.
-}
-
-void KBreadcrumbSelectionModelPrivate::syncBreadcrumbs()
-{
-  Q_Q(KBreadcrumbSelectionModel);
-  q->select(m_selectionModel->selection(), QItemSelectionModel::ClearAndSelect);
-}
-
diff --git a/src/libtomahawk/widgets/kbreadcrumbselectionmodel.h b/src/libtomahawk/widgets/kbreadcrumbselectionmodel.h
deleted file mode 100644
index 751be28ef..000000000
--- a/src/libtomahawk/widgets/kbreadcrumbselectionmodel.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
-    Copyright (C) 2010 Klarälvdalens Datakonsult AB,
-        a KDAB Group company, info@kdab.net,
-        author Stephen Kelly <stephen@kdab.com>
-
-    This library is free software; you can redistribute it and/or modify it
-    under the terms of the GNU Library General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or (at your
-    option) any later version.
-
-    This library 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 Library General Public
-    License for more details.
-
-    You should have received a copy of the GNU Library General Public License
-    along with this library; see the file COPYING.LIB.  If not, write to the
-    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-    02110-1301, USA.
-*/
-
-#ifndef KBREADCRUMBSPROXYMODEL_H
-#define KBREADCRUMBSPROXYMODEL_H
-
-#include <QtGui/QItemSelectionModel>
-#include <QtCore/QAbstractItemModel>
-#include <QObject>
-
-
-class KBreadcrumbSelectionModelPrivate;
-
-/**
-  @class KBreadcrumbSelectionModel kbreadcrumbselectionmodel.h
-
-  @brief Selects the parents of selected items to create breadcrumbs
-
-  For example, if the tree is
-  @verbatim
-    - A
-    - B
-    - - C
-    - - D
-    - - - E
-    - - - - F
-  @endverbatim
-
-  and E is selected, the selection can contain
-
-  @verbatim
-    - B
-    - D
-  @endverbatim
-
-  or
-
-  @verbatim
-    - B
-    - D
-    - E
-  @endverbatim
-
-  if isActualSelectionIncluded is true.
-
-  The depth of the selection may also be set. For example if the breadcrumbLength is 1:
-
-  @verbatim
-    - D
-    - E
-  @endverbatim
-
-  And if breadcrumbLength is 2:
-
-  @verbatim
-    - B
-    - D
-    - E
-  @endverbatim
-
-  A KBreadcrumbsProxyModel with a breadcrumbLength of 0 and including the actual selection is
-  the same as a KSelectionProxyModel in the KSelectionProxyModel::ExactSelection configuration.
-
-  @code
-    view1->setModel(rootModel);
-
-    QItemSelectionModel *breadcrumbSelectionModel = new QItemSelectionModel(rootModel, this);
-
-    KBreadcrumbSelectionModel *breadcrumbProxySelector = new KBreadcrumbSelectionModel(breadcrumbSelectionModel, rootModel, this);
-
-    view1->setSelectionModel(breadcrumbProxySelector);
-
-    KSelectionProxyModel *breadcrumbSelectionProxyModel = new KSelectionProxyModel( breadcrumbSelectionModel, this);
-    breadcrumbSelectionProxyModel->setSourceModel( rootModel );
-    breadcrumbSelectionProxyModel->setFilterBehavior( KSelectionProxyModel::ExactSelection );
-
-    view2->setModel(breadcrumbSelectionProxyModel);
-  @endcode
-
-  @image html kbreadcrumbselectionmodel.png "KBreadcrumbSelectionModel in several configurations"
-
-  This can work in two directions. One option is for a single selection in the KBreadcrumbSelectionModel to invoke
-  the breadcrumb selection in its constructor argument.
-
-  The other is for a selection in the itemselectionmodel in the constructor argument to cause a breadcrumb selection
-  in @p this.
-
-  @since 4.5
-
-*/
-class KBreadcrumbSelectionModel : public QItemSelectionModel
-{
-  Q_OBJECT
-public:
-  enum BreadcrumbTarget
-  {
-    MakeBreadcrumbSelectionInOther,
-    MakeBreadcrumbSelectionInSelf
-  };
-
-  explicit KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, QObject* parent = 0);
-  KBreadcrumbSelectionModel(QItemSelectionModel *selectionModel, BreadcrumbTarget target, QObject* parent = 0);
-  virtual ~KBreadcrumbSelectionModel();
-
-  /**
-    Returns whether the actual selection in included in the proxy.
-
-    The default is true.
-  */
-  bool isActualSelectionIncluded() const;
-
-  /**
-    Set whether the actual selection in included in the proxy to @p isActualSelectionIncluded.
-  */
-  void setActualSelectionIncluded(bool isActualSelectionIncluded);
-
-  /**
-    Returns the depth that the breadcrumb selection should go to.
-  */
-  int breadcrumbLength() const;
-
-  /**
-    Sets the depth that the breadcrumb selection should go to.
-
-    If the @p breadcrumbLength is -1, all breadcrumbs are selected.
-    The default is -1
-  */
-  void setBreadcrumbLength(int breadcrumbLength);
-
-  /* reimp */ void select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command);
-
-  /* reimp */ void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command);
-
-protected:
-  KBreadcrumbSelectionModelPrivate * const d_ptr;
-private:
-  //@cond PRIVATE
-  Q_DECLARE_PRIVATE(KBreadcrumbSelectionModel)
-  Q_PRIVATE_SLOT( d_func(),void sourceSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected))
-  Q_PRIVATE_SLOT( d_func(),void syncBreadcrumbs())
-  //@cond PRIVATE
-};
-
-
-#include "kbreadcrumbselectionmodel_p.h" //HACK ALERT - Why doesn't it compile without this?!
-#endif
diff --git a/src/libtomahawk/widgets/kbreadcrumbselectionmodel_p.h b/src/libtomahawk/widgets/kbreadcrumbselectionmodel_p.h
deleted file mode 100644
index 93aac514c..000000000
--- a/src/libtomahawk/widgets/kbreadcrumbselectionmodel_p.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
-    Copyright (C) 2010 Klarälvdalens Datakonsult AB,
-        a KDAB Group company, info@kdab.net,
-        author Stephen Kelly <stephen@kdab.com>
-
-    This library is free software; you can redistribute it and/or modify it
-    under the terms of the GNU Library General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or (at your
-    option) any later version.
-
-    This library 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 Library General Public
-    License for more details.
-
-    You should have received a copy of the GNU Library General Public License
-    along with this library; see the file COPYING.LIB.  If not, write to the
-    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-    02110-1301, USA.
-*/
-
-#ifndef KBREADCRUMBSPROXYMODEL_P_H
-#define KBREADCRUMBSPROXYMODEL_P_H
-
-#include "kbreadcrumbselectionmodel.h"
-
-#include <QtGui/QItemSelectionModel>
-#include <QtCore/QAbstractItemModel>
-#include <QObject>
-
-class KBreadcrumbSelectionModelPrivate
-{
-  Q_DECLARE_PUBLIC(KBreadcrumbSelectionModel)
-  KBreadcrumbSelectionModel * const q_ptr;
-public:
-  KBreadcrumbSelectionModelPrivate(KBreadcrumbSelectionModel *breadcrumbSelector, QItemSelectionModel *selectionModel, KBreadcrumbSelectionModel::BreadcrumbTarget direction)
-    : q_ptr(breadcrumbSelector),
-      m_includeActualSelection(true),
-      m_selectionDepth(-1),
-      m_showHiddenAscendantData(false),
-      m_selectionModel(selectionModel),
-      m_direction(direction),
-      m_ignoreCurrentChanged(false)
-  {
-
-  }
-
-  /**
-    Returns a selection containing the breadcrumbs for @p index
-  */
-  QItemSelection getBreadcrumbSelection(const QModelIndex &index);
-
-  /**
-    Returns a selection containing the breadcrumbs for @p selection
-  */
-  QItemSelection getBreadcrumbSelection(const QItemSelection &selection);
-
-  void sourceSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
-
-  void init();
-  void syncBreadcrumbs();
-
-  bool m_includeActualSelection;
-  int m_selectionDepth;
-  bool m_showHiddenAscendantData;
-  QItemSelectionModel *m_selectionModel;
-  KBreadcrumbSelectionModel::BreadcrumbTarget m_direction;
-  bool m_ignoreCurrentChanged;
-};
-
-#endif
diff --git a/src/libtomahawk/widgets/querylabel.cpp b/src/libtomahawk/widgets/querylabel.cpp
index eee17a469..0218bfc6d 100644
--- a/src/libtomahawk/widgets/querylabel.cpp
+++ b/src/libtomahawk/widgets/querylabel.cpp
@@ -80,8 +80,10 @@ QueryLabel::init()
     setContentsMargins( 0, 0, 0, 0 );
     setMouseTracking( true );
 
-    align = Qt::AlignLeft;
-    mode = Qt::ElideMiddle;
+    m_useCustomPen = false;
+    m_useCustomFont = false;
+    m_align = Qt::AlignLeft;
+    m_mode = Qt::ElideMiddle;
 }
 
 
@@ -234,38 +236,63 @@ QueryLabel::setQuery( const Tomahawk::query_ptr& query )
 Qt::Alignment
 QueryLabel::alignment() const
 {
-    return align;
+    return m_align;
 }
 
 
 void
 QueryLabel::setAlignment( Qt::Alignment alignment )
 {
-    if ( this->align != alignment )
+    if ( m_align != alignment )
     {
-        this->align = alignment;
+        m_align = alignment;
         update(); // no geometry change, repaint is sufficient
     }
 }
 
+void
+QueryLabel::setTextPen( const QPen & pen )
+{
+    m_useCustomPen = true;
+    m_textPen = pen;
+}
+
+QPen
+QueryLabel::textPen() const
+{
+    return m_textPen;
+}
 
 Qt::TextElideMode
 QueryLabel::elideMode() const
 {
-    return mode;
+    return m_mode;
 }
 
 
 void
 QueryLabel::setElideMode( Qt::TextElideMode mode )
 {
-    if ( this->mode != mode )
+    if ( m_mode != mode )
     {
-        this->mode = mode;
+        m_mode = mode;
         updateLabel();
     }
 }
 
+QFont
+QueryLabel::font() const
+{
+    return m_font;
+}
+
+void
+QueryLabel::setFont( const QFont& font )
+{
+    m_useCustomFont = true;
+    m_font = font;
+}
+
 
 void
 QueryLabel::updateLabel()
@@ -277,6 +304,17 @@ QueryLabel::updateLabel()
     update();
 }
 
+void
+QueryLabel::setExtraContentsMargins( int left, int top, int right, int bottom )
+{
+    QMargins margins = contentsMargins();
+    margins.setLeft( margins.left() + left );
+    margins.setTop( margins.top() + top );
+    margins.setRight( margins.right() + right );
+    margins.setBottom( margins.bottom() + bottom );
+    setContentsMargins( margins );
+}
+
 
 QSize
 QueryLabel::sizeHint() const
@@ -290,7 +328,7 @@ QueryLabel::sizeHint() const
 QSize
 QueryLabel::minimumSizeHint() const
 {
-    switch ( mode )
+    switch ( m_mode )
     {
         case Qt::ElideNone:
             return sizeHint();
@@ -312,23 +350,28 @@ QueryLabel::paintEvent( QPaintEvent* event )
     QPainter p( this );
     QRect r = contentsRect();
     QString s = text();
-    const QString elidedText = fontMetrics().elidedText( s, mode, r.width() );
+    const QString elidedText = fontMetrics().elidedText( s, m_mode, r.width() );
 
     p.save();
     p.setRenderHint( QPainter::Antialiasing );
 
+    QFontMetrics fm = fontMetrics();
+    if ( m_useCustomFont )
+    {
+        p.setFont( m_font );
+        fm = QFontMetrics( m_font );
+    }
+
     if ( m_hoverArea.width() )
     {
         if ( elidedText != s )
         {
             m_hoverArea.setLeft( 0 );
-            m_hoverArea.setRight( fontMetrics().width( elidedText ) + contentsMargins().left() * 2 );
+            m_hoverArea.setRight( fm.width( elidedText ) + contentsMargins().left() * 2 );
             m_hoverType = Track;
         }
 
-        p.setPen( palette().mid().color() );
-        p.setBrush( palette().highlight() );
-        p.drawRoundedRect( m_hoverArea, 4.0, 4.0 );
+        TomahawkUtils::drawQueryBackground( &p, palette(), m_hoverArea );
     }
 
     if ( elidedText != s || ( m_result.isNull() && m_query.isNull() ) )
@@ -343,20 +386,23 @@ QueryLabel::paintEvent( QPaintEvent* event )
             p.setBrush( palette().window() );
             p.setPen( palette().color( foregroundRole() ) );
         }
-        p.drawText( r, align, elidedText );
+        p.drawText( r, m_align, elidedText );
     }
     else
     {
-        const QFontMetrics& fm = fontMetrics();
         int dashX = fm.width( DASH );
         int artistX = m_type & Artist ? fm.width( artist() ) : 0;
         int albumX = m_type & Album ? fm.width( album() ) : 0;
         int trackX = m_type & Track ? fm.width( track() ) : 0;
 
+        if ( m_useCustomPen )
+            p.setPen( m_textPen );
+
         if ( m_type & Artist )
         {
             p.setBrush( palette().window() );
-            p.setPen( palette().color( foregroundRole() ) );
+            if ( !m_useCustomPen )
+                p.setPen( palette().color( foregroundRole() ) );
 
             if ( m_hoverType == Artist )
             {
@@ -364,17 +410,18 @@ QueryLabel::paintEvent( QPaintEvent* event )
                 p.setBrush( palette().highlight() );
             }
 
-            p.drawText( r, align, artist() );
+            p.drawText( r, m_align, artist() );
             r.adjust( artistX, 0, 0, 0 );
         }
         if ( m_type & Album )
         {
             p.setBrush( palette().window() );
-            p.setPen( palette().color( foregroundRole() ) );
+            if ( !m_useCustomPen )
+                p.setPen( palette().color( foregroundRole() ) );
 
             if ( m_type & Artist )
             {
-                p.drawText( r, align, DASH );
+                p.drawText( r, m_align, DASH );
                 r.adjust( dashX, 0, 0, 0 );
             }
             if ( m_hoverType == Album )
@@ -383,17 +430,18 @@ QueryLabel::paintEvent( QPaintEvent* event )
                 p.setBrush( palette().highlight() );
             }
 
-            p.drawText( r, align, album() );
+            p.drawText( r, m_align, album() );
             r.adjust( albumX, 0, 0, 0 );
         }
         if ( m_type & Track )
         {
             p.setBrush( palette().window() );
-            p.setPen( palette().color( foregroundRole() ) );
+            if ( !m_useCustomPen )
+                p.setPen( palette().color( foregroundRole() ) );
 
             if ( m_type & Artist || m_type & Album )
             {
-                p.drawText( r, align, DASH );
+                p.drawText( r, m_align, DASH );
                 r.adjust( dashX, 0, 0, 0 );
             }
             if ( m_hoverType == Track )
@@ -402,7 +450,7 @@ QueryLabel::paintEvent( QPaintEvent* event )
                 p.setBrush( palette().highlight() );
             }
 
-            p.drawText( r, align, track() );
+            p.drawText( r, m_align, track() );
             r.adjust( trackX, 0, 0, 0 );
         }
     }
@@ -432,7 +480,8 @@ void
 QueryLabel::mousePressEvent( QMouseEvent* event )
 {
     QFrame::mousePressEvent( event );
-    time.start();
+    m_time.restart();
+    m_dragPos = event->pos();
 }
 
 
@@ -442,7 +491,8 @@ QueryLabel::mouseReleaseEvent( QMouseEvent* event )
     QFrame::mouseReleaseEvent( event );
 
     m_dragPos = QPoint();
-    if ( time.elapsed() < qApp->doubleClickInterval() )
+    qDebug() << "ELAPSED TIME" << m_time.elapsed() << "limit:" << qApp->doubleClickInterval();
+    if ( m_time.elapsed() < qApp->doubleClickInterval() )
     {
         switch( m_hoverType )
         {
@@ -484,7 +534,10 @@ QueryLabel::mouseMoveEvent( QMouseEvent* event )
         return;
     }
 
-    const QFontMetrics& fm = fontMetrics();
+    QFontMetrics fm = fontMetrics();
+    if ( m_useCustomFont )
+        fm = QFontMetrics( m_font );
+
     int dashX = fm.width( DASH );
     int artistX = m_type & Artist ? fm.width( artist() ) : 0;
     int albumX = m_type & Album ? fm.width( album() ) : 0;
diff --git a/src/libtomahawk/widgets/querylabel.h b/src/libtomahawk/widgets/querylabel.h
index 1fed3d9c2..ef3b758f0 100644
--- a/src/libtomahawk/widgets/querylabel.h
+++ b/src/libtomahawk/widgets/querylabel.h
@@ -1,5 +1,5 @@
 /* === 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
@@ -21,6 +21,7 @@
 
 #include <QFrame>
 #include <QTime>
+#include <QPen>
 
 #include "result.h"
 #include "query.h"
@@ -67,6 +68,14 @@ public:
     Qt::TextElideMode elideMode() const;
     void setElideMode( Qt::TextElideMode mode );
 
+    void setTextPen( const QPen& );
+    QPen textPen() const;
+
+    void setFont( const QFont& );
+    QFont font() const;
+
+    void setExtraContentsMargins( int left, int top, int right, int bottom );
+
     virtual QSize sizeHint() const;
     virtual QSize minimumSizeHint() const;
 
@@ -98,18 +107,22 @@ protected:
     virtual void paintEvent( QPaintEvent* event );
 
     virtual void startDrag();
-    
+
 private:
     QString smartAppend( QString& text, const QString& appendage ) const;
-    QTime time;
+    QTime m_time;
 
     DisplayType m_type;
     QString m_text;
     Tomahawk::result_ptr m_result;
     Tomahawk::query_ptr m_query;
 
-    Qt::Alignment align;
-    Qt::TextElideMode mode;
+    Qt::Alignment m_align;
+    Qt::TextElideMode m_mode;
+
+    bool m_useCustomPen, m_useCustomFont;
+    QPen m_textPen;
+    QFont m_font;
 
     DisplayType m_hoverType;
     QRect m_hoverArea;
diff --git a/src/libtomahawk/widgets/siblingcrumbbutton.h b/src/libtomahawk/widgets/siblingcrumbbutton.h
deleted file mode 100644
index 7525f1c21..000000000
--- a/src/libtomahawk/widgets/siblingcrumbbutton.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
- *
- *   Copyright 2011, Casey Link <unnamedrambler@gmail.com>
- *
- *   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 SIBLINGCRUMBBUTTON_H
-#define SIBLINGCRUMBBUTTON_H
-
-#include "breadcrumbbuttonbase.h"
-#include "breadcrumbbar.h"
-
-#include "combobox.h"
-
-#include <QList>
-#include <QModelIndex>
-#include <QPointer>
-#include <QSize>
-#include <QMenu>
-
-/**
- * \brief A factory for sibling crumb buttons
- */
-class SiblingCrumbButtonFactory : public BreadcrumbButtonFactory
-{
-
-    public:
-        SiblingCrumbButtonFactory(){}
-
-        virtual ~SiblingCrumbButtonFactory(){}
-        virtual BreadcrumbButtonBase* newButton(QModelIndex index, BreadcrumbBar *parent);
-};
-
-/**
- * \brief A crumb button implementation where the dropdowns show sibling items
- * Unlike most crumb buttons, this one shows a list of sibling items (as
- * opposed to child items). This is desireable in certain circumstances.
- */
-class SiblingCrumbButton : public BreadcrumbButtonBase
-{
-    Q_OBJECT
-
-public:
-    SiblingCrumbButton(QModelIndex index, BreadcrumbBar *parent);
-
-    void setIndex(QModelIndex index);
-    QModelIndex index() const;
-    void setActive(bool active);
-    bool isActive() const;
-    virtual QSize sizeHint() const;
-
-protected:
-    virtual void paintEvent(QPaintEvent *event);
-
-private slots:
-    void fillCombo();
-    void comboboxActivated(int i);
-    void activateSelf();
-
-private:
-    bool hasChildren() const;
-
-    QModelIndex m_index; /*!< our current index */
-    ComboBox *m_combo; /*!< our combobox! */
-
-};
-
-#endif // SIBLINGCRUMBBUTTON_H
diff --git a/src/libtomahawk/widgets/whatshotwidget.cpp b/src/libtomahawk/widgets/whatshotwidget.cpp
index 89a113fa1..a0ac24639 100644
--- a/src/libtomahawk/widgets/whatshotwidget.cpp
+++ b/src/libtomahawk/widgets/whatshotwidget.cpp
@@ -34,8 +34,6 @@
 #include "playlist/playlistmodel.h"
 #include "playlist/treeproxymodel.h"
 #include "widgets/overlaywidget.h"
-#include "widgets/siblingcrumbbutton.h"
-#include "widgets/kbreadcrumbselectionmodel.h"
 #include "utils/tomahawkutils.h"
 #include "utils/logger.h"
 #include <pipeline.h>
@@ -52,6 +50,7 @@ static QString s_whatsHotIdentifier = QString( "WhatsHotWidget" );
 WhatsHotWidget::WhatsHotWidget( QWidget* parent )
     : QWidget( parent )
     , ui( new Ui::WhatsHotWidget )
+    , m_sortedProxy( 0 )
 {
     ui->setupUi( this );
 
@@ -65,17 +64,14 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent )
     TomahawkUtils::unmarginLayout( ui->breadCrumbLeft->layout() );
     TomahawkUtils::unmarginLayout( ui->verticalLayout->layout() );
 
-    //set crumb widgets
-    SiblingCrumbButtonFactory * crumbFactory = new SiblingCrumbButtonFactory;
-
     m_crumbModelLeft = new QStandardItemModel( this );
+    m_sortedProxy = new QSortFilterProxyModel( this );
+    m_sortedProxy->setDynamicSortFilter( true );
+    m_sortedProxy->setFilterCaseSensitivity( Qt::CaseInsensitive );
 
-    ui->breadCrumbLeft->setButtonFactory( crumbFactory );
-    ui->breadCrumbLeft->setModel( m_crumbModelLeft );
-    ui->breadCrumbLeft->setRootIcon(QIcon( RESPATH "images/charts.png" ));
-    ui->breadCrumbLeft->setUseAnimation( true );
+    ui->breadCrumbLeft->setRootIcon( QPixmap( RESPATH "images/charts.png" ) );
 
-    connect( ui->breadCrumbLeft, SIGNAL( currentIndexChanged( QModelIndex ) ), SLOT( leftCrumbIndexChanged(QModelIndex) ) );
+    connect( ui->breadCrumbLeft, SIGNAL( activateIndex( QModelIndex ) ), SLOT( leftCrumbIndexChanged(QModelIndex) ) );
 
     ui->tracksViewLeft->setFrameShape( QFrame::NoFrame );
     ui->tracksViewLeft->setAttribute( Qt::WA_MacShowFocusRect, 0 );
@@ -100,7 +96,6 @@ WhatsHotWidget::WhatsHotWidget( QWidget* parent )
 
     connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) );
 
-    connect( AudioEngine::instance(), SIGNAL( playlistChanged( Tomahawk::PlaylistInterface* ) ), this, SLOT( playlistChanged( Tomahawk::PlaylistInterface* ) ) );
     QTimer::singleShot( 0, this, SLOT( fetchData() ) );
 }
 
@@ -145,9 +140,10 @@ WhatsHotWidget::fetchData()
     requestData.caller = s_whatsHotIdentifier;
     requestData.customData = QVariantMap();
     requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo );
-
     requestData.type = Tomahawk::InfoSystem::InfoChartCapabilities;
-    Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData,  20000, true );
+    requestData.timeoutMillis = 20000;
+    requestData.allSources = true;
+    Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
 
     tDebug( LOGVERBOSE ) << "WhatsHot: requested InfoChartCapabilities";
 }
@@ -171,7 +167,12 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat
             QStandardItem *rootItem= m_crumbModelLeft->invisibleRootItem();
             tDebug( LOGVERBOSE ) << "WhatsHot:: " << returnedData.keys();
 
-            foreach( const QString label, returnedData.keys() )
+            QVariantMap defaults;
+            if ( returnedData.contains( "defaults" ) )
+                defaults = returnedData.take( "defaults" ).toMap();
+            QString defaultSource = returnedData.take( "defaultSource" ).toString();
+
+            foreach ( const QString label, returnedData.keys() )
             {
                 tDebug( LOGVERBOSE ) << "WhatsHot:: parsing " << label;
                 QStandardItem *childItem = parseNode( rootItem, label, returnedData[label] );
@@ -179,10 +180,42 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat
                 rootItem->appendRow(childItem);
             }
 
-            KBreadcrumbSelectionModel *selectionModelLeft = new KBreadcrumbSelectionModel(new QItemSelectionModel(m_crumbModelLeft, this), this);
-            ui->breadCrumbLeft->setSelectionModel(selectionModelLeft);
+            // Set the default source
+            // Set the default chart for each source
+            for ( int i = 0; i < rootItem->rowCount(); i++ )
+            {
+                QStandardItem* source = rootItem->child( i, 0 );
+                if ( defaultSource.toLower() == source->text().toLower() )
+                {
+                    qDebug() << "Setting DEFAULT SOURCE:" << source->text();
+                    source->setData( true, Breadcrumb::DefaultRole );
+                }
 
-            //ui->breadCrumbRight->setSelectionModel(selectionModelLeft);
+                if ( defaults.contains( source->text().toLower() ) )
+                {
+                    QStringList defaultIndices = defaults[ source->text().toLower() ].toStringList();
+                    QStandardItem* cur = source;
+
+                    foreach( const QString& index, defaultIndices )
+                    {
+                        // Go through the children of the current item, marking the default one as default
+                        for ( int k = 0; k < cur->rowCount(); k++ )
+                        {
+                            if ( cur->child( k, 0 )->text() == index )
+                            {
+                                tDebug() << "Found DEFAULT ITEM:" << index;
+                                cur = cur->child( k, 0 ); // this is the default, drill down into the default to pick the next default
+                                cur->setData( true, Breadcrumb::DefaultRole );
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+
+            m_sortedProxy->setSourceModel( m_crumbModelLeft );
+            m_sortedProxy->sort( 0, Qt::AscendingOrder );
+            ui->breadCrumbLeft->setModel( m_sortedProxy );
             break;
         }
         case InfoSystem::InfoChart:
@@ -212,7 +245,8 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat
 
                 m_artistModels[ chartId ] = artistsModel;
 
-                setLeftViewArtists( artistsModel );
+                if ( m_queueItemToShow == chartId )
+                    setLeftViewArtists( artistsModel );
             }
             else if( type == "albums" )
             {
@@ -233,7 +267,9 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat
                 albumModel->addAlbums( al );
 
                 m_albumModels[ chartId ] = albumModel;
-                setLeftViewAlbums( albumModel );
+
+                if ( m_queueItemToShow == chartId )
+                    setLeftViewAlbums( albumModel );
             }
             else if( type == "tracks" )
             {
@@ -252,7 +288,9 @@ WhatsHotWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestDat
                 trackModel->append( tracklist );
 
                 m_trackModels[ chartId ] = trackModel;
-                setLeftViewTracks( trackModel );
+
+                if ( m_queueItemToShow == chartId )
+                    setLeftViewTracks( trackModel );
             }
             else
             {
@@ -278,10 +316,10 @@ void
 WhatsHotWidget::leftCrumbIndexChanged( QModelIndex index )
 {
     tDebug( LOGVERBOSE ) << "WhatsHot:: left crumb changed" << index.data();
-    QStandardItem* item = m_crumbModelLeft->itemFromIndex( index );
+    QStandardItem* item = m_crumbModelLeft->itemFromIndex( m_sortedProxy->mapToSource( index ) );
     if( !item )
         return;
-    if( !item->data().isValid() )
+    if( !item->data( Breadcrumb::ChartIdRole ).isValid() )
         return;
 
 
@@ -293,7 +331,7 @@ WhatsHotWidget::leftCrumbIndexChanged( QModelIndex index )
     }
 
 
-    const QString chartId = item->data().toString();
+    const QString chartId = item->data( Breadcrumb::ChartIdRole ).toString();
 
     if ( m_artistModels.contains( chartId ) )
     {
@@ -327,13 +365,15 @@ WhatsHotWidget::leftCrumbIndexChanged( QModelIndex index )
     requestData.caller = s_whatsHotIdentifier;
     requestData.customData = customData;
     requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( criteria );
-
     requestData.type = Tomahawk::InfoSystem::InfoChart;
+    requestData.timeoutMillis = 20000;
+    requestData.allSources = true;
 
     qDebug() << "Making infosystem request for chart of type:" <<chartId;
-    Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData,  20000, true );
+    Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData );
 
     m_queuedFetches.insert( chartId );
+    m_queueItemToShow = chartId;
 }
 
 
@@ -367,7 +407,11 @@ WhatsHotWidget::parseNode( QStandardItem* parentItem, const QString &label, cons
         foreach ( Tomahawk::InfoSystem::InfoStringHash chart, charts )
         {
             QStandardItem *childItem= new QStandardItem( chart[ "label" ] );
-            childItem->setData( chart[ "id" ] );
+            childItem->setData( chart[ "id" ], Breadcrumb::ChartIdRole );
+            if ( chart.value( "default", "" ) == "true")
+            {
+                childItem->setData( true, Breadcrumb::DefaultRole );
+            }
             sourceItem->appendRow( childItem );
         }
     }
@@ -386,7 +430,6 @@ WhatsHotWidget::parseNode( QStandardItem* parentItem, const QString &label, cons
 
         foreach ( const QVariant value, dataList )
         {
-            qDebug() << "CREATED:" << value.toString();
             QStandardItem *childItem= new QStandardItem(value.toString());
             sourceItem->appendRow(childItem);
         }
diff --git a/src/libtomahawk/widgets/whatshotwidget.h b/src/libtomahawk/widgets/whatshotwidget.h
index e13c0421e..ecd0be8ef 100644
--- a/src/libtomahawk/widgets/whatshotwidget.h
+++ b/src/libtomahawk/widgets/whatshotwidget.h
@@ -31,6 +31,7 @@
 
 #include "dllmacro.h"
 
+class QSortFilterProxyModel;
 class QStandardItemModel;
 class QStandardItem;
 class TreeModel;
@@ -91,11 +92,13 @@ private:
     Ui::WhatsHotWidget *ui;
 
     QStandardItemModel* m_crumbModelLeft;
+    QSortFilterProxyModel* m_sortedProxy;
 
     // Cache our model data
     QHash< QString, AlbumModel* > m_albumModels;
     QHash< QString, TreeModel* > m_artistModels;
     QHash< QString, PlaylistModel* > m_trackModels;
+    QString m_queueItemToShow;
     QSet< QString > m_queuedFetches;
     QTimer* m_timer;
 };
diff --git a/src/libtomahawk/widgets/whatshotwidget.ui b/src/libtomahawk/widgets/whatshotwidget.ui
index 4caa5e32b..7c635acf8 100644
--- a/src/libtomahawk/widgets/whatshotwidget.ui
+++ b/src/libtomahawk/widgets/whatshotwidget.ui
@@ -12,7 +12,7 @@
   </property>
   <layout class="QVBoxLayout" name="verticalLayout_2">
    <item>
-    <widget class="HeaderBreadCrumb" name="breadCrumbLeft" native="true"/>
+    <widget class="Tomahawk::Breadcrumb" name="breadCrumbLeft" native="true"/>
    </item>
    <item>
     <widget class="QStackedWidget" name="stackLeft">
@@ -79,9 +79,10 @@
    <header>playlist/playlistview.h</header>
   </customwidget>
   <customwidget>
-   <class>HeaderBreadCrumb</class>
+   <class>Tomahawk::Breadcrumb</class>
    <extends>QWidget</extends>
-   <header location="global">widgets/headerbreadcrumb.h</header>
+   <header>widgets/Breadcrumb.h</header>
+   <container>1</container>
   </customwidget>
  </customwidgets>
  <resources/>
diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp
index ef56af80f..6914f938e 100644
--- a/src/settingsdialog.cpp
+++ b/src/settingsdialog.cpp
@@ -233,6 +233,7 @@ SettingsDialog::~SettingsDialog()
         m_resolversModel->saveScriptResolvers();
 
         s->applyChanges();
+        s->sync();
     }
     else
         qDebug() << "Settings dialog cancelled, NOT saving prefs.";
diff --git a/src/sourcetree/items/categoryitems.cpp b/src/sourcetree/items/categoryitems.cpp
index c1237cd97..896ffdd21 100644
--- a/src/sourcetree/items/categoryitems.cpp
+++ b/src/sourcetree/items/categoryitems.cpp
@@ -256,7 +256,7 @@ CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction )
     if ( data->hasFormat( "application/tomahawk.dragsource.type" ) )
         dj->setProperty( "dragsource", QString::fromUtf8( data->data( "application/tomahawk.dragsource.type" ) ) );
 
-    connect( dj, SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
+    connect( dj, SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ), Qt::QueuedConnection );
     if ( dropType() == DropTypeAllFromArtist )
         dj->setGetWholeArtists( true );
     if ( dropType() == DropTypeThisAlbum )
diff --git a/src/sourcetree/sourcetreeview.cpp b/src/sourcetree/sourcetreeview.cpp
index 59e9a4d01..39771bdc7 100644
--- a/src/sourcetree/sourcetreeview.cpp
+++ b/src/sourcetree/sourcetreeview.cpp
@@ -115,8 +115,11 @@ SourceTreeView::SourceTreeView( QWidget* parent )
     connect( this, SIGNAL( catchUpRequest() ), m_latchManager, SLOT( catchUpRequest() ) );
 }
 
+
 SourceTreeView::~SourceTreeView()
-{}
+{
+}
+
 
 void
 SourceTreeView::setupMenus()
@@ -151,7 +154,7 @@ SourceTreeView::setupMenus()
                 m_latchOnAction->setText( tr( "&Catch Up" ) );
                 m_latchMenu.addSeparator();
                 m_latchOffAction = m_latchMenu.addAction( tr( "&Stop Listening Along" ) );
-                connect( m_latchOffAction,       SIGNAL( triggered() ), SLOT( latchOff() ) );
+                connect( m_latchOffAction, SIGNAL( triggered() ), SLOT( latchOff() ) );
             }
         }
     }
@@ -231,6 +234,7 @@ SourceTreeView::selectRequest( const QPersistentModelIndex& idx )
     }
 }
 
+
 void
 SourceTreeView::expandRequest( const QPersistentModelIndex &idx )
 {
@@ -245,6 +249,7 @@ SourceTreeView::loadPlaylist()
     onItemActivated( m_contextMenuIndex );
 }
 
+
 void
 SourceTreeView::deletePlaylist( const QModelIndex& idxIn )
 {
@@ -353,6 +358,7 @@ SourceTreeView::latchOnOrCatchUp()
         emit latchRequest( source );
 }
 
+
 void
 SourceTreeView::latchOff()
 {
@@ -479,8 +485,9 @@ SourceTreeView::dragMoveEvent( QDragMoveEvent* event )
             m_dropRect = QRect();
         }
 
-        if ( accept )
+        if ( accept || DropJob::isDropType( DropJob::Playlist, event->mimeData() ) )
         {
+            // Playlists are accepted always since they can be dropped anywhere
             //tDebug() << Q_FUNC_INFO << "Accepting";
             event->setDropAction( Qt::CopyAction );
             event->accept();
@@ -533,6 +540,9 @@ SourceTreeView::dropEvent( QDropEvent* event )
             dropThis->setDropTypes( DropJob::Playlist );
             dropThis->setDropAction( DropJob::Create );
             dropThis->parseMimeData( event->mimeData() );
+
+            // Don't add it to the playlist under drop, it's a new playlist now
+            return;
         }
 
         QTreeView::dropEvent( event );
diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp
index be5c83fd6..69126417a 100644
--- a/src/tomahawkapp.cpp
+++ b/src/tomahawkapp.cpp
@@ -182,8 +182,6 @@ TomahawkApp::init()
 
     TomahawkUtils::setProxyFactory( proxyFactory );
 
-    Echonest::Config::instance()->setAPIKey( "JRIHWEP6GPOER2QQ6" );
-
     m_audioEngine = QWeakPointer<AudioEngine>( new AudioEngine );
     m_scanManager = QWeakPointer<ScanManager>( new ScanManager( this ) );
     new Pipeline( this );
@@ -194,6 +192,8 @@ TomahawkApp::init()
     tDebug() << "Init Database.";
     initDatabase();
 
+    Echonest::Config::instance()->setAPIKey( "JRIHWEP6GPOER2QQ6" );
+
     tDebug() << "Init Echonest Factory.";
     GeneratorFactory::registerFactory( "echonest", new EchonestFactory );
     tDebug() << "Init Database Factory.";
@@ -228,9 +228,7 @@ TomahawkApp::init()
     tDebug() << "Init InfoSystem.";
     m_infoSystem = QWeakPointer<Tomahawk::InfoSystem::InfoSystem>( new Tomahawk::InfoSystem::InfoSystem( this ) );
 
-    Echonest::Config::instance()->setAPIKey( "JRIHWEP6GPOER2QQ6" );
     Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() );
-
     EchonestGenerator::setupCatalogs();
 
 #ifndef TOMAHAWK_HEADLESS
@@ -310,10 +308,11 @@ TomahawkApp::~TomahawkApp()
 
     delete SipHandler::instance();
 
+    Pipeline::instance()->stop();
+
     if ( !m_database.isNull() )
         delete m_database.data();
 
-    Pipeline::instance()->stop();
     delete Pipeline::instance();
 
 #ifndef TOMAHAWK_HEADLESS
diff --git a/src/tomahawkwindow.cpp b/src/tomahawkwindow.cpp
index 9203e65ce..44931115d 100644
--- a/src/tomahawkwindow.cpp
+++ b/src/tomahawkwindow.cpp
@@ -180,6 +180,8 @@ TomahawkWindow::applyPlatformTweaks()
     setUnifiedTitleAndToolBarOnMac( true );
     delete ui->hline1;
     delete ui->hline2;
+    /// Mac users allready have Tomahawk appmenu, change the name
+    ui->menuApp->setTitle( "&Music Player" );
 #else
     ui->hline1->setStyleSheet( "border: 1px solid gray;" );
     ui->hline2->setStyleSheet( "border: 1px solid gray;" );
@@ -708,7 +710,7 @@ TomahawkWindow::showAboutTomahawk()
 {
     QMessageBox::about( this, tr( "About Tomahawk" ),
                         tr( "<h2><b>Tomahawk %1<br/>(%2)</h2>Copyright 2010, 2011<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>"
-                            "Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Michael Zanetti, Harald Sitter and Steve Robertson" )
+                            "Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson" )
                         .arg( TomahawkUtils::appFriendlyVersion() )
                         .arg( qApp->applicationVersion() ) );
 }
diff --git a/src/tomahawkwindow.ui b/src/tomahawkwindow.ui
index 57f53bbbc..97fdca390 100644
--- a/src/tomahawkwindow.ui
+++ b/src/tomahawkwindow.ui
@@ -67,7 +67,7 @@
      <x>0</x>
      <y>0</y>
      <width>1000</width>
-     <height>22</height>
+     <height>20</height>
     </rect>
    </property>
    <widget class="QMenu" name="menuSettings">
@@ -78,12 +78,16 @@
    </widget>
    <widget class="QMenu" name="menuApp">
     <property name="title">
-     <string>&amp;Music Player</string>
+     <string>&amp;Tomahawk</string>
     </property>
     <addaction name="actionPlay"/>
     <addaction name="actionPrevious"/>
     <addaction name="actionNext"/>
     <addaction name="separator"/>
+    <addaction name="actionCreatePlaylist"/>
+    <addaction name="actionCreate_New_Station"/>
+    <addaction name="actionLoadXSPF"/>
+    <addaction name="separator"/>
     <addaction name="actionUpdateCollection"/>
     <addaction name="actionRescanCollection"/>
     <addaction name="separator"/>
@@ -112,17 +116,7 @@
     <addaction name="actionDiagnostics"/>
     <addaction name="actionAboutTomahawk"/>
    </widget>
-   <widget class="QMenu" name="menuPlaylist">
-    <property name="title">
-     <string>&amp;Playlist</string>
-    </property>
-    <addaction name="actionCreatePlaylist"/>
-    <addaction name="actionCreate_New_Station"/>
-    <addaction name="separator"/>
-    <addaction name="actionLoadXSPF"/>
-   </widget>
    <addaction name="menuApp"/>
-   <addaction name="menuPlaylist"/>
    <addaction name="menuNetwork"/>
    <addaction name="menuSettings"/>
    <addaction name="menuWindow"/>