1 /****************************************************************************************
2 * Copyright (c) 2007 Nikolaj Hald Nielsen <nhn@kde.org> *
3 * Copyright (c) 2007 Casey Link <unnamedrambler@gmail.com> *
4 * *
5 * This program is free software; you can redistribute it and/or modify it under *
6 * the terms of the GNU General Public License as published by the Free Software *
7 * Foundation; either version 2 of the License, or (at your option) any later *
8 * version. *
9 * *
10 * This program is distributed in the hope that it will be useful, but WITHOUT ANY *
11 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A *
12 * PARTICULAR PURPOSE. See the GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License along with *
15 * this program. If not, see <http://www.gnu.org/licenses/>. *
16 ****************************************************************************************/
17
18 #define DEBUG_PREFIX "ServiceAlbumCoverDownloader"
19
20 #include "ServiceAlbumCoverDownloader.h"
21
22 #include "core/support/Amarok.h"
23 #include "amarokconfig.h"
24 #include "core/support/Debug.h"
25 #include "covermanager/CoverCache.h"
26
27 #include <thread>
28
29 #include <QDir>
30 #include <QImage>
31
32 using namespace Meta;
33
34
ServiceAlbumWithCover(const QString & name)35 Meta::ServiceAlbumWithCover::ServiceAlbumWithCover( const QString &name )
36 : ServiceAlbum( name )
37 , m_hasFetchedCover( false )
38 , m_isFetchingCover ( false )
39 {}
40
ServiceAlbumWithCover(const QStringList & resultRow)41 Meta::ServiceAlbumWithCover::ServiceAlbumWithCover( const QStringList &resultRow )
42 : ServiceAlbum( resultRow )
43 , m_hasFetchedCover( false )
44 , m_isFetchingCover ( false )
45 {}
46
~ServiceAlbumWithCover()47 Meta::ServiceAlbumWithCover::~ServiceAlbumWithCover()
48 {
49 CoverCache::invalidateAlbum( this );
50 }
51
52 QImage
image(int size) const53 ServiceAlbumWithCover::image( int size ) const
54 {
55 if( size > 1000 )
56 {
57 debug() << "Giant image detected, are you sure you want this?";
58 return Meta::Album::image( size );
59 }
60
61 const QString artist = hasAlbumArtist() ?
62 albumArtist()->name() :
63 QStringLiteral("NULL"); //no need to translate, only used as a caching key/temp filename
64
65 const QString coverName = QStringLiteral( "%1_%2_%3_cover.png" ).arg( downloadPrefix(), artist, name() );
66 const QString saveLocation = Amarok::saveLocation( QStringLiteral("albumcovers/cache/") );
67 const QDir cacheCoverDir = QDir( saveLocation );
68
69 //make sure that this dir exists
70 if( !cacheCoverDir.exists() )
71 cacheCoverDir.mkpath( saveLocation );
72
73 if( size <= 1 )
74 size = 100;
75
76 const QString sizeKey = QString::number( size ) + QLatin1Char('@');
77 const QString cacheCoverPath = cacheCoverDir.filePath( sizeKey + coverName );
78
79 if( QFile::exists( cacheCoverPath ) )
80 {
81 return QImage( cacheCoverPath );
82 }
83 else if( m_hasFetchedCover && !m_cover.isNull() )
84 {
85 QImage image( m_cover.scaled( size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation ) );
86 std::thread thread( QOverload<const QString&, const char*, int>::of( &QImage::save ), image, cacheCoverPath, "PNG", -1 );
87 thread.detach();
88 return image;
89 }
90 else if( !m_isFetchingCover && !coverUrl().isEmpty() )
91 {
92 m_isFetchingCover = true;
93
94 ( new ServiceAlbumCoverDownloader )->downloadCover(
95 ServiceAlbumWithCoverPtr(const_cast<ServiceAlbumWithCover*>(this)) );
96 }
97
98 return Meta::Album::image( size );
99 }
100
101 void
setImage(const QImage & image)102 ServiceAlbumWithCover::setImage( const QImage& image )
103 {
104 m_cover = image;
105 m_hasFetchedCover = true;
106 m_isFetchingCover = false;
107 CoverCache::invalidateAlbum( this );
108
109 notifyObservers();
110 }
111
112 void
imageDownloadCanceled() const113 ServiceAlbumWithCover::imageDownloadCanceled() const
114 {
115 m_hasFetchedCover = true;
116 m_isFetchingCover = false;
117 }
118
119
120 ///////////////////////////////////////////////////////////////////////////////
121 // Class ServiceAlbumCoverDownloader
122 ///////////////////////////////////////////////////////////////////////////////
123
ServiceAlbumCoverDownloader()124 ServiceAlbumCoverDownloader::ServiceAlbumCoverDownloader()
125 : m_albumDownloadJob( )
126 {
127 m_tempDir = new QTemporaryDir();
128 m_tempDir->setAutoRemove( true );
129 }
130
~ServiceAlbumCoverDownloader()131 ServiceAlbumCoverDownloader::~ServiceAlbumCoverDownloader()
132 {
133 delete m_tempDir;
134 }
135
136 void
downloadCover(ServiceAlbumWithCoverPtr album)137 ServiceAlbumCoverDownloader::downloadCover( ServiceAlbumWithCoverPtr album )
138 {
139 m_album = album;
140
141 QUrl downloadUrl( album->coverUrl() );
142
143 m_coverDownloadPath = m_tempDir->path() + QLatin1Char('/') + downloadUrl.fileName();
144
145 debug() << "Download Cover: " << downloadUrl.url() << " to: " << m_coverDownloadPath;
146
147 m_albumDownloadJob = KIO::file_copy( downloadUrl, QUrl::fromLocalFile( m_coverDownloadPath ), -1, KIO::Overwrite | KIO::HideProgressInfo );
148
149 connect( m_albumDownloadJob, &KJob::result, this, &ServiceAlbumCoverDownloader::coverDownloadComplete );
150 }
151
152 void
coverDownloadComplete(KJob * downloadJob)153 ServiceAlbumCoverDownloader::coverDownloadComplete( KJob * downloadJob )
154 {
155 if( !m_album ) // album was removed in between
156 {
157 debug() << "Bad album pointer";
158 return;
159 }
160
161 if ( downloadJob != m_albumDownloadJob )
162 return; //not the right job, so let's ignore it
163
164 if( !downloadJob || downloadJob->error() )
165 {
166 debug() << "Download Job failed!";
167
168 //we could not download, so inform album
169 coverDownloadCanceled( downloadJob );
170 return;
171 }
172
173 const QImage cover = QImage( m_coverDownloadPath );
174 if ( cover.isNull() )
175 {
176 debug() << "file not a valid image";
177 //the file wasn't an image, so inform album
178 m_album->imageDownloadCanceled();
179 return;
180 }
181
182 m_album->setImage( cover );
183
184 downloadJob->deleteLater();
185
186 deleteLater();
187 }
188
189 void
coverDownloadCanceled(KJob * downloadJob)190 ServiceAlbumCoverDownloader::coverDownloadCanceled( KJob *downloadJob )
191 {
192 Q_UNUSED( downloadJob );
193 DEBUG_BLOCK
194
195 if( !m_album ) // album was removed in between
196 return;
197
198 debug() << "Cover download cancelled";
199 m_album->imageDownloadCanceled();
200 deleteLater();
201 }
202
203
204