diff --git a/src/lyrics/LyricsManager.cpp b/src/lyrics/LyricsManager.cpp index 106ece95aa..2754572fdb 100644 --- a/src/lyrics/LyricsManager.cpp +++ b/src/lyrics/LyricsManager.cpp @@ -1,275 +1,292 @@ /**************************************************************************************** * Copyright (c) 2007 Leo Franchi * * Copyright (c) 2009 Seb Ruiz * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "LyricsManager" #include "LyricsManager.h" #include "EngineController.h" #include "core/meta/Meta.h" #include "core/support/Debug.h" #include "core-impl/collections/support/CollectionManager.h" #include #include #include #include -#define APIURL "http://lyrics.wikia.com/api.php?action=query&prop=revisions&rvprop=content&format=xml&titles=" +#define APIURL "https://lyrics.fandom.com/api.php?action=query&prop=revisions&rvprop=content&format=xml&titles=" LyricsManager* LyricsManager::s_self = nullptr; LyricsManager::LyricsManager() { s_self = this; connect( The::engineController(), &EngineController::trackChanged, this, &LyricsManager::newTrack ); } void LyricsManager::newTrack( const Meta::TrackPtr &track ) { loadLyrics( track ); } void LyricsManager::lyricsResult( const QByteArray& lyricsXML, Meta::TrackPtr track ) //SLOT { DEBUG_BLOCK QXmlStreamReader xml( lyricsXML ); while( !xml.atEnd() ) { xml.readNext(); if( xml.name() == QStringLiteral("lyric") || xml.name() == QStringLiteral( "lyrics" ) ) { QString lyrics( xml.readElementText() ); if( !isEmpty( lyrics ) ) { // overwrite cached lyrics (as either there were no lyrics available previously OR // the user explicitly agreed to overwrite the lyrics) debug() << "setting cached lyrics..."; track->setCachedLyrics( lyrics ); // TODO: setLyricsByPath? Q_EMIT newLyrics( track ); } else { ::error() << i18n("Retrieved lyrics is empty"); return; } } else if( xml.name() == QLatin1String("suggestions") ) { QVariantList suggestions; while( xml.readNextStartElement() ) { if( xml.name() != QLatin1String("suggestion") ) continue; const QXmlStreamAttributes &a = xml.attributes(); QString artist = a.value( QLatin1String("artist") ).toString(); QString title = a.value( QLatin1String("title") ).toString(); QString url = a.value( QLatin1String("url") ).toString(); if( !url.isEmpty() ) suggestions << ( QStringList() << title << artist << url ); xml.skipCurrentElement(); } debug() << "got" << suggestions.size() << "suggestions"; if( !suggestions.isEmpty() ) Q_EMIT newSuggestions( suggestions ); return; } } if( xml.hasError() ) { warning() << "errors occurred during reading lyrics xml result:" << xml.errorString(); Q_EMIT error( i18n("Lyrics data could not be parsed") ); } } void LyricsManager::loadLyrics( Meta::TrackPtr track, bool overwrite ) { DEBUG_BLOCK if( !track ) { debug() << "no current track"; return; } // -- get current title and artist QString title = track->name(); QString artist = track->artist() ? track->artist()->name() : QString(); sanitizeTitle( title ); sanitizeArtist( artist ); if( !isEmpty( track->cachedLyrics() ) && !overwrite ) { debug() << "Lyrics already cached."; return; } QUrl url( APIURL + artist + QLatin1Char(':') + title ); m_trackMap.insert( url, track ); + + connect( NetworkAccessManagerProxy::instance(), &NetworkAccessManagerProxy::requestRedirectedUrl, + this, &LyricsManager::updateRedirectedUrl); + NetworkAccessManagerProxy::instance()->getData( url, this, &LyricsManager::lyricsLoaded ); } void LyricsManager::lyricsLoaded( const QUrl& url, const QByteArray& data, const NetworkAccessManagerProxy::Error &err ) { DEBUG_BLOCK if( err.code ) { warning() << "A network error occurred:" << err.description; return; } Meta::TrackPtr track = m_trackMap.take( url ); if( !track ) { warning() << "No track belongs to this url:" << url.url(); return; } QDomDocument document; document.setContent( data ); auto list = document.elementsByTagName( QStringLiteral( "rev" ) ); if( list.isEmpty() ) { if( track->album() && track->album()->albumArtist() ) { QString albumArtist = track->album()->albumArtist()->name(); QString artist = track->artist() ? track->artist()->name() : QString(); QString title = track->name(); sanitizeTitle( title ); sanitizeArtist( artist ); sanitizeArtist( albumArtist ); //Try with album artist if( url == QUrl( APIURL + artist + QLatin1Char(':') + title ) && albumArtist != artist ) { debug() << "Try again with album artist."; QUrl newUrl( APIURL + albumArtist + QLatin1Char(':') + title ); m_trackMap.insert( newUrl, track ); NetworkAccessManagerProxy::instance()->getData( newUrl, this, &LyricsManager::lyricsLoaded ); return; } } debug() << "No lyrics found for track:" << track->name(); return; } QString rev = list.at( 0 ).toElement().text(); if( rev.contains( QStringLiteral( "lyrics" ) ) ) { int lindex = rev.indexOf( QStringLiteral( "" ) ); int rindex = rev.indexOf( QStringLiteral( "" ) ); lyricsResult( (rev.mid( lindex, rindex - lindex ) + "" ).toUtf8(), track ); } else if( rev.contains( QStringLiteral( "lyric" ) ) ) { int lindex = rev.indexOf( QStringLiteral( "" ) ); int rindex = rev.indexOf( QStringLiteral( "" ) ); lyricsResult( (rev.mid( lindex, rindex - lindex ) + "" ).toUtf8(), track ); } else if( rev.contains( QStringLiteral( "#REDIRECT" ) ) ) { debug() << "Redirect:" << data; int lindex = rev.indexOf( QStringLiteral( "#REDIRECT [[" ) ) + 12; int rindex = rev.indexOf( QStringLiteral( "]]" ) ); QStringList list = rev.mid( lindex, rindex - lindex ).split( QLatin1Char(':') ); if( list.size() == 2 ) { list[0] = list[0].replace( '&', QStringLiteral( "%26" ) ); list[1] = list[1].replace( '&', QStringLiteral( "%26" ) ); QUrl newUrl( APIURL + list.join( QLatin1Char(':') ) ); m_trackMap.insert( newUrl, track ); NetworkAccessManagerProxy::instance()->getData( newUrl, this, &LyricsManager::lyricsLoaded ); } } else warning() << "No lyrics found in data:" << data; } void LyricsManager::sanitizeTitle( QString& title ) { const QString magnatunePreviewString = QStringLiteral( "PREVIEW: buy it at www.magnatune.com" ); if( title.contains(magnatunePreviewString, Qt::CaseSensitive) ) title = title.remove( " (" + magnatunePreviewString + ')' ); title = title.remove( QStringLiteral( "(Live)" ) ); title = title.remove( QStringLiteral( "(live)" ) ); title = title.replace( '`', QStringLiteral( "'" ) ); title = title.replace( '&', QStringLiteral( "%26" ) ); } void LyricsManager::sanitizeArtist( QString& artist ) { const QString magnatunePreviewString = QStringLiteral( "PREVIEW: buy it at www.magnatune.com" ); if( artist.contains(magnatunePreviewString, Qt::CaseSensitive) ) artist = artist.remove( " (" + magnatunePreviewString + ')' ); // strip "featuring " from the artist int strip = artist.toLower().indexOf( QLatin1String(" ft. ")); if ( strip != -1 ) artist = artist.mid( 0, strip ); strip = artist.toLower().indexOf( QLatin1String(" feat. ") ); if ( strip != -1 ) artist = artist.mid( 0, strip ); strip = artist.toLower().indexOf( QLatin1String(" featuring ") ); if ( strip != -1 ) artist = artist.mid( 0, strip ); artist = artist.replace( '`', QStringLiteral( "'" ) ); artist = artist.replace( '&', QStringLiteral( "%26" ) ); } bool LyricsManager::isEmpty( const QString &lyrics ) const { QTextEdit testItem; // Set the text of the TextItem. if( Qt::mightBeRichText( lyrics ) ) testItem.setHtml( lyrics ); else testItem.setPlainText( lyrics ); // Get the plaintext content. // We use toPlainText() to strip all Html formatting, // so we can test if there's any text given. QString testText = testItem.toPlainText().trimmed(); return testText.isEmpty(); } + +void LyricsManager::updateRedirectedUrl(const QUrl& oldUrl, const QUrl& newUrl) +{ + if( m_trackMap.contains( oldUrl ) && !m_trackMap.contains( newUrl ) ) + { + // Get track for the old URL. + Meta::TrackPtr track = m_trackMap.value( oldUrl ); + + // Replace with redirected url for correct lookup + m_trackMap.insert( newUrl, track ); + m_trackMap.remove( oldUrl ); + } +} diff --git a/src/lyrics/LyricsManager.h b/src/lyrics/LyricsManager.h index ed11d88404..f2958d91e6 100644 --- a/src/lyrics/LyricsManager.h +++ b/src/lyrics/LyricsManager.h @@ -1,73 +1,76 @@ /**************************************************************************************** * Copyright (c) 2007 Leo Franchi * * * * This program 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 2 of the License, or (at your option) any later * * version. * * * * This program 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 * * this program. If not, see . * ****************************************************************************************/ #ifndef LYRICS_MANAGER_H #define LYRICS_MANAGER_H #include "amarok_export.h" #include "core/meta/Meta.h" #include "network/NetworkAccessManagerProxy.h" #include #include #include #include class AMAROK_EXPORT LyricsManager : public QObject { Q_OBJECT public: static LyricsManager* instance() { if( !s_self ) s_self = new LyricsManager(); return s_self; } /** * Tests if the given lyrics are empty. * * @param lyrics The lyrics which will be tested. * * @return true if the given lyrics are empty, otherwise false. */ bool isEmpty( const QString &lyrics ) const; void newTrack( const Meta::TrackPtr &track ); void lyricsResult( const QByteArray& lyrics, Meta::TrackPtr track ); void lyricsLoaded( const QUrl &url, const QByteArray &data, const NetworkAccessManagerProxy::Error &err ); void loadLyrics( Meta::TrackPtr track, bool overwrite = false ); Q_SIGNALS: void newLyrics( Meta::TrackPtr ); void newSuggestions( const QVariantList& ); void error( const QString &); private: LyricsManager(); void sanitizeTitle( QString &title ); void sanitizeArtist( QString &artist ); static LyricsManager* s_self; QMap m_trackMap; + + private Q_SLOTS: + void updateRedirectedUrl( const QUrl& oldUrl, const QUrl& newUrl ); }; #endif