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