Skip to content

Commit 706e252

Browse files
committed
feat jable.tv
1 parent 99e31eb commit 706e252

File tree

2 files changed

+159
-0
lines changed

2 files changed

+159
-0
lines changed

lib/routes/jable/index.ts

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import { load } from 'cheerio';
2+
3+
import type { Route } from '@/types';
4+
import cache from '@/utils/cache';
5+
import got from '@/utils/got';
6+
import { parseDate } from '@/utils/parse-date';
7+
8+
export const route: Route = {
9+
path: '/search/:query',
10+
categories: ['multimedia'],
11+
example: '/jable/search/みなみ羽琉',
12+
parameters: {
13+
query: 'Search keyword',
14+
},
15+
features: {
16+
requireConfig: false,
17+
requirePuppeteer: false,
18+
antiCrawler: false,
19+
supportBT: false,
20+
supportPodcast: false,
21+
supportScihub: false,
22+
nsfw: true,
23+
},
24+
name: 'Jable 搜索结果',
25+
maintainers: [],
26+
handler,
27+
};
28+
29+
const GOT_HEADERS = {
30+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
31+
'Accept-Language': 'en-US,en;q=0.9',
32+
Referer: 'https://jable.tv/',
33+
};
34+
35+
// function renderDescription(item) {
36+
// return `
37+
// <a href="${item.link}">
38+
// <img src="${item.thumb}" style="max-width:100%" />
39+
// </a>
40+
// <p>
41+
// ${item.duration ? `<strong>Duration:</strong> ${item.duration}` : ''}
42+
// ${item.views ? `|<strong>Views:</strong> ${item.views}` : ''}
43+
// ${item.favorites ? `|<strong>Favorites:</strong> ${item.favorites}` : ''}
44+
// ${item.author ? `|<strong>Author:</strong> ${item.author}` : ''}
45+
// </p>
46+
// `.trim();
47+
// }
48+
49+
async function handler(ctx) {
50+
const { query } = ctx.req.param();
51+
const encodedQuery = encodeURIComponent(query);
52+
53+
const apiUrl = `https://jable.tv/search/${encodedQuery}/` + `?mode=async&function=get_block&block_id=list_videos_videos_list_search_result` + `&q=${encodedQuery}&sort_by=post_date`;
54+
55+
const response = await got(apiUrl, { headers: GOT_HEADERS });
56+
const $ = load(response.data);
57+
58+
const pageAuthor = $('section.content-header h2').first().text().trim() || query;
59+
60+
const items = await Promise.all(
61+
$('.video-img-box')
62+
.toArray()
63+
.map((el) => {
64+
const $el = $(el);
65+
66+
const $titleLink = $el.find('.detail h6.title a');
67+
const title = $titleLink.text().trim();
68+
const link = $titleLink.attr('href') ?? '';
69+
70+
const thumb = $el.find('img[data-src]').attr('data-src') ?? '';
71+
const preview = $el.find('img[data-preview]').attr('data-preview') ?? '';
72+
73+
// const duration = $el.find('.label').text().trim();
74+
// const subText = $el.find('.sub-title').text().trim();
75+
// const nums = subText.split(/\s+/);
76+
// const views = nums[0] ?? '';
77+
// const favorites = nums[1] ?? '';
78+
79+
const videoId = $el.find('[data-fav-video-id]').attr('data-fav-video-id') ?? link;
80+
81+
return cache.tryGet(`jable:video:${videoId}`, async () => {
82+
let pubDate;
83+
const author = pageAuthor;
84+
let videoUrl;
85+
86+
try {
87+
const { data } = await got(link, { headers: GOT_HEADERS });
88+
const $page = load(data);
89+
90+
const dateText = $page('.video-date').text().trim();
91+
if (dateText) {
92+
pubDate = parseDate(dateText);
93+
}
94+
95+
const script = $page('script')
96+
.toArray()
97+
.map((s) => $(s).html())
98+
.find((s) => s && s.includes('sources'));
99+
100+
if (script) {
101+
const match = script.match(/file:\s*"([^"]+)"/);
102+
if (match) {
103+
videoUrl = match[1];
104+
}
105+
}
106+
} catch {
107+
// 忽略错误
108+
}
109+
110+
return {
111+
title,
112+
link,
113+
guid: `jable:video:${videoId}`,
114+
pubDate,
115+
author,
116+
// description: renderDescription({
117+
// title,
118+
// link,
119+
// thumb,
120+
// duration,
121+
// views,
122+
// favorites,
123+
// author,
124+
// }),
125+
media: {
126+
content: preview
127+
? {
128+
url: preview,
129+
type: 'video/mp4',
130+
}
131+
: videoUrl
132+
? {
133+
url: videoUrl,
134+
type: 'video/mp4',
135+
}
136+
: undefined,
137+
thumbnail: {
138+
url: thumb,
139+
},
140+
},
141+
};
142+
});
143+
})
144+
);
145+
146+
return {
147+
title: query,
148+
link: `https://jable.tv/search/${encodedQuery}/`,
149+
description: `Search results for ${query}`,
150+
item: items,
151+
};
152+
}

lib/routes/jable/namaspace.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { Namespace } from '@/types';
2+
3+
export const namespace: Namespace = {
4+
name: 'jable',
5+
url: 'jable.tv',
6+
lang: 'zh',
7+
};

0 commit comments

Comments
 (0)