/* * 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 constexpr const char *FEED = "/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() { if (m_httpClient) return; // Prepare the HTTP request boost::beast::http::request> request; request.version(11); if (m_lastCheck.isNull()) request.method(boost::beast::http::verb::get); else request.method(boost::beast::http::verb::head); request.target(FEED); request.set(boost::beast::http::field::host, "flowee.org"); auto *fp = FloweePay::instance(); m_httpClient = SimpleHttpClient::create(fp->ioContext(), fp->sslContext(), request); connect(m_httpClient.get(), SIGNAL(finished()), this, SLOT(downloadFinished())); connect(m_httpClient.get(), SIGNAL(errored(SimpleHttpClient::ErrorType)), this, SLOT(errored(SimpleHttpClient::ErrorType))); } void FeedDataProvider::downloadFinished() { if (m_httpClient->request().method() == boost::beast::http::verb::head) { const auto lastModified = m_httpClient->headerDate(boost::beast::http::field::last_modified); m_httpClient.reset(); // check if the server has a newer one. if (lastModified > m_lastCheck) { m_lastCheck = QDateTime(); assert(m_lastCheck.isNull()); start(); // this will now do a download of the actual data. } return; } if (m_httpClient->httpErrorCode() == 200) { auto data = m_httpClient->responseBody(); QFile feed(FLOWEE_JSON); if (feed.open(QIODevice::WriteOnly)) { feed.write(data.begin(), data.size()); feed.close(); } readFeed(); } m_httpClient.reset(); } 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(SimpleHttpClient::ErrorType e) { logDebug() << "got error on downloading the feed:" << e; if (m_httpClient.get()) logDebug() << " http error code:" << m_httpClient->httpErrorCode(); } // ------------------------------------ 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; }