summaryrefslogblamecommitdiffstats
path: root/src/fbgui/downloadmanager.cpp
blob: 90483ce28bdb36e81e8fbb9bb76a917d1dc8d132 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
                    
                            
 








                                                  
                                                                      
                                     
                                                                                                          








                                                                                                          
                                    
                                                               
                            
                                       
                                  
                
 
             
                                     
                
 
                                                                                                          
                                                
                                                     

                                     
                                                                                                  
                                    
                                               
                                                                                        
              
                                                                                        
                                     

                                                           
                                                                     
                                                                                                
                    
                              

                                                                                            


                                   
                                                                                             

         
                                                                                                           
 
                                                                                   
                                              
 


                                                                                                          
                                                             

                                                  
 
                                                                                                          
                                                         
                                         
 


                                                                                                          
                                                               
                       
                                                                

             
                                                             

                               
                                      
                                                                                                                                  



                                               
 
                                                                                                          
                                           
                                                    
                                  
                                
                                                             

             
                                                                                                                                                   
 
                                   
                                       
 


                                                  
 
                                                    
                                  
                                                                                             

                                                                                        
         
                                                               
                                                                 
 
                             
                                              
                                                                                                           

             
 

                                   





                                                                      
                           
                                                                            
                             


                                                                                     
                              
 


                                                                                                          
                                         

                                                              

                                                          
                                                                                                            
                                                       
 
                                                                                                          
                                       
                         
                                               
 
                                                                                                          
                                                                           
                                                 
                                                                                                                      


                             
                                                  










                                                                               


                                                            
          
                                                    
                                                                                                                                                                             
    
 
                                                                                                          
                                          
                      




                                                                                          
                                                                                                                                         

                                                                               
                                      

                     

                       
                                                                                                                                          
          
                                                                                            
                                      
    
                
                                   
                                  
                                
                                                             


                       
 
/* \package fbgui */
#include "downloadmanager.h"

#include <log4cxx/logger.h>
#include "qlog4cxx.h"


using namespace log4cxx;
using namespace log4cxx::helpers;
LoggerPtr dmLogger(Logger::getLogger("fbgui.dm"));


// static counter to save the number of successfully downloaded files.
int DownloadManager::_downloaded = 0;
// -------------------------------------------------------------------------------------------------------
//                                          Initialisation
// -------------------------------------------------------------------------------------------------------
// Constructor initialises a QNetworkAccessManager needed to create/received download requests/replies
// This object is then moved to the thread for the download manager, to ensure that downloads
// are done in a separate thread (to avoid GUI blocking.)
// The flag "_dip" starts as false, since no download is in progress.
// This flag is needed to control queueing functionality.
// Lastly the given download directory's existance is checked. (see below)

DownloadManager::DownloadManager() {
   LOG4CXX_DEBUG(dmLogger, "Initializing download manager...");
   checkDownloadDirectory();
   _qnam = new QNetworkAccessManager();
   _qnam->moveToThread(&dmThread);
   _dip = false;
}
// Destructor
DownloadManager::~DownloadManager() {
   delete _qnam;
}
// -------------------------------------------------------------------------------------------------------
void DownloadManager::checkDownloadDirectory() {
   // check if downloadPath exists, if not create it.
   _downloadDir = QDir(downloadPath);
   if (!_downloadDir.exists()) {
      LOG4CXX_DEBUG(dmLogger, "Download directory: " << _downloadDir.path() << " doesn't exist.");
      // try to create the directory
      if (QDir::current().mkdir(downloadPath)){
         LOG4CXX_DEBUG(dmLogger, "Created download directory: " << _downloadDir.path());
      } else {
         LOG4CXX_DEBUG(dmLogger, "Failed to create directory: " << _downloadDir.path());
         // try to save to /tmp/fbgui
         _downloadDir.setPath(QDir::tempPath() + "/fbgui");
         if (!_downloadDir.exists()) {
            if (QDir::current().mkdir(QDir::tempPath() + "/fbgui")) {
               LOG4CXX_DEBUG(dmLogger, "Successfully created: " << _downloadDir.absolutePath());
            } else {
               // just in case
               LOG4CXX_DEBUG(dmLogger, "Failed to create: " << _downloadDir.absolutePath());
               LOG4CXX_DEBUG(dmLogger, "Exiting...");
               exit( EXIT_FAILURE);
            }
         } else
            LOG4CXX_DEBUG(dmLogger, "" << _downloadDir.absolutePath() << " already exists.");
      }
   } else
      LOG4CXX_DEBUG(dmLogger, "Download directory: " << _downloadDir.absolutePath() << " already exists.");

   LOG4CXX_DEBUG(dmLogger, "Saving downloads to: " << _downloadDir.absolutePath());
   downloadPath = _downloadDir.absolutePath();
}
// -------------------------------------------------------------------------------------------------------
//                                         Public access
// -------------------------------------------------------------------------------------------------------
void DownloadManager::downloadFile(const QString& filename) {
   QUrl fileUrl(baseURL.resolved(QUrl(filename)));
   this->processDownloadRequest(fileUrl);
}
// -------------------------------------------------------------------------------------------------------
void DownloadManager::downloadFile(const QUrl& fileUrl) {
   this->processDownloadRequest(fileUrl);
}
// -------------------------------------------------------------------------------------------------------
//                    Private functions handling download requests and queueing
// -------------------------------------------------------------------------------------------------------
void DownloadManager::processDownloadRequest(const QUrl& url) {
   if (url.isEmpty()) {
      LOG4CXX_DEBUG(dmLogger, "No URL specified for download.");
      return;
   }
   LOG4CXX_DEBUG(dmLogger, "Enqueueing: " << url.toString());
   _downloadQueue.enqueue(url);
   if (_dip) {
      // download in progress, return.
      LOG4CXX_DEBUG(dmLogger, "Download in progress! Queued:" << url.toString() << "("  << _downloadQueue.size() << " in queue)");
      return;
   }
   // no running downloads: start next in queue
   startNextDownload();
}
// -------------------------------------------------------------------------------------------------------
void DownloadManager::startNextDownload() {
   //QWSServer::instance()->setCursorVisible(false);
   if (_downloadQueue.isEmpty()) {
      emit downloadQueueEmpty();
      LOG4CXX_DEBUG(dmLogger, "Download manager ready. (1)");
      return;
   }
   LOG4CXX_DEBUG(dmLogger, "Starting next download: " << _downloadQueue.head().toString() << "  ("  << _downloadQueue.size() - 1 << " in queue.)");

   // dequeue next URL to download.
   QUrl url = _downloadQueue.dequeue();

   // get filename from URL.
   QString tmp = url.path();
   tmp.remove(0, tmp.lastIndexOf(QChar('/')) + 1);

   // check if filename exists on target file system
   if (_downloadDir.exists(tmp)) {
      LOG4CXX_DEBUG(dmLogger, "File already exists: " << _downloadDir.absoluteFilePath(tmp));
      _outfile.setFileName(
            QString(_downloadDir.absolutePath() + "/" + tmp + ".\%1").arg(_downloaded));
   } else
      _outfile.setFileName(_downloadDir.absoluteFilePath(tmp));
   LOG4CXX_DEBUG(dmLogger, "Saving to: " << _outfile.fileName());

   // try to open for writing
   if (!_outfile.open(QIODevice::WriteOnly)) {
      LOG4CXX_DEBUG(dmLogger, "No write access to " << _outfile.fileName()   << " . Skipping download...");
      return;
   }

   // send the request for the file
   QNetworkRequest request(url);
   _currentDownload = _qnam->get(request);
   _lastProgress = 0;
   _currentProgress = 0;
   _dip = true;
   time.start();
   QObject::connect(_currentDownload, SIGNAL(readyRead()), this, SLOT(
         downloadReady()));
   QObject::connect(_currentDownload, SIGNAL(metaDataChanged()), this, SLOT(
         processMetaInfo()));
   QObject::connect(_currentDownload, SIGNAL(downloadProgress(qint64, qint64)), this,
         SLOT(downloadProgress(qint64, qint64)));
   QObject::connect(_currentDownload, SIGNAL(finished()), this, SLOT(
         downloadFinished()));
}
// -------------------------------------------------------------------------------------------------------
//                           Private slots to handle a download in progress
// -------------------------------------------------------------------------------------------------------
void DownloadManager::processMetaInfo() {
   // fetch filesize from header & filename from URL (for now)
   const QByteArray cltag = "Content-Length";
   QByteArray clinfo = _currentDownload->rawHeader(cltag);
   QFileInfo fi(_outfile);
   LOG4CXX_DEBUG(dmLogger, "Download Info: " << fi.fileName() << " (Size: " << clinfo.toDouble()    << ")");
   emit downloadInfo(fi.fileName(), clinfo.toDouble());
}
// -------------------------------------------------------------------------------------------------------
void DownloadManager::downloadReady() {
   // data ready, save it
   _outfile.write(_currentDownload->readAll());
}
// -------------------------------------------------------------------------------------------------------
void DownloadManager::downloadProgress(qint64 bytesIn, qint64 bytesTotal) {
   if (bytesIn > bytesTotal || bytesTotal <= 0) {
      LOG4CXX_DEBUG(dmLogger, "downloadProgress invalid values:" << "In:" << bytesIn   << " / Total: " << bytesTotal);
      return;
   }
   // calculate current speed
   double speed = bytesIn * 1000 / time.elapsed();
   QString unit;
   if (speed < 1024) {
      unit = "bytes/sec";
   } else if (speed < 1024 * 1024) {
      speed /= 1024;
      unit = "KB/s";
   } else {
      speed /= 1024 * 1024;
      unit = "MB/s";
   }
   // update progress only if difference higher than the updateInterval setting
   _currentProgress = ((bytesIn * 100) / bytesTotal);
   if (_currentProgress - _lastProgress >= updateInterval) {
      _lastProgress = _currentProgress;
      emit
      updateProgress(_currentProgress, speed, unit);
      LOG4CXX_DEBUG(dmLogger, "Download progress of " << _currentDownload->url().toString() << ": "     << bytesIn << "/" << bytesTotal << "(" << _currentProgress << "\%)");
   }
}
// -------------------------------------------------------------------------------------------------------
void DownloadManager::downloadFinished() {
   // check for errors
   if (_currentDownload->error()) {
      _outfile.close();
      _outfile.remove();
      int statusCode =
            _currentDownload->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
      LOG4CXX_DEBUG(dmLogger, "Download of " << _currentDownload->url().toString()    << " failed with HTTP error code: " << statusCode);
      emit
      notify(QString("Download failed! HTTP Status Code: %1").arg(statusCode));
      _currentDownload->deleteLater();
   } else {
      // end download
      _outfile.close();
      _downloaded++;
      LOG4CXX_DEBUG(dmLogger, "Download of " << _currentDownload->url().toString()  << " finished. (downloaded = " << _downloaded << ")");
      emit
      notify(QString("Successfully downloaded %1").arg(_currentDownload->url().toString()));
      _currentDownload->deleteLater();
   }
   _dip = false;
   // process next in queue, if any
   if (_downloadQueue.isEmpty()) {
      emit downloadQueueEmpty();
      LOG4CXX_DEBUG(dmLogger, "Download manager ready. (2)");
      return;
   }
   startNextDownload();
}