UNPKG

rsshub

Version:
177 lines (160 loc) 5.85 kB
import { Route } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import parser from '@/utils/rss-parser'; import * as cheerio from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import path from 'node:path'; const renderFanBox = (media) => art(path.join(__dirname, 'templates/fancybox.art'), { media, }); const renderDesc = (media, desc) => art(path.join(__dirname, 'templates/description.art'), { media: renderFanBox(media), desc, }); const fixFancybox = (element, $) => { const $e = $(element); const url = new URL($e.attr('href')); let video; if (url.hostname === 'videop.mingpao.com') { video = new URL(url.searchParams.get('file')); video.hostname = 'cfrvideo.mingpao.com'; // use cloudflare cdn video = video.href; } return { href: url.href, title: $e.attr('title'), video, }; }; export const route: Route = { path: '/:type?/:category?', name: '新聞', example: '/mingpao/ins/all', parameters: { type: { description: '新聞類型', default: 'ins', options: [ { value: 'ins', label: '即時新聞' }, { value: 'pns', label: '每日明報' }, ], }, category: '頻道,見下表', }, radar: [ { title: '即時新聞', source: ['news.mingpao.com/ins/:categoryName/section/:date/:category'], target: '/mingpao/ins/:category', }, { title: '每日明報', source: ['news.mingpao.com/pns/:categoryName/section/:date/:category'], target: '/mingpao/pns/:category', }, ], maintainers: ['TonyRL'], handler, description: `| category | 即時新聞頻道 | | -------- | ------------ | | all | 總目錄 | | s00001 | 港聞 | | s00002 | 經濟 | | s00003 | 地產 | | s00004 | 兩岸 | | s00005 | 國際 | | s00006 | 體育 | | s00007 | 娛樂 | | s00022 | 文摘 | | s00024 | 熱點 | | category | 每日明報頻道 | | -------- | ------------ | | s00001 | 要聞 | | s00002 | 港聞 | | s00003 | 社評 | | s00004 | 經濟 | | s00005 | 副刊 | | s00011 | 教育 | | s00012 | 觀點 | | s00013 | 中國 | | s00014 | 國際 | | s00015 | 體育 | | s00016 | 娛樂 | | s00017 | English | | s00018 | 作家專欄 |`, }; async function handler(ctx) { const type = ctx.req.param('type') ?? 'ins'; const category = ctx.req.param('category') ?? (type === 'ins' ? 'all' : 's00001'); const link = `https://news.mingpao.com/rss/${type}/${category}.xml`; const feed = await parser.parseURL(link); const items = await Promise.all( feed.items.map((item) => cache.tryGet(item.link, async () => { const response = await ofetch(item.link, { headers: { Referer: 'https://news.mingpao.com/', }, }); const $ = cheerio.load(response); const topVideo = $('#topvideo').length ? $('#topvideo iframe') .toArray() .map((e) => $(e).attr('href', $(e).attr('src'))) .map((e) => fixFancybox(e, $)) : []; const fancyboxImg = $('a.fancybox').length ? $('a.fancybox') : $('a.fancybox-buttons'); // remove unwanted elements $('div.ad300ins_m').remove(); $('div.clear, div.inReadLrecGroup, div.clr').remove(); $('div#ssm2').remove(); $('iframe').remove(); $('p[dir=ltr]').remove(); // extract categories item.category = item.categories; // fix fancybox image let fancybox = [...topVideo, ...fancyboxImg.toArray().map((e) => fixFancybox(e, $))]; const script = $('script') .toArray() .find((e) => $(e).text()?.includes("$('#lower').prepend('")); const lowerContent = script ? $(script) .text() ?.match(/\$\('#lower'\)\.prepend\('(.*)'\);/)?.[1] ?.replaceAll(/\\"/g, '"') : ''; if (lowerContent) { const $ = cheerio.load(lowerContent, null, false); fancybox = [ ...fancybox, ...$('a.fancybox') .toArray() .map((e) => fixFancybox(e, $)), ]; } // remove unwanted key value delete item.categories; delete item.content; delete item.contentSnippet; delete item.creator; delete item.isoDate; item.description = renderDesc(fancybox, $('.txt4').html() ?? $('.article_content.line_1_5em').html() ?? $('.txt3').html()); item.pubDate = parseDate(item.pubDate); item.guid = item.link.includes('?') ? item.link : item.link.slice(0, item.link.lastIndexOf('/')); return item; }) ) ); return { title: feed.title, link: feed.link, description: feed.description, item: items, image: feed.image.url, language: feed.language, }; }