From 8144c8a2a54025f4670435ba57d3a746f5cd9803 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 22 Feb 2023 02:34:10 -0400 Subject: [PATCH] feat(route): pikabu (#11929) * feat(route): pikabu * docs: add docs --- docs/en/bbs.md | 16 ++++++++++- lib/v2/pikabu/community.js | 37 +++++++++++++++++++++++++ lib/v2/pikabu/maintainer.js | 5 ++++ lib/v2/pikabu/radar.js | 25 +++++++++++++++++ lib/v2/pikabu/router.js | 4 +++ lib/v2/pikabu/templates/video.art | 8 ++++++ lib/v2/pikabu/user.js | 46 +++++++++++++++++++++++++++++++ lib/v2/pikabu/utils.js | 43 +++++++++++++++++++++++++++++ 8 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 lib/v2/pikabu/community.js create mode 100644 lib/v2/pikabu/maintainer.js create mode 100644 lib/v2/pikabu/radar.js create mode 100644 lib/v2/pikabu/router.js create mode 100644 lib/v2/pikabu/templates/video.art create mode 100644 lib/v2/pikabu/user.js create mode 100644 lib/v2/pikabu/utils.js diff --git a/docs/en/bbs.md b/docs/en/bbs.md index f2deab5d1..fff889c13 100644 --- a/docs/en/bbs.md +++ b/docs/en/bbs.md @@ -62,6 +62,20 @@ pageClass: routes +## Pikabu + +### Community + + + +### Tag + + + +### User + + + ## SCBOY forum ### Thread @@ -78,4 +92,4 @@ When accessing Joeyray's Bar, `SCBOY_BBS_TOKEN` needs to be filled in `environme ### forum - + diff --git a/lib/v2/pikabu/community.js b/lib/v2/pikabu/community.js new file mode 100644 index 000000000..45c71feae --- /dev/null +++ b/lib/v2/pikabu/community.js @@ -0,0 +1,37 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const { baseUrl, fixImage, fixVideo } = require('./utils'); + +module.exports = async (ctx) => { + const { type, name, sort = 'new' } = ctx.params; + const sortString = sort === 'default' || type === 'tag' ? '' : `/${sort}`; + const { data: response } = await got(`${baseUrl}/ajax/${type}/${name}${sortString}`); + + const items = response.data.stories.map((story) => { + const $ = cheerio.load(story.html, null, false); + const data = JSON.parse($('script[type="application/ld+json"]').text()); + + const content = $('.story__main'); + + fixImage(content); + content.find('.player').each((_, elem) => { + elem = $(elem); + fixVideo(elem); + }); + return { + title: data.name, + description: content.find('.story__content-inner').html(), + pubDate: parseDate(data.dateCreated), + author: data.author.name, + link: data.url, + }; + }); + + ctx.state.data = { + title: response.data.title, + link: `${baseUrl}/${type}/${name}`, + language: 'ru-RU', + item: items, + }; +}; diff --git a/lib/v2/pikabu/maintainer.js b/lib/v2/pikabu/maintainer.js new file mode 100644 index 000000000..036479c63 --- /dev/null +++ b/lib/v2/pikabu/maintainer.js @@ -0,0 +1,5 @@ +module.exports = { + '/community/:name': ['TonyRL'], + '/tag/:name': ['TonyRL'], + '/user/:name': ['TonyRL'], +}; diff --git a/lib/v2/pikabu/radar.js b/lib/v2/pikabu/radar.js new file mode 100644 index 000000000..4a72d4726 --- /dev/null +++ b/lib/v2/pikabu/radar.js @@ -0,0 +1,25 @@ +module.exports = { + 'pikabu.ru': { + _name: 'Pikabu', + '.': [ + { + title: 'Community', + docs: 'https://docs.rsshub.app/en/bbs.html#pikabu', + source: ['/community/:name'], + target: '/pikabu/community/:name', + }, + { + title: 'Tag', + docs: 'https://docs.rsshub.app/en/bbs.html#pikabu', + source: ['/tag/:name'], + target: '/pikabu/tag/:name', + }, + { + title: 'User', + docs: 'https://docs.rsshub.app/en/bbs.html#pikabu', + source: ['/:name'], + target: '/pikabu/user/:name', + }, + ], + }, +}; diff --git a/lib/v2/pikabu/router.js b/lib/v2/pikabu/router.js new file mode 100644 index 000000000..0483a08e8 --- /dev/null +++ b/lib/v2/pikabu/router.js @@ -0,0 +1,4 @@ +module.exports = (router) => { + router.get('/user/:name', require('./user')); + router.get('/:type/:name', require('./community')); +}; diff --git a/lib/v2/pikabu/templates/video.art b/lib/v2/pikabu/templates/video.art new file mode 100644 index 000000000..f76b0437e --- /dev/null +++ b/lib/v2/pikabu/templates/video.art @@ -0,0 +1,8 @@ +{{ if videoId }} + +{{ else if webm || mp4 }} + +{{ /if }} diff --git a/lib/v2/pikabu/user.js b/lib/v2/pikabu/user.js new file mode 100644 index 000000000..25fbbd6fc --- /dev/null +++ b/lib/v2/pikabu/user.js @@ -0,0 +1,46 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const iconv = require('iconv-lite'); +const { parseDate } = require('@/utils/parse-date'); +const { baseUrl, fixImage, fixVideo } = require('./utils'); + +module.exports = async (ctx) => { + const { name } = ctx.params; + const link = `${baseUrl}/${name}`; + const response = await got(link, { + responseType: 'buffer', + }); + + const charset = response.headers['content-type'].match(/charset=([\w-]+)/)[1]; // windows-1251 + const $ = cheerio.load(iconv.decode(response.data, charset)); + + const items = $('.story__main') + .not('.story__placeholder') + .toArray() + .map((story) => { + story = $(story); + + const a = story.find('.story__title a'); + fixImage(story); + story.find('.player').each((_, elem) => { + elem = $(elem); + fixVideo(elem); + }); + return { + title: a.text(), + link: a.attr('href'), + pubDate: parseDate(story.find('time').attr('datetime')), + description: story.find('.story__content-inner').html(), + author: story.find('.user__nick').text(), + }; + }); + + ctx.state.data = { + title: $('meta[property="og:title"]').attr('content'), + description: $('.profile__user-about-content').text(), + image: $('meta[property="og:image"]').attr('content'), + language: 'ru-RU', + link, + item: items, + }; +}; diff --git a/lib/v2/pikabu/utils.js b/lib/v2/pikabu/utils.js new file mode 100644 index 000000000..85b8a87c8 --- /dev/null +++ b/lib/v2/pikabu/utils.js @@ -0,0 +1,43 @@ +const { art } = require('@/utils/render'); +const path = require('path'); + +const baseUrl = 'https://pikabu.ru'; + +const fixImage = (element) => { + element.find('.story-image__stretch').remove(); + element.find('.story-image__image').each((_, img) => { + if (img.attribs['data-src'] && img.attribs['data-large-image']) { + img.attribs.src = img.attribs['data-large-image']; + delete img.attribs['data-src']; + delete img.attribs['data-large-image']; + } + }); +}; + +const fixVideo = (element) => { + const preview = element + .find('.player__preview') + .attr('style') + .match(/url\((.+)\);/)[1]; + const dataType = element.attr('data-type'); + let videoHtml = ''; + + if (dataType === 'video') { + const videoId = element.attr('data-source').match(/\/embed\/(.+)$/)[1]; + videoHtml = art(path.join(__dirname, 'templates/video.art'), { videoId }); + } else if (dataType === 'video-file') { + const width = element.find('.player__svg-stretch').attr('width'); + const mp4 = `${element.attr('data-source')}.mp4`; + const webm = element.attr('data-webm'); + videoHtml = art(path.join(__dirname, 'templates/video.art'), { preview, width, mp4, webm }); + } else { + throw Error(`Unknown video type: ${dataType}`); + } + element.replaceWith(videoHtml); +}; + +module.exports = { + baseUrl, + fixImage, + fixVideo, +};