Copertine: validazione contenuto prima del caching (niente più HTML in cache), id fisso per il dettaglio, handler con sintassi moderna

This commit is contained in:
2026-08-12 13:42:44 +02:00
parent f43521cc67
commit 9d8bd03990
3 changed files with 26 additions and 7 deletions
+4 -3
View File
@@ -172,13 +172,14 @@ Page {
Component.onCompleted: {
if (bookData.coverUrl && bookData.coverUrl.length > 0)
apiClient.fetchImage(bookData.coverUrl, "detail:" + bookData.id)
apiClient.fetchImage(bookData.coverUrl, "detailcover")
}
Connections {
target: apiClient
onImageReady: {
if (id === "detail:" + bookData.id)
// nota: parametro rinominato (imageId) — "id" è parola chiave QML e rompe il confronto
onImageReady: function(imageId, localPath) {
if (imageId === "detailcover")
page.coverSource = localPath
}
}
+2 -2
View File
@@ -175,9 +175,9 @@ Page {
page.loadingMore = false
page.errorMessage = message
}
onImageReady: {
onImageReady: function(imageId, localPath) {
for (var i = 0; i < feedModel.count; i++) {
if (feedModel.get(i).coverId === id) {
if (feedModel.get(i).coverId === imageId) {
feedModel.setProperty(i, "coverSource", localPath)
break
}
+20 -2
View File
@@ -127,6 +127,13 @@ void ApiClient::getFeed(const QString &url)
});
}
static bool looksLikeImage(const QByteArray &data)
{
// sniff dei magic byte per PNG/JPEG/GIF/WebP senza dipendere da QtGui
return data.startsWith("\x89PNG") || data.startsWith("\xFF\xD8")
|| data.startsWith("GIF8") || data.startsWith("RIFF");
}
void ApiClient::fetchImage(const QString &url, const QString &id)
{
if (url.isEmpty()) {
@@ -150,16 +157,27 @@ void ApiClient::fetchImage(const QString &url, const QString &id)
}
QNetworkReply *reply = startGet(imageUrl);
connect(reply, &QNetworkReply::finished, this, [this, reply, id, localPath]() {
connect(reply, &QNetworkReply::finished, this, [this, reply, id, localPath, imageUrl]() {
reply->deleteLater();
const int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (reply->error() != QNetworkReply::NoError || status >= 400) {
emit imageReady(id, QString());
return;
}
const QByteArray body = reply->readAll();
const QByteArray contentType = reply->header(
QNetworkRequest::ContentTypeHeader).toByteArray();
// non cachare risposte che non sono immagini (es. pagine HTML di errore 200)
const bool isImage = contentType.startsWith("image/")
|| (contentType.isEmpty() && looksLikeImage(body));
if (!isImage) {
qDebug() << "fetchImage: risposta non immagine scartata" << imageUrl;
emit imageReady(id, QString());
return;
}
QFile file(localPath);
if (file.open(QIODevice::WriteOnly)) {
file.write(reply->readAll());
file.write(body);
file.close();
emit imageReady(id, localPath);
} else {