diff --git a/CHANGELOG.md b/CHANGELOG.md index c62d3dcd..9f8dc6d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Version counting is based on semantic versioning (Major.Feature.Patch) ### YACReaderLibraryServer * Add `rescan-xml-info` command. +* Improved API to provide better integration with the clients (Android 1.4.0 and iOS 3.29.0). ## All Apps * New universal builds for macos. diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index c57d3dfe..a82fd48a 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -189,6 +189,57 @@ QString DBHelper::getFolderName(qulonglong libraryId, qulonglong id) QSqlDatabase::removeDatabase(connectionName); return name; } + +Folder DBHelper::getFolder(qulonglong libraryId, qulonglong id) +{ + QString libraryPath = DBHelper::getLibraries().getPath(libraryId); + + Folder folder; + QString connectionName = ""; + + { + QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary"); + QSqlQuery selectQuery(db); // TODO check + selectQuery.prepare("SELECT * FROM folder WHERE id = :id"); + selectQuery.bindValue(":id", id); + selectQuery.exec(); + + auto record = selectQuery.record(); + + int name = record.indexOf("name"); + int path = record.indexOf("path"); + int finished = record.indexOf("finished"); + int completed = record.indexOf("completed"); + int id = record.indexOf("id"); + int parentId = record.indexOf("parentId"); + int numChildren = record.indexOf("numChildren"); + int firstChildHash = record.indexOf("firstChildHash"); + int customImage = record.indexOf("customImage"); + int type = record.indexOf("type"); + int added = record.indexOf("added"); + int updated = record.indexOf("updated"); + + if (selectQuery.next()) { + folder = Folder(selectQuery.value(id).toULongLong(), parentId, selectQuery.value(name).toString(), selectQuery.value(path).toString()); + + folder.finished = selectQuery.value(finished).toBool(); + folder.completed = selectQuery.value(completed).toBool(); + if (!selectQuery.value(numChildren).isNull() && selectQuery.value(numChildren).isValid()) { + folder.numChildren = selectQuery.value(numChildren).toInt(); + } + folder.firstChildHash = selectQuery.value(firstChildHash).toString(); + folder.customImage = selectQuery.value(customImage).toString(); + folder.type = selectQuery.value(type).value(); + folder.added = selectQuery.value(added).toLongLong(); + folder.updated = selectQuery.value(updated).toLongLong(); + } + connectionName = db.connectionName(); + } + + QSqlDatabase::removeDatabase(connectionName); + return folder; +} + QList DBHelper::getLibrariesNames() { auto names = getLibraries().getNames(); @@ -292,7 +343,7 @@ QList DBHelper::getReading(qulonglong libraryId) { QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary"); QSqlQuery selectQuery(db); - selectQuery.prepare("SELECT c.id,c.parentId,c.fileName,ci.title,ci.currentPage,ci.numPages,ci.hash,ci.read,ci.coverSizeRatio " + selectQuery.prepare("SELECT c.id,c.parentId,c.fileName,ci.title,ci.currentPage,ci.numPages,ci.hash,ci.read,ci.coverSizeRatio,ci.number " "FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) " "WHERE ci.hasBeenOpened = 1 AND ci.read = 0 " "ORDER BY ci.lastTimeOpened DESC"); @@ -301,6 +352,7 @@ QList DBHelper::getReading(qulonglong libraryId) while (selectQuery.next()) { ComicDB comic; + // TODO: use QVariant when possible to keep nulls comic.id = selectQuery.value(0).toULongLong(); comic.parentId = selectQuery.value(1).toULongLong(); comic.name = selectQuery.value(2).toString(); @@ -310,6 +362,7 @@ QList DBHelper::getReading(qulonglong libraryId) comic.info.hash = selectQuery.value(6).toString(); comic.info.read = selectQuery.value(7).toBool(); comic.info.coverSizeRatio = selectQuery.value(8).toFloat(); + comic.info.number = selectQuery.value(9); list.append(comic); } @@ -1010,13 +1063,18 @@ QMap> DBHelper::updateFromRemoteClient(const QMap comicInfo.id contains comic id ComicDB comic = DBHelper::loadComic(comicInfo.id, db, found); if (comic.info.hash == comicInfo.hash) { bool isMoreRecent = false; // completion takes precedence over lastTimeOpened, if we just want to synchronize the lastest status we should use only lastTimeOpened - if ((comic.info.currentPage > 1 && comic.info.currentPage > comicInfo.currentPage) || comic.info.hasBeenOpened || (comic.info.read && !comicInfo.read)) { + if ((comic.info.currentPage > 1 && comic.info.currentPage > comicInfo.currentPage) || (comic.info.read && !comicInfo.read)) { + isMoreRecent = true; + } + + if (comic.info.hasBeenOpened && comic.info.currentPage > comicInfo.currentPage) { isMoreRecent = true; } diff --git a/YACReaderLibrary/db_helper.h b/YACReaderLibrary/db_helper.h index 5f0ace4c..14dad9b1 100644 --- a/YACReaderLibrary/db_helper.h +++ b/YACReaderLibrary/db_helper.h @@ -31,6 +31,7 @@ public: static ComicDB getComicInfo(qulonglong libraryId, qulonglong id); static QList getSiblings(qulonglong libraryId, qulonglong parentId); static QString getFolderName(qulonglong libraryId, qulonglong id); + static Folder getFolder(qulonglong libraryId, qulonglong id); static QList getLibrariesNames(); static QString getLibraryName(int id); static QList getLabelComics(qulonglong libraryId, qulonglong labelId); diff --git a/YACReaderLibrary/server/controllers/v2/comicfullinfocontroller_v2.cpp b/YACReaderLibrary/server/controllers/v2/comicfullinfocontroller_v2.cpp index 344d3a95..8bc43ce6 100644 --- a/YACReaderLibrary/server/controllers/v2/comicfullinfocontroller_v2.cpp +++ b/YACReaderLibrary/server/controllers/v2/comicfullinfocontroller_v2.cpp @@ -4,12 +4,10 @@ #include "db_helper.h" #include "comic_db.h" -#include "folder.h" +#include "yacreader_libraries.h" #include "yacreader_server_data_helper.h" -#include "qnaturalsorting.h" - #include using stefanfrings::HttpRequest; @@ -35,9 +33,11 @@ void ComicFullinfoController_v2::service(HttpRequest &request, HttpResponse &res void ComicFullinfoController_v2::serviceContent(const int &libraryId, const qulonglong &comicId, HttpResponse &response) { + auto libraryUuid = DBHelper::getLibraries().getLibraryIdFromLegacyId(libraryId); + ComicDB comic = DBHelper::getComicInfo(libraryId, comicId); - QJsonObject json = YACReaderServerDataHelper::fullComicToJSON(libraryId, comic); + QJsonObject json = YACReaderServerDataHelper::fullComicToJSON(libraryId, libraryUuid, comic); QJsonDocument output(json); diff --git a/YACReaderLibrary/server/controllers/v2/favoritescontroller_v2.cpp b/YACReaderLibrary/server/controllers/v2/favoritescontroller_v2.cpp index 7717ee84..a7bc39fb 100644 --- a/YACReaderLibrary/server/controllers/v2/favoritescontroller_v2.cpp +++ b/YACReaderLibrary/server/controllers/v2/favoritescontroller_v2.cpp @@ -3,6 +3,7 @@ #include "db_helper.h" #include "comic_db.h" +#include "yacreader_libraries.h" #include "yacreader_server_data_helper.h" using stefanfrings::HttpRequest; @@ -25,12 +26,14 @@ void FavoritesControllerV2::service(HttpRequest &request, HttpResponse &response void FavoritesControllerV2::serviceContent(const int library, HttpResponse &response) { + auto libraryUuid = DBHelper::getLibraries().getLibraryIdFromLegacyId(library); + QList comics = DBHelper::getFavorites(library); QJsonArray items; for (const ComicDB &comic : comics) { - items.append(YACReaderServerDataHelper::comicToJSON(library, comic)); + items.append(YACReaderServerDataHelper::comicToJSON(library, libraryUuid, comic)); } QJsonDocument output(items); diff --git a/YACReaderLibrary/server/controllers/v2/foldercontentcontroller_v2.cpp b/YACReaderLibrary/server/controllers/v2/foldercontentcontroller_v2.cpp index 9c59f67c..6f99993e 100644 --- a/YACReaderLibrary/server/controllers/v2/foldercontentcontroller_v2.cpp +++ b/YACReaderLibrary/server/controllers/v2/foldercontentcontroller_v2.cpp @@ -6,6 +6,7 @@ #include "comic_db.h" #include "folder.h" +#include "yacreader_libraries.h" #include "yacreader_server_data_helper.h" #include "qnaturalsorting.h" @@ -41,6 +42,8 @@ void FolderContentControllerV2::serviceContent(const int &library, const qulongl #ifdef QT_DEBUG auto started = std::chrono::high_resolution_clock::now(); #endif + auto libraryUuid = DBHelper::getLibraries().getLibraryIdFromLegacyId(library); + QList folderContent = DBHelper::getFolderSubfoldersFromLibrary(library, folderId); QList folderComics = DBHelper::getFolderComicsFromLibrary(library, folderId); @@ -56,10 +59,10 @@ void FolderContentControllerV2::serviceContent(const int &library, const qulongl for (QList::const_iterator itr = folderContent.constBegin(); itr != folderContent.constEnd(); itr++) { if ((*itr)->isDir()) { currentFolder = (Folder *)(*itr); - items.append(YACReaderServerDataHelper::folderToJSON(library, *currentFolder)); + items.append(YACReaderServerDataHelper::folderToJSON(library, libraryUuid, *currentFolder)); } else { currentComic = (ComicDB *)(*itr); - items.append(YACReaderServerDataHelper::comicToJSON(library, *currentComic)); + items.append(YACReaderServerDataHelper::comicToJSON(library, libraryUuid, *currentComic)); } } diff --git a/YACReaderLibrary/server/controllers/v2/foldermetadatacontroller_v2.cpp b/YACReaderLibrary/server/controllers/v2/foldermetadatacontroller_v2.cpp new file mode 100644 index 00000000..8910dcc7 --- /dev/null +++ b/YACReaderLibrary/server/controllers/v2/foldermetadatacontroller_v2.cpp @@ -0,0 +1,34 @@ +#include "foldermetadatacontroller_v2.h" + +#include "db_helper.h" +#include "folder.h" + +#include "yacreader_libraries.h" +#include "yacreader_server_data_helper.h" + +FolderMetadataControllerV2::FolderMetadataControllerV2() { } + +void FolderMetadataControllerV2::service(stefanfrings::HttpRequest &request, stefanfrings::HttpResponse &response) +{ + response.setHeader("Content-Type", "application/json"); + + QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); + QStringList pathElements = path.split('/'); + int libraryId = pathElements.at(3).toInt(); + qulonglong folderId = pathElements.at(5).toULongLong(); + + auto folder = DBHelper::getFolder(libraryId, folderId); + if (!folder.knownId) { + response.setStatus(404, "not found"); + response.write("404 not found", true); + return; + } + + auto libraryUuid = DBHelper::getLibraries().getLibraryIdFromLegacyId(libraryId); + + auto json = YACReaderServerDataHelper::folderToJSON(libraryId, libraryUuid, folder); + + QJsonDocument output(json); + + response.write(output.toJson(QJsonDocument::Compact)); +} diff --git a/YACReaderLibrary/server/controllers/v2/foldermetadatacontroller_v2.h b/YACReaderLibrary/server/controllers/v2/foldermetadatacontroller_v2.h new file mode 100644 index 00000000..03980355 --- /dev/null +++ b/YACReaderLibrary/server/controllers/v2/foldermetadatacontroller_v2.h @@ -0,0 +1,20 @@ +#ifndef FOLDERMETADATACONTROLLERV2_H +#define FOLDERMETADATACONTROLLERV2_H + +#include "httprequest.h" +#include "httpresponse.h" +#include "httprequesthandler.h" + +class FolderMetadataControllerV2 : public stefanfrings::HttpRequestHandler +{ + Q_OBJECT + Q_DISABLE_COPY(FolderMetadataControllerV2) +public: + /** Constructor */ + FolderMetadataControllerV2(); + + /** Generates the response */ + void service(stefanfrings::HttpRequest &request, stefanfrings::HttpResponse &response) override; +}; + +#endif // FOLDERMETADATACONTROLLERV2_H diff --git a/YACReaderLibrary/server/controllers/v2/librariescontroller_v2.cpp b/YACReaderLibrary/server/controllers/v2/librariescontroller_v2.cpp index ca5c84f9..ea949499 100644 --- a/YACReaderLibrary/server/controllers/v2/librariescontroller_v2.cpp +++ b/YACReaderLibrary/server/controllers/v2/librariescontroller_v2.cpp @@ -2,11 +2,6 @@ #include "db_helper.h" //get libraries #include "yacreader_libraries.h" -#include "template.h" -#include "../static.h" - -#include "QsLog.h" - using stefanfrings::HttpRequest; using stefanfrings::HttpResponse; @@ -16,20 +11,18 @@ void LibrariesControllerV2::service(HttpRequest & /* request */, HttpResponse &r { response.setHeader("Content-Type", "application/json"); - YACReaderLibraries libraries = DBHelper::getLibraries(); - QList names = DBHelper::getLibrariesNames(); + auto libraries = DBHelper::getLibraries().sortedLibraries(); QJsonArray librariesJson; - int currentId = 0; - foreach (QString name, names) { - currentId = libraries.getId(name); - QJsonObject library; + foreach (YACReaderLibrary library, libraries) { + QJsonObject libraryJson; - library["name"] = name; - library["id"] = currentId; + libraryJson["name"] = library.getName(); + libraryJson["id"] = library.getLegacyId(); + libraryJson["uuid"] = library.getId().toString(); - librariesJson.append(library); + librariesJson.append(libraryJson); } QJsonDocument output(librariesJson); diff --git a/YACReaderLibrary/server/controllers/v2/readingcomicscontroller_v2.cpp b/YACReaderLibrary/server/controllers/v2/readingcomicscontroller_v2.cpp index 6db58772..a63b3d7c 100644 --- a/YACReaderLibrary/server/controllers/v2/readingcomicscontroller_v2.cpp +++ b/YACReaderLibrary/server/controllers/v2/readingcomicscontroller_v2.cpp @@ -3,6 +3,7 @@ #include "db_helper.h" #include "comic_db.h" +#include "yacreader_libraries.h" #include "yacreader_server_data_helper.h" using stefanfrings::HttpRequest; @@ -28,12 +29,14 @@ void ReadingComicsControllerV2::service(HttpRequest &request, HttpResponse &resp void ReadingComicsControllerV2::serviceContent(const int &library, HttpResponse &response) { + auto libraryUuid = DBHelper::getLibraries().getLibraryIdFromLegacyId(library); + QList readingComics = DBHelper::getReading(library); QJsonArray comics; for (const ComicDB &comic : readingComics) { - comics.append(YACReaderServerDataHelper::comicToJSON(library, comic)); + comics.append(YACReaderServerDataHelper::comicToJSON(library, libraryUuid, comic)); } QJsonDocument output(comics); diff --git a/YACReaderLibrary/server/controllers/v2/readinglistcontentcontroller_v2.cpp b/YACReaderLibrary/server/controllers/v2/readinglistcontentcontroller_v2.cpp index 4a502883..0abb55fc 100644 --- a/YACReaderLibrary/server/controllers/v2/readinglistcontentcontroller_v2.cpp +++ b/YACReaderLibrary/server/controllers/v2/readinglistcontentcontroller_v2.cpp @@ -3,6 +3,7 @@ #include "db_helper.h" #include "comic_db.h" +#include "yacreader_libraries.h" #include "yacreader_server_data_helper.h" using stefanfrings::HttpRequest; @@ -28,12 +29,14 @@ void ReadingListContentControllerV2::service(HttpRequest &request, HttpResponse void ReadingListContentControllerV2::serviceContent(const int &library, const qulonglong &readingListId, HttpResponse &response) { + auto libraryUuid = DBHelper::getLibraries().getLibraryIdFromLegacyId(library); + QList comics = DBHelper::getReadingListFullContent(library, readingListId); QJsonArray items; for (const ComicDB &comic : comics) { - items.append(YACReaderServerDataHelper::comicToJSON(library, comic)); + items.append(YACReaderServerDataHelper::comicToJSON(library, libraryUuid, comic)); } QJsonDocument output(items); diff --git a/YACReaderLibrary/server/controllers/v2/readinglistscontroller_v2.cpp b/YACReaderLibrary/server/controllers/v2/readinglistscontroller_v2.cpp index a791c121..63da5fab 100644 --- a/YACReaderLibrary/server/controllers/v2/readinglistscontroller_v2.cpp +++ b/YACReaderLibrary/server/controllers/v2/readinglistscontroller_v2.cpp @@ -2,6 +2,7 @@ #include "db_helper.h" #include "reading_list.h" +#include "yacreader_libraries.h" #include "yacreader_server_data_helper.h" using stefanfrings::HttpRequest; @@ -26,12 +27,14 @@ void ReadingListsControllerV2::service(HttpRequest &request, HttpResponse &respo void ReadingListsControllerV2::serviceContent(const int library, HttpResponse &response) { + auto libraryUuid = DBHelper::getLibraries().getLibraryIdFromLegacyId(library); + QList readingLists = DBHelper::getReadingLists(library); QJsonArray items; for (QList::const_iterator itr = readingLists.constBegin(); itr != readingLists.constEnd(); itr++) { - items.append(YACReaderServerDataHelper::readingListToJSON(library, *itr)); + items.append(YACReaderServerDataHelper::readingListToJSON(library, libraryUuid, *itr)); } QJsonDocument output(items); diff --git a/YACReaderLibrary/server/controllers/v2/synccontroller_v2.cpp b/YACReaderLibrary/server/controllers/v2/synccontroller_v2.cpp index 46686e92..dc9de808 100644 --- a/YACReaderLibrary/server/controllers/v2/synccontroller_v2.cpp +++ b/YACReaderLibrary/server/controllers/v2/synccontroller_v2.cpp @@ -5,6 +5,7 @@ #include "comic_db.h" #include "db_helper.h" +#include "yacreader_libraries.h" #include "yacreader_server_data_helper.h" using stefanfrings::HttpRequest; @@ -33,10 +34,46 @@ void SyncControllerV2::service(HttpRequest &request, HttpResponse &response) QString hash; QMap> comics; QList comicsWithNoLibrary; + + auto libraries = DBHelper::getLibraries(); + foreach (QString comicInfo, data) { QList comicInfoProgress = comicInfo.split("\t"); - if (comicInfoProgress.length() >= 6) { + if (comicInfoProgress.length() >= 8) { + if (comicInfoProgress.at(0) != "u") { + continue; + } + + auto libraryUuid = QUuid(comicInfoProgress.at(1)); + if (!libraryUuid.isNull()) { + auto libraryId = libraries.getIdFromUuid(libraryUuid); + if (libraryId == -1) { + continue; + } + comicId = comicInfoProgress.at(2).toULongLong(); + hash = comicInfoProgress.at(3); + currentPage = comicInfoProgress.at(4).toInt(); + + ComicInfo info; + info.currentPage = currentPage; + info.hash = hash; // TODO remove the hash check and add UUIDs for libraries + info.id = comicId; + + currentRating = comicInfoProgress.at(5).toInt(); + info.rating = currentRating; + + lastTimeOpened = comicInfoProgress.at(6).toULong(); + info.lastTimeOpened = lastTimeOpened; + + info.read = comicInfoProgress.at(7).toInt(); + + if (!comics.contains(libraryId)) { + comics[libraryId] = QList(); + } + comics[libraryId].push_back(info); + } + } else if (comicInfoProgress.length() >= 6) { if (comicInfoProgress.at(0) != "unknown") { libraryId = comicInfoProgress.at(0).toULongLong(); comicId = comicInfoProgress.at(1).toULongLong(); @@ -81,13 +118,17 @@ void SyncControllerV2::service(HttpRequest &request, HttpResponse &response) } } - auto moreRecentComicsFound = DBHelper::updateFromRemoteClient(comics); - QJsonArray items; - foreach (qulonglong libraryId, moreRecentComicsFound.keys()) { - foreach (ComicDB comic, moreRecentComicsFound[libraryId]) { - items.append(YACReaderServerDataHelper::comicToJSON(libraryId, comic)); + if (!comics.isEmpty()) { + auto moreRecentComicsFound = DBHelper::updateFromRemoteClient(comics); + + foreach (qulonglong libraryId, moreRecentComicsFound.keys()) { + auto libraryUuid = DBHelper::getLibraries().getLibraryIdFromLegacyId(libraryId); + + foreach (ComicDB comic, moreRecentComicsFound[libraryId]) { + items.append(YACReaderServerDataHelper::comicToJSON(libraryId, libraryUuid, comic)); + } } } diff --git a/YACReaderLibrary/server/controllers/v2/tagcontentcontroller_v2.cpp b/YACReaderLibrary/server/controllers/v2/tagcontentcontroller_v2.cpp index 0aa353ec..8aca99cf 100644 --- a/YACReaderLibrary/server/controllers/v2/tagcontentcontroller_v2.cpp +++ b/YACReaderLibrary/server/controllers/v2/tagcontentcontroller_v2.cpp @@ -3,6 +3,7 @@ #include "db_helper.h" #include "comic_db.h" +#include "yacreader_libraries.h" #include "yacreader_server_data_helper.h" #include @@ -30,12 +31,14 @@ void TagContentControllerV2::service(HttpRequest &request, HttpResponse &respons void TagContentControllerV2::serviceContent(const int &library, const qulonglong &tagId, HttpResponse &response) { + auto libraryUuid = DBHelper::getLibraries().getLibraryIdFromLegacyId(library); + QList comics = DBHelper::getLabelComics(library, tagId); QJsonArray items; for (const ComicDB &comic : comics) { - items.append(YACReaderServerDataHelper::comicToJSON(library, comic)); + items.append(YACReaderServerDataHelper::comicToJSON(library, libraryUuid, comic)); } QJsonDocument output(items); diff --git a/YACReaderLibrary/server/controllers/v2/tagscontroller_v2.cpp b/YACReaderLibrary/server/controllers/v2/tagscontroller_v2.cpp index 954f1d03..28337ec7 100644 --- a/YACReaderLibrary/server/controllers/v2/tagscontroller_v2.cpp +++ b/YACReaderLibrary/server/controllers/v2/tagscontroller_v2.cpp @@ -4,13 +4,9 @@ #include "yacreader_libraries.h" #include "reading_list.h" -#include "../static.h" -#include "yacreader_global.h" #include "yacreader_server_data_helper.h" -#include "QsLog.h" - using stefanfrings::HttpRequest; using stefanfrings::HttpResponse; @@ -26,10 +22,12 @@ void TagsControllerV2::service(HttpRequest &request, HttpResponse &response) QList