diff --git a/src/context/applets/photos/plugin/PhotosEngine.cpp b/src/context/applets/photos/plugin/PhotosEngine.cpp --- a/src/context/applets/photos/plugin/PhotosEngine.cpp +++ b/src/context/applets/photos/plugin/PhotosEngine.cpp @@ -162,8 +162,7 @@ debug() << "Flickr url:" << flickrUrl; m_flickrUrls << flickrUrl; - The::networkAccessManager()->getData( flickrUrl, this, - SLOT(resultFlickr(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); + The::networkAccessManager()->getData( flickrUrl, this, &PhotosEngine::resultFlickr ); } } diff --git a/src/covermanager/CoverFetcher.cpp b/src/covermanager/CoverFetcher.cpp --- a/src/covermanager/CoverFetcher.cpp +++ b/src/covermanager/CoverFetcher.cpp @@ -166,8 +166,7 @@ if( !url.isValid() ) continue; - QNetworkReply *reply = The::networkAccessManager()->getData( url, this, - SLOT(slotResult(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); + QNetworkReply *reply = The::networkAccessManager()->getData( url, this, &CoverFetcher::slotResult ); m_urls.insert( url, unit ); if( payload->type() == CoverFetchPayload::Art ) diff --git a/src/covermanager/CoverFoundDialog.cpp b/src/covermanager/CoverFoundDialog.cpp --- a/src/covermanager/CoverFoundDialog.cpp +++ b/src/covermanager/CoverFoundDialog.cpp @@ -529,8 +529,7 @@ if( !url.isValid() ) return false; - QNetworkReply *reply = The::networkAccessManager()->getData( url, this, - SLOT(handleFetchResult(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); + QNetworkReply *reply = The::networkAccessManager()->getData( url, this, &CoverFoundDialog::handleFetchResult ); m_urls.insert( url, item ); if( !m_dialog ) diff --git a/src/network/NetworkAccessManagerProxy.h b/src/network/NetworkAccessManagerProxy.h --- a/src/network/NetworkAccessManagerProxy.h +++ b/src/network/NetworkAccessManagerProxy.h @@ -19,11 +19,16 @@ #include "amarok_export.h" #include +#include "core/support/Debug.h" #include -#include #include +#include +#include +#include +#include + class NetworkAccessManagerProxy; #ifdef DEBUG_BUILD_TYPE @@ -62,8 +67,26 @@ * @param type the #Qt::ConnectionType used for calling the @p method. * @return a QNetworkReply object for custom monitoring. */ - QNetworkReply *getData( const QUrl &url, QObject *receiver, const char *method, - Qt::ConnectionType type = Qt::AutoConnection ); + template + QNetworkReply *getData( const QUrl &url, Object *receiver, Return ( Object::*method )( Args... ), + Qt::ConnectionType type = Qt::AutoConnection ) + { + if( !url.isValid() ) + { + const QMetaObject *mo = receiver->metaObject(); + debug() << QString( "Error: URL '%1' is invalid (from %2)" ).arg( url.url() ).arg( mo->className() ); + return 0; + } + + QNetworkReply *r = get( QNetworkRequest(url) ); + m_urlMap.insert( url, r ); + auto lambda = [this, r, receiver, method, type] () + { + replyFinished( r, QPointer( receiver ), method, type ); + }; + connect( r, &QNetworkReply::finished, this, lambda ); + return r; + } int abortGet( const QUrl &url ); int abortGet( const QList &urls ); @@ -98,12 +121,66 @@ private: NetworkAccessManagerProxy( QObject *parent = 0 ); - void replyFinished(); + + template + void replyFinished( QNetworkReply *reply, QPointer receiver, Return ( Object::*method )( Args... ), Qt::ConnectionType type ) + { + if( !reply || !receiver ) + return; + + QUrl url = reply->request().url(); + QByteArray data = reply->readAll(); + data.detach(); // detach so the bytes are not deleted before methods are invoked + + // There may have been a redirect. + QUrl redirectUrl = getRedirectUrl( reply ); + + // Check if there's no redirect. + if( redirectUrl.isEmpty() ) + { + Error err = { reply->error(), reply->errorString() }; + + if( type == Qt::AutoConnection ) + { + if( QThread::currentThread() == receiver->thread() ) + type = Qt::DirectConnection; + else + type = Qt::QueuedConnection; + } + + if( type == Qt::DirectConnection ) + ( receiver->*method )( url, data, err ); + else + { + auto lambda = [receiver, method, url, data, err] () + { + ( receiver->*method )( url, data, err ); + }; + QTimer::singleShot( 0, receiver, lambda ); + } + } + else + { + debug() << "the server is redirecting the request to: " << redirectUrl; + + // Let's try to fetch the data again, but this time from the new url. + QNetworkReply *newReply = getData( redirectUrl, receiver.data(), method, type ); + + emit requestRedirectedUrl( url, redirectUrl ); + emit requestRedirectedReply( reply, newReply ); + } + + reply->deleteLater(); + } + static NetworkAccessManagerProxy *s_instance; - class NetworkAccessManagerProxyPrivate; - NetworkAccessManagerProxyPrivate* const d; - friend class NetworkAccessManagerProxyPrivate; + QMultiHash m_urlMap; + QString m_userAgent; + +#ifdef DEBUG_BUILD_TYPE + NetworkAccessViewer *m_viewer; +#endif // DEBUG_BUILD_TYPE Q_DISABLE_COPY( NetworkAccessManagerProxy ) }; diff --git a/src/network/NetworkAccessManagerProxy.cpp b/src/network/NetworkAccessManagerProxy.cpp --- a/src/network/NetworkAccessManagerProxy.cpp +++ b/src/network/NetworkAccessManagerProxy.cpp @@ -22,21 +22,16 @@ #include "NetworkAccessViewer.h" #endif // DEBUG_BUILD_TYPE -#include "core/support/Debug.h" - #include "Version.h" #include -#include -#include -#include -NetworkAccessManagerProxy *NetworkAccessManagerProxy::s_instance = 0; +NetworkAccessManagerProxy *NetworkAccessManagerProxy::s_instance = nullptr; NetworkAccessManagerProxy *NetworkAccessManagerProxy::instance() { - if( s_instance == 0 ) + if( s_instance == nullptr ) s_instance = new NetworkAccessManagerProxy(); return s_instance; } @@ -46,168 +41,45 @@ if( s_instance ) { delete s_instance; - s_instance = 0; + s_instance = nullptr; } } -class NetworkAccessManagerProxy::NetworkAccessManagerProxyPrivate -{ -public: - NetworkAccessManagerProxyPrivate( NetworkAccessManagerProxy *parent ) - : userAgent( QString( "Amarok/" ) + AMAROK_VERSION ) -#ifdef DEBUG_BUILD_TYPE - , viewer( 0 ) -#endif // DEBUG_BUILD_TYPE - , q_ptr( parent ) - {} - - ~NetworkAccessManagerProxyPrivate() {} - - void _replyFinished() - { - Q_Q( NetworkAccessManagerProxy ); - QNetworkReply *reply = static_cast( q->sender() ); - - QUrl url = reply->request().url(); - QList callbacks = urlMap.values( url ); - urlMap.remove( url ); - QByteArray data = reply->readAll(); - data.detach(); // detach so the bytes are not deleted before methods are invoked - foreach( const CallBackData *cb, callbacks ) - { - // There may have been a redirect. - QUrl redirectUrl = q->getRedirectUrl( reply ); - - // Check if there's no redirect. - if( redirectUrl.isEmpty() ) - { - QByteArray sig = QMetaObject::normalizedSignature( cb->method ); - sig.remove( 0, 1 ); // remove first char, which is the member code (see qobjectdefs.h) - // and let Qt's meta object system handle the rest. - if( cb->receiver ) - { - bool success( false ); - const QMetaObject *mo = cb->receiver->metaObject(); - int methodIndex = mo->indexOfSlot( sig ); - if( methodIndex != -1 ) - { - Error err = { reply->error(), reply->errorString() }; - QMetaMethod method = mo->method( methodIndex ); - success = method.invoke( cb->receiver.data(), - cb->type, - Q_ARG( QUrl, reply->request().url() ), - Q_ARG( QByteArray, data ), - Q_ARG( NetworkAccessManagerProxy::Error, err ) ); - } - - if( !success ) - { - debug() << QString( "Failed to invoke method %1 of %2" ) - .arg( QString(sig) ).arg( mo->className() ); - } - } - } - else - { - debug() << "the server is redirecting the request to: " << redirectUrl; - - // Let's try to fetch the data again, but this time from the new url. - QNetworkReply *newReply = q->getData( redirectUrl, cb->receiver.data(), cb->method, cb->type ); - - emit q->requestRedirectedUrl( url, redirectUrl ); - emit q->requestRedirectedReply( reply, newReply ); - } - } - - qDeleteAll( callbacks ); - reply->deleteLater(); - } - - class CallBackData - { - public: - CallBackData( QObject *rec, QNetworkReply *rep, const char *met, Qt::ConnectionType t ) - : receiver( rec ) - , reply( rep ) - , method( met ) - , type( t ) - {} - - ~CallBackData() - { - if( reply ) - reply->deleteLater(); - } - - QPointer receiver; - QPointer reply; - const char *method; - Qt::ConnectionType type; - }; - - QMultiHash urlMap; - QString userAgent; -#ifdef DEBUG_BUILD_TYPE - NetworkAccessViewer *viewer; -#endif // DEBUG_BUILD_TYPE - -private: - NetworkAccessManagerProxy *const q_ptr; - Q_DECLARE_PUBLIC( NetworkAccessManagerProxy ) -}; - NetworkAccessManagerProxy::NetworkAccessManagerProxy( QObject *parent ) : KIO::Integration::AccessManager( parent ) - , d( new NetworkAccessManagerProxyPrivate( this ) ) + , m_userAgent( QString( "Amarok/" ) + AMAROK_VERSION ) +#ifdef DEBUG_BUILD_TYPE + , m_viewer( nullptr ) +#endif // DEBUG_BUILD_TYPE { setCache(0); // disable QtWebKit cache to just use KIO one.. qRegisterMetaType(); } NetworkAccessManagerProxy::~NetworkAccessManagerProxy() { - delete d; - s_instance = 0; + s_instance = nullptr; } #ifdef DEBUG_BUILD_TYPE NetworkAccessViewer * NetworkAccessManagerProxy::networkAccessViewer() { - return d->viewer; + return m_viewer; } void NetworkAccessManagerProxy::setNetworkAccessViewer( NetworkAccessViewer *viewer ) { if( viewer ) { - if( d->viewer ) - delete d->viewer; - d->viewer = viewer; + if( m_viewer ) + delete m_viewer; + m_viewer = viewer; } } #endif // DEBUG_BUILD_TYPE -QNetworkReply * -NetworkAccessManagerProxy::getData( const QUrl &url, QObject *receiver, const char *method, - Qt::ConnectionType type ) -{ - if( !url.isValid() ) - { - const QMetaObject *mo = receiver->metaObject(); - debug() << QString( "Error: URL '%1' is invalid (from %2)" ).arg( url.url() ).arg( mo->className() ); - return 0; - } - - QNetworkReply *r = get( QNetworkRequest(url) ); - typedef NetworkAccessManagerProxyPrivate::CallBackData PrivateCallBackData; - PrivateCallBackData *cbm = new PrivateCallBackData( receiver, r, method, type ); - d->urlMap.insert( url, cbm ); - connect( r, &QNetworkReply::finished, this, &NetworkAccessManagerProxy::replyFinished, type ); - return r; -} - int NetworkAccessManagerProxy::abortGet( const QList &urls ) { @@ -221,11 +93,11 @@ int NetworkAccessManagerProxy::abortGet( const QUrl &url ) { - if( !d->urlMap.contains(url) ) + if( m_urlMap.contains(url) ) return 0; - qDeleteAll( d->urlMap.values( url ) ); - int removed = d->urlMap.remove( url ); + qDeleteAll( m_urlMap.values( url ) ); + int removed = m_urlMap.remove( url ); return removed; } @@ -261,7 +133,7 @@ if( !reply ) return; QUrl url = reply->request().url(); - d->urlMap.remove( url ); + m_urlMap.remove( url ); reply->deleteLater(); } @@ -271,9 +143,9 @@ QNetworkRequest request = req; request.setAttribute( QNetworkRequest::HttpPipeliningAllowedAttribute, true ); if ( request.hasRawHeader( "User-Agent" ) ) - request.setRawHeader( "User-Agent", d->userAgent.toLocal8Bit() + ' ' + request.rawHeader( "User-Agent" ) ); + request.setRawHeader( "User-Agent", m_userAgent.toLocal8Bit() + ' ' + request.rawHeader( "User-Agent" ) ); else - request.setRawHeader( "User-Agent", d->userAgent.toLocal8Bit() ); + request.setRawHeader( "User-Agent", m_userAgent.toLocal8Bit() ); KIO::CacheControl cc = KProtocolManager::cacheControl(); switch (cc) @@ -300,18 +172,12 @@ QNetworkReply *reply = KIO::Integration::AccessManager::createRequest( op, request, outgoingData ); #ifdef DEBUG_BUILD_TYPE - if( d->viewer ) - d->viewer->addRequest( op, request, outgoingData, reply ); + if( m_viewer ) + m_viewer->addRequest( op, request, outgoingData, reply ); #endif // DEBUG_BUILD_TYPE return reply; } -void -NetworkAccessManagerProxy::replyFinished() -{ - d->_replyFinished(); -} - namespace The { NetworkAccessManagerProxy *networkAccessManager() diff --git a/src/scripting/scriptengine/AmarokNetworkScript.h b/src/scripting/scriptengine/AmarokNetworkScript.h --- a/src/scripting/scriptengine/AmarokNetworkScript.h +++ b/src/scripting/scriptengine/AmarokNetworkScript.h @@ -72,7 +72,15 @@ private: void cleanUp( const QUrl &url ); - void newDownload( const QUrl &url, QScriptEngine* engine, QScriptValue obj, const char *slot ); + + template + void newDownload( const QUrl &url, QScriptEngine* engine, QScriptValue obj, Function slot ) + { + m_values[ url ] = obj; + m_engines[ url ] = engine; + + The::networkAccessManager()->getData( url, this, slot ); + } /** * Template function which updates the given @p sourceUrl to the given diff --git a/src/scripting/scriptengine/AmarokNetworkScript.cpp b/src/scripting/scriptengine/AmarokNetworkScript.cpp --- a/src/scripting/scriptengine/AmarokNetworkScript.cpp +++ b/src/scripting/scriptengine/AmarokNetworkScript.cpp @@ -126,22 +126,13 @@ AmarokDownloadHelper::newStringDownload( const QUrl &url, QScriptEngine* engine, QScriptValue obj, QString encoding ) { m_encodings[ url ] = encoding; - newDownload( url, engine, obj, SLOT(resultString(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); + newDownload( url, engine, obj, &AmarokDownloadHelper::resultString ); } void AmarokDownloadHelper::newDataDownload( const QUrl &url, QScriptEngine* engine, QScriptValue obj ) { - newDownload( url, engine, obj, SLOT(resultData(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); -} - -void -AmarokDownloadHelper::newDownload( const QUrl &url, QScriptEngine* engine, QScriptValue obj, const char *slot ) -{ - m_values[ url ] = obj; - m_engines[ url ] = engine; - - The::networkAccessManager()->getData( url, this, slot ); + newDownload( url, engine, obj, &AmarokDownloadHelper::resultData ); } void diff --git a/src/services/ampache/AmpacheAccountLogin.cpp b/src/services/ampache/AmpacheAccountLogin.cpp --- a/src/services/ampache/AmpacheAccountLogin.cpp +++ b/src/services/ampache/AmpacheAccountLogin.cpp @@ -62,8 +62,7 @@ debug() << "Verifying Ampache Version Using: " << url.url(); - m_pingRequest = The::networkAccessManager()->getData( url, this, - SLOT(authenticate(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); + m_pingRequest = The::networkAccessManager()->getData( url, this, &AmpacheAccountLogin::authenticate ); if( !m_pingRequest ) emit finished(); @@ -128,8 +127,7 @@ debug() << "Authenticating with string: " << url.url() << passPhrase; // TODO: Amarok::Components::logger()->newProgressOperation( m_xmlDownloadJob, i18n( "Authenticating with Ampache" ) ); - m_authRequest = The::networkAccessManager()->getData( url, this, - SLOT(authenticationComplete(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); + m_authRequest = The::networkAccessManager()->getData( url, this, &AmpacheAccountLogin::authenticationComplete ); if( !m_authRequest ) emit finished(); diff --git a/src/services/ampache/AmpacheServiceQueryMaker.cpp b/src/services/ampache/AmpacheServiceQueryMaker.cpp --- a/src/services/ampache/AmpacheServiceQueryMaker.cpp +++ b/src/services/ampache/AmpacheServiceQueryMaker.cpp @@ -229,8 +229,7 @@ } d->expectedReplies.ref(); - The::networkAccessManager()->getData( request, this, - SLOT(artistDownloadComplete(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); + The::networkAccessManager()->getData( request, this, &AmpacheServiceQueryMaker::artistDownloadComplete ); } void @@ -264,8 +263,7 @@ request.setQuery( query ); d->expectedReplies.ref(); - The::networkAccessManager()->getData( request, this, - SLOT(albumDownloadComplete(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); + The::networkAccessManager()->getData( request, this, &AmpacheServiceQueryMaker::albumDownloadComplete ); } } else @@ -280,8 +278,7 @@ } d->expectedReplies.ref(); - The::networkAccessManager()->getData( request, this, - SLOT(albumDownloadComplete(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); + The::networkAccessManager()->getData( request, this, &AmpacheServiceQueryMaker::albumDownloadComplete ); } } @@ -343,8 +340,7 @@ request.setQuery( query ); d->expectedReplies.ref(); - The::networkAccessManager()->getData( request, this, - SLOT(trackDownloadComplete(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); + The::networkAccessManager()->getData( request, this, &AmpacheServiceQueryMaker::trackDownloadComplete ); } } else if( !d->parentArtistIds.isEmpty() ) @@ -357,17 +353,15 @@ request.setQuery( query ); d->expectedReplies.ref(); - The::networkAccessManager()->getData( request, this, - SLOT(trackDownloadComplete(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); + The::networkAccessManager()->getData( request, this, &AmpacheServiceQueryMaker::trackDownloadComplete ); } } else { QUrl request = getRequestUrl( "songs" ); d->expectedReplies.ref(); - The::networkAccessManager()->getData( request, this, - SLOT(trackDownloadComplete(QUrl,QByteArray,NetworkAccessManagerProxy::Error)) ); + The::networkAccessManager()->getData( request, this, &AmpacheServiceQueryMaker::trackDownloadComplete ); } }