Changeset View
Changeset View
Standalone View
Standalone View
src/covermanager/CoverFetcher.cpp
Show All 26 Lines | |||||
27 | #include "core/meta/Meta.h" | 27 | #include "core/meta/Meta.h" | ||
28 | #include "core/support/Amarok.h" | 28 | #include "core/support/Amarok.h" | ||
29 | #include "core/support/Components.h" | 29 | #include "core/support/Components.h" | ||
30 | #include "core/support/Debug.h" | 30 | #include "core/support/Debug.h" | ||
31 | #include "CoverFetchQueue.h" | 31 | #include "CoverFetchQueue.h" | ||
32 | #include "CoverFoundDialog.h" | 32 | #include "CoverFoundDialog.h" | ||
33 | #include "CoverFetchUnit.h" | 33 | #include "CoverFetchUnit.h" | ||
34 | 34 | | |||
35 | #include <KLocalizedString> | | |||
36 | #include <QUrl> | | |||
37 | | ||||
38 | #include <QBuffer> | 35 | #include <QBuffer> | ||
39 | #include <QImageReader> | 36 | #include <QImageReader> | ||
37 | #include <QThread> | ||||
40 | #include <QUrl> | 38 | #include <QUrl> | ||
41 | 39 | | |||
42 | #include <KConfigGroup> | 40 | #include <KConfigGroup> | ||
43 | #include <KLocalizedString> | 41 | #include <KLocalizedString> | ||
44 | 42 | | |||
45 | CoverFetcher* CoverFetcher::s_instance = 0; | 43 | #include <functional> | ||
44 | #include <thread> | ||||
45 | | ||||
46 | | ||||
47 | CoverFetcher* CoverFetcher::s_instance = nullptr; | ||||
46 | 48 | | |||
47 | CoverFetcher* | 49 | CoverFetcher* | ||
48 | CoverFetcher::instance() | 50 | CoverFetcher::instance() | ||
49 | { | 51 | { | ||
50 | return s_instance ? s_instance : new CoverFetcher(); | 52 | return s_instance ? s_instance : new CoverFetcher(); | ||
51 | } | 53 | } | ||
52 | 54 | | |||
53 | void CoverFetcher::destroy() | 55 | void CoverFetcher::destroy() | ||
54 | { | 56 | { | ||
55 | if( s_instance ) | 57 | if( s_instance ) | ||
56 | { | 58 | { | ||
57 | delete s_instance; | 59 | delete s_instance; | ||
58 | s_instance = 0; | 60 | s_instance = nullptr; | ||
59 | } | 61 | } | ||
60 | } | 62 | } | ||
61 | 63 | | |||
62 | CoverFetcher::CoverFetcher() | 64 | CoverFetcher::CoverFetcher() | ||
63 | : QObject() | 65 | : QObject() | ||
64 | , m_limit( 10 ) | | |||
65 | { | 66 | { | ||
66 | DEBUG_BLOCK | 67 | DEBUG_BLOCK | ||
67 | setObjectName( "CoverFetcher" ); | 68 | setObjectName( "CoverFetcher" ); | ||
68 | qRegisterMetaType<CoverFetchUnit::Ptr>("CoverFetchUnit::Ptr"); | 69 | qRegisterMetaType<CoverFetchUnit::Ptr>("CoverFetchUnit::Ptr"); | ||
69 | 70 | | |||
70 | m_queue = new CoverFetchQueue( this ); | 71 | s_instance = this; | ||
72 | | ||||
73 | m_queueThread = new QThread( this ); | ||||
74 | m_queueThread->start(); | ||||
75 | m_queue = new CoverFetchQueue; | ||||
76 | m_queue->moveToThread( m_queueThread ); | ||||
77 | | ||||
71 | connect( m_queue, &CoverFetchQueue::fetchUnitAdded, | 78 | connect( m_queue, &CoverFetchQueue::fetchUnitAdded, | ||
72 | this, &CoverFetcher::slotFetch ); | 79 | this, &CoverFetcher::slotFetch ); | ||
73 | s_instance = this; | | |||
74 | 80 | | |||
75 | connect( The::networkAccessManager(), &NetworkAccessManagerProxy::requestRedirectedReply, | 81 | connect( The::networkAccessManager(), &NetworkAccessManagerProxy::requestRedirectedReply, | ||
76 | this, &CoverFetcher::fetchRequestRedirected ); | 82 | this, &CoverFetcher::fetchRequestRedirected ); | ||
77 | } | 83 | } | ||
78 | 84 | | |||
79 | CoverFetcher::~CoverFetcher() | 85 | CoverFetcher::~CoverFetcher() | ||
80 | { | 86 | { | ||
87 | m_queue->deleteLater(); | ||||
88 | m_queueThread->quit(); | ||||
89 | m_queueThread->wait(); | ||||
81 | } | 90 | } | ||
82 | 91 | | |||
83 | void | 92 | void | ||
84 | CoverFetcher::manualFetch( Meta::AlbumPtr album ) | 93 | CoverFetcher::manualFetch( Meta::AlbumPtr album ) | ||
85 | { | 94 | { | ||
86 | debug() << QString("Adding interactive cover fetch for: '%1' from %2") | 95 | debug() << QString("Adding interactive cover fetch for: '%1' from %2") | ||
87 | .arg( album->name(), | 96 | .arg( album->name(), | ||
88 | Amarok::config("Cover Fetcher").readEntry("Interactive Image Source", "LastFm") ); | 97 | Amarok::config("Cover Fetcher").readEntry("Interactive Image Source", "LastFm") ); | ||
89 | switch( fetchSource() ) | 98 | switch( fetchSource() ) | ||
90 | { | 99 | { | ||
91 | case CoverFetch::LastFm: | 100 | case CoverFetch::LastFm: | ||
92 | m_queue->add( album, CoverFetch::Interactive, fetchSource() ); | 101 | QTimer::singleShot( 0, m_queue, [=] () { m_queue->add( album, CoverFetch::Interactive, fetchSource() ); } ); | ||
93 | break; | 102 | break; | ||
94 | 103 | | |||
95 | case CoverFetch::Discogs: | 104 | case CoverFetch::Discogs: | ||
96 | case CoverFetch::Google: | 105 | case CoverFetch::Google: | ||
97 | queueQueryForAlbum( album ); | 106 | queueQueryForAlbum( album ); | ||
98 | break; | 107 | break; | ||
99 | 108 | | |||
100 | default: | 109 | default: | ||
101 | break; | 110 | break; | ||
102 | } | 111 | } | ||
103 | } | 112 | } | ||
104 | 113 | | |||
105 | void | 114 | void | ||
106 | CoverFetcher::queueAlbum( Meta::AlbumPtr album ) | 115 | CoverFetcher::queueAlbum( Meta::AlbumPtr album ) | ||
107 | { | 116 | { | ||
108 | if( m_queue->size() > m_limit ) | 117 | QTimer::singleShot( 0, m_queue, [=] () { m_queue->add( album, CoverFetch::Automatic ); } ); | ||
109 | m_queueLater.append( album ); | | |||
110 | else | | |||
111 | m_queue->add( album, CoverFetch::Automatic ); | | |||
112 | debug() << "Queueing automatic cover fetch for:" << album->name(); | 118 | debug() << "Queueing automatic cover fetch for:" << album->name(); | ||
113 | } | 119 | } | ||
114 | 120 | | |||
115 | void | 121 | void | ||
116 | CoverFetcher::queueAlbums( Meta::AlbumList albums ) | 122 | CoverFetcher::queueAlbums( Meta::AlbumList albums ) | ||
117 | { | 123 | { | ||
118 | foreach( Meta::AlbumPtr album, albums ) | 124 | foreach( Meta::AlbumPtr album, albums ) | ||
119 | { | 125 | { | ||
120 | if( m_queue->size() > m_limit ) | 126 | QTimer::singleShot( 0, m_queue, [=] () { m_queue->add( album, CoverFetch::Automatic ); } ); | ||
121 | m_queueLater.append( album ); | | |||
122 | else | | |||
123 | m_queue->add( album, CoverFetch::Automatic ); | | |||
124 | } | 127 | } | ||
125 | } | 128 | } | ||
126 | 129 | | |||
127 | void | 130 | void | ||
128 | CoverFetcher::queueQuery( Meta::AlbumPtr album, const QString &query, int page ) | 131 | CoverFetcher::queueQuery( Meta::AlbumPtr album, const QString &query, int page ) | ||
129 | { | 132 | { | ||
130 | m_queue->addQuery( query, fetchSource(), page, album ); | 133 | QTimer::singleShot( 0, m_queue, [=] () { m_queue->addQuery( query, fetchSource(), page, album ); } ); | ||
131 | debug() << QString( "Queueing cover fetch query: '%1' (page %2)" ).arg( query, QString::number( page ) ); | 134 | debug() << QString( "Queueing cover fetch query: '%1' (page %2)" ).arg( query, QString::number( page ) ); | ||
132 | } | 135 | } | ||
133 | 136 | | |||
134 | void | 137 | void | ||
135 | CoverFetcher::queueQueryForAlbum( Meta::AlbumPtr album ) | 138 | CoverFetcher::queueQueryForAlbum( Meta::AlbumPtr album ) | ||
136 | { | 139 | { | ||
137 | QString query( album->name() ); | 140 | QString query( album->name() ); | ||
138 | if( album->hasAlbumArtist() ) | 141 | if( album->hasAlbumArtist() ) | ||
Show All 36 Lines | 177 | if( unit->isInteractive() ) | |||
175 | Amarok::Logger::newProgressOperation( reply, i18n( "Fetching Cover" ) ); | 178 | Amarok::Logger::newProgressOperation( reply, i18n( "Fetching Cover" ) ); | ||
176 | else | 179 | else | ||
177 | return; // only one is needed when the fetch is non-interactive | 180 | return; // only one is needed when the fetch is non-interactive | ||
178 | } | 181 | } | ||
179 | } | 182 | } | ||
180 | } | 183 | } | ||
181 | 184 | | |||
182 | void | 185 | void | ||
183 | CoverFetcher::slotResult( const QUrl &url, QByteArray data, NetworkAccessManagerProxy::Error e ) | 186 | CoverFetcher::slotResult( const QUrl &url, const QByteArray &data, NetworkAccessManagerProxy::Error e ) | ||
184 | { | 187 | { | ||
185 | DEBUG_BLOCK | 188 | DEBUG_BLOCK | ||
186 | if( !m_urls.contains( url ) ) | 189 | if( !m_urls.contains( url ) ) | ||
187 | return; | 190 | return; | ||
188 | debug() << "Data dump from the result: " << data; | 191 | // debug() << "Data dump from the result: " << data; | ||
189 | 192 | | |||
190 | const CoverFetchUnit::Ptr unit( m_urls.take( url ) ); | 193 | const CoverFetchUnit::Ptr unit( m_urls.take( url ) ); | ||
191 | if( !unit ) | 194 | if( !unit ) | ||
192 | { | 195 | { | ||
193 | m_queue->remove( unit ); | 196 | QTimer::singleShot( 0, m_queue, [=] () { m_queue->remove( unit ); } ); | ||
194 | return; | 197 | return; | ||
195 | } | 198 | } | ||
196 | 199 | | |||
197 | if( e.code != QNetworkReply::NoError ) | 200 | if( e.code != QNetworkReply::NoError ) | ||
198 | { | 201 | { | ||
199 | finish( unit, Error, i18n("There was an error communicating with cover provider: %1", e.description) ); | 202 | finish( unit, Error, i18n("There was an error communicating with cover provider: %1", e.description) ); | ||
200 | return; | 203 | return; | ||
201 | } | 204 | } | ||
202 | 205 | | |||
203 | const CoverFetchPayload *payload = unit->payload(); | 206 | const CoverFetchPayload *payload = unit->payload(); | ||
204 | switch( payload->type() ) | 207 | switch( payload->type() ) | ||
205 | { | 208 | { | ||
206 | case CoverFetchPayload::Info: | 209 | case CoverFetchPayload::Info: | ||
207 | m_queue->add( unit->album(), unit->options(), payload->source(), data ); | 210 | QTimer::singleShot( 0, m_queue, [=] () { m_queue->add( unit->album(), unit->options(), payload->source(), data ); | ||
208 | m_queue->remove( unit ); | 211 | m_queue->remove( unit ); } ); | ||
209 | break; | 212 | break; | ||
210 | 213 | | |||
211 | case CoverFetchPayload::Search: | 214 | case CoverFetchPayload::Search: | ||
212 | m_queue->add( unit->options(), fetchSource(), data ); | 215 | QTimer::singleShot( 0, m_queue, [=] () { m_queue->add( unit->options(), fetchSource(), data ); | ||
213 | m_queue->remove( unit ); | 216 | m_queue->remove( unit ); } ); | ||
214 | break; | 217 | break; | ||
215 | 218 | | |||
216 | case CoverFetchPayload::Art: | 219 | case CoverFetchPayload::Art: | ||
217 | handleCoverPayload( unit, data, url ); | 220 | handleCoverPayload( unit, data, url ); | ||
218 | break; | 221 | break; | ||
219 | } | 222 | } | ||
220 | } | 223 | } | ||
221 | 224 | | |||
Show All 34 Lines | 227 | { | |||
256 | } | 259 | } | ||
257 | 260 | | |||
258 | if( unit->isInteractive() ) | 261 | if( unit->isInteractive() ) | ||
259 | { | 262 | { | ||
260 | QImage image; | 263 | QImage image; | ||
261 | if( reader.read( &image ) ) | 264 | if( reader.read( &image ) ) | ||
262 | { | 265 | { | ||
263 | showCover( unit, image, metadata ); | 266 | showCover( unit, image, metadata ); | ||
264 | m_queue->remove( unit ); | 267 | QTimer::singleShot( 0, m_queue, [=] () { m_queue->remove( unit ); } ); | ||
265 | return; | 268 | return; | ||
266 | } | 269 | } | ||
267 | } | 270 | } | ||
268 | else | 271 | else | ||
269 | { | 272 | { | ||
270 | QImage image; | 273 | QImage image; | ||
271 | if( reader.read( &image ) ) | 274 | if( reader.read( &image ) ) | ||
272 | { | 275 | { | ||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Line(s) | 349 | { | |||
348 | { | 351 | { | ||
349 | const Meta::AlbumPtr album = unit->album(); | 352 | const Meta::AlbumPtr album = unit->album(); | ||
350 | if( !album ) | 353 | if( !album ) | ||
351 | { | 354 | { | ||
352 | finish( unit, Error ); | 355 | finish( unit, Error ); | ||
353 | return; | 356 | return; | ||
354 | } | 357 | } | ||
355 | 358 | | |||
356 | m_dialog = new CoverFoundDialog( unit, data, static_cast<QWidget*>( parent() ) ); | 359 | m_dialog = new CoverFoundDialog( unit, data ); | ||
357 | connect( m_dialog.data(), &CoverFoundDialog::newCustomQuery, | 360 | connect( m_dialog.data(), &CoverFoundDialog::newCustomQuery, | ||
358 | this, &CoverFetcher::queueQuery ); | 361 | this, &CoverFetcher::queueQuery ); | ||
359 | connect( m_dialog.data(), &CoverFoundDialog::accepted, | 362 | connect( m_dialog.data(), &CoverFoundDialog::accepted, | ||
360 | this, &CoverFetcher::slotDialogFinished ); | 363 | this, &CoverFetcher::slotDialogFinished ); | ||
361 | connect( m_dialog.data(),&CoverFoundDialog::rejected, | 364 | connect( m_dialog.data(),&CoverFoundDialog::rejected, | ||
362 | this, &CoverFetcher::slotDialogFinished ); | 365 | this, &CoverFetcher::slotDialogFinished ); | ||
363 | 366 | | |||
364 | if( fetchSource() == CoverFetch::LastFm ) | 367 | if( fetchSource() == CoverFetch::LastFm ) | ||
Show All 14 Lines | 381 | if( payload ) | |||
379 | m_dialog->add( cover, data, payload->imageSize() ); | 382 | m_dialog->add( cover, data, payload->imageSize() ); | ||
380 | } | 383 | } | ||
381 | } | 384 | } | ||
382 | } | 385 | } | ||
383 | 386 | | |||
384 | void | 387 | void | ||
385 | CoverFetcher::abortFetch( CoverFetchUnit::Ptr unit ) | 388 | CoverFetcher::abortFetch( CoverFetchUnit::Ptr unit ) | ||
386 | { | 389 | { | ||
387 | m_queue->remove( unit ); | 390 | QTimer::singleShot( 0, m_queue, [=] () { m_queue->remove( unit ); } ); | ||
388 | m_queueLater.removeAll( unit->album() ); | | |||
389 | m_selectedImages.remove( unit ); | 391 | m_selectedImages.remove( unit ); | ||
390 | QList<QUrl> urls = m_urls.keys( unit ); | 392 | QList<QUrl> urls = m_urls.keys( unit ); | ||
391 | foreach( const QUrl &url, urls ) | 393 | foreach( const QUrl &url, urls ) | ||
392 | m_urls.remove( url ); | 394 | m_urls.remove( url ); | ||
393 | The::networkAccessManager()->abortGet( urls ); | 395 | The::networkAccessManager()->abortGet( urls ); | ||
394 | } | 396 | } | ||
395 | 397 | | |||
396 | void | 398 | void | ||
397 | CoverFetcher::finish( const CoverFetchUnit::Ptr unit, | 399 | CoverFetcher::finish( const CoverFetchUnit::Ptr unit, | ||
398 | CoverFetcher::FinishState state, | 400 | CoverFetcher::FinishState state, | ||
399 | const QString &message ) | 401 | const QString &message ) | ||
400 | { | 402 | { | ||
401 | Meta::AlbumPtr album = unit->album(); | 403 | Meta::AlbumPtr album = unit->album(); | ||
402 | const QString albumName = album ? album->name() : QString(); | 404 | const QString albumName = album ? album->name() : QString(); | ||
403 | 405 | | |||
404 | switch( state ) | 406 | switch( state ) | ||
405 | { | 407 | { | ||
406 | case Success: | 408 | case Success: | ||
409 | { | ||||
407 | if( !albumName.isEmpty() ) | 410 | if( !albumName.isEmpty() ) | ||
408 | { | 411 | { | ||
409 | const QString text = i18n( "Retrieved cover successfully for '%1'.", albumName ); | 412 | const QString text = i18n( "Retrieved cover successfully for '%1'.", albumName ); | ||
410 | Amarok::Logger::shortMessage( text ); | 413 | Amarok::Logger::shortMessage( text ); | ||
411 | debug() << "Finished successfully for album" << albumName; | 414 | debug() << "Finished successfully for album" << albumName; | ||
412 | } | 415 | } | ||
413 | album->setImage( m_selectedImages.take( unit ) ); | 416 | QImage image = m_selectedImages.take( unit ); | ||
417 | std::thread thread( std::bind( &Meta::Album::setImage, album, image ) ); | ||||
418 | thread.detach(); | ||||
414 | abortFetch( unit ); | 419 | abortFetch( unit ); | ||
415 | break; | 420 | break; | ||
416 | 421 | } | |||
417 | case Error: | 422 | case Error: | ||
418 | if( !albumName.isEmpty() ) | 423 | if( !albumName.isEmpty() ) | ||
419 | { | 424 | { | ||
420 | const QString text = i18n( "Fetching cover for '%1' failed.", albumName ); | 425 | const QString text = i18n( "Fetching cover for '%1' failed.", albumName ); | ||
421 | Amarok::Logger::shortMessage( text ); | 426 | Amarok::Logger::shortMessage( text ); | ||
422 | QString debugMessage; | 427 | QString debugMessage; | ||
423 | if( !message.isEmpty() ) | 428 | if( !message.isEmpty() ) | ||
424 | debugMessage = '[' + message + ']'; | 429 | debugMessage = '[' + message + ']'; | ||
Show All 18 Lines | 446 | { | |||
443 | //FIXME: Not visible behind cover manager | 448 | //FIXME: Not visible behind cover manager | ||
444 | Amarok::Logger::shortMessage( text ); | 449 | Amarok::Logger::shortMessage( text ); | ||
445 | m_errors += text; | 450 | m_errors += text; | ||
446 | debug() << "Finished due to cover not found for album" << albumName; | 451 | debug() << "Finished due to cover not found for album" << albumName; | ||
447 | } | 452 | } | ||
448 | break; | 453 | break; | ||
449 | } | 454 | } | ||
450 | 455 | | |||
451 | m_queue->remove( unit ); | 456 | QTimer::singleShot( 0, m_queue, [=] () { m_queue->remove( unit ); } ); | ||
452 | | ||||
453 | if( !m_queueLater.isEmpty() ) | | |||
454 | { | | |||
455 | const int diff = m_limit - m_queue->size(); | | |||
456 | if( diff > 0 ) | | |||
457 | { | | |||
458 | for( int i = 0; i < diff && !m_queueLater.isEmpty(); ++i ) | | |||
459 | { | | |||
460 | Meta::AlbumPtr album = m_queueLater.takeFirst(); | | |||
461 | // automatic fetching only uses Last.fm as source | | |||
462 | m_queue->add( album, CoverFetch::Automatic, CoverFetch::LastFm ); | | |||
463 | } | | |||
464 | } | | |||
465 | } | | |||
466 | 457 | | |||
467 | emit finishedSingle( static_cast< int >( state ) ); | 458 | emit finishedSingle( static_cast< int >( state ) ); | ||
468 | } | 459 | } | ||
469 | 460 | | |||
470 | CoverFetch::Source | 461 | CoverFetch::Source | ||
471 | CoverFetcher::fetchSource() const | 462 | CoverFetcher::fetchSource() const | ||
472 | { | 463 | { | ||
473 | const KConfigGroup config = Amarok::config( "Cover Fetcher" ); | 464 | const KConfigGroup config = Amarok::config( "Cover Fetcher" ); | ||
Show All 12 Lines |