Merge pull request #276 from YACReader/feature/xml_info_import

Feature: support for third party xml info import
This commit is contained in:
Luis Ángel San Martín 2021-09-27 18:40:44 +02:00 committed by GitHub
commit 43c80546aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 671 additions and 194 deletions

View File

@ -78,6 +78,7 @@ HEADERS += comic_flow.h \
db/comic_query_result_processor.h \
db/folder_query_result_processor.h \
db/query_lexer.h \
initial_comic_info_extractor.h \
library_comic_opener.h \
library_creator.h \
library_window.h \
@ -116,6 +117,8 @@ HEADERS += comic_flow.h \
no_libraries_widget.h \
import_widget.h \
trayicon_controller.h \
xml_info_library_scanner.h \
xml_info_parser.h \
yacreader_local_server.h \
yacreader_main_toolbar.h \
comics_remover.h \
@ -157,6 +160,7 @@ SOURCES += comic_flow.cpp \
db/comic_query_result_processor.cpp \
db/folder_query_result_processor.cpp \
db/query_lexer.cpp \
initial_comic_info_extractor.cpp \
library_comic_opener.cpp \
library_creator.cpp \
library_window.cpp \
@ -191,6 +195,8 @@ SOURCES += comic_flow.cpp \
no_libraries_widget.cpp \
import_widget.cpp \
trayicon_controller.cpp \
xml_info_library_scanner.cpp \
xml_info_parser.cpp \
yacreader_local_server.cpp \
yacreader_main_toolbar.cpp \
comics_remover.cpp \

View File

@ -1,12 +1,14 @@
#include "data_base_management.h"
#include <QtCore>
#include "library_creator.h"
#include "initial_comic_info_extractor.h"
#include "check_new_version.h"
#include "db_helper.h"
#include "QsLog.h"
using namespace YACReader;
static QString fields = "title ,"
"coverPage,"
@ -156,7 +158,7 @@ bool DataBaseManagement::createTables(QSqlDatabase &database)
"publisher TEXT,"
"format TEXT,"
"color BOOLEAN,"
"ageRating BOOLEAN,"
"ageRating BOOLEAN," //this is actually a string (TEXT), funny thing is that the current implementation works
"synopsis TEXT,"
"characters TEXT,"
@ -543,8 +545,8 @@ bool DataBaseManagement::importComicsInfo(QString source, QString dest)
QString basePath = QString(dest).remove("/.yacreaderlibrary/library.ydb");
QString path = basePath + getComic.record().value("path").toString();
int coverPage = getComic.record().value("coverPage").toInt();
ThumbnailCreator tc(path, basePath + "/.yacreaderlibrary/covers/" + hash + ".jpg", coverPage);
tc.create();
InitialComicInfoExtractor ie(path, basePath + "/.yacreaderlibrary/covers/" + hash + ".jpg", coverPage);
ie.extract();
}
}
sourceDBconnection = sourceDB.connectionName();

View File

@ -1154,7 +1154,7 @@ qulonglong DBHelper::insert(Folder *folder, QSqlDatabase &db)
return query.lastInsertId().toULongLong();
}
qulonglong DBHelper::insert(ComicDB *comic, QSqlDatabase &db)
qulonglong DBHelper::insert(ComicDB *comic, QSqlDatabase &db, bool insertAllInfo)
{
if (!comic->info.existOnDb) {
QSqlQuery comicInfoInsert(db);
@ -1167,6 +1167,10 @@ qulonglong DBHelper::insert(ComicDB *comic, QSqlDatabase &db)
comicInfoInsert.exec();
comic->info.id = comicInfoInsert.lastInsertId().toULongLong();
comic->_hasCover = false;
if (insertAllInfo) {
DBHelper::update(&(comic->info), db); //TODO use insert to insert all the info values, the common binding need to be extracted and shared between update and insert
}
} else
comic->_hasCover = true;

View File

@ -53,7 +53,7 @@ public:
static void deleteComicsFromReadingList(const QList<ComicDB> &comicsList, qulonglong readingListId, QSqlDatabase &db);
//inserts
static qulonglong insert(Folder *folder, QSqlDatabase &db);
static qulonglong insert(ComicDB *comic, QSqlDatabase &db);
static qulonglong insert(ComicDB *comic, QSqlDatabase &db, bool insertAllInfo);
static qulonglong insertLabel(const QString &name, YACReader::LabelColors color, QSqlDatabase &db);
static qulonglong insertReadingList(const QString &name, QSqlDatabase &db);
static qulonglong insertReadingSubList(const QString &name, qulonglong parentId, int ordering, QSqlDatabase &db);

View File

@ -364,6 +364,18 @@ void ImportWidget::setUpgradeLook()
hideButton->setVisible(false);
}
void ImportWidget::setXMLScanLook()
{
iconLabel->setPixmap(QPixmap(":/images/updatingIcon.png"));
text->setText("<font color=\"#495252\">" + tr("Scanning the library") + "</font>");
textDescription->setText("<font color=\"#565959\">" + tr("<p>Current library is being scanned for legacy XML metadata information.</p><p>This is only needed once, and only if the library was crated with YACReaderLibrary 9.8.2 or earlier.</p>") + "</font>");
stopButton->setVisible(true);
coversLabel->setVisible(false);
coversViewContainer->setVisible(false);
hideButton->setVisible(false);
}
void ImportWidget::clearScene()
{
}

View File

@ -20,6 +20,7 @@ public slots:
void setImportLook();
void setUpdateLook();
void setUpgradeLook();
void setXMLScanLook();
void showCovers(bool hide);
private:

View File

@ -0,0 +1,174 @@
#include "initial_comic_info_extractor.h"
#include <QsLog.h>
#include "pdf_comic.h"
#include "comic.h"
#include "compressed_archive.h"
#include "qnaturalsorting.h"
using namespace YACReader;
bool InitialComicInfoExtractor::crash = false;
InitialComicInfoExtractor::InitialComicInfoExtractor(QString fileSource, QString target, int coverPage)
: _fileSource(fileSource), _target(target), _numPages(0), _coverPage(coverPage), _xmlInfoData()
{
}
void InitialComicInfoExtractor::extract()
{
QFileInfo fi(_fileSource);
if (!fi.exists()) //TODO: error file not found.
{
_cover.load(":/images/notCover.png");
QLOG_WARN() << "Extracting cover: file not found " << _fileSource;
return;
}
#ifndef NO_PDF
if (fi.suffix().compare("pdf", Qt::CaseInsensitive) == 0) {
#if defined Q_OS_MAC && defined USE_PDFKIT
MacOSXPDFComic *pdfComic = new MacOSXPDFComic();
if (!pdfComic->openComic(_fileSource)) {
delete pdfComic;
//QImage p;
//p.load(":/images/notCover.png");
//p.save(_target);
return;
}
#elif defined USE_PDFIUM
auto pdfComic = new PdfiumComic();
if (!pdfComic->openComic(_fileSource)) {
delete pdfComic;
return;
}
#else
Poppler::Document *pdfComic = Poppler::Document::load(_fileSource);
#endif
if (!pdfComic) {
QLOG_WARN() << "Extracting cover: unable to open PDF file " << _fileSource;
//delete pdfComic; //TODO check if the delete is needed
pdfComic = 0;
//QImage p;
//p.load(":/images/notCover.png");
//p.save(_target);
return;
}
#if !defined USE_PDFKIT && !defined USE_PDFIUM
//poppler only, not mac
if (pdfComic->isLocked()) {
QLOG_WARN() << "Extracting cover: unable to open PDF file " << _fileSource;
delete pdfComic;
return;
}
#endif
_numPages = pdfComic->numPages();
if (_numPages >= _coverPage) {
#if defined Q_OS_MAC || defined USE_PDFIUM
QImage p = pdfComic->getPage(_coverPage - 1); //TODO check if the page is valid
#else
QImage p = pdfComic->page(_coverPage - 1)->renderToImage(72, 72);
#endif //
_cover = p;
_coverSize = QPair<int, int>(p.width(), p.height());
if (_target != "") {
QImage scaled;
if (p.width() > p.height()) //landscape??
{
scaled = p.scaledToWidth(640, Qt::SmoothTransformation);
} else {
scaled = p.scaledToWidth(480, Qt::SmoothTransformation);
}
scaled.save(_target, 0, 75);
} else if (_target != "") {
QLOG_WARN() << "Extracting cover: requested cover index greater than numPages " << _fileSource;
//QImage p;
//p.load(":/images/notCover.png");
//p.save(_target);
}
delete pdfComic;
}
return;
}
#endif //NO_PDF
if (crash) {
return;
}
CompressedArchive archive(_fileSource);
if (!archive.toolsLoaded()) {
QLOG_WARN() << "Extracting cover: 7z lib not loaded";
crash = true;
return;
}
if (!archive.isValid()) {
QLOG_WARN() << "Extracting cover: file format not supported " << _fileSource;
}
QList<QString> order = archive.getFileNames();
//Try to find embeded XML info (ComicRack or ComicTagger)
auto infoIndex = 0;
for (auto &fileName : order) {
if (fileName.endsWith(".xml", Qt::CaseInsensitive)) {
_xmlInfoData = archive.getRawDataAtIndex(infoIndex);
break;
}
infoIndex++;
}
//--------------------------
if (_target == "None") {
return;
}
//se filtran para obtener sólo los formatos soportados
QList<QString> fileNames = FileComic::filter(order);
_numPages = fileNames.size();
if (_numPages == 0) {
QLOG_WARN() << "Extracting cover: empty comic " << _fileSource;
_cover.load(":/images/notCover.png");
if (_target != "") {
_cover.save(_target);
}
} else {
if (_coverPage > _numPages) {
_coverPage = 1;
}
std::sort(fileNames.begin(), fileNames.end(), naturalSortLessThanCI);
int index = order.indexOf(fileNames.at(_coverPage - 1));
if (_target == "") {
if (!_cover.loadFromData(archive.getRawDataAtIndex(index))) {
QLOG_WARN() << "Extracting cover: unable to load image from extracted cover " << _fileSource;
_cover.load(":/images/notCover.png");
}
} else {
QImage p;
if (p.loadFromData(archive.getRawDataAtIndex(index))) {
_coverSize = QPair<int, int>(p.width(), p.height());
QImage scaled;
if (p.width() > p.height()) //landscape??
{
scaled = p.scaledToWidth(640, Qt::SmoothTransformation);
} else {
scaled = p.scaledToWidth(480, Qt::SmoothTransformation);
}
scaled.save(_target, 0, 75);
} else {
QLOG_WARN() << "Extracting cover: unable to load image from extracted cover " << _fileSource;
//p.load(":/images/notCover.png");
//p.save(_target);
}
}
}
}
QByteArray InitialComicInfoExtractor::getXMLInfoRawData()
{
return _xmlInfoData;
}

View File

@ -0,0 +1,36 @@
#ifndef INITIALCOMICINFOEXTRACTOR_H
#define INITIALCOMICINFOEXTRACTOR_H
#include <QtGui>
namespace YACReader {
class InitialComicInfoExtractor : public QObject
{
Q_OBJECT
public:
InitialComicInfoExtractor(QString fileSource, QString target = "", int coverPage = 1);
private:
QString _fileSource;
QString _target;
QString _currentName;
int _numPages;
QPair<int, int> _coverSize;
QImage _cover;
int _coverPage;
static bool crash;
QByteArray _xmlInfoData;
public slots:
void extract();
int getNumPages() { return _numPages; }
QPixmap getCover() { return QPixmap::fromImage(_cover); }
QPair<int, int> getOriginalCoverSize() { return _coverSize; }
QByteArray getXMLInfoRawData();
signals:
void openingError(QProcess::ProcessError error);
};
}
#endif // INITIALCOMICINFOEXTRACTOR_H

View File

@ -12,7 +12,8 @@
#include "qnaturalsorting.h"
#include "db_helper.h"
#include "compressed_archive.h"
#include "initial_comic_info_extractor.h"
#include "xml_info_parser.h"
#include "comic.h"
#include "pdf_comic.h"
#include "yacreader_global.h"
@ -20,7 +21,9 @@
#include "QsLog.h"
#include <algorithm>
using namespace std;
using namespace YACReader;
//--------------------------------------------------------------------------------
LibraryCreator::LibraryCreator()
@ -173,6 +176,7 @@ void LibraryCreator::run()
return;
}
QSqlQuery pragma("PRAGMA foreign_keys = ON", _database);
pragma.exec();
_database.transaction();
if (partialUpdate) {
@ -295,11 +299,13 @@ void LibraryCreator::insertComic(const QString &relativePath, const QFileInfo &f
int numPages = 0;
QPair<int, int> originalCoverSize = { 0, 0 };
bool exists = checkCover(hash);
YACReader::InitialComicInfoExtractor ie(QDir::cleanPath(fileInfo.absoluteFilePath()), _target + "/covers/" + hash + ".jpg", comic.info.coverPage.toInt());
if (!(comic.hasCover() && exists)) {
ThumbnailCreator tc(QDir::cleanPath(fileInfo.absoluteFilePath()), _target + "/covers/" + hash + ".jpg", comic.info.coverPage.toInt());
tc.create();
numPages = tc.getNumPages();
originalCoverSize = tc.getOriginalCoverSize();
ie.extract();
numPages = ie.getNumPages();
originalCoverSize = ie.getOriginalCoverSize();
if (numPages > 0) {
emit(comicAdded(relativePath, _target + "/covers/" + hash + ".jpg"));
}
@ -308,6 +314,9 @@ void LibraryCreator::insertComic(const QString &relativePath, const QFileInfo &f
if (numPages > 0 || exists) {
//en este punto sabemos que todos los folders que hay en _currentPath, deberían estar añadidos a la base de datos
insertFolders();
bool parsed = YACReader::parseXMLIntoInfo(ie.getXMLInfoRawData(), comic.info);
comic.info.numPages = numPages;
if (originalCoverSize.second > 0) {
comic.info.originalCoverSize = QString("%1x%2").arg(originalCoverSize.first).arg(originalCoverSize.second);
@ -316,7 +325,8 @@ void LibraryCreator::insertComic(const QString &relativePath, const QFileInfo &f
comic.parentId = _currentPathFolders.last().id;
comic.info.manga = _currentPathFolders.last().isManga();
DBHelper::insert(&comic, _database);
DBHelper::insert(&comic, _database, parsed);
}
}
@ -539,143 +549,3 @@ void LibraryCreator::update(QDir dirS)
}
}
}
bool ThumbnailCreator::crash = false;
ThumbnailCreator::ThumbnailCreator(QString fileSource, QString target, int coverPage)
: _fileSource(fileSource), _target(target), _numPages(0), _coverPage(coverPage)
{
}
void ThumbnailCreator::create()
{
QFileInfo fi(_fileSource);
if (!fi.exists()) //TODO: error file not found.
{
_cover.load(":/images/notCover.png");
QLOG_WARN() << "Extracting cover: file not found " << _fileSource;
return;
}
#ifndef NO_PDF
if (fi.suffix().compare("pdf", Qt::CaseInsensitive) == 0) {
#if defined Q_OS_MAC && defined USE_PDFKIT
MacOSXPDFComic *pdfComic = new MacOSXPDFComic();
if (!pdfComic->openComic(_fileSource)) {
delete pdfComic;
//QImage p;
//p.load(":/images/notCover.png");
//p.save(_target);
return;
}
#elif defined USE_PDFIUM
auto pdfComic = new PdfiumComic();
if (!pdfComic->openComic(_fileSource)) {
delete pdfComic;
return;
}
#else
Poppler::Document *pdfComic = Poppler::Document::load(_fileSource);
#endif
if (!pdfComic) {
QLOG_WARN() << "Extracting cover: unable to open PDF file " << _fileSource;
//delete pdfComic; //TODO check if the delete is needed
pdfComic = 0;
//QImage p;
//p.load(":/images/notCover.png");
//p.save(_target);
return;
}
#if !defined USE_PDFKIT && !defined USE_PDFIUM
//poppler only, not mac
if (pdfComic->isLocked()) {
QLOG_WARN() << "Extracting cover: unable to open PDF file " << _fileSource;
delete pdfComic;
return;
}
#endif
_numPages = pdfComic->numPages();
if (_numPages >= _coverPage) {
#if defined Q_OS_MAC || defined USE_PDFIUM
QImage p = pdfComic->getPage(_coverPage - 1); //TODO check if the page is valid
#else
QImage p = pdfComic->page(_coverPage - 1)->renderToImage(72, 72);
#endif //
_cover = p;
_coverSize = QPair<int, int>(p.width(), p.height());
if (_target != "") {
QImage scaled;
if (p.width() > p.height()) //landscape??
{
scaled = p.scaledToWidth(640, Qt::SmoothTransformation);
} else {
scaled = p.scaledToWidth(480, Qt::SmoothTransformation);
}
scaled.save(_target, 0, 75);
} else if (_target != "") {
QLOG_WARN() << "Extracting cover: requested cover index greater than numPages " << _fileSource;
//QImage p;
//p.load(":/images/notCover.png");
//p.save(_target);
}
delete pdfComic;
}
return;
}
#endif //NO_PDF
if (crash) {
return;
}
CompressedArchive archive(_fileSource);
if (!archive.toolsLoaded()) {
QLOG_WARN() << "Extracting cover: 7z lib not loaded";
crash = true;
return;
}
if (!archive.isValid()) {
QLOG_WARN() << "Extracting cover: file format not supported " << _fileSource;
}
//se filtran para obtener sólo los formatos soportados
QList<QString> order = archive.getFileNames();
QList<QString> fileNames = FileComic::filter(order);
_numPages = fileNames.size();
if (_numPages == 0) {
QLOG_WARN() << "Extracting cover: empty comic " << _fileSource;
_cover.load(":/images/notCover.png");
if (_target != "") {
_cover.save(_target);
}
} else {
if (_coverPage > _numPages) {
_coverPage = 1;
}
std::sort(fileNames.begin(), fileNames.end(), naturalSortLessThanCI);
int index = order.indexOf(fileNames.at(_coverPage - 1));
if (_target == "") {
if (!_cover.loadFromData(archive.getRawDataAtIndex(index))) {
QLOG_WARN() << "Extracting cover: unable to load image from extracted cover " << _fileSource;
_cover.load(":/images/notCover.png");
}
} else {
QImage p;
if (p.loadFromData(archive.getRawDataAtIndex(index))) {
_coverSize = QPair<int, int>(p.width(), p.height());
QImage scaled;
if (p.width() > p.height()) //landscape??
{
scaled = p.scaledToWidth(640, Qt::SmoothTransformation);
} else {
scaled = p.scaledToWidth(480, Qt::SmoothTransformation);
}
scaled.save(_target, 0, 75);
} else {
QLOG_WARN() << "Extracting cover: unable to load image from extracted cover " << _fileSource;
//p.load(":/images/notCover.png");
//p.save(_target);
}
}
}
}

View File

@ -67,30 +67,4 @@ signals:
void updatedCurrentFolder(QModelIndex);
};
class ThumbnailCreator : public QObject
{
Q_OBJECT
public:
ThumbnailCreator(QString fileSource, QString target = "", int coverPage = 1);
private:
QString _fileSource;
QString _target;
QString _currentName;
int _numPages;
QPair<int, int> _coverSize;
QImage _cover;
int _coverPage;
static bool crash;
public slots:
void create();
int getNumPages() { return _numPages; }
QPixmap getCover() { return QPixmap::fromImage(_cover); }
QPair<int, int> getOriginalCoverSize() { return _coverSize; }
signals:
void openingError(QProcess::ProcessError error);
};
#endif

View File

@ -33,6 +33,7 @@
#include "comic_db.h"
#include "library_creator.h"
#include "package_manager.h"
#include "xml_info_library_scanner.h"
#include "comic_flow_widget.h"
#include "create_library_dialog.h"
#include "rename_library_dialog.h"
@ -167,6 +168,7 @@ void LibraryWindow::setupUI()
libraryCreator = new LibraryCreator();
packageManager = new PackageManager();
xmlInfoLibraryScanner = new XMLInfoLibraryScanner();
historyController = new YACReaderHistoryController(this);
@ -425,7 +427,8 @@ void LibraryWindow::setUpShortcutsManagement()
<< importLibraryAction
<< updateLibraryAction
<< renameLibraryAction
<< removeLibraryAction);
<< removeLibraryAction
<< rescanLibraryForXMLInfoAction);
allActions << tmpList;
@ -532,6 +535,11 @@ void LibraryWindow::createActions()
removeLibraryAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(REMOVE_LIBRARY_ACTION_YL));
removeLibraryAction->setIcon(QIcon(":/images/menus_icons/removeLibraryIcon.png"));
rescanLibraryForXMLInfoAction = new QAction(tr("Rescan library for XML info"), this);
rescanLibraryForXMLInfoAction->setToolTip(tr("Tries to find XML info embedded in comic files. You only need to do this if the library was created with 9.8.2 or earlier versions or if you are using third party software to embed XML info in the files."));
rescanLibraryForXMLInfoAction->setData(RESCAN_LIBRARY_XML_INFO_ACTION_YL);
rescanLibraryForXMLInfoAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(RESCAN_LIBRARY_XML_INFO_ACTION_YL));
openComicAction = new QAction(tr("Open current comic"), this);
openComicAction->setToolTip(tr("Open current comic on YACReader"));
openComicAction->setData(OPEN_COMIC_ACTION_YL);
@ -868,6 +876,7 @@ void LibraryWindow::disableLibrariesActions(bool disabled)
exportComicsInfoAction->setDisabled(disabled);
importComicsInfoAction->setDisabled(disabled);
exportLibraryAction->setDisabled(disabled);
rescanLibraryForXMLInfoAction->setDisabled(disabled);
//importLibraryAction->setDisabled(disabled);
}
@ -877,6 +886,7 @@ void LibraryWindow::disableNoUpdatedLibrariesActions(bool disabled)
exportComicsInfoAction->setDisabled(disabled);
importComicsInfoAction->setDisabled(disabled);
exportLibraryAction->setDisabled(disabled);
rescanLibraryForXMLInfoAction->setDisabled(disabled);
}
void LibraryWindow::disableFoldersActions(bool disabled)
@ -1001,6 +1011,9 @@ void LibraryWindow::createMenus()
selectedLibrary->addAction(removeLibraryAction);
YACReader::addSperator(selectedLibrary);
selectedLibrary->addAction(rescanLibraryForXMLInfoAction);
YACReader::addSperator(selectedLibrary);
selectedLibrary->addAction(exportComicsInfoAction);
selectedLibrary->addAction(importComicsInfoAction);
YACReader::addSperator(selectedLibrary);
@ -1022,6 +1035,9 @@ void LibraryWindow::createMenus()
libraryMenu->addAction(removeLibraryAction);
libraryMenu->addSeparator();
libraryMenu->addAction(rescanLibraryForXMLInfoAction);
libraryMenu->addSeparator();
libraryMenu->addAction(exportComicsInfoAction);
libraryMenu->addAction(importComicsInfoAction);
@ -1083,8 +1099,13 @@ void LibraryWindow::createConnections()
connect(libraryCreator, &LibraryCreator::failedCreatingDB, this, &LibraryWindow::manageCreatingError);
connect(libraryCreator, SIGNAL(failedUpdatingDB(QString)), this, SLOT(manageUpdatingError(QString))); //TODO: implement failedUpdatingDB
connect(xmlInfoLibraryScanner, &QThread::finished, this, &LibraryWindow::showRootWidget);
connect(xmlInfoLibraryScanner, &QThread::finished, this, &LibraryWindow::reloadCurrentFolderComicsContent);
connect(xmlInfoLibraryScanner, &XMLInfoLibraryScanner::comicScanned, importWidget, &ImportWidget::newComic);
//new import widget
connect(importWidget, &ImportWidget::stop, this, &LibraryWindow::stopLibraryCreator);
connect(importWidget, &ImportWidget::stop, this, &LibraryWindow::stopXMLScanning);
//packageManager connections
connect(exportLibraryDialog, &ExportLibraryDialog::exportPath, this, &LibraryWindow::exportLibrary);
@ -1147,6 +1168,7 @@ void LibraryWindow::createConnections()
connect(renameLibraryAction, &QAction::triggered, this, &LibraryWindow::renameLibrary);
//connect(deleteLibraryAction,SIGNAL(triggered()),this,SLOT(deleteLibrary()));
connect(removeLibraryAction, &QAction::triggered, this, &LibraryWindow::removeLibrary);
connect(rescanLibraryForXMLInfoAction, &QAction::triggered, this, &LibraryWindow::rescanLibraryForXMLInfo);
connect(openComicAction, &QAction::triggered, this, QOverload<>::of(&LibraryWindow::openComic));
connect(helpAboutAction, &QAction::triggered, had, &QWidget::show);
connect(addFolderAction, &QAction::triggered, this, &LibraryWindow::addFolderToCurrentIndex);
@ -1298,12 +1320,13 @@ void LibraryWindow::loadLibrary(const QString &name)
d.setCurrent(libraries.getPath(name));
d.setFilter(QDir::AllDirs | QDir::Files | QDir::Hidden | QDir::NoSymLinks | QDir::NoDotAndDotDot);
if (d.count() <= 1) //librería de sólo lectura
if (d.count() <= 1) //read only library
{
//QMessageBox::critical(NULL,QString::number(d.count()),QString::number(d.count()));
disableLibrariesActions(false);
updateLibraryAction->setDisabled(true);
openContainingFolderAction->setDisabled(true);
rescanLibraryForXMLInfoAction->setDisabled(true);
disableComicsActions(true);
#ifndef Q_OS_MAC
toggleFullScreenAction->setEnabled(true);
@ -1514,6 +1537,13 @@ QProgressDialog *LibraryWindow::newProgressDialog(const QString &label, int maxV
return progressDialog;
}
void LibraryWindow::reloadCurrentFolderComicsContent()
{
navigationController->loadFolderInfo(getCurrentFolderIndex());
enableNeededActions();
}
void LibraryWindow::reloadAfterCopyMove(const QModelIndex &mi)
{
if (getCurrentFolderIndex() == mi) {
@ -2076,6 +2106,18 @@ void LibraryWindow::rename(QString newName) //TODO replace
//selectedLibrary->setCurrentIndex(selectedLibrary->findText(newName));
}
void LibraryWindow::rescanLibraryForXMLInfo()
{
importWidget->setXMLScanLook();
showImportingWidget();
QString currentLibrary = selectedLibrary->currentText();
QString path = libraries.getPath(currentLibrary);
_lastAdded = currentLibrary;
xmlInfoLibraryScanner->scanLibrary(path, path + "/.yacreaderlibrary");
}
void LibraryWindow::cancelCreating()
{
stopLibraryCreator();
@ -2087,6 +2129,12 @@ void LibraryWindow::stopLibraryCreator()
libraryCreator->wait();
}
void LibraryWindow::stopXMLScanning()
{
xmlInfoLibraryScanner->stop();
xmlInfoLibraryScanner->wait();
}
void LibraryWindow::setRootIndex()
{
if (!libraries.isEmpty()) {

View File

@ -82,6 +82,7 @@ class EmptyReadingListWidget;
namespace YACReader {
class TrayIconController;
class XMLInfoLibraryScanner;
}
#include "comic_db.h"
@ -103,6 +104,7 @@ public:
ImportComicsInfoDialog *importComicsInfoDialog;
AddLibraryDialog *addLibraryDialog;
LibraryCreator *libraryCreator;
XMLInfoLibraryScanner *xmlInfoLibraryScanner;
HelpAboutDialog *had;
RenameLibraryDialog *renameLibraryDialog;
PropertiesDialog *propertiesDialog;
@ -163,6 +165,8 @@ public:
QAction *exportLibraryAction;
QAction *importLibraryAction;
QAction *rescanLibraryForXMLInfoAction;
QAction *updateLibraryAction;
QAction *removeLibraryAction;
QAction *helpAboutAction;
@ -338,9 +342,11 @@ public slots:
void deleteCurrentLibrary();
void removeLibrary();
void renameLibrary();
void rescanLibraryForXMLInfo();
void rename(QString newName);
void cancelCreating();
void stopLibraryCreator();
void stopXMLScanning();
void setRootIndex();
void toggleFullScreen();
void toNormal();
@ -391,6 +397,7 @@ public slots:
void updateCurrentFolder();
void updateFolder(const QModelIndex &miFolder);
QProgressDialog *newProgressDialog(const QString &label, int maxValue);
void reloadCurrentFolderComicsContent();
void reloadAfterCopyMove(const QModelIndex &mi);
QModelIndex getCurrentFolderIndex();
void enableNeededActions();

View File

@ -1,7 +1,7 @@
#include "properties_dialog.h"
#include "data_base_management.h"
#include "library_creator.h"
#include "initial_comic_info_extractor.h"
#include "yacreader_field_edit.h"
#include "yacreader_field_plain_text_edit.h"
#include "db_helper.h"
@ -22,6 +22,8 @@
#include <QPushButton>
#include <QMessageBox>
using namespace YACReader;
PropertiesDialog::PropertiesDialog(QWidget *parent)
: QDialog(parent)
{
@ -734,12 +736,12 @@ void PropertiesDialog::save()
if (comics.count() == 1) {
if (coverChanged) // && coverPageEdit->text().toInt() != *comics[0].info.coverPage)
{
ThumbnailCreator tc(basePath + comics[0].path, basePath + "/.yacreaderlibrary/covers/" + comics[0].info.hash + ".jpg", comics[0].info.coverPage.toInt());
tc.create();
InitialComicInfoExtractor ie(basePath + comics[0].path, basePath + "/.yacreaderlibrary/covers/" + comics[0].info.hash + ".jpg", comics[0].info.coverPage.toInt());
ie.extract();
if (tc.getOriginalCoverSize().second > 0) {
comics[0].info.originalCoverSize = QString("%1x%2").arg(tc.getOriginalCoverSize().first).arg(tc.getOriginalCoverSize().second);
comics[0].info.coverSizeRatio = static_cast<float>(tc.getOriginalCoverSize().first) / tc.getOriginalCoverSize().second;
if (ie.getOriginalCoverSize().second > 0) {
comics[0].info.originalCoverSize = QString("%1x%2").arg(ie.getOriginalCoverSize().first).arg(ie.getOriginalCoverSize().second);
comics[0].info.coverSizeRatio = static_cast<float>(ie.getOriginalCoverSize().first) / ie.getOriginalCoverSize().second;
}
}
}
@ -831,9 +833,9 @@ void PropertiesDialog::loadNextCover()
if (current < comics.at(0).info.numPages.toInt()) {
updateCoverPageNumberLabel(current + 1);
ThumbnailCreator tc(basePath + comics[0].path, "", current + 1);
tc.create();
setCover(tc.getCover());
InitialComicInfoExtractor ie(basePath + comics[0].path, "", current + 1);
ie.extract();
setCover(ie.getCover());
repaint();
if ((current + 1) == comics.at(0).info.numPages.toInt()) {
@ -854,9 +856,9 @@ void PropertiesDialog::loadPreviousCover()
int current = coverPageNumberLabel->text().toInt();
if (current != 1) {
updateCoverPageNumberLabel(current - 1);
ThumbnailCreator tc(basePath + comics[0].path, "", current - 1);
tc.create();
setCover(tc.getCover());
InitialComicInfoExtractor ie(basePath + comics[0].path, "", current - 1);
ie.extract();
setCover(ie.getCover());
repaint();
if ((current - 1) == 1) {

View File

@ -0,0 +1,98 @@
#include "xml_info_library_scanner.h"
#include "comic_db.h"
#include "data_base_management.h"
#include "db_helper.h"
#include "initial_comic_info_extractor.h"
#include "xml_info_parser.h"
#include "yacreader_global.h"
#include "QsLog.h"
using namespace YACReader;
XMLInfoLibraryScanner::XMLInfoLibraryScanner()
: QThread()
{
}
void XMLInfoLibraryScanner::scanLibrary(const QString &source, const QString &target)
{
this->source = source;
this->target = target;
this->stopRunning = false;
start();
}
void XMLInfoLibraryScanner::run()
{
#ifndef use_unarr
//check for 7z lib
#if defined Q_OS_UNIX && !defined Q_OS_MAC
QLibrary *sevenzLib = new QLibrary(QString(LIBDIR) + "/p7zip/7z.so");
#else
QLibrary *sevenzLib = new QLibrary(QCoreApplication::applicationDirPath() + "/utils/7z");
#endif
if (!sevenzLib->load()) {
QLOG_ERROR() << "Loading 7z.dll : " + sevenzLib->errorString() << endl;
QCoreApplication::exit(YACReader::SevenZNotFound);
exit();
}
sevenzLib->deleteLater();
#endif
QString databaseConnection;
{
auto database = DataBaseManagement::loadDatabase(this->target);
databaseConnection = database.connectionName();
database.transaction();
QSqlQuery comicsInfo("SELECT * FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id)", database);
comicsInfo.exec();
QSqlRecord record = comicsInfo.record();
int id = record.indexOf("id");
//int parentIdIndex = record.indexOf("parentId");
int fileNameIndex = record.indexOf("fileName");
int pathIndex = record.indexOf("path");
while (comicsInfo.next()) {
if (this->stopRunning) {
break;
}
/* currentItem.id = selectQuery.value(id).toULongLong();
currentItem.parentId = parentId; //selectQuery.value(parentId).toULongLong();
currentItem.name = selectQuery.value(fileName).toString(); */
auto fileName = comicsInfo.value(fileNameIndex).toString();
auto path = comicsInfo.value(pathIndex).toString();
emit comicScanned(path, fileName);
auto info = DBHelper::getComicInfoFromQuery(comicsInfo, "comicInfoId");
InitialComicInfoExtractor ie(QDir::cleanPath(this->source + path), "None");
ie.extract();
if (parseXMLIntoInfo(ie.getXMLInfoRawData(), info)) {
DBHelper::update(&info, database);
}
}
database.commit();
database.close();
}
QSqlDatabase::removeDatabase(databaseConnection);
}
void XMLInfoLibraryScanner::stop()
{
stopRunning = true;
}

View File

@ -0,0 +1,32 @@
#ifndef XMLINFOLIBRARYSCANNER_H
#define XMLINFOLIBRARYSCANNER_H
#include <QtCore>
namespace YACReader {
class XMLInfoLibraryScanner : public QThread
{
Q_OBJECT
public:
XMLInfoLibraryScanner();
void scanLibrary(const QString &source, const QString &target);
protected:
void run() override;
public slots:
void stop();
signals:
void comicScanned(QString, QString);
private:
QString source;
QString target;
bool stopRunning;
};
}
#endif // XMLINFOLIBRARYSCANNER_H

View File

@ -0,0 +1,184 @@
#include "xml_info_parser.h"
#include <QtCore>
bool isValidText(const QString &string)
{
return !string.isEmpty() && !string.isNull();
}
QString transLateMultiValuedString(QString &string, const QString &originSeparator = ", ", const QString &targetSeparator = "\n")
{
return string.replace(originSeparator, targetSeparator);
}
bool parseField(QXmlStreamReader &reader, const QString &xmlName, QVariant &dest, bool multivalued = false)
{
auto name = reader.name();
if (name == xmlName) {
auto string = reader.readElementText();
if (isValidText(string)) {
if (multivalued) {
dest = transLateMultiValuedString(string);
} else {
dest = string;
}
}
return true;
}
return false;
}
bool parseFieldNumber(QXmlStreamReader &reader, const QString &xmlName, QVariant &dest)
{
if (reader.name() == xmlName) {
auto string = reader.readElementText();
if (isValidText(string)) {
bool success;
auto number = string.toInt(&success);
if (success) {
dest = number;
}
}
return true;
}
return false;
}
void consolidateDate(ComicInfo &info)
{
if (!info.year.isValid() && !info.month.isValid() && !info.day.isValid()) {
return;
}
auto year = info.year.isNull() ? 0 : info.year.toInt();
auto month = info.month.isNull() ? 1 : info.month.toInt();
auto day = info.date.isNull() ? 1 : info.date.toInt();
info.date = QString("%1/%2/%3").arg(day).arg(month).arg(year);
}
bool tryValues(QXmlStreamReader &reader, ComicInfo &info)
{
std::map<QString, QVariant &> stringValues = {
{ "Title", info.title },
{ "Volume", info.volume },
{ "StoryArc", info.storyArc },
{ "Genre", info.genere },
{ "Publisher", info.publisher },
{ "Format", info.format },
{ "AgeRating", info.ageRating },
{ "Summary", info.synopsis },
{ "Notes", info.notes },
};
std::map<QString, QVariant &> forcedNumbers = {
{ "Number", info.number },
{ "Count", info.count },
{ "AlternateNumber", info.arcNumber },
{ "AlternateCount", info.arcCount },
{ "Day", info.day },
{ "Month", info.month },
{ "Year", info.year }
};
std::map<QString, QVariant &> multiValuedStrings = {
{ "Writer", info.writer },
{ "Penciller", info.penciller },
{ "Inker", info.inker },
{ "Colorist", info.colorist },
{ "Letterer", info.letterer },
{ "CoverArtist", info.coverArtist },
{ "Characters", info.characters }
};
for (auto &pair : stringValues) {
if (parseField(reader, pair.first, pair.second)) {
return true;
}
}
for (auto &pair : forcedNumbers) {
if (parseFieldNumber(reader, pair.first, pair.second)) {
return true;
}
}
for (auto &pair : multiValuedStrings) {
if (parseField(reader, pair.first, pair.second, true)) {
return true;
}
}
if (reader.name() == "BlackAndWhite") {
auto string = reader.readElementText();
if (isValidText(string)) {
if (string == "Yes") {
info.color = false;
} else if (string == "No") {
info.color = true;
}
}
return true;
}
if (reader.name() == "Manga") {
auto string = reader.readElementText();
if (isValidText(string)) {
if (string == "Yes" || string == "YesAndRightToLeft") {
info.manga = true;
} else if (string == "No") {
info.manga = false;
}
}
return true;
}
if (reader.name() == "Web") {
auto string = reader.readElementText();
if (isValidText(string)) {
auto comicVineId = string.split("-").last().replace("/", "");
info.comicVineID = comicVineId;
}
return true;
}
return false;
}
bool YACReader::parseXMLIntoInfo(const QByteArray &xmlRawData, ComicInfo &info)
{
if (xmlRawData.isEmpty()) {
return false;
}
QXmlStreamReader reader(xmlRawData);
bool someDataWasParsed = false;
while (reader.readNextStartElement()) {
if (tryValues(reader, info)) {
someDataWasParsed = true;
} else {
if (reader.name() != "ComicInfo") {
reader.skipCurrentElement();
}
}
}
consolidateDate(info);
if (reader.error()) {
return false;
}
return someDataWasParsed;
}

View File

@ -0,0 +1,12 @@
#ifndef XMLINFOPARSER_H
#define XMLINFOPARSER_H
#include "comic_db.h"
namespace YACReader {
bool parseXMLIntoInfo(const QByteArray &xmlRawData, ComicInfo &info);
}
#endif // XMLINFOPARSER_H

View File

@ -44,6 +44,8 @@ HEADERS += ../YACReaderLibrary/library_creator.h \
../YACReaderLibrary/db_helper.h \
../YACReaderLibrary/db/data_base_management.h \
../YACReaderLibrary/db/reading_list.h \
../YACReaderLibrary/initial_comic_info_extractor.h \
../YACReaderLibrary/xml_info_parser.h \
../common/comic_db.h \
../common/folder.h \
../common/library_item.h \
@ -67,6 +69,8 @@ SOURCES += ../YACReaderLibrary/library_creator.cpp \
../YACReaderLibrary/db_helper.cpp \
../YACReaderLibrary/db/data_base_management.cpp \
../YACReaderLibrary/db/reading_list.cpp \
../YACReaderLibrary/initial_comic_info_extractor.cpp \
../YACReaderLibrary/xml_info_parser.cpp \
../common/comic_db.cpp \
../common/folder.cpp \
../common/library_item.cpp \

View File

@ -173,7 +173,9 @@ QString ComicDB::getTitleIncludingNumber() const
//COMIC_INFO-------------------------------------------------------------------
//-----------------------------------------------------------------------------
ComicInfo::ComicInfo()
: existOnDb(false),
: read(false),
edited(false),
existOnDb(false),
rating(0),
hasBeenOpened(false),
currentPage(1),

View File

@ -69,6 +69,11 @@ public:
QVariant coverArtist; //string
QVariant date; //string
QVariant day;
QVariant month;
QVariant year;
QVariant publisher; //string
QVariant format; //string
QVariant color; //bool
@ -177,6 +182,9 @@ public:
Q_PROPERTY(QVariant coverArtist MEMBER coverArtist CONSTANT)
Q_PROPERTY(QVariant date MEMBER date CONSTANT)
Q_PROPERTY(QVariant day MEMBER day CONSTANT)
Q_PROPERTY(QVariant month MEMBER month CONSTANT)
Q_PROPERTY(QVariant year MEMBER year CONSTANT)
Q_PROPERTY(QVariant publisher MEMBER publisher CONSTANT)
Q_PROPERTY(QVariant format MEMBER format CONSTANT)
Q_PROPERTY(QVariant color MEMBER color CONSTANT)

View File

@ -45,6 +45,7 @@ public:
#define UPDATE_LIBRARY_ACTION_YL "UPDATE_LIBRARY_ACTION_YL"
#define RENAME_LIBRARY_ACTION_YL "RENAME_LIBRARY_ACTION_YL"
#define REMOVE_LIBRARY_ACTION_YL "REMOVE_LIBRARY_ACTION_YL"
#define RESCAN_LIBRARY_XML_INFO_ACTION_YL "RESCAN_LIBRARY_XML_INFO_ACTION_YL"
#define OPEN_COMIC_ACTION_YL "OPEN_COMIC_ACTION_YL"
#define SET_AS_READ_ACTION_YL "SET_AS_READ_ACTION_YL"
#define SET_AS_NON_READ_ACTION_YL "SET_AS_NON_READ_ACTION_YL"