From 34fcabc04e328c58ce65f6d3fe24c22b80917312 Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 9 Aug 2025 16:03:39 +0500 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B0=D1=80=D1=81=D0=B8=D0=BC=20=D0=B8?= =?UTF-8?q?=D0=BD=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=86=D0=B8=D1=8E=20=D0=BE?= =?UTF-8?q?=20=D0=BA=D0=BD=D0=B8=D0=B3=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitmodules | 3 ++ external_libs/quazip.qbs | 34 ++++++++++++++ project.qbs | 1 + redkitty | 2 +- src/cpp-opds.qbs | 3 ++ src/fb2extractor.cpp | 79 +++++++++++++++++++++++++++++++++ src/fb2extractor.h | 26 +++++++++++ src/main.cpp | 95 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 external_libs/quazip.qbs create mode 100644 src/fb2extractor.cpp create mode 100644 src/fb2extractor.h diff --git a/.gitmodules b/.gitmodules index 4254dc4..7186484 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "redkitty"] path = redkitty url = git@gitea.redkit-lab.work:redkit-lab/redkitty.git +[submodule "external_libs/quazip"] + path = external_libs/quazip + url = https://github.com/stachenov/quazip.git diff --git a/external_libs/quazip.qbs b/external_libs/quazip.qbs new file mode 100644 index 0000000..bf7ce29 --- /dev/null +++ b/external_libs/quazip.qbs @@ -0,0 +1,34 @@ +import qbs + +PSLibrary { + name: "quazip" + + property string quazipPath: path + "/quazip" // Путь к исходникам QuaZip + property string noname: { + console.error("noname" + exportedIncludePaths) + return "noname" + } + + Depends { name: "zlib" } + + Group { + name: "h" + id: qh + files: [ + quazipPath + "/quazip/*.h", + ] + } + + Group { + name: "cpp" + files: [ + quazipPath + "/quazip/*.cpp", + quazipPath + "/quazip/*.c" + ] + } + + // Export { + // Depends { name:"cpp" } + // cpp.includePaths: qh + // } +} diff --git a/project.qbs b/project.qbs index fee6594..72e9161 100644 --- a/project.qbs +++ b/project.qbs @@ -17,5 +17,6 @@ Project { "src/database/database.qbs", "src/model/model.qbs", "src/restapi/restapi.qbs", + "external_libs/quazip.qbs", ] } diff --git a/redkitty b/redkitty index 2316dde..47579f9 160000 --- a/redkitty +++ b/redkitty @@ -1 +1 @@ -Subproject commit 2316ddea7250c3d828a7c4a45f49ea8bc100448b +Subproject commit 47579f9e2e5e2973e1bbf260565563380b53e849 diff --git a/src/cpp-opds.qbs b/src/cpp-opds.qbs index f1b969f..572fa67 100644 --- a/src/cpp-opds.qbs +++ b/src/cpp-opds.qbs @@ -22,12 +22,15 @@ PSApplication { Depends { name: "redkit_gen" } Depends { name: "rdbase" } Depends { name: "model" } + Depends { name: "quazip" } cpp.cxxLanguageVersion: "c++20" Group { name: "cpp" files: [ + "fb2extractor.cpp", + "fb2extractor.h", "main.cpp", ] } diff --git a/src/fb2extractor.cpp b/src/fb2extractor.cpp new file mode 100644 index 0000000..87b0156 --- /dev/null +++ b/src/fb2extractor.cpp @@ -0,0 +1,79 @@ +#include "fb2extractor.h" + +// FB2Extractor::FB2Extractor() {} + +Fb2Metadata parseFb2Metadata(QXmlStreamReader &sr) +{ + Fb2Metadata meta; + QString currentElement; + bool inTitleInfo = false; + bool inAuthor = false; + QString currentAuthor; + + while (!sr.atEnd()) + { + switch (sr.readNext()) + { + case QXmlStreamReader::StartElement: + currentElement = sr.name().toString(); + + if (currentElement == "title-info") + { + inTitleInfo = true; + } + else if (inTitleInfo) + { + if (currentElement == "book-title") + { + meta.title = sr.readElementText(); + } + else if (currentElement == "genre") + { + meta.genres << sr.readElementText(); + } + else if (currentElement == "author") + { + inAuthor = true; + currentAuthor.clear(); + } + else if (inAuthor && (currentElement == "first-name" || currentElement == "last-name" || currentElement == "middle-name")) + { + currentAuthor += sr.readElementText() + " "; + } + } + break; + + case QXmlStreamReader::EndElement: + if (sr.name().toString() == "title-info") + { + inTitleInfo = false; + } + else if (sr.name().toString() == "author" && inAuthor) + { + meta.authors << currentAuthor.trimmed(); + inAuthor = false; + } + break; + + case QXmlStreamReader::Characters: + // Обработка текста уже делается в readElementText() + break; + + default: + break; + } + + // Прерываем парсинг, если нашли все метаданные + if (!meta.title.isEmpty() && !meta.authors.isEmpty() && !meta.genres.isEmpty()) + { + break; + } + } + + if (sr.hasError()) + { + qWarning() << "XML parsing error:" << sr.errorString(); + } + + return meta; +} diff --git a/src/fb2extractor.h b/src/fb2extractor.h new file mode 100644 index 0000000..20c0ffd --- /dev/null +++ b/src/fb2extractor.h @@ -0,0 +1,26 @@ +#ifndef FB2EXTRACTOR_H +#define FB2EXTRACTOR_H + +#include +#include +#include + +#include +#include + +// class FB2Extractor +// { +// public: +// FB2Extractor(); +// }; + +struct Fb2Metadata +{ + QString title; + QStringList authors; + QStringList genres; +}; + +Fb2Metadata parseFb2Metadata(QXmlStreamReader& sr); + +#endif // FB2EXTRACTOR_H diff --git a/src/main.cpp b/src/main.cpp index 971d54c..8ded449 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -14,6 +15,69 @@ #include +#include "fb2extractor.h" + +QMap readFb2MetadataFromZip(const QString& zipPath) +{ + QMap metadata; + QuaZip zip(zipPath); + + if (!zip.open(QuaZip::mdUnzip)) + { + qWarning() << "Failed to open ZIP archive"; + return metadata; + } + + int count = 0; + for (bool more = zip.goToFirstFile(); more; more = zip.goToNextFile()) + { + ++count; + + qWarning() << "*********" << count << "*********"; + + // Получение информации о файле + QuaZipFileInfo fileInfo; + if (zip.getCurrentFileInfo(&fileInfo)) + { + qDebug() << "File:" << fileInfo.name + << "Size:" << fileInfo.uncompressedSize << "bytes" + << "Compressed:" << fileInfo.compressedSize << "bytes" + << "Modified:" << fileInfo.dateTime.toString(Qt::ISODate); + } + + QString fb2FileName = zip.getCurrentFileName(); + + if (fb2FileName.isEmpty()) + { + qWarning() << "No FB2 file found in archive"; + zip.close(); + return metadata; + } + + // Читаем FB2 файл из архива + // qWarning() << "Читаем FB2 файл из архива" << fb2FileName; + QuaZipFile fb2File(&zip); + bool isOpen = fb2File.open(QIODevice::ReadOnly); + int zipError = fb2File.getZipError(); + if (!isOpen || zipError != UNZ_OK) + { + qWarning() << "Failed to open FB2 file in archive"; + zip.close(); + return metadata; + } + + // Парсим XML метаданные + QXmlStreamReader fXmlBook(&fb2File); + const auto data = parseFb2Metadata(fXmlBook); + + qWarning() << data.title << data.authors << data.genres; + + qWarning() << "*********" << count << "*********"; + } + + return metadata; +} + void fillBooksBD(uDBase& db) { try @@ -119,6 +183,36 @@ int main(int argc, char* argv[]) RestApiServer server(*db); server.start(8080); + // QString zipArzh = "f.fb2-631519-634744.zip"; + QString zipArzh = "/home/alex/repos/exp/cpp-opds/f.fb2-631519-634744.zip"; + + const auto books = readFb2MetadataFromZip(zipArzh); + qWarning() << " books.count()" << books.count(); + + // QuaZip zip(zipArzh); + // if (!zip.open(QuaZip::mdUnzip)) + // { + // qWarning() << "Failed to open archive"; + // } + // else + // { + // qWarning() << "Total files:" << zip.getEntriesCount(); + + // for (bool more = zip.goToFirstFile(); more; more = zip.goToNextFile()) + // { + // QuaZipFileInfo fileInfo; + // if (zip.getCurrentFileInfo(&fileInfo)) + // { + // qDebug() << "File:" << fileInfo.name + // << "Size:" << fileInfo.uncompressedSize << "bytes" + // << "Compressed:" << fileInfo.compressedSize << "bytes" + // << "Modified:" << fileInfo.dateTime.toString(Qt::ISODate); + // } + // } + // } + + // zip.close(); + // Set up code that uses the Qt event loop here. // Call a.quit() or a.exit() to quit the application. // A not very useful example would be including @@ -130,5 +224,6 @@ int main(int argc, char* argv[]) // If you do not need a running Qt event loop, remove the call // to a.exec() or use the Non-Qt Plain C++ Application template. + qWarning() << "S EXIT"; return a.exec(); }