From e8c044a41c51590c6e4227e290bb3c9975bd901c Mon Sep 17 00:00:00 2001
From: Kilian Lackhove <Kilian.lackhove@gmail.com>
Date: Mon, 10 Sep 2012 02:01:27 +0200
Subject: [PATCH] MusicBrainzPlugin: Make more use of Musicbrainz search api
 features.   This offloads filtering and sorting of the results to musicbrainz
 and makes the artistID retrieval step obsolte and should speed things up.

---
 .../generic/musicbrainz/MusicBrainzPlugin.cpp | 266 ++++++++----------
 .../generic/musicbrainz/MusicBrainzPlugin.h   |   8 +-
 2 files changed, 118 insertions(+), 156 deletions(-)

diff --git a/src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.cpp b/src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.cpp
index 5ea6df815..6aa905379 100644
--- a/src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.cpp
+++ b/src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.cpp
@@ -62,7 +62,6 @@ MusicBrainzPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
     {
         case InfoArtistReleases:
         {
-
             Tomahawk::InfoSystem::InfoStringHash criteria;
             criteria["artist"] = hash["artist"];
 
@@ -72,7 +71,6 @@ MusicBrainzPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
 
         case InfoAlbumSongs:
         {
-
             Tomahawk::InfoSystem::InfoStringHash criteria;
             criteria["artist"] = hash["artist"];
             criteria["album"] = hash["album"];
@@ -94,31 +92,103 @@ MusicBrainzPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
 void
 MusicBrainzPlugin::notInCacheSlot( InfoStringHash criteria, InfoRequestData requestData )
 {
+    QString querySt;
     switch ( requestData.type )
     {
         case InfoArtistReleases:
         {
-            QString requestString( "http://musicbrainz.org/ws/2/artist" );
+            querySt.append( QString( "artist:\"%1\"" ).arg(criteria["artist"]) );
+            querySt.append( " AND (type:album OR type:ep)" );
+            querySt.append( " AND status:official" );
+            querySt.append( " AND NOT secondarytype:live" );
+            querySt.append( " AND NOT secondarytype:compilation" );
+            // we dont handle more than 100 results atm, but not even the beatles have more than 100 ep+albums, so its probably safe
+
+            QString requestString( "http://musicbrainz.org/ws/2/release-group" );
             QUrl url( requestString );
-            url.addQueryItem( "query", criteria["artist"] );
+            url.addQueryItem( "query", querySt );
+            url.addQueryItem( "limit", "100" );
+            tDebug() << Q_FUNC_INFO << url.toString();
             QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) );
             reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
 
-            connect( reply, SIGNAL( finished() ), SLOT( artistSearchSlot() ) );
+            connect( reply, SIGNAL( finished() ), SLOT( gotReleaseGroupsSlot() ) );
+
             break;
         }
-
         case InfoAlbumSongs:
         {
-            QString requestString( "http://musicbrainz.org/ws/2/artist" );
+            querySt.append( QString( "release:\"%1\"" ).arg(criteria["album"]) );
+            querySt.append( QString( " AND artist:\"%1\"" ).arg(criteria["artist"]) );
+            // not pre-filtering will yield more than 100 results which we dont handle atm. But since we only take the first result anyway that wont hurt
+
+            QString requestString( "http://musicbrainz.org/ws/2/release" );
             QUrl url( requestString );
-            url.addQueryItem( "query", criteria["artist"] );
+            url.addQueryItem( "query", querySt );
+            url.addQueryItem( "limit", "100" );
+            tDebug() << Q_FUNC_INFO << url.toString();
             QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) );
             reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) );
 
-            connect( reply, SIGNAL( finished() ), SLOT( albumSearchSlot() ) );
+            connect( reply, SIGNAL( finished() ), SLOT( gotReleasesSlot() ) );
+
             break;
         }
+        default:
+        {
+            Q_ASSERT( false );
+            break;
+        }
+    }
+
+
+}
+
+
+void
+MusicBrainzPlugin::gotReleaseGroupsSlot()
+{
+    QNetworkReply* oldReply = qobject_cast<QNetworkReply*>( sender() );
+    if ( !oldReply )
+        return; //timeout will handle it
+
+    QDomDocument doc;
+    doc.setContent( oldReply->readAll() );
+    QDomNodeList releaseGroupsNL = doc.elementsByTagName( "release-group" );
+    if ( releaseGroupsNL.isEmpty() )
+    {
+        emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+        tDebug() << Q_FUNC_INFO << doc.toString();
+        return;
+    }
+
+    Tomahawk::InfoSystem::InfoRequestData requestData = oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
+    InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
+    switch ( requestData.type )
+    {
+        case InfoArtistReleases:
+        {
+            QStringList albums;
+            for ( int i = 0; i < releaseGroupsNL.count(); i++ )
+            {
+                QString groupTitle = releaseGroupsNL.at(i).firstChildElement("title").text();
+                QString a = releaseGroupsNL.at(i).firstChildElement( "artist-credit" ).firstChildElement( "name-credit" ).firstChildElement( "artist" ).firstChildElement( "name" ).text();
+                if ( !albums.contains( groupTitle ) && hash["artist"] == a )
+                {
+                    albums << groupTitle;
+                    tDebug() << Q_FUNC_INFO << groupTitle;
+                }
+            }
+
+            QVariantMap returnedData;
+            returnedData["albums"] = albums;
+            emit info( requestData, returnedData );
+
+            Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>();
+            Tomahawk::InfoSystem::InfoStringHash criteria;
+            criteria["artist"] = origData["artist"];
+            emit updateCache( criteria, 0, requestData.type, returnedData );
+            }
 
         default:
         {
@@ -130,7 +200,7 @@ MusicBrainzPlugin::notInCacheSlot( InfoStringHash criteria, InfoRequestData requ
 
 
 void
-MusicBrainzPlugin::artistSearchSlot()
+MusicBrainzPlugin::gotReleasesSlot()
 {
     QNetworkReply* oldReply = qobject_cast<QNetworkReply*>( sender() );
     if ( !oldReply )
@@ -138,101 +208,44 @@ MusicBrainzPlugin::artistSearchSlot()
 
     QDomDocument doc;
     doc.setContent( oldReply->readAll() );
-    QDomNodeList domNodeList = doc.elementsByTagName( "artist" );
-    if ( domNodeList.isEmpty() )
+    QDomNodeList releasesNL = doc.elementsByTagName( "release" );
+    if ( releasesNL.isEmpty() )
     {
         emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
         tDebug() << Q_FUNC_INFO << doc.toString();
         return;
     }
 
-    QString artist_id = domNodeList.at( 0 ).toElement().attribute( "id" );
-    QString requestString( "http://musicbrainz.org/ws/2/release-group" );
-    QUrl url( requestString );
-    url.addQueryItem( "artist", artist_id );
-    url.addQueryItem( "type", "album|ep" );
-    url.addQueryItem( "limit", "100" );
-
-    QNetworkReply* newReply = TomahawkUtils::nam()->get( QNetworkRequest( url ) );
-    newReply->setProperty( "requestData", oldReply->property( "requestData" ) );
-    connect( newReply, SIGNAL( finished() ), SLOT( albumFoundSlot() ) );
-}
-
-
-void
-MusicBrainzPlugin::albumSearchSlot()
-{
-    QNetworkReply* oldReply = qobject_cast<QNetworkReply*>( sender() );
-    if ( !oldReply )
-        return; //timeout will handle it
-
-    QDomDocument doc;
-    doc.setContent( oldReply->readAll() );
-    QDomNodeList domNodeList = doc.elementsByTagName( "artist" );
-    if ( domNodeList.isEmpty() )
-    {
-        emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
-        return;
-    }
-
-    QString artist_id = domNodeList.at( 0 ).toElement().attribute( "id" );
-    QString requestString( "http://musicbrainz.org/ws/2/release-group" );
-    QUrl url( requestString );
-    url.addQueryItem( "artist", artist_id );
-    url.addQueryItem( "type", "album|ep" );
-    url.addQueryItem( "limit", "100" );
-
-    QNetworkReply* newReply = TomahawkUtils::nam()->get( QNetworkRequest( url ) );
-    newReply->setProperty( "requestData", oldReply->property( "requestData" ) );
-    connect( newReply, SIGNAL( finished() ), SLOT( tracksSearchSlot() ) );
-}
-
-
-void
-MusicBrainzPlugin::tracksSearchSlot()
-{
-    QNetworkReply* oldReply = qobject_cast<QNetworkReply*>( sender() );
-    if ( !oldReply )
-        return; //timeout will handle it
-
-    QDomDocument doc;
-    doc.setContent( oldReply->readAll() );
-    QDomNodeList domNodeList = doc.elementsByTagName( "release" );
-    if ( domNodeList.isEmpty() )
-    {
-        emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
-        return;
-    }
-
     Tomahawk::InfoSystem::InfoRequestData requestData = oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
-    InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >();
-
-    QDomElement element;
-    for ( int i = 0; i < domNodeList.count(); i++ )
+    switch ( requestData.type )
     {
-        QDomNodeList albumNodeList = domNodeList.at( i ).toElement().elementsByTagName( "title" );
-        if ( albumNodeList.at( 0 ).toElement().text() == hash["album"] )
-            element = domNodeList.at( i ).toElement();
+        case InfoAlbumSongs:
+        {
+            // we can simply use the first result as they are sorted by score
+            QString release_id = releasesNL.at(0).toElement().attribute( "id" );
+
+            QString requestString = QString( "http://musicbrainz.org/ws/2/release/%1" ).arg( release_id );
+            QUrl url( requestString );
+            url.addQueryItem( "inc", "recordings" );
+            tDebug() << Q_FUNC_INFO << url.toString();
+
+            QNetworkReply* newReply = TomahawkUtils::nam()->get( QNetworkRequest( url ) );
+            newReply->setProperty( "requestData", oldReply->property( "requestData" ) );
+            connect( newReply, SIGNAL( finished() ), SLOT( gotRecordingsSlot() ) );
+            }
+
+        default:
+        {
+            Q_ASSERT( false );
+            break;
+        }
     }
 
-    if ( element.isNull() )
-    {
-        emit info( oldReply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
-        return;
-    }
-
-    QString release_id = element.attribute( "id" );
-    QString requestString = QString( "http://musicbrainz.org/ws/2/release/%1?inc=recordings" ).arg( release_id );
-    QUrl url( requestString );
-
-    QNetworkReply* newReply = TomahawkUtils::nam()->get( QNetworkRequest( url ) );
-    newReply->setProperty( "requestData", oldReply->property( "requestData" ) );
-    connect( newReply, SIGNAL( finished() ), SLOT( tracksFoundSlot() ) );
 }
 
 
 void
-MusicBrainzPlugin::albumFoundSlot()
+MusicBrainzPlugin::gotRecordingsSlot()
 {
     QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() );
     if ( !reply )
@@ -240,75 +253,26 @@ MusicBrainzPlugin::albumFoundSlot()
 
     QDomDocument doc;
     doc.setContent( reply->readAll() );
-    QDomNodeList groups = doc.elementsByTagName( "release-group" );
-    if ( groups.isEmpty() )
+    QDomNodeList mediumList = doc.elementsByTagName( "medium-list" );
+    if ( mediumList.isEmpty() )
     {
         emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
+        tDebug() << Q_FUNC_INFO << doc.toString();
         return;
     }
 
-    QStringList albums;
-    for ( int i = 0; i < groups.count(); i++ )
+
+    QDomNodeList tracksNL = mediumList.at(0).toElement().elementsByTagName( "track" );
+    QStringList tracksSL;
+    for ( int i = 0; i < tracksNL.count(); i++ )
     {
-        QDomElement group = groups.at(i).toElement();
-        QDomNodeList secTypesDL = group.elementsByTagName("secondary-type");
-        QStringList secTypesSL;
-        for ( int i = 0; i < secTypesDL.count(); i++ )
-        {
-            secTypesSL.append(secTypesDL.at(i).toElement().text());
-        }
-        if ( !secTypesSL.contains("Live") && !secTypesSL.contains("Compilation") )
-        {
-            QString album = group.firstChildElement("title").text();
-            if ( !albums.contains( album ) )
-                albums << album;
-        }
+        QString track = tracksNL.at(i).firstChildElement( "recording" ).firstChildElement( "title" ).text();
+        tracksSL << track;
     }
 
     Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
     QVariantMap returnedData;
-    returnedData["albums"] = albums;
-    emit info( requestData, returnedData );
-
-    Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>();
-    Tomahawk::InfoSystem::InfoStringHash criteria;
-    criteria["artist"] = origData["artist"];
-    emit updateCache( criteria, 0, requestData.type, returnedData );
-}
-
-
-void
-MusicBrainzPlugin::tracksFoundSlot()
-{
-    QNetworkReply* reply = qobject_cast< QNetworkReply* >( sender() );
-    if ( !reply )
-        return; //timeout will handle it
-
-    QDomDocument doc;
-    doc.setContent( reply->readAll() );
-    QDomNodeList domNodeList = doc.elementsByTagName( "recording" );
-    if ( domNodeList.isEmpty() )
-    {
-        emit info( reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(), QVariant() );
-        return;
-    }
-
-    QStringList tracks;
-    for ( int i = 0; i < domNodeList.count(); i++ )
-    {
-        QDomNodeList trackNodeList = domNodeList.at( i ).toElement().elementsByTagName( "title" );
-
-        for ( int j = 0; j < trackNodeList.count(); j++ )
-        {
-            QString track = trackNodeList.at( j ).toElement().text();
-            if ( !tracks.contains( track ) )
-                tracks << track;
-        }
-    }
-
-    Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >();
-    QVariantMap returnedData;
-    returnedData["tracks"] = tracks;
+    returnedData["tracks"] = tracksSL;
     emit info( requestData, returnedData );
 
     Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash>();
@@ -318,5 +282,5 @@ MusicBrainzPlugin::tracksFoundSlot()
     emit updateCache( criteria, 0, requestData.type, returnedData );
 }
 
-
 Q_EXPORT_PLUGIN2( Tomahawk::InfoSystem::InfoPlugin, Tomahawk::InfoSystem::MusicBrainzPlugin )
+
diff --git a/src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.h b/src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.h
index 57ca6bc99..71ddb75b2 100644
--- a/src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.h
+++ b/src/infoplugins/generic/musicbrainz/MusicBrainzPlugin.h
@@ -53,12 +53,10 @@ protected slots:
 
 
 private slots:
-    void artistSearchSlot();
-    void albumSearchSlot();
-    void tracksSearchSlot();
 
-    void albumFoundSlot();
-    void tracksFoundSlot();
+    void gotReleaseGroupsSlot();
+    void gotReleasesSlot();
+    void gotRecordingsSlot();
 };
 
 }