feat(route): add OTOBANANA (#14160)

* feat(route): add OTOBANANA

* fix: live guid
This commit is contained in:
Tony 2024-01-02 10:55:39 +00:00 committed by GitHub
parent 8998f70f8a
commit c058d6a00c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 214 additions and 0 deletions

29
lib/v2/otobanana/cast.js Normal file
View File

@ -0,0 +1,29 @@
const got = require('@/utils/got');
const { apiBase, baseUrl, getUserInfo, renderCast } = require('./utils');
module.exports = async (ctx) => {
const { id } = ctx.params;
const userInfo = await getUserInfo(id, ctx.cache.tryGet);
const { data: castData } = await got(`${apiBase}/users/${id}/casts/`);
const casts = castData.results.map((item) => renderCast(item));
ctx.state.data = {
title: `${userInfo.name} (@${userInfo.username}) - 音声投稿 | OTOBANANA`,
description: userInfo.bio.replace(/\n/g, ' '),
link: `${baseUrl}/user/${id}`,
image: userInfo.avatar_url,
icon: userInfo.avatar_url,
logo: userInfo.avatar_url,
language: 'ja',
author: userInfo.name,
itunes_author: userInfo.name,
item: casts,
};
ctx.state.json = {
userInfo,
castData,
};
};

View File

@ -0,0 +1,29 @@
const got = require('@/utils/got');
const { apiBase, baseUrl, getUserInfo, renderLive } = require('./utils');
module.exports = async (ctx) => {
const { id } = ctx.params;
const userInfo = await getUserInfo(id, ctx.cache.tryGet);
const { data: liveData } = await got(`${apiBase}/users/${id}/livestreams/`);
const casts = liveData.results.map((item) => renderLive(item));
ctx.state.data = {
title: `${userInfo.name} (@${userInfo.username}) - ライブ配信 | OTOBANANA`,
description: userInfo.bio.replace(/\n/g, ' '),
link: `${baseUrl}/user/${id}`,
image: userInfo.avatar_url,
icon: userInfo.avatar_url,
logo: userInfo.avatar_url,
language: 'ja',
author: userInfo.name,
itunes_author: userInfo.name,
item: casts,
};
ctx.state.json = {
userInfo,
liveData,
};
};

View File

@ -0,0 +1,5 @@
module.exports = {
'/user/:id': ['TonyRL'],
'/user/:id/cast': ['TonyRL'],
'/user/:id/livestream': ['TonyRL'],
};

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

@ -0,0 +1,25 @@
module.exports = {
'otobanana.com': {
_name: 'OTOBANANA',
'.': [
{
title: 'Timeline タイムライン',
docs: 'https://docs.rsshub.app/multimedia#otobanana',
source: ['/user/:id'],
target: '/otobanana/user/:id',
},
{
title: 'Cast 音声投稿',
docs: 'https://docs.rsshub.app/multimedia#otobanana',
source: ['/user/:id/cast', '/user/:id'],
target: '/otobanana/user/:id/cast',
},
{
title: 'Livestream ライブ配信',
docs: 'https://docs.rsshub.app/multimedia#otobanana',
source: ['/user/:id/livestream', '/user/:id'],
target: '/otobanana/user/:id/livestream',
},
],
},
};

View File

@ -0,0 +1,5 @@
module.exports = (router) => {
router.get('/user/:id', require('./timeline'));
router.get('/user/:id/cast', require('./cast'));
router.get('/user/:id/livestream', require('./livestream'));
};

View File

@ -0,0 +1,11 @@
{{ if cast }}
<img src="{{ cast.thumbnail_url }}">
<br>
<audio controls>
<source src="{{ cast.audio_url }}" type="audio/x-m4a">
</audio>
<br>
💬 {{ cast.comment_count }} ❤️ {{ cast.like_count }} 🍌 {{ cast.gift_banana }} {{ cast.play_count }} 再生
<br>
{{@ cast.text.replace(/\n/g, '<br>') }}
{{ /if }}

View File

@ -0,0 +1,29 @@
const got = require('@/utils/got');
const { apiBase, baseUrl, getUserInfo, renderPost } = require('./utils');
module.exports = async (ctx) => {
const { id } = ctx.params;
const userInfo = await getUserInfo(id, ctx.cache.tryGet);
const { data: postData } = await got(`${apiBase}/users/${id}/posts/`);
const posts = postData.results.map((item) => renderPost(item));
ctx.state.data = {
title: `${userInfo.name} (@${userInfo.username}) - タイムライン | OTOBANANA`,
description: userInfo.bio.replace(/\n/g, ' '),
link: `${baseUrl}/user/${id}`,
image: userInfo.avatar_url,
icon: userInfo.avatar_url,
logo: userInfo.avatar_url,
language: 'ja',
author: userInfo.name,
itunes_author: userInfo.name,
item: posts,
};
ctx.state.json = {
userInfo,
postData,
};
};

67
lib/v2/otobanana/utils.js Normal file
View File

@ -0,0 +1,67 @@
const got = require('@/utils/got');
const { parseDate } = require('@/utils/parse-date');
const { art } = require('@/utils/render');
const { join } = require('path');
const domain = 'otobanana.com';
const apiBase = `https://api.${domain}`;
const baseUrl = `https://${domain}`;
const getUserInfo = (id, tryGet) =>
tryGet(`otobanana:user:${id}`, async () => {
const { data } = await got(`${apiBase}/users/${id}/`);
return data;
});
const renderCast = (cast) => ({
title: cast.title,
description: art(join(__dirname, 'templates/description.art'), { cast }),
pubDate: parseDate(cast.created_at),
link: `https://otobanana.com/cast/${cast.id}`,
author: `${cast.user.name} (@${cast.user.username})`,
itunes_item_image: cast.thumbnail_url,
itunes_duration: cast.duration_time,
enclosure_url: cast.audio_url,
enclosure_type: 'audio/x-m4a',
upvotes: cast.like_count,
comments: cast.comment_count,
});
const renderLive = (live) => ({
title: live.title,
description: live.is_open ? '配信中のライブ' : '終了しました',
pubDate: parseDate(live.created_at),
link: live.room_url,
guid: `${live.room_url}#${live.id}`,
author: `${live.user.name} (@${live.user.username})`,
upvotes: live.like_count,
comments: live.comment_count,
});
const renderPost = ({ id, type_label: type, cast, /** livestream */ message /** , event */ }) => {
switch (type) {
case 'cast':
return renderCast(cast);
case 'message':
return {
title: message.text.split('\n')[0],
description: message.text.replace(/\n/g, '<br>'),
pubDate: parseDate(message.created_at),
link: `https://otobanana.com/${type}/${id}`,
author: `${message.user.name} (@${message.user.username})`,
upvotes: message.like_count,
comments: message.comment_count,
};
default:
throw Error(`Unknown post type: ${type}`);
}
};
module.exports = {
apiBase,
baseUrl,
getUserInfo,
renderCast,
renderLive,
renderPost,
};

View File

@ -957,6 +957,20 @@ JavDB 有多个备用域名,本路由默认使用永久域名 `https://javdb.c
`day` 类型的关键词必须填写 **日期** ,按照示例写成形如 `20200730` 的格式
</Route>
## OTOBANANA {#otobanana}
### Timeline タイムライン {#otobanana-timeline-%E3%82%BF%E3%82%A4%E3%83%A0%E3%83%A9%E3%82%A4%E3%83%B3}
<Route author="TonyRL" example="/otobanana/user/cee16401-96b1-420f-8188-abd4d33093f1" path="/otobanana/user/:id" paramsDesc={['User ID, can be found in URL']} radar="1" supportPodcast="1" />
### Cast 音声投稿 {#otobanana-cast-yin-sheng-tou-gao}
<Route author="TonyRL" example="/otobanana/user/cee16401-96b1-420f-8188-abd4d33093f1/cast" path="/otobanana/user/:id/cast" paramsDesc={['User ID, can be found in URL']} radar="1" supportPodcast="1" />
### Livestream ライブ配信 {#otobanana-livestream-%E3%83%A9%E3%82%A4%E3%83%96-pei-xin}
<Route author="TonyRL" example="/otobanana/user/cee16401-96b1-420f-8188-abd4d33093f1/livestream" path="/otobanana/user/:id/livestream" paramsDesc={['User ID, can be found in URL']} radar="1" />
## PornHub {#pornhub}
### Category {#pornhub-category}