1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
// SPDX-License-Identifier: GPL-2.0
#include "dive.h"
#include "metrics.h"
#include "divelist.h"
#include "qthelper.h"
#include "imagedownloader.h"
#include <unistd.h>
#include <QString>
#include <QImageReader>
#include <QtConcurrent>
static QUrl cloudImageURL(const char *filename)
{
QString hash = hashString(filename);
return QUrl::fromUserInput(QString("https://cloud.subsurface-divelog.org/images/").append(hash));
}
ImageDownloader::ImageDownloader(const QString &filename_in) : filename(filename_in)
{
}
void ImageDownloader::load(bool fromHash)
{
if (fromHash && loadFromUrl(cloudImageURL(qPrintable(filename))))
return;
// If loading from hash failed, try to load from filename
loadFromUrl(QUrl::fromUserInput(filename));
}
bool ImageDownloader::loadFromUrl(const QUrl &url)
{
bool success = false;
if (url.isValid()) {
QEventLoop loop;
QNetworkAccessManager manager;
QNetworkRequest request(url);
connect(&manager, &QNetworkAccessManager::finished, this,
[this,&success] (QNetworkReply *reply) { saveImage(reply, success); });
connect(&manager, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
qDebug() << "Downloading image from" << url;
QNetworkReply *reply = manager.get(request);
loop.exec();
delete reply;
}
return success;
}
void ImageDownloader::saveImage(QNetworkReply *reply, bool &success)
{
success = false;
QByteArray imageData = reply->readAll();
QImage image;
image.loadFromData(imageData);
if (image.isNull())
return;
success = true;
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(imageData);
QString path = QStandardPaths::standardLocations(QStandardPaths::CacheLocation).first();
QDir dir(path);
if (!dir.exists())
dir.mkpath(path);
QFile imageFile(path.append("/").append(hash.result().toHex()));
if (imageFile.open(QIODevice::WriteOnly)) {
qDebug() << "Write image to" << imageFile.fileName();
QDataStream stream(&imageFile);
stream.writeRawData(imageData.data(), imageData.length());
imageFile.waitForBytesWritten(-1);
imageFile.close();
learnHash(filename, imageFile.fileName(), hash.result());
}
// This should be called to make the picture actually show.
// Problem is DivePictureModel is not in core.
// Nevertheless, the image shows when the dive is selected the next time.
// DivePictureModel::instance()->updateDivePictures();
}
static void loadPicture(QString filename, bool fromHash)
{
static QSet<QString> queuedPictures;
static QMutex pictureQueueMutex;
QMutexLocker locker(&pictureQueueMutex);
if (queuedPictures.contains(filename))
return;
queuedPictures.insert(filename);
locker.unlock();
ImageDownloader download(filename);
download.load(fromHash);
}
// Overwrite QImage::load() so that we can perform better error reporting.
static QImage loadImage(const QString &fileName, const char *format = nullptr)
{
QImageReader reader(fileName, format);
QImage res = reader.read();
if (res.isNull())
qInfo() << "Error loading image" << fileName << (int)reader.error() << reader.errorString();
return res;
}
QImage getHashedImage(const QString &file)
{
QImage res;
QUrl url = QUrl::fromUserInput(localFilePath(file));
if (url.isLocalFile())
res = loadImage(url.toLocalFile());
if (res.isNull()) {
// This did not load anything. Let's try to get the image from other sources
// Let's try to load it locally via its hash
QString filenameLocal = localFilePath(qPrintable(file));
qDebug() << QStringLiteral("Translated filename: %1 -> %2").arg(file, filenameLocal);
if (filenameLocal.isNull()) {
// That didn't produce a local filename.
// Try the cloud server
// TODO: This is dead code at the moment.
QtConcurrent::run(loadPicture, file, true);
} else {
// Load locally from translated file name
res = loadImage(filenameLocal);
if (!res.isNull()) {
// Make sure the hash still matches the image file
QtConcurrent::run(hashPicture, filenameLocal);
} else {
// Interpret filename as URL
QtConcurrent::run(loadPicture, filenameLocal, false);
}
}
} else {
// We loaded successfully. Now, make sure hash is up to date.
QtConcurrent::run(hashPicture, file);
}
return res;
}
|