redai/src/artmodel.cpp

204 lines
6.1 KiB
C++
Raw Normal View History

2023-08-31 09:09:52 +02:00
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include "artmodel.h"
2023-03-27 12:49:53 -04:00
2023-08-31 13:34:34 +02:00
#include <KLocalizedString>
#include <QCoroNetwork>
#include <QCoroTask>
2023-03-27 12:49:53 -04:00
#include <QDirIterator>
#include <QFile>
#include <QFileInfo>
#include <QIcon>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkReply>
2023-03-27 12:49:53 -04:00
#include <QPixmap>
#include <QtConcurrent>
2023-03-27 12:49:53 -04:00
ArtModel::ArtModel(const QDir &definitionDirectory, const QDir &assetDirectory, QObject *parent)
: QAbstractTableModel(parent)
2023-08-31 13:39:00 +02:00
{
piecesFuture = new QFutureWatcher<ArtPiece *>(this);
connect(piecesFuture, &QFutureWatcher<ArtPiece *>::resultReadyAt, this, &ArtModel::pieceFinished);
connect(piecesFuture, &QFutureWatcher<ArtPiece *>::finished, this, &ArtModel::finished);
2023-08-31 13:39:59 +02:00
struct PieceInformation {
QString definition;
QString asset;
};
QVector<PieceInformation> pieceList;
2023-08-31 13:39:59 +02:00
QDirIterator it(definitionDirectory);
while (it.hasNext()) {
QFileInfo info(it.next());
if (!info.isFile()) {
continue;
}
2023-03-27 12:49:53 -04:00
2023-08-31 13:39:00 +02:00
pieceList.push_back(PieceInformation{definitionDirectory.absoluteFilePath(info.baseName()), assetDirectory.absoluteFilePath(info.baseName())});
2023-03-27 12:49:53 -04:00
beginInsertRows(QModelIndex(), static_cast<int>(m_artPieces.size()), static_cast<int>(m_artPieces.size() + 1));
2023-03-27 12:49:53 -04:00
m_artPieces.push_back({});
2023-03-27 12:49:53 -04:00
2023-08-31 13:39:59 +02:00
endInsertRows();
}
2023-03-27 12:49:53 -04:00
const std::function<QCoro::Task<ArtPiece *>(const PieceInformation &info)> loadPiece = [](const PieceInformation &info) -> QCoro::Task<ArtPiece *> {
auto p = new ArtPiece(info.definition, info.asset);
// load thumbnail
if (!p->getThumbnailPath().isLocalFile()) {
QNetworkAccessManager nam;
auto reply = co_await nam.get(QNetworkRequest(p->getThumbnailPath()));
const auto data = reply->readAll();
reply->deleteLater();
2023-03-27 12:49:53 -04:00
p->thumbnail.loadFromData(data, "image/jpeg");
} else {
p->thumbnail.load(p->getThumbnailPath().toLocalFile());
}
p->thumbnail = p->thumbnail.scaled(100, 100, Qt::AspectRatioMode::KeepAspectRatio, Qt::SmoothTransformation);
co_return p;
2023-08-31 13:39:59 +02:00
};
piecesFuture->setFuture(QtConcurrent::mapped(pieceList, [loadPiece](const PieceInformation &info) -> ArtPiece * {
return QCoro::waitFor(loadPiece(info));
}));
2023-03-27 12:49:53 -04:00
}
2023-08-31 13:39:59 +02:00
int ArtModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return static_cast<int>(m_artPieces.size());
2023-03-27 12:49:53 -04:00
}
2023-08-31 13:39:59 +02:00
int ArtModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
2023-08-31 13:34:34 +02:00
return 4;
2023-03-27 12:49:53 -04:00
}
QVariant ArtModel::data(const QModelIndex &index, const int role) const
2023-08-31 13:39:59 +02:00
{
if (!index.isValid()) {
2023-08-31 13:39:59 +02:00
return {};
}
2023-08-31 13:39:59 +02:00
if (m_artPieces[index.row()] == nullptr) {
return {};
}
2023-08-31 13:39:59 +02:00
if (role == Qt::DisplayRole) {
switch (index.column()) {
2023-12-01 15:51:12 -05:00
case 0: {
const QString filename = m_artPieces[index.row()]->filename;
2023-12-01 15:51:12 -05:00
return filename.split('/').last();
}
2023-08-31 13:39:59 +02:00
case 1:
return {};
case 2:
return m_artPieces[index.row()]->title;
2023-08-31 13:39:59 +02:00
case 3:
return m_artPieces[index.row()]->hasAltText;
2023-08-31 13:34:34 +02:00
}
2023-08-31 13:39:59 +02:00
} else if (role == Qt::UserRole) {
return QVariant::fromValue(m_artPieces[index.row()]);
2023-08-31 13:39:59 +02:00
} else if (role == Qt::DecorationRole) {
switch (index.column()) {
case 1:
return m_artPieces[index.row()]->thumbnail;
2023-08-31 13:39:59 +02:00
case 3:
return m_artPieces[index.row()]->hasAltText ? QIcon::fromTheme(QStringLiteral("emblem-checked")) : QIcon::fromTheme(QStringLiteral("emblem-error"));
2023-08-31 13:39:59 +02:00
}
}
2023-03-27 12:49:53 -04:00
2023-08-31 13:39:59 +02:00
return {};
2023-03-27 12:49:53 -04:00
}
2023-08-31 13:39:59 +02:00
QVariant ArtModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Orientation::Horizontal && role == Qt::DisplayRole) {
switch (section) {
case 0:
return i18nc("@title:column", "Filename");
case 1:
return i18nc("@title:column", "Image");
case 2:
return i18nc("@title:column", "Title");
case 3:
return i18nc("@title:column", "Has Alt Text");
default:
Q_UNREACHABLE();
}
}
2023-03-27 12:49:53 -04:00
2023-08-31 13:39:59 +02:00
return QAbstractTableModel::headerData(section, orientation, role);
}
void ArtModel::pieceFinished(const int row)
{
m_artPieces[row] = piecesFuture->resultAt(row);
Q_EMIT dataChanged(index(row, 0), index(row + 1, 0));
}
void ArtModel::finished()
{
std::sort(m_artPieces.begin(), m_artPieces.end(), [](const ArtPiece *a, const ArtPiece *b) {
return a->date > b->date;
});
Q_EMIT dataChanged(index(0, 0), index(static_cast<int>(m_artPieces.size()), 3));
Q_EMIT loadingFinished();
}
ArtPiece::ArtPiece(const QString &filename, const QString &assetFilename)
2023-08-31 13:39:59 +02:00
{
jsonFilename = filename + ".json";
this->filename = assetFilename;
2023-08-31 13:39:59 +02:00
QFile artFile(jsonFilename);
2023-08-31 13:39:59 +02:00
artFile.open(QFile::ReadOnly);
const QJsonDocument artJson = QJsonDocument::fromJson(artFile.readAll());
2023-08-31 13:39:59 +02:00
if (artJson[QStringLiteral("date")].toString().contains(QStringLiteral("-"))) {
date = QDate::fromString(artJson[QStringLiteral("date")].toString(), QStringLiteral("yyyy-MM-dd"));
2023-08-31 13:39:59 +02:00
} else {
date = QDate::fromString(artJson[QStringLiteral("date")].toString(), QStringLiteral("yyyy"));
2023-08-31 13:39:59 +02:00
}
if (artJson.object().contains(QStringLiteral("title"))) {
title = artJson.object()[QStringLiteral("title")].toString();
2023-08-31 13:39:59 +02:00
}
if (artJson.object().contains(QStringLiteral("alt_text"))) {
hasAltText = true;
2023-08-31 13:39:59 +02:00
}
}
QUrl ArtPiece::getImagePath() const
{
if (date.year() > 2022) {
QFileInfo info(filename);
return QUrl::fromUserInput(QStringLiteral("https://images.redstrate.com/art/%1.jpg").arg(info.fileName()));
} else {
return QUrl::fromLocalFile(QStringLiteral("%1.webp").arg(filename));
}
}
QUrl ArtPiece::getThumbnailPath() const
{
if (date.year() > 2022) {
QFileInfo info(filename);
return QUrl::fromUserInput(QStringLiteral("https://images.redstrate.com/thumb/%1.jpg").arg(info.fileName()));
} else {
return QUrl::fromLocalFile(QStringLiteral("%1.webp").arg(filename));
}
}