diff --git a/lib/routes/theverge/index.ts b/lib/routes/theverge/index.ts index 607949f7fe47b0..6d9a6b725ed6d1 100644 --- a/lib/routes/theverge/index.ts +++ b/lib/routes/theverge/index.ts @@ -1,8 +1,13 @@ import { Route } from '@/types'; import cache from '@/utils/cache'; -import got from '@/utils/got'; +import ofetch from '@/utils/ofetch'; import parser from '@/utils/rss-parser'; import { load } from 'cheerio'; +import path from 'node:path'; +import { art } from '@/utils/render'; +import { getCurrentPath } from '@/utils/helpers'; + +const __dirname = getCurrentPath(import.meta.url); export const route: Route = { path: '/:hub?', @@ -22,7 +27,7 @@ export const route: Route = { source: ['theverge.com/:hub', 'theverge.com/'], }, ], - name: 'The Verge', + name: 'Category', maintainers: ['HenryQW', 'vbali'], handler, description: `| Hub | Hub name | @@ -43,6 +48,37 @@ export const route: Route = { Provides a better reading experience (full text articles) over the official one.`, }; +const renderBlock = (b) => { + switch (b.__typename) { + case 'CoreEmbedBlockType': + return b.embedHtml; + case 'CoreGalleryBlockType': + return b.images.map((i) => `
${i.alt}
${i.caption.html}
`).join(''); + case 'CoreHeadingBlockType': + return `${b.contents.html}`; + case 'CoreHTMLBlockType': + return b.markup; + case 'CoreImageBlockType': + return `
${b.alt}
${b.caption.html}
`; + case 'CoreListBlockType': + return `${b.ordered ? '
    ' : '
' : ''}`; + case 'CoreParagraphBlockType': + return b.contents.html; + case 'CorePullquoteBlockType': + return `
${b.contents.html}
`; + case 'CoreQuoteBlockType': + return `
${b.children.map((child) => renderBlock(child)).join('')}
`; + case 'CoreSeparatorBlockType': + return '
'; + case 'HighlightBlockType': + return b.children.map((c) => renderBlock(c)).join(''); + case 'MethodologyAccordionBlockType': + return `

${b.heading.html}

${b.sections.map((s) => `

${s.heading.html}

${s.content.html}`).join('')}`; + default: + throw new Error(`Unsupported block type: ${b.__typename}`); + } +}; + async function handler(ctx) { const link = ctx.req.param('hub') ? `https://www.theverge.com/${ctx.req.param('hub')}/rss/index.xml` : 'https://www.theverge.com/rss/index.xml'; @@ -51,73 +87,48 @@ async function handler(ctx) { const items = await Promise.all( feed.items.map((item) => cache.tryGet(item.link, async () => { - const response = await got(item.link); - - const $ = load(response.data); - - const content = $('#content'); - const body = $('.duet--article--article-body-component-container'); - - // 处理封面图片 - - const cover = $('meta[property="og:image"]'); - - if (cover.length > 0) { - $(``).insertBefore(body[0].childNodes[0]); - } + const response = await ofetch(item.link); - // 处理封面视频 - $('div.l-col__main > div.c-video-embed, div.c-entry-hero > div.c-video-embed').each((i, e) => { - const src = `https://volume.vox-cdn.com/embed/${e.attribs['data-volume-uuid']}?autoplay=false`; + const $ = load(response); - $(``).insertBefore(body[0].childNodes[0]); - }); + const nextData = JSON.parse($('script#__NEXT_DATA__').text()); + const node = nextData.props.pageProps.hydration.responses.find((x) => x.operationName === 'PostLayoutQuery' || x.operationName === 'StreamLayoutQuery').data.node; - // 处理封面视频 - $('div.l-col__main > div.c-video-embed--media iframe').each((i, e) => { - $(e).insertBefore(body[0].childNodes[0]); + let description = art(path.join(__dirname, 'templates/header.art'), { + featuredImage: node.featuredImage, + ledeMediaData: node.ledeMediaData, }); - // 处理文章图片 - content.find('figure.e-image').each((i, e) => { - let src, caption; - - // 处理 jpeg, png - if ($(e).find('picture > source').length > 0) { - src = $(e) - .find('picture > img')[0] - .attribs.srcset.match(/(?<=320w,).*?(?=520w)/g)[0] - .trim(); - } else if ($(e).find('img.c-dynamic-image').length > 0) { - // 处理 gif - src = $(e).find('span.e-image__image')[0].attribs['data-original']; - } - - // 处理 caption - if ($(e).find('span.e-image__meta').length > 0) { - caption = $(e).find('span.e-image__meta').text(); - } - - const figure = `
${caption ? `
${caption}
` : ''}
`; - - $(figure).insertBefore(e); - - $(e).remove(); - }); - - const lede = $('.duet--article--lede h2:first'); - if (lede[0]) { - lede.insertBefore(body[0].childNodes[0]); + description += node.blocks + .filter((b) => b.__typename !== 'NewsletterBlockType' && b.__typename !== 'RelatedPostsBlockType' && b.__typename !== 'ProductBlockType' && b.__typename !== 'TableOfContentsBlockType') + .map((b) => renderBlock(b)) + .join('

'); + + if (node.__typename === 'StreamResourceType') { + description += node.posts.edges + .map(({ node: n }) => { + let d = + `

${n.promo.headline || n.title}

` + + art(path.join(__dirname, 'templates/header.art'), { + ledeMediaData: n.ledeMediaData, + }); + switch (n.__typename) { + case 'PostResourceType': + d += n.excerpt.map((e) => e.contents.html).join('
'); + break; + case 'QuickPostResourceType': + d += n.blocks.map((b) => renderBlock(b)).join('
'); + break; + default: + break; + } + return d; + }) + .join('
'); } - // 移除无用 DOM - content.find('.duet--article--comments-join-the-conversation').remove(); - content.find('.duet--recirculation--related-list').remove(); - delete item.content; - delete item.contentSnippet; - delete item.isoDate; - - item.description = body.html(); + item.description = description; + item.category = node.categories; return item; }) diff --git a/lib/routes/theverge/templates/header.art b/lib/routes/theverge/templates/header.art new file mode 100644 index 00000000000000..e57a1dbf595134 --- /dev/null +++ b/lib/routes/theverge/templates/header.art @@ -0,0 +1,19 @@ +{{ if featuredImage }} +
+ {{ featuredImage.image.originalUrl.alt }} +
{{ featuredImage.image.title }}
+
+{{ /if }} + +{{ if ledeMediaData }} + {{ if ledeMediaData.__typename === 'LedeMediaEmbedType'}} + {{@ ledeMediaData.embedHtml }} + {{ else if ledeMediaData.__typename === 'LedeMediaImageType' && !featuredImage }} +
+ {{ ledeMediaData.image.title }} +
{{ ledeMediaData.image.credit.plaintext || ledeMediaData.image.title }}
+
+ {{ else if ledeMediaData.__typename === 'LedeMediaVideoType'}} + + {{ /if }} +{{ /if }}