PageRenderTime 55ms CodeModel.GetById 21ms RepoModel.GetById 0ms 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
Possible License(s): LGPL-2.1, BSD-3-Clause, GPL-3.0, GPL-2.0
  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. #include "InfoSystemWorker.h"
  21. #include "utils/Logger.h"
  22. #include "utils/ShortLinkHelper.h"
  23. #include "utils/TomahawkUtils.h"
  24. #include "config.h"
  25. #include "utils/LinkGenerator.h"
  26. #include "InfoSystemCache.h"
  27. #include "PlaylistEntry.h"
  28. #include "utils/TomahawkUtils.h"
  29. #include "utils/Logger.h"
  30. #include "utils/PluginLoader.h"
  31. #include "utils/Closure.h"
  32. #include "Source.h"
  33. #include <QCoreApplication>
  34. #include <QNetworkConfiguration>
  35. #include <QNetworkProxy>
  36. namespace Tomahawk
  37. {
  38. namespace InfoSystem
  39. {
  40. InfoSystemWorker::InfoSystemWorker()
  41. : QObject()
  42. , m_cache( 0 )
  43. {
  44. tDebug() << Q_FUNC_INFO;
  45. m_checkTimeoutsTimer.setInterval( 1000 );
  46. m_checkTimeoutsTimer.setSingleShot( false );
  47. connect( &m_checkTimeoutsTimer, SIGNAL( timeout() ), SLOT( checkTimeoutsTimerFired() ) );
  48. m_checkTimeoutsTimer.start();
  49. }
  50. InfoSystemWorker::~InfoSystemWorker()
  51. {
  52. tDebug() << Q_FUNC_INFO;
  53. }
  54. const QList< InfoPluginPtr >
  55. InfoSystemWorker::plugins() const
  56. {
  57. return m_plugins;
  58. }
  59. void
  60. InfoSystemWorker::init( Tomahawk::InfoSystem::InfoSystemCache* cache )
  61. {
  62. tDebug() << Q_FUNC_INFO;
  63. m_shortLinksWaiting = 0;
  64. m_cache = cache;
  65. loadInfoPlugins();
  66. }
  67. void
  68. InfoSystemWorker::addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin )
  69. {
  70. tDebug() << Q_FUNC_INFO << plugin;
  71. foreach ( InfoPluginPtr ptr, m_plugins )
  72. {
  73. if ( ptr == plugin )
  74. {
  75. tDebug() << Q_FUNC_INFO << "This plugin is already added to the infosystem.";
  76. return;
  77. }
  78. }
  79. if ( plugin.isNull() )
  80. {
  81. tDebug() << Q_FUNC_INFO << "passed-in plugin is null";
  82. return;
  83. }
  84. plugin.data()->moveToThread( this->thread() );
  85. m_plugins.append( plugin );
  86. registerInfoTypes( plugin, plugin.data()->supportedGetTypes(), plugin.data()->supportedPushTypes() );
  87. connect(
  88. plugin.data(),
  89. SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
  90. this,
  91. SLOT( infoSlot( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ),
  92. Qt::QueuedConnection
  93. );
  94. connect(
  95. plugin.data(),
  96. SIGNAL( getCachedInfo( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ),
  97. m_cache,
  98. SLOT( getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ),
  99. Qt::QueuedConnection
  100. );
  101. connect(
  102. plugin.data(),
  103. SIGNAL( updateCache( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ),
  104. m_cache,
  105. SLOT( updateCacheSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ),
  106. Qt::QueuedConnection
  107. );
  108. QMetaObject::invokeMethod( plugin.data(), "init", Qt::QueuedConnection );
  109. emit updatedSupportedGetTypes( QSet< InfoType >::fromList( m_infoGetMap.keys() ) );
  110. emit updatedSupportedPushTypes( QSet< InfoType >::fromList( m_infoPushMap.keys() ) );
  111. }
  112. void
  113. InfoSystemWorker::removeInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin )
  114. {
  115. tDebug() << Q_FUNC_INFO << plugin;
  116. if ( plugin.isNull() )
  117. {
  118. tDebug() << Q_FUNC_INFO << "passed-in plugin is null";
  119. return;
  120. }
  121. foreach ( InfoPluginPtr ptr, m_plugins )
  122. {
  123. if ( ptr == plugin )
  124. break;
  125. tDebug() << Q_FUNC_INFO << "This plugin does not exist in the infosystem.";
  126. return;
  127. }
  128. m_plugins.removeOne( plugin );
  129. deregisterInfoTypes( plugin, plugin.data()->supportedGetTypes(), plugin.data()->supportedPushTypes() );
  130. }
  131. void
  132. InfoSystemWorker::loadInfoPlugins()
  133. {
  134. QHash< QString, QObject* > plugins = Tomahawk::Utils::PluginLoader( "infoplugin" ).loadPlugins();
  135. foreach ( QObject* plugin, plugins.values() )
  136. {
  137. InfoPlugin* infoPlugin = qobject_cast< InfoPlugin* >( plugin );
  138. if ( infoPlugin )
  139. {
  140. tDebug() << Q_FUNC_INFO << "Loaded info plugin:" << plugins.key( plugin );
  141. infoPlugin->setFriendlyName( plugins.key( plugin ) );
  142. addInfoPlugin( InfoPluginPtr( infoPlugin ) );
  143. }
  144. else
  145. {
  146. tDebug() << Q_FUNC_INFO << "Loaded invalid plugin:" << plugins.key( plugin );
  147. }
  148. }
  149. }
  150. void
  151. InfoSystemWorker::registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType >& getTypes, const QSet< InfoType >& pushTypes )
  152. {
  153. Q_FOREACH( InfoType type, getTypes )
  154. m_infoGetMap[type].append( plugin );
  155. Q_FOREACH( InfoType type, pushTypes )
  156. m_infoPushMap[type].append( plugin );
  157. }
  158. void
  159. InfoSystemWorker::deregisterInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType >& getTypes, const QSet< InfoType >& pushTypes )
  160. {
  161. Q_FOREACH( InfoType type, getTypes )
  162. m_infoGetMap[type].removeOne( plugin );
  163. Q_FOREACH( InfoType type, pushTypes )
  164. m_infoPushMap[type].removeOne( plugin );
  165. }
  166. QList< InfoPluginPtr >
  167. InfoSystemWorker::determineOrderedMatches( const InfoType type ) const
  168. {
  169. //Dummy function for now that returns the various items in the QSet; at some point this will
  170. //probably need to support ordering based on the data source
  171. QList< InfoPluginPtr > providers;
  172. Q_FOREACH( InfoPluginPtr ptr, m_infoGetMap[type] )
  173. providers << ptr;
  174. return providers;
  175. }
  176. void
  177. InfoSystemWorker::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData )
  178. {
  179. //qDebug() << Q_FUNC_INFO << "type is " << requestData.type << " and allSources = " << (allSources ? "true" : "false" );
  180. QList< InfoPluginPtr > providers = determineOrderedMatches( requestData.type );
  181. if ( providers.isEmpty() )
  182. {
  183. // We're not ready yet, retry this request in a bit
  184. QTimer* timer = new QTimer();
  185. timer->setInterval( 500 );
  186. timer->setSingleShot( true );
  187. NewClosure( timer, SIGNAL( timeout() ), this, SLOT( getInfo( Tomahawk::InfoSystem::InfoRequestData ) ), requestData );
  188. timer->start();
  189. /* emit info( requestData, QVariant() );
  190. checkFinished( requestData );*/
  191. return;
  192. }
  193. if ( !requestData.allSources )
  194. providers = QList< InfoPluginPtr >( providers.mid( 0, 1 ) );
  195. bool foundOne = false;
  196. foreach ( InfoPluginPtr ptr, providers )
  197. {
  198. if ( !ptr )
  199. continue;
  200. // tDebug() << "Dispatching to worker:" << ptr->friendlyName() << requestData.type;
  201. foundOne = true;
  202. if ( requestData.allSources || m_savedRequestMap.contains( requestData.requestId ) )
  203. {
  204. if ( m_savedRequestMap.contains( requestData.requestId ) )
  205. tDebug() << Q_FUNC_INFO << "Warning: reassigning requestId because it already exists";
  206. requestData.internalId = TomahawkUtils::infosystemRequestId();
  207. }
  208. else
  209. requestData.internalId = requestData.requestId;
  210. quint64 requestId = requestData.internalId;
  211. m_requestSatisfiedMap[ requestId ] = false;
  212. if ( requestData.timeoutMillis != 0 )
  213. {
  214. qint64 currMs = QDateTime::currentMSecsSinceEpoch();
  215. m_timeRequestMapper.insert( currMs + requestData.timeoutMillis, requestId );
  216. }
  217. // qDebug() << "Assigning request with requestId" << requestId << "and type" << requestData.type;
  218. m_dataTracker[ requestData.caller ][ requestData.type ] = m_dataTracker[ requestData.caller ][ requestData.type ] + 1;
  219. // qDebug() << "Current count in dataTracker for target" << requestData.caller << "and type" << requestData.type << "is" << m_dataTracker[ requestData.caller ][ requestData.type ];
  220. InfoRequestData* data = new InfoRequestData;
  221. data->caller = requestData.caller;
  222. data->type = requestData.type;
  223. data->input = requestData.input;
  224. data->customData = requestData.customData;
  225. m_savedRequestMap[ requestId ] = data;
  226. QMetaObject::invokeMethod( ptr.data(), "getInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoRequestData, requestData ) );
  227. }
  228. if ( !foundOne )
  229. {
  230. emit info( requestData, QVariant() );
  231. checkFinished( requestData );
  232. }
  233. }
  234. void
  235. InfoSystemWorker::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData )
  236. {
  237. tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "type is " << pushData.type << "number of matching plugins: " << m_infoPushMap[ pushData.type ].size();
  238. Q_FOREACH( InfoPluginPtr ptr, m_infoPushMap[ pushData.type ] )
  239. {
  240. if( ptr )
  241. QMetaObject::invokeMethod( ptr.data(), "pushInfo", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPushData, pushData ) );
  242. }
  243. }
  244. void
  245. InfoSystemWorker::infoSlot( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
  246. {
  247. // qDebug() << Q_FUNC_INFO << "with requestId" << requestId;
  248. quint64 requestId = requestData.internalId;
  249. if ( m_dataTracker[ requestData.caller ][ requestData.type ] == 0 )
  250. {
  251. // qDebug() << Q_FUNC_INFO << "Caller was not waiting for that type of data!";
  252. return;
  253. }
  254. if ( !m_requestSatisfiedMap.contains( requestId ) || m_requestSatisfiedMap[ requestId ] )
  255. {
  256. // qDebug() << Q_FUNC_INFO << "Request was already taken care of!";
  257. return;
  258. }
  259. m_requestSatisfiedMap[ requestId ] = true;
  260. emit info( requestData, output );
  261. m_dataTracker[ requestData.caller ][ requestData.type ] = m_dataTracker[ requestData.caller ][ requestData.type ] - 1;
  262. // qDebug() << "Current count in dataTracker for target" << requestData.caller << "and type" << requestData.type << "is" << m_dataTracker[ requestData.caller ][ requestData.type ];
  263. delete m_savedRequestMap[ requestId ];
  264. m_savedRequestMap.remove( requestId );
  265. checkFinished( requestData );
  266. }
  267. void
  268. InfoSystemWorker::checkFinished( const Tomahawk::InfoSystem::InfoRequestData &requestData )
  269. {
  270. if ( m_dataTracker[ requestData.caller ][ requestData.type ] == 0 )
  271. emit finished( requestData.caller, requestData.type );
  272. Q_FOREACH( InfoType testtype, m_dataTracker[ requestData.caller ].keys() )
  273. {
  274. if ( m_dataTracker[ requestData.caller ][ testtype ] != 0 )
  275. return;
  276. }
  277. // qDebug() << "Emitting finished with target" << target;
  278. emit finished( requestData.caller );
  279. }
  280. void
  281. InfoSystemWorker::checkTimeoutsTimerFired()
  282. {
  283. qint64 currTime = QDateTime::currentMSecsSinceEpoch();
  284. Q_FOREACH( qint64 time, m_timeRequestMapper.keys() )
  285. {
  286. Q_FOREACH( quint64 requestId, m_timeRequestMapper.values( time ) )
  287. {
  288. if ( time < currTime )
  289. {
  290. if ( m_requestSatisfiedMap[ requestId ] )
  291. {
  292. // qDebug() << Q_FUNC_INFO << "Removing mapping of" << requestId << "which expired at time" << time << "and was already satisfied";
  293. m_timeRequestMapper.remove( time, requestId );
  294. if ( !m_timeRequestMapper.count( time ) )
  295. m_timeRequestMapper.remove( time );
  296. continue;
  297. }
  298. //doh, timed out
  299. // qDebug() << Q_FUNC_INFO << "Doh, timed out for requestId" << requestId;
  300. InfoRequestData *savedData = m_savedRequestMap[ requestId ];
  301. InfoRequestData returnData;
  302. returnData.caller = savedData->caller;
  303. returnData.type = savedData->type;
  304. returnData.input = savedData->input;
  305. returnData.customData = savedData->customData;
  306. emit info( returnData, QVariant() );
  307. delete savedData;
  308. m_savedRequestMap.remove( requestId );
  309. m_dataTracker[ returnData.caller ][ returnData.type ] = m_dataTracker[ returnData.caller ][ returnData.type ] - 1;
  310. // qDebug() << "Current count in dataTracker for target" << returnData.caller << "is" << m_dataTracker[ returnData.caller ][ returnData.type ];
  311. m_requestSatisfiedMap[ requestId ] = true;
  312. m_timeRequestMapper.remove( time, requestId );
  313. if ( !m_timeRequestMapper.count( time ) )
  314. m_timeRequestMapper.remove( time );
  315. checkFinished( returnData );
  316. }
  317. else
  318. {
  319. //we've caught up, the remaining requets still have time to work
  320. return;
  321. }
  322. }
  323. }
  324. }
  325. } //namespace InfoSystem
  326. } //namespace Tomahawk