PageRenderTime 206ms CodeModel.GetById 60ms app.highlight 92ms RepoModel.GetById 49ms app.codeStats 0ms

/src/libtomahawk/infosystem/InfoSystemWorker.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 397 lines | 288 code | 71 blank | 38 comment | 29 complexity | 645a5f73fde0d2c9c7a4390efd338388 MD5 | raw file
  1/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
  2 *
  3 *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
  4 *   Copyright 2012       Leo Franchi <lfranchi@kde.org>
  5 *   Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org>
  6 *
  7 *   Tomahawk is free software: you can redistribute it and/or modify
  8 *   it under the terms of the GNU General Public License as published by
  9 *   the Free Software Foundation, either version 3 of the License, or
 10 *   (at your option) any later version.
 11 *
 12 *   Tomahawk is distributed in the hope that it will be useful,
 13 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 14 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 15 *   GNU General Public License for more details.
 16 *
 17 *   You should have received a copy of the GNU General Public License
 18 *   along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
 19 */
 20
 21#include "InfoSystemWorker.h"
 22
 23#include "utils/Logger.h"
 24#include "utils/ShortLinkHelper.h"
 25#include "utils/TomahawkUtils.h"
 26#include "config.h"
 27#include "utils/LinkGenerator.h"
 28#include "InfoSystemCache.h"
 29#include "PlaylistEntry.h"
 30#include "utils/TomahawkUtils.h"
 31#include "utils/Logger.h"
 32#include "utils/PluginLoader.h"
 33#include "utils/Closure.h"
 34#include "Source.h"
 35
 36#include <QCoreApplication>
 37#include <QNetworkConfiguration>
 38#include <QNetworkProxy>
 39
 40namespace Tomahawk
 41{
 42
 43namespace InfoSystem
 44{
 45
 46InfoSystemWorker::InfoSystemWorker()
 47    : QObject()
 48    , m_cache( 0 )
 49{
 50    tDebug() << Q_FUNC_INFO;
 51
 52    m_checkTimeoutsTimer.setInterval( 1000 );
 53    m_checkTimeoutsTimer.setSingleShot( false );
 54    connect( &m_checkTimeoutsTimer, SIGNAL( timeout() ), SLOT( checkTimeoutsTimerFired() ) );
 55    m_checkTimeoutsTimer.start();
 56}
 57
 58
 59InfoSystemWorker::~InfoSystemWorker()
 60{
 61    tDebug() << Q_FUNC_INFO;
 62}
 63
 64
 65const QList< InfoPluginPtr >
 66InfoSystemWorker::plugins() const
 67{
 68    return m_plugins;
 69}
 70
 71
 72void
 73InfoSystemWorker::init( Tomahawk::InfoSystem::InfoSystemCache* cache )
 74{
 75    tDebug() << Q_FUNC_INFO;
 76    m_shortLinksWaiting = 0;
 77    m_cache = cache;
 78
 79    loadInfoPlugins();
 80}
 81
 82
 83void
 84InfoSystemWorker::addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin )
 85{
 86    tDebug() << Q_FUNC_INFO << plugin;
 87    foreach ( InfoPluginPtr ptr, m_plugins )
 88    {
 89        if ( ptr == plugin )
 90        {
 91            tDebug() << Q_FUNC_INFO << "This plugin is already added to the infosystem.";
 92            return;
 93        }
 94    }
 95
 96    if ( plugin.isNull() )
 97    {
 98        tDebug() << Q_FUNC_INFO << "passed-in plugin is null";
 99        return;
100    }
101
102    plugin.data()->moveToThread( this->thread() );
103    m_plugins.append( plugin );
104    registerInfoTypes( plugin, plugin.data()->supportedGetTypes(), plugin.data()->supportedPushTypes() );
105
106    connect(
107        plugin.data(),
108            SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
109            this,
110            SLOT( infoSlot( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
111            Qt::QueuedConnection
112    );
113
114    connect(
115        plugin.data(),
116            SIGNAL( getCachedInfo( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ),
117            m_cache,
118            SLOT( getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ),
119            Qt::QueuedConnection
120    );
121    connect(
122        plugin.data(),
123            SIGNAL( updateCache( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ),
124            m_cache,
125            SLOT( updateCacheSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ),
126            Qt::QueuedConnection
127    );
128
129    QMetaObject::invokeMethod( plugin.data(), "init", Qt::QueuedConnection );
130
131    emit updatedSupportedGetTypes( QSet< InfoType >::fromList( m_infoGetMap.keys() ) );
132    emit updatedSupportedPushTypes( QSet< InfoType >::fromList( m_infoPushMap.keys() ) );
133}
134
135
136void
137InfoSystemWorker::removeInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin )
138{
139    tDebug() << Q_FUNC_INFO << plugin;
140
141    if ( plugin.isNull() )
142    {
143        tDebug() << Q_FUNC_INFO << "passed-in plugin is null";
144        return;
145    }
146
147    foreach ( InfoPluginPtr ptr, m_plugins )
148    {
149        if ( ptr == plugin )
150            break;
151
152        tDebug() << Q_FUNC_INFO << "This plugin does not exist in the infosystem.";
153        return;
154    }
155
156    m_plugins.removeOne( plugin );
157    deregisterInfoTypes( plugin, plugin.data()->supportedGetTypes(), plugin.data()->supportedPushTypes() );
158}
159
160
161void
162InfoSystemWorker::loadInfoPlugins()
163{
164    QHash< QString, QObject* > plugins = Tomahawk::Utils::PluginLoader( "infoplugin" ).loadPlugins();
165    foreach ( QObject* plugin, plugins.values() )
166    {
167        InfoPlugin* infoPlugin = qobject_cast< InfoPlugin* >( plugin );
168        if ( infoPlugin )
169        {
170            tDebug() << Q_FUNC_INFO << "Loaded info plugin:" << plugins.key( plugin );
171            infoPlugin->setFriendlyName( plugins.key( plugin ) );
172            addInfoPlugin( InfoPluginPtr( infoPlugin ) );
173        }
174        else
175        {
176            tDebug() << Q_FUNC_INFO << "Loaded invalid plugin:" << plugins.key( plugin );
177        }
178    }
179}
180
181
182void
183InfoSystemWorker::registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType >& getTypes, const QSet< InfoType >& pushTypes )
184{
185    Q_FOREACH( InfoType type, getTypes )
186        m_infoGetMap[type].append( plugin );
187    Q_FOREACH( InfoType type, pushTypes )
188        m_infoPushMap[type].append( plugin );
189}
190
191
192void
193InfoSystemWorker::deregisterInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType >& getTypes, const QSet< InfoType >& pushTypes )
194{
195    Q_FOREACH( InfoType type, getTypes )
196        m_infoGetMap[type].removeOne( plugin );
197    Q_FOREACH( InfoType type, pushTypes )
198        m_infoPushMap[type].removeOne( plugin );
199}
200
201
202QList< InfoPluginPtr >
203InfoSystemWorker::determineOrderedMatches( const InfoType type ) const
204{
205    //Dummy function for now that returns the various items in the QSet; at some point this will
206    //probably need to support ordering based on the data source
207    QList< InfoPluginPtr > providers;
208    Q_FOREACH( InfoPluginPtr ptr, m_infoGetMap[type] )
209        providers << ptr;
210    return providers;
211}
212
213
214void
215InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
216{
217    //qDebug() << Q_FUNC_INFO << "type is " << requestData.type << " and allSources = " << (allSources ? "true" : "false" );
218
219    QList< InfoPluginPtr > providers = determineOrderedMatches( requestData.type );
220    if ( providers.isEmpty() )
221    {
222        // We're not ready yet, retry this request in a bit
223        QTimer* timer = new QTimer();
224        timer->setInterval( 500 );
225        timer->setSingleShot( true );
226        NewClosure( timer, SIGNAL( timeout() ), this, SLOT( getInfo( Tomahawk::InfoSystem::InfoRequestData ) ), requestData );
227        timer->start();
228
229/*        emit info( requestData, QVariant() );
230        checkFinished( requestData );*/
231        return;
232    }
233
234    if ( !requestData.allSources )
235        providers = QList< InfoPluginPtr >( providers.mid( 0, 1 ) );
236
237    bool foundOne = false;
238    foreach ( InfoPluginPtr ptr, providers )
239    {
240        if ( !ptr )
241            continue;
242        // tDebug() << "Dispatching to worker:" << ptr->friendlyName() << requestData.type;
243
244        foundOne = true;
245
246        if ( requestData.allSources || m_savedRequestMap.contains( requestData.requestId ) )
247        {
248            if ( m_savedRequestMap.contains( requestData.requestId ) )
249                tDebug() << Q_FUNC_INFO << "Warning: reassigning requestId because it already exists";
250            requestData.internalId = TomahawkUtils::infosystemRequestId();
251        }
252        else
253            requestData.internalId = requestData.requestId;
254
255        quint64 requestId = requestData.internalId;
256        m_requestSatisfiedMap[ requestId ] = false;
257        if ( requestData.timeoutMillis != 0 )
258        {
259            qint64 currMs = QDateTime::currentMSecsSinceEpoch();
260            m_timeRequestMapper.insert( currMs + requestData.timeoutMillis, requestId );
261        }
262    //    qDebug() << "Assigning request with requestId" << requestId << "and type" << requestData.type;
263        m_dataTracker[ requestData.caller ][ requestData.type ] = m_dataTracker[ requestData.caller ][ requestData.type ] + 1;
264    //    qDebug() << "Current count in dataTracker for target" << requestData.caller << "and type" << requestData.type << "is" << m_dataTracker[ requestData.caller ][ requestData.type ];
265
266        InfoRequestData* data = new InfoRequestData;
267        data->caller = requestData.caller;
268        data->type = requestData.type;
269        data->input = requestData.input;
270        data->customData = requestData.customData;
271        m_savedRequestMap[ requestId ] = data;
272
273        QMetaObject::invokeMethod( ptr.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) );
274    }
275
276    if ( !foundOne )
277    {
278        emit info( requestData, QVariant() );
279        checkFinished( requestData );
280    }
281}
282
283
284void
285InfoSystemWorker::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData )
286{
287    tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "type is " << pushData.type << "number of matching plugins: " << m_infoPushMap[ pushData.type ].size();
288
289    Q_FOREACH( InfoPluginPtr ptr, m_infoPushMap[ pushData.type ] )
290    {
291        if( ptr )
292            QMetaObject::invokeMethod( ptr.data(), "pushInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPushData, pushData ) );
293    }
294}
295
296
297void
298InfoSystemWorker::infoSlot( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
299{
300//    qDebug() << Q_FUNC_INFO << "with requestId" << requestId;
301
302    quint64 requestId = requestData.internalId;
303
304    if ( m_dataTracker[ requestData.caller ][ requestData.type ] == 0 )
305    {
306//        qDebug() << Q_FUNC_INFO << "Caller was not waiting for that type of data!";
307        return;
308    }
309    if ( !m_requestSatisfiedMap.contains( requestId ) || m_requestSatisfiedMap[ requestId ] )
310    {
311//        qDebug() << Q_FUNC_INFO << "Request was already taken care of!";
312        return;
313    }
314
315    m_requestSatisfiedMap[ requestId ] = true;
316    emit info( requestData, output );
317
318    m_dataTracker[ requestData.caller ][ requestData.type ] = m_dataTracker[ requestData.caller ][ requestData.type ] - 1;
319//    qDebug() << "Current count in dataTracker for target" << requestData.caller << "and type" << requestData.type << "is" << m_dataTracker[ requestData.caller ][ requestData.type ];
320    delete m_savedRequestMap[ requestId ];
321    m_savedRequestMap.remove( requestId );
322    checkFinished( requestData );
323}
324
325
326void
327InfoSystemWorker::checkFinished( const Tomahawk::InfoSystem::InfoRequestData &requestData )
328{
329    if ( m_dataTracker[ requestData.caller ][ requestData.type ] == 0 )
330        emit finished( requestData.caller, requestData.type );
331
332    Q_FOREACH( InfoType testtype, m_dataTracker[ requestData.caller ].keys() )
333    {
334        if ( m_dataTracker[ requestData.caller ][ testtype ] != 0 )
335            return;
336    }
337//    qDebug() << "Emitting finished with target" << target;
338    emit finished( requestData.caller );
339}
340
341
342void
343InfoSystemWorker::checkTimeoutsTimerFired()
344{
345    qint64 currTime = QDateTime::currentMSecsSinceEpoch();
346    Q_FOREACH( qint64 time, m_timeRequestMapper.keys() )
347    {
348        Q_FOREACH( quint64 requestId, m_timeRequestMapper.values( time ) )
349        {
350            if ( time < currTime )
351            {
352                if ( m_requestSatisfiedMap[ requestId ] )
353                {
354//                    qDebug() << Q_FUNC_INFO << "Removing mapping of" << requestId << "which expired at time" << time << "and was already satisfied";
355                    m_timeRequestMapper.remove( time, requestId );
356                    if ( !m_timeRequestMapper.count( time ) )
357                        m_timeRequestMapper.remove( time );
358                    continue;
359                }
360
361                //doh, timed out
362//                qDebug() << Q_FUNC_INFO << "Doh, timed out for requestId" << requestId;
363                InfoRequestData *savedData = m_savedRequestMap[ requestId ];
364
365                InfoRequestData returnData;
366                returnData.caller = savedData->caller;
367                returnData.type = savedData->type;
368                returnData.input = savedData->input;
369                returnData.customData = savedData->customData;
370                emit info( returnData, QVariant() );
371
372                delete savedData;
373                m_savedRequestMap.remove( requestId );
374
375                m_dataTracker[ returnData.caller ][ returnData.type ] = m_dataTracker[ returnData.caller ][ returnData.type ] - 1;
376//                qDebug() << "Current count in dataTracker for target" << returnData.caller << "is" << m_dataTracker[ returnData.caller ][ returnData.type ];
377
378                m_requestSatisfiedMap[ requestId ] = true;
379                m_timeRequestMapper.remove( time, requestId );
380                if ( !m_timeRequestMapper.count( time ) )
381                    m_timeRequestMapper.remove( time );
382
383                checkFinished( returnData );
384            }
385            else
386            {
387                //we've caught up, the remaining requets still have time to work
388                return;
389            }
390        }
391    }
392}
393
394
395} //namespace InfoSystem
396
397} //namespace Tomahawk