diff --git a/lib/v2/tvtropes/featured.js b/lib/v2/tvtropes/featured.js new file mode 100644 index 000000000..d765e2d50 --- /dev/null +++ b/lib/v2/tvtropes/featured.js @@ -0,0 +1,73 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { art } = require('@/utils/render'); +const path = require('path'); + +const categories = { + today: 'left', + newest: 'right', +}; + +module.exports = async (ctx) => { + const { category = 'today' } = ctx.params; + + const rootUrl = 'https://tvtropes.org'; + + const { data: response } = await got(rootUrl); + + const $ = cheerio.load(response); + + const item = $(`div#featured-tropes div.${categories[category]}`); + + const link = new URL(item.find('h2.entry-title a').prop('href'), rootUrl).href; + + const { data: detailResponse } = await got(link); + + const content = cheerio.load(detailResponse); + + content('div.folderlabel').remove(); + + content('div.lazy_load_img_box').each((_, el) => { + el = content(el); + + const image = el.find('img'); + + el.replaceWith( + art(path.join(__dirname, 'templates/description.art'), { + images: [ + { + src: image.prop('src'), + alt: image.prop('alt'), + width: image.prop('width'), + height: image.prop('height'), + }, + ], + }) + ); + }); + + const items = [ + { + title: item.find('h2.entry-title').text(), + link, + description: content('div#main-article').html(), + }, + ]; + + const image = new URL($('img.logo-big').prop('src'), rootUrl).href; + const icon = $('link[rel="shortcut icon"]').prop('href'); + + ctx.state.data = { + item: items, + title: `${$('title').text()} - ${item.find('span.box-title').text()}`, + link: rootUrl, + description: $('meta[name="description"]').prop('content'), + language: $('html').prop('lang'), + image, + icon, + logo: icon, + subtitle: $('meta[property="og:title"]').prop('content'), + author: $('meta[property="og:site_name"]').prop('content'), + allowEmpty: true, + }; +}; diff --git a/lib/v2/tvtropes/maintainer.js b/lib/v2/tvtropes/maintainer.js new file mode 100644 index 000000000..d5f298526 --- /dev/null +++ b/lib/v2/tvtropes/maintainer.js @@ -0,0 +1,3 @@ +module.exports = { + '/featured/:category?': ['nczitzk'], +}; diff --git a/lib/v2/tvtropes/radar.js b/lib/v2/tvtropes/radar.js new file mode 100644 index 000000000..85e853eab --- /dev/null +++ b/lib/v2/tvtropes/radar.js @@ -0,0 +1,19 @@ +module.exports = { + 'tvtropes.org': { + _name: 'TV Tropes', + '.': [ + { + title: "Featured - Today's Featured Trope", + docs: 'https://docs.rsshub.app/routes/other#tvtropes-featured', + source: ['/'], + target: '/tvtropes/featured/today', + }, + { + title: 'Featured - Newest Trope', + docs: 'https://docs.rsshub.app/routes/other#tvtropes-featured', + source: ['/'], + target: '/tvtropes/featured/newest', + }, + ], + }, +}; diff --git a/lib/v2/tvtropes/router.js b/lib/v2/tvtropes/router.js new file mode 100644 index 000000000..6c4235863 --- /dev/null +++ b/lib/v2/tvtropes/router.js @@ -0,0 +1,3 @@ +module.exports = (router) => { + router.get('/featured/:category?', require('./featured')); +}; diff --git a/lib/v2/tvtropes/templates/description.art b/lib/v2/tvtropes/templates/description.art new file mode 100644 index 000000000..48df396d7 --- /dev/null +++ b/lib/v2/tvtropes/templates/description.art @@ -0,0 +1,23 @@ +{{ if images }} + {{ each images image }} + {{ if image?.src }} +
+ {{ image.alt }} +
+ {{ /if }} + {{ /each }} +{{ /if }} + +{{ if description }} + {{@ description }} +{{ /if }} \ No newline at end of file diff --git a/website/docs/routes/other.mdx b/website/docs/routes/other.mdx index 9979244a9..53d4a990d 100644 --- a/website/docs/routes/other.mdx +++ b/website/docs/routes/other.mdx @@ -528,6 +528,16 @@ Specify options (in the format of query string) in parameter `routeParams` param | `routeParams` | `title=Example` | +## TV Tropes {#tv-tropes} + +### Featured {#tv-tropes-featured} + + + | Today's Featured Trope | Newest Trope | + | ---------------------- | ------------ | + | today | newest | + + ## Urban Dictionary {#urban-dictionary} ### Random words {#urban-dictionary-random-words}