mirror of
https://github.com/YACReader/yacreader
synced 2025-05-25 18:00:46 -04:00
Add support for setting custom covers on folders
This commit is contained in:
parent
f0b9d45033
commit
b976b7f809
@ -16,6 +16,7 @@ Version counting is based on semantic versioning (Major.Feature.Patch)
|
|||||||
* New setting in Comic Vine scraper to force exact volume matches.
|
* New setting in Comic Vine scraper to force exact volume matches.
|
||||||
* Better default search query in the Comic Vine scraper.
|
* 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.
|
* Improved navigation in Comic Vine scraper, including keeping the current query around to make edits and refined searches easier.
|
||||||
|
* Add support for adding custom covers for folders using the context menu.
|
||||||
|
|
||||||
### YACReaderLibraryServer
|
### YACReaderLibraryServer
|
||||||
* Log libraries validation when the app starts.
|
* Log libraries validation when the app starts.
|
||||||
|
@ -74,6 +74,7 @@ greaterThan(QT_MAJOR_VERSION, 5): QT += openglwidgets core5compat
|
|||||||
# Input
|
# Input
|
||||||
HEADERS += comic_flow.h \
|
HEADERS += comic_flow.h \
|
||||||
../common/concurrent_queue.h \
|
../common/concurrent_queue.h \
|
||||||
|
../common/cover_utils.h \
|
||||||
create_library_dialog.h \
|
create_library_dialog.h \
|
||||||
db/comic_query_result_processor.h \
|
db/comic_query_result_processor.h \
|
||||||
db/folder_query_result_processor.h \
|
db/folder_query_result_processor.h \
|
||||||
@ -163,6 +164,7 @@ HEADERS += comic_flow.h \
|
|||||||
|
|
||||||
SOURCES += comic_flow.cpp \
|
SOURCES += comic_flow.cpp \
|
||||||
../common/concurrent_queue.cpp \
|
../common/concurrent_queue.cpp \
|
||||||
|
../common/cover_utils.cpp \
|
||||||
create_library_dialog.cpp \
|
create_library_dialog.cpp \
|
||||||
db/comic_query_result_processor.cpp \
|
db/comic_query_result_processor.cpp \
|
||||||
db/folder_query_result_processor.cpp \
|
db/folder_query_result_processor.cpp \
|
||||||
|
@ -370,8 +370,12 @@ QVariant FolderModel::data(const QModelIndex &index, int role) const
|
|||||||
if (role == FolderModel::IdRole)
|
if (role == FolderModel::IdRole)
|
||||||
return item->id;
|
return item->id;
|
||||||
|
|
||||||
if (role == FolderModel::CoverPathRole)
|
if (role == FolderModel::CoverPathRole) {
|
||||||
return getCoverUrlPathForComicHash(item->data(FirstChildHash).toString());
|
if (item->data(FolderModel::CustomImage).toString().isEmpty())
|
||||||
|
return getCoverUrlPathForComicHash(item->data(FirstChildHash).toString());
|
||||||
|
else
|
||||||
|
return getCoverUrlPathForFolderId(item->id);
|
||||||
|
}
|
||||||
|
|
||||||
if (role == FolderModel::NumChildrenRole)
|
if (role == FolderModel::NumChildrenRole)
|
||||||
return item->data(NumChildren);
|
return item->data(NumChildren);
|
||||||
@ -675,6 +679,50 @@ void FolderModel::updateTreeType(YACReader::FileType type)
|
|||||||
QSqlDatabase::removeDatabase(connectionName);
|
QSqlDatabase::removeDatabase(connectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FolderModel::setCustomFolderCover(const QModelIndex &index, const QString &path)
|
||||||
|
{
|
||||||
|
QString connectionName = "";
|
||||||
|
{
|
||||||
|
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
|
||||||
|
db.transaction();
|
||||||
|
|
||||||
|
auto item = static_cast<FolderItem *>(index.internalPointer());
|
||||||
|
item->setData(FolderModel::CustomImage, path);
|
||||||
|
|
||||||
|
Folder f = DBHelper::loadFolder(item->id, db);
|
||||||
|
f.customImage = path;
|
||||||
|
DBHelper::update(f, db);
|
||||||
|
|
||||||
|
db.commit();
|
||||||
|
connectionName = db.connectionName();
|
||||||
|
}
|
||||||
|
QSqlDatabase::removeDatabase(connectionName);
|
||||||
|
|
||||||
|
emit dataChanged(index, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderModel::resetFolderCover(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
QString connectionName = "";
|
||||||
|
{
|
||||||
|
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
|
||||||
|
db.transaction();
|
||||||
|
|
||||||
|
auto item = static_cast<FolderItem *>(index.internalPointer());
|
||||||
|
item->setData(FolderModel::CustomImage, "");
|
||||||
|
|
||||||
|
Folder f = DBHelper::loadFolder(item->id, db);
|
||||||
|
f.customImage = "";
|
||||||
|
DBHelper::update(f, db);
|
||||||
|
|
||||||
|
db.commit();
|
||||||
|
connectionName = db.connectionName();
|
||||||
|
}
|
||||||
|
QSqlDatabase::removeDatabase(connectionName);
|
||||||
|
|
||||||
|
emit dataChanged(index, index);
|
||||||
|
}
|
||||||
|
|
||||||
QStringList FolderModel::getSubfoldersNames(const QModelIndex &mi)
|
QStringList FolderModel::getSubfoldersNames(const QModelIndex &mi)
|
||||||
{
|
{
|
||||||
QStringList result;
|
QStringList result;
|
||||||
@ -858,6 +906,12 @@ QUrl FolderModel::getCoverUrlPathForComicHash(const QString &hash) const
|
|||||||
return QUrl::fromLocalFile(coverPath);
|
return QUrl::fromLocalFile(coverPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QUrl FolderModel::getCoverUrlPathForFolderId(qulonglong folderId) const
|
||||||
|
{
|
||||||
|
auto coverPath = LibraryPaths::customFolderCoverPathFromDataPath(_databasePath, QString::number(folderId));
|
||||||
|
return QUrl::fromLocalFile(coverPath);
|
||||||
|
}
|
||||||
|
|
||||||
void FolderModel::setShowRecent(bool showRecent)
|
void FolderModel::setShowRecent(bool showRecent)
|
||||||
{
|
{
|
||||||
if (this->showRecent == showRecent)
|
if (this->showRecent == showRecent)
|
||||||
|
@ -70,6 +70,8 @@ public:
|
|||||||
void updateFolderFinishedStatus(const QModelIndexList &list, bool status);
|
void updateFolderFinishedStatus(const QModelIndexList &list, bool status);
|
||||||
void updateFolderType(const QModelIndexList &list, YACReader::FileType type);
|
void updateFolderType(const QModelIndexList &list, YACReader::FileType type);
|
||||||
void updateTreeType(YACReader::FileType type);
|
void updateTreeType(YACReader::FileType type);
|
||||||
|
void setCustomFolderCover(const QModelIndex &index, const QString &path);
|
||||||
|
void resetFolderCover(const QModelIndex &index);
|
||||||
|
|
||||||
QStringList getSubfoldersNames(const QModelIndex &mi);
|
QStringList getSubfoldersNames(const QModelIndex &mi);
|
||||||
FolderModel *getSubfoldersModel(const QModelIndex &mi); // it creates a model that contains just the direct subfolders
|
FolderModel *getSubfoldersModel(const QModelIndex &mi); // it creates a model that contains just the direct subfolders
|
||||||
@ -81,6 +83,7 @@ public:
|
|||||||
QModelIndex addFolderAtParent(const QString &folderName, const QModelIndex &parent);
|
QModelIndex addFolderAtParent(const QString &folderName, const QModelIndex &parent);
|
||||||
|
|
||||||
Q_INVOKABLE QUrl getCoverUrlPathForComicHash(const QString &hash) const;
|
Q_INVOKABLE QUrl getCoverUrlPathForComicHash(const QString &hash) const;
|
||||||
|
Q_INVOKABLE QUrl getCoverUrlPathForFolderId(qulonglong folderId) const;
|
||||||
|
|
||||||
void setShowRecent(bool showRecent);
|
void setShowRecent(bool showRecent);
|
||||||
void setRecentRange(int days);
|
void setRecentRange(int days);
|
||||||
|
@ -824,13 +824,34 @@ void DBHelper::updateAdded(ComicInfo *comicInfo, QSqlDatabase &db)
|
|||||||
void DBHelper::update(const Folder &folder, QSqlDatabase &db)
|
void DBHelper::update(const Folder &folder, QSqlDatabase &db)
|
||||||
{
|
{
|
||||||
QSqlQuery updateFolderInfo(db);
|
QSqlQuery updateFolderInfo(db);
|
||||||
|
|
||||||
updateFolderInfo.prepare("UPDATE folder SET "
|
updateFolderInfo.prepare("UPDATE folder SET "
|
||||||
|
"parentId = :parentId, "
|
||||||
|
"name = :name, "
|
||||||
|
"path = :path, "
|
||||||
"finished = :finished, "
|
"finished = :finished, "
|
||||||
"completed = :completed "
|
"completed = :completed, "
|
||||||
"WHERE id = :id ");
|
"numChildren = :numChildren, "
|
||||||
|
"firstChildHash = :firstChildHash, "
|
||||||
|
"customImage = :customImage, "
|
||||||
|
"type = :type, "
|
||||||
|
"added = :added, "
|
||||||
|
"updated = :updated "
|
||||||
|
"WHERE id = :id");
|
||||||
|
|
||||||
|
updateFolderInfo.bindValue(":parentId", folder.parentId);
|
||||||
|
updateFolderInfo.bindValue(":name", folder.name);
|
||||||
|
updateFolderInfo.bindValue(":path", folder.path);
|
||||||
updateFolderInfo.bindValue(":finished", folder.finished ? 1 : 0);
|
updateFolderInfo.bindValue(":finished", folder.finished ? 1 : 0);
|
||||||
updateFolderInfo.bindValue(":completed", folder.completed ? 1 : 0);
|
updateFolderInfo.bindValue(":completed", folder.completed ? 1 : 0);
|
||||||
|
updateFolderInfo.bindValue(":numChildren", folder.numChildren);
|
||||||
|
updateFolderInfo.bindValue(":firstChildHash", folder.firstChildHash);
|
||||||
|
updateFolderInfo.bindValue(":customImage", folder.customImage);
|
||||||
|
updateFolderInfo.bindValue(":type", static_cast<int>(folder.type));
|
||||||
|
updateFolderInfo.bindValue(":added", folder.added);
|
||||||
|
updateFolderInfo.bindValue(":updated", folder.updated);
|
||||||
updateFolderInfo.bindValue(":id", folder.id);
|
updateFolderInfo.bindValue(":id", folder.id);
|
||||||
|
|
||||||
updateFolderInfo.exec();
|
updateFolderInfo.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ public:
|
|||||||
static void update(ComicInfo *comicInfo, QSqlDatabase &db);
|
static void update(ComicInfo *comicInfo, QSqlDatabase &db);
|
||||||
static void updateRead(ComicInfo *comicInfo, QSqlDatabase &db);
|
static void updateRead(ComicInfo *comicInfo, QSqlDatabase &db);
|
||||||
static void updateAdded(ComicInfo *comicInfo, QSqlDatabase &db);
|
static void updateAdded(ComicInfo *comicInfo, QSqlDatabase &db);
|
||||||
static void update(const Folder &folder, QSqlDatabase &db); // only for finished/completed fields
|
static void update(const Folder &folder, QSqlDatabase &db);
|
||||||
static void propagateFolderUpdatesToParent(const Folder &folder, QSqlDatabase &db);
|
static void propagateFolderUpdatesToParent(const Folder &folder, QSqlDatabase &db);
|
||||||
static Folder updateChildrenInfo(qulonglong folderId, QSqlDatabase &db);
|
static Folder updateChildrenInfo(qulonglong folderId, QSqlDatabase &db);
|
||||||
static void updateChildrenInfo(QSqlDatabase &db);
|
static void updateChildrenInfo(QSqlDatabase &db);
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "comic.h"
|
#include "comic.h"
|
||||||
#include "compressed_archive.h"
|
#include "compressed_archive.h"
|
||||||
#include "qnaturalsorting.h"
|
#include "qnaturalsorting.h"
|
||||||
|
#include "cover_utils.h"
|
||||||
|
|
||||||
using namespace YACReader;
|
using namespace YACReader;
|
||||||
|
|
||||||
@ -154,17 +155,5 @@ QByteArray InitialComicInfoExtractor::getXMLInfoRawData()
|
|||||||
|
|
||||||
void InitialComicInfoExtractor::saveCover(const QString &path, const QImage &cover)
|
void InitialComicInfoExtractor::saveCover(const QString &path, const QImage &cover)
|
||||||
{
|
{
|
||||||
QImage scaled;
|
YACReader::saveCover(path, cover);
|
||||||
if (cover.width() > cover.height()) {
|
|
||||||
scaled = cover.scaledToWidth(640, Qt::SmoothTransformation);
|
|
||||||
} else {
|
|
||||||
auto aspectRatio = static_cast<double>(cover.width()) / static_cast<double>(cover.height());
|
|
||||||
auto maxAllowedAspectRatio = 0.5;
|
|
||||||
if (aspectRatio < maxAllowedAspectRatio) { // cover is too tall, e.g. webtoon
|
|
||||||
scaled = cover.scaledToHeight(960, Qt::SmoothTransformation);
|
|
||||||
} else {
|
|
||||||
scaled = cover.scaledToWidth(480, Qt::SmoothTransformation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scaled.save(_target, 0, 75);
|
|
||||||
}
|
}
|
||||||
|
@ -83,6 +83,8 @@
|
|||||||
|
|
||||||
#include "recent_visibility_coordinator.h"
|
#include "recent_visibility_coordinator.h"
|
||||||
|
|
||||||
|
#include "cover_utils.h"
|
||||||
|
|
||||||
#include "QsLog.h"
|
#include "QsLog.h"
|
||||||
|
|
||||||
#include "yacreader_http_server.h"
|
#include "yacreader_http_server.h"
|
||||||
@ -536,6 +538,10 @@ void LibraryWindow::createMenus()
|
|||||||
foldersView->addAction(actions.setFolderAsWesternMangaAction);
|
foldersView->addAction(actions.setFolderAsWesternMangaAction);
|
||||||
foldersView->addAction(actions.setFolderAsWebComicAction);
|
foldersView->addAction(actions.setFolderAsWebComicAction);
|
||||||
foldersView->addAction(actions.setFolderAsYonkomaAction);
|
foldersView->addAction(actions.setFolderAsYonkomaAction);
|
||||||
|
YACReader::addSperator(foldersView);
|
||||||
|
|
||||||
|
foldersView->addAction(actions.setFolderCoverAction);
|
||||||
|
foldersView->addAction(actions.deleteCustomFolderCoverAction);
|
||||||
|
|
||||||
selectedLibrary->addAction(actions.updateLibraryAction);
|
selectedLibrary->addAction(actions.updateLibraryAction);
|
||||||
selectedLibrary->addAction(actions.renameLibraryAction);
|
selectedLibrary->addAction(actions.renameLibraryAction);
|
||||||
@ -669,11 +675,14 @@ void LibraryWindow::createMenus()
|
|||||||
folderMenu->addAction(actions.setFolderAsReadAction);
|
folderMenu->addAction(actions.setFolderAsReadAction);
|
||||||
folderMenu->addAction(actions.setFolderAsUnreadAction);
|
folderMenu->addAction(actions.setFolderAsUnreadAction);
|
||||||
folderMenu->addSeparator();
|
folderMenu->addSeparator();
|
||||||
foldersView->addAction(actions.setFolderAsNormalAction);
|
folderMenu->addAction(actions.setFolderAsNormalAction);
|
||||||
foldersView->addAction(actions.setFolderAsMangaAction);
|
folderMenu->addAction(actions.setFolderAsMangaAction);
|
||||||
foldersView->addAction(actions.setFolderAsWesternMangaAction);
|
folderMenu->addAction(actions.setFolderAsWesternMangaAction);
|
||||||
foldersView->addAction(actions.setFolderAsWebComicAction);
|
folderMenu->addAction(actions.setFolderAsWebComicAction);
|
||||||
foldersView->addAction(actions.setFolderAsYonkomaAction);
|
folderMenu->addAction(actions.setFolderAsYonkomaAction);
|
||||||
|
folderMenu->addSeparator();
|
||||||
|
folderMenu->addAction(actions.setFolderCoverAction);
|
||||||
|
folderMenu->addAction(actions.deleteCustomFolderCoverAction);
|
||||||
|
|
||||||
// comic
|
// comic
|
||||||
QMenu *comicMenu = new QMenu(tr("Comic"));
|
QMenu *comicMenu = new QMenu(tr("Comic"));
|
||||||
@ -823,11 +832,15 @@ void LibraryWindow::loadLibrary(const QString &name)
|
|||||||
showRootWidget();
|
showRootWidget();
|
||||||
QString rootPath = libraries.getPath(name);
|
QString rootPath = libraries.getPath(name);
|
||||||
QString path = LibraryPaths::libraryDataPath(rootPath);
|
QString path = LibraryPaths::libraryDataPath(rootPath);
|
||||||
|
QString customFolderCoversPath = LibraryPaths::libraryCustomFoldersCoverPath(rootPath);
|
||||||
QString databasePath = LibraryPaths::libraryDatabasePath(rootPath);
|
QString databasePath = LibraryPaths::libraryDatabasePath(rootPath);
|
||||||
QDir d; // TODO change this by static methods (utils class?? with delTree for example)
|
QDir d; // TODO change this by static methods (utils class?? with delTree for example)
|
||||||
QString dbVersion;
|
QString dbVersion;
|
||||||
if (d.exists(path) && d.exists(databasePath) && (dbVersion = DataBaseManagement::checkValidDB(databasePath)) != "") // si existe en disco la biblioteca seleccionada, y es válida..
|
if (d.exists(path) && d.exists(databasePath) && (dbVersion = DataBaseManagement::checkValidDB(databasePath)) != "") // si existe en disco la biblioteca seleccionada, y es válida..
|
||||||
{
|
{
|
||||||
|
// this folde was added in 9.16, it needs to exist before the user starts importing custom covers for folders
|
||||||
|
d.mkdir(customFolderCoversPath);
|
||||||
|
|
||||||
int comparation = DataBaseManagement::compareVersions(dbVersion, DB_VERSION);
|
int comparation = DataBaseManagement::compareVersions(dbVersion, DB_VERSION);
|
||||||
|
|
||||||
if (comparation < 0) {
|
if (comparation < 0) {
|
||||||
@ -1441,6 +1454,12 @@ void LibraryWindow::showGridFoldersContextMenu(QPoint point, Folder folder)
|
|||||||
auto setFolderAs4KomaAction = new QAction();
|
auto setFolderAs4KomaAction = new QAction();
|
||||||
setFolderAs4KomaAction->setText(tr("4koma (top to botom)"));
|
setFolderAs4KomaAction->setText(tr("4koma (top to botom)"));
|
||||||
|
|
||||||
|
auto setFolderCoverAction = new QAction();
|
||||||
|
setFolderCoverAction->setText(tr("Set custom cover"));
|
||||||
|
|
||||||
|
auto deleteCustomFolderCoverAction = new QAction();
|
||||||
|
deleteCustomFolderCoverAction->setText(tr("Delete custom cover"));
|
||||||
|
|
||||||
menu.addAction(openContainingFolderAction);
|
menu.addAction(openContainingFolderAction);
|
||||||
menu.addAction(updateFolderAction);
|
menu.addAction(updateFolderAction);
|
||||||
menu.addSeparator();
|
menu.addSeparator();
|
||||||
@ -1536,6 +1555,20 @@ void LibraryWindow::showGridFoldersContextMenu(QPoint point, Folder folder)
|
|||||||
foldersModel->updateFolderType(QModelIndexList() << foldersModel->getIndexFromFolder(folder), FileType::Yonkoma);
|
foldersModel->updateFolderType(QModelIndexList() << foldersModel->getIndexFromFolder(folder), FileType::Yonkoma);
|
||||||
subfolderModel->updateFolderType(QModelIndexList() << foldersModel->getIndexFromFolder(folder), FileType::Yonkoma);
|
subfolderModel->updateFolderType(QModelIndexList() << foldersModel->getIndexFromFolder(folder), FileType::Yonkoma);
|
||||||
});
|
});
|
||||||
|
connect(setFolderCoverAction, &QAction::triggered, this, [=]() {
|
||||||
|
setCustomFolderCover(folder);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(deleteCustomFolderCoverAction, &QAction::triggered, this, [=]() {
|
||||||
|
resetFolderCover(folder);
|
||||||
|
});
|
||||||
|
|
||||||
|
menu.addSeparator();
|
||||||
|
|
||||||
|
menu.addAction(setFolderCoverAction);
|
||||||
|
if (!folder.customImage.isEmpty()) {
|
||||||
|
menu.addAction(deleteCustomFolderCoverAction);
|
||||||
|
}
|
||||||
|
|
||||||
menu.exec(contentViewsManager->folderContentView->mapToGlobal(point));
|
menu.exec(contentViewsManager->folderContentView->mapToGlobal(point));
|
||||||
}
|
}
|
||||||
@ -2272,6 +2305,55 @@ void LibraryWindow::setFolderType(FileType type)
|
|||||||
foldersModel->updateFolderType(QModelIndexList() << foldersModelProxy->mapToSource(foldersView->currentIndex()), type);
|
foldersModel->updateFolderType(QModelIndexList() << foldersModelProxy->mapToSource(foldersView->currentIndex()), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LibraryWindow::setFolderCover()
|
||||||
|
{
|
||||||
|
auto folder = foldersModel->getFolder(foldersModelProxy->mapToSource(foldersView->currentIndex()));
|
||||||
|
setCustomFolderCover(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryWindow::setCustomFolderCover(Folder folder)
|
||||||
|
{
|
||||||
|
QString supportedImageFormatsString;
|
||||||
|
for (const QByteArray &format : QImageReader::supportedImageFormats()) {
|
||||||
|
supportedImageFormatsString += QString("*.%1 ").arg(QString(format));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString customCoverPath = QFileDialog::getOpenFileName(this, tr("Select custom cover"), QDir::homePath(), tr("Images (%1)").arg(supportedImageFormatsString));
|
||||||
|
if (!customCoverPath.isEmpty()) {
|
||||||
|
QImage cover(customCoverPath);
|
||||||
|
if (cover.isNull()) {
|
||||||
|
QMessageBox::warning(this, tr("Invalid image"), tr("The selected file is not a valid image."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto folderCoverPath = LibraryPaths::customFolderCoverPath(libraries.getPath(selectedLibrary->currentText()), QString::number(folder.id));
|
||||||
|
if (!YACReader::saveCover(folderCoverPath, cover)) {
|
||||||
|
QMessageBox::warning(this, tr("Error saving cover"), tr("There was an error saving the cover image."));
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex folderIndex = foldersModel->getIndexFromFolder(folder);
|
||||||
|
auto coversPath = LibraryPaths::libraryCoversFolderPath(libraries.getPath(selectedLibrary->currentText()));
|
||||||
|
auto relativePath = folderCoverPath.remove(coversPath);
|
||||||
|
foldersModel->setCustomFolderCover(folderIndex, relativePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryWindow::deleteCustomFolderCover()
|
||||||
|
{
|
||||||
|
auto folder = foldersModel->getFolder(foldersModelProxy->mapToSource(foldersView->currentIndex()));
|
||||||
|
resetFolderCover(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibraryWindow::resetFolderCover(Folder folder)
|
||||||
|
{
|
||||||
|
auto folderCoverPath = LibraryPaths::customFolderCoverPath(libraries.getPath(selectedLibrary->currentText()), QString::number(folder.id));
|
||||||
|
if (QFile::exists(folderCoverPath)) {
|
||||||
|
QFile::remove(folderCoverPath);
|
||||||
|
}
|
||||||
|
QModelIndex folderIndex = foldersModel->getIndexFromFolder(folder);
|
||||||
|
foldersModel->resetFolderCover(folderIndex);
|
||||||
|
}
|
||||||
|
|
||||||
void LibraryWindow::exportLibrary(QString destPath)
|
void LibraryWindow::exportLibrary(QString destPath)
|
||||||
{
|
{
|
||||||
QString currentLibrary = selectedLibrary->currentText();
|
QString currentLibrary = selectedLibrary->currentText();
|
||||||
@ -2513,9 +2595,7 @@ void LibraryWindow::showFoldersContextMenu(const QPoint &point)
|
|||||||
{
|
{
|
||||||
QModelIndex sourceMI = foldersModelProxy->mapToSource(foldersView->indexAt(point));
|
QModelIndex sourceMI = foldersModelProxy->mapToSource(foldersView->indexAt(point));
|
||||||
|
|
||||||
bool isCompleted = sourceMI.data(FolderModel::CompletedRole).toBool();
|
auto folder = foldersModel->getFolder(sourceMI);
|
||||||
bool isRead = sourceMI.data(FolderModel::FinishedRole).toBool();
|
|
||||||
auto type = sourceMI.data(FolderModel::TypeRole).value<YACReader::FileType>();
|
|
||||||
|
|
||||||
actions.setFolderAsNormalAction->setCheckable(true);
|
actions.setFolderAsNormalAction->setCheckable(true);
|
||||||
actions.setFolderAsMangaAction->setCheckable(true);
|
actions.setFolderAsMangaAction->setCheckable(true);
|
||||||
@ -2529,7 +2609,7 @@ void LibraryWindow::showFoldersContextMenu(const QPoint &point)
|
|||||||
actions.setFolderAsWebComicAction->setChecked(false);
|
actions.setFolderAsWebComicAction->setChecked(false);
|
||||||
actions.setFolderAsYonkomaAction->setChecked(false);
|
actions.setFolderAsYonkomaAction->setChecked(false);
|
||||||
|
|
||||||
switch (type) {
|
switch (folder.type) {
|
||||||
case FileType::Comic:
|
case FileType::Comic:
|
||||||
actions.setFolderAsNormalAction->setChecked(true);
|
actions.setFolderAsNormalAction->setChecked(true);
|
||||||
break;
|
break;
|
||||||
@ -2554,12 +2634,12 @@ void LibraryWindow::showFoldersContextMenu(const QPoint &point)
|
|||||||
menu.addSeparator(); //-------------------------------
|
menu.addSeparator(); //-------------------------------
|
||||||
menu.addAction(actions.rescanXMLFromCurrentFolderAction);
|
menu.addAction(actions.rescanXMLFromCurrentFolderAction);
|
||||||
menu.addSeparator(); //-------------------------------
|
menu.addSeparator(); //-------------------------------
|
||||||
if (isCompleted)
|
if (folder.completed)
|
||||||
menu.addAction(actions.setFolderAsNotCompletedAction);
|
menu.addAction(actions.setFolderAsNotCompletedAction);
|
||||||
else
|
else
|
||||||
menu.addAction(actions.setFolderAsCompletedAction);
|
menu.addAction(actions.setFolderAsCompletedAction);
|
||||||
menu.addSeparator(); //-------------------------------
|
menu.addSeparator(); //-------------------------------
|
||||||
if (isRead)
|
if (folder.finished)
|
||||||
menu.addAction(actions.setFolderAsUnreadAction);
|
menu.addAction(actions.setFolderAsUnreadAction);
|
||||||
else
|
else
|
||||||
menu.addAction(actions.setFolderAsReadAction);
|
menu.addAction(actions.setFolderAsReadAction);
|
||||||
@ -2571,6 +2651,11 @@ void LibraryWindow::showFoldersContextMenu(const QPoint &point)
|
|||||||
typeMenu->addAction(actions.setFolderAsWesternMangaAction);
|
typeMenu->addAction(actions.setFolderAsWesternMangaAction);
|
||||||
typeMenu->addAction(actions.setFolderAsWebComicAction);
|
typeMenu->addAction(actions.setFolderAsWebComicAction);
|
||||||
typeMenu->addAction(actions.setFolderAsYonkomaAction);
|
typeMenu->addAction(actions.setFolderAsYonkomaAction);
|
||||||
|
menu.addSeparator(); //-------------------------------
|
||||||
|
menu.addAction(actions.setFolderCoverAction);
|
||||||
|
if (!folder.customImage.isEmpty()) {
|
||||||
|
menu.addAction(actions.deleteCustomFolderCoverAction);
|
||||||
|
}
|
||||||
|
|
||||||
menu.exec(foldersView->mapToGlobal(point));
|
menu.exec(foldersView->mapToGlobal(point));
|
||||||
}
|
}
|
||||||
|
@ -246,6 +246,10 @@ public slots:
|
|||||||
void setFolderAsRead();
|
void setFolderAsRead();
|
||||||
void setFolderAsUnread();
|
void setFolderAsUnread();
|
||||||
void setFolderType(FileType type);
|
void setFolderType(FileType type);
|
||||||
|
void setFolderCover();
|
||||||
|
void setCustomFolderCover(Folder folder);
|
||||||
|
void deleteCustomFolderCover();
|
||||||
|
void resetFolderCover(Folder folder);
|
||||||
void openContainingFolderComic();
|
void openContainingFolderComic();
|
||||||
void deleteCurrentLibrary();
|
void deleteCurrentLibrary();
|
||||||
void removeLibrary();
|
void removeLibrary();
|
||||||
|
@ -281,6 +281,16 @@ void LibraryWindowActions::createActions(LibraryWindow *window, QSettings *setti
|
|||||||
setFolderAsUnreadAction->setData(SET_FOLDER_AS_UNREAD_ACTION_YL);
|
setFolderAsUnreadAction->setData(SET_FOLDER_AS_UNREAD_ACTION_YL);
|
||||||
setFolderAsUnreadAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_FOLDER_AS_UNREAD_ACTION_YL));
|
setFolderAsUnreadAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_FOLDER_AS_UNREAD_ACTION_YL));
|
||||||
|
|
||||||
|
setFolderCoverAction = new QAction(window);
|
||||||
|
setFolderCoverAction->setText(tr("Set custom cover"));
|
||||||
|
setFolderCoverAction->setData(SET_FOLDER_COVER_ACTION_YL);
|
||||||
|
setFolderCoverAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_FOLDER_COVER_ACTION_YL));
|
||||||
|
|
||||||
|
deleteCustomFolderCoverAction = new QAction(window);
|
||||||
|
deleteCustomFolderCoverAction->setText(tr("Delete custom cover"));
|
||||||
|
deleteCustomFolderCoverAction->setData(DELETE_CUSTOM_FOLDER_COVER_ACTION_YL);
|
||||||
|
deleteCustomFolderCoverAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(DELETE_CUSTOM_FOLDER_COVER_ACTION_YL));
|
||||||
|
|
||||||
setFolderAsMangaAction = new QAction(window);
|
setFolderAsMangaAction = new QAction(window);
|
||||||
setFolderAsMangaAction->setText(tr("manga"));
|
setFolderAsMangaAction->setText(tr("manga"));
|
||||||
setFolderAsMangaAction->setData(SET_FOLDER_AS_MANGA_ACTION_YL);
|
setFolderAsMangaAction->setData(SET_FOLDER_AS_MANGA_ACTION_YL);
|
||||||
@ -445,6 +455,8 @@ void LibraryWindowActions::createActions(LibraryWindow *window, QSettings *setti
|
|||||||
window->addAction(setFolderAsWesternMangaAction);
|
window->addAction(setFolderAsWesternMangaAction);
|
||||||
window->addAction(setFolderAsWebComicAction);
|
window->addAction(setFolderAsWebComicAction);
|
||||||
window->addAction(setFolderAsYonkomaAction);
|
window->addAction(setFolderAsYonkomaAction);
|
||||||
|
window->addAction(setFolderCoverAction);
|
||||||
|
window->addAction(deleteCustomFolderCoverAction);
|
||||||
window->addAction(deleteMetadataAction);
|
window->addAction(deleteMetadataAction);
|
||||||
window->addAction(rescanXMLFromCurrentFolderAction);
|
window->addAction(rescanXMLFromCurrentFolderAction);
|
||||||
window->addAction(openContainingFolderComicAction);
|
window->addAction(openContainingFolderComicAction);
|
||||||
@ -512,6 +524,8 @@ void LibraryWindowActions::createConnections(
|
|||||||
QObject::connect(setFolderAsReadAction, &QAction::triggered, window, &LibraryWindow::setFolderAsRead);
|
QObject::connect(setFolderAsReadAction, &QAction::triggered, window, &LibraryWindow::setFolderAsRead);
|
||||||
QObject::connect(setFolderAsUnreadAction, &QAction::triggered, window, &LibraryWindow::setFolderAsUnread);
|
QObject::connect(setFolderAsUnreadAction, &QAction::triggered, window, &LibraryWindow::setFolderAsUnread);
|
||||||
QObject::connect(openContainingFolderAction, &QAction::triggered, window, &LibraryWindow::openContainingFolder);
|
QObject::connect(openContainingFolderAction, &QAction::triggered, window, &LibraryWindow::openContainingFolder);
|
||||||
|
QObject::connect(setFolderCoverAction, &QAction::triggered, window, &LibraryWindow::setFolderCover);
|
||||||
|
QObject::connect(deleteCustomFolderCoverAction, &QAction::triggered, window, &LibraryWindow::deleteCustomFolderCover);
|
||||||
|
|
||||||
QObject::connect(setFolderAsMangaAction, &QAction::triggered, window, [=]() {
|
QObject::connect(setFolderAsMangaAction, &QAction::triggered, window, [=]() {
|
||||||
window->setFolderType(FileType::Manga);
|
window->setFolderType(FileType::Manga);
|
||||||
@ -630,7 +644,9 @@ void LibraryWindowActions::setUpShortcutsManagement(EditShortcutsDialog *editSho
|
|||||||
<< setFolderAsMangaAction
|
<< setFolderAsMangaAction
|
||||||
<< setFolderAsNormalAction
|
<< setFolderAsNormalAction
|
||||||
<< updateCurrentFolderAction
|
<< updateCurrentFolderAction
|
||||||
<< rescanXMLFromCurrentFolderAction);
|
<< rescanXMLFromCurrentFolderAction
|
||||||
|
<< setFolderCoverAction
|
||||||
|
<< deleteCustomFolderCoverAction);
|
||||||
allActions << tmpList;
|
allActions << tmpList;
|
||||||
|
|
||||||
editShortcutsDialog->addActionsGroup("Lists", QIcon(":/images/shortcuts_group_folders.svg"), // TODO change icon
|
editShortcutsDialog->addActionsGroup("Lists", QIcon(":/images/shortcuts_group_folders.svg"), // TODO change icon
|
||||||
|
@ -72,6 +72,9 @@ public:
|
|||||||
QAction *setFolderAsWesternMangaAction;
|
QAction *setFolderAsWesternMangaAction;
|
||||||
QAction *setFolderAsWebComicAction;
|
QAction *setFolderAsWebComicAction;
|
||||||
QAction *setFolderAsYonkomaAction;
|
QAction *setFolderAsYonkomaAction;
|
||||||
|
//--
|
||||||
|
QAction *setFolderCoverAction;
|
||||||
|
QAction *deleteCustomFolderCoverAction;
|
||||||
|
|
||||||
QAction *openContainingFolderComicAction;
|
QAction *openContainingFolderComicAction;
|
||||||
QAction *setAsReadAction;
|
QAction *setAsReadAction;
|
||||||
|
@ -18,7 +18,8 @@ void CoverControllerV2::service(HttpRequest &request, HttpResponse &response)
|
|||||||
QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8();
|
QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8();
|
||||||
QStringList pathElements = path.split('/');
|
QStringList pathElements = path.split('/');
|
||||||
QString libraryName = DBHelper::getLibraryName(pathElements.at(3).toInt());
|
QString libraryName = DBHelper::getLibraryName(pathElements.at(3).toInt());
|
||||||
QString fileName = pathElements.at(5);
|
QStringList remainingPathElements = pathElements.mid(5);
|
||||||
|
QString fileName = remainingPathElements.join('/');
|
||||||
|
|
||||||
QImage img(YACReader::LibraryPaths::coverPathWithFileName(libraries.getPath(libraryName), fileName));
|
QImage img(YACReader::LibraryPaths::coverPathWithFileName(libraries.getPath(libraryName), fileName));
|
||||||
if (!img.isNull()) {
|
if (!img.isNull()) {
|
||||||
|
@ -246,7 +246,7 @@ void RequestMapper::serviceV2(HttpRequest &request, HttpResponse &response)
|
|||||||
QRegExp comicFullInfo("/v2/library/.+/comic/[0-9]+/fullinfo/?"); // get comic info
|
QRegExp comicFullInfo("/v2/library/.+/comic/[0-9]+/fullinfo/?"); // get comic info
|
||||||
QRegExp comicUpdate("/v2/library/.+/comic/[0-9]+/update/?"); // get comic info
|
QRegExp comicUpdate("/v2/library/.+/comic/[0-9]+/update/?"); // get comic info
|
||||||
QRegExp comicClose("/v2/library/.+/comic/[0-9]+/close/?"); // the server will close the comic and free memory
|
QRegExp comicClose("/v2/library/.+/comic/[0-9]+/close/?"); // the server will close the comic and free memory
|
||||||
QRegExp cover("/v2/library/.+/cover/[0-9a-f]+.jpg"); // get comic cover (navigation)
|
QRegExp cover("/v2/library/.+/cover/.+"); // get comic cover (navigation)
|
||||||
QRegExp comicPage("/v2/library/.+/comic/[0-9]+/page/[0-9]+/?"); // get comic page
|
QRegExp comicPage("/v2/library/.+/comic/[0-9]+/page/[0-9]+/?"); // get comic page
|
||||||
QRegExp comicPageRemote("/v2/library/.+/comic/[0-9]+/page/[0-9]+/remote?"); // get comic page (remote reading)
|
QRegExp comicPageRemote("/v2/library/.+/comic/[0-9]+/page/[0-9]+/remote?"); // get comic page (remote reading)
|
||||||
QRegExp serverVersion("/v2/version/?");
|
QRegExp serverVersion("/v2/version/?");
|
||||||
|
18
common/cover_utils.cpp
Normal file
18
common/cover_utils.cpp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#include "cover_utils.h"
|
||||||
|
|
||||||
|
bool YACReader::saveCover(const QString &path, const QImage &cover)
|
||||||
|
{
|
||||||
|
QImage scaled;
|
||||||
|
if (cover.width() > cover.height()) {
|
||||||
|
scaled = cover.scaledToWidth(640, Qt::SmoothTransformation);
|
||||||
|
} else {
|
||||||
|
auto aspectRatio = static_cast<double>(cover.width()) / static_cast<double>(cover.height());
|
||||||
|
auto maxAllowedAspectRatio = 0.5;
|
||||||
|
if (aspectRatio < maxAllowedAspectRatio) { // cover is too tall, e.g. webtoon
|
||||||
|
scaled = cover.scaledToHeight(960, Qt::SmoothTransformation);
|
||||||
|
} else {
|
||||||
|
scaled = cover.scaledToWidth(480, Qt::SmoothTransformation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scaled.save(path, 0, 75);
|
||||||
|
}
|
9
common/cover_utils.h
Normal file
9
common/cover_utils.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#ifndef COVER_UTILS_H
|
||||||
|
#define COVER_UTILS_H
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
|
namespace YACReader {
|
||||||
|
bool saveCover(const QString &path, const QImage &image);
|
||||||
|
}
|
||||||
|
#endif // COVER_UTILS_H
|
@ -109,6 +109,7 @@ void iterate(const QModelIndex &index,
|
|||||||
const QAbstractItemModel *model,
|
const QAbstractItemModel *model,
|
||||||
const std::function<bool(const QModelIndex &)> &iteration);
|
const std::function<bool(const QModelIndex &)> &iteration);
|
||||||
|
|
||||||
|
// TODO: remove all the dataPath variants and always use the root folder of a library `libraryPath` to get all the paths.
|
||||||
struct LibraryPaths {
|
struct LibraryPaths {
|
||||||
LibraryPaths() = delete; // Prevent instantiation
|
LibraryPaths() = delete; // Prevent instantiation
|
||||||
|
|
||||||
@ -137,14 +138,34 @@ struct LibraryPaths {
|
|||||||
return QDir(libraryCoversFolderPath(libraryPath)).filePath(coverFileName(hash));
|
return QDir(libraryCoversFolderPath(libraryPath)).filePath(coverFileName(hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QString libraryCustomFoldersCoverPath(const QString &libraryPath) // libraryPath + /.yacreaderlibrary/covers/folders
|
||||||
|
{
|
||||||
|
return QDir(libraryCoversFolderPath(libraryPath)).filePath("folders");
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString libraryCustomFoldersCoverPathFromLibraryDataPath(const QString &libraryDataPath)
|
||||||
|
{
|
||||||
|
return QDir(libraryCoversPathFromLibraryDataPath(libraryDataPath)).filePath("folders");
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString customFolderCoverPath(const QString &libraryPath, const QString &folderId)
|
||||||
|
{
|
||||||
|
return QDir(libraryCustomFoldersCoverPath(libraryPath)).filePath(coverFileName(folderId));
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString customFolderCoverPathFromDataPath(const QString &libraryDataPath, const QString &folderId)
|
||||||
|
{
|
||||||
|
return QDir(libraryCustomFoldersCoverPathFromLibraryDataPath(libraryDataPath)).filePath(coverFileName(folderId));
|
||||||
|
}
|
||||||
|
|
||||||
static QString coverPathFromLibraryDataPath(const QString &libraryDataPath, const QString &hash) // libraryDataPath + /covers/hash + .jpg
|
static QString coverPathFromLibraryDataPath(const QString &libraryDataPath, const QString &hash) // libraryDataPath + /covers/hash + .jpg
|
||||||
{
|
{
|
||||||
return QDir(libraryCoversPathFromLibraryDataPath(libraryDataPath)).filePath(coverFileName(hash));
|
return QDir(libraryCoversPathFromLibraryDataPath(libraryDataPath)).filePath(coverFileName(hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString coverFileName(const QString &hash) // hash + .jpg
|
static QString coverFileName(const QString &id) // id + .jpg (it can be a comic hash or a folder id)
|
||||||
{
|
{
|
||||||
return hash + ".jpg";
|
return id + ".jpg";
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString coverPathWithFileName(const QString &libraryPath, const QString &fileName) // libraryPath + /.yacreaderlibrary/covers/hash + fileName
|
static QString coverPathWithFileName(const QString &libraryPath, const QString &fileName) // libraryPath + /.yacreaderlibrary/covers/hash + fileName
|
||||||
|
@ -74,6 +74,8 @@ public:
|
|||||||
#define SET_FOLDER_AS_NORMAL_ACTION_YL "SET_FOLDER_AS_NORMAL_ACTION_YL"
|
#define SET_FOLDER_AS_NORMAL_ACTION_YL "SET_FOLDER_AS_NORMAL_ACTION_YL"
|
||||||
#define SET_FOLDER_AS_WEB_COMIC_ACTION_YL "SET_FOLDER_AS_WEB_COMIC_ACTION_YL"
|
#define SET_FOLDER_AS_WEB_COMIC_ACTION_YL "SET_FOLDER_AS_WEB_COMIC_ACTION_YL"
|
||||||
#define SET_FOLDER_AS_YONKOMA_ACTION_YL "SET_FOLDER_AS_YONKOMA_ACTION_YL"
|
#define SET_FOLDER_AS_YONKOMA_ACTION_YL "SET_FOLDER_AS_YONKOMA_ACTION_YL"
|
||||||
|
#define SET_FOLDER_COVER_ACTION_YL "SET_FOLDER_COVER_ACTION_YL"
|
||||||
|
#define DELETE_CUSTOM_FOLDER_COVER_ACTION_YL "DELETE_CUSTOM_FOLDER_COVER_ACTION_YL"
|
||||||
#define OPEN_CONTAINING_FOLDER_COMIC_ACTION_YL "OPEN_CONTAINING_FOLDER_COMIC_ACTION_YL"
|
#define OPEN_CONTAINING_FOLDER_COMIC_ACTION_YL "OPEN_CONTAINING_FOLDER_COMIC_ACTION_YL"
|
||||||
#define RESET_COMIC_RATING_ACTION_YL "RESET_COMIC_RATING_ACTION_YL"
|
#define RESET_COMIC_RATING_ACTION_YL "RESET_COMIC_RATING_ACTION_YL"
|
||||||
#define SELECT_ALL_COMICS_ACTION_YL "SELECT_ALL_COMICS_ACTION_YL"
|
#define SELECT_ALL_COMICS_ACTION_YL "SELECT_ALL_COMICS_ACTION_YL"
|
||||||
|
Loading…
Reference in New Issue
Block a user