File indexing completed on 2025-02-02 04:11:04
0001 /* 0002 * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 #pragma once 0007 0008 #include <unordered_map> 0009 0010 #include <QNetworkAccessManager> 0011 #include <QNetworkRequest> 0012 #include <QNetworkReply> 0013 0014 0015 namespace glaxnimate::model { 0016 0017 class NetworkDownloader: public QObject 0018 { 0019 Q_OBJECT 0020 0021 private: 0022 struct PendingRequest 0023 { 0024 PendingRequest(QNetworkReply* reply) : reply(reply) {} 0025 PendingRequest(const PendingRequest&) = delete; 0026 PendingRequest& operator=(const PendingRequest&) = delete; 0027 PendingRequest(PendingRequest&& oth) 0028 : reply(oth.reply) 0029 { 0030 oth.reply = nullptr; 0031 } 0032 0033 PendingRequest& operator=(PendingRequest&& oth) 0034 { 0035 std::swap(reply, oth.reply); 0036 return *this; 0037 } 0038 0039 ~PendingRequest() 0040 { 0041 if ( reply ) 0042 { 0043 aborted = true; 0044 if ( reply->isRunning() ) 0045 reply->abort(); 0046 reply->deleteLater(); 0047 } 0048 } 0049 0050 QNetworkReply* reply = nullptr; 0051 qint64 received = 0; 0052 qint64 total = 0; 0053 bool aborted = false; 0054 }; 0055 0056 public: 0057 template<class Func> 0058 void get(const QUrl& url, const Func& callback, QObject* receiver = nullptr) 0059 { 0060 auto reply = manager.get(QNetworkRequest(url)); 0061 pending.insert({reply, PendingRequest(reply)}); 0062 connect(reply, &QNetworkReply::downloadProgress, this, &NetworkDownloader::on_download_progress); 0063 connect(reply, &QNetworkReply::finished, receiver ? receiver : this, [this, reply, callback]{ 0064 if ( !reply->error() ) 0065 callback(reply->readAll()); 0066 0067 auto it = pending.find(reply); 0068 if ( it != pending.end() && !it->second.aborted ) 0069 { 0070 total -= it->second.total; 0071 received -= it->second.received; 0072 pending.erase(it); 0073 if ( pending.empty() ) 0074 Q_EMIT download_finished(); 0075 } 0076 }); 0077 } 0078 0079 private Q_SLOTS: 0080 void on_download_progress(qint64 bytes_received, qint64 bytes_total) 0081 { 0082 if ( bytes_total == -1 ) 0083 bytes_total = 0; 0084 0085 QObject* request = sender(); 0086 auto it = pending.find(request); 0087 if ( it == pending.end() ) 0088 return; 0089 0090 if ( bytes_total != it->second.total ) 0091 { 0092 total += bytes_total - it->second.total; 0093 it->second.total = bytes_total; 0094 } 0095 0096 it->second.received = bytes_received; 0097 0098 received += bytes_received; 0099 if ( bytes_total > 0 ) 0100 Q_EMIT download_progress(received, total); 0101 } 0102 0103 Q_SIGNALS: 0104 void download_progress(qint64 bytes_received, qint64 bytes_total); 0105 void download_finished(); 0106 0107 private: 0108 QNetworkAccessManager manager; 0109 std::unordered_map<QObject*, PendingRequest> pending; 0110 qint64 total = 0; 0111 qint64 received = 0; 0112 }; 0113 0114 } // namespace glaxnimate::model