mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-01-22 20:44:15 +00:00
parent
eddeda7bab
commit
c47e29c7c8
@ -70,6 +70,7 @@ add_library(qbt_base STATIC
|
||||
preferences.h
|
||||
profile.h
|
||||
profile_p.h
|
||||
rss/feed_serializer.h
|
||||
rss/rss_article.h
|
||||
rss/rss_autodownloader.h
|
||||
rss/rss_autodownloadrule.h
|
||||
@ -152,6 +153,7 @@ add_library(qbt_base STATIC
|
||||
preferences.cpp
|
||||
profile.cpp
|
||||
profile_p.cpp
|
||||
rss/feed_serializer.cpp
|
||||
rss/rss_article.cpp
|
||||
rss/rss_autodownloader.cpp
|
||||
rss/rss_autodownloadrule.cpp
|
||||
|
@ -69,6 +69,7 @@ HEADERS += \
|
||||
$$PWD/preferences.h \
|
||||
$$PWD/profile.h \
|
||||
$$PWD/profile_p.h \
|
||||
$$PWD/rss/feed_serializer.h \
|
||||
$$PWD/rss/rss_article.h \
|
||||
$$PWD/rss/rss_autodownloader.h \
|
||||
$$PWD/rss/rss_autodownloadrule.h \
|
||||
@ -152,6 +153,7 @@ SOURCES += \
|
||||
$$PWD/preferences.cpp \
|
||||
$$PWD/profile.cpp \
|
||||
$$PWD/profile_p.cpp \
|
||||
$$PWD/rss/feed_serializer.cpp \
|
||||
$$PWD/rss/rss_article.cpp \
|
||||
$$PWD/rss/rss_autodownloader.cpp \
|
||||
$$PWD/rss/rss_autodownloadrule.cpp \
|
||||
|
126
src/base/rss/feed_serializer.cpp
Normal file
126
src/base/rss/feed_serializer.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2022 Prince Gupta <guptaprince8832@gmail.com>
|
||||
* Copyright (C) 2015-2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
|
||||
* Copyright (C) 2010 Arnaud Demaiziere <arnaud@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "feed_serializer.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QVector>
|
||||
|
||||
#include "base/logger.h"
|
||||
#include "base/path.h"
|
||||
#include "base/utils/io.h"
|
||||
#include "rss_article.h"
|
||||
|
||||
void RSS::Private::FeedSerializer::load(const Path &dataFileName, const QString &url)
|
||||
{
|
||||
QFile file {dataFileName.data()};
|
||||
|
||||
if (!file.exists())
|
||||
{
|
||||
emit loadingFinished({});
|
||||
}
|
||||
else if (file.open(QFile::ReadOnly))
|
||||
{
|
||||
emit loadingFinished(loadArticles(file.readAll(), url));
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMsg(tr("Couldn't read RSS Session data from %1. Error: %2")
|
||||
.arg(dataFileName.toString(), file.errorString())
|
||||
, Log::WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
void RSS::Private::FeedSerializer::store(const Path &dataFileName, const QVector<QVariantHash> &articlesData)
|
||||
{
|
||||
QJsonArray arr;
|
||||
for (const QVariantHash &data : articlesData)
|
||||
{
|
||||
auto jsonObj = QJsonObject::fromVariantHash(data);
|
||||
// JSON object doesn't support DateTime so we need to convert it
|
||||
jsonObj[Article::KeyDate] = data[Article::KeyDate].toDateTime().toString(Qt::RFC2822Date);
|
||||
|
||||
arr << jsonObj;
|
||||
}
|
||||
|
||||
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(dataFileName, QJsonDocument(arr).toJson());
|
||||
if (!result)
|
||||
{
|
||||
LogMsg(tr("Failed to save RSS feed in '%1', Reason: %2").arg(dataFileName.toString(), result.error())
|
||||
, Log::WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
QVector<QVariantHash> RSS::Private::FeedSerializer::loadArticles(const QByteArray &data, const QString &url)
|
||||
{
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
|
||||
if (jsonError.error != QJsonParseError::NoError)
|
||||
{
|
||||
LogMsg(tr("Couldn't parse RSS Session data. Error: %1").arg(jsonError.errorString())
|
||||
, Log::WARNING);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!jsonDoc.isArray())
|
||||
{
|
||||
LogMsg(tr("Couldn't load RSS Session data. Invalid data format."), Log::WARNING);
|
||||
return {};
|
||||
}
|
||||
|
||||
QVector<QVariantHash> result;
|
||||
const QJsonArray jsonArr = jsonDoc.array();
|
||||
result.reserve(jsonArr.size());
|
||||
for (int i = 0; i < jsonArr.size(); ++i)
|
||||
{
|
||||
const QJsonValue jsonVal = jsonArr[i];
|
||||
if (!jsonVal.isObject())
|
||||
{
|
||||
LogMsg(tr("Couldn't load RSS article '%1#%2'. Invalid data format.")
|
||||
.arg(url, QString::number(i)), Log::WARNING);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto jsonObj = jsonVal.toObject();
|
||||
auto varHash = jsonObj.toVariantHash();
|
||||
// JSON object store DateTime as string so we need to convert it
|
||||
varHash[Article::KeyDate] =
|
||||
QDateTime::fromString(jsonObj.value(Article::KeyDate).toString(), Qt::RFC2822Date);
|
||||
|
||||
result.push_back(varHash);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
60
src/base/rss/feed_serializer.h
Normal file
60
src/base/rss/feed_serializer.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2022 Prince Gupta <guptaprince8832@gmail.com>
|
||||
* Copyright (C) 2015-2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
|
||||
* Copyright (C) 2010 Arnaud Demaiziere <arnaud@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtContainerFwd>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVariantHash>
|
||||
|
||||
#include "base/pathfwd.h"
|
||||
|
||||
namespace RSS::Private
|
||||
{
|
||||
class FeedSerializer final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(FeedSerializer)
|
||||
|
||||
public:
|
||||
using QObject::QObject;
|
||||
|
||||
void load(const Path &dataFileName, const QString &url);
|
||||
void store(const Path &dataFileName, const QVector<QVariantHash> &articlesData);
|
||||
|
||||
signals:
|
||||
void loadingFinished(const QVector<QVariantHash> &articles);
|
||||
|
||||
private:
|
||||
QVector<QVariantHash> loadArticles(const QByteArray &data, const QString &url);
|
||||
};
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015, 2017 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2015-2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
|
||||
* Copyright (C) 2010 Arnaud Demaiziere <arnaud@qbittorrent.org>
|
||||
*
|
||||
@ -34,12 +34,12 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <QDir>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QUrl>
|
||||
#include <QVector>
|
||||
|
||||
#include "base/asyncfilestorage.h"
|
||||
#include "base/global.h"
|
||||
@ -47,6 +47,7 @@
|
||||
#include "base/net/downloadmanager.h"
|
||||
#include "base/profile.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "feed_serializer.h"
|
||||
#include "rss_article.h"
|
||||
#include "rss_parser.h"
|
||||
#include "rss_session.h"
|
||||
@ -79,6 +80,11 @@ Feed::Feed(const QUuid &uid, const QString &url, const QString &path, Session *s
|
||||
|
||||
m_iconPath = storageDir / Path(uidHex + u".ico");
|
||||
|
||||
m_serializer = new Private::FeedSerializer;
|
||||
m_serializer->moveToThread(m_session->workingThread());
|
||||
connect(this, &Feed::destroyed, m_serializer, &Private::FeedSerializer::deleteLater);
|
||||
connect(m_serializer, &Private::FeedSerializer::loadingFinished, this, &Feed::handleArticleLoadFinished);
|
||||
|
||||
m_parser = new Private::Parser(m_lastBuildDate);
|
||||
m_parser->moveToThread(m_session->workingThread());
|
||||
connect(this, &Feed::destroyed, m_parser, &Private::Parser::deleteLater);
|
||||
@ -98,6 +104,7 @@ Feed::Feed(const QUuid &uid, const QString &url, const QString &path, Session *s
|
||||
|
||||
Feed::~Feed()
|
||||
{
|
||||
store();
|
||||
emit aboutToBeDestroyed(this);
|
||||
}
|
||||
|
||||
@ -130,6 +137,12 @@ void Feed::markAsRead()
|
||||
|
||||
void Feed::refresh()
|
||||
{
|
||||
if (!m_isInitialized)
|
||||
{
|
||||
m_pendingRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_downloadHandler)
|
||||
m_downloadHandler->cancel();
|
||||
|
||||
@ -162,7 +175,7 @@ QString Feed::title() const
|
||||
|
||||
bool Feed::isLoading() const
|
||||
{
|
||||
return m_isLoading;
|
||||
return m_isLoading || !m_isInitialized;
|
||||
}
|
||||
|
||||
QString Feed::lastBuildDate() const
|
||||
@ -261,100 +274,30 @@ void Feed::handleParsingFinished(const RSS::Private::ParsingResult &result)
|
||||
|
||||
void Feed::load()
|
||||
{
|
||||
QFile file {(m_session->dataFileStorage()->storageDir() / m_dataFileName).data()};
|
||||
|
||||
if (!file.exists())
|
||||
QMetaObject::invokeMethod(m_serializer, [this]()
|
||||
{
|
||||
loadArticlesLegacy();
|
||||
m_dirty = true;
|
||||
store(); // convert to new format
|
||||
}
|
||||
else if (file.open(QFile::ReadOnly))
|
||||
{
|
||||
loadArticles(file.readAll());
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMsg(tr("Couldn't read RSS Session data from %1. Error: %2")
|
||||
.arg(m_dataFileName.toString(), file.errorString())
|
||||
, Log::WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
void Feed::loadArticles(const QByteArray &data)
|
||||
{
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
|
||||
if (jsonError.error != QJsonParseError::NoError)
|
||||
{
|
||||
LogMsg(tr("Couldn't parse RSS Session data. Error: %1").arg(jsonError.errorString())
|
||||
, Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!jsonDoc.isArray())
|
||||
{
|
||||
LogMsg(tr("Couldn't load RSS Session data. Invalid data format."), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
const QJsonArray jsonArr = jsonDoc.array();
|
||||
int i = -1;
|
||||
for (const QJsonValue &jsonVal : jsonArr)
|
||||
{
|
||||
++i;
|
||||
if (!jsonVal.isObject())
|
||||
{
|
||||
LogMsg(tr("Couldn't load RSS article '%1#%2'. Invalid data format.").arg(m_url).arg(i)
|
||||
, Log::WARNING);
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto article = new Article(this, jsonVal.toObject());
|
||||
if (!addArticle(article))
|
||||
delete article;
|
||||
}
|
||||
catch (const RuntimeError &) {}
|
||||
}
|
||||
}
|
||||
|
||||
void Feed::loadArticlesLegacy()
|
||||
{
|
||||
const SettingsPtr qBTRSSFeeds = Profile::instance()->applicationSettings(u"qBittorrent-rss-feeds"_qs);
|
||||
const QVariantHash allOldItems = qBTRSSFeeds->value(u"old_items"_qs).toHash();
|
||||
|
||||
for (const QVariant &var : asConst(allOldItems.value(m_url).toList()))
|
||||
{
|
||||
auto hash = var.toHash();
|
||||
// update legacy keys
|
||||
hash[Article::KeyLink] = hash.take(u"news_link"_qs);
|
||||
hash[Article::KeyTorrentURL] = hash.take(u"torrent_url"_qs);
|
||||
hash[Article::KeyIsRead] = hash.take(u"read"_qs);
|
||||
try
|
||||
{
|
||||
auto article = new Article(this, hash);
|
||||
if (!addArticle(article))
|
||||
delete article;
|
||||
}
|
||||
catch (const RuntimeError &) {}
|
||||
}
|
||||
m_serializer->load((m_session->dataFileStorage()->storageDir() / m_dataFileName), m_url);
|
||||
});
|
||||
}
|
||||
|
||||
void Feed::store()
|
||||
{
|
||||
if (!m_dirty) return;
|
||||
if (!m_dirty)
|
||||
return;
|
||||
|
||||
m_dirty = false;
|
||||
m_savingTimer.stop();
|
||||
|
||||
QJsonArray jsonArr;
|
||||
for (Article *article :asConst(m_articles))
|
||||
jsonArr << article->toJsonObject();
|
||||
QVector<QVariantHash> articlesData;
|
||||
articlesData.reserve(m_articles.size());
|
||||
|
||||
m_session->dataFileStorage()->store(m_dataFileName, QJsonDocument(jsonArr).toJson());
|
||||
for (Article *article :asConst(m_articles))
|
||||
articlesData.push_back(article->data());
|
||||
|
||||
QMetaObject::invokeMethod(m_serializer, [this, articlesData]()
|
||||
{
|
||||
m_serializer->store((m_session->dataFileStorage()->storageDir() / m_dataFileName), articlesData);
|
||||
});
|
||||
}
|
||||
|
||||
void Feed::storeDeferred()
|
||||
@ -546,8 +489,33 @@ void Feed::handleArticleRead(Article *article)
|
||||
storeDeferred();
|
||||
}
|
||||
|
||||
void Feed::handleArticleLoadFinished(const QVector<QVariantHash> &articles)
|
||||
{
|
||||
for (const QVariantHash &data : articles)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto *article = new Article(this, data);
|
||||
if (!addArticle(article))
|
||||
delete article;
|
||||
}
|
||||
catch (const RuntimeError &) {}
|
||||
}
|
||||
|
||||
m_isInitialized = true;
|
||||
emit stateChanged(this);
|
||||
|
||||
if (m_pendingRefresh)
|
||||
{
|
||||
m_pendingRefresh = false;
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void Feed::cleanup()
|
||||
{
|
||||
m_dirty = false;
|
||||
m_savingTimer.stop();
|
||||
Utils::Fs::removeFile(m_session->dataFileStorage()->storageDir() / m_dataFileName);
|
||||
Utils::Fs::removeFile(m_iconPath);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015, 2017 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2015-2022 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
|
||||
* Copyright (C) 2010 Arnaud Demaiziere <arnaud@qbittorrent.org>
|
||||
*
|
||||
@ -30,10 +30,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtContainerFwd>
|
||||
#include <QBasicTimer>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QUuid>
|
||||
#include <QVariantHash>
|
||||
|
||||
#include "base/path.h"
|
||||
#include "rss_item.h"
|
||||
@ -53,6 +55,7 @@ namespace RSS
|
||||
|
||||
namespace Private
|
||||
{
|
||||
class FeedSerializer;
|
||||
class Parser;
|
||||
struct ParsingResult;
|
||||
}
|
||||
@ -96,13 +99,12 @@ namespace RSS
|
||||
void handleDownloadFinished(const Net::DownloadResult &result);
|
||||
void handleParsingFinished(const Private::ParsingResult &result);
|
||||
void handleArticleRead(Article *article);
|
||||
void handleArticleLoadFinished(const QVector<QVariantHash> &articles);
|
||||
|
||||
private:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
void cleanup() override;
|
||||
void load();
|
||||
void loadArticles(const QByteArray &data);
|
||||
void loadArticlesLegacy();
|
||||
void store();
|
||||
void storeDeferred();
|
||||
bool addArticle(Article *article);
|
||||
@ -112,14 +114,17 @@ namespace RSS
|
||||
void downloadIcon();
|
||||
int updateArticles(const QList<QVariantHash> &loadedArticles);
|
||||
|
||||
Session *m_session;
|
||||
Private::Parser *m_parser;
|
||||
Session *m_session = nullptr;
|
||||
Private::Parser *m_parser = nullptr;
|
||||
Private::FeedSerializer *m_serializer = nullptr;
|
||||
const QUuid m_uid;
|
||||
const QString m_url;
|
||||
QString m_title;
|
||||
QString m_lastBuildDate;
|
||||
bool m_hasError = false;
|
||||
bool m_isLoading = false;
|
||||
bool m_isInitialized = false;
|
||||
bool m_pendingRefresh = false;
|
||||
QHash<QString, Article *> m_articles;
|
||||
QList<Article *> m_articlesByDate;
|
||||
int m_unreadCount = 0;
|
||||
|
@ -122,12 +122,14 @@ Session::~Session()
|
||||
{
|
||||
qDebug() << "Deleting RSS Session...";
|
||||
|
||||
m_workingThread->quit();
|
||||
m_workingThread->wait();
|
||||
|
||||
//store();
|
||||
delete m_itemsByPath[u""_qs]; // deleting root folder
|
||||
|
||||
// some items may add I/O operation at destruction
|
||||
// stop working thread after deleting root folder
|
||||
m_workingThread->quit();
|
||||
m_workingThread->wait();
|
||||
|
||||
qDebug() << "RSS Session deleted.";
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user