import * as cheerio from 'cheerio'; import { getMetadataCache } from '~/store/v2/metadata'; import { renderComments } from '~/utils/comment'; import type { ArticleMetadata } from '~/utils/download/types'; const ITEM_SHOW_TYPE = { 图片分享: 8, 文本分享: 10, 普通图文: 0, }; /** * 根据解析的 cgiDataNew 对象进行渲染 * @param cgiData * @param comments 是否渲染留言数据,默认是 */ export async function renderHTMLFromCgiDataNew(cgiData: any, comments = true) { const title = extractTitle(cgiData); const meta = renderMetaInfo(cgiData); const contentHTML = extractContentHTML(cgiData); const bottomBarHTML = await renderBottomBar(cgiData); // 渲染留言 let commentHTML = ''; if (comments) { commentHTML = await renderComments(cgiData.link); } return ` ${title}

${title}

${meta}
原文地址: ${cgiData.link}
${contentHTML} ${commentHTML} ${bottomBarHTML}
`; } /** * 提取标题字段(title) * @param cgiData */ function extractTitle(cgiData: any): string { let title = ''; switch (cgiData.item_show_type) { case ITEM_SHOW_TYPE.图片分享: case ITEM_SHOW_TYPE.普通图文: title = cgiData.title; break; case ITEM_SHOW_TYPE.文本分享: if (cgiData.text_page_info.is_user_title === 1) { title = cgiData.title; } else { title = '(无标题)'; } break; default: title = '(unknown)'; break; } return title; } /** * 提取内容html * @param cgiData */ function extractContentHTML(cgiData: any): string { // 未购买的付费文章:content_noencode 只包含占位元素,需展示付费预览信息 if (cgiData.is_pay_subscribe === 1 && isPayPreviewPlaceholder(cgiData.content_noencode)) { return renderContent_paySubscribe(cgiData); } let contentHTML = ''; switch (cgiData.item_show_type) { case ITEM_SHOW_TYPE.图片分享: contentHTML = renderContent_8(cgiData); break; case ITEM_SHOW_TYPE.文本分享: contentHTML = renderContent_10(cgiData); break; case ITEM_SHOW_TYPE.普通图文: contentHTML = renderContent_0(cgiData); break; default: contentHTML = '(unknown)'; break; } return contentHTML; } /** * 判断 content_noencode 是否为未购买付费文章的占位内容 */ function isPayPreviewPlaceholder(contentNoencode: string | undefined): boolean { if (!contentNoencode) return true; const trimmed = contentNoencode.trim(); return !trimmed || trimmed.includes('mp-pay-preview-filter'); } /** * 渲染【付费订阅】类文章的内容部分 * @param cgiData */ function renderContent_paySubscribe(cgiData: any): string { const payInfo = cgiData.pay_subscribe_info || {}; const desc = (payInfo.desc || '').replace(/\r?\n/g, '
'); const fee = payInfo.fee ? `${payInfo.fee / 100} 元` : ''; const wecoinAmount = payInfo.wecoin_amount ? `${payInfo.wecoin_amount} 微币` : ''; const priceText = fee || wecoinAmount || '付费'; return `
付费内容 · ${priceText}
${desc ? `
${desc}
` : ''}
本文为付费文章,完整内容需购买后查看
`; } /** * 渲染【图片分享(8)】类文章的内容部分 * @param cgiData */ function renderContent_8(cgiData: any): string { // 文本内容 let textContent = cgiData.content_noencode.replace(/\n/g, '
'); // 替换函数:在 标签上插入 href 属性,href="#图X" const regex = /()(\s*图\2\s*)(<\/a>)/g; textContent = textContent.replace(regex, (match: any, openTag: any, number: any, content: any, closeTag: any) => { const newOpenTag = openTag.replace(/>$/, ` href="#图${number}">`); return newOpenTag + content + closeTag; }); // 图片内容 const pictureContent = cgiData.picture_page_info_list .map((item: any) => item.cdn_url.replace(/&/g, '&')) .map( (url: string, idx: number) => `
图${idx + 1}

图${idx + 1}

` ) .join('\n'); return `

${textContent}

${pictureContent}
`; } /** * 渲染【文本分享(10)】类文章的内容部分 * @param cgiData */ function renderContent_10(cgiData: any): string { // 文本内容 const textContent = cgiData.text_page_info.content_noencode.replace(/\n/g, '
'); return `

${textContent}

`; } /** * 渲染【普通图文(0)】类文章的内容部分 * @param cgiData */ function renderContent_0(cgiData: any): string { let contentHTML = cgiData.content_noencode || ''; // 使用 cheerio 处理 HTML 片段 const $ = cheerio.load(contentHTML, null, false); // 1. 处理懒加载图片:data-src → src $('img[data-src]').each((i, elem) => { const $img = $(elem); const dataSrc = $img.attr('data-src'); if (dataSrc) { $img.attr('src', dataSrc); $img.removeAttr('data-src'); $img.attr('loading', 'eager'); } }); // 2. 修复图片的宽高比例 (删除`height`属性即可) $('img[height]').each((i, elem) => { const $img = $(elem); $img.removeAttr('height'); }); // 3. 处理内嵌视频 // 获取处理后的 HTML 片段(cheerio 会正确序列化多顶级元素和自闭合标签) let modifiedContent = $.html(); return `
${modifiedContent}
`; } /** * 根据解析的 cgiDataNew 对象提取纯文本内容 * 用于 TXT/Excel/JSON 等纯文本导出场景 * @param cgiData */ export function renderTextFromCgiDataNew(cgiData: any): string { const title = extractTitle(cgiData); let text = ''; // 未购买的付费文章:使用付费预览描述 if (cgiData.is_pay_subscribe === 1 && isPayPreviewPlaceholder(cgiData.content_noencode)) { text = cgiData.pay_subscribe_info?.desc || '[付费内容]'; return `${title}\n\n[付费文章]\n${text.trim()}`; } switch (cgiData.item_show_type) { case ITEM_SHOW_TYPE.普通图文: { // 普通图文 & 文章分享(都是 item_show_type=0) const $ = cheerio.load(cgiData.content_noencode || '', null, false); text = $.text(); break; } case ITEM_SHOW_TYPE.图片分享: text = cgiData.content_noencode || ''; break; case ITEM_SHOW_TYPE.文本分享: text = cgiData.text_page_info?.content_noencode || ''; break; default: break; } return `${title}\n\n${text.trim()}`; } /** * 渲染元数据 * @param cgiData */ function renderMetaInfo(cgiData: any): string { return `
原创 ${cgiData.author} ${cgiData.nick_name} ${cgiData.create_time} ${cgiData.ip_wording?.province_name}
`; } /** * 渲染底部 BottomBar * @param cgiData */ async function renderBottomBar(cgiData: any) { const metadata: ArticleMetadata = (await getMetadataCache(cgiData.link)) || { readNum: 0, oldLikeNum: 0, commentNum: 0, likeNum: 0, shareNum: 0, }; return `
${cgiData.nick_name}
`; }