From b7fb45a3316aef60c5f56e4633e48ba5df16123d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sat, 14 Jun 2025 09:56:33 +0200 Subject: [PATCH] Add support for storing and serving the new comic filters format It's already implemented on iOS and next is supporting it on Android. --- CHANGELOG.md | 3 +- YACReaderLibrary/db/data_base_management.cpp | 35 +++++++++- YACReaderLibrary/db_helper.cpp | 64 ++++++++++++++++++- YACReaderLibrary/db_helper.h | 1 + .../v2/updatecomiccontroller_v2.cpp | 16 ++++- .../server/yacreader_server_data_helper.cpp | 6 ++ common/comic_db.cpp | 35 ++++++++++ common/comic_db.h | 18 +++++- common/yacreader_global.h | 4 +- custom_widgets/whats_new_dialog.cpp | 49 ++++++-------- 10 files changed, 191 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c899522a..b2eb03cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ Version counting is based on semantic versioning (Major.Feature.Patch) -## WIP (9.15.1) +## WIP (9.16.0) ### YACReader * Don't use scroll animations on macos by default, it where hdpi scroll is most likely to be used. * New toolbar on macos. @@ -21,6 +21,7 @@ Version counting is based on semantic versioning (Major.Feature.Patch) * Add support for custom covers for comics using the edit metadata dialog, you can use a pick file button or drag&drop an image into the cover view in the dialog. * Covers can be set in bulk for various comics at once. * New button to reset to the default cover of a comic. +* Support for storing the new image filters from iOS and Android apps. ### YACReaderLibraryServer * Log libraries validation when the app starts. diff --git a/YACReaderLibrary/db/data_base_management.cpp b/YACReaderLibrary/db/data_base_management.cpp index f64f4c50..0aea4457 100644 --- a/YACReaderLibrary/db/data_base_management.cpp +++ b/YACReaderLibrary/db/data_base_management.cpp @@ -81,7 +81,13 @@ static QString fields = "title," "seriesGroup," "mainCharacterOrTeam," "review," - "tags"; + "tags," + // new 9.16 fields + "imageFiltersJson," + "lastTimeImageFiltersSet," + "lastTimeCoverSet," + "usesExternalCover," + "lastTimeMetadataSet"; DataBaseManagement::DataBaseManagement() : QObject(), dataBasesList() @@ -284,7 +290,13 @@ bool DataBaseManagement::createComicInfoTable(QSqlDatabase &database, QString ta "seriesGroup TEXT," "mainCharacterOrTeam TEXT," "review TEXT," - "tags TEXT" + "tags TEXT," + // new 9.16 fields + "imageFiltersJson TEXT," + "lastTimeImageFiltersSet INTEGER DEFAULT 0," + "lastTimeCoverSet INTEGER DEFAULT 0," + "usesExternalCover BOOLEAN DEFAULT 0," + "lastTimeMetadataSet INTEGER DEFAULT 0" ")"); return queryComicInfo.exec(); @@ -862,6 +874,7 @@ bool DataBaseManagement::updateToCurrentVersion(const QString &libraryPath) bool pre9_8 = false; bool pre9_13 = false; bool pre9_14 = false; + bool pre9_16 = false; QString libraryDatabasePath = LibraryPaths::libraryDatabasePath(libraryPath); @@ -879,6 +892,8 @@ bool DataBaseManagement::updateToCurrentVersion(const QString &libraryPath) pre9_13 = true; if (compareVersions(DataBaseManagement::checkValidDB(libraryDatabasePath), "9.14.0") < 0) pre9_14 = true; + if (compareVersions(DataBaseManagement::checkValidDB(libraryDatabasePath), "9.16.0") < 0) + pre9_16 = true; QString connectionName = ""; bool returnValue = true; @@ -1080,6 +1095,22 @@ bool DataBaseManagement::updateToCurrentVersion(const QString &libraryPath) } } + if (pre9_16) { + { // comic_info + QStringList columnDefs; + columnDefs << "imageFiltersJson TEXT"; + columnDefs << "lastTimeImageFiltersSet INTEGER DEFAULT 0"; + + columnDefs << "lastTimeCoverSet INTEGER DEFAULT 0"; + columnDefs << "usesExternalCover BOOLEAN DEFAULT 0"; + + columnDefs << "lastTimeMetadataSet INTEGER DEFAULT 0"; + + bool successAddingColumns = addColumns("comic_info", columnDefs, db); + returnValue = returnValue && successAddingColumns; + } + } + if (returnValue) { QSqlQuery updateVersion(db); updateVersion.prepare("UPDATE db_info SET " diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index e0e2f5c4..c2963004 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -712,7 +712,14 @@ void DBHelper::update(ComicInfo *comicInfo, QSqlDatabase &db) "seriesGroup = :seriesGroup," "mainCharacterOrTeam = :mainCharacterOrTeam," "review = :review," - "tags = :tags" + "tags = :tags," + + // new 9.16 fields + "imageFiltersJson = :imageFiltersJson," + "lastTimeImageFiltersSet = :lastTimeImageFiltersSet," + "lastTimeCoverSet = :lastTimeCoverSet," + "usesExternalCover = :usesExternalCover," + "lastTimeMetadataSet = :lastTimeMetadataSet" //-- " WHERE id = :id"); @@ -790,6 +797,12 @@ void DBHelper::update(ComicInfo *comicInfo, QSqlDatabase &db) updateComicInfo.bindValue(":review", comicInfo->review); updateComicInfo.bindValue(":tags", comicInfo->tags); + updateComicInfo.bindValue(":imageFiltersJson", comicInfo->imageFiltersJson); + updateComicInfo.bindValue(":lastTimeImageFiltersSet", comicInfo->lastTimeImageFiltersSet); + updateComicInfo.bindValue(":lastTimeCoverSet", comicInfo->lastTimeCoverSet); + updateComicInfo.bindValue(":usesExternalCover", comicInfo->usesExternalCover); + updateComicInfo.bindValue(":lastTimeMetadataSet", comicInfo->lastTimeMetadataSet); + updateComicInfo.exec(); QLOG_INFO() << updateComicInfo.lastError().databaseText(); @@ -962,6 +975,41 @@ void DBHelper::updateProgress(qulonglong libraryId, const ComicInfo &comicInfo) QSqlDatabase::removeDatabase(connectionName); } +void DBHelper::updateImageFilters(qulonglong libraryId, const ComicInfo &comicInfo) +{ + QString libraryPath = DBHelper::getLibraries().getPath(libraryId); + QString connectionName = ""; + { + QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath)); + + bool found; + ComicDB comic = DBHelper::loadComic(comicInfo.id, db, found); + + if (found && comic.info.lastTimeImageFiltersSet.toULongLong() < comicInfo.lastTimeImageFiltersSet.toULongLong()) { + QSqlQuery updateComicInfo(db); + updateComicInfo.prepare("UPDATE comic_info SET " + "lastTimeImageFiltersSet = :lastTimeImageFiltersSet, " + "imageFiltersJson = :imageFiltersJson " + " WHERE id = :id "); + + updateComicInfo.bindValue(":lastTimeImageFiltersSet", comicInfo.lastTimeImageFiltersSet.toULongLong()); + updateComicInfo.bindValue(":imageFiltersJson", comicInfo.imageFiltersJson); + updateComicInfo.bindValue(":id", comic.info.id); + + auto ret = updateComicInfo.exec(); + if (!ret) { + QLOG_ERROR() << "Error updating image filters for comic ID:" << comicInfo.id; + QLOG_ERROR() << updateComicInfo.lastError().databaseText(); + QLOG_ERROR() << updateComicInfo.lastError().text(); + } + } + + connectionName = db.connectionName(); + } + + QSqlDatabase::removeDatabase(connectionName); +} + void DBHelper::setComicAsReading(qulonglong libraryId, const ComicInfo &comicInfo) { QString libraryPath = DBHelper::getLibraries().getPath(libraryId); @@ -1944,6 +1992,12 @@ ComicInfo DBHelper::getComicInfoFromQuery(QSqlQuery &query, const QString &idKey int review = record.indexOf("review"); int tags = record.indexOf("tags"); + int imageFiltersJson = record.indexOf("imageFiltersJson"); + int lastTimeImageFiltersSet = record.indexOf("lastTimeImageFiltersSet"); + int lastTimeCoverSet = record.indexOf("lastTimeCoverSet"); + int usesExternalCover = record.indexOf("usesExternalCover"); + int lastTimeMetadataSet = record.indexOf("lastTimeMetadataSet"); + ComicInfo comicInfo; comicInfo.hash = query.value(hash).toString(); @@ -2026,6 +2080,14 @@ ComicInfo DBHelper::getComicInfoFromQuery(QSqlQuery &query, const QString &idKey comicInfo.tags = query.value(tags); //-- + // new 9.16 fields + comicInfo.imageFiltersJson = query.value(imageFiltersJson); + comicInfo.lastTimeImageFiltersSet = query.value(lastTimeImageFiltersSet); + comicInfo.lastTimeCoverSet = query.value(lastTimeCoverSet); + comicInfo.usesExternalCover = query.value(usesExternalCover).toBool(); + comicInfo.lastTimeMetadataSet = query.value(lastTimeMetadataSet); + //-- + comicInfo.existOnDb = true; return comicInfo; diff --git a/YACReaderLibrary/db_helper.h b/YACReaderLibrary/db_helper.h index 66d3981d..37d80250 100644 --- a/YACReaderLibrary/db_helper.h +++ b/YACReaderLibrary/db_helper.h @@ -72,6 +72,7 @@ public: static Folder updateChildrenInfo(qulonglong folderId, QSqlDatabase &db); static void updateChildrenInfo(QSqlDatabase &db); static void updateProgress(qulonglong libraryId, const ComicInfo &comicInfo); + static void updateImageFilters(qulonglong libraryId, const ComicInfo &comicInfo); static void setComicAsReading(qulonglong libraryId, const ComicInfo &comicInfo); [[deprecated("Server v1")]] static void updateFromRemoteClient(qulonglong libraryId, const ComicInfo &comicInfo); static void updateReadingRemoteProgress(const ComicInfo &comicInfo, QSqlDatabase &db); diff --git a/YACReaderLibrary/server/controllers/v2/updatecomiccontroller_v2.cpp b/YACReaderLibrary/server/controllers/v2/updatecomiccontroller_v2.cpp index d11ce4b6..94324838 100644 --- a/YACReaderLibrary/server/controllers/v2/updatecomiccontroller_v2.cpp +++ b/YACReaderLibrary/server/controllers/v2/updatecomiccontroller_v2.cpp @@ -34,7 +34,6 @@ void UpdateComicControllerV2::service(HttpRequest &request, HttpResponse &respon ComicInfo info; info.currentPage = currentPage; info.id = comicId; - DBHelper::updateProgress(libraryId, info); if (data.length() > 1) { if (data.at(1).isEmpty() == false) { @@ -43,8 +42,23 @@ void UpdateComicControllerV2::service(HttpRequest &request, HttpResponse &respon info.id = nextComicId.toULongLong(); DBHelper::setComicAsReading(libraryId, info); } + + if (data.length() > 2) { + QStringList imageFiltersData = data.at(2).split("\t"); + auto epoch = imageFiltersData.at(0).toULongLong(); + QString imageFiltersJson = imageFiltersData.at(1); + + ComicInfo imageFiltersInfo; + imageFiltersInfo.id = comicId; + imageFiltersInfo.lastTimeImageFiltersSet = epoch; + imageFiltersInfo.imageFiltersJson = imageFiltersJson.isEmpty() ? QVariant() : imageFiltersJson; + + DBHelper::updateImageFilters(libraryId, imageFiltersInfo); + } } + DBHelper::updateProgress(libraryId, info); + error = false; updatedLibraryId = libraryId; updatedComicId = comicId; diff --git a/YACReaderLibrary/server/yacreader_server_data_helper.cpp b/YACReaderLibrary/server/yacreader_server_data_helper.cpp index d43e0c51..7fafe7ab 100644 --- a/YACReaderLibrary/server/yacreader_server_data_helper.cpp +++ b/YACReaderLibrary/server/yacreader_server_data_helper.cpp @@ -170,6 +170,12 @@ QJsonObject YACReaderServerDataHelper::fullComicToJSON(const qulonglong libraryI json["brightness"] = comic.info.brightness; json["contrast"] = comic.info.contrast; json["gamma"] = comic.info.gamma; + // 9.16 + variantToJson("image_filters_json", QMetaType::QString, comic.info.imageFiltersJson, json); + variantToJson("last_time_image_filters_set", QMetaType::LongLong, comic.info.lastTimeImageFiltersSet, json); + variantToJson("last_time_cover_set", QMetaType::LongLong, comic.info.lastTimeCoverSet, json); + variantToJson("uses_external_cover", QMetaType::Bool, comic.info.usesExternalCover, json); + variantToJson("last_time_metadata_set", QMetaType::LongLong, comic.info.lastTimeMetadataSet, json); return json; } diff --git a/common/comic_db.cpp b/common/comic_db.cpp index 3b57650b..9fa6edb3 100644 --- a/common/comic_db.cpp +++ b/common/comic_db.cpp @@ -166,6 +166,23 @@ QString ComicDB::toTXT() if (!info.tags.isNull()) txt.append(QString("tags:%1\r\n").arg(info.tags.toString())); + // 9.16 + + if (!info.imageFiltersJson.isNull()) + txt.append(QString("imageFiltersJson:%1\r\n").arg(info.imageFiltersJson.toString())); + + if (!info.lastTimeImageFiltersSet.isNull()) + txt.append(QString("lastTimeImageFiltersSet:%1\r\n").arg(info.lastTimeImageFiltersSet.toULongLong())); + + if (!info.lastTimeCoverSet.isNull()) + txt.append(QString("lastTimeCoverSet:%1\r\n").arg(info.lastTimeCoverSet.toULongLong())); + + if (!info.usesExternalCover.isNull()) + txt.append(QString("usesExternalCover:%1\r\n").arg(info.usesExternalCover.toBool() ? "1" : "0")); + + if (!info.lastTimeMetadataSet.isNull()) + txt.append(QString("lastTimeMetadataSet:%1\r\n").arg(info.lastTimeMetadataSet.toULongLong())); + return txt; } @@ -399,6 +416,12 @@ ComicInfo &ComicInfo::operator=(const ComicInfo &comicInfo) review = comicInfo.review; tags = comicInfo.tags; + imageFiltersJson = comicInfo.imageFiltersJson; + lastTimeImageFiltersSet = comicInfo.lastTimeImageFiltersSet; + lastTimeCoverSet = comicInfo.lastTimeCoverSet; + usesExternalCover = comicInfo.usesExternalCover; + lastTimeMetadataSet = comicInfo.lastTimeMetadataSet; + return *this; } @@ -691,6 +714,12 @@ QDataStream &operator<<(QDataStream &stream, const ComicInfo &comicInfo) stream << comicInfo.review; stream << comicInfo.tags; + stream << comicInfo.imageFiltersJson; + stream << comicInfo.lastTimeImageFiltersSet; + stream << comicInfo.lastTimeCoverSet; + stream << comicInfo.usesExternalCover; + stream << comicInfo.lastTimeMetadataSet; + return stream; } @@ -770,5 +799,11 @@ QDataStream &operator>>(QDataStream &stream, ComicInfo &comicInfo) stream >> comicInfo.review; stream >> comicInfo.tags; + stream >> comicInfo.imageFiltersJson; + stream >> comicInfo.lastTimeImageFiltersSet; + stream >> comicInfo.lastTimeCoverSet; + stream >> comicInfo.usesExternalCover; + stream >> comicInfo.lastTimeMetadataSet; + return stream; } diff --git a/common/comic_db.h b/common/comic_db.h index 77554c45..9f3b70d6 100644 --- a/common/comic_db.h +++ b/common/comic_db.h @@ -39,9 +39,9 @@ public: int bookmark1; int bookmark2; int bookmark3; - int brightness; - int contrast; - int gamma; + [[deprecated("Use imageFiltersJson")]] int brightness; + [[deprecated("Use imageFiltersJson")]] int contrast; + [[deprecated("Use imageFiltersJson")]] int gamma; //----------------- QVariant title; // string @@ -104,6 +104,12 @@ public: QVariant review; // string QVariant tags; // string/list + QVariant imageFiltersJson; // string, JSON with image filters + QVariant lastTimeImageFiltersSet; // integer/date, last time image filters were set + QVariant lastTimeCoverSet; // integer/date, last time cover was set + QVariant usesExternalCover; // bool, whether the cover is external or not + QVariant lastTimeMetadataSet; // integer/date, last time metadata was set + QPixmap getCover(const QString &basePath); Q_INVOKABLE QStringList getWriters(); @@ -208,6 +214,12 @@ public: Q_PROPERTY(QVariant review MEMBER review CONSTANT) Q_PROPERTY(QVariant tags MEMBER tags CONSTANT) + Q_PROPERTY(QVariant imageFiltersJson MEMBER imageFiltersJson CONSTANT) + Q_PROPERTY(QVariant lastTimeImageFiltersSet MEMBER lastTimeImageFiltersSet CONSTANT) + Q_PROPERTY(QVariant lastTimeCoverSet MEMBER lastTimeCoverSet CONSTANT) + Q_PROPERTY(QVariant usesExternalCover MEMBER usesExternalCover CONSTANT) + Q_PROPERTY(QVariant lastTimeMetadataSet MEMBER lastTimeMetadataSet CONSTANT) + //-new properties, not loaded from the DB automatically bool isFavorite; Q_PROPERTY(bool isFavorite MEMBER isFavorite WRITE setFavorite NOTIFY favoriteChanged) diff --git a/common/yacreader_global.h b/common/yacreader_global.h index 3508bfcb..57828611 100644 --- a/common/yacreader_global.h +++ b/common/yacreader_global.h @@ -9,11 +9,11 @@ class QLibrary; -#define VERSION "9.15.1" +#define VERSION "9.16.0" // Used to check if the database needs to be updated, the version is stored in the database. // This value is only incremented when the database structure changes. -#define DB_VERSION "9.14.0" +#define DB_VERSION "9.16.0" #define IMPORT_COMIC_INFO_XML_METADATA "IMPORT_COMIC_INFO_XML_METADATA" #define COMPARE_MODIFIED_DATE_ON_LIBRARY_UPDATES "COMPARE_MODIFIED_DATE_ON_LIBRARY_UPDATES" diff --git a/custom_widgets/whats_new_dialog.cpp b/custom_widgets/whats_new_dialog.cpp index c0947291..d9a09861 100644 --- a/custom_widgets/whats_new_dialog.cpp +++ b/custom_widgets/whats_new_dialog.cpp @@ -43,42 +43,31 @@ YACReader::WhatsNewDialog::WhatsNewDialog(QWidget *parent) versionLabel->setAlignment(Qt::AlignCenter); versionLabel->setStyleSheet("padding:0 0 0 0;" "background-color:transparent;" - "color:#858585;"); - - auto text = new QLabel(); - text->setText("This version brings new features, improved functionality, enhanced customization options, bug fixes, and performance improvements across all apps:
" + "color:#858585;"); auto text = new QLabel(); + text->setText("This version brings exciting new features, improved functionality, enhanced customization options, bug fixes, and performance improvements across all apps:
" "
" "YACReader
" - " • Save magnifying glass size and zoom level
" - " • Add shortcut to reset the magnifying glass to its defaults (size and zoom), it is `slash` by default but it can be reassigned
" - " • Bump PDF render size
" - " • Fix trackpad scrolling, it makes using trackpads more responsive and natural
" - " • Added more info to Help -> System info
" - " • Don't use scroll animations on macos by default, it where hdpi scroll is most likely to be used and it causes scroll issues. (new 9.15.1)
" + " • Don't use scroll animations on macOS by default, where hdpi scroll is most likely to be used
" + " • New toolbar on macOS
" + " • New mouse modes to turn pages - you can setup the app to use the left/right buttons to turn pages directly or click on the left/right part of the screen to turn pages
" "
" "YACReaderLibrary
" - " • Fix headers in the table view getting stuck in a non-movable state
" - " • Add option to set the type of a library. It will convert all the content to the desired type (comic, manga, etc.) and set that type as the default for the library. Available in the library context menu
" - " • Added more info to Help -> System info
" - " • New setting to open comics in third-party reader apps by entering a command that launches the app, e.g., \"/path/to/the/app {comic_file_path}\". Use `{comic_file_path}` as a placeholder where `YACReaderLibrary` places the path to the comic file
" - " • Purge covers and metadata not being used after a full library update
" - " • Fix crash when updating the current folder content after a library update
" - " • Fix crash when current folder is empty after an update
" - " • Enable dropping content on the FolderContentView
" - " • Fix `open containing folder...` shortcut for comics
" - " • Add a dialog to show information about a library, including the number of folders, comics, and read comics
" - " • Fix occasional crashes when using automatic library updates
" - " • Add setting to hide the \"Continue Reading...\" banner from the home view
" - " • Improve Grid and Flow Info comics view scroll performance
" - " • Improve flexibility of the open comic in third party app setting so more complex commands can be used. e.g. `open -a \"/Applications/My Reader.app" - "{comic_file_path}\"` (new 9.15.1)
" + " • Improve flexibility of the open comic in third party app setting so more complex commands can be used, e.g. `open -a \"/Applications/My Reader.app\" \"{comic_file_path}\"`
" + " • Fix setting the comic rating in the table view
" + " • Log libraries validation when the app starts
" + " • New toolbar on macOS
" + " • New setting in Comic Vine scraper to force exact volume matches
" + " • Better default search query in the Comic Vine scraper
" + " • Improved navigation in Comic Vine scraper, including keeping the current query around to make edits and refined searches easier
" + " • Add support for custom covers for any folder using the context menu
" + " • The edit cover buttons now support looping through pages, going forward from the last returns to the first, and going backward from the first jumps to the last
" + " • Add support for custom covers for comics using the edit metadata dialog, you can use a pick file button or drag&drop an image into the cover view in the dialog
" + " • Covers can be set in bulk for various comics at once
" + " • New button to reset to the default cover of a comic
" + " • Support for storing the new image filters from iOS and Android apps
" "
" "YACReaderLibraryServer
" - " • New command --system-info to print information about the execution environment and available resources (including what image formats are supported and what libraries are used by the app).
" - " • Fix automatic libraries updates not being triggered.
" - "
" - "All apps
" - " • Sorting heuristic to try to find spreads in the content of a comic is now only used for files with less than 1000 pages to avoid false positives.
" + " • Log libraries validation when the app starts
" "
" "I hope you enjoy the new update. Please, if you like YACReader consider to become a patron in Patreon " "or donate some money using Pay-Pal and help keeping the project alive. "