javdict 1.3.3 → 1.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/README_de.md +11 -3
- package/README_jp.md +11 -3
- package/README_kr.md +11 -3
- package/README_zh.md +11 -3
- package/assets/Javdict-Demo-de.gif +0 -0
- package/assets/Javdict-Demo-en.gif +0 -0
- package/assets/Javdict-Demo-jp.gif +0 -0
- package/assets/Javdict-Demo-kr.gif +0 -0
- package/assets/Javdict-Demo-zh.gif +0 -0
- package/index.js +11 -3
- package/lib/cache.js +10 -5
- package/lib/fetcher.js +36 -22
- package/lib/i18n.js +11 -1
- package/package.json +1 -1
- package/test/cache.test.js +14 -1
- package/test/fetcher.test.js +75 -9
- package/Javdict-Demo.gif +0 -0
package/README.md
CHANGED
|
@@ -9,15 +9,15 @@ English | [中文](README_zh.md) | [日本語](README_jp.md) | [한국어](READM
|
|
|
9
9
|
[](LICENSE)
|
|
10
10
|
[]()
|
|
11
11
|
|
|
12
|
-
Type a title ID, get back the full details — cast, release date, studio, duration, tags, and more. Multiple data sources are queried automatically
|
|
12
|
+
Type a title ID, get back the full details — cast, release date, studio, duration, tags, and more. Multiple data sources are queried automatically by default, and you can optionally force a single source.
|
|
13
13
|
|
|
14
|
-

|
|
14
|
+

|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
## Features
|
|
19
19
|
|
|
20
|
-
- 🔍 **Multi-source fallback** —
|
|
20
|
+
- 🔍 **Multi-source fallback** — By default it queries JAVBUS → NJAV → JavLibrary → JAVDB in order and returns on first hit; you can also force a single source manually
|
|
21
21
|
- 📋 **Rich metadata** — Cast, release date, duration, studio, label, director, series, tags, cover image, rating
|
|
22
22
|
- 💾 **Local cache** — Results cached for 7 days to reduce repeat requests
|
|
23
23
|
- 🖥️ **Cross-platform** — Linux, Windows, and macOS supported
|
|
@@ -108,6 +108,13 @@ jav -l en SSIS-001 # shorthand
|
|
|
108
108
|
jav -r SSIS-001
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
+
**Force a specific data source:**
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
jav --source javbus SSIS-001
|
|
115
|
+
jav -s njav SSIS-001
|
|
116
|
+
```
|
|
117
|
+
|
|
111
118
|
**Clear local cache:**
|
|
112
119
|
|
|
113
120
|
```bash
|
|
@@ -139,6 +146,7 @@ Arguments:
|
|
|
139
146
|
Options:
|
|
140
147
|
-v, --version Print version number
|
|
141
148
|
-l, --lang <lang> Output language: zh/en/jp/kr/de (default: zh)
|
|
149
|
+
-s, --source <source> Select source: auto/javbus/njav/javlibrary/javdb (default: auto)
|
|
142
150
|
-r, --raw Output raw JSON instead of formatted display
|
|
143
151
|
--setup Configure JAVDB Cookie (optional, improves coverage)
|
|
144
152
|
--clear-cache Clear local result cache
|
package/README_de.md
CHANGED
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
[](LICENSE)
|
|
10
10
|
[]()
|
|
11
11
|
|
|
12
|
-
Gib eine Titel-ID ein und erhalte sofort vollständige Informationen — Besetzung, Erscheinungsdatum, Studio, Laufzeit, Tags und mehr. Mehrere Datenquellen werden automatisch abgefragt,
|
|
12
|
+
Gib eine Titel-ID ein und erhalte sofort vollständige Informationen — Besetzung, Erscheinungsdatum, Studio, Laufzeit, Tags und mehr. Mehrere Datenquellen werden standardmäßig automatisch abgefragt, bei Bedarf kannst du aber auch eine einzelne Quelle erzwingen.
|
|
13
13
|
|
|
14
|
-

|
|
14
|
+

|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
## Funktionen
|
|
19
19
|
|
|
20
|
-
- 🔍 **Automatisches Multi-Source-Fallback** — Fragt JAVBUS → NJAV → JavLibrary → JAVDB der Reihe nach ab und gibt beim ersten Treffer zurück
|
|
20
|
+
- 🔍 **Automatisches Multi-Source-Fallback** — Fragt standardmäßig JAVBUS → NJAV → JavLibrary → JAVDB der Reihe nach ab und gibt beim ersten Treffer zurück; bei Bedarf kannst du auch eine einzelne Quelle erzwingen
|
|
21
21
|
- 📋 **Umfangreiche Metadaten** — Besetzung, Erscheinungsdatum, Laufzeit, Studio, Label, Regisseur, Serie, Tags, Cover-Bild, Bewertung
|
|
22
22
|
- 💾 **Lokaler Cache** — Ergebnisse werden 7 Tage lang gecacht, um wiederholte Anfragen zu reduzieren
|
|
23
23
|
- 🖥️ **Plattformübergreifend** — Linux, Windows und macOS werden unterstützt
|
|
@@ -110,6 +110,13 @@ jav -l de SSIS-001 # Kurzform
|
|
|
110
110
|
jav -r SSIS-001
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
+
**Eine bestimmte Datenquelle erzwingen:**
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
jav --source javbus SSIS-001
|
|
117
|
+
jav -s njav SSIS-001
|
|
118
|
+
```
|
|
119
|
+
|
|
113
120
|
**Lokalen Cache leeren:**
|
|
114
121
|
|
|
115
122
|
```bash
|
|
@@ -141,6 +148,7 @@ Arguments:
|
|
|
141
148
|
Options:
|
|
142
149
|
-v, --version Versionsnummer ausgeben
|
|
143
150
|
-l, --lang <Sprache> Ausgabesprache: zh/en/jp/kr/de (Standard: zh)
|
|
151
|
+
-s, --source <source> Datenquelle wählen: auto/javbus/njav/javlibrary/javdb (Standard: auto)
|
|
144
152
|
-r, --raw Ausgabe als rohes JSON
|
|
145
153
|
--setup JAVDB Cookie konfigurieren(optional, verbessert Abdeckung)
|
|
146
154
|
--clear-cache Lokalen Cache leeren
|
package/README_jp.md
CHANGED
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
[](LICENSE)
|
|
10
10
|
[]()
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
番号を入力するだけで、出演者・発売日・メーカー・収録時間・タグなどの詳細情報を取得できます。複数のデータソースを既定で自動フォールバックし、必要に応じて単一ソースを手動指定できます。
|
|
13
13
|
|
|
14
|
-

|
|
14
|
+

|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
## 特徴
|
|
19
19
|
|
|
20
|
-
- 🔍 **複数データソース自動フォールバック** — JAVBUS → NJAV → JavLibrary → JAVDB
|
|
20
|
+
- 🔍 **複数データソース自動フォールバック** — 既定では JAVBUS → NJAV → JavLibrary → JAVDB の順に検索して最初のヒットを返し、必要に応じて単一ソースを手動指定できます
|
|
21
21
|
- 📋 **豊富な情報** — 女優、男優、発売日、収録時間、メーカー、レーベル、監督、シリーズ、タグ、パッケージ画像、評価
|
|
22
22
|
- 💾 **ローカルキャッシュ** — 検索結果を7日間キャッシュし、重複リクエストを削減
|
|
23
23
|
- 🖥️ **クロスプラットフォーム** — Linux、Windows、macOS 対応
|
|
@@ -110,6 +110,13 @@ jav -l ja SSIS-001 # 省略形
|
|
|
110
110
|
jav -r SSIS-001
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
+
**データソースを手動指定:**
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
jav --source javbus SSIS-001
|
|
117
|
+
jav -s njav SSIS-001
|
|
118
|
+
```
|
|
119
|
+
|
|
113
120
|
**キャッシュを削除:**
|
|
114
121
|
|
|
115
122
|
```bash
|
|
@@ -141,6 +148,7 @@ Arguments:
|
|
|
141
148
|
Options:
|
|
142
149
|
-v, --version バージョンを表示
|
|
143
150
|
-l, --lang <言語> 出力言語:zh/en/jp/kr/de(デフォルト:zh)
|
|
151
|
+
-s, --source <source> データソースを指定:auto/javbus/njav/javlibrary/javdb(デフォルト:auto)
|
|
144
152
|
-r, --raw 生の JSON 形式で出力
|
|
145
153
|
--setup JAVDB Cookie を設定(任意、カバレッジ向上)
|
|
146
154
|
--clear-cache ローカルキャッシュを削除
|
package/README_kr.md
CHANGED
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
[](LICENSE)
|
|
10
10
|
[]()
|
|
11
11
|
|
|
12
|
-
번호를 입력하면 출연진, 발매일, 제작사, 재생 시간, 태그 등 상세 정보를 바로 확인할 수 있습니다. 여러 데이터 소스를
|
|
12
|
+
번호를 입력하면 출연진, 발매일, 제작사, 재생 시간, 태그 등 상세 정보를 바로 확인할 수 있습니다. 기본값으로 여러 데이터 소스를 자동 폴백하며, 필요하면 단일 소스를 직접 지정할 수 있습니다.
|
|
13
13
|
|
|
14
|
-

|
|
14
|
+

|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
## 주요 기능
|
|
19
19
|
|
|
20
|
-
- 🔍 **다중 소스 자동 폴백** — JAVBUS → NJAV → JavLibrary → JAVDB 순서로
|
|
20
|
+
- 🔍 **다중 소스 자동 폴백** — 기본값으로 JAVBUS → NJAV → JavLibrary → JAVDB 순서로 검색해 첫 결과를 반환하며, 필요하면 단일 소스를 직접 지정할 수 있습니다
|
|
21
21
|
- 📋 **풍부한 정보** — 여배우, 남배우, 발매일, 재생 시간, 제작사, 레이블, 감독, 시리즈, 태그, 커버 이미지, 평점
|
|
22
22
|
- 💾 **로컬 캐시** — 검색 결과를 7일간 캐시하여 중복 요청 감소
|
|
23
23
|
- 🖥️ **크로스 플랫폼** — Linux, Windows, macOS 지원
|
|
@@ -110,6 +110,13 @@ jav -l ko SSIS-001 # 축약형
|
|
|
110
110
|
jav -r SSIS-001
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
+
**데이터 소스 직접 지정:**
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
jav --source javbus SSIS-001
|
|
117
|
+
jav -s njav SSIS-001
|
|
118
|
+
```
|
|
119
|
+
|
|
113
120
|
**로컬 캐시 삭제:**
|
|
114
121
|
|
|
115
122
|
```bash
|
|
@@ -141,6 +148,7 @@ Arguments:
|
|
|
141
148
|
Options:
|
|
142
149
|
-v, --version 버전 출력
|
|
143
150
|
-l, --lang <언어> 출력 언어: zh/en/jp/kr/de (기본값: zh)
|
|
151
|
+
-s, --source <source> 데이터 소스 지정: auto/javbus/njav/javlibrary/javdb (기본값: auto)
|
|
144
152
|
-r, --raw 원시 JSON 형식으로 출력
|
|
145
153
|
--setup JAVDB Cookie 설정(선택, 커버리지 향상)
|
|
146
154
|
--clear-cache 로컬 캐시 삭제
|
package/README_zh.md
CHANGED
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
[](LICENSE)
|
|
10
10
|
[]()
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
输入一个车牌号,即可获取女优、发售日期、制作商、时长、类别等完整信息。默认启用多数据源自动兜底,也支持手动指定单一数据源。
|
|
13
13
|
|
|
14
|
-

|
|
14
|
+

|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
## 功能特点
|
|
19
19
|
|
|
20
|
-
- 🔍 **多数据源自动兜底** —
|
|
20
|
+
- 🔍 **多数据源自动兜底** — 默认依次查询 JAVBUS → NJAV → JavLibrary → JAVDB,任意一个命中即返回,也可手动指定单一数据源
|
|
21
21
|
- 📋 **信息完整** — 女优、男优、发售日期、时长、制作商、发行商、导演、系列、类别、封面、评分
|
|
22
22
|
- 💾 **本地缓存** — 查询结果缓存 7 天,减少重复请求
|
|
23
23
|
- 🖥️ **跨平台** — 支持 Linux、Windows、macOS
|
|
@@ -110,6 +110,13 @@ jav -l en SSIS-001 # 缩写
|
|
|
110
110
|
jav -r SSIS-001
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
+
**手动指定数据源:**
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
jav --source javbus SSIS-001
|
|
117
|
+
jav -s njav SSIS-001
|
|
118
|
+
```
|
|
119
|
+
|
|
113
120
|
**清空本地缓存:**
|
|
114
121
|
|
|
115
122
|
```bash
|
|
@@ -141,6 +148,7 @@ Arguments:
|
|
|
141
148
|
Options:
|
|
142
149
|
-v, --version 显示版本号
|
|
143
150
|
-l, --lang <语言> 输出语言:zh/en/jp/kr/de(默认:zh)
|
|
151
|
+
-s, --source <source> 指定数据源:auto/javbus/njav/javlibrary/javdb(默认:auto)
|
|
144
152
|
-r, --raw 以原始 JSON 格式输出结果
|
|
145
153
|
--setup 配置 JAVDB Cookie(可选,提高覆盖率)
|
|
146
154
|
--clear-cache 清空本地缓存
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { createInterface } from 'readline';
|
|
4
4
|
import { program } from 'commander';
|
|
5
5
|
import { createRequire } from 'module';
|
|
6
|
-
import { search } from './lib/fetcher.js';
|
|
6
|
+
import { search, SUPPORTED_SOURCES } from './lib/fetcher.js';
|
|
7
7
|
import { display } from './lib/display.js';
|
|
8
8
|
import { clearCache, setConfig } from './lib/cache.js';
|
|
9
9
|
import ora from 'ora';
|
|
@@ -22,9 +22,11 @@ program
|
|
|
22
22
|
.option('--clear-cache', '清空本地缓存')
|
|
23
23
|
.option('--setup', '配置JAVDB Cookie(可选,提高查询覆盖率)')
|
|
24
24
|
.option('-l, --lang <lang>', '显示语言 zh/en/jp/kr/de', 'zh')
|
|
25
|
+
.option('-s, --source <source>', '指定数据源 auto/javbus/njav/javlibrary/javdb', 'auto')
|
|
25
26
|
.action(async (id, options) => {
|
|
26
27
|
|
|
27
28
|
const lang = options.lang || 'zh';
|
|
29
|
+
const source = (options.source || 'auto').toLowerCase();
|
|
28
30
|
const t = getLang(lang); // 统一获取语言包
|
|
29
31
|
|
|
30
32
|
if (options.setup) {
|
|
@@ -68,10 +70,16 @@ program
|
|
|
68
70
|
process.exit(0);
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
if (!SUPPORTED_SOURCES.includes(source)) {
|
|
74
|
+
console.error(`\n${t.invalidSource}: ${source}`);
|
|
75
|
+
console.error(`${t.availableSources}: ${SUPPORTED_SOURCES.join(', ')}`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
|
|
71
79
|
const spinner = ora(`${t.searching} ${id.toUpperCase()} ...`).start();
|
|
72
80
|
|
|
73
81
|
try {
|
|
74
|
-
const result = await search(id.toUpperCase(), lang);
|
|
82
|
+
const result = await search(id.toUpperCase(), lang, source);
|
|
75
83
|
spinner.stop();
|
|
76
84
|
|
|
77
85
|
if (!result) {
|
|
@@ -91,4 +99,4 @@ program
|
|
|
91
99
|
}
|
|
92
100
|
});
|
|
93
101
|
|
|
94
|
-
program.parse();
|
|
102
|
+
program.parse();
|
package/lib/cache.js
CHANGED
|
@@ -10,6 +10,11 @@ const CACHE_FILE = join(CACHE_DIR, 'cache.json');
|
|
|
10
10
|
// 缓存有效期:7 天(单位毫秒)
|
|
11
11
|
const TTL = 7 * 24 * 60 * 60 * 1000;
|
|
12
12
|
|
|
13
|
+
function getCacheKey(id, source = 'auto') {
|
|
14
|
+
const normalizedSource = (source || 'auto').toLowerCase();
|
|
15
|
+
return normalizedSource === 'auto' ? id : `${normalizedSource}:${id}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
function loadCache() {
|
|
14
19
|
if (!existsSync(CACHE_FILE)) return {};
|
|
15
20
|
try {
|
|
@@ -26,9 +31,9 @@ function saveCache(data) {
|
|
|
26
31
|
writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2), 'utf-8');
|
|
27
32
|
}
|
|
28
33
|
|
|
29
|
-
export function getCache(id) {
|
|
34
|
+
export function getCache(id, source = 'auto') {
|
|
30
35
|
const cache = loadCache();
|
|
31
|
-
const entry = cache[id];
|
|
36
|
+
const entry = cache[getCacheKey(id, source)];
|
|
32
37
|
if (!entry) return null;
|
|
33
38
|
|
|
34
39
|
const isExpired = Date.now() - entry.cachedAt > TTL;
|
|
@@ -37,9 +42,9 @@ export function getCache(id) {
|
|
|
37
42
|
return entry.data;
|
|
38
43
|
}
|
|
39
44
|
|
|
40
|
-
export function setCache(id, data) {
|
|
45
|
+
export function setCache(id, data, source = 'auto') {
|
|
41
46
|
const cache = loadCache();
|
|
42
|
-
cache[id] = {
|
|
47
|
+
cache[getCacheKey(id, source)] = {
|
|
43
48
|
cachedAt: Date.now(),
|
|
44
49
|
data,
|
|
45
50
|
};
|
|
@@ -79,4 +84,4 @@ export function setConfig(data) {
|
|
|
79
84
|
}
|
|
80
85
|
const configFile = join(CACHE_DIR, 'config.json');
|
|
81
86
|
writeFileSync(configFile, JSON.stringify(data, null, 2), 'utf-8');
|
|
82
|
-
}
|
|
87
|
+
}
|
package/lib/fetcher.js
CHANGED
|
@@ -4,6 +4,8 @@ import { getCache, setCache, getConfig } from './cache.js';
|
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import {getLang} from "./i18n.js";
|
|
6
6
|
|
|
7
|
+
export const SUPPORTED_SOURCES = ['auto', 'javbus', 'njav', 'javlibrary', 'javdb'];
|
|
8
|
+
|
|
7
9
|
// ─── 通用请求函数(JAVBUS / JavLibrary 使用)────────────
|
|
8
10
|
function fetchHtml(url, cookie = '') {
|
|
9
11
|
const cookieHeader = cookie ? `-H "Cookie: ${cookie}"` : '';
|
|
@@ -323,9 +325,31 @@ function parseJavdb(html, queryId) {
|
|
|
323
325
|
return result.title ? result : null;
|
|
324
326
|
}
|
|
325
327
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
+
const SOURCE_HANDLERS = {
|
|
329
|
+
javbus: searchJavbus,
|
|
330
|
+
njav: searchNjav,
|
|
331
|
+
javlibrary: searchJavlibrary,
|
|
332
|
+
javdb: searchJavdb,
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
function getSearchPipeline(source = 'auto') {
|
|
336
|
+
const normalizedSource = (source || 'auto').toLowerCase();
|
|
337
|
+
if (normalizedSource === 'auto') {
|
|
338
|
+
return ['javbus', 'njav', 'javlibrary', 'javdb'];
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return SOURCE_HANDLERS[normalizedSource] ? [normalizedSource] : [];
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ─── 主入口:默认自动兜底,手动指定时仅查询单一数据源 ─────────
|
|
345
|
+
export async function search(id, lang = 'zh', source = 'auto') {
|
|
328
346
|
const t = getLang(lang);
|
|
347
|
+
const normalizedSource = (source || 'auto').toLowerCase();
|
|
348
|
+
const searchPipeline = getSearchPipeline(normalizedSource);
|
|
349
|
+
|
|
350
|
+
if (!searchPipeline.length) {
|
|
351
|
+
throw new Error(`Unsupported source: ${source}`);
|
|
352
|
+
}
|
|
329
353
|
|
|
330
354
|
// FC2 格式识别:031926-100 → 031926_100
|
|
331
355
|
let searchId = id;
|
|
@@ -334,29 +358,19 @@ export async function search(id, lang = 'zh') {
|
|
|
334
358
|
console.log(chalk.gray(` ${t.fc2Detected}: ${id} → ${searchId}`));
|
|
335
359
|
}
|
|
336
360
|
|
|
337
|
-
const cached = getCache(id);
|
|
361
|
+
const cached = getCache(id, normalizedSource);
|
|
338
362
|
if (cached) return cached;
|
|
339
363
|
|
|
340
|
-
const MAX_RETRY = 3;
|
|
364
|
+
const MAX_RETRY = normalizedSource === 'auto' ? 3 : 1;
|
|
341
365
|
|
|
342
366
|
for (let attempt = 1; attempt <= MAX_RETRY; attempt++) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
result = searchNjav(searchId);
|
|
351
|
-
if (result) { setCache(id, result); return result; }
|
|
352
|
-
|
|
353
|
-
// 第三优先:JavLibrary(补充数据)
|
|
354
|
-
result = searchJavlibrary(searchId);
|
|
355
|
-
if (result) { setCache(id, result); return result; }
|
|
356
|
-
|
|
357
|
-
// 第四优先:JAVDB(需要Cookie,仅Linux)
|
|
358
|
-
result = await searchJavdb(searchId);
|
|
359
|
-
if (result) { setCache(id, result); return result; }
|
|
367
|
+
for (const sourceName of searchPipeline) {
|
|
368
|
+
const result = await SOURCE_HANDLERS[sourceName](searchId);
|
|
369
|
+
if (result) {
|
|
370
|
+
setCache(id, result, normalizedSource);
|
|
371
|
+
return result;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
360
374
|
|
|
361
375
|
if (attempt < MAX_RETRY) {
|
|
362
376
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
@@ -364,4 +378,4 @@ export async function search(id, lang = 'zh') {
|
|
|
364
378
|
}
|
|
365
379
|
|
|
366
380
|
return null;
|
|
367
|
-
}
|
|
381
|
+
}
|
package/lib/i18n.js
CHANGED
|
@@ -16,6 +16,8 @@ export const messages = {
|
|
|
16
16
|
searching: '正在查询',
|
|
17
17
|
notFound: '未找到番号',
|
|
18
18
|
queryFailed: '查询失败',
|
|
19
|
+
invalidSource: '不支持的数据源',
|
|
20
|
+
availableSources: '可选数据源',
|
|
19
21
|
cacheCleared: '缓存已清空',
|
|
20
22
|
cacheClearFailed: '清空缓存失败',
|
|
21
23
|
cachePath: '缓存路径',
|
|
@@ -50,6 +52,8 @@ export const messages = {
|
|
|
50
52
|
searching: 'Searching',
|
|
51
53
|
notFound: 'Title not found',
|
|
52
54
|
queryFailed: 'Query failed',
|
|
55
|
+
invalidSource: 'Unsupported data source',
|
|
56
|
+
availableSources: 'Available sources',
|
|
53
57
|
cacheCleared: 'Cache cleared',
|
|
54
58
|
cacheClearFailed: 'Failed to clear cache',
|
|
55
59
|
cachePath: 'Cache path',
|
|
@@ -84,6 +88,8 @@ export const messages = {
|
|
|
84
88
|
searching: '検索中',
|
|
85
89
|
notFound: '番号が見つかりません',
|
|
86
90
|
queryFailed: '検索失敗',
|
|
91
|
+
invalidSource: 'サポートされていないデータソース',
|
|
92
|
+
availableSources: '利用可能なデータソース',
|
|
87
93
|
cacheCleared: 'キャッシュを削除しました',
|
|
88
94
|
cacheClearFailed: 'キャッシュの削除に失敗しました',
|
|
89
95
|
cachePath: 'キャッシュパス',
|
|
@@ -118,6 +124,8 @@ export const messages = {
|
|
|
118
124
|
searching: '검색 중',
|
|
119
125
|
notFound: '번호를 찾을 수 없습니다',
|
|
120
126
|
queryFailed: '검색 실패',
|
|
127
|
+
invalidSource: '지원되지 않는 데이터 소스',
|
|
128
|
+
availableSources: '사용 가능한 데이터 소스',
|
|
121
129
|
cacheCleared: '캐시가 삭제되었습니다',
|
|
122
130
|
cacheClearFailed: '캐시 삭제 실패',
|
|
123
131
|
cachePath: '캐시 경로',
|
|
@@ -152,6 +160,8 @@ export const messages = {
|
|
|
152
160
|
searching: 'Suche läuft',
|
|
153
161
|
notFound: 'Titel nicht gefunden',
|
|
154
162
|
queryFailed: 'Suche fehlgeschlagen',
|
|
163
|
+
invalidSource: 'Nicht unterstützte Datenquelle',
|
|
164
|
+
availableSources: 'Verfügbare Datenquellen',
|
|
155
165
|
cacheCleared: 'Cache geleert',
|
|
156
166
|
cacheClearFailed: 'Cache konnte nicht geleert werden',
|
|
157
167
|
cachePath: 'Cache-Pfad',
|
|
@@ -173,4 +183,4 @@ export const messages = {
|
|
|
173
183
|
|
|
174
184
|
export function getLang(lang) {
|
|
175
185
|
return messages[lang] || messages.zh; // 默认中文
|
|
176
|
-
}
|
|
186
|
+
}
|
package/package.json
CHANGED
package/test/cache.test.js
CHANGED
|
@@ -69,6 +69,19 @@ describe('cache.js', () => {
|
|
|
69
69
|
expect(getCache('IPX-001')).toEqual(dataB);
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
+
it('setCache:自动模式和手动指定数据源的缓存彼此隔离', async () => {
|
|
73
|
+
const { getCache, setCache } = await importCache();
|
|
74
|
+
const autoData = { id: 'SSIS-001', source: 'NJAV', title: '自动兜底结果' };
|
|
75
|
+
const manualData = { id: 'SSIS-001', source: 'JAVBUS', title: '手动指定结果' };
|
|
76
|
+
|
|
77
|
+
setCache('SSIS-001', autoData);
|
|
78
|
+
setCache('SSIS-001', manualData, 'javbus');
|
|
79
|
+
|
|
80
|
+
expect(getCache('SSIS-001')).toEqual(autoData);
|
|
81
|
+
expect(getCache('SSIS-001', 'javbus')).toEqual(manualData);
|
|
82
|
+
expect(getCache('SSIS-001', 'njav')).toBeNull();
|
|
83
|
+
});
|
|
84
|
+
|
|
72
85
|
it('clearCache:清空后所有缓存消失', async () => {
|
|
73
86
|
const { getCache, setCache, clearCache } = await importCache();
|
|
74
87
|
setCache('SSIS-001', { id: 'SSIS-001' });
|
|
@@ -79,4 +92,4 @@ describe('cache.js', () => {
|
|
|
79
92
|
expect(getCache('SSIS-001')).toBeNull();
|
|
80
93
|
expect(getCache('IPX-001')).toBeNull();
|
|
81
94
|
});
|
|
82
|
-
});
|
|
95
|
+
});
|
package/test/fetcher.test.js
CHANGED
|
@@ -43,6 +43,7 @@ vi.mock('../lib/cache.js', () => ({
|
|
|
43
43
|
}));
|
|
44
44
|
|
|
45
45
|
import { execSync } from 'child_process';
|
|
46
|
+
import { getCache, setCache } from '../lib/cache.js';
|
|
46
47
|
import { search } from '../lib/fetcher.js';
|
|
47
48
|
|
|
48
49
|
// 模拟 HTML 数据
|
|
@@ -80,9 +81,32 @@ const MOCK_404_HTML = `
|
|
|
80
81
|
</html>
|
|
81
82
|
`;
|
|
82
83
|
|
|
84
|
+
const MOCK_NJAV_HTML = `
|
|
85
|
+
<html>
|
|
86
|
+
<head><title>SSIS-001 - NJAV</title></head>
|
|
87
|
+
<body>
|
|
88
|
+
<script type="application/ld+json">
|
|
89
|
+
{
|
|
90
|
+
"name": "SSIS-001 NJAV 标题",
|
|
91
|
+
"uploadDate": "2021-02-03T00:00:00Z",
|
|
92
|
+
"duration": "PT2H05M06S",
|
|
93
|
+
"actor": [{ "name": "女优B" }],
|
|
94
|
+
"genre": ["人妻", "剧情"],
|
|
95
|
+
"partOfSeries": { "name": "系列B" }
|
|
96
|
+
}
|
|
97
|
+
</script>
|
|
98
|
+
<div class="detail-item">
|
|
99
|
+
<div><span>片商</span><span>Studio B</span></div>
|
|
100
|
+
<div><span>導演</span><span>导演B</span></div>
|
|
101
|
+
</div>
|
|
102
|
+
</body>
|
|
103
|
+
</html>
|
|
104
|
+
`;
|
|
105
|
+
|
|
83
106
|
describe('fetcher.js', () => {
|
|
84
107
|
beforeEach(() => {
|
|
85
108
|
vi.clearAllMocks();
|
|
109
|
+
getCache.mockReturnValue(null);
|
|
86
110
|
});
|
|
87
111
|
|
|
88
112
|
it('search:正常番号能返回结构完整的对象', async () => {
|
|
@@ -96,22 +120,64 @@ describe('fetcher.js', () => {
|
|
|
96
120
|
expect(result.actresses).toContain('天使もえ');
|
|
97
121
|
expect(result.releaseDate).toBe('2021-01-01');
|
|
98
122
|
expect(result.studio).toBe('SOD Create');
|
|
123
|
+
expect(getCache).toHaveBeenCalledWith('SSIS-001', 'auto');
|
|
124
|
+
expect(setCache).toHaveBeenCalledWith('SSIS-001', expect.objectContaining({ source: 'JAVBUS' }), 'auto');
|
|
99
125
|
});
|
|
100
126
|
|
|
101
|
-
it('search
|
|
102
|
-
execSync.
|
|
127
|
+
it('search:默认模式会按顺序自动兜底到下一个数据源', async () => {
|
|
128
|
+
execSync.mockImplementation((command) => {
|
|
129
|
+
if (command.includes('https://www.javbus.com/SSIS-001')) {
|
|
130
|
+
return Buffer.from(MOCK_404_HTML);
|
|
131
|
+
}
|
|
103
132
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
133
|
+
if (command.includes('https://www.njav.com/zh/xvideos/ssis-001')) {
|
|
134
|
+
return Buffer.from(MOCK_NJAV_HTML);
|
|
135
|
+
}
|
|
107
136
|
|
|
108
|
-
|
|
109
|
-
execSync.mockImplementation(() => {
|
|
110
|
-
throw new Error('Network Error');
|
|
137
|
+
throw new Error(`Unexpected command: ${command}`);
|
|
111
138
|
});
|
|
112
139
|
|
|
113
140
|
const result = await search('SSIS-001');
|
|
141
|
+
|
|
142
|
+
expect(result).not.toBeNull();
|
|
143
|
+
expect(result.source).toBe('NJAV');
|
|
144
|
+
expect(result.title).toBe('SSIS-001 NJAV 标题');
|
|
145
|
+
expect(result.studio).toBe('Studio B');
|
|
146
|
+
expect(execSync).toHaveBeenCalledTimes(2);
|
|
147
|
+
expect(setCache).toHaveBeenCalledWith('SSIS-001', expect.objectContaining({ source: 'NJAV' }), 'auto');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('search:手动指定数据源时只查询该数据源', async () => {
|
|
151
|
+
execSync.mockImplementation((command) => {
|
|
152
|
+
if (command.includes('https://www.javbus.com/SSIS-001')) {
|
|
153
|
+
return Buffer.from(MOCK_JAVBUS_HTML);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
throw new Error(`Unexpected command: ${command}`);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const result = await search('SSIS-001', 'zh', 'javbus');
|
|
160
|
+
|
|
161
|
+
expect(result).not.toBeNull();
|
|
162
|
+
expect(result.source).toBe('JAVBUS');
|
|
163
|
+
expect(execSync).toHaveBeenCalledTimes(1);
|
|
164
|
+
expect(getCache).toHaveBeenCalledWith('SSIS-001', 'javbus');
|
|
165
|
+
expect(setCache).toHaveBeenCalledWith('SSIS-001', expect.objectContaining({ source: 'JAVBUS' }), 'javbus');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('search:手动指定数据源未命中时不会回退到其他数据源', async () => {
|
|
169
|
+
execSync.mockImplementation((command) => {
|
|
170
|
+
if (command.includes('https://www.javlibrary.com/cn/vl_searchbyid.php?keyword=SSIS-001')) {
|
|
171
|
+
return Buffer.from('<html><body>empty</body></html>');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
throw new Error(`Unexpected command: ${command}`);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const result = await search('SSIS-001', 'zh', 'javlibrary');
|
|
178
|
+
|
|
114
179
|
expect(result).toBeNull();
|
|
180
|
+
expect(execSync).toHaveBeenCalledTimes(1);
|
|
115
181
|
});
|
|
116
182
|
|
|
117
183
|
it('search:返回对象包含所有预期字段', async () => {
|
|
@@ -128,4 +194,4 @@ describe('fetcher.js', () => {
|
|
|
128
194
|
expect(result).toHaveProperty(field);
|
|
129
195
|
});
|
|
130
196
|
});
|
|
131
|
-
});
|
|
197
|
+
});
|
package/Javdict-Demo.gif
DELETED
|
Binary file
|