77
88sys .path .append (os .path .dirname (os .path .dirname (os .path .abspath (__file__ ))))
99
10+ from asyncio import Semaphore
1011from clients .bangumi_client import BangumiClient
1112from clients .notion_client import NotionClient
1213from config .config_fields import FIELDS
1516from core .mapping_manager import BangumiMappingManager
1617from core .schema_manager import NotionSchemaManager
1718from utils import logger
19+ from tqdm .asyncio import tqdm_asyncio
1820
1921
2022async 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(
92118async 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
129150if __name__ == "__main__" :
0 commit comments