newsblur can load list of feeds and categories

This commit is contained in:
Martin Rotter 2022-02-18 15:49:58 +01:00
parent 4ab483d14a
commit 2975f3d825
4 changed files with 111 additions and 16 deletions

View File

@ -12,5 +12,6 @@
#define NEWSBLUR_API_LOGIN "api/login" #define NEWSBLUR_API_LOGIN "api/login"
#define NEWSBLUR_API_LOGOUT "api/logout" #define NEWSBLUR_API_LOGOUT "api/logout"
#define NEWSBLUR_API_SIGNUP "api/signup" #define NEWSBLUR_API_SIGNUP "api/signup"
#define NEWSBLUR_API_FEEDS "reader/feeds"
#endif // NEWSBLUR_DEFINITIONS_H #endif // NEWSBLUR_DEFINITIONS_H

View File

@ -26,6 +26,90 @@ NewsBlurNetwork::NewsBlurNetwork(QObject* parent)
clearCredentials(); clearCredentials();
} }
RootItem* NewsBlurNetwork::categoriesFeedsLabelsTree(const QNetworkProxy& proxy) {
QJsonDocument json = feeds(proxy);
RootItem* root = new RootItem();
const auto timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
QMap<QString, RootItem*> cats;
QList<QPair<RootItem*, QJsonArray>> cats_array = {
{ root, json.object()["folders"].toArray() }
};
while (!cats_array.isEmpty()) {
// Add direct descendants as categories to parent, then process their children.
QPair<RootItem*, QJsonArray> cats_for_parent = cats_array.takeFirst();
for (const QJsonValue& var : cats_for_parent.second) {
if (var.type() == QJsonValue::Type::Double) {
// We have feed.
Feed* feed = new Feed();
feed->setCustomId(QString::number(var.toInt()));
QJsonObject feed_json = json.object()["feeds"].toObject()[feed->customId()].toObject();
feed->setTitle(feed_json["feed_title"].toString());
feed->setSource(feed_json["feed_link"].toString());
QString favicon_url = feed_json["favicon_url"].toString();
if (!favicon_url.isEmpty()) {
QIcon icon;
if (NetworkFactory::downloadIcon({ { favicon_url, true } }, timeout, icon, {}, proxy) ==
QNetworkReply::NetworkError::NoError) {
feed->setIcon(icon);
}
}
cats_for_parent.first->appendChild(feed);
}
else if (var.type() == QJsonValue::Type::Object) {
const QString category_name = var.toObject().keys().first();
Category* category = new Category();
category->setTitle(category_name);
category->setCustomId(category_name);
cats_for_parent.first->appendChild(category);
cats_array.append({
category,
var.toObject()[category_name].toArray()
});
}
}
}
return root;
}
QJsonDocument NewsBlurNetwork::feeds(const QNetworkProxy& proxy) {
ensureLogin(proxy);
const QString full_url = generateFullUrl(Operations::Feeds);
const auto timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
QByteArray output;
auto network_result = NetworkFactory::performNetworkOperation(full_url,
timeout,
{},
output,
QNetworkAccessManager::Operation::GetOperation,
{},
false,
{},
{},
proxy);
if (network_result.m_networkError == QNetworkReply::NetworkError::NoError) {
ApiResult res; res.decodeBaseResponse(output);
return res.m_json;
}
else {
throw NetworkException(network_result.m_networkError, output);
}
}
LoginResult NewsBlurNetwork::login(const QNetworkProxy& proxy) { LoginResult NewsBlurNetwork::login(const QNetworkProxy& proxy) {
const QString full_url = generateFullUrl(Operations::Login); const QString full_url = generateFullUrl(Operations::Login);
const auto timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); const auto timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
@ -46,17 +130,9 @@ LoginResult NewsBlurNetwork::login(const QNetworkProxy& proxy) {
proxy); proxy);
if (network_result.m_networkError == QNetworkReply::NetworkError::NoError) { if (network_result.m_networkError == QNetworkReply::NetworkError::NoError) {
QJsonParseError err; LoginResult res; res.decodeBaseResponse(output);
QJsonDocument doc = QJsonDocument::fromJson(output, &err);
if (err.error != QJsonParseError::ParseError::NoError) { res.m_userId = res.m_json.object()["user_id"].toInt();
throw ApplicationException(err.errorString());
}
LoginResult res;
res.decodeBaseResponse(doc);
res.m_userId = doc.object()["user_id"].toInt();
res.m_sessiodId = boolinq::from(network_result.m_cookies).firstOrDefault([](const QNetworkCookie& c) { res.m_sessiodId = boolinq::from(network_result.m_cookies).firstOrDefault([](const QNetworkCookie& c) {
return c.name() == QSL(NEWSBLUS_AUTH_COOKIE); return c.name() == QSL(NEWSBLUS_AUTH_COOKIE);
}).value(); }).value();
@ -146,6 +222,9 @@ QString NewsBlurNetwork::generateFullUrl(NewsBlurNetwork::Operations operation)
case Operations::Login: case Operations::Login:
return sanitizedBaseUrl() + QSL(NEWSBLUR_API_LOGIN); return sanitizedBaseUrl() + QSL(NEWSBLUR_API_LOGIN);
case Operations::Feeds:
return sanitizedBaseUrl() + QSL(NEWSBLUR_API_FEEDS);
default: default:
return sanitizedBaseUrl(); return sanitizedBaseUrl();
} }
@ -163,7 +242,15 @@ void NewsBlurNetwork::setDownloadOnlyUnreadMessages(bool download_only_unread) {
m_downloadOnlyUnreadMessages = download_only_unread; m_downloadOnlyUnreadMessages = download_only_unread;
} }
void ApiResult::decodeBaseResponse(const QJsonDocument& doc) { void ApiResult::decodeBaseResponse(const QByteArray& json_data) {
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(json_data, &err);
if (err.error != QJsonParseError::ParseError::NoError) {
throw ApplicationException(err.errorString());
}
m_json = doc;
m_authenticated = doc.object()["authenticated"].toBool(); m_authenticated = doc.object()["authenticated"].toBool();
m_code = doc.object()["code"].toInt(); m_code = doc.object()["code"].toInt();

View File

@ -13,8 +13,9 @@ struct ApiResult {
bool m_authenticated; bool m_authenticated;
int m_code; int m_code;
QStringList m_errors; QStringList m_errors;
QJsonDocument m_json;
void decodeBaseResponse(const QJsonDocument& doc); void decodeBaseResponse(const QByteArray& json_data);
}; };
struct LoginResult : ApiResult { struct LoginResult : ApiResult {
@ -27,11 +28,19 @@ class NewsBlurNetwork : public QObject {
public: public:
enum class Operations { enum class Operations {
Login Login,
Feeds
}; };
explicit NewsBlurNetwork(QObject* parent = nullptr); explicit NewsBlurNetwork(QObject* parent = nullptr);
// Convenience methods.
RootItem* categoriesFeedsLabelsTree(const QNetworkProxy& proxy);
// API.
QJsonDocument feeds(const QNetworkProxy& proxy);
// Misc.
void clearCredentials(); void clearCredentials();
QString username() const; QString username() const;

View File

@ -139,7 +139,5 @@ void NewsBlurServiceRoot::updateTitleIcon() {
} }
RootItem* NewsBlurServiceRoot::obtainNewTreeForSyncIn() const { RootItem* NewsBlurServiceRoot::obtainNewTreeForSyncIn() const {
return nullptr; return m_network->categoriesFeedsLabelsTree(networkProxy());;
//return m_network->categoriesFeedsLabelsTree(true, networkProxy());
} }