File indexing completed on 2024-12-08 07:19:10
0001 /* 0002 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "assetrepository_p.h" 0008 #include "logging.h" 0009 0010 #include <KPublicTransport/Attribution> 0011 0012 #include <QDebug> 0013 #include <QDir> 0014 #include <QFileInfo> 0015 #include <QJsonArray> 0016 #include <QJsonDocument> 0017 #include <QNetworkAccessManager> 0018 #include <QNetworkReply> 0019 #include <QStandardPaths> 0020 #include <QUrl> 0021 0022 using namespace KPublicTransport; 0023 0024 AssetRepository* AssetRepository::s_instance = nullptr; 0025 0026 AssetRepository::AssetRepository(QObject *parent) 0027 : QObject(parent) 0028 { 0029 if (!s_instance) { 0030 s_instance = this; 0031 } 0032 } 0033 0034 AssetRepository::~AssetRepository() 0035 { 0036 if (s_instance == this) { 0037 s_instance = nullptr; 0038 } 0039 } 0040 0041 AssetRepository* AssetRepository::instance() 0042 { 0043 return s_instance; 0044 } 0045 0046 void AssetRepository::setNetworkAccessManagerProvider(std::function<QNetworkAccessManager*()> namProvider) 0047 { 0048 m_namProvider = namProvider; 0049 } 0050 0051 static QString cachePath() 0052 { 0053 return QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/org.kde.kpublictransport/assets/"); 0054 } 0055 0056 QUrl AssetRepository::localFile(const QUrl& url) 0057 { 0058 if (!url.isValid() || url.fileName().isEmpty()) { 0059 return {}; 0060 } 0061 0062 QFileInfo fi(cachePath() + url.fileName()); 0063 if (fi.exists() && fi.size() > 0) { 0064 return QUrl::fromLocalFile(fi.absoluteFilePath()); 0065 } 0066 return {}; 0067 } 0068 0069 bool AssetRepository::download(const QUrl &url) 0070 { 0071 if (!url.isValid() || url.scheme() != QLatin1String("https") || url.fileName().isEmpty() || !m_namProvider || !m_namProvider()) { 0072 return false; 0073 } 0074 0075 if (QFileInfo::exists(cachePath() + url.fileName())) { // already downloaded, or persistent error 0076 return false; 0077 } 0078 0079 if (std::find(m_queue.begin(), m_queue.end(), url) != m_queue.end()) { 0080 return false; 0081 } 0082 0083 m_queue.push_back(url); 0084 if (m_queue.size() == 1) { 0085 downloadNext(); 0086 } 0087 return true; 0088 } 0089 0090 bool AssetRepository::isQueueEmpty() 0091 { 0092 return m_queue.empty(); 0093 } 0094 0095 void AssetRepository::downloadNext() 0096 { 0097 if (m_queue.empty()) { 0098 Q_EMIT downloadFinished(); 0099 return; 0100 } 0101 0102 QNetworkRequest req(m_queue.front()); 0103 auto reply = m_namProvider()->get(req); 0104 connect(reply, &QNetworkReply::finished, this, [this, reply]() { 0105 reply->deleteLater(); 0106 0107 switch (reply->error()) { 0108 case QNetworkReply::NoError: 0109 { 0110 QDir().mkpath(cachePath()); 0111 QFile f(cachePath() + reply->request().url().fileName()); 0112 if (!f.open(QFile::WriteOnly)) { 0113 qWarning() << "Failed to open file for storing asset" << f.errorString() << f.fileName(); 0114 } else { 0115 f.write(reply->readAll()); 0116 } 0117 break; 0118 } 0119 // persistent errors, empty file prevents forther 0120 case QNetworkReply::ContentNotFoundError: 0121 case QNetworkReply::ContentGoneError: 0122 case QNetworkReply::UnknownContentError: 0123 case QNetworkReply::TooManyRedirectsError: 0124 { 0125 qWarning() << reply->errorString(); 0126 QDir().mkpath(cachePath()); 0127 QFile f(cachePath() + reply->request().url().fileName()); 0128 f.open(QFile::WriteOnly); 0129 break; 0130 } 0131 // transient errors 0132 default: 0133 qWarning() << reply->errorString(); 0134 break; 0135 } 0136 0137 m_queue.pop_front(); 0138 downloadNext(); 0139 }); 0140 } 0141 0142 const std::vector<Attribution>& AssetRepository::attributions() const 0143 { 0144 if (m_attributions.empty()) { 0145 QFile f(QStringLiteral(":/org.kde.kpublictransport/assets/asset-attributions.json")); 0146 if (!f.open(QFile::ReadOnly)) { 0147 qCWarning(Log) << f.fileName() << f.errorString(); 0148 return m_attributions; 0149 } 0150 0151 m_attributions = Attribution::fromJson(QJsonDocument::fromJson(f.readAll()).array()); 0152 } 0153 0154 return m_attributions; 0155 }