// SPDX-FileCopyrightText: 2023 Joshua Goins // // SPDX-License-Identifier: GPL-3.0-or-later #include "artmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include ArtModel::ArtModel(const QDir &definitionDirectory, const QDir &assetDirectory, QObject *parent) : QAbstractTableModel(parent) { piecesFuture = new QFutureWatcher(this); connect(piecesFuture, &QFutureWatcher::resultReadyAt, this, &ArtModel::pieceFinished); connect(piecesFuture, &QFutureWatcher::finished, this, &ArtModel::finished); struct PieceInformation { QString definition; QString asset; }; QVector pieceList; QDirIterator it(definitionDirectory); while (it.hasNext()) { QFileInfo info(it.next()); if (!info.isFile()) { continue; } pieceList.push_back(PieceInformation{definitionDirectory.absoluteFilePath(info.baseName()), assetDirectory.absoluteFilePath(info.baseName())}); beginInsertRows(QModelIndex(), static_cast(m_artPieces.size()), static_cast(m_artPieces.size() + 1)); m_artPieces.push_back({}); endInsertRows(); } const std::function(const PieceInformation &info)> loadPiece = [](const PieceInformation &info) -> QCoro::Task { 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(); 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; }; piecesFuture->setFuture(QtConcurrent::mapped(pieceList, [loadPiece](const PieceInformation &info) -> ArtPiece * { return QCoro::waitFor(loadPiece(info)); })); } int ArtModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return static_cast(m_artPieces.size()); } int ArtModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) return 4; } QVariant ArtModel::data(const QModelIndex &index, const int role) const { if (!index.isValid()) { return {}; } if (m_artPieces[index.row()] == nullptr) { return {}; } if (role == Qt::DisplayRole) { switch (index.column()) { case 0: { const QString filename = m_artPieces[index.row()]->filename; return filename.split('/').last(); } case 1: return {}; case 2: return m_artPieces[index.row()]->title; case 3: return m_artPieces[index.row()]->hasAltText; } } else if (role == Qt::UserRole) { return QVariant::fromValue(m_artPieces[index.row()]); } else if (role == Qt::DecorationRole) { switch (index.column()) { case 1: return m_artPieces[index.row()]->thumbnail; case 3: return m_artPieces[index.row()]->hasAltText ? QIcon::fromTheme(QStringLiteral("emblem-checked")) : QIcon::fromTheme(QStringLiteral("emblem-error")); } } return {}; } 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(); } } 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(m_artPieces.size()), 3)); Q_EMIT loadingFinished(); } ArtPiece::ArtPiece(const QString &filename, const QString &assetFilename) { jsonFilename = filename + ".json"; this->filename = assetFilename; QFile artFile(jsonFilename); artFile.open(QFile::ReadOnly); const QJsonDocument artJson = QJsonDocument::fromJson(artFile.readAll()); if (artJson[QStringLiteral("date")].toString().contains(QStringLiteral("-"))) { date = QDate::fromString(artJson[QStringLiteral("date")].toString(), QStringLiteral("yyyy-MM-dd")); } else { date = QDate::fromString(artJson[QStringLiteral("date")].toString(), QStringLiteral("yyyy")); } if (artJson.object().contains(QStringLiteral("title"))) { title = artJson.object()[QStringLiteral("title")].toString(); } if (artJson.object().contains(QStringLiteral("alt_text"))) { hasAltText = true; } } 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)); } }