LCOV - code coverage report
Current view: top level - src/utilities - RawFeedRewriter.cpp (source / functions) Coverage Total Hit
Test: coverage.info.cleaned Lines: 71.4 % 63 45
Test Date: 2026-04-19 00:35:54 Functions: 100.0 % 8 8

            Line data    Source code
       1              : #include "RawFeedRewriter.h"
       2              : 
       3              : #include <QDebug>
       4              : #include <QtConcurrent>
       5              : 
       6              : #include "QImageCache.h"
       7              : 
       8           32 : RawFeedRewriter::RawFeedRewriter(QObject *parent, QNetworkAccessManager* networkManager) :
       9              :     QObject(parent),
      10           32 :     newsList(nullptr),
      11           32 :     sanitizer(),
      12           32 :     imageGrabber(nullptr, networkManager)
      13              : {
      14           32 :     connect(&imageGrabber, &ImageGrabber::finished, this, &RawFeedRewriter::onImageGrabberFinished);
      15           32 :     connect(&sanitizeWatcher, &QFutureWatcher<QSet<QUrl>>::finished, this, &RawFeedRewriter::onSanitizeFinished);
      16           40 :     connect(&finalizeWatcher, &QFutureWatcher<void>::finished, this, [this]() { emit finished(); });
      17           32 : }
      18              : 
      19           32 : void RawFeedRewriter::rewrite(QList<std::shared_ptr<RawNews>> *newsList)
      20              : {
      21           32 :     this->newsList = newsList;
      22              : 
      23              :     // Reset sanitizer state for new batch.
      24           32 :     sanitizer.reset();
      25              : 
      26              :     // First pass: sanitize all HTML and collect image URLs.
      27              :     // Run on a background thread to avoid blocking the UI.
      28            0 :     auto future = QtConcurrent::run([this]() -> QSet<QUrl> {
      29           32 :         QSet<QUrl> allImageURLs;
      30              : 
      31           64 :         for (const auto& news : *this->newsList) {
      32           32 :             QSet<QUrl> imageURLs;
      33              : 
      34           32 :             if (!news->content.isEmpty()) {
      35            0 :                 news->content = sanitizer.sanitize(news->content, imageURLs);
      36            0 :                 allImageURLs.unite(imageURLs);
      37              :             }
      38              : 
      39           32 :             if (!news->description.isEmpty()) {
      40           31 :                 news->description = sanitizer.sanitize(news->description, imageURLs);
      41           31 :                 allImageURLs.unite(imageURLs);
      42              :             }
      43              : 
      44              :             // Include media image URLs for downloading and caching.
      45           32 :             if (!news->mediaImageURL.isEmpty()) {
      46            0 :                 allImageURLs.insert(QUrl(news->mediaImageURL));
      47              :             }
      48           32 :         }
      49              : 
      50           32 :         return allImageURLs;
      51           32 :     });
      52           32 :     sanitizeWatcher.setFuture(future);
      53           32 : }
      54              : 
      55           32 : void RawFeedRewriter::onSanitizeFinished()
      56              : {
      57           32 :     QSet<QUrl> allImageURLs = sanitizeWatcher.result();
      58              : 
      59              :     // No images? Finalize and we're done.
      60           32 :     if (allImageURLs.isEmpty()) {
      61           24 :         finalizeAll();
      62           24 :         emit finished();
      63           24 :         return;
      64              :     }
      65              : 
      66              :     // Fetch images, then finalize when done.
      67            8 :     imageGrabber.fetchUrls(allImageURLs.values());
      68           32 : }
      69              : 
      70            8 : void RawFeedRewriter::onImageGrabberFinished()
      71              : {
      72              :     // Run finalization on a background thread to avoid blocking the UI
      73              :     // during XML parsing and image caching.
      74            0 :     auto future = QtConcurrent::run([this]() {
      75            8 :         finalizeAll();
      76            8 :     });
      77            8 :     finalizeWatcher.setFuture(future);
      78            8 : }
      79              : 
      80           32 : void RawFeedRewriter::finalizeAll()
      81              : {
      82           32 :     QMap<QUrl, ImageData> imageResults = *imageGrabber.getResults();
      83              : 
      84           64 :     for (const auto& news : *newsList) {
      85           32 :         if (!news->content.isEmpty()) {
      86            0 :             news->content = sanitizer.finalize(news->content, imageResults);
      87              :         }
      88              : 
      89           32 :         if (!news->description.isEmpty()) {
      90           31 :             news->description = sanitizer.finalize(news->description, imageResults);
      91              :         }
      92              : 
      93              :         // Cache media image and store cached path with dimensions.
      94           32 :         if (!news->mediaImageURL.isEmpty()) {
      95            0 :             QUrl mediaUrl(news->mediaImageURL);
      96            0 :             if (imageResults.contains(mediaUrl)) {
      97            0 :                 ImageData imageData = imageResults.value(mediaUrl);
      98            0 :                 QString cachedPath = "/images/" + QImageCache::saveImage(mediaUrl, imageData);
      99            0 :                 int w = imageData.image.width();
     100            0 :                 int h = imageData.image.height();
     101            0 :                 if (w > 0 && h > 0) {
     102            0 :                     cachedPath += "?w=" + QString::number(w) + "&h=" + QString::number(h);
     103              :                 }
     104            0 :                 news->mediaImageURL = cachedPath;
     105            0 :             } else {
     106            0 :                 news->mediaImageURL = "";
     107              :             }
     108            0 :         }
     109              :     }
     110           32 : }
        

Generated by: LCOV version 2.0-1