Skip to content

Commit 91bd476

Browse files
committed
feat: bump to v0.5.4 with add game completion status and optimize mixed API logic
- Add game completion status management (mark as completed/uncompleted) - Optimize mixed API - Add mixed api ID search mode support (BGM ID or VNDB ID)
1 parent f5ce015 commit 91bd476

28 files changed

Lines changed: 551 additions & 182 deletions

README.ja_JP.md

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,43 +17,52 @@
1717

1818
</div>
1919

20-
## 技術スタック
20+
## Stacks
2121

2222
- Tauri 2.0
23+
2324
- React
25+
2426
- Material UI
27+
2528
- UnoCSS
29+
2630
- Zustand
31+
2732
- Sqlite
33+
2834
- Rust
2935

3036
## Todo
3137

32-
- [x] ゲームを起動するための実行ファイルを追加
33-
- [x] ローカルゲームフォルダを開く
34-
- [x] ホームページ機能
35-
- [x] ゲーム検索のためのVNDB APIを追加
36-
- [x] 多言語対応
37-
- [ ] ゲームのカスタムデータ
38-
- [x] プレイ時間のカウント
39-
- [ ] 各ページの美化
40-
- [x] 詳細ページのデザイン
41-
- [x] データベースクエリのリファクタリング
42-
- [x] ゲーム検索のための混合APIを追加
43-
- [x] 編集ページ機能
44-
45-
## デモバージョン
46-
47-
##### フロントエンドデモ
48-
- ウェブ版を試す: [https://reina.huoshen80.top](https://reina.huoshen80.top)
49-
50-
##### デスクトップアプリケーションデモ
51-
- GitHub Actionsから最新のビルドをダウンロード:
52-
1. [Actionsページ](https://github.com/huoshen80/ReinaManager/actions/workflows/build.yml)に移動
53-
2. 最新の成功したワークフローの実行をクリック
54-
3. 「Artifacts」セクションまでスクロール
55-
4. 必要なアーティファクトをダウンロード
56-
57-
## ライセンス
58-
59-
このプロジェクトは [AGPL-3.0 ライセンス](https://github.com/huoshen80/ReinaManager#AGPL-3.0-1-ov-file) の下で提供されています
38+
- [x] Add exe to Launch games
39+
- [x] Open the local game folder
40+
- [x] Home page functions
41+
- [x] Add VNDB api to search games
42+
- [x] I18n support
43+
- [ ] Custom data of games
44+
- [x] Count the time spent playing
45+
- [ ] Beautify individual pages
46+
- [x] Design the detail page
47+
- [x] Refactor database queries
48+
- [x] Add mixed api to search games
49+
- [x] Edit page functions
50+
- [ ] Auto backup function
51+
- [ ] Sync games status with Bangumi
52+
53+
54+
## Demo Versions
55+
56+
##### Frontend Demo
57+
- Try the web version: [https://reina.huoshen80.top](https://reina.huoshen80.top)
58+
59+
##### Desktop Application Demo
60+
- Download the latest build from GitHub Actions:
61+
1. Go to [Actions page](https://github.com/huoshen80/ReinaManager/actions/workflows/build.yml)
62+
2. Click on the most recent successful workflow run
63+
3. Scroll down to the "Artifacts" section
64+
4. Download the artifact you want
65+
66+
## License
67+
68+
This project is licensed under the [AGPL-3.0 license](https://github.com/huoshen80/ReinaManager#AGPL-3.0-1-ov-file)

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ The `Reina` in the name is the character <a href="https://vndb.org/c64303"><b>
4747
- [x] Refactor database queries
4848
- [x] Add mixed api to search games
4949
- [x] Edit page functions
50+
- [ ] Auto backup function
51+
- [ ] Sync games status with Bangumi
5052

5153

5254
## Demo Versions

README.zh_CN.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,17 @@
2020
## 技术栈
2121

2222
- Tauri 2.0
23+
2324
- React
25+
2426
- Material UI
27+
2528
- UnoCSS
29+
2630
- Zustand
31+
2732
- Sqlite
33+
2834
- Rust
2935

3036
## 待办事项
@@ -41,6 +47,9 @@
4147
- [x] 重构数据库查询
4248
- [x] 添加混合API搜索游戏
4349
- [x] 编辑页面功能
50+
- [ ] 自动备份功能
51+
- [ ] 与Bangumi同步游戏状态
52+
4453

4554
## 演示版本
4655

README.zh_TW.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,17 @@
2020
## 技術棧
2121

2222
- Tauri 2.0
23+
2324
- React
25+
2426
- Material UI
27+
2528
- UnoCSS
29+
2630
- Zustand
31+
2732
- Sqlite
33+
2834
- Rust
2935

3036
## 待辦事項
@@ -41,6 +47,9 @@
4147
- [x] 重構數據庫查詢
4248
- [x] 添加混合API搜尋遊戲
4349
- [x] 編輯頁面功能
50+
- [ ] 自動備份功能
51+
- [ ] 與Bangumi同步遊戲狀態
52+
4453

4554
## 演示版本
4655

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "reinamanager",
33
"private": true,
4-
"version": "0.5.3",
4+
"version": "0.5.4",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

src-tauri/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ReinaManager"
3-
version = "0.5.3"
3+
version = "0.5.4"
44
description = "A lightweight visual novel manager"
55
authors = ["huoshen80"]
66
license = "GNU AFFERO GENERAL PUBLIC LICENSE"

src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ import { AliveScope } from 'react-activation';
3737
*/
3838
const App: React.FC = () => {
3939
const { t, i18n } = useTranslation();
40-
const { games } = useStore();
40+
const { allGames } = useStore();
4141
// 动态生成游戏库子菜单
42-
const lists = games.map((game) => {
42+
const lists = allGames.map((game) => {
4343
const title = getGameDisplayName(game, i18n.language);
4444

4545
return {

src/api/mixed.ts

Lines changed: 133 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,146 @@
1+
/**
2+
* @file 混合数据获取 API 封装
3+
* @description 同时从 Bangumi 和 VNDB 获取游戏信息并合并数据
4+
* @module src/api/mixed
5+
* @author ReinaManager
6+
* @copyright AGPL-3.0
7+
* * 逻辑说明:
8+
* 1. 根据传入的 ID 类型智能选择获取策略:
9+
* - 只有 BGM ID:先获取 BGM 数据,再用其名称搜索 VNDB
10+
* - 只有 VNDB ID:先获取 VNDB 数据,再用其名称搜索 BGM
11+
* - 两个 ID 都有:并行获取两个数据源
12+
* - 没有 ID:用名称搜索,优先 BGM,失败则尝试 VNDB
13+
* 2. 使用安全模式避免单个数据源失败导致整体失败
14+
* 3. 合并数据时以 BGM 数据为主,VNDB 数据为补充
15+
* 4. 确保至少有一个数据源成功才返回结果
16+
*
17+
* 主要导出:
18+
* - fetchMixedData:混合获取游戏数据的主函数
19+
*/
20+
121
import { fetchFromBgm } from "./bgm";
222
import { fetchFromVNDB } from "./vndb";
23+
import type { GameData } from "../types";
324

4-
const fetchMixedData = async (name: string, BGM_TOKEN: string, bgm_id?: string, vndb_id?: string) => {
25+
const fetchMixedData = async (
26+
name: string,
27+
BGM_TOKEN: string,
28+
bgm_id?: string,
29+
vndb_id?: string
30+
): Promise<GameData | string> => {
531
try {
6-
let BGMdata = null;
7-
let VNDBdata = null;
8-
9-
if (bgm_id) {
10-
BGMdata = await fetchFromBgm(name, BGM_TOKEN, bgm_id);
32+
let BGMdata: GameData | null = null;
33+
let VNDBdata: GameData | null = null;
34+
35+
// 根据传入的ID类型决定获取策略
36+
if (bgm_id && !vndb_id) {
37+
// 只有 Bangumi ID:先获取 BGM 数据,再用 BGM 名称搜索 VNDB
38+
BGMdata = await getBangumiData(name, BGM_TOKEN, bgm_id);
39+
VNDBdata = await getVNDBDataSafely(BGMdata.name);
40+
} else if (vndb_id && !bgm_id) {
41+
// 只有 VNDB ID:先获取 VNDB 数据,再用 VNDB 名称搜索 BGM
42+
VNDBdata = await getVNDBData(name, vndb_id);
43+
BGMdata = await getBangumiDataSafely(VNDBdata.name, BGM_TOKEN);
44+
} else if (bgm_id && vndb_id) {
45+
// 有两个 ID:直接分别获取
46+
const [bgmResult, vndbResult] = await Promise.allSettled([
47+
getBangumiData(name, BGM_TOKEN, bgm_id),
48+
getVNDBData(name, vndb_id)
49+
]);
50+
51+
if (bgmResult.status === 'fulfilled') BGMdata = bgmResult.value;
52+
if (vndbResult.status === 'fulfilled') VNDBdata = vndbResult.value;
1153
} else {
12-
BGMdata = await fetchFromBgm(name, BGM_TOKEN);
13-
}
14-
15-
if (!BGMdata || typeof BGMdata === "string") {
16-
throw new Error(`Bangumi 数据获取失败: ${BGMdata}`);
54+
// 没有 ID:用名称搜索,优先 BGM
55+
BGMdata = await getBangumiDataSafely(name, BGM_TOKEN);
56+
if (BGMdata) {
57+
VNDBdata = await getVNDBDataSafely(BGMdata.name);
58+
} else {
59+
// 如果 BGM 搜索失败,尝试 VNDB
60+
VNDBdata = await getVNDBDataSafely(name);
61+
}
1762
}
1863

19-
if (vndb_id) {
20-
VNDBdata = await fetchFromVNDB(BGMdata.name, vndb_id);
21-
} else {
22-
VNDBdata = await fetchFromVNDB(BGMdata.name);
64+
// 确保至少有一个数据源成功
65+
if (!BGMdata && !VNDBdata) {
66+
throw new Error("无法从任何数据源获取游戏信息");
2367
}
68+
69+
// 合并数据
70+
return mergeData(BGMdata, VNDBdata);
71+
72+
} catch (error) {
73+
console.error("Mixed API 调用失败:", error instanceof Error ? error.message : error);
74+
return "获取数据失败,请稍后重试";
75+
}
76+
};
2477

25-
if (!VNDBdata || typeof VNDBdata === "string") {
26-
throw new Error(`VNDB 数据获取失败: ${VNDBdata}`);
27-
}
78+
// 辅助函数:获取Bangumi数据(严格模式,失败时抛出异常)
79+
async function getBangumiData(name: string, BGM_TOKEN: string, bgm_id?: string): Promise<GameData> {
80+
const BGMdata = await fetchFromBgm(name, BGM_TOKEN, bgm_id);
81+
82+
if (!BGMdata || typeof BGMdata === "string") {
83+
throw new Error(`Bangumi 数据获取失败: ${BGMdata}`);
84+
}
85+
86+
return BGMdata;
87+
}
2888

29-
return {
30-
bgm_id: BGMdata.bgm_id,
31-
vndb_id: VNDBdata.vndb_id,
32-
id_type: "mixed",
33-
date: BGMdata.date || VNDBdata.date,
34-
image: BGMdata.image || VNDBdata.image,
35-
summary: BGMdata.summary || VNDBdata.summary,
36-
name: BGMdata.name || VNDBdata.name,
37-
name_cn: BGMdata.name_cn || VNDBdata.name_cn,
38-
all_titles: VNDBdata.all_titles || [],
39-
tags: BGMdata.tags || VNDBdata.tags,
40-
rank: BGMdata.rank || undefined,
41-
score: BGMdata.score || VNDBdata.score,
42-
developer: BGMdata.developer || VNDBdata.developer,
43-
aveage_hours: VNDBdata.aveage_hours || undefined,
44-
};
45-
} catch (error) {
46-
Promise.reject(new Error("Mixed API 调用失败"));
47-
if (error instanceof Error) {
48-
console.error("错误消息:", error.message);
49-
}
50-
return "获取数据失败,请稍后重试";
89+
// 辅助函数:获取Bangumi数据(安全模式,失败时返回null)
90+
async function getBangumiDataSafely(name: string, BGM_TOKEN: string, bgm_id?: string): Promise<GameData | null> {
91+
try {
92+
return await getBangumiData(name, BGM_TOKEN, bgm_id);
93+
} catch {
94+
return null;
95+
}
96+
}
97+
98+
// 辅助函数:获取VNDB数据(严格模式,失败时抛出异常)
99+
async function getVNDBData(searchName: string, vndb_id?: string): Promise<GameData> {
100+
const VNDBdata = await fetchFromVNDB(searchName, vndb_id);
101+
102+
if (!VNDBdata || typeof VNDBdata === "string") {
103+
throw new Error(`VNDB 数据获取失败: ${VNDBdata}`);
104+
}
105+
106+
return VNDBdata;
107+
}
108+
109+
// 辅助函数:获取VNDB数据(安全模式,失败时返回null)
110+
async function getVNDBDataSafely(searchName: string, vndb_id?: string): Promise<GameData | null> {
111+
try {
112+
return await getVNDBData(searchName, vndb_id);
113+
} catch {
114+
return null;
115+
}
116+
}
117+
118+
// 辅助函数:合并数据
119+
function mergeData(BGMdata: GameData | null, VNDBdata: GameData | null): GameData {
120+
// 如果只有一个数据源,直接返回
121+
if (!BGMdata && VNDBdata) return VNDBdata;
122+
if (BGMdata && !VNDBdata) return BGMdata;
123+
if (!BGMdata && !VNDBdata) {
124+
throw new Error("没有可用的数据进行合并");
125+
}
126+
127+
// 两个数据源都有,进行合并(BGM 数据优先)
128+
return {
129+
bgm_id: BGMdata?.bgm_id || null,
130+
vndb_id: VNDBdata?.vndb_id || null,
131+
id_type: "mixed",
132+
date: BGMdata?.date || VNDBdata?.date,
133+
image: BGMdata?.image || VNDBdata?.image,
134+
summary: BGMdata?.summary || VNDBdata?.summary,
135+
name: BGMdata?.name || VNDBdata?.name || '',
136+
name_cn: BGMdata?.name_cn || VNDBdata?.name_cn,
137+
all_titles: VNDBdata?.all_titles || BGMdata?.all_titles || [],
138+
tags: BGMdata?.tags || VNDBdata?.tags,
139+
rank: BGMdata?.rank || undefined,
140+
score: BGMdata?.score || VNDBdata?.score,
141+
developer: BGMdata?.developer || VNDBdata?.developer,
142+
aveage_hours: VNDBdata?.aveage_hours || undefined,
143+
};
51144
}
52-
};
53145

54146
export default fetchMixedData;

0 commit comments

Comments
 (0)