1+ import asyncio
12import os
23import time
34
45import flet as ft
56
67from . import execute_dir
7- from .core .config .config_manager import ConfigManager
8- from .core .config .language_manager import LanguageManager
9- from .core .recording .record_manager import RecordingManager
10- from .core .runtime .process_manager import AsyncProcessManager
8+ from .core .runtime .backend_services import BackendServices
119from .core .update .update_checker import UpdateChecker
1210from .initialization .installation_manager import InstallationManager
1311from .ui .components .business .recording_card import RecordingCardManager
1816from .ui .views .recordings_view import RecordingsPage
1917from .ui .views .settings_view import SettingsPage
2018from .ui .views .storage_view import StoragePage
21- from .utils import utils
2219from .utils .logger import logger
2320
2421
2522class App :
26- def __init__ (self , page : ft .Page ):
27- self .install_progress = None
23+ def __init__ (self , page : ft .Page , services : BackendServices | None = None ):
2824 self .page = page
25+ self .install_progress = None
26+
27+ if services is None :
28+ services = BackendServices .get_or_none ()
29+ if services is None :
30+ services = BackendServices .bootstrap (execute_dir )
31+ self .services = services
32+
2933 self .run_path = execute_dir
3034 self .assets_dir = os .path .join (execute_dir , "assets" )
31- self .process_manager = AsyncProcessManager ()
32- self .config_manager = ConfigManager (self .run_path )
35+
36+ self .config_manager = services .config_manager
37+ self .process_manager = services .process_manager
38+ self .language_manager = services .language_manager
39+ self .record_manager = services .recording_manager
40+
3341 self .is_web_mode = False
3442 self .auth_manager = None
3543 self .current_username = None
@@ -41,7 +49,10 @@ def __init__(self, page: ft.Page):
4149 )
4250
4351 self .settings = SettingsPage (self )
44- self .language_manager = LanguageManager (self )
52+ services .settings_config .adopt_user_config (self .settings .user_config )
53+ services .settings_config .adopt_cookies_config (self .settings .cookies_config )
54+ services .settings_config .adopt_accounts_config (self .settings .accounts_config )
55+ services .settings_config .language_code = self .settings .language_code or services .settings_config .language_code
4556 self .language_code = self .settings .language_code
4657 self .about = AboutPage (self )
4758 self .recordings = RecordingsPage (self )
@@ -64,18 +75,82 @@ def __init__(self, page: ft.Page):
6475 ],
6576 )
6677 self .snack_bar = ShowSnackBar (self )
67- self .subprocess_start_up_info = utils . get_startup_info ()
78+ self .subprocess_start_up_info = services . subprocess_start_up_info
6879 self .record_card_manager = RecordingCardManager (self )
69- self .record_manager = RecordingManager (self )
7080 self .current_page = None
7181 self ._loading_page = False
72- self .recording_enabled = True
7382 self .install_manager = InstallationManager (self )
7483 self .update_checker = UpdateChecker (self )
7584 self .page .run_task (self .install_manager .check_env )
76- self .page .run_task (self .record_manager .check_free_space )
85+ if self .record_manager is not None :
86+ self .page .run_task (self .record_manager .check_free_space )
7787 self .page .run_task (self ._check_for_updates )
7888
89+ services .register_ui_bridge (self )
90+
91+ @property
92+ def recording_enabled (self ):
93+ return self .services .recording_enabled
94+
95+ @recording_enabled .setter
96+ def recording_enabled (self , value : bool ):
97+ self .services .recording_enabled = value
98+
99+ @property
100+ def tray_manager (self ):
101+ return self .services .tray_manager
102+
103+ @tray_manager .setter
104+ def tray_manager (self , value ):
105+ self .services .tray_manager = value
106+
107+ def _get_session_loop (self ) -> asyncio .AbstractEventLoop | None :
108+ """Return the asyncio event loop bound to the current Flet session,
109+ or ``None`` if the session/connection is dead."""
110+ try :
111+ session = self .page .session
112+ if session is None :
113+ return None
114+ connection = getattr (session , "connection" , None )
115+ if connection is None :
116+ return None
117+ return getattr (connection , "loop" , None )
118+ except Exception :
119+ return None
120+
121+ def schedule_card_update (self , recording ) -> None :
122+ loop = self ._get_session_loop ()
123+ if loop is None :
124+ return
125+ try :
126+ asyncio .run_coroutine_threadsafe (self .record_card_manager .update_card (recording ), loop )
127+ except Exception as exc :
128+ logger .debug (f"schedule_card_update dropped: { exc } " )
129+
130+ def schedule_card_remove (self , recordings ) -> None :
131+ loop = self ._get_session_loop ()
132+ if loop is None :
133+ return
134+ try :
135+ asyncio .run_coroutine_threadsafe (self .record_card_manager .remove_recording_card (recordings ), loop )
136+ except Exception as exc :
137+ logger .debug (f"schedule_card_remove dropped: { exc } " )
138+
139+ def schedule_snack (self , text : str , ** kw ) -> None :
140+ loop = self ._get_session_loop ()
141+ if loop is None :
142+ return
143+ try :
144+ asyncio .run_coroutine_threadsafe (self .snack_bar .show_snack_bar (text , ** kw ), loop )
145+ except Exception as exc :
146+ logger .debug (f"schedule_snack dropped: { exc } " )
147+
148+ def schedule_pubsub (self , topic : str , payload ) -> None :
149+ try :
150+ self .page .pubsub .send_others_on_topic (topic , payload )
151+ except Exception as exc :
152+ logger .debug (f"schedule_pubsub dropped: { exc } " )
153+
79154 def initialize_pages (self ):
80155 return {
81156 "settings" : self .settings ,
@@ -104,12 +179,15 @@ async def clear_content_area(self):
104179 self .content_area .update ()
105180
106181 async def cleanup (self ):
107- try :
108- await self .process_manager .cleanup ()
109- except ConnectionError :
110- logger .warning ("Connection lost, process may have terminated" )
111- except Exception as e :
112- logger .error (f"Error during cleanup: { e } " )
182+ self .services .unregister_ui_bridge (self )
183+
184+ if not self .is_web_mode :
185+ try :
186+ await self .process_manager .cleanup ()
187+ except ConnectionError :
188+ logger .warning ("Connection lost, process may have terminated" )
189+ except Exception as e :
190+ logger .error (f"Error during cleanup: { e } " )
113191
114192 def add_ffmpeg_process (self , process ):
115193 self .process_manager .add_process (process )
@@ -130,10 +208,13 @@ async def _check_for_updates(self):
130208 await self .config_manager .save_user_config (self .settings .user_config )
131209
132210 if update_info .get ("has_update" , False ):
133- await self .update_checker .show_update_dialog (update_info )
211+ await self .update_checker .show_update_dialog (dict ( update_info ) )
134212 except Exception as e :
135213 logger .error (f"Update check failed: { e } " )
136214
137215 async def start_periodic_tasks (self ):
138- """Start all periodic tasks"""
139- await self .record_manager .setup_periodic_live_check (int (self .record_manager .loop_time_seconds or 180 ))
216+ rm = self .record_manager
217+ if rm is None :
218+ return
219+ interval = int (rm .loop_time_seconds or 180 )
220+ await rm .setup_periodic_live_check (interval )
0 commit comments