first implementation of image only download

This commit is contained in:
Blue 2019-09-17 18:16:09 +03:00
parent 59f539c460
commit 94a3766aaf
10 changed files with 359 additions and 22 deletions

View file

@ -16,6 +16,7 @@ set(squawkCORE_SRC
contact.cpp
conference.cpp
storage.cpp
networkaccess.cpp
)
# Tell CMake to create the helloworld executable

199
core/networkaccess.cpp Normal file
View file

@ -0,0 +1,199 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "networkaccess.h"
Core::NetworkAccess::NetworkAccess(QObject* parent):
QObject(parent),
running(false),
manager(),
files("files"),
downloads()
{
}
Core::NetworkAccess::~NetworkAccess()
{
}
void Core::NetworkAccess::fileLocalPathRequest(const QString& messageId, const QString& url)
{
std::map<QString, Download*>::iterator itr = downloads.find(url);
if (itr != downloads.end()) {
Download* dwn = itr->second;
std::set<QString>::const_iterator mItr = dwn->messages.find(messageId);
if (mItr == dwn->messages.end()) {
dwn->messages.insert(messageId);
}
emit downloadFileProgress(messageId, dwn->progress);
} else {
try {
QString path = files.getRecord(url);
QFileInfo info(path);
if (info.exists() && info.isFile()) {
emit fileLocalPathResponse(messageId, path);
} else {
files.removeRecord(url);
emit fileLocalPathResponse(messageId, "");
}
} catch (Archive::NotFound e) {
emit fileLocalPathResponse(messageId, "");
} catch (Archive::Unknown e) {
qDebug() << "Error requesting file path:" << e.what();
emit fileLocalPathResponse(messageId, "");
}
}
}
void Core::NetworkAccess::downladFileRequest(const QString& messageId, const QString& url)
{
std::map<QString, Download*>::iterator itr = downloads.find(url);
if (itr != downloads.end()) {
Download* dwn = itr->second;
std::set<QString>::const_iterator mItr = dwn->messages.find(messageId);
if (mItr == dwn->messages.end()) {
dwn->messages.insert(messageId);
}
emit downloadFileProgress(messageId, dwn->progress);
} else {
try {
QString path = files.getRecord(url);
QFileInfo info(path);
if (info.exists() && info.isFile()) {
emit fileLocalPathResponse(messageId, path);
} else {
files.removeRecord(url);
startDownload(messageId, url);
}
} catch (Archive::NotFound e) {
startDownload(messageId, url);
} catch (Archive::Unknown e) {
qDebug() << "Error requesting file path:" << e.what();
startDownload(messageId, url);
}
}
}
void Core::NetworkAccess::start()
{
if (!running) {
files.open();
running = true;
}
}
void Core::NetworkAccess::stop()
{
if (running) {
files.close();
running = false;
}
}
void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
QString url = rpl->url().toString();
std::map<QString, Download*>::const_iterator itr = downloads.find(url);
if (itr == downloads.end()) {
qDebug() << "an error downloading" << url << ": the request had some progress but seems like noone is waiting for it, skipping";
} else {
Download* dwn = itr->second;
for (std::set<QString>::const_iterator mItr = dwn->messages.begin(), end = dwn->messages.end(); mItr != end; ++mItr) {
emit downloadFileProgress(*mItr, bytesReceived/bytesTotal);
}
}
}
void Core::NetworkAccess::onRequestError(QNetworkReply::NetworkError code)
{
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
QString url = rpl->url().toString();
std::map<QString, Download*>::const_iterator itr = downloads.find(url);
if (itr == downloads.end()) {
qDebug() << "an error downloading" << url << ": the request is reporting an error but seems like noone is waiting for it, skipping";
} else {
itr->second->success = false;
}
}
void Core::NetworkAccess::onRequestFinished()
{
QString path("");
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
QString url = rpl->url().toString();
std::map<QString, Download*>::const_iterator itr = downloads.find(url);
if (itr == downloads.end()) {
qDebug() << "an error downloading" << url << ": the request is done but seems like noone is waiting for it, skipping";
} else {
Download* dwn = itr->second;
if (dwn->success) {
qDebug() << "download success for" << url;
QStringList hops = url.split("/");
QString fileName = hops.back();
QStringList parts = fileName.split(".");
path = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
QString suffix("");
QString realName = parts.front();
for (QStringList::const_iterator sItr = (parts.begin()++), sEnd = parts.end(); sItr != sEnd; ++sItr) {
suffix += "." + (*sItr);
}
QString postfix("");
QFileInfo proposedName(path + realName + postfix + suffix);
int counter = 0;
while (proposedName.exists()) {
suffix = QString("(") + std::to_string(++counter).c_str() + ")";
proposedName = QFileInfo(path + realName + postfix + suffix);
}
path = proposedName.absoluteFilePath();
QFile file(path);
if (file.open(QIODevice::WriteOnly)) {
file.write(dwn->reply->readAll());
file.close();
files.addRecord(url, path);
qDebug() << "file" << path << "was successfully downloaded";
} else {
qDebug() << "couldn't save file" << path;
path = "";
}
}
for (std::set<QString>::const_iterator mItr = dwn->messages.begin(), end = dwn->messages.end(); mItr != end; ++mItr) {
emit fileLocalPathResponse(*mItr, path);
}
dwn->reply->deleteLater();
delete dwn;
downloads.erase(itr);
}
}
void Core::NetworkAccess::startDownload(const QString& messageId, const QString& url)
{
Download* dwn = new Download({{messageId}, 0, 0, true});
QNetworkRequest req(url);
dwn->reply = manager.get(req);
connect(dwn->reply, SIGNAL(downloadProgress(qint64, qint64)), SLOT(onDownloadProgress(qint64, qint64)));
connect(dwn->reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(onRequestError(QNetworkReply::NetworkError)));
connect(dwn->reply, SIGNAL(finished()), SLOT(onRequestFinished()));
downloads.insert(std::make_pair(url, dwn));
emit downloadFileProgress(messageId, 0);
}

82
core/networkaccess.h Normal file
View file

@ -0,0 +1,82 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CORE_NETWORKACCESS_H
#define CORE_NETWORKACCESS_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QFileInfo>
#include <QFile>
#include <QStandardPaths>
#include <set>
#include "storage.h"
namespace Core {
/**
* @todo write docs
*/
class NetworkAccess : public QObject
{
Q_OBJECT
struct Download;
public:
NetworkAccess(QObject* parent = nullptr);
virtual ~NetworkAccess();
void start();
void stop();
signals:
void fileLocalPathResponse(const QString& messageId, const QString& path);
void downloadFileProgress(const QString& messageId, qreal value);
public slots:
void fileLocalPathRequest(const QString& messageId, const QString& url);
void downladFileRequest(const QString& messageId, const QString& url);
private:
void startDownload(const QString& messageId, const QString& url);
private slots:
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void onRequestError(QNetworkReply::NetworkError code);
void onRequestFinished();
private:
bool running;
QNetworkAccessManager manager;
Storage files;
std::map<QString, Download*> downloads;
struct Download {
std::set<QString> messages;
qreal progress;
QNetworkReply* reply;
bool success;
};
};
}
#endif // CORE_NETWORKACCESS_H

View file

@ -26,9 +26,10 @@ Core::Squawk::Squawk(QObject* parent):
QObject(parent),
accounts(),
amap(),
files("files")
network()
{
connect(&network, SIGNAL(fileLocalPathResponse(const QString&, const QString&)), this, SIGNAL(fileLocalPathResponse(const QString&, const QString&)));
connect(&network, SIGNAL(downloadFileProgress(const QString&, qreal)), this, SIGNAL(downloadFileProgress(const QString&, qreal)));
}
Core::Squawk::~Squawk()
@ -43,7 +44,7 @@ Core::Squawk::~Squawk()
void Core::Squawk::stop()
{
qDebug("Stopping squawk core..");
files.close();
network.stop();
QSettings settings;
settings.beginGroup("core");
settings.beginWriteArray("accounts");
@ -83,7 +84,7 @@ void Core::Squawk::start()
}
settings.endArray();
settings.endGroup();
files.open();
network.start();
}
void Core::Squawk::newAccountRequest(const QMap<QString, QVariant>& map)
@ -489,13 +490,10 @@ void Core::Squawk::addRoomRequest(const QString& account, const QString& jid, co
void Core::Squawk::fileLocalPathRequest(const QString& messageId, const QString& url)
{
try {
QString path = files.getRecord(url);
emit fileLocalPathResponse(messageId, path);
} catch (Archive::NotFound e) {
emit fileLocalPathResponse(messageId, "");
} catch (Archive::Unknown e) {
qDebug() << "Error requesting file path:" << e.what();
emit fileLocalPathResponse(messageId, "");
}
network.fileLocalPathRequest(messageId, url);
}
void Core::Squawk::downloadFileRequest(const QString& messageId, const QString& url)
{
network.downladFileRequest(messageId, url);
}

View file

@ -19,7 +19,7 @@
#ifndef CORE_SQUAWK_H
#define CORE_SQUAWK_H
#include <QtCore/QObject>
#include <QObject>
#include <QString>
#include <QVariant>
#include <QMap>
@ -28,7 +28,7 @@
#include "account.h"
#include "../global.h"
#include "storage.h"
#include "networkaccess.h"
namespace Core
{
@ -63,6 +63,7 @@ signals:
void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
void removeRoomParticipant(const QString& account, const QString& jid, const QString& name);
void fileLocalPathResponse(const QString& messageId, const QString& path);
void downloadFileProgress(const QString& messageId, qreal value);
public slots:
void start();
@ -84,6 +85,7 @@ public slots:
void addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin);
void removeRoomRequest(const QString& account, const QString& jid);
void fileLocalPathRequest(const QString& messageId, const QString& url);
void downloadFileRequest(const QString& messageId, const QString& url);
private:
typedef std::deque<Account*> Accounts;
@ -92,7 +94,7 @@ private:
Accounts accounts;
AccountsMap amap;
Shared::Availability state;
Storage files;
NetworkAccess network;
private:
void addAccount(const QString& login, const QString& server, const QString& password, const QString& name, const QString& resource);