File indexing completed on 2024-05-05 04:49:15

0001 /****************************************************************************************
0002  * Copyright (c) 2007 Nikolaj Hald Nielsen <nhn@kde.org>                                *
0003  * Copyright (c) 2007 Casey Link <unnamedrambler@gmail.com>                             *
0004  *                                                                                      *
0005  * This program is free software; you can redistribute it and/or modify it under        *
0006  * the terms of the GNU General Public License as published by the Free Software        *
0007  * Foundation; either version 2 of the License, or (at your option) any later           *
0008  * version.                                                                             *
0009  *                                                                                      *
0010  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0011  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0012  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0013  *                                                                                      *
0014  * You should have received a copy of the GNU General Public License along with         *
0015  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0016  ****************************************************************************************/
0017 
0018 #define DEBUG_PREFIX "ServiceAlbumCoverDownloader"
0019 
0020 #include "ServiceAlbumCoverDownloader.h"
0021 
0022 #include "core/support/Amarok.h"
0023 #include "amarokconfig.h"
0024 #include "core/support/Debug.h"
0025 #include "covermanager/CoverCache.h"
0026 
0027 #include <thread>
0028 
0029 #include <QDir>
0030 #include <QImage>
0031 
0032 using namespace Meta;
0033 
0034 
0035 Meta::ServiceAlbumWithCover::ServiceAlbumWithCover( const QString &name )
0036     : ServiceAlbum( name )
0037     , m_hasFetchedCover( false )
0038     , m_isFetchingCover ( false )
0039 {}
0040 
0041 Meta::ServiceAlbumWithCover::ServiceAlbumWithCover( const QStringList &resultRow )
0042     : ServiceAlbum( resultRow )
0043     , m_hasFetchedCover( false )
0044     , m_isFetchingCover ( false )
0045 {}
0046 
0047 Meta::ServiceAlbumWithCover::~ServiceAlbumWithCover()
0048 {
0049     CoverCache::invalidateAlbum( this );
0050 }
0051 
0052 QImage
0053 ServiceAlbumWithCover::image( int size ) const
0054 {
0055     if( size > 1000 )
0056     {
0057         debug() << "Giant image detected, are you sure you want this?";
0058         return Meta::Album::image( size );
0059     }
0060 
0061     const QString artist = hasAlbumArtist() ?
0062         albumArtist()->name() :
0063         QStringLiteral("NULL"); //no need to translate, only used as a caching key/temp filename
0064 
0065     const QString coverName = QStringLiteral( "%1_%2_%3_cover.png" ).arg( downloadPrefix(), artist, name() );
0066     const QString saveLocation = Amarok::saveLocation( QStringLiteral("albumcovers/cache/") );
0067     const QDir cacheCoverDir = QDir( saveLocation );
0068 
0069     //make sure that this dir exists
0070     if( !cacheCoverDir.exists() )
0071         cacheCoverDir.mkpath( saveLocation );
0072 
0073     if( size <= 1 )
0074         size = 100;
0075 
0076     const QString sizeKey = QString::number( size ) + QLatin1Char('@');
0077     const QString cacheCoverPath = cacheCoverDir.filePath( sizeKey + coverName );
0078 
0079     if( QFile::exists( cacheCoverPath ) )
0080     {
0081         return QImage( cacheCoverPath );
0082     }
0083     else if( m_hasFetchedCover && !m_cover.isNull() )
0084     {
0085         QImage image( m_cover.scaled( size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation ) );
0086         std::thread thread( QOverload<const QString&, const char*, int>::of( &QImage::save ), image, cacheCoverPath, "PNG", -1 );
0087         thread.detach();
0088         return image;
0089     }
0090     else if( !m_isFetchingCover && !coverUrl().isEmpty() )
0091     {
0092         m_isFetchingCover = true;
0093 
0094         ( new ServiceAlbumCoverDownloader )->downloadCover(
0095             ServiceAlbumWithCoverPtr(const_cast<ServiceAlbumWithCover*>(this)) );
0096     }
0097 
0098     return Meta::Album::image( size );
0099 }
0100 
0101 void
0102 ServiceAlbumWithCover::setImage( const QImage& image )
0103 {
0104     m_cover = image;
0105     m_hasFetchedCover = true;
0106     m_isFetchingCover = false;
0107     CoverCache::invalidateAlbum( this );
0108 
0109     notifyObservers();
0110 }
0111 
0112 void
0113 ServiceAlbumWithCover::imageDownloadCanceled() const
0114 {
0115     m_hasFetchedCover = true;
0116     m_isFetchingCover = false;
0117 }
0118 
0119 
0120 ///////////////////////////////////////////////////////////////////////////////
0121 // Class ServiceAlbumCoverDownloader
0122 ///////////////////////////////////////////////////////////////////////////////
0123 
0124 ServiceAlbumCoverDownloader::ServiceAlbumCoverDownloader()
0125     : m_albumDownloadJob( )
0126 {
0127     m_tempDir = new QTemporaryDir();
0128     m_tempDir->setAutoRemove( true );
0129 }
0130 
0131 ServiceAlbumCoverDownloader::~ServiceAlbumCoverDownloader()
0132 {
0133     delete m_tempDir;
0134 }
0135 
0136 void
0137 ServiceAlbumCoverDownloader::downloadCover( ServiceAlbumWithCoverPtr album )
0138 {
0139     m_album = album;
0140 
0141     QUrl downloadUrl( album->coverUrl() );
0142     // KIO::file_copy in KF5 needs scheme
0143     if (downloadUrl.isRelative() && downloadUrl.host().isEmpty())
0144         downloadUrl.setScheme("file");
0145 
0146     m_coverDownloadPath = m_tempDir->path() + QLatin1Char('/') + downloadUrl.fileName();
0147 
0148     debug() << "Download Cover: " << downloadUrl.url() << " to: " << m_coverDownloadPath;
0149 
0150     m_albumDownloadJob = KIO::file_copy( downloadUrl, QUrl::fromLocalFile( m_coverDownloadPath ), -1, KIO::Overwrite | KIO::HideProgressInfo );
0151 
0152     connect( m_albumDownloadJob, &KJob::result, this, &ServiceAlbumCoverDownloader::coverDownloadComplete );
0153 }
0154 
0155 void
0156 ServiceAlbumCoverDownloader::coverDownloadComplete( KJob * downloadJob )
0157 {
0158     if( !m_album ) // album was removed in between
0159     {
0160         debug() << "Bad album pointer";
0161         return;
0162     }
0163 
0164     if ( downloadJob != m_albumDownloadJob )
0165         return; //not the right job, so let's ignore it
0166 
0167     if( !downloadJob || downloadJob->error() )
0168     {
0169         debug() << "Download Job failed!";
0170 
0171         //we could not download, so inform album
0172         coverDownloadCanceled( downloadJob );
0173         return;
0174     }
0175 
0176     const QImage cover = QImage( m_coverDownloadPath );
0177     if ( cover.isNull() )
0178     {
0179         debug() << "file not a valid image";
0180         //the file wasn't an image, so inform album
0181         m_album->imageDownloadCanceled();
0182         return;
0183     }
0184 
0185     m_album->setImage( cover );
0186 
0187     downloadJob->deleteLater();
0188 
0189     deleteLater();
0190 }
0191 
0192 void
0193 ServiceAlbumCoverDownloader::coverDownloadCanceled( KJob *downloadJob )
0194 {
0195     Q_UNUSED( downloadJob );
0196     DEBUG_BLOCK
0197 
0198     if( !m_album ) // album was removed in between
0199         return;
0200 
0201     debug() << "Cover download cancelled";
0202     m_album->imageDownloadCanceled();
0203     deleteLater();
0204 }
0205 
0206