1414
1515class GameSyncWorker (QThread ):
1616 process_completed = Signal (bool )
17+ # --- Signals to MainWindow to request showing a dialog ---
1718 selection_required = Signal (list , str , str )
1819 duplicate_check_required = Signal (list )
1920 bangumi_mapping_required = Signal (dict )
@@ -31,9 +32,6 @@ def __init__(self, keyword, manual_mode=False, parent=None, shared_context=None)
3132 self .manual_mode = manual_mode
3233 self .shared_context = shared_context
3334 self .context = {}
34- self .mutex = QMutex ()
35- self .wait_condition = QWaitCondition ()
36- self .user_choice = None
3735 self .interaction_provider = None
3836 self .loop = None
3937
@@ -55,19 +53,20 @@ async def setup_context():
5553 self .context = {** self .shared_context , ** loop_specific_context }
5654
5755 try :
58- # Run setup first to create the provider
5956 self .loop .run_until_complete (setup_context ())
6057
61- # Then connect signals
58+ # Connect all interaction signals from the provider to the worker's proxy slots
6259 self .interaction_provider .handle_new_bangumi_key_requested .connect (self ._on_bangumi_mapping_requested )
6360 self .interaction_provider .ask_for_new_property_type_requested .connect (self ._on_property_type_requested )
6461 self .interaction_provider .select_bangumi_game_requested .connect (self ._on_bangumi_selection_requested )
6562 self .interaction_provider .tag_translation_required .connect (self ._on_tag_translation_requested )
6663 self .interaction_provider .concept_merge_required .connect (self ._on_concept_merge_requested )
6764 self .interaction_provider .name_split_decision_required .connect (self ._on_name_split_decision_requested )
6865 self .interaction_provider .confirm_brand_merge_requested .connect (self ._on_brand_merge_requested )
66+ # --- Newly refactored signal connections ---
67+ self .interaction_provider .select_game_requested .connect (self ._on_select_game_requested )
68+ self .interaction_provider .duplicate_check_requested .connect (self ._on_duplicate_check_requested )
6969
70- # Now run the main game flow
7170 self .loop .run_until_complete (self .game_flow ())
7271
7372 except Exception as e :
@@ -77,18 +76,14 @@ async def setup_context():
7776 finally :
7877 if self .interaction_provider :
7978 try :
80- self .interaction_provider .handle_new_bangumi_key_requested .disconnect (self ._on_bangumi_mapping_requested )
81- self .interaction_provider .ask_for_new_property_type_requested .disconnect (self ._on_property_type_requested )
82- self .interaction_provider .select_bangumi_game_requested .disconnect (self ._on_bangumi_selection_requested )
83- self .interaction_provider .tag_translation_required .disconnect (self ._on_tag_translation_requested )
84- self .interaction_provider .concept_merge_required .disconnect (self ._on_concept_merge_requested )
85- self .interaction_provider .name_split_decision_required .disconnect (self ._on_name_split_decision_requested )
86- self .interaction_provider .confirm_brand_merge_requested .disconnect (self ._on_brand_merge_requested )
87- except RuntimeError :
79+ # Disconnect all signals
80+ for signal_name in dir (self .interaction_provider ):
81+ if isinstance (getattr (self .interaction_provider , signal_name ), Signal ):
82+ getattr (self .interaction_provider , signal_name ).disconnect ()
83+ except (RuntimeError , TypeError ):
8884 pass
8985
9086 async def cleanup_tasks ():
91- # Cancel background tasks first
9287 background_tasks = self .context .get ("background_tasks" , [])
9388 if background_tasks :
9489 logger .system (f"正在取消 { len (background_tasks )} 个后台任务..." )
@@ -97,7 +92,6 @@ async def cleanup_tasks():
9792 await asyncio .gather (* background_tasks , return_exceptions = True )
9893 logger .system ("所有后台任务已处理。" )
9994
100- # Close HTTP client
10195 if self .context .get ("async_client" ):
10296 await self .context ["async_client" ].aclose ()
10397 logger .system ("线程内HTTP客户端已关闭。" )
@@ -107,6 +101,7 @@ async def cleanup_tasks():
107101
108102 self .loop .close ()
109103
104+ # --- Proxy slots to forward signals from InteractionProvider to MainWindow ---
110105 def _on_bangumi_mapping_requested (self , request_data ):
111106 self .bangumi_mapping_required .emit (request_data )
112107
@@ -128,40 +123,18 @@ def _on_name_split_decision_requested(self, text, parts):
128123 def _on_brand_merge_requested (self , new_brand_name , suggested_brand ):
129124 self .confirm_brand_merge_requested .emit (new_brand_name , suggested_brand )
130125
126+ def _on_select_game_requested (self , choices , title , source ):
127+ self .selection_required .emit (choices , title , source )
128+
129+ def _on_duplicate_check_requested (self , candidates ):
130+ self .duplicate_check_required .emit (candidates )
131+
132+ # --- Method for MainWindow to send response back ---
131133 def set_interaction_response (self , response ):
132134 if self .loop and self .interaction_provider :
133135 self .loop .call_soon_threadsafe (self .interaction_provider .set_response , response )
134136
135- def set_choice (self , choice ):
136- self .mutex .lock ()
137- self .user_choice = choice
138- self .mutex .unlock ()
139- self .wait_condition .wakeAll ()
140-
141- async def wait_for_choice (self , choices : list , title : str , source : str = "" ):
142- # 1. 先发射信号,此时不持有任何锁,避免死锁
143- if source :
144- self .selection_required .emit (choices , title , source )
145- else :
146- self .duplicate_check_required .emit (choices )
147-
148- # 2. 现在获取锁,并等待主线程的响应
149- self .mutex .lock ()
150- try :
151- # 为等待用户输入设置60秒超时
152- timed_out = not self .wait_condition .wait (self .mutex , 60000 )
153- if timed_out and self .user_choice is None :
154- logger .warn ("等待用户选择超时(60秒),将自动执行‘跳过’操作。" )
155- choice = "skip"
156- else :
157- choice = self .user_choice
158- finally :
159- # 3. 重置选择并解锁,为下一次交互做准备
160- self .user_choice = None
161- self .mutex .unlock ()
162-
163- return choice
164-
137+ # --- Core async logic ---
165138 async def _select_game_from_results (self , results , source ):
166139 game = None
167140 while True :
@@ -178,7 +151,9 @@ async def _select_game_from_results(self, results, source):
178151 logger .info (f"智能模式匹配度 ({ best_score :.2f} ) 过低,转为手动选择。" )
179152
180153 if game is None :
181- choice = await self .wait_for_choice (results , f"请从 { source .upper ()} 结果中选择" , source )
154+ # REFACTORED: Call the provider instead of wait_for_choice
155+ choice = await self .interaction_provider .select_game (results , f"请从 { source .upper ()} 结果中选择" , source )
156+
182157 if choice == "search_fanza" :
183158 logger .info ("切换到 Fanza 搜索..." )
184159 results , source = await search_all_sites (self .context ["dlsite" ], self .context ["fanza" ], self .keyword , site = "fanza" )
@@ -198,7 +173,9 @@ async def _check_for_duplicates(self, title):
198173 if not candidates :
199174 return None
200175
201- choice = await self .wait_for_choice (candidates , "发现重复游戏" )
176+ # REFACTORED: Call the provider instead of wait_for_choice
177+ choice = await self .interaction_provider .confirm_duplicate (candidates )
178+
202179 if choice == "skip" :
203180 logger .info ("已选择跳过。" )
204181 return "skip"
@@ -209,7 +186,7 @@ async def _check_for_duplicates(self, title):
209186 elif choice == "create" :
210187 logger .info ("已选择强制创建新游戏。" )
211188 return None
212- return None
189+ return None # Default to cancel
213190
214191 async def _fetch_ggbases_data (self , keyword , manual_mode ):
215192 logger .info ("[GGBases] 开始获取 GGBases 数据..." )
@@ -222,7 +199,8 @@ async def _fetch_ggbases_data(self, keyword, manual_mode):
222199 selected_game = None
223200 if manual_mode :
224201 logger .info ("[GGBases] 手动模式,需要用户选择。" )
225- choice = await self .wait_for_choice (candidates , "请从GGBases结果中选择" , "ggbases" )
202+ # REFACTORED: Call the provider instead of wait_for_choice
203+ choice = await self .interaction_provider .select_game (candidates , "请从GGBases结果中选择" , "ggbases" )
226204 if isinstance (choice , int ) and choice != - 1 :
227205 selected_game = candidates [choice ]
228206 else :
0 commit comments