UNPKG

rsshub

Version:
117 lines (98 loc) 4.19 kB
import type { DataItem, Route } from '@/types'; import cache from '@/utils/cache'; import crypto from 'node:crypto'; import ofetch from '@/utils/ofetch'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; import { config } from '@/config'; const qingtingId = config.qingting.id ?? ''; export const route: Route = { path: '/podcast/:id', categories: ['multimedia'], example: '/qingting/podcast/293411', parameters: { id: '专辑id, 可在专辑页 URL 中找到' }, features: { supportPodcast: true, requireConfig: [ { name: 'QINGTING_ID', optional: true, description: '用户id, 部分专辑需要会员身份,用户id可以通过从网页端登录蜻蜓fm后使用开发者工具,在控制台中运行JSON.parse(localStorage.getItem("user")).qingting_id获取', }, ], }, radar: [ { source: ['qingting.fm/channels/:id'], }, ], name: '播客', maintainers: ['RookieZoe', 'huyyi', 'pseudoyu'], handler, description: `获取的播放 URL 有效期只有 1 天,需要开启播客 APP 的自动下载功能。`, }; function getMediaUrl(channelId: string, mediaId: string) { const path = `/audiostream/redirect/${channelId}/${mediaId}?access_token=&device_id=MOBILESITE&qingting_id=${qingtingId}&t=${Date.now()}`; const sign = crypto.createHmac('md5', 'fpMn12&38f_2e').update(path).digest('hex').toString(); return `https://audio.qingting.fm${path}&sign=${sign}`; } async function handler(ctx) { const channelId = ctx.req.param('id'); const pageSize = Number(ctx.req.query('limit')) || 30; const channelUrl = `https://i.qingting.fm/capi/v3/channel/${channelId}`; const response = await ofetch(channelUrl, { headers: { Referer: 'https://www.qingting.fm/', }, }); const title = response.data.title; const channel_img = response.data.thumbs['400_thumb']; const authors = response.data.podcasters.map((author) => author.nick_name).join(','); const desc = response.data.description; const programUrl = `https://i.qingting.fm/capi/channel/${channelId}/programs/${response.data.v}?curpage=1&pagesize=${pageSize}&order=asc`; const { data: { programs }, } = await ofetch(programUrl, { headers: { Referer: 'https://www.qingting.fm/', }, }); const { data: channelInfo } = await ofetch(`https://i.qingting.fm/capi/v3/channel/${channelId}?user_id=${qingtingId}`); const isCharged = channelInfo.purchase?.item_type !== 0; const isPaid = channelInfo.user_relevance?.sale_status === 'paid'; const resultItems = await Promise.all( programs.map(async (item) => { const data = (await cache.tryGet(`qingting:podcast:${channelId}:${item.id}`, async () => { const link = `https://www.qingting.fm/channels/${channelId}/programs/${item.id}/`; const detailRes = await ofetch(link, { headers: { Referer: 'https://www.qingting.fm/', }, }); const detail = JSON.parse(detailRes.match(/},"program":(.*?),"plist":/)[1]); const rssItem = { title: item.title, link, itunes_item_image: item.cover, itunes_duration: item.duration, pubDate: timezone(parseDate(item.update_time), +8), description: detail.richtext, }; return rssItem; })) as DataItem; if (!isCharged || isPaid || item.isfree) { data.enclosure_url = getMediaUrl(channelId, item.id); data.enclosure_type = 'audio/x-m4a'; } return data; }) ); return { title: `${title} - 蜻蜓FM`, description: desc, itunes_author: authors, image: channel_img, link: `https://www.qingting.fm/channels/${channelId}`, item: resultItems, }; }