feat(route): add e-hentai (#7741)

This commit is contained in:
Howard Yin
2021-08-25 22:46:16 +08:00
committed by GitHub
parent b3a8e067b7
commit 6b8a40794e
9 changed files with 243 additions and 0 deletions

View File

@@ -3042,6 +3042,34 @@
},
],
},
'e-hentai.org/': {
_name: 'E-Hentai',
'.': [
{
title: '收藏',
docs: 'https://docs.rsshub.app/picture.html#ehentai',
source: '/favorites.php',
target: '/ehentai/favorites',
},
{
title: '标签',
docs: 'https://docs.rsshub.app/picture.html#ehentai',
source: '/tag/:tag',
target: '/ehentai/tag/:tag',
},
{
title: '搜索',
docs: 'https://docs.rsshub.app/picture.html#ehentai',
source: '/',
target: (params, url) => {
const keyword = new URL(url).searchParams.toString();
if (keyword) {
return `/ehentai/search/${keyword}`;
}
},
},
],
},
'iyingdi.com': {
_name: '旅法师营地',
www: [

View File

@@ -696,3 +696,8 @@ RSSHub 支持使用访问密钥 / 码,白名单和黑名单三种方式进行
| <https://pic1.xuehuaimg.com/proxy/> | cloudflare |
| <https://cors.netnr.workers.dev/> | cloudflare |
| <https://netnr-proxy.openode.io/> | digitalocean |
- E-Hentai
- `EH_IPB_MEMBER_ID`: E-Hentai 账户登录后 cookie 的 `ipb_member_id`
- `EH_IPB_PASS_HASH`: E-Hentai 账户登录后 cookie 的 `ipb_pass_hash`
- `EH_SK`: E-Hentai 账户登录后 cookie 中的`sk`

View File

@@ -479,3 +479,17 @@ R18 显示
### 最新主题
<Route author="nczitzk" example="/zhutix/latest" path="/zhutix/latest"/>
## E-Hentai
### 收藏
<Route author="Howard Yin" example="/ehentai/favorites/0/posted" path="/ehentai/favorites/:favcat?/:order?/:page?/:bittorrent?" :paramsDesc="['收藏夹编号,可选','顺序可选posted-按画廊发布时间排序favorited-按添加收藏的时间排序', '爬获取第多少页的数据', '是否获取种子链接']" anticrawler="1" supportBT="1" />
### 标签
<Route author="Howard Yin" example="/ehentai/tag/language:chinese/1" path="/ehentai/tag/:tag/:page?/:bittorrent?" :paramsDesc="['标签', '爬获取第多少页的数据', '是否获取种子链接']" anticrawler="1" supportBT="1" />
### 搜索
<Route author="Howard Yin" example="/ehentai/search/:params?/:page?" path="/ehentai/search/:params?/:page?/:bittorrent?" :paramsDesc="['用于搜索的关键词。可在原网站搜索后复制 `https://e-hentai.org/?` 后面的内容', '爬获取第多少页的数据', '是否获取种子链接']" anticrawler="1" supportBT="1" />

View File

@@ -193,6 +193,11 @@ const calculateValue = () => {
wordpress: {
cdnUrl: envs.WORDPRESS_CDN,
},
ehentai: {
ipb_member_id: envs.EH_IPB_MEMBER_ID,
ipb_pass_hash: envs.EH_IPB_PASS_HASH,
sk: envs.EH_SK,
},
};
};
calculateValue();

View File

@@ -4140,6 +4140,11 @@ router.get('/tongli/news/:type', lazyloadRouteHandler('./routes/tongli/news'));
// OR
router.get('/or/:id?', lazyloadRouteHandler('./routes/or'));
// e-hentai
router.get('/ehentai/favorites/:favcat?/:order?/:page?/:bittorrent?', lazyloadRouteHandler('./routes/ehentai/favorites'));
router.get('/ehentai/search/:params?/:page?/:bittorrent?', lazyloadRouteHandler('./routes/ehentai/search'));
router.get('/ehentai/tag/:tag/:page?/:bittorrent?', lazyloadRouteHandler('./routes/ehentai/tag'));
// 字型故事
router.get('/fontstory', require('./routes/fontstory/tw'));

134
lib/routes/ehentai/ehapi.js Normal file
View File

@@ -0,0 +1,134 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const config = require('@/config').value.ehentai;
const headers = {};
const has_cookie = config.ipb_member_id && config.ipb_pass_hash && config.sk;
if (has_cookie) {
const { ipb_member_id, ipb_pass_hash, sk } = config;
headers.cookie = `ipb_member_id=${ipb_member_id};ipb_pass_hash=${ipb_pass_hash};sk=${sk}`;
}
function ehgot(url) {
return got({ method: 'get', url: `https://e-hentai.org/${url}`, headers });
}
async function parsePage(cache, data, get_bittorrent = false) {
const $ = cheerio.load(data);
const table = $('table[class="itg gltc"] tbody');
if (!table) {
return [];
}
async function parseTableRow(cache, element) {
const el = $(element);
const el_a = el.find('td[class="gl3c glname"] a');
const el_img = el.find('td.gl2c div.glthumb div img');
const title = el_a.find('div.glink').html();
const thumbnail = el_img.attr('data-src') ? el_img.attr('data-src') : el_img.attr('src');
const description = `<img src='${thumbnail}'>`;
const pubDate = el.find('div[id^="posted_"]').html();
const link = el_a.attr('href');
if (title && link) {
const item = { title, description, pubDate, link };
if (get_bittorrent) {
const el_down = el.find('td.gl2c div div.gldown');
const bittorrent_page_url = el_down.find('a').attr('href');
if (bittorrent_page_url) {
const bittorrent_url = await getBittorrent(cache, bittorrent_page_url);
if (bittorrent_url) {
item.enclosure_url = bittorrent_url;
item.enclosure_type = 'application/x-bittorrent';
item.bittorrent_page_url = bittorrent_page_url;
}
}
}
return item;
}
}
const item_Promises = [];
table.children('tr').each((index, element) => {
item_Promises.push(parseTableRow(cache, element));
});
const items_with_null = await Promise.all(item_Promises);
const items = [];
for (const item of items_with_null) {
if (item) {
items.push(item);
}
}
return items;
}
let p = '';
function getBittorrent(cache, bittorrent_page_url) {
return cache.tryGet(bittorrent_page_url, async () => {
try {
const response = await got({ method: 'get', url: bittorrent_page_url, headers });
const $ = cheerio.load(response.data);
const el_forms = $('form').get();
let bittorrent_url = undefined;
for (const el_form of el_forms) {
const el_a = $(el_form).find('a');
const onclick = el_a.attr('onclick');
if (onclick) {
const match = onclick.match(/'(.*?)'/);
if (match) {
bittorrent_url = match[1];
const match_p = bittorrent_url.match(/torrent\?p=(.*?)$/);
if (match_p) {
p = match_p[1];
}
break;
}
}
}
return bittorrent_url;
} catch {
return undefined;
}
});
}
function updateBittorrent_url(cache, items) {
// 下种子文件需要动态密码,密码每几次请求就更新一次
for (const item of items) {
if (item.enclosure_url) {
item.enclosure_url = item.enclosure_url.replace(/torrent\?p=.*$/, `torrent?p=${p}`);
cache.set(item.bittorrent_page_url, item.enclosure_url);
}
}
return items;
}
async function gatherItemsByPage(cache, url, get_bittorrent = false) {
const response = await ehgot(url);
const items = await parsePage(cache, response.data, get_bittorrent);
return updateBittorrent_url(cache, items);
}
async function getFavoritesItems(cache, page, favcat, inline_set, get_bittorrent = false) {
const response = await ehgot(`favorites.php?favcat=${favcat}&inline_set=${inline_set}`);
if (parseInt(page) === 0) {
const items = await parsePage(cache, response.data, get_bittorrent);
return updateBittorrent_url(cache, items);
}
return gatherItemsByPage(cache, `favorites.php?page=${page}&favcat=${favcat}`, get_bittorrent);
}
function getSearchItems(cache, params, page = undefined, get_bittorrent = false) {
if (page) {
return gatherItemsByPage(cache, `?page=${page}&${params}`, get_bittorrent);
} else {
return gatherItemsByPage(cache, `?${params}`, get_bittorrent);
}
}
function getTagItems(cache, tag, page, get_bittorrent = false) {
return gatherItemsByPage(cache, `tag/${tag}/${page}`, get_bittorrent);
}
module.exports = { getFavoritesItems, getSearchItems, getTagItems };

View File

@@ -0,0 +1,19 @@
const EhAPI = require('./ehapi');
const config = require('@/config').value;
module.exports = async (ctx) => {
if (!config.ehentai || !config.ehentai.ipb_member_id || !config.ehentai.ipb_pass_hash || !config.ehentai.sk) {
throw 'Ehentai favorites RSS is disabled due to the lack of <a href="https://docs.rsshub.app/install/#pei-zhi-bu-fen-rss-mo-kuai-pei-zhi">relevant config</a>';
}
const favcat = ctx.params.favcat ? parseInt(ctx.params.favcat) : 0;
const page = ctx.params.page ? parseInt(ctx.params.page) : 0;
const bittorrent = ctx.params.bittorrent || false;
const inline_set = ctx.params.order === 'posted' ? 'fs_p' : 'fs_f';
const items = await EhAPI.getFavoritesItems(ctx.cache, page, favcat, inline_set, bittorrent);
ctx.state.data = {
title: 'E-Hentai Favorites',
link: `https://e-hentai.org/favorites.php?favcat=${favcat}&inline_set=${inline_set}`,
item: items,
};
};

View File

@@ -0,0 +1,20 @@
const EhAPI = require('./ehapi');
module.exports = async (ctx) => {
const page = ctx.params.page;
let params = ctx.params.params;
const bittorrent = ctx.params.bittorrent || false;
let items;
if (page) {
// 如果定义了page就要覆盖params
params = params.replace(/&*page=[^&]$/, '').replace(/page=[^&]&/, '');
items = await EhAPI.getSearchItems(ctx.cache, params, parseInt(page), bittorrent);
} else {
items = await EhAPI.getSearchItems(ctx.cache, params, undefined, bittorrent);
}
ctx.state.data = {
title: 'E-Hentai Search',
link: `https://e-hentai.org/?${params}`,
item: items,
};
};

13
lib/routes/ehentai/tag.js Normal file
View File

@@ -0,0 +1,13 @@
const EhAPI = require('./ehapi');
module.exports = async (ctx) => {
const page = ctx.params.page ? parseInt(ctx.params.page) : 0;
const tag = ctx.params.tag;
const bittorrent = ctx.params.bittorrent || false;
const items = await EhAPI.getTagItems(ctx.cache, tag, page, bittorrent);
ctx.state.data = {
title: 'E-Hentai Search',
link: `https://e-hentai.org/tag/${tag}`,
item: items,
};
};