feedkeeper 0.26.2 → 0.26.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -76,12 +76,28 @@ function removeReaderNoise(root, title, stats) {
|
|
|
76
76
|
const headline = normalized(title ?? "");
|
|
77
77
|
const seen = new Set();
|
|
78
78
|
let leading = 0;
|
|
79
|
+
let reachedText = false;
|
|
79
80
|
for (const block of leafBlocks(root)) {
|
|
80
81
|
const text = flat(block.textContent);
|
|
81
82
|
if (!text)
|
|
82
83
|
continue;
|
|
83
84
|
const position = leading++;
|
|
84
85
|
const heading = /^H[1-3]$/.test(block.tagName);
|
|
86
|
+
// Before the first real paragraph: a standalone date and a section chip repeat what the page head says.
|
|
87
|
+
if (!reachedText && text.length >= 80)
|
|
88
|
+
reachedText = true;
|
|
89
|
+
if (!reachedText && position < 12) {
|
|
90
|
+
const anchorsInBlock = block.querySelectorAll("a");
|
|
91
|
+
const chip = anchorsInBlock.length === 1 && flat(anchorsInBlock[0].textContent) === text && text.length <= 30 && text.split(" ").length <= 3;
|
|
92
|
+
if (chip) {
|
|
93
|
+
drop(block, "section-chip");
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (/^(\d{1,2}\.\s?\d{1,2}\.\s?\d{2,4}|[A-Z][a-z]{2,8}\.? \d{1,2}, \d{4})([,\s•·|-]+\d{1,2}[:.]\d{2}( Uhr)?)?$/.test(text)) {
|
|
97
|
+
drop(block, "dateline");
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
85
101
|
if (headline && normalized(text) === headline && (position < 4 || (heading && position < 14))) {
|
|
86
102
|
drop(block, "title-repeat");
|
|
87
103
|
continue;
|
|
@@ -228,9 +244,37 @@ export function cleanReaderHtml(html, title, stats = {}) {
|
|
|
228
244
|
if (!html)
|
|
229
245
|
return html;
|
|
230
246
|
const root = JSDOM.fragment(html);
|
|
247
|
+
const images = resolveCustomImages(root, stats);
|
|
231
248
|
const noise = removeReaderNoise(root, title, stats);
|
|
232
249
|
const typography = removeTypography(root, stats);
|
|
233
|
-
return noise || typography ? serialize(root) : html;
|
|
250
|
+
return images || noise || typography ? serialize(root) : html;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Some publishers wrap a picture in their own element and let a script fill the image: `<a-img src="/imgs/…">`
|
|
254
|
+
* around an `<img>` that only holds a grey placeholder. Without the script no reader would ever show the picture, so
|
|
255
|
+
* the wrapper's address takes the placeholder's place and the wrapper, which means nothing outside the page, goes.
|
|
256
|
+
*/
|
|
257
|
+
function resolveCustomImages(root, stats) {
|
|
258
|
+
let changed = false;
|
|
259
|
+
for (const wrapper of [...root.querySelectorAll("a-img")]) {
|
|
260
|
+
const source = (wrapper.getAttribute("src") ?? "").trim();
|
|
261
|
+
if (source && !/^data:/i.test(source)) {
|
|
262
|
+
let image = wrapper.querySelector("img");
|
|
263
|
+
if (!image) {
|
|
264
|
+
image = wrapper.ownerDocument.createElement("img");
|
|
265
|
+
image.setAttribute("alt", wrapper.getAttribute("alt") ?? "");
|
|
266
|
+
wrapper.append(image);
|
|
267
|
+
}
|
|
268
|
+
const current = (image.getAttribute("src") ?? "").trim();
|
|
269
|
+
if (!current || /^data:/i.test(current))
|
|
270
|
+
image.setAttribute("src", source);
|
|
271
|
+
}
|
|
272
|
+
wrapper.replaceWith(...wrapper.childNodes);
|
|
273
|
+
changed = true;
|
|
274
|
+
}
|
|
275
|
+
if (changed)
|
|
276
|
+
stats["custom-image"] = (stats["custom-image"] ?? 0) + 1;
|
|
277
|
+
return changed;
|
|
234
278
|
}
|
|
235
279
|
const CREDIT_PATTERNS = [
|
|
236
280
|
/^(©|\(c\)\s|copyright\b)/i,
|