UNPKG

rsshub

Version:
193 lines (192 loc) 8 kB
import { t as parseDate } from "./parse-date-Cr0wQjV_.mjs"; import { t as cache_default } from "./cache-BkqOokyU.mjs"; import { t as got_default } from "./got-BTowW50G.mjs"; import dayjs from "dayjs"; import { jsx, jsxs } from "hono/jsx/jsx-runtime"; import { load } from "cheerio"; import { renderToString } from "hono/jsx/dom/server"; //#region lib/routes/chnmuseum/namespace.ts const namespace = { name: "National Museum Of China", url: "www.chnmuseum.cn", description: "中国国家博物馆(National Museum of China)位于北京市中心天安门广场东侧,东长安街南侧,与人民大会堂东西相对称,是一座系统展示中华民族文化历史的综合性博物馆,也是世界上最大的博物馆之一。", zh: { name: "中国国家博物馆" } }; //#endregion //#region lib/routes/chnmuseum/zl.tsx const titleTagMap = { zhanlanyugao: "正在展出", jbcl: "基本陈列", ztcl: "专题展览", lszl: "临时展览", "lszl/lswh": "临时展览 - 历史文化", "lszl/gjjl": "临时展览 - 国际交流", "lszl/zdzt": "临时展览 - 重大主题", "lszl/yscx": "临时展览 - 艺术创新", gjzl: "国家展览", gbxz: "国博巡展" }; const formatExhibitionDate = (dateStr) => { if (!dateStr) return; return dayjs(dateStr.replaceAll(/[年月/.]/g, "-").replaceAll("日", "")).format("YYYY-MM-DD"); }; const parseExhibitionDuration = (duration) => { if (!duration) return { startDate: void 0, endDate: void 0 }; const cleanStr = duration.replaceAll(/\s+/g, "").replaceAll(/([^)]*)/g, ""); const allDates = cleanStr.match(/(\d{4}[./年-]\d{1,2}[./月-]\d{1,2}日?)|(\d{1,2}[./月-]\d{1,2}日?)/g) || []; let startDateRaw; let endDateRaw; if (allDates.length >= 2) { startDateRaw = allDates[0]; let rawEnd = allDates[1]; if (!/\d{4}/.test(rawEnd) && startDateRaw) { const startYear = startDateRaw.match(/^\d{4}/); if (startYear?.[0]) rawEnd = `${startYear[0]}${startDateRaw[4]}${rawEnd}`; } endDateRaw = rawEnd; } else if (allDates.length === 1) if (cleanStr.includes("闭展")) endDateRaw = allDates[0]; else startDateRaw = allDates[0]; return { startDate: formatExhibitionDate(startDateRaw), endDate: formatExhibitionDate(endDateRaw) }; }; const resolveRouteConfig = (type, subtype, baseUrl) => { let url = `${baseUrl}/zl/`; let cleanType = ""; if (type) { cleanType = subtype ? `${type}/${subtype}` : type; url = `${baseUrl}/zl/${cleanType}/`; } return { cleanType, url, titleTag: titleTagMap[cleanType] || "展览" }; }; const buildItemLink = (rawLink, contextUrl, baseUrl) => rawLink ? new URL(rawLink, contextUrl).href : baseUrl; const buildExhibitionLink = (rawZtzl, itemLink, baseUrl) => rawZtzl ? new URL(rawZtzl, baseUrl).href : itemLink; const fetchTargetElements = async (cleanType, subtype, url, baseUrl) => { const items = []; const seenLinks = /* @__PURE__ */ new Set(); const extractItems = (html, contextUrl) => { const $ = load(html); const pageElements = $(["ul[id=\"div\"] > li > a", "li.scale_imgs > a"].join(", ")).not(".zl_lszl_list *").toArray(); for (const el of pageElements) { const $item = $(el).closest("li"); if ($item.length > 0) { const rawLink = $(el).attr("href") || ""; const rawZtzl = $(el).attr("ztzlurl") || ""; const itemLink = buildItemLink(rawLink, contextUrl, baseUrl); const exhibitionLink = buildExhibitionLink(rawZtzl, itemLink, baseUrl); if (exhibitionLink && !seenLinks.has(exhibitionLink)) { seenLinks.add(exhibitionLink); items.push({ $item, contextUrl, itemLink, exhibitionLink, rawZtzl }); } } } }; if (cleanType === "lszl") { const subKeys = Object.keys(titleTagMap).filter((key) => key.startsWith("lszl/")); const pagesData = await Promise.all(subKeys.map(async (subKey) => { const targetSubUrl = `${baseUrl}/zl/${subKey}/`; return { html: (await got_default(targetSubUrl)).data, targetSubUrl }; })); for (const page of pagesData) extractItems(page.html, page.targetSubUrl); } else extractItems((await got_default(url)).data, url); return items; }; const route = { path: "/zl/:type?/:subType?", categories: ["travel"], example: "/chnmuseum/zl/lszl/zdzt", parameters: { type: "Exhibition type, supported values: zhanlanyugao(正在展出)、jbcl(基本陈列)、ztcl(专题展览)、lszl(临时展览)、gjzl(国家展览)、gbxz(国博巡展). Default: All exhibitions.", subType: "subtype only works under type lszl(临时展览), supported values: zdzt(重大主题)、lswh(历史文化)、yscx(艺术创新)、gjjl(国际交流)" }, name: "Exhibitions", maintainers: ["magazian"], radar: [{ source: ["www.chnmuseum.cn/zl/"], target: "/zl" }], handler: async (ctx) => { const type = ctx.req.param("type"); const subtype = ctx.req.param("subType"); const museumName = namespace.zh?.name || namespace.name; const baseUrl = "https://www.chnmuseum.cn"; const { cleanType, url, titleTag } = resolveRouteConfig(type, subtype, baseUrl); const itemsToParse = await fetchTargetElements(cleanType, subtype, url, baseUrl); const list = (await Promise.all(itemsToParse.map(({ $item, contextUrl }) => { const aTag = $item.find("a").first(); const rawLink = aTag.attr("href") || ""; const itemLink = buildItemLink(rawLink, contextUrl, baseUrl); return cache_default.tryGet(itemLink, async () => { const rawZtzl = aTag.attr("ztzlurl") || ""; const exhibitionLink = buildExhibitionLink(rawZtzl, itemLink, baseUrl); const imgTag = $item.find("img"); let title = $item.find(".hide_title").text() || ""; const rawSrc = imgTag.attr("src"); const imgUrl = new URL(rawSrc, contextUrl).href; const hideBox = $item.find(".hide_box"); const hideBoxes = hideBox.toArray().map((_, i) => hideBox.eq(i)); const findValue = (keyword) => hideBoxes.find((box) => box.find("p").first().text().includes(keyword))?.find("p").last().text() ?? ""; const location = findValue("地点"); let fullDuration = findValue("展期"); if (!title || title.endsWith("...")) { const detailResponse = await got_default(itemLink); const $detail = load(detailResponse.data); title = $detail(".crumb_mod_box .title").text() || $detail("title").text().replace(/-?\s*中国国家博物馆/, ""); if (!fullDuration) { const textDuration = $detail("li, strong, p").toArray().map((el) => $detail(el).text().trim()).find((text) => text.startsWith("展期:") || text.includes("闭展"))?.replace("展期:", "").trim(); const regexDuration = detailResponse.data.match(/var\s+qtxszq\s*=\s*"(.*?)";/)?.[1]; fullDuration = textDuration || regexDuration || ""; } } const { startDate, endDate } = parseExhibitionDuration(fullDuration); const pubDate = startDate ? parseDate(startDate) : void 0; const description = renderToString(/* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("img", { src: imgUrl }), /* @__PURE__ */ jsx("br", {}), /* @__PURE__ */ jsxs("p", { children: [/* @__PURE__ */ jsx("b", { children: "地点:" }), location || "参考详情"] }), /* @__PURE__ */ jsxs("p", { children: [/* @__PURE__ */ jsx("b", { children: "开展:" }), startDate || "未定/常设"] }), /* @__PURE__ */ jsxs("p", { children: [/* @__PURE__ */ jsx("b", { children: "闭展:" }), endDate || "未定/常设"] }), fullDuration && /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsxs("small", { children: ["原始展期:", fullDuration] }) }) ] })); return { title, link: exhibitionLink, guid: itemLink, pubDate, description, _extra: { museumName, location, startDate, endDate } }; }); }))).filter((i) => i !== null); return { title: `${museumName} - ${titleTag}`, link: url, language: "zh-CN", item: list }; } }; //#endregion export { route };