/* * This file is part of the Flowee project * Copyright (C) 2025 Tom Zander * * 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 3 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, see . */ #include "FeedDataProvider.h" #include #include #include #include #include #include #include #include #include #include #include constexpr const char *FEED = "https://flowee.org/v/index.json"; constexpr const char *FLOWEE_JSON = "social-feed-flowee.json"; FeedDataProvider::FeedDataProvider(QObject *parent) : QObject(parent) { QFileInfo info(FLOWEE_JSON); bool useOld = info.exists(); if (useOld) { m_lastCheck = info.lastModified(); readFeed(); } if (m_lastCheck.isNull() || m_lastCheck.msecsTo(QDateTime::currentDateTime()) > 4 * 60 * 60 * 1000) { QTimer::singleShot(10, this, SLOT(start())); } } void FeedDataProvider::start() { QUrl uri(FEED); QNetworkRequest req(uri); auto app = QCoreApplication::instance(); QString useragent = QString("%1%2/%3") .arg(app->organizationName(), app->applicationName(), app->applicationVersion()); req.setHeader(QNetworkRequest::UserAgentHeader, useragent); if (m_lastCheck.isNull()) m_reply = FloweePay::instance()->network()->get(req); else m_reply = FloweePay::instance()->network()->head(req); logInfo() << "fetching social feed file from:" << FEED; connect(m_reply, SIGNAL(finished()), this, SLOT(downloadFinished())); connect(m_reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(errored(QNetworkReply::NetworkError))); connect(m_reply, SIGNAL(sslErrors(QList)), this, SLOT(sslErrors(QList))); } void FeedDataProvider::downloadFinished() { assert(m_reply); if (m_reply->operation() == QNetworkAccessManager::HeadOperation) { auto lastModified = m_reply->header(QNetworkRequest::LastModifiedHeader); m_reply->deleteLater(); m_reply = nullptr; // check if the server has a newer one. if (lastModified.toDateTime() > m_lastCheck) { m_lastCheck = QDateTime(); assert(m_lastCheck.isNull()); start(); // this will now do a download of the actual data. } return; } auto data = m_reply->readAll(); m_reply->deleteLater(); m_reply = nullptr; QFile feed(FLOWEE_JSON); if (feed.open(QIODevice::WriteOnly)) { feed.write(data); feed.close(); } readFeed(); } void FeedDataProvider::readFeed() { QFile feed(FLOWEE_JSON); if (!feed.open(QIODevice::ReadOnly)) return; auto data = feed.readAll(); feed.close(); QJsonDocument doc = QJsonDocument::fromJson(data); if (doc.isNull()) { logWarning() << "Parsing error of social json"; return; } m_listItems.clear(); QJsonArray list = doc.array(); for (auto i = list.begin(); i != list.end(); ++i) { auto obj = i->toObject(); auto text = obj["text"]; if (!text.isString()) { logWarning() << "Skipping item in JSON, missing text attribute"; continue; } auto *item = new ListItem(this); item->m_text = text.toString(); item->m_url = obj["url"].toString(); item->m_videoLength = obj["length"].toString(); item->m_title = obj["title"].toString(); auto date = obj["date"].toString(); item->m_date = QDate::fromString(date, Qt::ISODate); m_listItems.append(item); } emit listItemsChanged(); } QList FeedDataProvider::listItems() const { return m_listItems; } void FeedDataProvider::errored(QNetworkReply::NetworkError err) { // TODO } void FeedDataProvider::sslErrors(const QList &errors) { // TODO } // ------------------------------------ ListItem::ListItem(QObject *parent) : QObject(parent) { } QDate ListItem::date() const { return m_date; } QString ListItem::videoLength() const { return m_videoLength; } QString ListItem::text() const { return m_text; } QString ListItem::title() const { return m_title; } QString ListItem::url() const { return m_url; }