diff --git a/CastAPI.py b/CastAPI.py index 1f5f529..cac7bb3 100644 --- a/CastAPI.py +++ b/CastAPI.py @@ -2564,6 +2564,8 @@ async def cast_manage_page(): async def tabs_info_page(): + """ generate action/info page split by classes and show all running casts """ + # grab data info_data = await util_casts_info() # take only info data key @@ -2595,13 +2597,16 @@ async def tabs_info_page(): with tabs: p_desktop = ui.tab('Desktop', icon='computer').classes('bg-slate-400') p_media = ui.tab('Media', icon='image').classes('bg-slate-400') + if Desktop.count > Media.count: tab_to_show = p_desktop elif Desktop.count < Media.count: tab_to_show = p_media else: tab_to_show = '' + with (ui.tab_panels(tabs, value=tab_to_show).classes('w-full')): + with ui.tab_panel(p_desktop): if not desktop_threads: ui.label('No CAST').classes('animate-pulse') \ @@ -2638,19 +2643,44 @@ async def tabs_info_page(): ''') await nice.generate_actions_to_cast('Media', media_threads, action_to_casts, info_data) -def action_to_casts(class_name, cast_name, action, params, clear, execute, exp_item=None): - action_to_thread(class_name, cast_name, action, params, clear, execute) - if action == 'stop': - exp_item.close() - ui.notification(f'Stopping {cast_name}...', type='warning', position='center', timeout=1) - exp_item.delete() - elif action == 'shot': - ui.notification(f'Saving image to buffer for {cast_name}...', type='positive', timeout=1) - elif action == 'close_preview': - ui.notification(f'Preview window terminated for {cast_name}...', type='info', timeout=1) + +async def action_to_casts(class_name, cast_name, action, params, clear, execute, exp_item=None): + """ execute action from icon click and display a message """ + + def valid_ip(): + if new_ip.value == '127.0.0.1' or Utils.check_ip_alive(new_ip.value): + action_to_thread(class_name, cast_name, action, new_ip.value, clear, execute) + ui.notification('IP address applied', type='positive', position='center', timeout=2) + else: + ui.notification('Bad IP address or not reachable', type='negative', position='center', timeout=2) + + if action == 'host': + with ui.dialog() as dialog, ui.card() as ip_card: + dialog.open() + ip_card.classes('w-full') + with ui.row(): + new_ip = ui.input('IP',placeholder='Enter new IP address') + ui.button('OK', on_click=valid_ip) + + ui.notification(f'Change IP address for {cast_name}...', type='info', position='top', timeout=2) + + else: + + action_to_thread(class_name, cast_name, action, params, clear, execute) + + if action == 'stop': + exp_item.close() + ui.notification(f'Stopping {cast_name}...', type='warning', position='center', timeout=1) + exp_item.delete() + elif action == 'shot': + ui.notification(f'Saving image to buffer for {cast_name}...', type='positive', timeout=1) + elif action == 'close_preview': + ui.notification(f'Preview window terminated for {cast_name}...', type='info', timeout=1) async def show_thread_info(): + """ show all info from running cats """ + dialog = ui.dialog().props(add='transition-show="slide-down" transition-hide="slide-up"') with dialog, ui.card(): cast_info = await util_casts_info() diff --git a/ddp_queue.py b/ddp_queue.py index 337b55a..d31bb12 100644 --- a/ddp_queue.py +++ b/ddp_queue.py @@ -41,7 +41,6 @@ class DDPDevice: def __init__(self, dest, port=4048): self._online = None - self.name = dest self.frame_count = 0 self.retry_number = 0 self.connection_warning = False @@ -80,12 +79,12 @@ def flush_from_queue(self, data): retry_number=self.retry_number ) if self.connection_warning: - logger.warning(f"DDP connection reestablished to {self.name}") + logger.warning(f"DDP connection reestablished to {self._destination}") self.connection_warning = False self._online = True except OSError as error: if not self.connection_warning: - logger.error(f"Error in DDP connection to {self.name}: {error}") + logger.error(f"Error in DDP connection to {self._destination}: {error}") self.connection_warning = True self._online = False diff --git a/desktop.py b/desktop.py index 3e8f2f7..cf9dd68 100644 --- a/desktop.py +++ b/desktop.py @@ -614,8 +614,10 @@ def send_multicast_images_to_ips(images_buffer, to_ip_addresses): elif "host" in action: ip_addresses[0] = params - if params != '127.0.0.1': - ddp_host = DDPDevice(params) + if ddp_host is not None: + ddp_host._destination = params + else: + ddp_host=DDPDevice(params) except Exception as error: logger.error(traceback.format_exc()) diff --git a/media.py b/media.py index a07703d..786b6de 100644 --- a/media.py +++ b/media.py @@ -620,7 +620,9 @@ def send_multicast_images_to_ips(images_buffer, to_ip_addresses): elif "host" in action: ip_addresses[0] = params - if params != '127.0.0.1': + if ddp_host is not None: + ddp_host._destination = params + else: ddp_host = DDPDevice(params) except Exception as error: diff --git a/niceutils.py b/niceutils.py index 537de5a..1fba9e0 100644 --- a/niceutils.py +++ b/niceutils.py @@ -27,6 +27,9 @@ from pathlib import Path from typing import Optional +if sys.platform.lower() == 'win32': + import win32api + """ When this env var exist, this mean run from the one-file compressed executable. Load of the config is not possible, folder config should not exist yet. @@ -575,27 +578,36 @@ async def generate_actions_to_cast(class_name, class_threads, action_to_casts, i clear=False, execute=True) ).classes('shadow-lg').tooltip('Stop Preview') + ui.button(icon='settings_ethernet', + on_click=lambda item_v=item_th: action_to_casts(class_name=class_name, + cast_name=item_v, + action='host', + params='', + clear=False, + execute=True) + ).classes('shadow-lg').tooltip('Change IP devices') editor = ui.json_editor({'content': {'json': info_data[item_th]["data"]}}) \ .run_editor_method('updateProps', {'readOnly': True}) class LocalFilePicker(ui.dialog): + """Local File Picker + + This is simple file picker that allows you to select a file from the local filesystem where NiceGUI is running. + Right-click on a file will display image if available. + + :param directory: The directory to start in. + :param upper_limit: The directory to stop at (None: no limit, default: same as the starting directory). + :param multiple: Whether to allow multiple files to be selected. + :param show_hidden_files: Whether to show hidden files. + :param thumbs : generate thumbnails + """ def __init__(self, directory: str, *, upper_limit: Optional[str] = ..., multiple: bool = False, show_hidden_files: bool = False, thumbs: bool = True) -> None: - """Local File Picker - - This is simple file picker that allows you to select a file from the local filesystem where NiceGUI is running. - Right-click on a file will display image if available. - :param directory: The directory to start in. - :param upper_limit: The directory to stop at (None: no limit, default: same as the starting directory). - :param multiple: Whether to allow multiple files to be selected. - :param show_hidden_files: Whether to show hidden files. - :param thumbs : generate thumbnails - """ super().__init__() self.drives_toggle = None @@ -629,7 +641,6 @@ def __init__(self, directory: str, *, def add_drives_toggle(self): if sys.platform.lower() == 'win32': - import win32api drives = win32api.GetLogicalDriveStrings().split('\000')[:-1] self.drives_toggle = ui.toggle(drives, value=drives[0], on_change=self.update_drive) diff --git a/requirements.txt b/requirements.txt index adce58e..510b47c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,6 +23,7 @@ av==13.0.0 youtube-search-python==1.6.6 # Platform dependant +win32api ; sys_platform == "win32" pywebview==5.1 ; sys_platform != "linux" pywebview[qt]==5.1 ; sys_platform == "linux" pycairo ; sys_platform == "linux" diff --git a/utils.py b/utils.py index f64c479..69bd422 100644 --- a/utils.py +++ b/utils.py @@ -17,7 +17,7 @@ print(f'INFO : this is Not a YT version: {e}') import inspect -# import concurrent_log_handler +import concurrent_log_handler import json from asyncio import create_task