RSSHub/lib/routes/tingtingfm/program.ts

133 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
import cache from '@/utils/cache';
import got from '@/utils/got';
import { config } from '@/config';
import { art } from '@/utils/render';
import path from 'node:path';
import { parseDate } from '@/utils/parse-date';
import { getClientVal, sign } from './utils';
export const route: Route = {
path: '/program/:programId',
categories: ['multimedia'],
example: '/tingtingfm/program/M7VJv6Jj4R',
parameters: { programId: '节目 ID可以在 URL 中找到' },
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: true,
supportScihub: false,
},
radar: [
{
source: ['mobile.tingtingfm.com/v3/program/:programId'],
},
],
name: '节目',
maintainers: ['TonyRL'],
handler,
};
async function handler(ctx) {
const programId = ctx.req.param('programId');
const apiBaseUrl = 'https://api-v3.tingtingfm.com';
const mobileBaseUrl = 'https://mobile.tingtingfm.com';
const params = {
version: 'h5_5.16',
client: getClientVal(30),
h_program_id: programId,
};
const radioInfo = await cache.tryGet(`tingtingfm:program:${programId}`, async () => {
// the double slash here and below is not a typo
const { data: response } = await got.post(`${apiBaseUrl}//broadcast/get_program_v3_8`, {
searchParams: {
...params,
api_sign: sign(params),
},
});
if (response.errno !== 0) {
throw new Error(response.error);
}
return response.data.info;
});
const latestAudio = await cache.tryGet(
`tingtingfm:audio_list:${programId}`,
async () => {
const { data: response } = await got.post(`${apiBaseUrl}//broadcast/get_program_audio_list`, {
searchParams: {
...params,
api_sign: sign(params),
},
});
if (response.errno !== 0) {
throw new Error(response.error);
}
return response.data[0];
},
config.cache.routeExpire,
false
);
const audioList = await cache.tryGet(
`tingtingfm:play_audio:${programId}`,
async () => {
const playAudioParams = {
// remove h_program_id from params
...Object.fromEntries(Object.entries(params).filter(([key]) => key !== 'h_program_id')),
type: '',
sort: '-1',
audio_id: latestAudio.h_audio_id,
};
const { data: response } = await got.post(`${apiBaseUrl}//albumaudio/play_audio`, {
searchParams: {
...playAudioParams,
api_sign: sign(playAudioParams),
},
});
if (response.errno !== 0) {
throw new Error(response.error);
}
return { radioCover: response.data.info.radio_cover, list: response.data.list };
},
config.cache.routeExpire,
false
);
const { radioCover, list } = audioList;
const items = list.map((audio) => ({
title: audio.title,
link: `${mobileBaseUrl}/v3/vod/2/${audio.h_audio_id}`,
description: art(path.join(__dirname, 'templates/audio.art'), {
url: audio.play_url,
}),
pubDate: parseDate(audio.add_time, 'X'),
itunes_item_image: radioCover,
itunes_duration: audio.duration,
enclosure_url: audio.play_url,
enclosure_type: 'audio/x-m4a',
}));
return {
title: `${radioInfo.title} - ${radioInfo.belong_radio}${radioInfo.belong_fm}`,
description: radioInfo.description,
link: `${mobileBaseUrl}/v3/program/${programId}`,
image: radioInfo.cover.split('?x-oss')[0],
itunes_author: radioInfo.anchor.join(', '),
itunes_category: radioInfo.category,
itunes_explicit: false,
item: items,
};
}