RSSHub/lib/routes/f95zone/utils.ts

72 lines
2.0 KiB
TypeScript

import { load } from 'cheerio';
const ALLOWED_TAGS = new Set(['div', 'span', 'p', 'br', 'b', 'strong', 'i', 'em', 'u', 's', 'a', 'img', 'ul', 'ol', 'li', 'blockquote', 'hr', 'pre', 'code']);
const ALLOWED_ATTRS: Record<string, string[]> = {
a: ['href', 'target', 'rel'],
img: ['src', 'alt', 'title', 'style'],
div: ['style'],
span: ['style'],
};
export const processContent = (html: string): string => {
const $ = load(html);
// Process images: use original URLs, remove duplicates
const seenImages = new Set<string>();
$('img').each((_, el) => {
const $img = $(el);
const $parent = $img.parent('a');
let src = $parent.attr('href') || $img.attr('data-src') || $img.attr('src') || '';
src = src.replace('/thumb/', '/');
if (!src || src.startsWith('data:') || seenImages.has(src)) {
$img.remove();
} else {
seenImages.add(src);
$img.attr('src', src).removeAttr('data-src');
if ($parent.length) {
$parent.replaceWith($img);
}
}
});
// Remove unwanted tags completely
$('button, script, style, noscript').remove();
// Remove disallowed tags but keep content
let changed = true;
while (changed) {
changed = false;
$('*').each((_, el) => {
if (el.type !== 'tag' || ALLOWED_TAGS.has(el.name)) {
return;
}
$(el).replaceWith($(el).html() || '');
changed = true;
return false;
});
}
// Clean attributes
$('*').each((_, el) => {
if (el.type !== 'tag') {
return;
}
const allowed = new Set(ALLOWED_ATTRS[el.name] || []);
const attrKeys = Object.keys(el.attribs || {});
for (const attr of attrKeys) {
if (!allowed.has(attr)) {
$(el).removeAttr(attr);
}
}
});
// Remove empty divs
$('div')
.filter((_, el) => !$(el).html()?.trim())
.remove();
return $.html() || '';
};