KPublicTransport

assetrepository.cpp
1/*
2 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "assetrepository_p.h"
8#include "logging.h"
9
10#include <KPublicTransport/Attribution>
11
12#include <QDebug>
13#include <QDir>
14#include <QFileInfo>
15#include <QJsonArray>
16#include <QJsonDocument>
17#include <QNetworkAccessManager>
18#include <QNetworkReply>
19#include <QStandardPaths>
20#include <QUrl>
21
22using namespace KPublicTransport;
23
24AssetRepository* AssetRepository::s_instance = nullptr;
25
26AssetRepository::AssetRepository(QObject *parent)
27 : QObject(parent)
28{
29 if (!s_instance) {
30 s_instance = this;
31 }
32}
33
34AssetRepository::~AssetRepository()
35{
36 if (s_instance == this) {
37 s_instance = nullptr;
38 }
39}
40
41AssetRepository* AssetRepository::instance()
42{
43 return s_instance;
44}
45
46void AssetRepository::setNetworkAccessManagerProvider(std::function<QNetworkAccessManager*()> namProvider)
47{
48 m_namProvider = namProvider;
49}
50
51static QString cachePath()
52{
53 return QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/org.kde.kpublictransport/assets/");
54}
55
56QUrl AssetRepository::localFile(const QUrl& url)
57{
58 if (!url.isValid() || url.fileName().isEmpty()) {
59 return {};
60 }
61
62 QFileInfo fi(cachePath() + url.fileName());
63 if (fi.exists() && fi.size() > 0) {
64 return QUrl::fromLocalFile(fi.absoluteFilePath());
65 }
66 return {};
67}
68
69bool AssetRepository::download(const QUrl &url)
70{
71 if (!url.isValid() || url.scheme() != QLatin1String("https") || url.fileName().isEmpty() || !m_namProvider || !m_namProvider()) {
72 return false;
73 }
74
75 if (QFileInfo::exists(cachePath() + url.fileName())) { // already downloaded, or persistent error
76 return false;
77 }
78
79 if (std::find(m_queue.begin(), m_queue.end(), url) != m_queue.end()) {
80 return false;
81 }
82
83 m_queue.push_back(url);
84 if (m_queue.size() == 1) {
85 downloadNext();
86 }
87 return true;
88}
89
90bool AssetRepository::isQueueEmpty()
91{
92 return m_queue.empty();
93}
94
95void AssetRepository::downloadNext()
96{
97 if (m_queue.empty()) {
98 Q_EMIT downloadFinished();
99 return;
100 }
101
102 QNetworkRequest req(m_queue.front());
103 auto reply = m_namProvider()->get(req);
104 connect(reply, &QNetworkReply::finished, this, [this, reply]() {
105 reply->deleteLater();
106
107 switch (reply->error()) {
108 case QNetworkReply::NoError:
109 {
110 QDir().mkpath(cachePath());
111 QFile f(cachePath() + reply->request().url().fileName());
112 if (!f.open(QFile::WriteOnly)) {
113 qWarning() << "Failed to open file for storing asset" << f.errorString() << f.fileName();
114 } else {
115 f.write(reply->readAll());
116 }
117 break;
118 }
119 // persistent errors, empty file prevents forther
121 case QNetworkReply::ContentGoneError:
122 case QNetworkReply::UnknownContentError:
123 case QNetworkReply::TooManyRedirectsError:
124 {
125 qWarning() << reply->errorString();
126 QDir().mkpath(cachePath());
127 QFile f(cachePath() + reply->request().url().fileName());
128 f.open(QFile::WriteOnly);
129 break;
130 }
131 // transient errors
132 default:
133 qWarning() << reply->errorString();
134 break;
135 }
136
137 m_queue.pop_front();
138 downloadNext();
139 });
140}
141
142const std::vector<Attribution>& AssetRepository::attributions() const
143{
144 if (m_attributions.empty()) {
145 QFile f(QStringLiteral(":/org.kde.kpublictransport/assets/asset-attributions.json"));
146 if (!f.open(QFile::ReadOnly)) {
147 qCWarning(Log) << f.fileName() << f.errorString();
148 return m_attributions;
149 }
150
151 m_attributions = Attribution::fromJson(QJsonDocument::fromJson(f.readAll()).array());
152 }
153
154 return m_attributions;
155}
static Attribution fromJson(const QJsonObject &obj)
Deserialize an Attribution object from JSON.
Query operations and data types for accessing realtime public transport information from online servi...
bool mkpath(const QString &dirPath) const const
bool exists() const const
QString errorString() const const
QJsonArray array() const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QString writableLocation(StandardLocation type)
bool isEmpty() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QString fileName(ComponentFormattingOptions options) const const
QUrl fromLocalFile(const QString &localFile)
bool isValid() const const
QString scheme() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:06 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.