DownloadManager.cpp 5.81 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
/*
 *  Kaidan - A user-friendly XMPP client for every device!
 *
 *  Copyright (C) 2016-2019 Kaidan developers and contributors
 *  (see the LICENSE file for a full list of copyright authors)
 *
 *  Kaidan is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  In addition, as a special exception, the author of Kaidan gives
 *  permission to link the code of its release with the OpenSSL
 *  project's "OpenSSL" library (or with modified versions of it that
 *  use the same license as the "OpenSSL" library), and distribute the
 *  linked executables. You must obey the GNU General Public License in
 *  all respects for all of the code used other than "OpenSSL". If you
 *  modify this file, you may extend this exception to your version of
 *  the file, but you are not obligated to do so.  If you do not wish to
 *  do so, delete this exception statement from your version.
 *
 *  Kaidan is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Kaidan.  If not, see <http://www.gnu.org/licenses/>.
 */

// Kaidan
#include "DownloadManager.h"
Linus Jahn's avatar
Linus Jahn committed
33
#include "Globals.h"
34 35
#include "Kaidan.h"
#include "MessageModel.h"
Linus Jahn's avatar
Linus Jahn committed
36
#include "TransferCache.h"
37 38
// C++
#include <utility>
39
// Qt
Linus Jahn's avatar
Linus Jahn committed
40 41 42 43 44
#include <QDir>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QStandardPaths>
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

DownloadManager::DownloadManager(Kaidan *kaidan, TransferCache *transferCache,
                                 MessageModel *model, QObject *parent)
        : QObject(parent), thread(new DownloadThread()),
          netMngr(new QNetworkAccessManager), kaidan(kaidan),
          transferCache(transferCache), model(model)
{
	connect(this, &DownloadManager::startDownloadRequested,
	        this, &DownloadManager::startDownload);
	connect(this, &DownloadManager::abortDownloadRequested,
	        this, &DownloadManager::abortDownload);

	connect(kaidan, &Kaidan::downloadMedia, this, &DownloadManager::startDownload);

	netMngr->moveToThread(thread);
	thread->start();
}

DownloadManager::~DownloadManager()
{
	delete netMngr;
	delete thread;
}

69
void DownloadManager::startDownload(const QString &msgId, const QString &url)
70 71
{
	// don't download the same file twice and in parallel
Linus Jahn's avatar
Linus Jahn committed
72
	if (downloads.contains(msgId)) {
73 74 75 76 77 78
		qWarning() << "Tried to download a file that is currently being "
		              "downloaded.";
		return;
	}

	// we want to save files to 'Downloads/Kaidan/'
79 80 81
	QString dirPath =
	        QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)
	        + QDir::separator() + APPLICATION_DISPLAY_NAME + QDir::separator();
82

83
	DownloadJob *dl = new DownloadJob(msgId, QUrl(url), dirPath, netMngr,
84 85 86 87
	                                  transferCache, kaidan);
	dl->moveToThread(thread);
	downloads[msgId] = dl;

88 89 90 91 92
	connect(dl, &DownloadJob::finished, this, [=]() {
		const QString mediaLocation = dl->downloadLocation();
		emit model->updateMessageRequested(msgId, [=] (Message &msg) {
			msg.setMediaLocation(mediaLocation);
		});
93 94 95

		abortDownload(msgId);
	});
96
	connect(dl, &DownloadJob::failed, this, [=]() {
97 98 99 100 101 102
		abortDownload(msgId);
	});

	emit dl->startDownloadRequested();
}

103
void DownloadManager::abortDownload(const QString &msgId)
104 105
{
	DownloadJob *job = downloads.value(msgId);
106
	delete job;
107 108 109 110 111
	downloads.remove(msgId);

	emit transferCache->removeJobRequested(msgId);
}

112 113 114
DownloadJob::DownloadJob(QString msgId,
                         QUrl source,
                         QString filePath,
115
                         QNetworkAccessManager *netMngr,
116 117 118 119 120 121 122 123 124
                         TransferCache *transferCache,
                         Kaidan *kaidan)
        : QObject(nullptr),
          msgId(std::move(msgId)),
          source(std::move(source)),
          filePath(std::move(filePath)),
          netMngr(netMngr),
          transferCache(transferCache),
          kaidan(kaidan)
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
{
	connect(this, &DownloadJob::startDownloadRequested,
	        this, &DownloadJob::startDownload);
}

void DownloadJob::startDownload()
{
	QDir dlDir(filePath);
	if (!dlDir.exists())
		dlDir.mkpath(".");

	// don't override other files
	file.setFileName(filePath + source.fileName());
	int counter = 1;
	while (file.exists()) {
140 141
		file.setFileName(filePath + source.fileName() + "-"
		                 + QString::number(counter++));
142 143 144
	}

	if (!file.open(QIODevice::WriteOnly)) {
145 146 147 148
		qWarning() << "Could not open file for writing:"
		           << file.errorString();
		emit kaidan->passiveNotificationRequested(
		        tr("Could not save file: %1").arg(file.errorString()));
149 150 151 152 153 154 155 156 157 158 159 160 161
		emit failed();
		return;
	}

	QNetworkRequest request(source);
	QNetworkReply *reply = netMngr->get(request);

	emit transferCache->addJobRequested(msgId, 0);

	connect(reply, &QNetworkReply::downloadProgress,
	        this, [this] (qint64 bytesReceived, qint64 bytesTotal) {
		emit transferCache->setJobProgressRequested(msgId, bytesReceived, bytesTotal);
	});
162
	connect(reply, &QNetworkReply::finished, this, [=] () {
163 164 165 166
		emit transferCache->removeJobRequested(msgId);
		emit finished();
	});
	connect(reply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error),
167
	        this, [=] () {
168
		emit transferCache->removeJobRequested(msgId);
169 170 171
		qWarning() << "Couldn't download file:" << reply->errorString();
		emit kaidan->passiveNotificationRequested(
		        tr("Download failed: %1").arg(reply->errorString()));
172 173
		emit finished();
	});
174
	connect(reply, &QNetworkReply::readyRead, this, [=](){
175 176 177 178 179 180 181 182
		file.write(reply->readAll());
	});
}

QString DownloadJob::downloadLocation() const
{
	return file.fileName();
}