preload all local IDs, fix feedslist item reloading upon feed fetching, add handler to allow plugins to perform plugin-wide feed fetching befor individual feeds are fetched

This commit is contained in:
Martin Rotter 2021-07-30 08:34:07 +02:00
parent dfc28bcd4e
commit 79827ad6af
20 changed files with 284 additions and 280 deletions

View File

@ -30,7 +30,7 @@
<url type="donation">https://martinrotter.github.io/donate/</url> <url type="donation">https://martinrotter.github.io/donate/</url>
<content_rating type="oars-1.1" /> <content_rating type="oars-1.1" />
<releases> <releases>
<release version="3.9.2" date="2021-07-28"/> <release version="3.9.2" date="2021-07-30"/>
</releases> </releases>
<content_rating type="oars-1.0"> <content_rating type="oars-1.0">
<content_attribute id="violence-cartoon">none</content_attribute> <content_attribute id="violence-cartoon">none</content_attribute>

View File

@ -38,31 +38,7 @@ bool FeedDownloader::isUpdateRunning() const {
return !m_feeds.isEmpty(); return !m_feeds.isEmpty();
} }
void FeedDownloader::updateAvailableFeeds() { void FeedDownloader::synchronizeAccountCaches(const QList<CacheForServiceRoot*>& caches, bool emit_signals) {
for (const Feed* feed : qAsConst(m_feeds)) {
auto* cache = dynamic_cast<CacheForServiceRoot*>(feed->getParentServiceRoot());
if (cache != nullptr) {
qDebugNN << LOGSEC_FEEDDOWNLOADER
<< "Saving cache for feed with DB ID '" << feed->id()
<< "' and title '" << feed->title() << "'.";
cache->saveAllCachedData(false);
}
if (m_stopCacheSynchronization) {
qWarningNN << LOGSEC_FEEDDOWNLOADER << "Aborting cache synchronization.";
m_stopCacheSynchronization = false;
break;
}
}
while (!m_feeds.isEmpty()) {
updateOneFeed(m_feeds.takeFirst());
}
}
void FeedDownloader::synchronizeAccountCaches(const QList<CacheForServiceRoot*>& caches) {
m_isCacheSynchronizationRunning = true; m_isCacheSynchronizationRunning = true;
for (CacheForServiceRoot* cache : caches) { for (CacheForServiceRoot* cache : caches) {
@ -80,7 +56,10 @@ void FeedDownloader::synchronizeAccountCaches(const QList<CacheForServiceRoot*>&
m_isCacheSynchronizationRunning = false; m_isCacheSynchronizationRunning = false;
qDebugNN << LOGSEC_FEEDDOWNLOADER << "All caches synchronized."; qDebugNN << LOGSEC_FEEDDOWNLOADER << "All caches synchronized.";
emit cachesSynchronized();
if (emit_signals) {
emit cachesSynchronized();
}
} }
void FeedDownloader::updateFeeds(const QList<Feed*>& feeds) { void FeedDownloader::updateFeeds(const QList<Feed*>& feeds) {
@ -100,8 +79,81 @@ void FeedDownloader::updateFeeds(const QList<Feed*>& feeds) {
// Job starts now. // Job starts now.
emit updateStarted(); emit updateStarted();
QSet<CacheForServiceRoot*> caches;
QMultiHash<ServiceRoot*, Feed*> feeds_per_root;
updateAvailableFeeds(); // 1. key - account.
// 2. key - feed custom ID.
// 3. key - msg state.
QHash<ServiceRoot*, QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>> stated_messages;
// 1. key - account.
// 2. key - label custom ID.
QHash<ServiceRoot*, QHash<QString, QStringList>> tagged_messages;
for (auto* fd : feeds) {
CacheForServiceRoot* fd_cache = fd->getParentServiceRoot()->toCache();
if (fd_cache != nullptr) {
caches.insert(fd_cache);
}
feeds_per_root.insert(fd->getParentServiceRoot(), fd);
}
synchronizeAccountCaches(QList<CacheForServiceRoot*>(caches.begin(), caches.end()), false);
auto roots = feeds_per_root.uniqueKeys();
bool is_main_thread = QThread::currentThread() == qApp->thread();
QSqlDatabase database = is_main_thread ?
qApp->database()->driver()->connection(metaObject()->className()) :
qApp->database()->driver()->connection(QSL("feed_upd"));
for (auto* rt : roots) {
// Obtain lists of local IDs.
if (rt->wantsBaggedIdsOfExistingMessages()) {
// Tagged messages for the account.
tagged_messages.insert(rt, DatabaseQueries::bagsOfMessages(database, rt->labelsNode()->labels()));
QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>> per_acc_states;
// This account has activated intelligent downloading of messages.
// Prepare bags.
auto fds = feeds_per_root.values(rt);
for (Feed* fd : fds) {
QHash<ServiceRoot::BagOfMessages, QStringList> per_feed_states;
per_feed_states.insert(ServiceRoot::BagOfMessages::Read,
DatabaseQueries::bagOfMessages(database,
ServiceRoot::BagOfMessages::Read,
fd));
per_feed_states.insert(ServiceRoot::BagOfMessages::Unread,
DatabaseQueries::bagOfMessages(database,
ServiceRoot::BagOfMessages::Unread,
fd));
per_feed_states.insert(ServiceRoot::BagOfMessages::Starred,
DatabaseQueries::bagOfMessages(database,
ServiceRoot::BagOfMessages::Starred,
fd));
per_acc_states.insert(fd->customId(), per_feed_states);
}
stated_messages.insert(rt, per_acc_states);
}
rt->aboutToBeginFeedFetching(feeds_per_root.values(rt),
stated_messages.value(rt),
tagged_messages.value(rt));
}
while (!m_feeds.isEmpty()) {
auto n_f = m_feeds.takeFirst();
updateOneFeed(n_f,
stated_messages.value(n_f->getParentServiceRoot()).value(n_f->customId()),
tagged_messages.value(n_f->getParentServiceRoot()));
}
} }
finalizeUpdate(); finalizeUpdate();
@ -113,7 +165,9 @@ void FeedDownloader::stopRunningUpdate() {
m_feedsOriginalCount = m_feedsUpdated = 0; m_feedsOriginalCount = m_feedsUpdated = 0;
} }
void FeedDownloader::updateOneFeed(Feed* feed) { void FeedDownloader::updateOneFeed(Feed* feed,
const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages) {
qDebugNN << LOGSEC_FEEDDOWNLOADER qDebugNN << LOGSEC_FEEDDOWNLOADER
<< "Downloading new messages for feed ID '" << "Downloading new messages for feed ID '"
<< feed->customId() << "' URL: '" << feed->source() << "' title: '" << feed->title() << "' in thread: '" << feed->customId() << "' URL: '" << feed->source() << "' title: '" << feed->title() << "' in thread: '"
@ -127,33 +181,7 @@ void FeedDownloader::updateOneFeed(Feed* feed) {
QSqlDatabase database = is_main_thread ? QSqlDatabase database = is_main_thread ?
qApp->database()->driver()->connection(metaObject()->className()) : qApp->database()->driver()->connection(metaObject()->className()) :
qApp->database()->driver()->connection(QSL("feed_upd")); qApp->database()->driver()->connection(QSL("feed_upd"));
QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>> stated_messages; QList<Message> msgs = feed->getParentServiceRoot()->obtainNewMessages(feed,
QHash<QString, QStringList> tagged_messages;
if (feed->getParentServiceRoot()->wantsBaggedIdsOfExistingMessages()) {
// This account has activated intelligent downloading of messages.
// Prepare bags.
QHash<ServiceRoot::BagOfMessages, QStringList> per_feed_states;
per_feed_states.insert(ServiceRoot::BagOfMessages::Read,
DatabaseQueries::bagOfMessages(database,
ServiceRoot::BagOfMessages::Read,
feed));
per_feed_states.insert(ServiceRoot::BagOfMessages::Unread,
DatabaseQueries::bagOfMessages(database,
ServiceRoot::BagOfMessages::Unread,
feed));
per_feed_states.insert(ServiceRoot::BagOfMessages::Starred,
DatabaseQueries::bagOfMessages(database,
ServiceRoot::BagOfMessages::Starred,
feed));
stated_messages.insert(feed->customId(), per_feed_states);
tagged_messages = DatabaseQueries::bagsOfMessages(database,
feed->getParentServiceRoot()->labelsNode()->labels());
}
QList<Message> msgs = feed->getParentServiceRoot()->obtainNewMessages({ feed },
stated_messages, stated_messages,
tagged_messages); tagged_messages);
@ -343,7 +371,6 @@ void FeedDownloader::updateOneFeed(Feed* feed) {
<< QUOTE_W_SPACE_DOT(feed_ex.message()); << QUOTE_W_SPACE_DOT(feed_ex.message());
feed->setStatus(feed_ex.feedStatus(), feed_ex.message()); feed->setStatus(feed_ex.feedStatus(), feed_ex.message());
feed->getParentServiceRoot()->itemChanged({ feed });
} }
catch (const ApplicationException& app_ex) { catch (const ApplicationException& app_ex) {
@ -353,9 +380,10 @@ void FeedDownloader::updateOneFeed(Feed* feed) {
<< QUOTE_W_SPACE_DOT(app_ex.message()); << QUOTE_W_SPACE_DOT(app_ex.message());
feed->setStatus(Feed::Status::OtherError, app_ex.message()); feed->setStatus(Feed::Status::OtherError, app_ex.message());
feed->getParentServiceRoot()->itemChanged({ feed });
} }
feed->getParentServiceRoot()->itemChanged({ feed });
m_feedsUpdated++; m_feedsUpdated++;
qDebugNN << LOGSEC_FEEDDOWNLOADER qDebugNN << LOGSEC_FEEDDOWNLOADER

View File

@ -43,7 +43,7 @@ class FeedDownloader : public QObject {
bool isCacheSynchronizationRunning() const; bool isCacheSynchronizationRunning() const;
public slots: public slots:
void synchronizeAccountCaches(const QList<CacheForServiceRoot*>& caches); void synchronizeAccountCaches(const QList<CacheForServiceRoot*>& caches, bool emit_signals);
void updateFeeds(const QList<Feed*>& feeds); void updateFeeds(const QList<Feed*>& feeds);
void stopRunningUpdate(); void stopRunningUpdate();
@ -54,8 +54,9 @@ class FeedDownloader : public QObject {
void updateProgress(const Feed* feed, int current, int total); void updateProgress(const Feed* feed, int current, int total);
private: private:
void updateOneFeed(Feed* feed); void updateOneFeed(Feed* feed,
void updateAvailableFeeds(); const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages);
void finalizeUpdate(); void finalizeUpdate();
bool m_isCacheSynchronizationRunning; bool m_isCacheSynchronizationRunning;

View File

@ -88,7 +88,8 @@ void FeedReader::updateFeeds(const QList<Feed*>& feeds) {
void FeedReader::synchronizeMessageData(const QList<CacheForServiceRoot*>& caches) { void FeedReader::synchronizeMessageData(const QList<CacheForServiceRoot*>& caches) {
QMetaObject::invokeMethod(m_feedDownloader, "synchronizeAccountCaches", QMetaObject::invokeMethod(m_feedDownloader, "synchronizeAccountCaches",
Qt::ConnectionType::QueuedConnection, Qt::ConnectionType::QueuedConnection,
Q_ARG(QList<CacheForServiceRoot*>, caches)); Q_ARG(QList<CacheForServiceRoot*>, caches),
Q_ARG(bool, true));
} }
void FeedReader::initializeFeedDownloader() { void FeedReader::initializeFeedDownloader() {

View File

@ -315,6 +315,14 @@ bool ServiceRoot::wantsBaggedIdsOfExistingMessages() const {
return false; return false;
} }
void ServiceRoot::aboutToBeginFeedFetching(const QList<Feed*>& feeds,
const QHash<QString, QHash<BagOfMessages, QStringList>>& stated_msgs,
const QHash<QString, QStringList>& tagged_msgs) {
Q_UNUSED(feeds)
Q_UNUSED(stated_msgs)
Q_UNUSED(tagged_msgs)
}
void ServiceRoot::itemChanged(const QList<RootItem*>& items) { void ServiceRoot::itemChanged(const QList<RootItem*>& items) {
emit dataChanged(items); emit dataChanged(items);
} }

View File

@ -65,6 +65,9 @@ class ServiceRoot : public RootItem {
virtual QVariantHash customDatabaseData() const; virtual QVariantHash customDatabaseData() const;
virtual void setCustomDatabaseData(const QVariantHash& data); virtual void setCustomDatabaseData(const QVariantHash& data);
virtual bool wantsBaggedIdsOfExistingMessages() const; virtual bool wantsBaggedIdsOfExistingMessages() const;
virtual void aboutToBeginFeedFetching(const QList<Feed*>& feeds,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& stated_msgs,
const QHash<QString, QStringList>& tagged_msgs);
// Returns list of specific actions for "Add new item" main window menu. // Returns list of specific actions for "Add new item" main window menu.
// So typical list of returned actions could look like: // So typical list of returned actions could look like:
@ -101,8 +104,8 @@ class ServiceRoot : public RootItem {
// Obtains list of messages. // Obtains list of messages.
// Throws exception subclassed from ApplicationException, preferably FeedFetchException // Throws exception subclassed from ApplicationException, preferably FeedFetchException
// if any problems arise. // if any problems arise.
virtual QList<Message> obtainNewMessages(const QList<Feed*>& feeds, virtual QList<Message> obtainNewMessages(Feed* feed,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& stated_messages, const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages) = 0; const QHash<QString, QStringList>& tagged_messages) = 0;
// This method should prepare messages for given "item" (download them maybe?) // This method should prepare messages for given "item" (download them maybe?)

View File

@ -72,24 +72,18 @@ void FeedlyServiceRoot::setCustomDatabaseData(const QVariantHash& data) {
m_network->setDownloadOnlyUnreadMessages(data["download_only_unread"].toBool()); m_network->setDownloadOnlyUnreadMessages(data["download_only_unread"].toBool());
} }
QList<Message> FeedlyServiceRoot::obtainNewMessages(const QList<Feed*>& feeds, QList<Message> FeedlyServiceRoot::obtainNewMessages(Feed* feed,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& stated_messages, const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages) { const QHash<QString, QStringList>& tagged_messages) {
Q_UNUSED(stated_messages) Q_UNUSED(stated_messages)
Q_UNUSED(tagged_messages) Q_UNUSED(tagged_messages)
QList<Message> messages; try {
return m_network->streamContents(feed->customId());
for (Feed* feed : feeds) { }
try { catch (const ApplicationException& ex) {
messages << m_network->streamContents(feed->customId()); throw FeedFetchException(Feed::Status::NetworkError, ex.message());
}
catch (const ApplicationException& ex) {
throw FeedFetchException(Feed::Status::NetworkError, ex.message());
}
} }
return messages;
} }
void FeedlyServiceRoot::start(bool freshly_activated) { void FeedlyServiceRoot::start(bool freshly_activated) {

View File

@ -23,8 +23,8 @@ class FeedlyServiceRoot : public ServiceRoot, public CacheForServiceRoot {
virtual LabelOperation supportedLabelOperations() const; virtual LabelOperation supportedLabelOperations() const;
virtual QVariantHash customDatabaseData() const; virtual QVariantHash customDatabaseData() const;
virtual void setCustomDatabaseData(const QVariantHash& data); virtual void setCustomDatabaseData(const QVariantHash& data);
virtual QList<Message> obtainNewMessages(const QList<Feed*>& feeds, virtual QList<Message> obtainNewMessages(Feed* feed,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& stated_messages, const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages); const QHash<QString, QStringList>& tagged_messages);
FeedlyNetwork* network() const; FeedlyNetwork* network() const;

View File

@ -74,22 +74,17 @@ void GmailServiceRoot::setCustomDatabaseData(const QVariantHash& data) {
m_network->oauth()->setRedirectUrl(data["redirect_uri"].toString()); m_network->oauth()->setRedirectUrl(data["redirect_uri"].toString());
} }
QList<Message> GmailServiceRoot::obtainNewMessages(const QList<Feed*>& feeds, QList<Message> GmailServiceRoot::obtainNewMessages(Feed* feed,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& stated_messages, const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages) { const QHash<QString, QStringList>& tagged_messages) {
Q_UNUSED(stated_messages) Q_UNUSED(stated_messages)
Q_UNUSED(tagged_messages) Q_UNUSED(tagged_messages)
QList<Message> messages; Feed::Status error = Feed::Status::Normal;
QList<Message> messages = network()->messages(feed->customId(), error, networkProxy());
for (Feed* feed : feeds) { if (error != Feed::Status::NewMessages && error != Feed::Status::Normal) {
Feed::Status error = Feed::Status::Normal; throw FeedFetchException(error);
messages << network()->messages(feed->customId(), error, networkProxy());
if (error == Feed::Status::NetworkError || error == Feed::Status::AuthError || error == Feed::Status::ParsingError) {
throw FeedFetchException(error);
}
} }
return messages; return messages;

View File

@ -31,8 +31,8 @@ class GmailServiceRoot : public ServiceRoot, public CacheForServiceRoot {
virtual void saveAllCachedData(bool ignore_errors); virtual void saveAllCachedData(bool ignore_errors);
virtual QVariantHash customDatabaseData() const; virtual QVariantHash customDatabaseData() const;
virtual void setCustomDatabaseData(const QVariantHash& data); virtual void setCustomDatabaseData(const QVariantHash& data);
virtual QList<Message> obtainNewMessages(const QList<Feed*>& feeds, virtual QList<Message> obtainNewMessages(Feed* feed,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& stated_messages, const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages); const QHash<QString, QStringList>& tagged_messages);
protected: protected:

View File

@ -57,35 +57,29 @@ void GreaderServiceRoot::setCustomDatabaseData(const QVariantHash& data) {
m_network->setDownloadOnlyUnreadMessages(data["download_only_unread"].toBool()); m_network->setDownloadOnlyUnreadMessages(data["download_only_unread"].toBool());
} }
QList<Message> GreaderServiceRoot::obtainNewMessages(const QList<Feed*>& feeds, QList<Message> GreaderServiceRoot::obtainNewMessages(Feed* feed,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& stated_messages, const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages) { const QHash<QString, QStringList>& tagged_messages) {
Q_UNUSED(stated_messages) Q_UNUSED(stated_messages)
Q_UNUSED(tagged_messages) Q_UNUSED(tagged_messages)
QList<Message> messages; Feed::Status error = Feed::Status::Normal;
for (Feed* feed : feeds) { if (true /* intelligent downloading */ ) {
Feed::Status error = Feed::Status::Normal; return network()->getMessagesIntelligently(this,
feed->customId(),
if (true /* intelligent downloading */ ) { stated_messages,
messages << network()->getMessagesIntelligently(this, tagged_messages,
feed->customId(), error,
stated_messages.value(feed->customId()), networkProxy());
tagged_messages, }
error, else {
networkProxy()); return network()->streamContents(this, feed->customId(), error, networkProxy());
}
else {
messages << network()->streamContents(this, feed->customId(), error, networkProxy());
}
if (error == Feed::Status::NetworkError || error == Feed::Status::AuthError) {
throw FeedFetchException(error);
}
} }
return messages; if (error != Feed::Status::NewMessages && error != Feed::Status::Normal) {
throw FeedFetchException(error);
}
} }
bool GreaderServiceRoot::wantsBaggedIdsOfExistingMessages() const { bool GreaderServiceRoot::wantsBaggedIdsOfExistingMessages() const {

View File

@ -31,8 +31,8 @@ class GreaderServiceRoot : public ServiceRoot, public CacheForServiceRoot {
virtual LabelOperation supportedLabelOperations() const; virtual LabelOperation supportedLabelOperations() const;
virtual QVariantHash customDatabaseData() const; virtual QVariantHash customDatabaseData() const;
virtual void setCustomDatabaseData(const QVariantHash& data); virtual void setCustomDatabaseData(const QVariantHash& data);
virtual QList<Message> obtainNewMessages(const QList<Feed*>& feeds, virtual QList<Message> obtainNewMessages(Feed* feed,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& stated_messages, const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages); const QHash<QString, QStringList>& tagged_messages);
virtual bool wantsBaggedIdsOfExistingMessages() const; virtual bool wantsBaggedIdsOfExistingMessages() const;

View File

@ -56,22 +56,17 @@ void InoreaderServiceRoot::setCustomDatabaseData(const QVariantHash& data) {
m_network->oauth()->setRedirectUrl(data["redirect_uri"].toString()); m_network->oauth()->setRedirectUrl(data["redirect_uri"].toString());
} }
QList<Message> InoreaderServiceRoot::obtainNewMessages(const QList<Feed*>& feeds, QList<Message> InoreaderServiceRoot::obtainNewMessages(Feed* feed,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& stated_messages, const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages) { const QHash<QString, QStringList>& tagged_messages) {
Q_UNUSED(stated_messages) Q_UNUSED(stated_messages)
Q_UNUSED(tagged_messages) Q_UNUSED(tagged_messages)
QList<Message> messages; Feed::Status error = Feed::Status::Normal;
QList<Message> messages = network()->messages(this, feed->customId(), error);
for (Feed* feed : feeds) { if (error != Feed::Status::NewMessages && error != Feed::Status::Normal) {
Feed::Status error = Feed::Status::Normal; throw FeedFetchException(error);
messages << network()->messages(this, feed->customId(), error);
if (error == Feed::Status::NetworkError || error == Feed::Status::AuthError) {
throw FeedFetchException(error);
}
} }
return messages; return messages;

View File

@ -29,8 +29,8 @@ class InoreaderServiceRoot : public ServiceRoot, public CacheForServiceRoot {
virtual void saveAllCachedData(bool ignore_errors); virtual void saveAllCachedData(bool ignore_errors);
virtual QVariantHash customDatabaseData() const; virtual QVariantHash customDatabaseData() const;
virtual void setCustomDatabaseData(const QVariantHash& data); virtual void setCustomDatabaseData(const QVariantHash& data);
virtual QList<Message> obtainNewMessages(const QList<Feed*>& feeds, virtual QList<Message> obtainNewMessages(Feed* feed,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& stated_messages, const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages); const QHash<QString, QStringList>& tagged_messages);
protected: protected:

View File

@ -150,24 +150,18 @@ void OwnCloudServiceRoot::setCustomDatabaseData(const QVariantHash& data) {
m_network->setDownloadOnlyUnreadMessages(data["download_only_unread"].toBool()); m_network->setDownloadOnlyUnreadMessages(data["download_only_unread"].toBool());
} }
QList<Message> OwnCloudServiceRoot::obtainNewMessages(const QList<Feed*>& feeds, QList<Message> OwnCloudServiceRoot::obtainNewMessages(Feed* feed,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& stated_messages, const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages) { const QHash<QString, QStringList>& tagged_messages) {
Q_UNUSED(stated_messages) Q_UNUSED(stated_messages)
Q_UNUSED(tagged_messages) Q_UNUSED(tagged_messages)
QList<Message> msgs; OwnCloudGetMessagesResponse messages = network()->getMessages(feed->customNumericId(), networkProxy());
for (Feed* feed : feeds) { if (messages.networkError() != QNetworkReply::NetworkError::NoError) {
OwnCloudGetMessagesResponse messages = network()->getMessages(feed->customNumericId(), networkProxy()); throw FeedFetchException(Feed::Status::NetworkError);
}
if (messages.networkError() != QNetworkReply::NetworkError::NoError) { else {
throw FeedFetchException(Feed::Status::NetworkError); return messages.messages();
}
else {
msgs << messages.messages();
}
} }
return msgs;
} }

View File

@ -28,8 +28,8 @@ class OwnCloudServiceRoot : public ServiceRoot, public CacheForServiceRoot {
virtual void saveAllCachedData(bool ignore_errors); virtual void saveAllCachedData(bool ignore_errors);
virtual QVariantHash customDatabaseData() const; virtual QVariantHash customDatabaseData() const;
virtual void setCustomDatabaseData(const QVariantHash& data); virtual void setCustomDatabaseData(const QVariantHash& data);
virtual QList<Message> obtainNewMessages(const QList<Feed*>& feeds, virtual QList<Message> obtainNewMessages(Feed* feed,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& stated_messages, const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages); const QHash<QString, QStringList>& tagged_messages);
OwnCloudNetworkFactory* network() const; OwnCloudNetworkFactory* network() const;

View File

@ -144,134 +144,128 @@ Qt::ItemFlags StandardServiceRoot::additionalFlags() const {
return Qt::ItemFlag::ItemIsDropEnabled; return Qt::ItemFlag::ItemIsDropEnabled;
} }
QList<Message> StandardServiceRoot::obtainNewMessages(const QList<Feed*>& feeds, QList<Message> StandardServiceRoot::obtainNewMessages(Feed* feed,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& stated_messages, const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages) { const QHash<QString, QStringList>& tagged_messages) {
Q_UNUSED(stated_messages) Q_UNUSED(stated_messages)
Q_UNUSED(tagged_messages) Q_UNUSED(tagged_messages)
QList<Message> msgs; StandardFeed* f = static_cast<StandardFeed*>(feed);
QString formatted_feed_contents;
int download_timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
for (Feed* f : feeds) { if (f->sourceType() == StandardFeed::SourceType::Url) {
StandardFeed* feed = static_cast<StandardFeed*>(f); qDebugNN << LOGSEC_CORE
QString formatted_feed_contents; << "Downloading URL"
int download_timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); << QUOTE_W_SPACE(feed->source())
<< "to obtain feed data.";
if (feed->sourceType() == StandardFeed::SourceType::Url) { QByteArray feed_contents;
qDebugNN << LOGSEC_CORE QList<QPair<QByteArray, QByteArray>> headers;
<< "Downloading URL"
<< QUOTE_W_SPACE(feed->source())
<< "to obtain feed data.";
QByteArray feed_contents; headers << NetworkFactory::generateBasicAuthHeader(f->username(), f->password());
QList<QPair<QByteArray, QByteArray>> headers;
headers << NetworkFactory::generateBasicAuthHeader(feed->username(), feed->password()); auto network_result = NetworkFactory::performNetworkOperation(feed->source(),
download_timeout,
{},
feed_contents,
QNetworkAccessManager::Operation::GetOperation,
headers,
false,
{},
{},
networkProxy()).first;
auto network_result = NetworkFactory::performNetworkOperation(feed->source(), if (network_result != QNetworkReply::NetworkError::NoError) {
download_timeout, qWarningNN << LOGSEC_CORE
{}, << "Error"
feed_contents, << QUOTE_W_SPACE(network_result)
QNetworkAccessManager::Operation::GetOperation, << "during fetching of new messages for feed"
headers, << QUOTE_W_SPACE_DOT(feed->source());
false, throw FeedFetchException(Feed::Status::NetworkError, NetworkFactory::networkErrorText(network_result));
{}, }
{},
networkProxy()).first;
if (network_result != QNetworkReply::NetworkError::NoError) { // Encode downloaded data for further parsing.
qWarningNN << LOGSEC_CORE QTextCodec* codec = QTextCodec::codecForName(f->encoding().toLocal8Bit());
<< "Error"
<< QUOTE_W_SPACE(network_result)
<< "during fetching of new messages for feed"
<< QUOTE_W_SPACE_DOT(feed->source());
throw FeedFetchException(Feed::Status::NetworkError, NetworkFactory::networkErrorText(network_result));
}
// Encode downloaded data for further parsing. if (codec == nullptr) {
QTextCodec* codec = QTextCodec::codecForName(feed->encoding().toLocal8Bit()); // No suitable codec for this encoding was found.
// Use non-converted data.
if (codec == nullptr) { formatted_feed_contents = feed_contents;
// No suitable codec for this encoding was found.
// Use non-converted data.
formatted_feed_contents = feed_contents;
}
else {
formatted_feed_contents = codec->toUnicode(feed_contents);
}
} }
else { else {
qDebugNN << LOGSEC_CORE formatted_feed_contents = codec->toUnicode(feed_contents);
<< "Running custom script"
<< QUOTE_W_SPACE(feed->source())
<< "to obtain feed data.";
// Use script to generate feed file.
try {
formatted_feed_contents = StandardFeed::generateFeedFileWithScript(feed->source(), download_timeout);
}
catch (const ScriptException& ex) {
qCriticalNN << LOGSEC_CORE
<< "Custom script for generating feed file failed:"
<< QUOTE_W_SPACE_DOT(ex.message());
throw FeedFetchException(Feed::Status::OtherError, ex.message());
}
} }
}
else {
qDebugNN << LOGSEC_CORE
<< "Running custom script"
<< QUOTE_W_SPACE(feed->source())
<< "to obtain feed data.";
if (!feed->postProcessScript().simplified().isEmpty()) { // Use script to generate feed file.
qDebugNN << LOGSEC_CORE try {
<< "We will process feed data with post-process script" formatted_feed_contents = StandardFeed::generateFeedFileWithScript(feed->source(), download_timeout);
<< QUOTE_W_SPACE_DOT(feed->postProcessScript());
try {
formatted_feed_contents = StandardFeed::postProcessFeedFileWithScript(feed->postProcessScript(),
formatted_feed_contents,
download_timeout);
}
catch (const ScriptException& ex) {
qCriticalNN << LOGSEC_CORE
<< "Post-processing script for feed file failed:"
<< QUOTE_W_SPACE_DOT(ex.message());
throw FeedFetchException(Feed::Status::OtherError, ex.message());
}
} }
catch (const ScriptException& ex) {
qCriticalNN << LOGSEC_CORE
<< "Custom script for generating feed file failed:"
<< QUOTE_W_SPACE_DOT(ex.message());
// Feed data are downloaded and encoded. throw FeedFetchException(Feed::Status::OtherError, ex.message());
// Parse data and obtain messages.
QList<Message> messages;
switch (feed->type()) {
case StandardFeed::Type::Rss0X:
case StandardFeed::Type::Rss2X:
messages = RssParser(formatted_feed_contents).messages();
break;
case StandardFeed::Type::Rdf:
messages = RdfParser(formatted_feed_contents).messages();
break;
case StandardFeed::Type::Atom10:
messages = AtomParser(formatted_feed_contents).messages();
break;
case StandardFeed::Type::Json:
messages = JsonParser(formatted_feed_contents).messages();
break;
default:
break;
} }
for (Message& mess : messages) {
mess.m_feedId = f->customId();
}
msgs << messages;
} }
return msgs; if (!f->postProcessScript().simplified().isEmpty()) {
qDebugNN << LOGSEC_CORE
<< "We will process feed data with post-process script"
<< QUOTE_W_SPACE_DOT(f->postProcessScript());
try {
formatted_feed_contents = StandardFeed::postProcessFeedFileWithScript(f->postProcessScript(),
formatted_feed_contents,
download_timeout);
}
catch (const ScriptException& ex) {
qCriticalNN << LOGSEC_CORE
<< "Post-processing script for feed file failed:"
<< QUOTE_W_SPACE_DOT(ex.message());
throw FeedFetchException(Feed::Status::OtherError, ex.message());
}
}
// Feed data are downloaded and encoded.
// Parse data and obtain messages.
QList<Message> messages;
switch (f->type()) {
case StandardFeed::Type::Rss0X:
case StandardFeed::Type::Rss2X:
messages = RssParser(formatted_feed_contents).messages();
break;
case StandardFeed::Type::Rdf:
messages = RdfParser(formatted_feed_contents).messages();
break;
case StandardFeed::Type::Atom10:
messages = AtomParser(formatted_feed_contents).messages();
break;
case StandardFeed::Type::Json:
messages = JsonParser(formatted_feed_contents).messages();
break;
default:
break;
}
for (Message& mess : messages) {
mess.m_feedId = feed->customId();
}
return messages;
} }
QList<QAction*> StandardServiceRoot::getContextMenuForFeed(StandardFeed* feed) { QList<QAction*> StandardServiceRoot::getContextMenuForFeed(StandardFeed* feed) {

View File

@ -32,8 +32,8 @@ class StandardServiceRoot : public ServiceRoot {
virtual bool supportsFeedAdding() const; virtual bool supportsFeedAdding() const;
virtual bool supportsCategoryAdding() const; virtual bool supportsCategoryAdding() const;
virtual Qt::ItemFlags additionalFlags() const; virtual Qt::ItemFlags additionalFlags() const;
virtual QList<Message> obtainNewMessages(const QList<Feed*>& feeds, virtual QList<Message> obtainNewMessages(Feed* feed,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& stated_messages, const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages); const QHash<QString, QStringList>& tagged_messages);
QList<QAction*> serviceMenu(); QList<QAction*> serviceMenu();

View File

@ -214,38 +214,35 @@ void TtRssServiceRoot::setCustomDatabaseData(const QVariantHash& data) {
m_network->setDownloadOnlyUnreadMessages(data["download_only_unread"].toBool()); m_network->setDownloadOnlyUnreadMessages(data["download_only_unread"].toBool());
} }
QList<Message> TtRssServiceRoot::obtainNewMessages(const QList<Feed*>& feeds, QList<Message> TtRssServiceRoot::obtainNewMessages(Feed* feed,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& stated_messages, const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages) { const QHash<QString, QStringList>& tagged_messages) {
Q_UNUSED(stated_messages) Q_UNUSED(stated_messages)
Q_UNUSED(tagged_messages) Q_UNUSED(tagged_messages)
QList<Message> messages; QList<Message> messages;
int newly_added_messages = 0;
int limit = network()->batchSize() <= 0 ? TTRSS_MAX_MESSAGES : network()->batchSize();
int skip = 0;
for (Feed* feed : feeds) { do {
int newly_added_messages = 0; TtRssGetHeadlinesResponse headlines = network()->getHeadlines(feed->customNumericId(), limit, skip,
int limit = network()->batchSize() <= 0 ? TTRSS_MAX_MESSAGES : network()->batchSize(); true, true, false,
int skip = 0; network()->downloadOnlyUnreadMessages(),
networkProxy());
do { if (network()->lastError() != QNetworkReply::NetworkError::NoError) {
TtRssGetHeadlinesResponse headlines = network()->getHeadlines(feed->customNumericId(), limit, skip, throw FeedFetchException(Feed::Status::NetworkError, headlines.error());
true, true, false, }
network()->downloadOnlyUnreadMessages(), else {
networkProxy()); QList<Message> new_messages = headlines.messages(this);
if (network()->lastError() != QNetworkReply::NetworkError::NoError) { messages << new_messages;
throw FeedFetchException(Feed::Status::NetworkError, headlines.error()); newly_added_messages = new_messages.size();
} skip += newly_added_messages;
else {
QList<Message> new_messages = headlines.messages(this);
messages << new_messages;
newly_added_messages = new_messages.size();
skip += newly_added_messages;
}
} }
while (newly_added_messages > 0 && (network()->batchSize() <= 0 || messages.size() < network()->batchSize()));
} }
while (newly_added_messages > 0 && (network()->batchSize() <= 0 || messages.size() < network()->batchSize()));
return messages; return messages;
} }

View File

@ -33,8 +33,8 @@ class TtRssServiceRoot : public ServiceRoot, public CacheForServiceRoot {
virtual void saveAllCachedData(bool ignore_errors); virtual void saveAllCachedData(bool ignore_errors);
virtual QVariantHash customDatabaseData() const; virtual QVariantHash customDatabaseData() const;
virtual void setCustomDatabaseData(const QVariantHash& data); virtual void setCustomDatabaseData(const QVariantHash& data);
virtual QList<Message> obtainNewMessages(const QList<Feed*>& feeds, virtual QList<Message> obtainNewMessages(Feed* feed,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& stated_messages, const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages); const QHash<QString, QStringList>& tagged_messages);
// Access to network. // Access to network.