#include "comic_vine_dialog.h" #include #include #include #include #include #include #include #include #include #include #include "data_base_management.h" #include #include "yacreader_busy_widget.h" #include "comic_vine_client.h" #include "comic_vine_json_parser.h" #include "scraper_lineedit.h" #include "title_header.h" #include "series_question.h" #include "search_single_comic.h" #include "search_volume.h" #include "select_comic.h" #include "select_volume.h" #include "selected_volume_info.h" #include "sort_volume_comics.h" #include "db_helper.h" #include "response_parser.h" #include "QsLog.h" ComicVineDialog::ComicVineDialog(QWidget *parent) : QDialog(parent) { setWindowFlags(Qt::Window); setModal(true); doLayout(); doStackedWidgets(); doConnections(); } void ComicVineDialog::closeEvent(QCloseEvent *event) { QDialog::closeEvent(event); clearState(); } void ComicVineDialog::doLayout() { setStyleSheet("" "QDialog {background-color: #404040; }" ""); QString dialogButtonsStyleSheet = "QPushButton {border: 1px solid #242424; background: #2e2e2e; color:white; padding: 5px 26px 5px 26px; font-size:12px;font-family:Arial; font-weight:bold;}"; skipButton = new QPushButton(tr("skip")); backButton = new QPushButton(tr("back")); nextButton = new QPushButton(tr("next")); searchButton = new QPushButton(tr("search")); closeButton = new QPushButton(tr("close")); backButton->setShortcut(QKeySequence(Qt::Key_Backspace)); closeButton->setDefault(false); closeButton->setAutoDefault(false); skipButton->setStyleSheet(dialogButtonsStyleSheet); backButton->setStyleSheet(dialogButtonsStyleSheet); nextButton->setStyleSheet(dialogButtonsStyleSheet); searchButton->setStyleSheet(dialogButtonsStyleSheet); closeButton->setStyleSheet(dialogButtonsStyleSheet); content = new QStackedWidget(this); auto mainLayout = new QVBoxLayout; auto buttonLayout = new QHBoxLayout; buttonLayout->addStretch(); buttonLayout->addWidget(skipButton); buttonLayout->addWidget(backButton); buttonLayout->addWidget(nextButton); buttonLayout->addWidget(searchButton); buttonLayout->addWidget(closeButton); buttonLayout->setContentsMargins(0, 0, 0, 0); mainLayout->addWidget(titleHeader = new TitleHeader, 0); mainLayout->addWidget(content, 1); mainLayout->addLayout(buttonLayout, 0); mainLayout->setContentsMargins(26, 16, 26, 11); setLayout(mainLayout); setWindowTitle("Comic Vine Scraper"); } void ComicVineDialog::doStackedWidgets() { doLoading(); content->addWidget(seriesQuestionWidget = new SeriesQuestion); content->addWidget(searchSingleComicWidget = new SearchSingleComic); content->addWidget(searchVolumeWidget = new SearchVolume); content->addWidget(selectVolumeWidget = new SelectVolume); content->addWidget(selectComicWidget = new SelectComic); content->addWidget(sortVolumeComicsWidget = new SortVolumeComics); } void ComicVineDialog::doConnections() { connect(closeButton, &QAbstractButton::clicked, this, &QWidget::close); connect(nextButton, &QAbstractButton::clicked, this, &ComicVineDialog::goNext); connect(backButton, &QAbstractButton::clicked, this, &ComicVineDialog::goBack); connect(searchButton, &QAbstractButton::clicked, this, &ComicVineDialog::search); connect(skipButton, &QAbstractButton::clicked, this, &ComicVineDialog::goToNextComic); connect(selectVolumeWidget, &SelectVolume::loadPage, this, &ComicVineDialog::searchVolume); connect(selectComicWidget, &SelectComic::loadPage, this, &ComicVineDialog::getVolumeComicsInfo); connect(sortVolumeComicsWidget, &SortVolumeComics::loadPage, this, &ComicVineDialog::getVolumeComicsInfo); connect(this, &QDialog::accepted, this, &QWidget::close, Qt::QueuedConnection); } void ComicVineDialog::goNext() { clearState(); // if (content->currentWidget() == seriesQuestionWidget) { if (seriesQuestionWidget->getYes()) { QString volumeSearchString = comics[0].getParentFolderName(); mode = ScraperMode::Volume; showSearchVolume(volumeSearchString); } else { status = ScraperStatus::AutoSearching; mode = ScraperMode::SingleComicInList; ComicDB comic = comics[currentIndex]; QString title = comic.getTitleOrFileName(); titleHeader->setSubTitle(tr("comic %1 of %2 - %3").arg(currentIndex + 1).arg(comics.length()).arg(title)); showLoading(tr("Looking for volume...")); searchVolume({ volumeSearchStringFromComic(comic), 1, true }); } } else if (content->currentWidget() == selectVolumeWidget) { currentVolumeId = selectVolumeWidget->getSelectedVolumeInfo().id; getVolumeComicsInfo(currentVolumeId); } else if (content->currentWidget() == sortVolumeComicsWidget) { showLoading(); // ComicDB-ComicVineID QList> matchingInfo = sortVolumeComicsWidget->getMatchingInfo(); auto volumeInfo = selectVolumeWidget->getSelectedVolumeInfo(); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QtConcurrent::run(&ComicVineDialog::getComicsInfo, this, matchingInfo, volumeInfo); #else QtConcurrent::run(this, &ComicVineDialog::getComicsInfo, matchingInfo, volumeInfo); #endif } else if (content->currentWidget() == selectComicWidget) { showLoading(); QString comicId = selectComicWidget->getSelectedComicId(); auto volumeInfo = selectVolumeWidget->getSelectedVolumeInfo(); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QtConcurrent::run(&ComicVineDialog::getComicInfo, this, comicId, volumeInfo); #else QtConcurrent::run(this, &ComicVineDialog::getComicInfo, comicId, volumeInfo); #endif } } void ComicVineDialog::goBack() { clearState(); switch (status) { case ScraperStatus::SelectingSeries: if (mode == ScraperMode::Volume) showSearchVolume(currentVolumeSearchQuery.volume); else showSearchSingleComic(currentVolumeSearchQuery.volume); break; case ScraperStatus::SortingComics: showSelectVolume(); break; case ScraperStatus::SelectingComic: if (mode == ScraperMode::SingleComic) showSelectVolume(); break; case ScraperStatus::AutoSearching: if (mode == ScraperMode::Volume) showSearchVolume(currentVolumeSearchQuery.volume); else showSearchSingleComic(currentVolumeSearchQuery.volume); break; case ScraperStatus::AskingForInfo: case ScraperStatus::SearchingSingleComic: case ScraperStatus::SearchingVolume: case ScraperStatus::SearchingExactVolume: case ScraperStatus::GettingVolumeComics: if (mode == ScraperMode::Volume) showSearchVolume(currentVolumeSearchQuery.volume); else showSearchSingleComic(currentVolumeSearchQuery.volume); break; } } void ComicVineDialog::setComics(const QList &comics) { this->comics = comics; } QSize ComicVineDialog::sizeHint() const { QScreen *screen = window()->screen(); if (screen == nullptr) { screen = QApplication::screens().constFirst(); } int heightDesktopResolution = screen->geometry().height(); int widthDesktopResolution = screen->geometry().width(); int height, width; height = qMax(529, static_cast(heightDesktopResolution * 0.5)); width = height * 1.65; if (width > widthDesktopResolution) return minimumSizeHint(); return QSize(width, height); } QSize ComicVineDialog::minimumSizeHint() const { return QSize(872, 529); } void ComicVineDialog::show() { QDialog::show(); currentIndex = 0; seriesQuestionWidget->setYes(true); searchSingleComicWidget->clean(); searchVolumeWidget->clean(); if (comics.length() == 1) { status = ScraperStatus::AskingForInfo; mode = ScraperMode::SingleComic; ComicDB singleComic = comics[0]; QString title = singleComic.getTitleOrFileName(); titleHeader->setSubTitle(title); showSearchSingleComic(volumeSearchStringFromComic(singleComic)); } else if (comics.length() > 1) { titleHeader->setSubTitle(tr("%1 comics selected").arg(comics.length())); showSeriesQuestion(); } } void ComicVineDialog::doLoading() { QWidget *w = new QWidget; auto l = new QVBoxLayout; auto bw = new YACReaderBusyWidget; loadingMessage = new QLabel; loadingMessage->setStyleSheet("QLabel {color:white; font-size:12px;font-family:Arial;}"); l->addStretch(); l->addWidget(bw, 0, Qt::AlignHCenter); l->addStretch(); l->addWidget(loadingMessage); l->setContentsMargins(0, 0, 0, 0); w->setLayout(l); w->setContentsMargins(0, 0, 0, 0); content->addWidget(w); } void ComicVineDialog::processClientResults(const QString &string) { ResponseParser p; p.loadJSONResponse(string); // QMessageBox::information(0,"Result", QString("Number of results : %1").arg(p.getNumResults())); if (p.responseError()) { QMessageBox::critical(0, tr("Error connecting to ComicVine"), p.errorDescription()); goBack(); } else { switch (mode) { case ScraperMode::SingleComic: case ScraperMode::SingleComicInList: if (p.getNumResults() == 0) showSearchSingleComic(currentVolumeSearchQuery.volume); else if (status == ScraperStatus::SearchingVolume || status == ScraperStatus::SearchingExactVolume) showSelectVolume(string); else showSelectComic(string); break; case ScraperMode::Volume: if (p.getNumResults() == 0) showSearchVolume(currentVolumeSearchQuery.volume); else showSelectVolume(string); break; } } } void ComicVineDialog::showSeriesQuestion() { status = ScraperStatus::AskingForInfo; content->setCurrentWidget(seriesQuestionWidget); backButton->setHidden(true); skipButton->setHidden(true); nextButton->setVisible(true); searchButton->setHidden(true); closeButton->setVisible(true); nextButton->setDefault(true); toggleSkipButton(); } void ComicVineDialog::showSearchSingleComic(const QString &volume) { searchSingleComicWidget->setVolumeInfo(volume); status = ScraperStatus::AskingForInfo; content->setCurrentWidget(searchSingleComicWidget); backButton->setHidden(true); skipButton->setHidden(true); nextButton->setHidden(true); searchButton->setVisible(true); closeButton->setVisible(true); searchButton->setDefault(true); toggleSkipButton(); } void ComicVineDialog::showSearchVolume(const QString &volume) { searchVolumeWidget->setVolumeInfo(volume); status = ScraperStatus::AskingForInfo; content->setCurrentWidget(searchVolumeWidget); backButton->setHidden(true); nextButton->setHidden(true); searchButton->setVisible(true); closeButton->setVisible(true); searchButton->setDefault(true); toggleSkipButton(); } void ComicVineDialog::showSelectVolume(const QString &json) { showSelectVolume(); selectVolumeWidget->load(json, currentVolumeSearchQuery); } void ComicVineDialog::showSelectVolume() { status = ScraperStatus::SelectingSeries; content->setCurrentWidget(selectVolumeWidget); backButton->setVisible(true); nextButton->setVisible(true); searchButton->setHidden(true); closeButton->setVisible(true); nextButton->setDefault(true); toggleSkipButton(); } void ComicVineDialog::showSelectComic(const QString &json) { status = ScraperStatus::SelectingComic; content->setCurrentWidget(selectComicWidget); selectComicWidget->load(json, currentVolumeId); backButton->setVisible(true); nextButton->setVisible(true); searchButton->setHidden(true); closeButton->setVisible(true); nextButton->setDefault(true); toggleSkipButton(); } void ComicVineDialog::showSortVolumeComics(const QString &json) { status = ScraperStatus::SortingComics; content->setCurrentWidget(sortVolumeComicsWidget); sortVolumeComicsWidget->setData(comics, json, currentVolumeId); backButton->setVisible(true); nextButton->setVisible(true); searchButton->setHidden(true); closeButton->setVisible(true); nextButton->setDefault(true); toggleSkipButton(); } void ComicVineDialog::queryTimeOut() { QMessageBox::warning(this, "Comic Vine error", "Time out connecting to Comic Vine"); switch (status) { case ScraperStatus::AutoSearching: if (mode == ScraperMode::Volume) showSearchVolume(currentVolumeSearchQuery.volume); else showSearchSingleComic(currentVolumeSearchQuery.volume); break; case ScraperStatus::SearchingVolume: case ScraperStatus::SearchingExactVolume: if (mode == ScraperMode::Volume) showSearchVolume(currentVolumeSearchQuery.volume); else showSearchSingleComic(currentVolumeSearchQuery.volume); break; case ScraperStatus::SearchingSingleComic: showSearchSingleComic(currentVolumeSearchQuery.volume); break; case ScraperStatus::GettingVolumeComics: showSelectVolume(); break; case ScraperStatus::AskingForInfo: case ScraperStatus::SelectingComic: case ScraperStatus::SelectingSeries: case ScraperStatus::SortingComics: break; } } void ComicVineDialog::getComicsInfo(QList> matchingInfo, const SelectedVolumeInfo &volumeInfo) { QPair p; QList comics; foreach (p, matchingInfo) { auto comicVineClient = new ComicVineClient; // connect(comicVineClient,SIGNAL(searchResult(QString)),this,SLOT(debugClientResults(QString))); // connect(comicVineClient,SIGNAL(timeOut()),this,SLOT(queryTimeOut())); // connect(comicVineClient,SIGNAL(finished()),comicVineClient,SLOT(deleteLater())); bool error; bool timeout; QByteArray result = comicVineClient->getComicDetail(p.second, error, timeout); // TODO check timeOut or Connection error if (error || timeout) continue; // TODO ComicDB comic = YACReader::parseCVJSONComicInfo(p.first, result, volumeInfo); // TODO check result error comic.info.comicVineID = p.second; comics.push_back(comic); setLoadingMessage(tr("Retrieving tags for : %1").arg(p.first.getFileName())); } DBHelper::updateComicsInfo(comics, databasePath); emit accepted(); } void ComicVineDialog::getComicInfo(const QString &comicId, const SelectedVolumeInfo &volumeInfo) { auto comicVineClient = new ComicVineClient; bool error; bool timeout; QByteArray result = comicVineClient->getComicDetail(comicId, error, timeout); // TODO check timeOut or Connection error if (error || timeout) { // TODO if (mode == ScraperMode::SingleComic || currentIndex == (comics.count() - 1)) { emit accepted(); } else { goToNextComic(); } } ComicDB comic = YACReader::parseCVJSONComicInfo(comics[currentIndex], result, volumeInfo); // TODO check result error comic.info.comicVineID = comicId; setLoadingMessage(tr("Retrieving tags for : %1").arg(comics[currentIndex].getFileName())); QString connectionName = ""; { QSqlDatabase db = DataBaseManagement::loadDatabase(databasePath); db.open(); db.transaction(); DBHelper::update(&(comic.info), db); db.commit(); connectionName = db.connectionName(); } QSqlDatabase::removeDatabase(connectionName); if (mode == ScraperMode::SingleComic || currentIndex == (comics.count() - 1)) { emit accepted(); } else { goToNextComic(); } } void ComicVineDialog::toggleSkipButton() { if (mode == ScraperMode::SingleComicInList) skipButton->setVisible(true); else skipButton->setHidden(true); } QString ComicVineDialog::volumeSearchStringFromComic(const ComicDB &comic) { auto volume = comic.info.volume.toString().trimmed(); if (!volume.isEmpty()) return volume; auto series = comic.info.series.toString().trimmed(); if (!series.isEmpty()) return series; auto alternateSeries = comic.info.alternateSeries.toString().trimmed(); if (!alternateSeries.isEmpty()) return alternateSeries; // extract information from file name auto parentFolderName = comic.getParentFolderName(); return parentFolderName; } void ComicVineDialog::goToNextComic() { if (mode == ScraperMode::SingleComic || currentIndex == (comics.count() - 1)) { emit accepted(); return; } currentIndex++; ComicDB comic = comics[currentIndex]; QString title = comic.getTitleOrFileName(); titleHeader->setSubTitle(tr("comic %1 of %2 - %3").arg(currentIndex + 1).arg(comics.length()).arg(title)); showSearchSingleComic(volumeSearchStringFromComic(comic)); } void ComicVineDialog::clearState() { selectVolumeWidget->clearFilter(); } void ComicVineDialog::showLoading(const QString &message) { content->setCurrentIndex(0); loadingMessage->setText(message); backButton->setHidden(true); skipButton->setHidden(true); nextButton->setHidden(true); searchButton->setHidden(true); closeButton->setVisible(true); } void ComicVineDialog::setLoadingMessage(const QString &message) { loadingMessage->setText(message); } void ComicVineDialog::search() { switch (mode) { case ScraperMode::Volume: launchSearchVolume(); break; case ScraperMode::SingleComic: case ScraperMode::SingleComicInList: launchSearchComic(); break; } } void ComicVineDialog::searchVolume(const VolumeSearchQuery &query) { showLoading(tr("Looking for volume...")); currentVolumeSearchQuery = query; auto comicVineClient = new ComicVineClient; connect(comicVineClient, &ComicVineClient::searchResult, this, &ComicVineDialog::processClientResults); connect(comicVineClient, &ComicVineClient::timeOut, this, &ComicVineDialog::queryTimeOut); connect(comicVineClient, &ComicVineClient::finished, comicVineClient, &QObject::deleteLater); if (query.exactMatch) { status = ScraperStatus::SearchingExactVolume; comicVineClient->searchExactVolume(query.volume, query.page); } else { status = ScraperStatus::SearchingVolume; comicVineClient->search(query.volume, query.page); } } void ComicVineDialog::getVolumeComicsInfo(const QString &vID, int /* page */) { showLoading(tr("Retrieving volume info...")); status = ScraperStatus::GettingVolumeComics; auto comicVineClient = new ComicVineClient; if (mode == ScraperMode::Volume) connect(comicVineClient, &ComicVineClient::volumeComicsInfo, this, &ComicVineDialog::showSortVolumeComics); else connect(comicVineClient, &ComicVineClient::volumeComicsInfo, this, &ComicVineDialog::showSelectComic); connect(comicVineClient, &ComicVineClient::timeOut, this, &ComicVineDialog::queryTimeOut); connect(comicVineClient, &ComicVineClient::finished, comicVineClient, &QObject::deleteLater); QLOG_TRACE() << vID; comicVineClient->getAllVolumeComicsInfo(vID); } // TODO: get the search configuration for exact match or not void ComicVineDialog::launchSearchVolume() { showLoading(tr("Looking for volume...")); // TODO: check if volume info is empty. QString volumeInfo = searchVolumeWidget->getVolumeInfo(); bool exactMatch = searchVolumeWidget->getExactMatch(); searchVolume({ volumeInfo, 1, exactMatch }); } // TODO: get the search configuration for exact match or not void ComicVineDialog::launchSearchComic() { showLoading(tr("Looking for comic...")); QString volumeInfo = searchSingleComicWidget->getVolumeInfo(); bool exactMatch = searchSingleComicWidget->getExactMatch(); // QString comicInfo = searchSingleComicWidget->getComicInfo(); // int comicNumber = searchSingleComicWidget->getComicNumber(); // if(comicInfo.isEmpty() && comicNumber == -1) searchVolume({ volumeInfo, 1, exactMatch }); }