freshrss experimental working
This commit is contained in:
parent
8cc268ca9a
commit
6dc5e016f1
5 changed files with 164 additions and 33 deletions
|
@ -8,11 +8,19 @@
|
||||||
#define GREADER_API_STATE_READ "state/com.google/read"
|
#define GREADER_API_STATE_READ "state/com.google/read"
|
||||||
#define GREADER_API_STATE_IMPORTANT "state/com.google/starred"
|
#define GREADER_API_STATE_IMPORTANT "state/com.google/starred"
|
||||||
|
|
||||||
|
#define GREADER_API_FULL_STATE_READING_LIST "user/-/state/com.google/reading-list"
|
||||||
|
#define GREADER_API_FULL_STATE_READ "user/-/state/com.google/read"
|
||||||
|
#define GREADER_API_FULL_STATE_IMPORTANT "user/-/state/com.google/starred"
|
||||||
|
|
||||||
// API.
|
// API.
|
||||||
#define GREADER_API_CLIENT_LOGIN "accounts/ClientLogin?Email=%1&Passwd=%2"
|
#define GREADER_API_CLIENT_LOGIN "accounts/ClientLogin?Email=%1&Passwd=%2"
|
||||||
#define GREADER_API_TAG_LIST "reader/api/0/tag/list?output=json"
|
#define GREADER_API_TAG_LIST "reader/api/0/tag/list?output=json"
|
||||||
#define GREADER_API_SUBSCRIPTION_LIST "reader/api/0/subscription/list?output=json"
|
#define GREADER_API_SUBSCRIPTION_LIST "reader/api/0/subscription/list?output=json"
|
||||||
#define GREADER_API_STREAM_CONTENTS "reader/api/0/stream/contents/%1?output=json&n=%2"
|
#define GREADER_API_STREAM_CONTENTS "reader/api/0/stream/contents/%1?output=json&n=%2"
|
||||||
|
#define GREADER_API_EDIT_TAG "reader/api/0/edit-tag"
|
||||||
|
|
||||||
|
// Misc.
|
||||||
|
#define GREADER_API_EDIT_TAG_BATCH 200
|
||||||
|
|
||||||
// FreshRSS.
|
// FreshRSS.
|
||||||
#define FRESHRSS_BASE_URL_PATH "api/greader.php/"
|
#define FRESHRSS_BASE_URL_PATH "api/greader.php/"
|
||||||
|
|
|
@ -22,6 +22,84 @@ GreaderNetwork::GreaderNetwork(QObject* parent)
|
||||||
clearCredentials();
|
clearCredentials();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QNetworkReply::NetworkError GreaderNetwork::editLabels(const QString& state,
|
||||||
|
bool assign,
|
||||||
|
const QStringList& msg_custom_ids,
|
||||||
|
const QNetworkProxy& proxy) {
|
||||||
|
QString full_url = generateFullUrl(Operations::EditTag);
|
||||||
|
int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
|
||||||
|
|
||||||
|
QNetworkReply::NetworkError network_err;
|
||||||
|
|
||||||
|
if (!ensureLogin(proxy, &network_err)) {
|
||||||
|
return network_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList trimmed_ids;
|
||||||
|
QRegularExpression regex_short_id(QSL("[0-9a-zA-Z]+$"));
|
||||||
|
|
||||||
|
for (const QString& id : msg_custom_ids) {
|
||||||
|
trimmed_ids.append(QString("i=") + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList working_subset; working_subset.reserve(std::min(GREADER_API_EDIT_TAG_BATCH, trimmed_ids.size()));
|
||||||
|
|
||||||
|
// Now, we perform messages update in batches (max X messages per batch).
|
||||||
|
while (!trimmed_ids.isEmpty()) {
|
||||||
|
// We take X IDs.
|
||||||
|
for (int i = 0; i < GREADER_API_EDIT_TAG_BATCH && !trimmed_ids.isEmpty(); i++) {
|
||||||
|
working_subset.append(trimmed_ids.takeFirst());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString args;
|
||||||
|
|
||||||
|
if (assign) {
|
||||||
|
args = QString("a=") + state + "&";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
args = QString("r=") + state + "&";
|
||||||
|
}
|
||||||
|
|
||||||
|
args += working_subset.join(QL1C('&'));
|
||||||
|
|
||||||
|
// We send this batch.
|
||||||
|
QByteArray output;
|
||||||
|
auto result_edit = NetworkFactory::performNetworkOperation(full_url,
|
||||||
|
timeout,
|
||||||
|
args.toUtf8(),
|
||||||
|
output,
|
||||||
|
QNetworkAccessManager::Operation::PostOperation,
|
||||||
|
{ authHeader(),
|
||||||
|
{ QSL("Content-Type").toLocal8Bit(),
|
||||||
|
QSL("application/x-www-form-urlencoded").toLocal8Bit() } },
|
||||||
|
false,
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
proxy);
|
||||||
|
|
||||||
|
if (result_edit.first != QNetworkReply::NetworkError::NoError) {
|
||||||
|
return result_edit.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup for next batch.
|
||||||
|
working_subset.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return QNetworkReply::NetworkError::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
QNetworkReply::NetworkError GreaderNetwork::markMessagesRead(RootItem::ReadStatus status,
|
||||||
|
const QStringList& msg_custom_ids,
|
||||||
|
const QNetworkProxy& proxy) {
|
||||||
|
return editLabels(GREADER_API_FULL_STATE_READ, status == RootItem::ReadStatus::Read, msg_custom_ids, proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
QNetworkReply::NetworkError GreaderNetwork::markMessagesStarred(RootItem::Importance importance,
|
||||||
|
const QStringList& msg_custom_ids,
|
||||||
|
const QNetworkProxy& proxy) {
|
||||||
|
return editLabels(GREADER_API_FULL_STATE_IMPORTANT, importance == RootItem::Importance::Important, msg_custom_ids, proxy);
|
||||||
|
}
|
||||||
|
|
||||||
QList<Message> GreaderNetwork::streamContents(ServiceRoot* root, const QString& stream_id,
|
QList<Message> GreaderNetwork::streamContents(ServiceRoot* root, const QString& stream_id,
|
||||||
Feed::Status& error, const QNetworkProxy& proxy) {
|
Feed::Status& error, const QNetworkProxy& proxy) {
|
||||||
QString full_url = generateFullUrl(Operations::StreamContents).arg(stream_id,
|
QString full_url = generateFullUrl(Operations::StreamContents).arg(stream_id,
|
||||||
|
@ -307,10 +385,14 @@ QPair<QByteArray, QByteArray> GreaderNetwork::authHeader() const {
|
||||||
return { QSL("Authorization").toLocal8Bit(), QSL("GoogleLogin auth=%1").arg(m_authAuth).toLocal8Bit() };
|
return { QSL("Authorization").toLocal8Bit(), QSL("GoogleLogin auth=%1").arg(m_authAuth).toLocal8Bit() };
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GreaderNetwork::ensureLogin(const QNetworkProxy& proxy) {
|
bool GreaderNetwork::ensureLogin(const QNetworkProxy& proxy, QNetworkReply::NetworkError* output) {
|
||||||
if (m_authSid.isEmpty()) {
|
if (m_authSid.isEmpty()) {
|
||||||
auto login = clientLogin(proxy);
|
auto login = clientLogin(proxy);
|
||||||
|
|
||||||
|
if (output != nullptr) {
|
||||||
|
*output = login;
|
||||||
|
}
|
||||||
|
|
||||||
if (login != QNetworkReply::NetworkError::NoError) {
|
if (login != QNetworkReply::NetworkError::NoError) {
|
||||||
qCriticalNN << LOGSEC_GREADER
|
qCriticalNN << LOGSEC_GREADER
|
||||||
<< "Login failed with error:"
|
<< "Login failed with error:"
|
||||||
|
@ -440,5 +522,8 @@ QString GreaderNetwork::generateFullUrl(GreaderNetwork::Operations operation) co
|
||||||
|
|
||||||
case Operations::StreamContents:
|
case Operations::StreamContents:
|
||||||
return sanitizedBaseUrl() + GREADER_API_STREAM_CONTENTS;
|
return sanitizedBaseUrl() + GREADER_API_STREAM_CONTENTS;
|
||||||
|
|
||||||
|
case Operations::EditTag:
|
||||||
|
return sanitizedBaseUrl() + GREADER_API_EDIT_TAG;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,23 @@ class GreaderNetwork : public QObject {
|
||||||
ClientLogin,
|
ClientLogin,
|
||||||
TagList,
|
TagList,
|
||||||
SubscriptionList,
|
SubscriptionList,
|
||||||
StreamContents
|
StreamContents,
|
||||||
|
EditTag
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit GreaderNetwork(QObject* parent = nullptr);
|
explicit GreaderNetwork(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
QNetworkReply::NetworkError markMessagesRead(RootItem::ReadStatus status,
|
||||||
|
const QStringList& msg_custom_ids,
|
||||||
|
const QNetworkProxy& proxy);
|
||||||
|
QNetworkReply::NetworkError markMessagesStarred(RootItem::Importance importance,
|
||||||
|
const QStringList& msg_custom_ids,
|
||||||
|
const QNetworkProxy& proxy);
|
||||||
|
|
||||||
|
// Assign/deassign tags to/from message(s).
|
||||||
|
QNetworkReply::NetworkError editLabels(const QString& state, bool assign,
|
||||||
|
const QStringList& msg_custom_ids, const QNetworkProxy& proxy);
|
||||||
|
|
||||||
// Stream contents for a feed/label/etc.
|
// Stream contents for a feed/label/etc.
|
||||||
QList<Message> streamContents(ServiceRoot* root, const QString& stream_id,
|
QList<Message> streamContents(ServiceRoot* root, const QString& stream_id,
|
||||||
Feed::Status& error, const QNetworkProxy& proxy);
|
Feed::Status& error, const QNetworkProxy& proxy);
|
||||||
|
@ -56,7 +68,7 @@ class GreaderNetwork : public QObject {
|
||||||
QPair<QByteArray, QByteArray> authHeader() const;
|
QPair<QByteArray, QByteArray> authHeader() const;
|
||||||
|
|
||||||
// Make sure we are logged in and if we are not, return error.
|
// Make sure we are logged in and if we are not, return error.
|
||||||
bool ensureLogin(const QNetworkProxy& proxy);
|
bool ensureLogin(const QNetworkProxy& proxy, QNetworkReply::NetworkError* output = nullptr);
|
||||||
|
|
||||||
QList<Message> decodeStreamContents(ServiceRoot* root, const QString& stream_json_data, const QString& stream_id);
|
QList<Message> decodeStreamContents(ServiceRoot* root, const QString& stream_json_data, const QString& stream_id);
|
||||||
RootItem* decodeTagsSubscriptions(const QString& categories, const QString& feeds, bool obtain_icons);
|
RootItem* decodeTagsSubscriptions(const QString& categories, const QString& feeds, bool obtain_icons);
|
||||||
|
|
|
@ -61,48 +61,75 @@ QString GreaderServiceRoot::code() const {
|
||||||
|
|
||||||
void GreaderServiceRoot::saveAllCachedData(bool ignore_errors) {
|
void GreaderServiceRoot::saveAllCachedData(bool ignore_errors) {
|
||||||
auto msg_cache = takeMessageCache();
|
auto msg_cache = takeMessageCache();
|
||||||
|
QMapIterator<RootItem::ReadStatus, QStringList> i(msg_cache.m_cachedStatesRead);
|
||||||
|
|
||||||
/*
|
// Save the actual data read/unread.
|
||||||
QMapIterator<RootItem::ReadStatus, QStringList> i(msg_cache.m_cachedStatesRead);
|
while (i.hasNext()) {
|
||||||
|
i.next();
|
||||||
|
auto key = i.key();
|
||||||
|
QStringList ids = i.value();
|
||||||
|
|
||||||
// Save the actual data read/unread.
|
if (!ids.isEmpty()) {
|
||||||
while (i.hasNext()) {
|
if (network()->markMessagesRead(key, ids, networkProxy()) != QNetworkReply::NetworkError::NoError &&
|
||||||
i.next();
|
!ignore_errors) {
|
||||||
auto key = i.key();
|
|
||||||
QStringList ids = i.value();
|
|
||||||
|
|
||||||
if (!ids.isEmpty()) {
|
|
||||||
auto res = network()->markMessagesRead(key, ids, networkProxy());
|
|
||||||
|
|
||||||
if (!ignore_errors && res.first != QNetworkReply::NetworkError::NoError) {
|
|
||||||
addMessageStatesToCache(ids, key);
|
addMessageStatesToCache(ids, key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QMapIterator<RootItem::Importance, QList<Message>> j(msg_cache.m_cachedStatesImportant);
|
QMapIterator<RootItem::Importance, QList<Message>> j(msg_cache.m_cachedStatesImportant);
|
||||||
|
|
||||||
// Save the actual data important/not important.
|
// Save the actual data important/not important.
|
||||||
while (j.hasNext()) {
|
while (j.hasNext()) {
|
||||||
j.next();
|
j.next();
|
||||||
auto key = j.key();
|
auto key = j.key();
|
||||||
QList<Message> messages = j.value();
|
QList<Message> messages = j.value();
|
||||||
|
|
||||||
if (!messages.isEmpty()) {
|
if (!messages.isEmpty()) {
|
||||||
QStringList feed_ids, guid_hashes;
|
QStringList custom_ids;
|
||||||
|
|
||||||
for (const Message& msg : messages) {
|
for (const Message& msg : messages) {
|
||||||
feed_ids.append(msg.m_feedId);
|
custom_ids.append(msg.m_customId);
|
||||||
guid_hashes.append(msg.m_customHash);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res = network()->markMessagesStarred(key, feed_ids, guid_hashes, networkProxy());
|
if (network()->markMessagesStarred(key, custom_ids, networkProxy()) != QNetworkReply::NetworkError::NoError &&
|
||||||
|
!ignore_errors) {
|
||||||
if (!ignore_errors && res.first != QNetworkReply::NetworkError::NoError) {
|
|
||||||
addMessageStatesToCache(messages, key);
|
addMessageStatesToCache(messages, key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
|
QMapIterator<QString, QStringList> k(msg_cache.m_cachedLabelAssignments);
|
||||||
|
|
||||||
|
// Assign label for these messages.
|
||||||
|
while (k.hasNext()) {
|
||||||
|
k.next();
|
||||||
|
auto label_custom_id = k.key();
|
||||||
|
QStringList messages = k.value();
|
||||||
|
|
||||||
|
if (!messages.isEmpty()) {
|
||||||
|
if (network()->editLabels(label_custom_id, true, messages, networkProxy()) != QNetworkReply::NetworkError::NoError &&
|
||||||
|
!ignore_errors) {
|
||||||
|
addLabelsAssignmentsToCache(messages, label_custom_id, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QMapIterator<QString, QStringList> l(msg_cache.m_cachedLabelDeassignments);
|
||||||
|
|
||||||
|
// Remove label from these messages.
|
||||||
|
while (l.hasNext()) {
|
||||||
|
l.next();
|
||||||
|
auto label_custom_id = l.key();
|
||||||
|
QStringList messages = l.value();
|
||||||
|
|
||||||
|
if (!messages.isEmpty()) {
|
||||||
|
if (network()->editLabels(label_custom_id, false, messages, networkProxy()) != QNetworkReply::NetworkError::NoError &&
|
||||||
|
!ignore_errors) {
|
||||||
|
addLabelsAssignmentsToCache(messages, label_custom_id, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GreaderServiceRoot::updateTitle() {
|
void GreaderServiceRoot::updateTitle() {
|
||||||
|
|
|
@ -222,7 +222,6 @@ QNetworkReply::NetworkError InoreaderNetworkFactory::editLabels(const QString& s
|
||||||
m_oauth2->bearer().toLocal8Bit()));
|
m_oauth2->bearer().toLocal8Bit()));
|
||||||
|
|
||||||
QStringList trimmed_ids;
|
QStringList trimmed_ids;
|
||||||
QRegularExpression regex_short_id(QSL("[0-9a-zA-Z]+$"));
|
|
||||||
|
|
||||||
for (const QString& id : msg_custom_ids) {
|
for (const QString& id : msg_custom_ids) {
|
||||||
trimmed_ids.append(QString("i=") + id);
|
trimmed_ids.append(QString("i=") + id);
|
||||||
|
|
Loading…
Add table
Reference in a new issue