mirror of
https://github.com/DIYgod/RSSHub.git
synced 2026-09-01 14:57:14 +08:00
feat(route): support podwise.ai (#15670)
* feature: podwise.ai * refactor: simplify item, remove some unnessary code * feat: add podcast feed info * chore: remove comment * refactor: use import instead of require * convert enclosure type
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import { Route } from '@/types';
|
||||
import { load } from 'cheerio';
|
||||
import ofetch from '@/utils/ofetch'; // 统一使用的请求库
|
||||
|
||||
export const route: Route = {
|
||||
path: '/explore',
|
||||
categories: ['multimedia'],
|
||||
example: '/podwise/explore',
|
||||
radar: [
|
||||
{
|
||||
source: ['podwise.ai', 'podwise.ai/explore'],
|
||||
},
|
||||
],
|
||||
name: 'Collections',
|
||||
maintainers: ['lyling'],
|
||||
handler: async () => {
|
||||
const link = 'https://podwise.ai/explore';
|
||||
const response = await ofetch(link);
|
||||
const $ = load(response);
|
||||
const content = $('#navigator').next();
|
||||
// header/[div => content]/footer, content p(2)
|
||||
const collectinDescription = content.find('p').eq(1).text();
|
||||
|
||||
const items = content
|
||||
.find('.group')
|
||||
.toArray()
|
||||
.map((item) => {
|
||||
item = $(item);
|
||||
const title = item.find('a').first().text();
|
||||
const link = item.find('a').first().attr('href');
|
||||
const description = item.find('p').first().text();
|
||||
return {
|
||||
title,
|
||||
link: `https://podwise.ai${link}`,
|
||||
description,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
title: $('title').text(),
|
||||
description: collectinDescription,
|
||||
item: items,
|
||||
link,
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,119 @@
|
||||
import { Route } from '@/types';
|
||||
import { load } from 'cheerio';
|
||||
import ofetch from '@/utils/ofetch'; // 统一使用的请求库
|
||||
import cache from '@/utils/cache';
|
||||
import { parseDate } from '@/utils/parse-date';
|
||||
import timezone from '@/utils/timezone';
|
||||
import dayjs from 'dayjs';
|
||||
import duration from 'dayjs/plugin/duration';
|
||||
dayjs.extend(duration);
|
||||
|
||||
export const route: Route = {
|
||||
path: '/explore/:type',
|
||||
categories: ['multimedia'],
|
||||
example: '/podwise/explore/latest',
|
||||
parameters: { type: 'latest or all episodes.' },
|
||||
radar: [
|
||||
{
|
||||
source: ['podwise.ai/explore/:type'],
|
||||
},
|
||||
],
|
||||
name: 'Episodes',
|
||||
maintainers: ['lyling'],
|
||||
handler: async (ctx) => {
|
||||
const type = ctx.req.param('type');
|
||||
const link = `https://podwise.ai/explore/${type}`;
|
||||
const response = await ofetch(link);
|
||||
const $ = load(response);
|
||||
const content = $('#navigator').next();
|
||||
// header/[div => content]/footer, content>div(2)>h1
|
||||
const title = content.find('h1').first().text();
|
||||
const description = content.find('p').eq(1).text();
|
||||
|
||||
const list = content
|
||||
.find('.group')
|
||||
.toArray()
|
||||
.map((item) => {
|
||||
item = $(item);
|
||||
const link = item.find('a').first().attr('href');
|
||||
const description = item.find('p').first().text();
|
||||
const pubDate = item.find('a').next().children('span').text();
|
||||
|
||||
return {
|
||||
link: `https://podwise.ai${link}`,
|
||||
description,
|
||||
pubDate: timezone(parseDate(pubDate, 'DD MMM YYYY', 'en'), 8),
|
||||
};
|
||||
});
|
||||
|
||||
const items = await Promise.all(
|
||||
list.map((item) =>
|
||||
cache.tryGet(item.link, async () => {
|
||||
const response = await ofetch(item.link);
|
||||
const $ = load(response);
|
||||
|
||||
item.description = $('summary').first().html();
|
||||
|
||||
// duration
|
||||
const $cover = $('img[alt="Podcast cover"]').eq(1);
|
||||
const $duration = $cover.next().find('span').eq(2);
|
||||
|
||||
const nextData = JSON.parse(
|
||||
$('script:contains("podName")')
|
||||
.first()
|
||||
.text()
|
||||
.match(/self\.__next_f\.push\((.+)\)/)?.[1] ?? ''
|
||||
);
|
||||
const podcastData = JSON.parse(nextData[1].slice(2))[1][3].children[3].episode;
|
||||
|
||||
// rss feed
|
||||
item.title = podcastData.title;
|
||||
item.author = podcastData.podName;
|
||||
|
||||
// podcast feed
|
||||
item.itunes_item_image = podcastData.cover;
|
||||
item.itunes_duration = parseDuration($duration.text());
|
||||
item.enclosure_url = podcastData.link;
|
||||
// item.enclosure_length: nothing can convert to.
|
||||
item.enclosure_type = toEnclosureType(podcastData.linkType);
|
||||
|
||||
return item;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
link,
|
||||
item: items,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
function parseDuration(durationStr) {
|
||||
const matches = durationStr.match(/(\d+h)?(\d+m)?/);
|
||||
const hours = matches[1] ? Number.parseInt(matches[1]) : 0;
|
||||
const minutes = matches[2] ? Number.parseInt(matches[2]) : 0;
|
||||
|
||||
// 使用 dayjs 的 duration 创建持续时间对象
|
||||
return dayjs
|
||||
.duration({
|
||||
hours,
|
||||
minutes,
|
||||
})
|
||||
.asSeconds();
|
||||
}
|
||||
|
||||
function toEnclosureType(linkType: string): string {
|
||||
switch (linkType) {
|
||||
case 'mp3':
|
||||
return 'audio/mpeg';
|
||||
case 'm4a':
|
||||
return 'audio/x-m4a';
|
||||
case 'mp4':
|
||||
return 'video/mp4';
|
||||
default:
|
||||
return linkType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { Namespace } from '@/types';
|
||||
|
||||
export const namespace: Namespace = {
|
||||
name: 'Podwise',
|
||||
url: 'podwise.ai',
|
||||
zh: {
|
||||
name: 'Podwise',
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user