File indexing completed on 2024-05-12 16:21:29
0001 /* 0002 * SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org> 0003 * SPDX-FileCopyrightText: 2021 Bart De Vries <bart@mogwai.be> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include <QVariant> 0009 0010 #include "author.h" 0011 #include "database.h" 0012 #include "datamanager.h" 0013 #include "error.h" 0014 #include "feed.h" 0015 #include "feedlogging.h" 0016 #include "fetcher.h" 0017 0018 Feed::Feed(const QString &feedurl) 0019 : QObject(&DataManager::instance()) 0020 { 0021 QSqlQuery query; 0022 query.prepare(QStringLiteral("SELECT * FROM Feeds WHERE url=:feedurl;")); 0023 query.bindValue(QStringLiteral(":feedurl"), feedurl); 0024 Database::instance().execute(query); 0025 if (!query.next()) 0026 qWarning() << "Failed to load feed" << feedurl; 0027 0028 m_subscribed.setSecsSinceEpoch(query.value(QStringLiteral("subscribed")).toInt()); 0029 0030 m_lastUpdated.setSecsSinceEpoch(query.value(QStringLiteral("lastUpdated")).toInt()); 0031 0032 m_url = query.value(QStringLiteral("url")).toString(); 0033 m_name = query.value(QStringLiteral("name")).toString(); 0034 m_image = query.value(QStringLiteral("image")).toString(); 0035 m_link = query.value(QStringLiteral("link")).toString(); 0036 m_description = query.value(QStringLiteral("description")).toString(); 0037 m_deleteAfterCount = query.value(QStringLiteral("deleteAfterCount")).toInt(); 0038 m_deleteAfterType = query.value(QStringLiteral("deleteAfterType")).toInt(); 0039 m_notify = query.value(QStringLiteral("notify")).toBool(); 0040 m_dirname = query.value(QStringLiteral("dirname")).toString(); 0041 0042 m_errorId = 0; 0043 m_errorString = QLatin1String(""); 0044 0045 updateAuthors(); 0046 updateUnreadEntryCountFromDB(); 0047 updateNewEntryCountFromDB(); 0048 updateFavoriteEntryCountFromDB(); 0049 0050 connect(&Fetcher::instance(), &Fetcher::feedUpdateStatusChanged, this, [this](const QString &url, bool status) { 0051 if (url == m_url) { 0052 setRefreshing(status); 0053 } 0054 }); 0055 connect(&DataManager::instance(), &DataManager::feedEntriesUpdated, this, [this](const QString &url) { 0056 if (url == m_url) { 0057 Q_EMIT entryCountChanged(); 0058 updateUnreadEntryCountFromDB(); 0059 Q_EMIT DataManager::instance().unreadEntryCountChanged(m_url); 0060 Q_EMIT unreadEntryCountChanged(); 0061 Q_EMIT DataManager::instance().newEntryCountChanged(m_url); 0062 Q_EMIT newEntryCountChanged(); 0063 setErrorId(0); 0064 setErrorString(QLatin1String("")); 0065 } 0066 }); 0067 connect(&DataManager::instance(), &DataManager::newEntryCountChanged, this, [this](const QString &url) { 0068 if (url == m_url) { 0069 updateNewEntryCountFromDB(); 0070 Q_EMIT newEntryCountChanged(); 0071 } 0072 }); 0073 connect(&DataManager::instance(), &DataManager::favoriteEntryCountChanged, this, [this](const QString &url) { 0074 if (url == m_url) { 0075 updateFavoriteEntryCountFromDB(); 0076 Q_EMIT favoriteEntryCountChanged(); 0077 } 0078 }); 0079 connect(&Fetcher::instance(), 0080 &Fetcher::error, 0081 this, 0082 [this](const Error::Type type, const QString &url, const QString &id, int errorId, const QString &errorString) { 0083 Q_UNUSED(type) 0084 Q_UNUSED(id) 0085 if (url == m_url) { 0086 setErrorId(errorId); 0087 setErrorString(errorString); 0088 setRefreshing(false); 0089 } 0090 }); 0091 connect(&Fetcher::instance(), &Fetcher::downloadFinished, this, [this](QString url) { 0092 if (url == m_image) { 0093 Q_EMIT imageChanged(url); 0094 Q_EMIT cachedImageChanged(cachedImage()); 0095 } 0096 }); 0097 0098 m_entries = new EntriesProxyModel(this); 0099 } 0100 0101 Feed::~Feed() 0102 { 0103 } 0104 0105 void Feed::updateAuthors() 0106 { 0107 QVector<Author *> newAuthors; 0108 bool haveAuthorsChanged = false; 0109 0110 QSqlQuery authorQuery; 0111 authorQuery.prepare(QStringLiteral("SELECT * FROM Authors WHERE id='' AND feed=:feed")); 0112 authorQuery.bindValue(QStringLiteral(":feed"), m_url); 0113 Database::instance().execute(authorQuery); 0114 while (authorQuery.next()) { 0115 // check if author already exists, if so, then reuse 0116 bool existingAuthor = false; 0117 QString name = authorQuery.value(QStringLiteral("name")).toString(); 0118 QString email = authorQuery.value(QStringLiteral("email")).toString(); 0119 QString url = authorQuery.value(QStringLiteral("uri")).toString(); 0120 qCDebug(kastsFeed) << name << email << url; 0121 for (int i = 0; i < m_authors.count(); i++) { 0122 qCDebug(kastsFeed) << "old authors" << m_authors[i]->name() << m_authors[i]->email() << m_authors[i]->url(); 0123 if (m_authors[i] && m_authors[i]->name() == name && m_authors[i]->email() == email && m_authors[i]->url() == url) { 0124 existingAuthor = true; 0125 newAuthors += m_authors[i]; 0126 } 0127 } 0128 if (!existingAuthor) { 0129 newAuthors += new Author(name, email, url, nullptr); 0130 haveAuthorsChanged = true; 0131 } 0132 } 0133 0134 // Finally check whether m_authors and newAuthors are identical 0135 // if not, then delete the authors that were removed 0136 for (int i = 0; i < m_authors.count(); i++) { 0137 if (!newAuthors.contains(m_authors[i])) { 0138 delete m_authors[i]; 0139 haveAuthorsChanged = true; 0140 } 0141 } 0142 0143 m_authors = newAuthors; 0144 0145 if (haveAuthorsChanged) 0146 Q_EMIT authorsChanged(m_authors); 0147 qCDebug(kastsFeed) << "feed" << m_name << "authors have changed?" << haveAuthorsChanged; 0148 } 0149 0150 void Feed::updateUnreadEntryCountFromDB() 0151 { 0152 QSqlQuery query; 0153 query.prepare(QStringLiteral("SELECT COUNT (id) FROM Entries where feed=:feed AND read=0;")); 0154 query.bindValue(QStringLiteral(":feed"), m_url); 0155 Database::instance().execute(query); 0156 if (!query.next()) 0157 m_unreadEntryCount = -1; 0158 m_unreadEntryCount = query.value(0).toInt(); 0159 } 0160 0161 void Feed::updateNewEntryCountFromDB() 0162 { 0163 QSqlQuery query; 0164 query.prepare(QStringLiteral("SELECT COUNT (id) FROM Entries where feed=:feed AND new=1;")); 0165 query.bindValue(QStringLiteral(":feed"), m_url); 0166 Database::instance().execute(query); 0167 if (!query.next()) 0168 m_newEntryCount = -1; 0169 m_newEntryCount = query.value(0).toInt(); 0170 } 0171 0172 void Feed::updateFavoriteEntryCountFromDB() 0173 { 0174 QSqlQuery query; 0175 query.prepare(QStringLiteral("SELECT COUNT (id) FROM Entries where feed=:feed AND favorite=1;")); 0176 query.bindValue(QStringLiteral(":feed"), m_url); 0177 Database::instance().execute(query); 0178 if (!query.next()) 0179 m_favoriteEntryCount = -1; 0180 m_favoriteEntryCount = query.value(0).toInt(); 0181 } 0182 0183 QString Feed::url() const 0184 { 0185 return m_url; 0186 } 0187 0188 QString Feed::name() const 0189 { 0190 return m_name; 0191 } 0192 0193 QString Feed::image() const 0194 { 0195 return m_image; 0196 } 0197 0198 QString Feed::cachedImage() const 0199 { 0200 return Fetcher::instance().image(m_image); 0201 } 0202 0203 QString Feed::link() const 0204 { 0205 return m_link; 0206 } 0207 0208 QString Feed::description() const 0209 { 0210 return m_description; 0211 } 0212 0213 QVector<Author *> Feed::authors() const 0214 { 0215 return m_authors; 0216 } 0217 0218 int Feed::deleteAfterCount() const 0219 { 0220 return m_deleteAfterCount; 0221 } 0222 0223 int Feed::deleteAfterType() const 0224 { 0225 return m_deleteAfterType; 0226 } 0227 0228 QDateTime Feed::subscribed() const 0229 { 0230 return m_subscribed; 0231 } 0232 0233 QDateTime Feed::lastUpdated() const 0234 { 0235 return m_lastUpdated; 0236 } 0237 0238 bool Feed::notify() const 0239 { 0240 return m_notify; 0241 } 0242 0243 QString Feed::dirname() const 0244 { 0245 return m_dirname; 0246 } 0247 0248 int Feed::entryCount() const 0249 { 0250 return DataManager::instance().entryCount(this); 0251 } 0252 0253 int Feed::unreadEntryCount() const 0254 { 0255 return m_unreadEntryCount; 0256 } 0257 0258 int Feed::newEntryCount() const 0259 { 0260 return m_newEntryCount; 0261 } 0262 0263 int Feed::favoriteEntryCount() const 0264 { 0265 return m_favoriteEntryCount; 0266 } 0267 0268 bool Feed::refreshing() const 0269 { 0270 return m_refreshing; 0271 } 0272 0273 int Feed::errorId() const 0274 { 0275 return m_errorId; 0276 } 0277 0278 QString Feed::errorString() const 0279 { 0280 return m_errorString; 0281 } 0282 0283 void Feed::setName(const QString &name) 0284 { 0285 if (name != m_name) { 0286 m_name = name; 0287 Q_EMIT nameChanged(m_name); 0288 } 0289 } 0290 0291 void Feed::setImage(const QString &image) 0292 { 0293 if (image != m_image) { 0294 m_image = image; 0295 Q_EMIT imageChanged(m_image); 0296 Q_EMIT cachedImageChanged(cachedImage()); 0297 } 0298 } 0299 0300 void Feed::setLink(const QString &link) 0301 { 0302 if (link != m_link) { 0303 m_link = link; 0304 Q_EMIT linkChanged(m_link); 0305 } 0306 } 0307 0308 void Feed::setDescription(const QString &description) 0309 { 0310 if (description != m_description) { 0311 m_description = description; 0312 Q_EMIT descriptionChanged(m_description); 0313 } 0314 } 0315 0316 void Feed::setAuthors(const QVector<Author *> &authors) 0317 { 0318 for (auto &author : m_authors) { 0319 delete author; 0320 } 0321 m_authors.clear(); 0322 m_authors = authors; 0323 Q_EMIT authorsChanged(m_authors); 0324 } 0325 0326 void Feed::setDeleteAfterCount(int count) 0327 { 0328 m_deleteAfterCount = count; 0329 Q_EMIT deleteAfterCountChanged(m_deleteAfterCount); 0330 } 0331 0332 void Feed::setDeleteAfterType(int type) 0333 { 0334 m_deleteAfterType = type; 0335 Q_EMIT deleteAfterTypeChanged(m_deleteAfterType); 0336 } 0337 0338 void Feed::setLastUpdated(const QDateTime &lastUpdated) 0339 { 0340 if (lastUpdated != m_lastUpdated) { 0341 m_lastUpdated = lastUpdated; 0342 Q_EMIT lastUpdatedChanged(m_lastUpdated); 0343 } 0344 } 0345 0346 void Feed::setNotify(bool notify) 0347 { 0348 if (notify != m_notify) { 0349 m_notify = notify; 0350 Q_EMIT notifyChanged(m_notify); 0351 } 0352 } 0353 0354 void Feed::setDirname(const QString &dirname) 0355 { 0356 if (dirname != m_dirname) { 0357 m_dirname = dirname; 0358 Q_EMIT dirnameChanged(m_dirname); 0359 } 0360 } 0361 0362 void Feed::setUnreadEntryCount(const int count) 0363 { 0364 if (count != m_unreadEntryCount) { 0365 m_unreadEntryCount = count; 0366 Q_EMIT unreadEntryCountChanged(); 0367 Q_EMIT DataManager::instance().unreadEntryCountChanged(m_url); 0368 // TODO: can one of the two slots be removed?? 0369 } 0370 } 0371 0372 void Feed::setRefreshing(bool refreshing) 0373 { 0374 if (refreshing != m_refreshing) { 0375 m_refreshing = refreshing; 0376 if (!m_refreshing) { 0377 m_errorId = 0; 0378 m_errorString = QString(); 0379 } 0380 Q_EMIT refreshingChanged(m_refreshing); 0381 } 0382 } 0383 0384 void Feed::setErrorId(int errorId) 0385 { 0386 if (errorId != m_errorId) { 0387 m_errorId = errorId; 0388 Q_EMIT errorIdChanged(m_errorId); 0389 } 0390 } 0391 0392 void Feed::setErrorString(const QString &errorString) 0393 { 0394 if (errorString != m_errorString) { 0395 m_errorString = errorString; 0396 Q_EMIT errorStringChanged(m_errorString); 0397 } 0398 } 0399 0400 void Feed::refresh() 0401 { 0402 Fetcher::instance().fetch(m_url); 0403 }