diff --git a/src/libtomahawk/audio/AudioEngine.cpp b/src/libtomahawk/audio/AudioEngine.cpp index fe776a707..5be27baa2 100644 --- a/src/libtomahawk/audio/AudioEngine.cpp +++ b/src/libtomahawk/audio/AudioEngine.cpp @@ -2,6 +2,7 @@ * * Copyright 2010-2012, Christian Muehlhaeuser * Copyright 2010-2012, Jeff Mitchell + * Copyright 2013, Teo Mrnjavac * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -39,6 +40,8 @@ #include "utils/Logger.h" #include "playlist/SingleTrackPlaylistInterface.h" +#include + #include #include #include @@ -430,30 +433,43 @@ AudioEngine::onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType type ) } -bool +void AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) +{ + if ( result.isNull() ) + { + stop(); + return; + } + + setCurrentTrack( result ); + + if ( !TomahawkUtils::isHttpResult( m_currentTrack->url() ) && + !TomahawkUtils::isLocalResult( m_currentTrack->url() ) ) + { + boost::function< void ( QSharedPointer< QIODevice >& ) > callback = + boost::bind( &AudioEngine::performLoadTrack, this, result, _1 ); + Servent::instance()->getIODeviceForUrl( m_currentTrack, callback ); + } + else + { + QSharedPointer< QIODevice > io; + performLoadTrack( result, io ); + } +} + + +void +AudioEngine::performLoadTrack( const Tomahawk::result_ptr& result, QSharedPointer< QIODevice >& io ) { bool err = false; { - QSharedPointer io; - - if ( result.isNull() ) - err = true; - else + if ( !TomahawkUtils::isHttpResult( m_currentTrack->url() ) && + !TomahawkUtils::isLocalResult( m_currentTrack->url() ) && + ( !io || io.isNull() ) ) { - setCurrentTrack( result ); - - if ( !TomahawkUtils::isHttpResult( m_currentTrack->url() ) && - !TomahawkUtils::isLocalResult( m_currentTrack->url() ) ) - { - io = Servent::instance()->getIODeviceForUrl( m_currentTrack ); - - if ( !io || io.isNull() ) - { - tLog() << "Error getting iodevice for" << result->url(); - err = true; - } - } + tLog() << "Error getting iodevice for" << result->url(); + err = true; } if ( !err ) @@ -520,11 +536,11 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result ) if ( err ) { stop(); - return false; + return; } m_waitingOnNewTrack = false; - return true; + return; } diff --git a/src/libtomahawk/audio/AudioEngine.h b/src/libtomahawk/audio/AudioEngine.h index 67d83aaef..99d22c0a6 100644 --- a/src/libtomahawk/audio/AudioEngine.h +++ b/src/libtomahawk/audio/AudioEngine.h @@ -2,6 +2,7 @@ * * Copyright 2010-2012, Christian Muehlhaeuser * Copyright 2010-2012, Jeff Mitchell + * Copyright 2013, Teo Mrnjavac * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -128,7 +129,8 @@ signals: void error( AudioEngine::AudioErrorCode errorCode ); private slots: - bool loadTrack( const Tomahawk::result_ptr& result ); + void loadTrack( const Tomahawk::result_ptr& result ); //async! + void performLoadTrack( const Tomahawk::result_ptr& result, QSharedPointer< QIODevice >& io ); //only call from loadTrack kthxbi void loadPreviousTrack(); void loadNextTrack(); diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 761a75e73..957701ae3 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2012, Jeff Mitchell + * Copyright 2013, Teo Mrnjavac * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -75,17 +76,18 @@ Servent::Servent( QObject* parent ) setProxy( QNetworkProxy::NoProxy ); { - boost::function(result_ptr)> fac = boost::bind( &Servent::localFileIODeviceFactory, this, _1 ); + // _1 = result, _2 = callback function for IODevice + IODeviceFactoryFunc fac = boost::bind( &Servent::localFileIODeviceFactory, this, _1, _2 ); this->registerIODeviceFactory( "file", fac ); } { - boost::function(result_ptr)> fac = boost::bind( &Servent::remoteIODeviceFactory, this, _1 ); + IODeviceFactoryFunc fac = boost::bind( &Servent::remoteIODeviceFactory, this, _1, _2 ); this->registerIODeviceFactory( "servent", fac ); } { - boost::function(result_ptr)> fac = boost::bind( &Servent::httpIODeviceFactory, this, _1 ); + IODeviceFactoryFunc fac = boost::bind( &Servent::httpIODeviceFactory, this, _1, _2 ); this->registerIODeviceFactory( "http", fac ); this->registerIODeviceFactory( "https", fac ); } @@ -944,8 +946,9 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString } -QSharedPointer -Servent::remoteIODeviceFactory( const result_ptr& result ) +void +Servent::remoteIODeviceFactory( const Tomahawk::result_ptr& result, + boost::function< void ( QSharedPointer< QIODevice >& ) > callback ) { QSharedPointer sp; @@ -954,12 +957,18 @@ Servent::remoteIODeviceFactory( const result_ptr& result ) const QString fileId = parts.at( 1 ); source_ptr s = SourceList::instance()->get( sourceName ); if ( s.isNull() || !s->controlConnection() ) - return sp; + { + callback( sp ); + return; + } ControlConnection* cc = s->controlConnection(); StreamConnection* sc = new StreamConnection( this, cc, fileId, result ); createParallelConnection( cc, sc, QString( "FILE_REQUEST_KEY:%1" ).arg( fileId ) ); - return sc->iodevice(); + + //boost::functions cannot accept temporaries as parameters + sp = sc->iodevice(); + callback( sp ); } @@ -1066,45 +1075,61 @@ Servent::triggerDBSync() void -Servent::registerIODeviceFactory( const QString &proto, boost::function(Tomahawk::result_ptr)> fac ) +Servent::registerIODeviceFactory( const QString &proto, + IODeviceFactoryFunc fac ) { m_iofactories.insert( proto, fac ); } -QSharedPointer -Servent::getIODeviceForUrl( const Tomahawk::result_ptr& result ) +void +Servent::getIODeviceForUrl( const Tomahawk::result_ptr& result, + boost::function< void ( QSharedPointer< QIODevice >& ) > callback ) { QSharedPointer sp; QRegExp rx( "^([a-zA-Z0-9]+)://(.+)$" ); if ( rx.indexIn( result->url() ) == -1 ) - return sp; + { + callback( sp ); + return; + } const QString proto = rx.cap( 1 ); if ( !m_iofactories.contains( proto ) ) - return sp; + { + callback( sp ); + return; + } - return m_iofactories.value( proto )( result ); + //QtScriptResolverHelper::customIODeviceFactory is async! + m_iofactories.value( proto )( result, callback ); } -QSharedPointer -Servent::localFileIODeviceFactory( const Tomahawk::result_ptr& result ) +void +Servent::localFileIODeviceFactory( const Tomahawk::result_ptr& result, + boost::function< void ( QSharedPointer< QIODevice >& ) > callback ) { // ignore "file://" at front of url QFile* io = new QFile( result->url().mid( QString( "file://" ).length() ) ); if ( io ) io->open( QIODevice::ReadOnly ); - return QSharedPointer( io ); + //boost::functions cannot accept temporaries as parameters + QSharedPointer< QIODevice > sp = QSharedPointer( io ); + callback( sp ); } -QSharedPointer -Servent::httpIODeviceFactory( const Tomahawk::result_ptr& result ) +void +Servent::httpIODeviceFactory( const Tomahawk::result_ptr& result, + boost::function< void ( QSharedPointer< QIODevice >& ) > callback ) { QNetworkRequest req( result->url() ); QNetworkReply* reply = TomahawkUtils::nam()->get( req ); - return QSharedPointer( reply, &QObject::deleteLater ); + + //boost::functions cannot accept temporaries as parameters + QSharedPointer< QIODevice > sp = QSharedPointer< QIODevice >( reply, &QObject::deleteLater ); + callback( sp ); } diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index d4052a97b..7452fdb57 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2012, Jeff Mitchell + * Copyright 2013, Teo Mrnjavac * * 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,6 +55,9 @@ class PortFwdThread; class PeerInfo; class SipInfo; +typedef boost::function< void( const Tomahawk::result_ptr&, + boost::function< void( QSharedPointer< QIODevice >& ) > )> IODeviceFactoryFunc; + // this is used to hold a bit of state, so when a connected signal is emitted // from a socket, we can associate it with a Connection object etc. class DLLEXPORT QTcpSocketExtra : public QTcpSocket @@ -121,7 +125,6 @@ public: QString externalAddress() const { return !m_externalHostname.isNull() ? m_externalHostname : m_externalAddress.toString(); } int externalPort() const { return m_externalPort; } - QSharedPointer< QIODevice > remoteIODeviceFactory( const Tomahawk::result_ptr& ); static bool isIPWhitelisted( QHostAddress ip ); bool connectedToSession( const QString& session ); @@ -129,10 +132,11 @@ public: QList< StreamConnection* > streams() const { return m_scsessions; } - QSharedPointer< QIODevice > getIODeviceForUrl( const Tomahawk::result_ptr& result ); - void registerIODeviceFactory( const QString &proto, boost::function< QSharedPointer< QIODevice >(Tomahawk::result_ptr) > fac ); - QSharedPointer< QIODevice > localFileIODeviceFactory( const Tomahawk::result_ptr& result ); - QSharedPointer< QIODevice > httpIODeviceFactory( const Tomahawk::result_ptr& result ); + void getIODeviceForUrl( const Tomahawk::result_ptr& result, boost::function< void ( QSharedPointer< QIODevice >& ) > callback ); + void registerIODeviceFactory( const QString &proto, IODeviceFactoryFunc fac ); + void remoteIODeviceFactory( const Tomahawk::result_ptr& result, boost::function< void ( QSharedPointer< QIODevice >& ) > callback ); + void localFileIODeviceFactory( const Tomahawk::result_ptr& result, boost::function< void ( QSharedPointer< QIODevice >& ) > callback ); + void httpIODeviceFactory( const Tomahawk::result_ptr& result, boost::function< void ( QSharedPointer< QIODevice >& ) > callback ); bool isReady() const { return m_ready; }; @@ -184,7 +188,7 @@ private: QList< StreamConnection* > m_scsessions; QMutex m_ftsession_mut; - QMap< QString,boost::function< QSharedPointer< QIODevice >(Tomahawk::result_ptr) > > m_iofactories; + QMap< QString, IODeviceFactoryFunc > m_iofactories; QPointer< PortFwdThread > m_portfwd; static Servent* s_instance; diff --git a/src/libtomahawk/network/StreamConnection.cpp b/src/libtomahawk/network/StreamConnection.cpp index 9f8b4c47b..fe6d24362 100644 --- a/src/libtomahawk/network/StreamConnection.cpp +++ b/src/libtomahawk/network/StreamConnection.cpp @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Jeff Mitchell + * Copyright 2013, Teo Mrnjavac * * 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,10 +20,7 @@ #include "StreamConnection.h" -#include - #include "Result.h" - #include "BufferIoDevice.h" #include "network/ControlConnection.h" #include "network/Servent.h" @@ -31,6 +29,10 @@ #include "SourceList.h" #include "utils/Logger.h" +#include + +#include + using namespace Tomahawk; @@ -179,8 +181,16 @@ StreamConnection::startSending( const Tomahawk::result_ptr& result ) m_result = result; qDebug() << "Starting to transmit" << m_result->url(); - QSharedPointer io = Servent::instance()->getIODeviceForUrl( m_result ); - if( !io ) + boost::function< void ( QSharedPointer< QIODevice >& ) > callback = + boost::bind( &StreamConnection::reallyStartSending, this, result, _1 ); + Servent::instance()->getIODeviceForUrl( m_result, callback ); +} + + +void +StreamConnection::reallyStartSending( const Tomahawk::result_ptr& result, QSharedPointer< QIODevice >& io ) +{ + if( !io || io.isNull() ) { qDebug() << "Couldn't read from source:" << m_result->url(); shutdown(); diff --git a/src/libtomahawk/network/StreamConnection.h b/src/libtomahawk/network/StreamConnection.h index 52e2943de..f41351ba4 100644 --- a/src/libtomahawk/network/StreamConnection.h +++ b/src/libtomahawk/network/StreamConnection.h @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Jeff Mitchell + * Copyright 2013, Teo Mrnjavac * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -71,7 +72,8 @@ protected slots: virtual void handleMsg( msg_ptr msg ); private slots: - void startSending( const Tomahawk::result_ptr& ); + void startSending( const Tomahawk::result_ptr& result ); + void reallyStartSending( const Tomahawk::result_ptr& result, QSharedPointer< QIODevice >& io ); //only called back from startSending void sendSome(); void showStats( qint64 tx, qint64 rx ); diff --git a/src/libtomahawk/resolvers/QtScriptResolver.cpp b/src/libtomahawk/resolvers/QtScriptResolver.cpp index 822169788..678903c6f 100644 --- a/src/libtomahawk/resolvers/QtScriptResolver.cpp +++ b/src/libtomahawk/resolvers/QtScriptResolver.cpp @@ -57,6 +57,7 @@ QtScriptResolverHelper::QtScriptResolverHelper( const QString& scriptPath, QtScriptResolver* parent ) : QObject( parent ) + , m_urlCallbackIsAsync( false ) { m_scriptPath = scriptPath; m_resolver = parent; @@ -291,9 +292,15 @@ QtScriptResolverHelper::md5( const QByteArray& input ) void -QtScriptResolverHelper::addCustomUrlHandler( const QString& protocol, const QString& callbackFuncName ) +QtScriptResolverHelper::addCustomUrlHandler( const QString& protocol, + const QString& callbackFuncName, + const QString& isAsynchronous ) { - boost::function(Tomahawk::result_ptr)> fac = boost::bind( &QtScriptResolverHelper::customIODeviceFactory, this, _1 ); + m_urlCallbackIsAsync = ( isAsynchronous.toLower() == "true" ) ? true : false; + + boost::function< void( const Tomahawk::result_ptr&, + boost::function< void( QSharedPointer< QIODevice >& ) > )> fac = + boost::bind( &QtScriptResolverHelper::customIODeviceFactory, this, _1, _2 ); Servent::instance()->registerIODeviceFactory( protocol, fac ); m_urlCallback = callbackFuncName; @@ -314,22 +321,66 @@ QtScriptResolverHelper::base64Decode( const QByteArray& input ) } -QSharedPointer< QIODevice > -QtScriptResolverHelper::customIODeviceFactory( const Tomahawk::result_ptr& result ) +void +QtScriptResolverHelper::customIODeviceFactory( const Tomahawk::result_ptr& result, + boost::function< void( QSharedPointer< QIODevice >& ) > callback ) { - QString getUrl = QString( "Tomahawk.resolver.instance.%1( '%2' );" ).arg( m_urlCallback ) - .arg( QString( QUrl( result->url() ).toEncoded() ) ); + //can be sync or async + QString origResultUrl = QString( QUrl( result->url() ).toEncoded() ); - QString urlStr = m_resolver->m_engine->mainFrame()->evaluateJavaScript( getUrl ).toString(); + if ( m_urlCallbackIsAsync ) + { + QString qid = uuid(); + QString getUrl = QString( "Tomahawk.resolver.instance.%1( '%2', '%3' );" ).arg( m_urlCallback ) + .arg( qid ) + .arg( origResultUrl ); - if ( urlStr.isEmpty() ) - return QSharedPointer< QIODevice >(); + m_streamCallbacks.insert( qid, callback ); + m_resolver->m_engine->mainFrame()->evaluateJavaScript( getUrl ); + } + else + { + QString getUrl = QString( "Tomahawk.resolver.instance.%1( '%2' );" ).arg( m_urlCallback ) + .arg( origResultUrl ); - QUrl url = QUrl::fromEncoded( urlStr.toUtf8() ); + QString urlStr = m_resolver->m_engine->mainFrame()->evaluateJavaScript( getUrl ).toString(); + + returnStreamUrl( urlStr, callback ); + } +} + + +void +QtScriptResolverHelper::reportStreamUrl( const QString& qid, + const QString& streamUrl ) +{ + if ( !m_streamCallbacks.contains( qid ) ) + return; + + boost::function< void( QSharedPointer< QIODevice >& ) > callback = m_streamCallbacks.take( qid ); + + returnStreamUrl( streamUrl, callback ); +} + + +void +QtScriptResolverHelper::returnStreamUrl( const QString& streamUrl, boost::function< void( QSharedPointer< QIODevice >& ) > callback ) +{ + QSharedPointer< QIODevice > sp; + if ( streamUrl.isEmpty() ) + { + callback( sp ); + return; + } + + QUrl url = QUrl::fromEncoded( streamUrl.toUtf8() ); QNetworkRequest req( url ); tDebug() << "Creating a QNetowrkReply with url:" << req.url().toString(); QNetworkReply* reply = TomahawkUtils::nam()->get( req ); - return QSharedPointer( reply, &QObject::deleteLater ); + + //boost::functions cannot accept temporaries as parameters + sp = QSharedPointer< QIODevice >( reply, &QObject::deleteLater ); + callback( sp ); } diff --git a/src/libtomahawk/resolvers/QtScriptResolver.h b/src/libtomahawk/resolvers/QtScriptResolver.h index 4156bbe71..70f6472f5 100644 --- a/src/libtomahawk/resolvers/QtScriptResolver.h +++ b/src/libtomahawk/resolvers/QtScriptResolver.h @@ -54,12 +54,14 @@ public: Q_INVOKABLE QString hmac( const QByteArray& key, const QByteArray& input ); Q_INVOKABLE QString md5( const QByteArray& input ); - Q_INVOKABLE void addCustomUrlHandler( const QString& protocol, const QString& callbackFuncName ); + Q_INVOKABLE void addCustomUrlHandler( const QString& protocol, const QString& callbackFuncName, const QString& isAsynchronous = "false" ); + Q_INVOKABLE void reportStreamUrl( const QString& qid, const QString& streamUrl ); Q_INVOKABLE QByteArray base64Encode( const QByteArray& input ); Q_INVOKABLE QByteArray base64Decode( const QByteArray& input ); - QSharedPointer customIODeviceFactory( const Tomahawk::result_ptr& result ); + void customIODeviceFactory( const Tomahawk::result_ptr& result, + boost::function< void( QSharedPointer< QIODevice >& ) > callback ); // async public slots: QByteArray readRaw( const QString& fileName ); @@ -81,7 +83,10 @@ public slots: void reportCapabilities( const QVariant& capabilities ); private: + void returnStreamUrl( const QString& streamUrl, boost::function< void( QSharedPointer< QIODevice >& ) > callback ); QString m_scriptPath, m_urlCallback; + QHash< QString, boost::function< void( QSharedPointer< QIODevice >& ) > > m_streamCallbacks; + bool m_urlCallbackIsAsync; QVariantMap m_resolverConfig; QtScriptResolver* m_resolver; #ifdef QCA2_FOUND diff --git a/src/web/Api_v1.cpp b/src/web/Api_v1.cpp index 9ac84660c..b24555339 100644 --- a/src/web/Api_v1.cpp +++ b/src/web/Api_v1.cpp @@ -3,6 +3,7 @@ * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Jeff Mitchell * Copyright 2010-2011, Leo Franchi + * Copyright 2013, Teo Mrnjavac * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,6 +31,8 @@ #include "Pipeline.h" #include "Source.h" +#include + #include @@ -189,8 +192,16 @@ Api_v1::sid( QxtWebRequestEvent* event, QString unused ) return send404( event ); } - QSharedPointer iodev = Servent::instance()->getIODeviceForUrl( rp ); - if ( iodev.isNull() ) + boost::function< void ( QSharedPointer< QIODevice >& ) > callback = + boost::bind( &Api_v1::processSid, this, event, rp, _1 ); + Servent::instance()->getIODeviceForUrl( rp, callback ); +} + + +void +Api_v1::processSid( QxtWebRequestEvent* event, Tomahawk::result_ptr& rp, QSharedPointer< QIODevice >& iodev ) +{ + if ( !iodev || iodev.isNull() ) { return send404( event ); // 503? } diff --git a/src/web/Api_v1.h b/src/web/Api_v1.h index 4ad4c88cf..06cb411dc 100644 --- a/src/web/Api_v1.h +++ b/src/web/Api_v1.h @@ -2,6 +2,7 @@ * * Copyright 2010-2011, Christian Muehlhaeuser * Copyright 2010-2011, Leo Franchi + * Copyright 2013, Teo Mrnjavac * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,6 +37,12 @@ #include #include +namespace Tomahawk +{ + class Result; + typedef QSharedPointer< Result > result_ptr; +} + class Api_v1 : public QxtWebSlotService { Q_OBJECT @@ -70,6 +77,7 @@ public slots: void index( QxtWebRequestEvent* event ); private: + void processSid( QxtWebRequestEvent* event, Tomahawk::result_ptr&, QSharedPointer< QIODevice >& ); QxtWebRequestEvent* m_storedEvent; };