Line data Source code
1 : #include "RawFeedRewriter.h"
2 :
3 : #include <QDebug>
4 : #include <QtConcurrent>
5 :
6 : #include "ImageCache.h"
7 :
8 31 : RawFeedRewriter::RawFeedRewriter(QObject *parent, QNetworkAccessManager* networkManager) :
9 : FangObject(parent),
10 31 : newsList(nullptr),
11 31 : sanitizer(),
12 31 : imageGrabber(nullptr, networkManager)
13 : {
14 31 : connect(&imageGrabber, &ImageGrabber::finished, this, &RawFeedRewriter::onImageGrabberFinished);
15 31 : connect(&sanitizeWatcher, &QFutureWatcher<QSet<QUrl>>::finished, this, &RawFeedRewriter::onSanitizeFinished);
16 38 : connect(&finalizeWatcher, &QFutureWatcher<void>::finished, this, [this]() { emit finished(); });
17 31 : }
18 :
19 31 : void RawFeedRewriter::rewrite(QList<RawNews *> *newsList)
20 : {
21 31 : this->newsList = newsList;
22 :
23 : // Reset sanitizer state for new batch.
24 31 : 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 31 : QSet<QUrl> allImageURLs;
30 :
31 62 : for (RawNews* news : *this->newsList) {
32 31 : QSet<QUrl> imageURLs;
33 :
34 31 : if (!news->content.isEmpty()) {
35 0 : news->content = sanitizer.sanitize(news->content, imageURLs);
36 0 : allImageURLs.unite(imageURLs);
37 : }
38 :
39 31 : if (!news->description.isEmpty()) {
40 30 : news->description = sanitizer.sanitize(news->description, imageURLs);
41 30 : allImageURLs.unite(imageURLs);
42 : }
43 :
44 : // Include media image URLs for downloading and caching.
45 31 : if (!news->mediaImageURL.isEmpty()) {
46 0 : allImageURLs.insert(QUrl(news->mediaImageURL));
47 : }
48 31 : }
49 :
50 31 : return allImageURLs;
51 31 : });
52 31 : sanitizeWatcher.setFuture(future);
53 31 : }
54 :
55 31 : void RawFeedRewriter::onSanitizeFinished()
56 : {
57 31 : QSet<QUrl> allImageURLs = sanitizeWatcher.result();
58 :
59 : // No images? Finalize and we're done.
60 31 : if (allImageURLs.isEmpty()) {
61 24 : finalizeAll();
62 24 : emit finished();
63 24 : return;
64 : }
65 :
66 : // Fetch images, then finalize when done.
67 7 : imageGrabber.fetchUrls(allImageURLs.values());
68 31 : }
69 :
70 7 : 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 7 : finalizeAll();
76 7 : });
77 7 : finalizeWatcher.setFuture(future);
78 7 : }
79 :
80 31 : void RawFeedRewriter::finalizeAll()
81 : {
82 31 : QMap<QUrl, ImageData> imageResults = *imageGrabber.getResults();
83 :
84 62 : for (RawNews* news : *newsList) {
85 31 : if (!news->content.isEmpty()) {
86 0 : news->content = sanitizer.finalize(news->content, imageResults);
87 : }
88 :
89 31 : if (!news->description.isEmpty()) {
90 30 : news->description = sanitizer.finalize(news->description, imageResults);
91 : }
92 :
93 : // Cache media image and store cached path.
94 31 : if (!news->mediaImageURL.isEmpty()) {
95 0 : QUrl mediaUrl(news->mediaImageURL);
96 0 : if (imageResults.contains(mediaUrl)) {
97 0 : QString cachedPath = ImageCache::saveImage(mediaUrl, imageResults.value(mediaUrl));
98 0 : news->mediaImageURL = cachedPath;
99 0 : } else {
100 0 : news->mediaImageURL = "";
101 : }
102 0 : }
103 : }
104 31 : }
|