rsshub
Version:
Make RSS Great Again!
211 lines (177 loc) • 7.31 kB
text/typescript
import { Route, ViewType } from '@/types';
import { getSubPath } from '@/utils/common-utils';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
import { config } from '@/config';
import ConfigNotFoundError from '@/errors/types/config-not-found';
const toSize = (raw) => {
const matches = raw.match(/(\d+(\.\d+)?)(\w+)/);
return matches[3] === 'GB' ? matches[1] * 1024 : matches[1];
};
const allowDomain = new Set(['javbus.com', 'javbus.org', 'javsee.icu', 'javsee.one']);
export const route: Route = {
path: '/:path{.+}?',
radar: [
{
source: ['www.javbus.com/:path*'],
target: '/:path',
},
],
name: 'Works',
maintainers: ['MegrezZhu', 'CoderTonyChan', 'nczitzk', 'Felix2yu'],
categories: ['multimedia'],
view: ViewType.Videos,
handler,
url: 'www.javbus.com',
example: '/javbus/star/rwt',
parameters: {
path: {
description: 'Any path of list page on javbus',
},
},
features: {
nsfw: true,
},
};
async function handler(ctx) {
const isWestern = /^\/western/.test(getSubPath(ctx));
const domain = ctx.req.query('domain') ?? 'javbus.com';
const westernDomain = ctx.req.query('western_domain') ?? 'javbus.org';
const rootUrl = `https://www.${domain}`;
const westernUrl = `https://www.${westernDomain}`;
if (!config.feature.allow_user_supply_unsafe_domain && (!allowDomain.has(new URL(`https://${domain}/`).hostname) || !allowDomain.has(new URL(`https://${westernDomain}/`).hostname))) {
throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}
const currentUrl = `${isWestern ? westernUrl : rootUrl}${getSubPath(ctx)
.replace(/^\/western/, '')
.replace(/\/home/, '')}`;
const headers = {
'accept-language': 'zh-CN',
};
const response = await got({
method: 'get',
url: currentUrl,
headers,
});
const $ = load(response.data);
let items = $('.movie-box')
.slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50)
.toArray()
.map((item) => {
item = $(item);
return {
link: item.attr('href'),
guid: item.find('date').first().text(),
pubDate: parseDate(item.find('date').last().text()),
};
});
items = await Promise.all(
items.map((item) =>
cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
headers,
});
const content = load(detailResponse.data);
content('.genre').last().parent().remove();
content('input[type="checkbox"], button').remove();
const stars = content('.avatar-box span')
.toArray()
.map((s) => content(s).text());
const cacheIn = {
author: stars.join(', '),
title: content('h3').text(),
category: [
...content('.genre label')
.toArray()
.map((c) => content(c).text()),
...stars,
],
info: content('.row.movie').html(),
thumbs: content('.sample-box')
.toArray()
.map((i) => {
const thumbSrc = content(i).attr('href');
return thumbSrc.startsWith('http') ? thumbSrc : `${rootUrl}${thumbSrc}`;
}),
};
let magnets, videoSrc, videoPreview;
// To fetch magnets.
try {
const matches = detailResponse.data.match(/var gid = (\d+);[\S\s]*var uc = (\d+);[\S\s]*var img = '(.*)';/);
const magnetResponse = await got({
method: 'get',
url: `${rootUrl}/ajax/uncledatoolsbyajax.php`,
searchParams: {
gid: matches[1],
lang: 'zh',
img: matches[3],
uc: matches[2],
floor: 800,
},
headers: {
Referer: item.link,
},
});
const content = load(`<table>${magnetResponse.data}</table>`);
magnets = content('tr')
.toArray()
.map((tr) => {
const td = content(tr).find('a[href]');
return {
title: td.first().text().trim(),
link: td.first().attr('href'),
size: td.eq(1).text().trim(),
date: td.last().text().trim(),
score: content(tr).find('a').length ** 8 * toSize(td.eq(1).text().trim()),
};
});
if (magnets) {
item.enclosure_url = magnets.sort((a, b) => b.score - a.score)[0].link;
item.enclosure_type = 'application/x-bittorrent';
}
} catch {
// no-empty
}
// If the video is not western, go fetch preview.
if (!isWestern) {
try {
const avgleResponse = await got({
method: 'get',
url: `https://api.avgle.com/v1/jav/${item.guid}/0`,
});
// full video
videoSrc = avgleResponse.data.response.videos[0]?.embedded_url ?? '';
// video preview
videoPreview = avgleResponse.data.response.videos[0]?.preview_video_url ?? '';
} catch {
// no-empty
}
}
item.author = cacheIn.author;
item.title = cacheIn.title;
item.category = cacheIn.category;
item.description = art(path.join(__dirname, 'templates/description.art'), {
info: cacheIn.info,
thumbs: cacheIn.thumbs,
magnets,
videoSrc,
videoPreview,
});
return item;
})
)
);
const title = $('head title').text();
return {
title: `${title.startsWith('JavBus') ? '' : 'JavBus - '}${title.replace(/ - AV磁力連結分享/, '')}`,
link: currentUrl,
item: items,
allowEmpty: true,
};
}