1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-08-01 11:50:37 +02:00

Merge pull request #168 from tomahawk-player/asynciodevices

Allow asynchronous requests of stream URLs from resolvers.
This commit is contained in:
Christian Muehlhaeuser
2013-03-22 18:17:40 -07:00
10 changed files with 202 additions and 68 deletions

View File

@@ -2,6 +2,7 @@
*
* Copyright 2010-2012, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2013, Teo Mrnjavac <teo@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
@@ -39,6 +40,8 @@
#include "utils/Logger.h"
#include "playlist/SingleTrackPlaylistInterface.h"
#include <boost/bind.hpp>
#include <QtCore/QUrl>
#include <QDir>
#include <QtNetwork/QNetworkReply>
@@ -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<QIODevice> 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;
}

View File

@@ -2,6 +2,7 @@
*
* Copyright 2010-2012, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2013, Teo Mrnjavac <teo@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
@@ -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();

View File

@@ -2,6 +2,7 @@
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2013, Teo Mrnjavac <teo@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
@@ -75,17 +76,18 @@ Servent::Servent( QObject* parent )
setProxy( QNetworkProxy::NoProxy );
{
boost::function<QSharedPointer<QIODevice>(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<QSharedPointer<QIODevice>(result_ptr)> fac = boost::bind( &Servent::remoteIODeviceFactory, this, _1 );
IODeviceFactoryFunc fac = boost::bind( &Servent::remoteIODeviceFactory, this, _1, _2 );
this->registerIODeviceFactory( "servent", fac );
}
{
boost::function<QSharedPointer<QIODevice>(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<QIODevice>
Servent::remoteIODeviceFactory( const result_ptr& result )
void
Servent::remoteIODeviceFactory( const Tomahawk::result_ptr& result,
boost::function< void ( QSharedPointer< QIODevice >& ) > callback )
{
QSharedPointer<QIODevice> 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<QSharedPointer<QIODevice>(Tomahawk::result_ptr)> fac )
Servent::registerIODeviceFactory( const QString &proto,
IODeviceFactoryFunc fac )
{
m_iofactories.insert( proto, fac );
}
QSharedPointer<QIODevice>
Servent::getIODeviceForUrl( const Tomahawk::result_ptr& result )
void
Servent::getIODeviceForUrl( const Tomahawk::result_ptr& result,
boost::function< void ( QSharedPointer< QIODevice >& ) > callback )
{
QSharedPointer<QIODevice> 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<QIODevice>
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<QIODevice>( io );
//boost::functions cannot accept temporaries as parameters
QSharedPointer< QIODevice > sp = QSharedPointer<QIODevice>( io );
callback( sp );
}
QSharedPointer<QIODevice>
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<QIODevice>( reply, &QObject::deleteLater );
//boost::functions cannot accept temporaries as parameters
QSharedPointer< QIODevice > sp = QSharedPointer< QIODevice >( reply, &QObject::deleteLater );
callback( sp );
}

View File

@@ -2,6 +2,7 @@
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2013, Teo Mrnjavac <teo@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,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;

View File

@@ -2,6 +2,7 @@
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2013, Teo Mrnjavac <teo@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
@@ -19,10 +20,7 @@
#include "StreamConnection.h"
#include <QFile>
#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 <boost/bind.hpp>
#include <QFile>
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<QIODevice> 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();

View File

@@ -2,6 +2,7 @@
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2013, Teo Mrnjavac <teo@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
@@ -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 );

View File

@@ -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<QSharedPointer<QIODevice>(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<QIODevice>( reply, &QObject::deleteLater );
//boost::functions cannot accept temporaries as parameters
sp = QSharedPointer< QIODevice >( reply, &QObject::deleteLater );
callback( sp );
}

View File

@@ -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<QIODevice> 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

View File

@@ -3,6 +3,7 @@
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2013, Teo Mrnjavac <teo@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
@@ -30,6 +31,8 @@
#include "Pipeline.h"
#include "Source.h"
#include <boost/bind.hpp>
#include <QHash>
@@ -189,8 +192,16 @@ Api_v1::sid( QxtWebRequestEvent* event, QString unused )
return send404( event );
}
QSharedPointer<QIODevice> 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?
}

View File

@@ -2,6 +2,7 @@
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2013, Teo Mrnjavac <teo@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
@@ -36,6 +37,12 @@
#include <QSharedPointer>
#include <QStringList>
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;
};