javdict 1.3.4 → 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 CHANGED
@@ -9,7 +9,7 @@ English | [中文](README_zh.md) | [日本語](README_jp.md) | [한국어](READM
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
10
10
  [![Platform](https://img.shields.io/badge/platform-Linux%20%7C%20Windows%20%7C%20macOS-lightgrey.svg)]()
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 with no manual switching required.
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
  ![JavDict-Demo-en](https://raw.githubusercontent.com/gdjdkid/AvDict/master/assets/Javdict-Demo-en.gif)
15
15
 
@@ -17,7 +17,7 @@ Type a title ID, get back the full details — cast, release date, studio, durat
17
17
 
18
18
  ## Features
19
19
 
20
- - 🔍 **Multi-source fallback** — Queries JAVBUS → NJAV → JavLibrary → JAVDB in order; returns on first hit
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,7 +9,7 @@
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
10
10
  [![Platform](https://img.shields.io/badge/platform-Linux%20%7C%20Windows%20%7C%20macOS-lightgrey.svg)]()
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, ohne manuelles Umschalten.
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
  ![JavDict-Demo-de](https://raw.githubusercontent.com/gdjdkid/AvDict/master/assets/Javdict-Demo-de.gif)
15
15
 
@@ -17,7 +17,7 @@ Gib eine Titel-ID ein und erhalte sofort vollständige Informationen — Besetzu
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,7 +9,7 @@
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
10
10
  [![Platform](https://img.shields.io/badge/platform-Linux%20%7C%20Windows%20%7C%20macOS-lightgrey.svg)]()
11
11
 
12
- 番号を入力するだけで、出演者・発売日・メーカー・収録時間・タグなどの詳細情報を取得できます。複数のデータソースを自動的に切り替えて検索します。
12
+ 番号を入力するだけで、出演者・発売日・メーカー・収録時間・タグなどの詳細情報を取得できます。複数のデータソースを既定で自動フォールバックし、必要に応じて単一ソースを手動指定できます。
13
13
 
14
14
  ![JavDict-Demo-jp](https://raw.githubusercontent.com/gdjdkid/AvDict/master/assets/Javdict-Demo-jp.gif)
15
15
 
@@ -17,7 +17,7 @@
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,7 +9,7 @@
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
10
10
  [![Platform](https://img.shields.io/badge/platform-Linux%20%7C%20Windows%20%7C%20macOS-lightgrey.svg)]()
11
11
 
12
- 번호를 입력하면 출연진, 발매일, 제작사, 재생 시간, 태그 등 상세 정보를 바로 확인할 수 있습니다. 여러 데이터 소스를 자동으로 전환하며 검색합니다.
12
+ 번호를 입력하면 출연진, 발매일, 제작사, 재생 시간, 태그 등 상세 정보를 바로 확인할 수 있습니다. 기본값으로 여러 데이터 소스를 자동 폴백하며, 필요하면 단일 소스를 직접 지정할 수 있습니다.
13
13
 
14
14
  ![JavDict-Demo-kr](https://raw.githubusercontent.com/gdjdkid/AvDict/master/assets/Javdict-Demo-kr.gif)
15
15
 
@@ -17,7 +17,7 @@
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,7 +9,7 @@
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
10
10
  [![Platform](https://img.shields.io/badge/platform-Linux%20%7C%20Windows%20%7C%20macOS-lightgrey.svg)]()
11
11
 
12
- 输入一个车牌号,即可获取女优、发售日期、制作商、时长、类别等完整信息。支持多数据源自动兜底,无需手动切换。
12
+ 输入一个车牌号,即可获取女优、发售日期、制作商、时长、类别等完整信息。默认启用多数据源自动兜底,也支持手动指定单一数据源。
13
13
 
14
14
  ![JavDict-Demo-zh](https://raw.githubusercontent.com/gdjdkid/AvDict/master/assets/Javdict-Demo-zh.gif)
15
15
 
@@ -17,7 +17,7 @@
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 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 清空本地缓存
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
- export async function search(id, lang = 'zh') {
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
- let result = null;
344
-
345
- // 第一优先:JAVBUS(最快,直接番号拼URL)
346
- result = searchJavbus(searchId);
347
- if (result) { setCache(id, result); return result; }
348
-
349
- // 第二优先:NJAV(无需Cookie,覆盖率高,能查到JUR等小众番号)
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "javdict",
3
- "version": "1.3.4",
3
+ "version": "1.3.5",
4
4
  "description": "AV番号命令行查询工具",
5
5
  "main": "./index.js",
6
6
  "type": "module",
@@ -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
+ });
@@ -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:搜索结果为空时返回 null', async () => {
102
- execSync.mockReturnValue(Buffer.from(MOCK_404_HTML));
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
- const result = await search('INVALID-999');
105
- expect(result).toBeNull();
106
- });
133
+ if (command.includes('https://www.njav.com/zh/xvideos/ssis-001')) {
134
+ return Buffer.from(MOCK_NJAV_HTML);
135
+ }
107
136
 
108
- it('search:网络请求失败时返回 null', async () => {
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
+ });