UNPKG

rsshub

Version:
299 lines (296 loc) • 10.8 kB
import { n as init_esm_shims, t as __dirname } from "./esm-shims-CzJ_djXG.mjs"; import { t as ofetch_default } from "./ofetch-BIyrKU3Y.mjs"; import { t as parseDate } from "./parse-date-BrP7mxXf.mjs"; import { t as art } from "./render-BQo6B4tL.mjs"; import path from "node:path"; import { load } from "cheerio"; //#region lib/routes/gcores/parser.ts init_esm_shims(); const imageBaseUrl$1 = "https://image.gcores.com"; const STYLES = { BOLD: { fontWeight: "bold" }, CODE: { fontFamily: "monospace", wordWrap: "break-word" }, ITALIC: { fontStyle: "italic" }, STRIKETHROUGH: { textDecoration: "line-through" }, UNDERLINE: { textDecoration: "underline" } }; const BLOCK_TYPES = { "header-one": { element: "h1" }, "header-two": { element: "h2" }, "header-three": { element: "h3" }, "header-four": { element: "h4" }, "header-five": { element: "h5" }, "header-six": { element: "h6" }, "unordered-list-item": { element: "li", parentElement: "ul" }, "ordered-list-item": { element: "li", parentElement: "ol" }, blockquote: { element: "blockquote" }, atomic: { element: void 0 }, "code-block": { element: "pre" }, unstyled: { element: "p" } }; /** * Creates a styled HTML fragment for a given text and style object. * @param text - The text content. * @param style - CSS styles as key-value pairs. * @returns HTML string with applied styles. */ const createStyledFragment = (text, style) => { return `<span style="${Object.entries(style).map(([key, value]) => `${key.replaceAll(/([A-Z])/g, "-$1").toLowerCase()}: ${value}`).join("; ")}">${text}</span>`; }; /** * Creates an HTML element for a given entity. * @param entity - The entity to create an element for. * @param text - The text content of the entity. * @returns HTML element string. */ const createEntityElement = (entity, text) => { switch (entity.type) { case "EMBED": return entity.data.content.startsWith("http") ? `<a href="${entity.data.content}" target="_blank">${entity.data.content}</a>` : entity.data.content; case "IMAGE": return art(path.join(__dirname, "templates/description-57cbf0bd.art"), { images: entity.data.path ? [{ src: new URL(entity.data.path, imageBaseUrl$1).href, alt: entity.data.caption, width: entity.data.width, height: entity.data.height }] : void 0 }).replaceAll("\n", ""); case "GALLERY": if (!entity.data.images || !Array.isArray(entity.data.images)) return ""; return art(path.join(__dirname, "templates/description-57cbf0bd.art"), { images: entity.data.images.map((image) => ({ src: new URL(image.path, imageBaseUrl$1).href, alt: image.caption ?? entity.data.caption, width: image.width, height: image.height })) }).replaceAll("\n", ""); case "LINK": return `<a href="${entity.data.url}" target="_blank">${text}</a>`; case "WIDGET": return `<a href="${entity.data.url}" target="_blank">${entity.data.title}</a>`; default: return ""; } }; /** * Parses a block into an HTML string with applied styles and entities. * @param block - The block to parse. * @param entityMap - The entity map. * @returns HTML string representing the block. */ const parseBlock = (block, entityMap) => { const blockType = BLOCK_TYPES[block.type]; if (!blockType) return ""; const { text, inlineStyleRanges, entityRanges } = block; const combinedRanges = []; for (const range of inlineStyleRanges) combinedRanges.push({ ...range, styles: [STYLES[range.style]], entity: null }); for (const range of entityRanges) combinedRanges.push({ ...range, styles: [], entity: entityMap[range.key] }); combinedRanges.sort((a, b) => a.offset - b.offset); const groupedRangesMap = /* @__PURE__ */ new Map(); const groupedRanges = []; for (const range of combinedRanges) { const rangeKey = `${range.offset}-${range.length}`; let existingRange = groupedRangesMap.get(rangeKey); if (!existingRange) { existingRange = { offset: range.offset, length: range.length, styles: [], entities: [] }; groupedRangesMap.set(rangeKey, existingRange); groupedRanges.push(existingRange); } if (range.styles.length > 0) existingRange.styles.push(...range.styles); if (range.entity) existingRange.entities.push(range.entity); } const resultParts = []; let lastOffset = 0; for (const range of groupedRanges) { resultParts.push(text.substring(lastOffset, range.offset)); let styledText = text.substring(range.offset, range.offset + range.length); if (range.styles.length > 0) { const combinedStyle = {}; for (const style of range.styles) for (const [key, value] of Object.entries(style)) combinedStyle[key] = value; styledText = createStyledFragment(styledText, combinedStyle); } if (range.entities.length > 0) for (const entity of range.entities) styledText = createEntityElement(entity, styledText); resultParts.push(styledText); lastOffset = range.offset + range.length; } resultParts.push(text.slice(lastOffset)); return `${blockType.element ? `<${blockType.element}>` : ""}${resultParts.join("").replaceAll("\n", "<br>")}${blockType.element ? `</${blockType.element}>` : ""}`; }; /** * Parses a Content object into an HTML string. * @param content - The Content object to parse. * @returns HTML string representing the content. */ const parseContent = (content) => { const { blocks, entityMap } = content; if (!blocks || blocks.length === 0) return ""; const htmlParts = []; let currentParent = void 0; let parentContent = []; for (const block of blocks) { const blockType = BLOCK_TYPES[block.type]; if (!blockType) continue; const parsedBlock = parseBlock(block, entityMap); if (blockType.parentElement) if (currentParent === blockType.parentElement) parentContent.push(parsedBlock); else { if (currentParent) htmlParts.push(`<${currentParent}>${parentContent.join("")}</${currentParent}>`); currentParent = blockType.parentElement; parentContent = [parsedBlock]; } else { if (currentParent) { htmlParts.push(`<${currentParent}>${parentContent.join("")}</${currentParent}>`); currentParent = void 0; parentContent = []; } htmlParts.push(parsedBlock); } } if (currentParent) htmlParts.push(`<${currentParent}>${parentContent.join("")}</${currentParent}>`); return htmlParts.join(""); }; //#endregion //#region lib/routes/gcores/util.ts init_esm_shims(); const baseUrl = "https://www.gcores.com"; const imageBaseUrl = "https://image.gcores.com"; const audioBaseUrl = "https://alioss.gcores.com"; const types = new Set([ "radios", "articles", "news", "videos", "talks" ]); const processItems = async (limit, query, apiUrl, targetUrl) => { const response = await ofetch_default(apiUrl, { query: query ?? { "page[limit]": limit, sort: "-published-at", include: "category,user,media", "filter[list-all]": 1 } }); const $ = load(await ofetch_default(targetUrl)); const language = $("html").attr("lang") ?? "zh-CN"; const included = response.included; const data = [...response.data, ...included].filter((item) => types.has(item.type)); let items = []; items = data?.slice(0, limit).map((item) => { const attributes = item.attributes; const relationships = item.relationships; const title$1 = attributes.title; const pubDate = attributes["published-at"]; const linkUrl = `${item.type}/${item.id}`; const categories = [ relationships?.category?.data, relationships?.tag?.data, relationships?.topic?.data ].filter(Boolean).map((obj) => { const attributes$1 = included.find((i) => i.type === obj.type && i.id === obj.id)?.attributes; return attributes$1?.name ?? attributes$1?.title; }).filter(Boolean); const authorObj = relationships?.user?.data; const authorIncluded = authorObj ? included.find((i) => i.type === authorObj.type && i.id === authorObj.id) : void 0; const authors = authorIncluded ? [{ name: authorIncluded.attributes?.nickname, url: authorIncluded.id ? new URL(`${authorObj.type}/${authorIncluded.id}`, baseUrl).href : void 0, avatar: authorIncluded.thumb ? new URL(authorIncluded.thumb, imageBaseUrl).href : void 0 }] : void 0; const guid = `gcores-${item.id}`; const image = attributes.cover ?? attributes.thumb ? new URL(attributes.cover ?? attributes.thumb, imageBaseUrl).href : void 0; const updated = pubDate; let processedItem = { title: title$1, pubDate: pubDate ? parseDate(pubDate) : void 0, link: new URL(linkUrl, baseUrl).href, category: categories, author: authors, guid, id: guid, image, banner: image, updated: updated ? parseDate(updated) : void 0, language }; let enclosureUrl; let enclosureType; const mediaAttrs = included.find((i) => i.id === relationships.media?.data?.id)?.attributes; if (attributes["speech-path"]) { enclosureUrl = new URL(`uploads/audio/${attributes["speech-path"]}`, audioBaseUrl).href; enclosureType = `audio/${enclosureUrl?.split(/\./).pop()}`; } else if (mediaAttrs) { if (mediaAttrs.audio) { enclosureUrl = mediaAttrs.audio; enclosureType = `audio/${enclosureUrl?.split(/\./).pop()}`; } else if (mediaAttrs["original-src"]) { enclosureUrl = mediaAttrs["original-src"]; enclosureType = `video/${enclosureUrl?.split(/\?/).pop() ? /^id=\d+$/.test(enclosureUrl?.split(/\?/).pop()) ? "taptap" : enclosureUrl?.split(/\./).pop() : ""}`; } } if (enclosureUrl) { const enclosureLength = attributes.duration ? Number(attributes.duration) : 0; processedItem = { ...processedItem, enclosure_url: enclosureUrl, enclosure_type: enclosureType, enclosure_title: title$1, enclosure_length: enclosureLength, itunes_duration: enclosureLength, itunes_item_image: image }; } const description = art(path.join(__dirname, "templates/description-57cbf0bd.art"), { images: attributes.cover ? [{ src: new URL(attributes.cover, imageBaseUrl).href, alt: title$1 }] : void 0, audios: enclosureType?.startsWith("audio") && enclosureUrl ? [{ src: enclosureUrl, type: enclosureType }] : void 0, videos: enclosureType?.startsWith("video") && enclosureUrl ? [{ src: enclosureUrl, type: enclosureType }] : void 0, intro: attributes.desc || attributes.excerpt, description: attributes.content ? parseContent(JSON.parse(attributes.content)) : void 0 }); processedItem = { ...processedItem, title: title$1 ?? $(description).text(), description, content: { html: description, text: description } }; return processedItem; }); const title = $("title").text(); return { title, description: $("meta[name=\"description\"]").attr("content"), link: targetUrl, item: items, allowEmpty: true, author: title.split(/\|/).pop()?.trim(), language, id: $("meta[property=\"og:url\"]").attr("content") }; }; //#endregion export { processItems as n, baseUrl as t };