File indexing completed on 2024-05-19 04:49:30

0001 /****************************************************************************************
0002  * Copyright (c) 2009 Bart Cerneels <bart.cerneels@kde.org>                             *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #include "core/podcasts/PodcastImageFetcher.h"
0018 
0019 #include "core/support/Debug.h"
0020 
0021 #include <QCryptographicHash>
0022 #include <QNetworkConfigurationManager>
0023 
0024 #include <KIO/Job>
0025 
0026 PodcastImageFetcher::PodcastImageFetcher()
0027 {
0028 }
0029 
0030 void
0031 PodcastImageFetcher::addChannel( Podcasts::PodcastChannelPtr channel )
0032 {
0033     DEBUG_BLOCK
0034     if( channel->imageUrl().isEmpty() )
0035     {
0036         debug() << channel->title() << " does not have an imageUrl";
0037         return;
0038     }
0039 
0040     if( hasCachedImage( channel ) )
0041     {
0042         debug() << "using cached image for " << channel->title();
0043         QString imagePath = cachedImagePath( channel ).toLocalFile();
0044         QImage image( imagePath );
0045         if( image.isNull() )
0046             error() << "could not load pixmap from " << imagePath;
0047         else
0048             channel->setImage( image );
0049         return;
0050     }
0051 
0052     if( m_channels.contains( channel ) )
0053     {
0054         debug() << "Channel already queued:" << channel->title();
0055         return;
0056     }
0057 
0058     if( m_jobChannelMap.values().contains( channel ) )
0059     {
0060         debug() << "Copy job already running for channel:" << channel->title();
0061         return;
0062     }
0063 
0064     debug() << "Adding " << channel->title() << " to fetch queue";
0065     m_channels.append( channel );
0066 }
0067 
0068 void
0069 PodcastImageFetcher::addEpisode( const Podcasts::PodcastEpisodePtr &episode )
0070 {
0071     Q_UNUSED( episode );
0072 }
0073 
0074 QUrl
0075 PodcastImageFetcher::cachedImagePath( const Podcasts::PodcastChannelPtr &channel )
0076 {
0077     return cachedImagePath( channel.data() );
0078 }
0079 
0080 QUrl
0081 PodcastImageFetcher::cachedImagePath( Podcasts::PodcastChannel *channel )
0082 {
0083     QUrl imagePath = channel->saveLocation();
0084     if( imagePath.isEmpty() || !imagePath.isLocalFile() )
0085         imagePath = QUrl::fromLocalFile( Amarok::saveLocation( QStringLiteral("podcasts") ) );
0086     QCryptographicHash md5( QCryptographicHash::Md5 );
0087     md5.addData( channel->url().url().toLocal8Bit() );
0088     QString extension = Amarok::extension( channel->imageUrl().fileName() );
0089     imagePath = imagePath.adjusted( QUrl::StripTrailingSlash );
0090     imagePath.setPath( imagePath.path() + QLatin1Char('/') + ( md5.result().toHex() + QLatin1Char('.') + extension ) );
0091     return imagePath;
0092 }
0093 
0094 bool
0095 PodcastImageFetcher::hasCachedImage( const Podcasts::PodcastChannelPtr &channel )
0096 {
0097     DEBUG_BLOCK
0098     return QFile( PodcastImageFetcher::cachedImagePath(
0099             Podcasts::PodcastChannelPtr::dynamicCast( channel ) ).toLocalFile() ).exists();
0100 }
0101 
0102 void
0103 PodcastImageFetcher::run()
0104 {
0105     if( m_channels.isEmpty() && m_episodes.isEmpty()
0106         && m_jobChannelMap.isEmpty() && m_jobEpisodeMap.isEmpty() )
0107     {
0108         //nothing to do
0109         Q_EMIT( done( this ) );
0110         return;
0111     }
0112 
0113     QNetworkConfigurationManager mgr;
0114     if( !mgr.isOnline() )
0115     {
0116         debug() << "QNetworkConfigurationManager reports we are not online, canceling podcast image download";
0117         Q_EMIT( done( this ) );
0118         //TODO: schedule another run after Solid reports we are online again
0119         return;
0120     }
0121 
0122     foreach( Podcasts::PodcastChannelPtr channel, m_channels )
0123     {
0124         QUrl channelImageUrl = channel->imageUrl();
0125         // KIO::file_copy in KF5 needs scheme
0126         if (channelImageUrl.isRelative() && channelImageUrl.host().isEmpty()) {
0127             channelImageUrl.setScheme("file");
0128         }
0129 
0130         QUrl cachedPath = cachedImagePath( channel );
0131         KIO::mkdir( cachedPath.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash) );
0132         KIO::FileCopyJob *job = KIO::file_copy(channelImageUrl, cachedPath,
0133                                                -1, KIO::HideProgressInfo | KIO::Overwrite );
0134         //remove channel from the todo list
0135         m_channels.removeAll( channel );
0136         m_jobChannelMap.insert( job, channel );
0137         connect( job, &KIO::FileCopyJob::finished, this, &PodcastImageFetcher::slotDownloadFinished );
0138     }
0139 
0140     //TODO: episodes
0141 }
0142 
0143 void
0144 PodcastImageFetcher::slotDownloadFinished( KJob *job )
0145 {
0146     DEBUG_BLOCK
0147 
0148     //QMap::take() also removes the entry from the map.
0149     Podcasts::PodcastChannelPtr channel = m_jobChannelMap.take( job );
0150     if( channel.isNull() )
0151     {
0152         error() << "got null PodcastChannelPtr " << __FILE__ << ":" << __LINE__;
0153         return;
0154     }
0155 
0156     if( job->error() )
0157     {
0158         error() << "downloading podcast image " << job->errorString();
0159     }
0160     else
0161     {
0162         QString imagePath = cachedImagePath( channel ).toLocalFile();
0163         QImage image( imagePath );
0164         if( image.isNull() )
0165             error() << "could not load pixmap from " << imagePath;
0166         else
0167             channel->setImage( image );
0168     }
0169 
0170     //call run again to start the next batch of transfers.
0171     run();
0172 }