mirror of
https://github.com/overte-org/overte.git
synced 2025-08-07 13:50:35 +02:00
reuse same disk cache as AssetClient
This commit is contained in:
parent
31cdd5cca2
commit
395cc663dd
5 changed files with 128 additions and 115 deletions
|
@ -100,6 +100,105 @@ void AssetClient::cacheInfoRequest(MiniPromise::Promise deferred) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AssetClient::queryCacheMeta(MiniPromise::Promise deferred, const QUrl& url) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "cacheInfoRequest", Q_ARG(MiniPromise::Promise, deferred), Q_ARG(const QUrl&, url));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (auto cache = NetworkAccessManager::getInstance().cache()) {
|
||||||
|
QNetworkCacheMetaData metaData = cache->metaData(url);
|
||||||
|
QVariantMap attributes, rawHeaders;
|
||||||
|
|
||||||
|
QHashIterator<QNetworkRequest::Attribute, QVariant> i(metaData.attributes());
|
||||||
|
while (i.hasNext()) {
|
||||||
|
i.next();
|
||||||
|
attributes[QString::number(i.key())] = i.value();
|
||||||
|
}
|
||||||
|
for (const auto& i : metaData.rawHeaders()) {
|
||||||
|
rawHeaders[i.first] = i.second;
|
||||||
|
}
|
||||||
|
deferred->resolve({
|
||||||
|
{ "isValid", metaData.isValid() },
|
||||||
|
{ "url", metaData.url() },
|
||||||
|
{ "expirationDate", metaData.expirationDate() },
|
||||||
|
{ "lastModified", metaData.lastModified().toString().isEmpty() ? QDateTime() : metaData.lastModified() },
|
||||||
|
{ "saveToDisk", metaData.saveToDisk() },
|
||||||
|
{ "attributes", attributes },
|
||||||
|
{ "rawHeaders", rawHeaders },
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
deferred->reject("cache currently unavailable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetClient::loadFromCache(MiniPromise::Promise deferred, const QUrl& url) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "loadFromCache", Q_ARG(MiniPromise::Promise, deferred), Q_ARG(const QUrl&, url));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (auto cache = NetworkAccessManager::getInstance().cache()) {
|
||||||
|
MiniPromise::Promise metaRequest = makePromise(__FUNCTION__);
|
||||||
|
queryCacheMeta(metaRequest, url);
|
||||||
|
metaRequest->then([&](QString error, QVariantMap metadata) {
|
||||||
|
if (!error.isEmpty()) {
|
||||||
|
deferred->reject(error, metadata);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QVariantMap result = {
|
||||||
|
{ "metadata", metadata },
|
||||||
|
{ "data", QByteArray() },
|
||||||
|
};
|
||||||
|
// caller is responsible for the deletion of the ioDevice, hence the unique_ptr
|
||||||
|
if (auto ioDevice = std::unique_ptr<QIODevice>(cache->data(url))) {
|
||||||
|
QByteArray data = ioDevice->readAll();
|
||||||
|
result["data"] = data;
|
||||||
|
} else {
|
||||||
|
error = "cache data unavailable";
|
||||||
|
}
|
||||||
|
deferred->handle(error, result);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
deferred->reject("cache currently unavailable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// parse RFC 1123 HTTP date format
|
||||||
|
QDateTime parseHttpDate(const QString& dateString) {
|
||||||
|
QDateTime dt = QDateTime::fromString(dateString.left(25), "ddd, dd MMM yyyy HH:mm:ss");
|
||||||
|
dt.setTimeSpec(Qt::UTC);
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetClient::saveToCache(MiniPromise::Promise deferred, const QUrl& url, const QByteArray& data, const QVariantMap& headers) {
|
||||||
|
if (auto cache = NetworkAccessManager::getInstance().cache()) {
|
||||||
|
QDateTime lastModified = headers.contains("last-modified") ?
|
||||||
|
parseHttpDate(headers["last-modified"].toString()) :
|
||||||
|
QDateTime::currentDateTimeUtc();
|
||||||
|
QDateTime expirationDate = headers.contains("expires") ?
|
||||||
|
parseHttpDate(headers["expires"].toString()) :
|
||||||
|
QDateTime(); // never expires
|
||||||
|
QNetworkCacheMetaData metaData;
|
||||||
|
metaData.setUrl(url);
|
||||||
|
metaData.setSaveToDisk(true);
|
||||||
|
metaData.setLastModified(lastModified);
|
||||||
|
metaData.setExpirationDate(expirationDate);
|
||||||
|
if (auto ioDevice = cache->prepare(metaData)) {
|
||||||
|
ioDevice->write(data);
|
||||||
|
cache->insert(ioDevice);
|
||||||
|
qCDebug(asset_client) << url.toDisplayString() << "saved to disk cache ("<< data.size()<<" bytes)";
|
||||||
|
deferred->resolve({{ "success", true }});
|
||||||
|
} else {
|
||||||
|
auto error = QString("Could not save %1 to disk cache").arg(url.toDisplayString());
|
||||||
|
qCWarning(asset_client) << error;
|
||||||
|
deferred->reject(error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
deferred->reject("cache currently unavailable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AssetClient::cacheInfoRequest(QObject* reciever, QString slot) {
|
void AssetClient::cacheInfoRequest(QObject* reciever, QString slot) {
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
QMetaObject::invokeMethod(this, "cacheInfoRequest", Qt::QueuedConnection,
|
QMetaObject::invokeMethod(this, "cacheInfoRequest", Qt::QueuedConnection,
|
||||||
|
|
|
@ -68,6 +68,9 @@ public slots:
|
||||||
|
|
||||||
void cacheInfoRequest(QObject* reciever, QString slot);
|
void cacheInfoRequest(QObject* reciever, QString slot);
|
||||||
void cacheInfoRequest(MiniPromise::Promise deferred);
|
void cacheInfoRequest(MiniPromise::Promise deferred);
|
||||||
|
void queryCacheMeta(MiniPromise::Promise deferred, const QUrl& url);
|
||||||
|
void loadFromCache(MiniPromise::Promise deferred, const QUrl& url);
|
||||||
|
void saveToCache(MiniPromise::Promise deferred, const QUrl& url, const QByteArray& data, const QVariantMap& metadata = QVariantMap());
|
||||||
void clearCache();
|
void clearCache();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
|
@ -31,31 +31,23 @@ QSharedPointer<AssetClient> BaseAssetScriptingInterface::assetClient() {
|
||||||
return DependencyManager::get<AssetClient>();
|
return DependencyManager::get<AssetClient>();
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseAssetScriptingInterface::BaseAssetScriptingInterface(QObject* parent) : QObject(parent), _cache(this) {
|
BaseAssetScriptingInterface::BaseAssetScriptingInterface(QObject* parent) : QObject(parent) {}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool BaseAssetScriptingInterface::initializeCache() {
|
bool BaseAssetScriptingInterface::initializeCache() {
|
||||||
// NOTE: *instances* of QNetworkDiskCache are not thread-safe -- however, different threads can effectively
|
|
||||||
// use the same underlying cache if configured with identical settings. Once AssetClient's disk cache settings
|
|
||||||
// become available we configure our instance to match.
|
|
||||||
auto assets = assetClient();
|
auto assets = assetClient();
|
||||||
if (!assets) {
|
if (!assets) {
|
||||||
return false; // not yet possible to initialize the cache
|
return false; // not yet possible to initialize the cache
|
||||||
}
|
}
|
||||||
if (_cache.cacheDirectory().size()) {
|
if (!_cacheDirectory.isEmpty()) {
|
||||||
return true; // cache is ready
|
return true; // cache is ready
|
||||||
}
|
}
|
||||||
|
|
||||||
// attempt to initialize the cache
|
// attempt to initialize the cache
|
||||||
QMetaObject::invokeMethod(assetClient().data(), "init");
|
QMetaObject::invokeMethod(assets.data(), "init");
|
||||||
|
|
||||||
Promise deferred = makePromise("BaseAssetScriptingInterface--queryCacheStatus");
|
Promise deferred = makePromise("BaseAssetScriptingInterface--queryCacheStatus");
|
||||||
deferred->then([&](QVariantMap result) {
|
deferred->then([&](QVariantMap result) {
|
||||||
auto cacheDirectory = result.value("cacheDirectory").toString();
|
_cacheDirectory = result.value("cacheDirectory").toString();
|
||||||
auto maximumCacheSize = result.value("maximumCacheSize").toLongLong();
|
|
||||||
_cache.setCacheDirectory(cacheDirectory);
|
|
||||||
_cache.setMaximumCacheSize(maximumCacheSize);
|
|
||||||
});
|
});
|
||||||
deferred->fail([&](QString error) {
|
deferred->fail([&](QString error) {
|
||||||
qDebug() << "BaseAssetScriptingInterface::queryCacheStatus ERROR" << QThread::currentThread() << error;
|
qDebug() << "BaseAssetScriptingInterface::queryCacheStatus ERROR" << QThread::currentThread() << error;
|
||||||
|
@ -64,79 +56,28 @@ bool BaseAssetScriptingInterface::initializeCache() {
|
||||||
return false; // cache is not ready yet
|
return false; // cache is not ready yet
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap BaseAssetScriptingInterface::getCacheStatus() {
|
Promise BaseAssetScriptingInterface::getCacheStatus() {
|
||||||
return {
|
Promise deferred = makePromise(__FUNCTION__);
|
||||||
{ "cacheDirectory", _cache.cacheDirectory() },
|
DependencyManager::get<AssetClient>()->cacheInfoRequest(deferred);
|
||||||
{ "cacheSize", _cache.cacheSize() },
|
return deferred;
|
||||||
{ "maximumCacheSize", _cache.maximumCacheSize() },
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap BaseAssetScriptingInterface::queryCacheMeta(const QUrl& url) {
|
Promise BaseAssetScriptingInterface::queryCacheMeta(const QUrl& url) {
|
||||||
QNetworkCacheMetaData metaData = _cache.metaData(url);
|
Promise deferred = makePromise(__FUNCTION__);
|
||||||
QVariantMap attributes, rawHeaders;
|
DependencyManager::get<AssetClient>()->queryCacheMeta(deferred, url);
|
||||||
|
return deferred;
|
||||||
QHashIterator<QNetworkRequest::Attribute, QVariant> i(metaData.attributes());
|
|
||||||
while (i.hasNext()) {
|
|
||||||
i.next();
|
|
||||||
attributes[QString::number(i.key())] = i.value();
|
|
||||||
}
|
|
||||||
for (const auto& i : metaData.rawHeaders()) {
|
|
||||||
rawHeaders[i.first] = i.second;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
{ "isValid", metaData.isValid() },
|
|
||||||
{ "url", metaData.url() },
|
|
||||||
{ "expirationDate", metaData.expirationDate() },
|
|
||||||
{ "lastModified", metaData.lastModified().toString().isEmpty() ? QDateTime() : metaData.lastModified() },
|
|
||||||
{ "saveToDisk", metaData.saveToDisk() },
|
|
||||||
{ "attributes", attributes },
|
|
||||||
{ "rawHeaders", rawHeaders },
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap BaseAssetScriptingInterface::loadFromCache(const QUrl& url) {
|
Promise BaseAssetScriptingInterface::loadFromCache(const QUrl& url) {
|
||||||
QVariantMap result = {
|
Promise deferred = makePromise(__FUNCTION__);
|
||||||
{ "metadata", queryCacheMeta(url) },
|
DependencyManager::get<AssetClient>()->loadFromCache(deferred, url);
|
||||||
{ "data", QByteArray() },
|
return deferred;
|
||||||
};
|
|
||||||
// caller is responsible for the deletion of the ioDevice, hence the unique_ptr
|
|
||||||
if (auto ioDevice = std::unique_ptr<QIODevice>(_cache.data(url))) {
|
|
||||||
QByteArray data = ioDevice->readAll();
|
|
||||||
result["data"] = data;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
Promise BaseAssetScriptingInterface::saveToCache(const QUrl& url, const QByteArray& data, const QVariantMap& headers) {
|
||||||
// parse RFC 1123 HTTP date format
|
Promise deferred = makePromise(__FUNCTION__);
|
||||||
QDateTime parseHttpDate(const QString& dateString) {
|
DependencyManager::get<AssetClient>()->saveToCache(deferred, url, data, headers);
|
||||||
QDateTime dt = QDateTime::fromString(dateString.left(25), "ddd, dd MMM yyyy HH:mm:ss");
|
return deferred;
|
||||||
dt.setTimeSpec(Qt::UTC);
|
|
||||||
return dt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BaseAssetScriptingInterface::saveToCache(const QUrl& url, const QByteArray& data, const QVariantMap& headers) {
|
|
||||||
QDateTime lastModified = headers.contains("last-modified") ?
|
|
||||||
parseHttpDate(headers["last-modified"].toString()) :
|
|
||||||
QDateTime::currentDateTimeUtc();
|
|
||||||
QDateTime expirationDate = headers.contains("expires") ?
|
|
||||||
parseHttpDate(headers["expires"].toString()) :
|
|
||||||
QDateTime(); // never expires
|
|
||||||
QNetworkCacheMetaData metaData;
|
|
||||||
metaData.setUrl(url);
|
|
||||||
metaData.setSaveToDisk(true);
|
|
||||||
metaData.setLastModified(lastModified);
|
|
||||||
metaData.setExpirationDate(expirationDate);
|
|
||||||
if (auto ioDevice = _cache.prepare(metaData)) {
|
|
||||||
ioDevice->write(data);
|
|
||||||
_cache.insert(ioDevice);
|
|
||||||
qCDebug(asset_client) << url.toDisplayString() << "saved to disk cache ("<< data.size()<<" bytes)";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
qCWarning(asset_client) << "Could not save" << url.toDisplayString() << "to disk cache.";
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise BaseAssetScriptingInterface::loadAsset(QString asset, bool decompress, QString responseType) {
|
Promise BaseAssetScriptingInterface::loadAsset(QString asset, bool decompress, QString responseType) {
|
||||||
|
|
|
@ -33,15 +33,7 @@ public:
|
||||||
BaseAssetScriptingInterface(QObject* parent = nullptr);
|
BaseAssetScriptingInterface(QObject* parent = nullptr);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
/**jsdoc
|
Promise getCacheStatus();
|
||||||
* Get the current status of the disk cache (if available)
|
|
||||||
* @function Assets.uploadData
|
|
||||||
* @static
|
|
||||||
* @return {String} result.cacheDirectory (path to the current disk cache)
|
|
||||||
* @return {Number} result.cacheSize (used cache size in bytes)
|
|
||||||
* @return {Number} result.maximumCacheSize (maxmimum cache size in bytes)
|
|
||||||
*/
|
|
||||||
QVariantMap getCacheStatus();
|
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Initialize the disk cache (returns true if already initialized)
|
* Initialize the disk cache (returns true if already initialized)
|
||||||
|
@ -56,14 +48,14 @@ public slots:
|
||||||
QString extractAssetHash(QString input) { return AssetUtils::extractAssetHash(input); }
|
QString extractAssetHash(QString input) { return AssetUtils::extractAssetHash(input); }
|
||||||
bool isValidHash(QString input) { return AssetUtils::isValidHash(input); }
|
bool isValidHash(QString input) { return AssetUtils::isValidHash(input); }
|
||||||
QByteArray hashData(const QByteArray& data) { return AssetUtils::hashData(data); }
|
QByteArray hashData(const QByteArray& data) { return AssetUtils::hashData(data); }
|
||||||
|
QString hashDataHex(const QByteArray& data) { return hashData(data).toHex(); }
|
||||||
|
|
||||||
virtual QVariantMap queryCacheMeta(const QUrl& url);
|
virtual Promise queryCacheMeta(const QUrl& url);
|
||||||
virtual QVariantMap loadFromCache(const QUrl& url);
|
virtual Promise loadFromCache(const QUrl& url);
|
||||||
virtual bool saveToCache(const QUrl& url, const QByteArray& data, const QVariantMap& metadata = QVariantMap());
|
virtual Promise saveToCache(const QUrl& url, const QByteArray& data, const QVariantMap& metadata = QVariantMap());
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//void onCacheInfoResponse(QString cacheDirectory, qint64 cacheSize, qint64 maximumCacheSize);
|
QString _cacheDirectory;
|
||||||
QNetworkDiskCache _cache;
|
|
||||||
const QString NoError{};
|
const QString NoError{};
|
||||||
//virtual bool jsAssert(bool condition, const QString& error) = 0;
|
//virtual bool jsAssert(bool condition, const QString& error) = 0;
|
||||||
Promise loadAsset(QString asset, bool decompress, QString responseType);
|
Promise loadAsset(QString asset, bool decompress, QString responseType);
|
||||||
|
|
|
@ -75,28 +75,6 @@ void AssetScriptingInterface::setMapping(QString path, QString hash, QScriptValu
|
||||||
setMappingRequest->start();
|
setMappingRequest->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetScriptingInterface::getMapping(QString path, QScriptValue callback) {
|
|
||||||
auto request = DependencyManager::get<AssetClient>()->createGetMappingRequest(path);
|
|
||||||
QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable {
|
|
||||||
auto result = request->getError();
|
|
||||||
if (callback.isFunction()) {
|
|
||||||
if (result == GetMappingRequest::NotFound) {
|
|
||||||
QScriptValueList args { "", true };
|
|
||||||
callback.call(_engine->currentContext()->thisObject(), args);
|
|
||||||
} else if (result == GetMappingRequest::NoError) {
|
|
||||||
QScriptValueList args { request->getHash(), true };
|
|
||||||
callback.call(_engine->currentContext()->thisObject(), args);
|
|
||||||
} else {
|
|
||||||
qCDebug(scriptengine) << "error -- " << request->getError() << " -- " << request->getErrorString();
|
|
||||||
QScriptValueList args { "", false };
|
|
||||||
callback.call(_engine->currentContext()->thisObject(), args);
|
|
||||||
}
|
|
||||||
request->deleteLater();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
request->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callback) {
|
void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callback) {
|
||||||
// FIXME: historically this API method failed silently when given a non-atp prefixed
|
// FIXME: historically this API method failed silently when given a non-atp prefixed
|
||||||
// urlString (or if the AssetRequest failed).
|
// urlString (or if the AssetRequest failed).
|
||||||
|
|
Loading…
Reference in a new issue