Skip to content

Commit b2c4a1d

Browse files
committed
fix: 修复Bangumi选择交互的参数错误并重构脚本并发模型
本次提交包含一项关键的Bug修复和对批处理脚本的重大性能重构。 **1. Bug修复:Bangumi交互流程** * **问题**: 在GUI模式下,当需要手动选择Bangumi条目时,程序因信号与槽之间的参数数量不匹配而崩溃 (`TypeError`)。 * **修复**: * 统一了从 `BangumiClient` 到 `MainWindow` 的整个调用链中 `get_bangumi_game_choice` 相关方法的签名。 * 确保了 `search_term` (游戏名) 和 `candidates` (候选列表) 两个参数被正确地一路传递。 * 现在GUI弹窗可以正确显示,并且标题会包含当前正在搜索的游戏名,提升了用户体验。 **2. 重构:脚本并发处理** * **动机**: 提高 `scripts` 目录下批处理脚本(如 `fill_missing_bangumi`)的执行效率,并为API调用提供更安全的速率限制。 * **实现**: * 为 `fill_missing_bangumi`, `update_Brand_Info`, 和 `update_brand_latestBeat` 脚本引入了 `asyncio.Semaphore` 来控制并发。 * 根据不同API(Notion为3,Bangumi为1)设置了合理的并发数,以尊重其速率限制,增强了脚本的稳定性。 * 在 `update_Brand_Info` 脚本中加入了 `tqdm` 进度条,优化了命令行下的用户体验。 **3. 数据更新** * 更新了 `bangumi_prop_mapping.json`, `tag_fanza_to_cn.json` 等映射文件。
1 parent c131e4d commit b2c4a1d

13 files changed

+224
-114
lines changed

clients/bangumi_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ async def search_and_select_bangumi_id(self, keyword: str) -> str | None:
145145
gui_candidates.append({'id': str(item['id']), 'display': display_text})
146146

147147
# Use the interaction provider to get the user's choice
148-
selected_id = await self.interaction_provider.get_bangumi_game_choice(gui_candidates)
148+
selected_id = await self.interaction_provider.get_bangumi_game_choice(keyword, gui_candidates)
149149

150150
return selected_id
151151

core/gui_worker.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class GameSyncWorker(QThread):
1919
bangumi_mapping_required = Signal(dict)
2020
property_type_required = Signal(dict)
2121
context_created = Signal(dict)
22-
bangumi_selection_required = Signal(list)
22+
bangumi_selection_required = Signal(str, list)
2323
tag_translation_required = Signal(str, str)
2424
concept_merge_required = Signal(str, str)
2525
name_split_decision_required = Signal(str, list)
@@ -109,8 +109,8 @@ def _on_bangumi_mapping_requested(self, request_data):
109109
def _on_property_type_requested(self, request_data):
110110
self.property_type_required.emit(request_data)
111111

112-
def _on_bangumi_selection_requested(self, candidates):
113-
self.bangumi_selection_required.emit(candidates)
112+
def _on_bangumi_selection_requested(self, game_name, candidates):
113+
self.bangumi_selection_required.emit(game_name, candidates)
114114

115115
def _on_tag_translation_requested(self, tag, source_name):
116116
self.tag_translation_required.emit(tag, source_name)

core/interaction.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ async def handle_new_bangumi_key(
4040
pass
4141

4242
@abstractmethod
43-
async def get_bangumi_game_choice(self, candidates: List[Dict]) -> str | None:
43+
async def get_bangumi_game_choice(self, search_term: str, candidates: List[Dict]) -> str | None:
4444
"""Ask user to select a game from Bangumi search results."""
4545
pass
4646

@@ -138,11 +138,11 @@ def _get_action_input():
138138
logger.error("输入无效,将忽略此属性。")
139139
return {"action": "ignore_session"}
140140

141-
async def get_bangumi_game_choice(self, candidates: List[Dict]) -> str | None:
141+
async def get_bangumi_game_choice(self, search_term: str, candidates: List[Dict]) -> str | None:
142142
if not candidates:
143143
return None
144144

145-
print("") # Add a newline for better formatting
145+
logger.info(f'请为 "{search_term}" 选择最匹配的 Bangumi 条目:')
146146
for candidate in candidates:
147147
print(f" {candidate['display']}")
148148
print("") # Add a newline for better formatting

gui/dialogs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ def set_result_and_accept(self, result):
8686
self.accept()
8787

8888
class BangumiSelectionDialog(QDialog):
89-
def __init__(self, candidates, parent=None):
89+
def __init__(self, game_name, candidates, parent=None):
9090
super().__init__(parent)
91-
self.setWindowTitle("手动选择Bangumi条目")
91+
self.setWindowTitle(f"为【{game_name}】选择Bangumi条目")
9292
self.setMinimumWidth(700)
9393
self.selected_id = None
9494

gui/main_window.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,14 +236,17 @@ def handle_concept_merge_required(self, concept, candidate):
236236
else:
237237
worker.set_interaction_response(None) # Cancel
238238

239-
def handle_bangumi_selection_required(self, candidates):
239+
def handle_bangumi_selection_required(self, game_name, candidates):
240240
project_logger.system("[GUI] Received bangumi_selection_required, creating dialog.")
241-
dialog = BangumiSelectionDialog(candidates, self)
241+
dialog = BangumiSelectionDialog(game_name, candidates, self)
242242
worker = self.sender()
243-
if dialog.exec() == QDialog.Accepted:
243+
244+
result = dialog.exec()
245+
246+
if result == QDialog.Accepted:
244247
worker.set_interaction_response(dialog.selected_id)
245248
else:
246-
worker.set_interaction_response(None) # User cancelled
249+
worker.set_interaction_response(None)
247250

248251
def handle_bangumi_mapping(self, request_data):
249252
project_logger.info("需要进行 Bangumi 属性映射,等待用户操作...\n")

mapping/bangumi_prop_mapping.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@
209209
"性格": [
210210
"性格"
211211
],
212+
"惯用手": [
213+
"惯用手"
214+
],
212215
"战斗": [
213216
"戦闘"
214217
],
@@ -451,7 +454,8 @@
451454
"发行日期(HD版)",
452455
"发行日期-DLsite",
453456
"发行日期-SaikeyStudios",
454-
"发行日期-Steam"
457+
"发行日期-Steam",
458+
"其他发行时间"
455459
],
456460
"售价": [
457461
"售价",
@@ -555,6 +559,9 @@
555559
"ディレクション",
556560
"制作总指挥"
557561
],
562+
"网站制作": [
563+
"WEB制作"
564+
],
558565
"美工": [
559566
"美工"
560567
],

mapping/tag_fanza_to_cn.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,39 @@
11
{
22
"3P・4P": "乱交",
3+
"お姉さん": "姐姐",
34
"お嬢様・令嬢": "大小姐",
45
"くノ一": "女忍者",
56
"アイドル": "偶像",
67
"アナル": "肛交",
78
"アニメーション": "动态",
9+
"アニメ化": "动画化",
810
"イチャラブ": "甜蜜恋爱",
911
"ウェイトレス": "女服务员",
1012
"オナニー": "自慰",
1113
"ギャグ・コメディ": "恋爱喜剧",
1214
"コスプレ": "角色扮演",
1315
"シナリオがいい": "剧情不错",
16+
"スカトロ": "重口",
1417
"ダーク系": "暗黑系",
1518
"ツインテール": "双马尾",
1619
"ドラッグ": "药物",
20+
"ネコミミ・獣系": "猫耳娘",
1721
"ハーレム": "后宫",
1822
"バトル": "战斗",
1923
"パイズリ": "乳交",
2024
"ファンタジー": "幻想",
25+
"フェラ": "口交",
2126
"フェラチオ": "口交",
2227
"メイド": "女仆",
2328
"メガネっ娘": "眼镜娘",
2429
"ラブコメ": "恋爱喜剧",
2530
"世界観がいい": "世界观好",
2631
"中出し": "中出",
2732
"乱交": "乱交",
33+
"人外": "人外",
2834
"制服": "制服",
2935
"同棲": "同居",
36+
"和服・浴衣": "和服",
3037
"変身ヒロイン": "变身女主",
3138
"外国人": "外国人",
3239
"女子校生": "女学生",
@@ -54,11 +61,14 @@
5461
"濡れスケ": "湿身透视",
5562
"田舎が舞台のゲーム": "乡下",
5663
"癒されるゲーム": "治愈",
64+
"監禁": "监禁",
5765
"美少女": "美少女",
66+
"羞恥": "羞耻",
5867
"褐色肌": "小麦肤",
5968
"辱め": "屈辱",
6069
"野外・露出": "野外露出",
6170
"音楽がいい": "音乐不错",
71+
"鬼畜": "鬼畜",
6272
"魔法少女": "魔法少女",
6373
"黒髪": "黑发"
6474
}

mapping/tag_ignore_list.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"CLEARRAVE",
77
"DL版独占販売",
88
"DMM GAME PLAYER専用",
9+
"INTERHEART作品",
910
"RPG",
1011
"Windows10対応作品",
1112
"Windows11対応作品",

mapping/tag_mapping_dict.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@
6767
"女王",
6868
"公主"
6969
],
70+
"姐姐系": [
71+
"姐姐",
72+
"姐姐系"
73+
],
7074
"学校/校园": [
7175
"学校",
7276
"学校/校园"
@@ -118,6 +122,10 @@
118122
"护士",
119123
"护士装"
120124
],
125+
"挠痒调教": [
126+
"挠痒",
127+
"挠痒调教"
128+
],
121129
"捆绑/紧缚": [
122130
"紧缚",
123131
"捆绑"
@@ -149,6 +157,10 @@
149157
"科幻(SF)": [
150158
"科幻"
151159
],
160+
"羞耻/耻辱": [
161+
"羞耻",
162+
"羞耻/耻辱"
163+
],
152164
"肛交": [
153165
"尻✕",
154166
"爆缸",
@@ -185,6 +197,10 @@
185197
"透视": [
186198
"透视"
187199
],
200+
"重口/变态": [
201+
"重口",
202+
"重口/变态"
203+
],
188204
"阴毛/腋毛": [
189205
"腋毛",
190206
"阴毛/腋毛"

scripts/fill_missing_bangumi.py

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
99

10+
from asyncio import Semaphore
1011
from clients.bangumi_client import BangumiClient
1112
from clients.notion_client import NotionClient
1213
from config.config_fields import FIELDS
@@ -15,6 +16,7 @@
1516
from core.mapping_manager import BangumiMappingManager
1617
from core.schema_manager import NotionSchemaManager
1718
from utils import logger
19+
from tqdm.asyncio import tqdm_asyncio
1820

1921

2022
async def get_games_missing_bangumi(notion_client: NotionClient) -> list:
@@ -40,45 +42,69 @@ async def get_games_missing_bangumi(notion_client: NotionClient) -> list:
4042
return all_games
4143

4244

43-
async def fill_missing_bangumi_links(
44-
notion_client: NotionClient, bangumi_client: BangumiClient
45-
):
45+
async def process_single_game(
46+
game_page: dict, notion_client: NotionClient, bangumi_client: BangumiClient
47+
) -> tuple[str, bool]:
48+
"""处理单个游戏的核心逻辑,作为一个独立的原子操作。"""
49+
page_id = game_page["id"]
50+
title = notion_client.get_page_title(game_page)
51+
logger.info(f"\n正在处理游戏: {title}")
52+
53+
try:
54+
subject_id = await bangumi_client.search_and_select_bangumi_id(title)
55+
56+
if not subject_id:
57+
logger.warn(f"❌ 未能为 '{title}' 找到匹配的 Bangumi 条目,已跳过。")
58+
return title, False
59+
60+
logger.success(f"匹配成功!Bangumi Subject ID: {subject_id}")
61+
logger.info("开始获取角色信息并更新 Notion 页面...")
62+
63+
await bangumi_client.create_or_link_characters(page_id, subject_id)
64+
65+
logger.success(f"✅ 游戏 '{title}' 的 Bangumi 信息和角色关联已全部处理完毕。")
66+
return title, True
67+
68+
except Exception as e:
69+
logger.error(f"处理游戏 '{title}' 时发生未知异常: {e}", exc_info=True)
70+
return title, False
71+
finally:
72+
# 仍然保留一个小的延时,作为最后的保险,使整体请求更平滑
73+
await asyncio.sleep(1.5)
74+
75+
76+
async def fill_missing_bangumi_links(context: dict):
4677
"""为缺少 Bangumi 链接的游戏查找、匹配并填充信息。"""
78+
notion_client = context["notion"]
79+
bangumi_client = context["bangumi"]
80+
4781
games_to_process = await get_games_missing_bangumi(notion_client)
4882
if not games_to_process:
4983
logger.info("✅ 所有游戏都已包含 Bangumi 链接,无需处理。")
5084
return
5185

5286
total = len(games_to_process)
53-
logger.info(f"找到 {total} 个缺少 Bangumi 链接的游戏,开始处理。")
54-
unmatched_titles = []
55-
56-
for idx, game_page in enumerate(games_to_process, 1):
57-
page_id = game_page["id"]
58-
title = notion_client.get_page_title(game_page)
59-
logger.info(f"\n[{idx}/{total}] 正在处理游戏: {title}")
87+
logger.info(f"找到 {total} 个缺少 Bangumi 链接的游戏,开始并发处理。")
6088

61-
try:
62-
subject_id = await bangumi_client.search_and_select_bangumi_id(title)
89+
# 在脚本内部创建信号量,限制并发处理的游戏数量
90+
semaphore = Semaphore(3)
6391

64-
if not subject_id:
65-
logger.warn(f"❌ 未能为 '{title}' 找到匹配的 Bangumi 条目,已跳过。")
66-
unmatched_titles.append(title)
67-
continue
92+
async def process_with_semaphore(game_page):
93+
async with semaphore:
94+
return await process_single_game(game_page, notion_client, bangumi_client)
6895

69-
logger.success(f"匹配成功!Bangumi Subject ID: {subject_id}")
70-
logger.info("开始获取角色信息并更新 Notion 页面...")
96+
tasks = [process_with_semaphore(gp) for gp in games_to_process]
7197

72-
await bangumi_client.create_or_link_characters(page_id, subject_id)
98+
results = await asyncio.gather(*tasks, return_exceptions=True)
7399

74-
logger.success(f"✅ 游戏 '{title}' 的 Bangumi 信息和角色关联已全部处理完毕。")
75-
76-
except Exception as e:
77-
logger.error(f"处理游戏 '{title}' 时发生未知异常: {e}", exc_info=True)
100+
unmatched_titles = []
101+
for result in results:
102+
if isinstance(result, Exception):
103+
logger.error(f"任务执行中发生严重异常: {result}")
104+
continue
105+
title, success = result
106+
if not success:
78107
unmatched_titles.append(title)
79-
80-
finally:
81-
await asyncio.sleep(1.5) # 尊重 API 速率限制
82108

83109
if unmatched_titles:
84110
logger.warn("\n--- 未匹配的游戏 ---")
@@ -92,9 +118,9 @@ async def fill_missing_bangumi_links(
92118
async def main():
93119
"""脚本独立运行时的入口函数。"""
94120
context = {}
121+
bgm_mapper = None
95122
try:
96123
async with httpx.AsyncClient(timeout=30, follow_redirects=True, http2=True) as async_client:
97-
# 1. 初始化所有必要的组件
98124
interaction_provider = ConsoleInteractionProvider()
99125
notion_client = NotionClient(NOTION_TOKEN, GAME_DB_ID, BRAND_DB_ID, async_client)
100126
schema_manager = NotionSchemaManager(notion_client)
@@ -110,20 +136,15 @@ async def main():
110136
client=async_client,
111137
interaction_provider=interaction_provider,
112138
)
113-
114-
context = {
115-
"notion": notion_client,
116-
"bangumi": bangumi_client,
117-
# ... other clients if needed by other functions
118-
}
119-
120-
# 2. 执行核心逻辑
139+
140+
context = {"notion": notion_client, "bangumi": bangumi_client}
121141
await fill_missing_bangumi_links(context)
122142

123-
# 3. 保存可能发生的映射变更
124-
bgm_mapper.save_mappings()
125143
except Exception as e:
126144
logger.error(f"脚本主函数运行出错: {e}", exc_info=True)
145+
finally:
146+
if bgm_mapper:
147+
bgm_mapper.save_mappings()
127148

128149

129150
if __name__ == "__main__":

0 commit comments

Comments
 (0)