diff --git a/lib/fetcher.js b/lib/fetcher.js index 33b7a0b..c2e5ba2 100644 --- a/lib/fetcher.js +++ b/lib/fetcher.js @@ -188,6 +188,80 @@ async function searchJavdb(id) { } } +// ─── 数据源四:NJAV(无需Cookie,覆盖率高)──────────── +function searchNjav(id) { + try { + const url = `https://www.njav.com/zh/xvideos/${id.toLowerCase()}`; + const html = fetchHtml(url); + const $ = cheerio.load(html); + + // 确认页面包含正确番号 + const pageTitle = $('title').text(); + if (!pageTitle.includes(id.toUpperCase())) return null; + + const result = { + id, + source: 'NJAV', + title: '', + actresses: [], + actors: [], + releaseDate: '', + duration: '', + studio: '', + label: '', + director: '', + series: '', + tags: [], + coverUrl: `https://static.javcdn.vip/images/${id.toLowerCase()}/thumb_h.webp`, + score: '', + }; + + // 优先用 JSON-LD 解析,最稳定 + const jsonLd = $('script[type="application/ld+json"]').text(); + if (jsonLd) { + try { + const data = JSON.parse(jsonLd); + result.title = data.name || ''; + result.releaseDate = data.uploadDate?.substring(0, 10) || ''; + // 解析时长 PT3H22M51S → 3:22:51 + if (data.duration) { + const m = data.duration.match(/PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?/); + if (m) { + const h = m[1] || '0'; + const min = m[2] ? m[2].padStart(2, '0') : '00'; + const sec = m[3] ? m[3].padStart(2, '0') : '00'; + result.duration = `${h}:${min}:${sec}`; + } + } + if (Array.isArray(data.actor)) { + result.actresses = data.actor.map(a => a.name).filter(Boolean); + } + if (Array.isArray(data.genre)) { + result.tags = data.genre; + } + if (data.partOfSeries?.name) { + result.series = data.partOfSeries.name; + } + } catch {} + } + + // 补充 HTML 里的字段(JSON-LD 没有的) + $('.detail-item div').each((_, el) => { + const label = $(el).find('span').first().text().trim(); + const value = $(el).find('span').last().text().trim(); + if (/片商|制作|製作|Studio|Maker/i.test(label)) { + result.studio = value; + } else if (/導演|Director/i.test(label)) { + result.director = value; + } + }); + + return result.title ? result : null; + } catch { + return null; + } +} + function parseJavdb(html, queryId) { const $ = cheerio.load(html); const result = { @@ -263,12 +337,19 @@ export async function search(id) { for (let attempt = 1; attempt <= MAX_RETRY; attempt++) { let result = null; + // 第一优先:JAVBUS(最快,直接番号拼URL) result = searchJavbus(searchId); if (result) { setCache(id, result); return result; } + // 第二优先:NJAV(无需Cookie,覆盖率高,能查到JUR等小众番号) + result = searchNjav(searchId); + if (result) { setCache(id, result); return result; } + + // 第三优先:JavLibrary(补充数据) result = searchJavlibrary(searchId); if (result) { setCache(id, result); return result; } + // 第四优先:JAVDB(需要Cookie,仅Linux) result = await searchJavdb(searchId); if (result) { setCache(id, result); return result; } diff --git a/package.json b/package.json index d674e08..5d7e641 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "javinfo", - "version": "1.1.12", + "version": "1.2.0", "description": "AV番号命令行查询工具", "main": "./index.js", "type": "module",