feat(route): pikabu (#11929)

* feat(route): pikabu

* docs: add docs
This commit is contained in:
Tony 2023-02-22 02:34:10 -04:00 committed by GitHub
parent 0218338a3e
commit 8144c8a2a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 183 additions and 1 deletions

View File

@ -62,6 +62,20 @@ pageClass: routes
</RouteEn>
## Pikabu
### Community
<RouteEn author="TonyRL" example="/pikabu/community/real_true_story" path="/pikabu/community/:name" :paramsDesc="['Community name']" radar="1" rssbud="1"/>
### Tag
<RouteEn author="TonyRL" example="/pikabu/tag/Metallica" path="/pikabu/tag/:name" :paramsDesc="['Tag name']" radar="1" rssbud="1"/>
### User
<RouteEn author="TonyRL" example="/pikabu/user/@Eujenu" path="/pikabu/user/:name" :paramsDesc="['User name']" radar="1" rssbud="1"/>
## SCBOY forum
### Thread
@ -78,4 +92,4 @@ When accessing Joeyray's Bar, `SCBOY_BBS_TOKEN` needs to be filled in `environme
### forum
<RouteEn author="FeCCC" example="/zodgame/forum/13" path="/zodgame/forum/:fid?" :paramsDesc="['forum idcan be found in URL']" radar="1" rssbud="1" selfhost="1"/>
<RouteEn author="FeCCC" example="/zodgame/forum/13" path="/zodgame/forum/:fid?" :paramsDesc="['forum id, can be found in URL']" radar="1" rssbud="1" selfhost="1"/>

View File

@ -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,
};
};

View File

@ -0,0 +1,5 @@
module.exports = {
'/community/:name': ['TonyRL'],
'/tag/:name': ['TonyRL'],
'/user/:name': ['TonyRL'],
};

25
lib/v2/pikabu/radar.js Normal file
View File

@ -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',
},
],
},
};

4
lib/v2/pikabu/router.js Normal file
View File

@ -0,0 +1,4 @@
module.exports = (router) => {
router.get('/user/:name', require('./user'));
router.get('/:type/:name', require('./community'));
};

View File

@ -0,0 +1,8 @@
{{ if videoId }}
<iframe id="ytplayer" type="text/html" width="640" height="360" src="https://www.youtube-nocookie.com/embed/{{ videoId }}" frameborder="0" allowfullscreen></iframe>
{{ else if webm || mp4 }}
<video controls preload="none" poster="{{ preview }}" width="{{ width }}">
{{ if webm }}<source src="{{ webm }}" type="video/webm">{{ /if }}
{{ if mp4 }}<source src="{{ mp4 }}" type="video/mp4">{{ /if }}
</video>
{{ /if }}

46
lib/v2/pikabu/user.js Normal file
View File

@ -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,
};
};

43
lib/v2/pikabu/utils.js Normal file
View File

@ -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,
};