Merge pull request #743 from kiwix/monitorDirectory

Enable directory monitoring for new zims
This commit is contained in:
Kelson 2022-02-08 21:25:49 +01:00 committed by GitHub
commit df2a95273a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 293 additions and 27 deletions

View File

@ -1,5 +1,5 @@
QWidget#widget {
max-width: 500;
max-width: 600;
}
QLabel,
@ -23,8 +23,8 @@ QLabel#settingsLabel {
font-weight: bold;
}
QSpinBox,
QLabel#downloadDirPath {
font: bold 12pt "Cantarell";
QLabel#downloadDirPath, #monitorDirPath {
font: bold 12pt;
}
QPushButton {
background-color: white;
@ -38,3 +38,18 @@ QPushButton:hover {
background-color: #3366cc;
color: white;
}
#monitorDirLabel {
padding-right: 0;
}
#monitorHelp {
padding: 1;
margin: 0;
background: #666666;
color: white;
border-radius: 2px;
max-height: 18px;
min-width: 14px;
font: bold;
}

View File

@ -69,7 +69,10 @@
"port-for-local-kiwix-server-setting":"Port for local Kiwix server",
"zoom-level-setting":"Default zoom level",
"download-directory-setting":"Download directory",
"monitor-directory-setting":"Monitor directory",
"monitor-directory-default":"Not set",
"reset":"Reset",
"clear":"Clear",
"browse":"Browse",
"about-kiwix-desktop-title":"Kiwix Desktop",
"about-kiwix-desktop-description":"Kiwix enables you to have the whole Wikipedia at hand wherever you go! On a boat, in the middle of nowhere or in jail, Kiwix gives you access to the whole human knowledge. You don't need Internet, everything is stored on your computer.",
@ -124,5 +127,10 @@
"download-dir-dialog-msg":"The new download directory path will be:\n{{DIRECTORY}}",
"invalid-port":"Invalid port",
"zim-open-fail-title":"Invalid file",
"zim-open-fail-text":"The ZIM file <b>{{ZIM}}</b> cannot be opened properly. It will be removed from your library."
"zim-open-fail-text":"The ZIM file <b>{{ZIM}}</b> cannot be opened properly. It will be removed from your library.",
"monitor-dir-dialog-title":"Are you sure you want to change the monitor directory?",
"monitor-dir-dialog-msg":"The new monitor directory path will be:\n{{DIRECTORY}}",
"monitor-clear-dir-dialog-title":"Are you sure you want to clear the monitor directory?",
"monitor-clear-dir-dialog-msg":"This will stop checking the monitor directory for new ZIM files.",
"monitor-directory-tooltip":"All ZIM files in this directory will be automatically added to the library."
}

View File

@ -101,6 +101,15 @@ void KiwixApp::init()
this->openZimFile(message);
}
});
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, [=](QString monitorDir) {
m_library.asyncLoadMonitorDir(monitorDir);
});
QString monitorDir = m_settingsManager.getMonitorDir();
if (monitorDir != "") {
m_library.setMonitorDirZims(m_library.getLibraryZimsFromDir(monitorDir));
m_watcher.addPath(monitorDir);
m_library.asyncLoadMonitorDir(monitorDir);
}
}
KiwixApp::~KiwixApp()
@ -273,6 +282,18 @@ bool KiwixApp::isCurrentArticleBookmarked()
return false;
}
void KiwixApp::setMonitorDir(const QString &dir) {
m_settingsManager.setMonitorDir(dir);
m_library.setMonitorDirZims(QStringList());
for (auto path : m_watcher.directories()) {
m_watcher.removePath(path);
}
if (dir != "") {
m_watcher.addPath(dir);
m_library.asyncLoadMonitorDir(dir);
}
}
#define CREATE_ACTION(ID, TEXT) \
mpa_actions[ID] = new QAction(TEXT)
#define SET_SHORTCUT(ID, TEXT, SHORTCUT) \

View File

@ -79,7 +79,7 @@ public:
kiwix::Server* getLocalServer() { return &m_server; }
SettingsManager* getSettingsManager() { return &m_settingsManager; };
QString getText(const QString &key) { return m_translation.getText(key); };
void setMonitorDir(const QString &dir);
bool isCurrentArticleBookmarked();
public slots:
@ -108,6 +108,7 @@ private:
kiwix::UpdatableNameMapper m_nameMapper;
kiwix::Server m_server;
Translation m_translation;
QFileSystemWatcher m_watcher;
QAction* mpa_actions[MAX_ACTION];

View File

@ -5,6 +5,8 @@
#include <kiwix/tools.h>
#include <QtDebug>
#include <QtConcurrent/QtConcurrentRun>
class LibraryManipulator: public kiwix::LibraryManipulator {
public:
@ -76,7 +78,7 @@ std::shared_ptr<kiwix::Searcher> Library::getSearcher(const QString &zimId)
return searcher;
}
QStringList Library::getBookIds()
QStringList Library::getBookIds() const
{
QStringList list;
for(auto& id: m_library.getBooksIds()) {
@ -85,7 +87,7 @@ QStringList Library::getBookIds()
return list;
}
QStringList Library::listBookIds(const kiwix::Filter& filter, kiwix::supportedListSortBy sortBy, bool ascending)
QStringList Library::listBookIds(const kiwix::Filter& filter, kiwix::supportedListSortBy sortBy, bool ascending) const
{
QStringList list;
auto bookIds = m_library.filter(filter);
@ -123,6 +125,58 @@ void Library::save()
m_library.writeBookmarksToFile(kiwix::appendToDirectory(m_libraryDirectory.toStdString(), "library.bookmarks.xml"));
}
void Library::setMonitorDirZims(QStringList zimList)
{
m_monitorDirZims = zimList;
}
QStringList Library::getLibraryZimsFromDir(QString dir) const
{
QStringList zimsInDir;
for (auto str : getBookIds()) {
auto filePath = QString::fromStdString(getBookById(str).getPath());
QDir absoluteDir = QFileInfo(filePath).absoluteDir();
if (absoluteDir == dir) {
zimsInDir.push_back(filePath);
}
}
return zimsInDir;
}
void Library::loadMonitorDir(QString monitorDir)
{
QMutex mutex;
QMutexLocker locker(&mutex);
const QDir dir(monitorDir);
QStringList newDirEntries = dir.entryList({"*.zim"});
for (auto &str : newDirEntries) {
str = monitorDir + QDir::separator() + str;
}
QSet<QString> newDir = QSet<QString>::fromList(newDirEntries);
QStringList oldDirEntries = m_monitorDirZims;
QSet<QString> oldDir = QSet<QString>::fromList(oldDirEntries);
QStringList addedZims = (newDir - oldDir).values();
QStringList removedZims = (oldDir - newDir).values();
setMonitorDirZims(newDir.values());
auto manipulator = LibraryManipulator(this);
auto manager = kiwix::Manager(&manipulator);
for (auto book : addedZims) {
manager.addBookFromPath(book.toStdString());
}
for (auto bookPath : removedZims) {
removeBookFromLibraryById(QString::fromStdString(m_library.getBookByPath(bookPath.toStdString()).getId()));
}
emit(booksChanged());
save();
}
void Library::asyncLoadMonitorDir(QString dir)
{
QtConcurrent::run( [=]() {
loadMonitorDir(dir);
});
}
const kiwix::Book &Library::getBookById(QString id) const
{
return m_library.getBookById(id.toStdString());

View File

@ -30,14 +30,18 @@ public:
QString openBookFromPath(const QString& zimPath);
std::shared_ptr<kiwix::Reader> getReader(const QString& zimId);
std::shared_ptr<kiwix::Searcher> getSearcher(const QString& zimId);
QStringList getBookIds();
QStringList listBookIds(const kiwix::Filter& filter, kiwix::supportedListSortBy sortBy, bool ascending);
const std::vector<kiwix::Bookmark> getBookmarks(bool onlyValidBookmarks = false) { return m_library.getBookmarks(onlyValidBookmarks); }
QStringList getBookIds() const;
QStringList listBookIds(const kiwix::Filter& filter, kiwix::supportedListSortBy sortBy, bool ascending) const;
const std::vector<kiwix::Bookmark> getBookmarks(bool onlyValidBookmarks = false) const { return m_library.getBookmarks(onlyValidBookmarks); }
QStringList getLibraryZimsFromDir(QString dir) const;
void setMonitorDirZims(QStringList zimList);
void addBookToLibrary(kiwix::Book& book);
void removeBookFromLibraryById(const QString& id);
void addBookmark(kiwix::Bookmark& bookmark);
void removeBookmark(const QString& zimId, const QString& url);
void save();
void loadMonitorDir(QString dir);
void asyncLoadMonitorDir(QString dir);
kiwix::Library& getKiwixLibrary() { return m_library; }
public slots:
const kiwix::Book& getBookById(QString id) const;
@ -49,6 +53,7 @@ signals:
private:
kiwix::Library m_library;
QString m_libraryDirectory;
QStringList m_monitorDirZims;
friend class LibraryManipulator;
};

View File

@ -16,7 +16,7 @@ SettingsView* SettingsManager::getView()
{
if (m_view == nullptr) {
auto view = new SettingsView();
view->init(m_zoomFactor * 100, m_downloadDir);
view->init(m_zoomFactor * 100, m_downloadDir, m_monitorDir);
connect(view, &QObject::destroyed, this, [=]() { m_view = nullptr; });
m_view = view;
}
@ -73,12 +73,18 @@ void SettingsManager::setZoomFactor(qreal zoomFactor)
emit(zoomChanged(zoomFactor));
}
bool SettingsManager::setDownloadDir(QString downloadDir)
void SettingsManager::setDownloadDir(QString downloadDir)
{
m_downloadDir = downloadDir;
m_settings.setValue("download/dir", downloadDir);
emit(downloadDirChanged(downloadDir));
return true;
}
void SettingsManager::setMonitorDir(QString monitorDir)
{
m_monitorDir = monitorDir;
m_settings.setValue("monitor/dir", monitorDir);
emit(monitorDirChanged(monitorDir));
}
void SettingsManager::initSettings()
@ -87,4 +93,5 @@ void SettingsManager::initSettings()
m_zoomFactor = m_settings.value("view/zoomFactor", 1).toDouble();
m_downloadDir = m_settings.value("download/dir", QString::fromStdString(kiwix::getDataDirectory())).toString();
m_kiwixServerIpAddress = m_settings.value("localKiwixServer/ipAddress", QString("0.0.0.0")).toString();
m_monitorDir = m_settings.value("monitor/dir", QString("")).toString();
}

View File

@ -22,16 +22,18 @@ public:
bool settingsExists(const QString &key);
QVariant getSettings(const QString &key);
qreal getZoomFactorByZimId(const QString &id);
int getKiwixServerPort() const { return m_kiwixServerPort; }
QString getKiwixServerIpAddress() const { return m_kiwixServerIpAddress; }
qreal getZoomFactor() const { return m_zoomFactor; }
QString getDownloadDir() const { return m_downloadDir; }
QString getMonitorDir() const { return m_monitorDir; }
public slots:
void setKiwixServerPort(int port);
int getKiwixServerPort() { return m_kiwixServerPort; };
void setKiwixServerIpAddress(QString ipAddress);
QString getKiwixServerIpAddress() { return m_kiwixServerIpAddress; };
void setZoomFactor(qreal zoomFactor);
qreal getZoomFactor() { return m_zoomFactor; };
bool setDownloadDir(QString downloadDir);
QString getDownloadDir() { return m_downloadDir; }
void setDownloadDir(QString downloadDir);
void setMonitorDir(QString monitorDir);
private:
void initSettings();
@ -39,6 +41,7 @@ signals:
void portChanged(int port);
void zoomChanged(qreal zoomFactor);
void downloadDirChanged(QString downloadDir);
void monitorDirChanged(QString monitorDir);
private:
QSettings m_settings;
@ -47,6 +50,7 @@ private:
QString m_kiwixServerIpAddress;
qreal m_zoomFactor;
QString m_downloadDir;
QString m_monitorDir;
};
#endif // SETTINGSMANAGER_H

View File

@ -16,27 +16,38 @@ SettingsView::SettingsView(QWidget *parent)
connect(ui->zoomPercentSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &SettingsView::setZoom);
connect(ui->browseButton, &QPushButton::clicked, this, &SettingsView::browseDownloadDir);
connect(ui->resetButton, &QPushButton::clicked, this, &SettingsView::resetDownloadDir);
connect(ui->monitorBrowse, &QPushButton::clicked, this, &SettingsView::browseMonitorDir);
connect(ui->monitorClear, &QPushButton::clicked, this, &SettingsView::clearMonitorDir);
connect(KiwixApp::instance()->getSettingsManager(), &SettingsManager::downloadDirChanged, this, &SettingsView::onDownloadDirChanged);
connect(KiwixApp::instance()->getSettingsManager(), &SettingsManager::monitorDirChanged, this, &SettingsView::onMonitorDirChanged);
connect(KiwixApp::instance()->getSettingsManager(), &SettingsManager::zoomChanged, this, &SettingsView::onZoomChanged);
ui->settingsLabel->setText(gt("settings"));
ui->zoomPercentLabel->setText(gt("zoom-level-setting"));
ui->downloadDirLabel->setText(gt("download-directory-setting"));
ui->monitorDirLabel->setText(gt("monitor-directory-setting"));
ui->resetButton->setText(gt("reset"));
ui->browseButton->setText(gt("browse"));
ui->monitorClear->setText(gt("clear"));
ui->monitorBrowse->setText(gt("browse"));
ui->monitorHelp->setText("<b>?</b>");
ui->monitorHelp->setToolTip(gt("monitor-directory-tooltip"));
}
void SettingsView::init(int zoomPercent, const QString &dir)
void SettingsView::init(int zoomPercent, const QString &downloadDir, const QString &monitorDir)
{
ui->zoomPercentSpinBox->setValue(zoomPercent);
ui->downloadDirPath->setText(dir);
ui->downloadDirPath->setText(downloadDir);
if (monitorDir == QString()) {
ui->monitorClear->hide();
}
ui->monitorDirPath->setText(monitorDir);
}
bool SettingsView::confirmDialogDownloadDir(const QString& dir)
bool SettingsView::confirmDialog( QString messageText, QString messageTitle)
{
auto text = gt("download-dir-dialog-msg");
text = text.replace("{{DIRECTORY}}", dir);
QMessageBox msgBox(
QMessageBox::Question, //Icon
gt("download-dir-dialog-title"), //Title
text, //Text
messageTitle, //Title
messageText, //Text
QMessageBox::Ok | QMessageBox::Cancel //Buttons
);
msgBox.setDefaultButton(QMessageBox::Ok);
@ -45,6 +56,19 @@ bool SettingsView::confirmDialogDownloadDir(const QString& dir)
return (ret == QMessageBox::Ok);
}
bool SettingsView::confirmDialogDownloadDir(const QString &dir) {
auto messageText = gt("download-dir-dialog-msg");
messageText = messageText.replace("{{DIRECTORY}}", dir);
return confirmDialog(messageText, gt("download-dir-dialog-title"));
}
bool SettingsView::confirmDialogMonitorDir(const QString &dir) {
auto messageText = gt("monitor-dir-dialog-msg");
messageText = messageText.replace("{{DIRECTORY}}", dir);
auto messageTitle = gt("monitor-dir-dialog-title");
return confirmDialog(messageText, messageTitle);
}
void SettingsView::resetDownloadDir()
{
auto dir = QString::fromStdString(kiwix::getDataDirectory());
@ -73,6 +97,34 @@ void SettingsView::browseDownloadDir()
}
}
void SettingsView::browseMonitorDir()
{
const auto &monitorDir = KiwixApp::instance()->getSettingsManager()->getMonitorDir();
QString previousDir;
if (monitorDir == "") {
previousDir = KiwixApp::instance()->getSettingsManager()->getDownloadDir();
} else {
previousDir = monitorDir;
}
QString dir = QFileDialog::getExistingDirectory(KiwixApp::instance()->getMainWindow(),
gt("browse-directory"),
previousDir,
QFileDialog::ShowDirsOnly);
if (dir == monitorDir || dir.isEmpty()) {
return;
}
if (confirmDialogMonitorDir(dir)) {
KiwixApp::instance()->setMonitorDir(dir);
}
}
void SettingsView::clearMonitorDir()
{
if (confirmDialog(gt("monitor-clear-dir-dialog-msg"), gt("monitor-clear-dir-dialog-title"))) {
KiwixApp::instance()->setMonitorDir("");
}
}
void SettingsView::setZoom(int zoomPercent)
{
qreal zoomFactor = (qreal) zoomPercent/100;
@ -84,6 +136,16 @@ void SettingsView::onDownloadDirChanged(const QString &dir)
ui->downloadDirPath->setText(dir);
}
void SettingsView::onMonitorDirChanged(const QString &dir)
{
if (dir == "") {
ui->monitorClear->hide();
} else {
ui->monitorClear->show();
}
ui->monitorDirPath->setText(dir);
}
void SettingsView::onZoomChanged(qreal zoomFactor)
{
qreal zoomPercent = zoomFactor * 100;

View File

@ -11,16 +11,20 @@ class SettingsView : public QWidget
public:
SettingsView(QWidget *parent = nullptr);
~SettingsView(){};
void init(int zoomPercent, const QString &dir);
void init(int zoomPercent, const QString &downloadDir, const QString &monitorDir);
public Q_SLOTS:
void resetDownloadDir();
void browseDownloadDir();
void browseMonitorDir();
void clearMonitorDir();
void setZoom(int zoomPercent);
void onDownloadDirChanged(const QString &dir);
void onMonitorDirChanged(const QString &dir);
void onZoomChanged(qreal zoomFactor);
private:
bool confirmDialogDownloadDir(const QString& dir);
bool confirmDialog(QString messageText, QString messageTitle);
bool confirmDialogMonitorDir(const QString& dir);
Ui::Settings *ui;
};

View File

@ -199,6 +199,91 @@
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="monitorDirLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="monitorHelp">
<property name="text">
<string>?</string>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="indent">
<number>-5</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="monitorDirPath">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="monitorClear">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="monitorBrowse">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">