#include "downloadmanager.h"
// 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() {
qxtLog->debug() << "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()) {
qxtLog->debug() << "[dm] Download directory: " << _downloadDir.path() << " doesn't exist.";
// try to create the directory
if (QDir::current().mkdir(downloadPath))
qxtLog->debug() << "[dm] Created download directory: " << _downloadDir.path();
else {
qxtLog->debug() << "[dm] 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"))
qxtLog->debug() << "[dm] Successfully created: " << _downloadDir.absolutePath();
else {
// just in case
qxtLog->debug() << "[dm] Failed to create: " << _downloadDir.absolutePath();
qxtLog->debug() << "[dm] Exiting...";
exit( EXIT_FAILURE);
}
} else
qxtLog->debug() << "[dm] " << _downloadDir.absolutePath() << " already exists.";
}
} else
qxtLog->debug() << "[dm] Download directory: " << _downloadDir.absolutePath()
<< " already exists.";
qxtLog->debug() << "[dm] 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()) {
qxtLog->debug() << "[dm] No URL specified for download.";
return;
}
qxtLog->debug() << "[dm] Enqueueing: " << url.toString();
_downloadQueue.enqueue(url);
if (_dip) {
// download in progress, return.
qxtLog->debug() << "[dm] 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();
qxtLog->debug() << "[dm] Download manager ready. (1)";
return;
}
qxtLog->debug() << "[dm] 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)) {
qxtLog->debug() << "[dm] File already exists: " << _downloadDir.absoluteFilePath(tmp);
_outfile.setFileName(
QString(_downloadDir.absolutePath() + "/" + tmp + ".\%1").arg(_downloaded));
} else
_outfile.setFileName(_downloadDir.absoluteFilePath(tmp));
qxtLog->debug() << "[dm] Saving to: " << _outfile.fileName();
// try to open for writing
if (!_outfile.open(QIODevice::WriteOnly)) {
qxtLog->debug() << "[dm] 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);
qxtLog->debug() << "[dm] 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) {
qxtLog->debug() << "[dm] 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);
qxtLog->debug() << "[dm] 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();
qxtLog->debug() << "[dm] 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++;
qxtLog->debug() << "[dm] 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();
qxtLog->debug() << "[dm] Download manager ready. (2)";
return;
}
startNextDownload();
}