From 630a8c7180650eb0a078014d8e869ace79d1e9e2 Mon Sep 17 00:00:00 2001 From: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> Date: Sat, 5 Sep 2026 15:25:48 -0400 Subject: chore: remove arm runtime support --- py_modules/lsfg_vk/configuration.py | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) (limited to 'py_modules/lsfg_vk/configuration.py') diff --git a/py_modules/lsfg_vk/configuration.py b/py_modules/lsfg_vk/configuration.py index fce3738..de11b48 100644 --- a/py_modules/lsfg_vk/configuration.py +++ b/py_modules/lsfg_vk/configuration.py @@ -8,7 +8,6 @@ from typing import Dict, Any from .base_service import BaseService from .config_schema import ConfigurationManager, CONFIG_SCHEMA, ProfileData, DEFAULT_PROFILE_NAME from .config_schema_generated import ConfigurationData, get_script_generation_logic -from .constants import ARMADA_DEVICE_ENV, ARMADA_GAME_LAUNCH from .types import ConfigurationResponse, ProfilesResponse, ProfileResponse @@ -124,8 +123,10 @@ class ConfigurationService(BaseService): generate_script_lines = get_script_generation_logic() lines.extend(generate_script_lines(config)) - lines.append("export LSFG_PROCESS=decky-lsfg-vk") - lines.extend(self._generate_game_launch_lines()) + lines.extend([ + "export LSFG_PROCESS=decky-lsfg-vk", + 'exec "$@"' + ]) return "\n".join(lines) + "\n" @@ -153,29 +154,13 @@ class ConfigurationService(BaseService): generate_script_lines = get_script_generation_logic() lines.extend(generate_script_lines(merged_config)) - lines.append(f"export LSFG_PROCESS={current_profile}") - lines.extend(self._generate_game_launch_lines()) + lines.extend([ + f"export LSFG_PROCESS={current_profile}", + 'exec "$@"' + ]) return "\n".join(lines) + "\n" - @staticmethod - def _generate_game_launch_lines() -> list[str]: - """Generate a portable exec block with Armada's host wrapper.""" - device_env = ARMADA_DEVICE_ENV.as_posix() - game_launch = ARMADA_GAME_LAUNCH.as_posix() - return [ - f'armada_game_launch="{game_launch}"', - 'for argument in "$@"; do', - ' if [ "$argument" = "$armada_game_launch" ]; then', - ' exec "$@"', - " fi", - "done", - f'if [ -f "{device_env}" ] && [ -x "$armada_game_launch" ]; then', - ' exec "$armada_game_launch" "$@"', - "fi", - 'exec "$@"', - ] - def _get_profile_data(self) -> ProfileData: """Get current profile data from config file""" if not self.config_file_path.exists(): -- cgit v1.2.3 From 4fe21381b6c694d5eb615447c4827e4921593a52 Mon Sep 17 00:00:00 2001 From: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> Date: Sat, 5 Sep 2026 15:30:42 -0400 Subject: feat: migrate native runtime to lsfg-vk v2 --- py_modules/lsfg_vk/configuration.py | 545 +++++++++++------------------------- 1 file changed, 169 insertions(+), 376 deletions(-) (limited to 'py_modules/lsfg_vk/configuration.py') diff --git a/py_modules/lsfg_vk/configuration.py b/py_modules/lsfg_vk/configuration.py index de11b48..9b4d536 100644 --- a/py_modules/lsfg_vk/configuration.py +++ b/py_modules/lsfg_vk/configuration.py @@ -1,431 +1,224 @@ -""" -Configuration service for TOML-based lsfg configuration management. -""" - -from pathlib import Path -from typing import Dict, Any +import shlex from .base_service import BaseService -from .config_schema import ConfigurationManager, CONFIG_SCHEMA, ProfileData, DEFAULT_PROFILE_NAME +from .config_schema import ConfigurationManager, DEFAULT_PROFILE_NAME, ProfileData from .config_schema_generated import ConfigurationData, get_script_generation_logic -from .types import ConfigurationResponse, ProfilesResponse, ProfileResponse +from .types import ConfigurationResponse, ProfileResponse, ProfilesResponse class ConfigurationService(BaseService): - """Service for managing TOML-based lsfg configuration""" - def get_config(self) -> ConfigurationResponse: - """Read current TOML configuration merged with launch script environment variables - - Returns: - ConfigurationResponse with current configuration or error - """ try: - if not self.config_file_path.exists(): - from .dll_detection import DllDetectionService - dll_service = DllDetectionService(self.log) - toml_config = ConfigurationManager.get_defaults_with_dll_detection(dll_service) - else: - content = self.config_file_path.read_text(encoding='utf-8') - toml_config = ConfigurationManager.parse_toml_content(content) - - script_values = {} - if self.lsfg_script_path.exists(): - try: - script_content = self.lsfg_script_path.read_text(encoding='utf-8') - script_values = ConfigurationManager.parse_script_content(script_content) - self.log.info(f"Parsed script values: {script_values}") - except Exception as e: - self.log.warning(f"Failed to parse launch script: {str(e)}") - - config = ConfigurationManager.merge_config_with_script(toml_config, script_values) - + profile_data = self._get_profile_data() + current_profile = profile_data["current_profile"] + config = profile_data["profiles"].get(current_profile, dict(ConfigurationManager.get_defaults())) return self._success_response(ConfigurationResponse, config=config) - - except (OSError, IOError) as e: - error_msg = f"Error reading lsfg config: {str(e)}" - self.log.error(error_msg) - return self._error_response(ConfigurationResponse, str(e), config=None) - except Exception as e: - error_msg = f"Error parsing config file: {str(e)}" - self.log.error(error_msg) - from .dll_detection import DllDetectionService - dll_service = DllDetectionService(self.log) - config = ConfigurationManager.get_defaults_with_dll_detection(dll_service) - return self._success_response(ConfigurationResponse, - f"Using default configuration due to parse error: {str(e)}", - config=config) - + except Exception as error: + self.log.error(f"Error reading lsfg config: {error}") + return self._error_response(ConfigurationResponse, str(error), config=None) + def update_config_from_dict(self, config: ConfigurationData) -> ConfigurationResponse: - """Update TOML configuration from configuration dictionary (eliminates parameter duplication) - - Args: - config: Complete configuration data dictionary - - Returns: - ConfigurationResponse with success status - """ try: profile_data = self._get_profile_data() - current_profile = profile_data["current_profile"] - - return self.update_profile_config(current_profile, config) - - except (OSError, IOError) as e: - error_msg = f"Error updating lsfg config: {str(e)}" - self.log.error(error_msg) - return self._error_response(ConfigurationResponse, str(e), config=None) - except ValueError as e: - error_msg = f"Invalid configuration arguments: {str(e)}" - self.log.error(error_msg) - return self._error_response(ConfigurationResponse, str(e), config=None) - + return self.update_profile_config(profile_data["current_profile"], config) + except Exception as error: + self.log.error(f"Error updating lsfg config: {error}") + return self._error_response(ConfigurationResponse, str(error), config=None) + def update_lsfg_script(self, config: ConfigurationData) -> ConfigurationResponse: - """Update the ~/lsfg launch script with current configuration - - Args: - config: Configuration data to apply to the script - - Returns: - ConfigurationResponse indicating success or failure - """ try: - script_content = self._generate_script_content(config) - - self._write_file(self.lsfg_script_path, script_content, 0o755) - - self.log.info(f"Updated lsfg launch script at {self.lsfg_script_path}") - - return self._success_response(ConfigurationResponse, - "Launch script updated successfully", - config=config) - - except Exception as e: - error_msg = f"Error updating launch script: {str(e)}" - self.log.error(error_msg) - return self._error_response(ConfigurationResponse, str(e), config=None) - - def _generate_script_content(self, config: ConfigurationData) -> str: - """Generate the content for the ~/lsfg launch script - - Args: - config: Configuration data to apply to the script - - Returns: - The complete script content as a string - """ - lines = [ - "#!/bin/bash", - "# lsfg-vk launch script generated by decky-lossless-scaling-vk plugin", - "# This script sets up the environment for lsfg-vk to work with the plugin configuration", - ] - - generate_script_lines = get_script_generation_logic() - lines.extend(generate_script_lines(config)) - - lines.extend([ - "export LSFG_PROCESS=decky-lsfg-vk", - 'exec "$@"' - ]) - - return "\n".join(lines) + "\n" - + profile_data: ProfileData = { + "current_profile": DEFAULT_PROFILE_NAME, + "profiles": {DEFAULT_PROFILE_NAME: dict(config)}, + "global_config": { + "dll": config.get("dll", ""), + "no_fp16": config.get("no_fp16", False), + }, + } + return self.update_lsfg_script_from_profile_data(profile_data) + except Exception as error: + return self._error_response(ConfigurationResponse, str(error), config=None) + def _generate_script_content_for_profile(self, profile_data: ProfileData) -> str: - """Generate the content for the ~/lsfg launch script with profile support - - Args: - profile_data: Profile data containing current profile and configurations - - Returns: - The complete script content as a string - """ current_profile = profile_data["current_profile"] - config = profile_data["profiles"].get(current_profile, ConfigurationManager.get_defaults()) - - merged_config = dict(config) - for field_name, value in profile_data["global_config"].items(): - merged_config[field_name] = value - - lines = [ - "#!/bin/bash", - f"# Current profile: {current_profile}", - ] - - generate_script_lines = get_script_generation_logic() - lines.extend(generate_script_lines(merged_config)) - - lines.extend([ - f"export LSFG_PROCESS={current_profile}", - 'exec "$@"' - ]) - + config = dict(profile_data["profiles"].get(current_profile, ConfigurationManager.get_defaults())) + config["dll"] = profile_data["global_config"].get("dll", config.get("dll", "")) + config["no_fp16"] = profile_data["global_config"].get("no_fp16", config.get("no_fp16", False)) + + lines = ["#!/bin/bash"] + lines.extend(get_script_generation_logic()(config)) + lines.extend( + [ + f"export LSFGVK_CONFIG={shlex.quote(str(self.config_file_path))}", + f"export LSFGVK_PROFILE={shlex.quote(current_profile)}", + 'exec "$@"', + ] + ) return "\n".join(lines) + "\n" + def _generate_script_content(self, config: ConfigurationData) -> str: + profile_data: ProfileData = { + "current_profile": DEFAULT_PROFILE_NAME, + "profiles": {DEFAULT_PROFILE_NAME: dict(config)}, + "global_config": { + "dll": config.get("dll", ""), + "no_fp16": config.get("no_fp16", False), + }, + } + return self._generate_script_content_for_profile(profile_data) + def _get_profile_data(self) -> ProfileData: - """Get current profile data from config file""" if not self.config_file_path.exists(): from .dll_detection import DllDetectionService - dll_service = DllDetectionService(self.log) - default_config = ConfigurationManager.get_defaults_with_dll_detection(dll_service) + + default = ConfigurationManager.get_defaults_with_dll_detection(DllDetectionService(self.log)) return ProfileData( current_profile=DEFAULT_PROFILE_NAME, - profiles={DEFAULT_PROFILE_NAME: default_config}, + profiles={DEFAULT_PROFILE_NAME: dict(default)}, global_config={ - "dll": default_config.get("dll", ""), - "no_fp16": False - } + "dll": default.get("dll", ""), + "no_fp16": default.get("no_fp16", False), + }, + ) + + profile_data = ConfigurationManager.parse_toml_content_multi_profile( + self.config_file_path.read_text(encoding="utf-8") + ) + if self.lsfg_script_path.exists(): + script_content = self.lsfg_script_path.read_text(encoding="utf-8") + selected = ConfigurationManager.parse_profile_selection(script_content) + if selected in profile_data["profiles"]: + profile_data["current_profile"] = selected + current_profile = profile_data["current_profile"] + profile_data["profiles"][current_profile] = ConfigurationManager.merge_config_with_script( + profile_data["profiles"][current_profile], + ConfigurationManager.parse_script_content(script_content), ) - - content = self.config_file_path.read_text(encoding='utf-8') - return ConfigurationManager.parse_toml_content_multi_profile(content) - + return profile_data + def _save_profile_data(self, profile_data: ProfileData) -> None: - """Save profile data to config file""" - toml_content = ConfigurationManager.generate_toml_content_multi_profile(profile_data) - - self.config_dir.mkdir(parents=True, exist_ok=True) - - self._write_file(self.config_file_path, toml_content, 0o644) - + self._write_file( + self.config_file_path, + ConfigurationManager.generate_toml_content_multi_profile(profile_data), + 0o644, + ) + def get_profiles(self) -> ProfilesResponse: - """Get list of all profiles and current profile - - Returns: - ProfilesResponse with profile list and current profile - """ try: profile_data = self._get_profile_data() - - return self._success_response(ProfilesResponse, - "Profiles retrieved successfully", - profiles=list(profile_data["profiles"].keys()), - current_profile=profile_data["current_profile"]) - - except Exception as e: - error_msg = f"Error getting profiles: {str(e)}" - self.log.error(error_msg) - return self._error_response(ProfilesResponse, str(e), - profiles=None, current_profile=None) - + return self._success_response( + ProfilesResponse, + "Profiles retrieved successfully", + profiles=list(profile_data["profiles"]), + current_profile=profile_data["current_profile"], + ) + except Exception as error: + return self._error_response( + ProfilesResponse, + str(error), + profiles=None, + current_profile=None, + ) + def create_profile(self, profile_name: str, source_profile: str = None) -> ProfileResponse: - """Create a new profile - - Args: - profile_name: Name for the new profile (spaces will be converted to dashes) - source_profile: Optional source profile to copy from (default: current profile) - - Returns: - ProfileResponse with success status and the normalized profile name - """ try: profile_data = self._get_profile_data() - - if not source_profile: - source_profile = profile_data["current_profile"] - - # Get the normalized name that will be used for storage - normalized_name = ConfigurationManager.normalize_profile_name(profile_name) - new_profile_data = ConfigurationManager.create_profile(profile_data, profile_name, source_profile) - self._save_profile_data(new_profile_data) - - self.log.info(f"Created profile '{normalized_name}' from '{source_profile}'") - - # Return the normalized name so frontend can use the actual stored name - return self._success_response(ProfileResponse, - f"Profile '{normalized_name}' created successfully", - profile_name=normalized_name) - - except ValueError as e: - error_msg = f"Invalid profile operation: {str(e)}" - self.log.error(error_msg) - return self._error_response(ProfileResponse, str(e), profile_name=None) - except Exception as e: - error_msg = f"Error creating profile: {str(e)}" - self.log.error(error_msg) - return self._error_response(ProfileResponse, str(e), profile_name=None) - + normalized = ConfigurationManager.normalize_profile_name(profile_name) + return self._success_response( + ProfileResponse, + f"Profile '{normalized}' created successfully", + profile_name=normalized, + ) + except Exception as error: + return self._error_response(ProfileResponse, str(error), profile_name=None) + def delete_profile(self, profile_name: str) -> ProfileResponse: - """Delete a profile - - Args: - profile_name: Name of the profile to delete - - Returns: - ProfileResponse with success status - """ try: - profile_data = self._get_profile_data() - - new_profile_data = ConfigurationManager.delete_profile(profile_data, profile_name) - - self._save_profile_data(new_profile_data) - - script_result = self.update_lsfg_script_from_profile_data(new_profile_data) + profile_data = ConfigurationManager.delete_profile(self._get_profile_data(), profile_name) + self._save_profile_data(profile_data) + script_result = self.update_lsfg_script_from_profile_data(profile_data) if not script_result["success"]: - self.log.warning(f"Failed to update launch script: {script_result['error']}") - - self.log.info(f"Deleted profile '{profile_name}'") - - return self._success_response(ProfileResponse, - f"Profile '{profile_name}' deleted successfully", - profile_name=profile_name) - - except ValueError as e: - error_msg = f"Invalid profile operation: {str(e)}" - self.log.error(error_msg) - return self._error_response(ProfileResponse, str(e), profile_name=None) - except Exception as e: - error_msg = f"Error deleting profile: {str(e)}" - self.log.error(error_msg) - return self._error_response(ProfileResponse, str(e), profile_name=None) - + raise OSError(script_result["error"]) + return self._success_response( + ProfileResponse, + f"Profile '{profile_name}' deleted successfully", + profile_name=profile_name, + ) + except Exception as error: + return self._error_response(ProfileResponse, str(error), profile_name=None) + def rename_profile(self, old_name: str, new_name: str) -> ProfileResponse: - """Rename a profile - - Args: - old_name: Current profile name - new_name: New profile name (spaces will be converted to dashes) - - Returns: - ProfileResponse with success status and the normalized profile name - """ try: - profile_data = self._get_profile_data() - - # Get the normalized name that will be used for storage - normalized_name = ConfigurationManager.normalize_profile_name(new_name) - - new_profile_data = ConfigurationManager.rename_profile(profile_data, old_name, new_name) - - self._save_profile_data(new_profile_data) - - script_result = self.update_lsfg_script_from_profile_data(new_profile_data) + profile_data = ConfigurationManager.rename_profile(self._get_profile_data(), old_name, new_name) + self._save_profile_data(profile_data) + script_result = self.update_lsfg_script_from_profile_data(profile_data) if not script_result["success"]: - self.log.warning(f"Failed to update launch script: {script_result['error']}") - - self.log.info(f"Renamed profile '{old_name}' to '{normalized_name}'") - - # Return the normalized name so frontend can use the actual stored name - return self._success_response(ProfileResponse, - f"Profile renamed from '{old_name}' to '{normalized_name}' successfully", - profile_name=normalized_name) - - except ValueError as e: - error_msg = f"Invalid profile operation: {str(e)}" - self.log.error(error_msg) - return self._error_response(ProfileResponse, str(e), profile_name=None) - except Exception as e: - error_msg = f"Error renaming profile: {str(e)}" - self.log.error(error_msg) - return self._error_response(ProfileResponse, str(e), profile_name=None) - + raise OSError(script_result["error"]) + normalized = ConfigurationManager.normalize_profile_name(new_name) + return self._success_response( + ProfileResponse, + f"Profile renamed to '{normalized}' successfully", + profile_name=normalized, + ) + except Exception as error: + return self._error_response(ProfileResponse, str(error), profile_name=None) + def set_current_profile(self, profile_name: str) -> ProfileResponse: - """Set the current active profile - - Args: - profile_name: Name of the profile to set as current - - Returns: - ProfileResponse with success status - """ try: - profile_data = self._get_profile_data() - - new_profile_data = ConfigurationManager.set_current_profile(profile_data, profile_name) - - self._save_profile_data(new_profile_data) - - script_result = self.update_lsfg_script_from_profile_data(new_profile_data) + profile_data = ConfigurationManager.set_current_profile(self._get_profile_data(), profile_name) + script_result = self.update_lsfg_script_from_profile_data(profile_data) if not script_result["success"]: - self.log.warning(f"Failed to update launch script: {script_result['error']}") - - self.log.info(f"Set current profile to '{profile_name}'") - - return self._success_response(ProfileResponse, - f"Current profile set to '{profile_name}' successfully", - profile_name=profile_name) - - except ValueError as e: - error_msg = f"Invalid profile operation: {str(e)}" - self.log.error(error_msg) - return self._error_response(ProfileResponse, str(e), profile_name=None) - except Exception as e: - error_msg = f"Error setting current profile: {str(e)}" - self.log.error(error_msg) - return self._error_response(ProfileResponse, str(e), profile_name=None) - + raise OSError(script_result["error"]) + return self._success_response( + ProfileResponse, + f"Current profile set to '{profile_name}' successfully", + profile_name=profile_name, + ) + except Exception as error: + return self._error_response(ProfileResponse, str(error), profile_name=None) + def update_profile_config(self, profile_name: str, config: ConfigurationData) -> ConfigurationResponse: - """Update configuration for a specific profile - - Args: - profile_name: Name of the profile to update - config: Configuration data to apply - - Returns: - ConfigurationResponse with success status - """ try: profile_data = self._get_profile_data() - if profile_name not in profile_data["profiles"]: - return self._error_response(ConfigurationResponse, - f"Profile '{profile_name}' does not exist", - config=None) - - # Update the profile's config - profile_data["profiles"][profile_name] = config - - # Update global config fields if they're in the config - for field_name in ["dll", "no_fp16"]: - if field_name in config: - profile_data["global_config"][field_name] = config[field_name] - + raise ValueError(f"Profile '{profile_name}' does not exist") + + validated = ConfigurationManager.validate_config(config) + profile_data["profiles"][profile_name] = { + **profile_data["profiles"][profile_name], + **validated, + } + profile_data["global_config"]["dll"] = validated.get("dll", "") + profile_data["global_config"]["no_fp16"] = validated.get("no_fp16", False) self._save_profile_data(profile_data) - + if profile_name == profile_data["current_profile"]: script_result = self.update_lsfg_script_from_profile_data(profile_data) if not script_result["success"]: - self.log.warning(f"Failed to update launch script: {script_result['error']}") - - field_values = ", ".join(f"{k}={repr(v)}" for k, v in config.items()) - self.log.info(f"Updated profile '{profile_name}' configuration: {field_values}") - - return self._success_response(ConfigurationResponse, - f"Profile '{profile_name}' configuration updated successfully", - config=config) - - except Exception as e: - error_msg = f"Error updating profile configuration: {str(e)}" - self.log.error(error_msg) - return self._error_response(ConfigurationResponse, str(e), config=None) - + raise OSError(script_result["error"]) + + return self._success_response( + ConfigurationResponse, + f"Profile '{profile_name}' configuration updated successfully", + config=validated, + ) + except Exception as error: + return self._error_response(ConfigurationResponse, str(error), config=None) + def update_lsfg_script_from_profile_data(self, profile_data: ProfileData) -> ConfigurationResponse: - """Update the ~/lsfg launch script from profile data - - Args: - profile_data: Profile data to apply to the script - - Returns: - ConfigurationResponse indicating success or failure - """ try: script_content = self._generate_script_content_for_profile(profile_data) - - # Write the script file self._write_file(self.lsfg_script_path, script_content, 0o755) - - self.log.info(f"Updated lsfg launch script at {self.lsfg_script_path} for profile '{profile_data['current_profile']}'") - - # Get current profile config for response - current_config = profile_data["profiles"].get(profile_data["current_profile"], ConfigurationManager.get_defaults()) - - return self._success_response(ConfigurationResponse, - "Launch script updated successfully", - config=current_config) - - except Exception as e: - error_msg = f"Error updating launch script: {str(e)}" - self.log.error(error_msg) - return self._error_response(ConfigurationResponse, str(e), config=None) + current_config = profile_data["profiles"].get( + profile_data["current_profile"], + dict(ConfigurationManager.get_defaults()), + ) + return self._success_response( + ConfigurationResponse, + "Launch script updated successfully", + config=current_config, + ) + except Exception as error: + return self._error_response(ConfigurationResponse, str(error), config=None) -- cgit v1.2.3 From e8e469f99078858dc953663cba6f3428e80b1c5d Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Sat, 5 Sep 2026 23:44:37 -0400 Subject: refactor: delegate runtime checks to lsfg-vk --- py_modules/lsfg_vk/configuration.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'py_modules/lsfg_vk/configuration.py') diff --git a/py_modules/lsfg_vk/configuration.py b/py_modules/lsfg_vk/configuration.py index 9b4d536..12710cc 100644 --- a/py_modules/lsfg_vk/configuration.py +++ b/py_modules/lsfg_vk/configuration.py @@ -3,10 +3,15 @@ import shlex from .base_service import BaseService from .config_schema import ConfigurationManager, DEFAULT_PROFILE_NAME, ProfileData from .config_schema_generated import ConfigurationData, get_script_generation_logic +from .runtime_service import RuntimeService from .types import ConfigurationResponse, ProfileResponse, ProfilesResponse class ConfigurationService(BaseService): + def __init__(self, logger=None, runtime_service: RuntimeService = None): + super().__init__(logger) + self.runtime_service = runtime_service or RuntimeService(logger=self.log) + def get_config(self) -> ConfigurationResponse: try: profile_data = self._get_profile_data() @@ -69,9 +74,7 @@ class ConfigurationService(BaseService): def _get_profile_data(self) -> ProfileData: if not self.config_file_path.exists(): - from .dll_detection import DllDetectionService - - default = ConfigurationManager.get_defaults_with_dll_detection(DllDetectionService(self.log)) + default = ConfigurationManager.get_defaults() return ProfileData( current_profile=DEFAULT_PROFILE_NAME, profiles={DEFAULT_PROFILE_NAME: dict(default)}, @@ -97,9 +100,11 @@ class ConfigurationService(BaseService): return profile_data def _save_profile_data(self, profile_data: ProfileData) -> None: + content = ConfigurationManager.generate_toml_content_multi_profile(profile_data) + self.runtime_service.validate_config_content(content) self._write_file( self.config_file_path, - ConfigurationManager.generate_toml_content_multi_profile(profile_data), + content, 0o644, ) -- cgit v1.2.3 From ad2b182777bfd0a5ceef6e654df75ff13eb8b503 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Sun, 6 Sep 2026 15:08:45 -0400 Subject: refactor: offload configuration to lsfg-vk --- py_modules/lsfg_vk/configuration.py | 289 ++++++++++++------------------------ 1 file changed, 97 insertions(+), 192 deletions(-) (limited to 'py_modules/lsfg_vk/configuration.py') diff --git a/py_modules/lsfg_vk/configuration.py b/py_modules/lsfg_vk/configuration.py index 12710cc..2626a66 100644 --- a/py_modules/lsfg_vk/configuration.py +++ b/py_modules/lsfg_vk/configuration.py @@ -1,229 +1,134 @@ +import re import shlex +from typing import Any, Dict from .base_service import BaseService from .config_schema import ConfigurationManager, DEFAULT_PROFILE_NAME, ProfileData -from .config_schema_generated import ConfigurationData, get_script_generation_logic from .runtime_service import RuntimeService -from .types import ConfigurationResponse, ProfileResponse, ProfilesResponse class ConfigurationService(BaseService): + """Controller-facing adapter over upstream lsfg-vk profiles.""" + def __init__(self, logger=None, runtime_service: RuntimeService = None): super().__init__(logger) self.runtime_service = runtime_service or RuntimeService(logger=self.log) - def get_config(self) -> ConfigurationResponse: + def _default_data(self) -> ProfileData: + defaults = ConfigurationManager.validate_config({}) + return {"current_profile": DEFAULT_PROFILE_NAME, "profiles": {DEFAULT_PROFILE_NAME: defaults}, "global_config": {"dll": "", "no_fp16": False}} + + def _get_profile_data(self) -> ProfileData: + if not self.config_file_path.exists(): + return self._default_data() + return ConfigurationManager.parse_toml_content_multi_profile(self.config_file_path.read_text(encoding="utf-8")) + + def _save_profile_data(self, data: ProfileData) -> None: + content = ConfigurationManager.generate_toml_content_multi_profile(data) + self.runtime_service.validate_config_content(content) + self._write_file(self.config_file_path, content, 0o644) + + @staticmethod + def _game_profile_name(appid: str) -> str: + if not re.fullmatch(r"[0-9]+", str(appid)): + raise ValueError("appid must be numeric") + return f"game-{appid}" + + @staticmethod + def _public_config(config: Dict[str, Any]) -> Dict[str, Any]: + return ConfigurationManager.validate_config(config) + + def get_config(self) -> Dict[str, Any]: try: - profile_data = self._get_profile_data() - current_profile = profile_data["current_profile"] - config = profile_data["profiles"].get(current_profile, dict(ConfigurationManager.get_defaults())) - return self._success_response(ConfigurationResponse, config=config) + data = self._get_profile_data() + return self._success_response(dict, config=self._public_config(data["profiles"][DEFAULT_PROFILE_NAME])) except Exception as error: self.log.error(f"Error reading lsfg config: {error}") - return self._error_response(ConfigurationResponse, str(error), config=None) + return self._error_response(dict, str(error), config=None) - def update_config_from_dict(self, config: ConfigurationData) -> ConfigurationResponse: + def get_game_configs(self) -> Dict[str, Any]: try: - profile_data = self._get_profile_data() - return self.update_profile_config(profile_data["current_profile"], config) + data = self._get_profile_data() + games = [] + for name, raw in data["profiles"].items(): + active_in = raw.get("active_in", []) + if len(active_in) != 1 or not str(active_in[0]).isdigit(): + continue + games.append({"appid": str(active_in[0]), "profile": name, "config": self._public_config(raw)}) + return self._success_response(dict, default=self._public_config(data["profiles"][DEFAULT_PROFILE_NAME]), games=games) except Exception as error: - self.log.error(f"Error updating lsfg config: {error}") - return self._error_response(ConfigurationResponse, str(error), config=None) + self.log.error(f"Error reading game configs: {error}") + return self._error_response(dict, str(error), default=None, games=[]) - def update_lsfg_script(self, config: ConfigurationData) -> ConfigurationResponse: + def get_game_config(self, appid: str) -> Dict[str, Any]: try: - profile_data: ProfileData = { - "current_profile": DEFAULT_PROFILE_NAME, - "profiles": {DEFAULT_PROFILE_NAME: dict(config)}, - "global_config": { - "dll": config.get("dll", ""), - "no_fp16": config.get("no_fp16", False), - }, - } - return self.update_lsfg_script_from_profile_data(profile_data) + data = self._get_profile_data() + name = self._game_profile_name(appid) + profile = data["profiles"].get(name) + if profile is None: + profile = next((value for value in data["profiles"].values() if str(appid) in value.get("active_in", [])), None) + return self._success_response(dict, appid=str(appid), exists=profile is not None, config=self._public_config(profile or data["profiles"][DEFAULT_PROFILE_NAME])) except Exception as error: - return self._error_response(ConfigurationResponse, str(error), config=None) - - def _generate_script_content_for_profile(self, profile_data: ProfileData) -> str: - current_profile = profile_data["current_profile"] - config = dict(profile_data["profiles"].get(current_profile, ConfigurationManager.get_defaults())) - config["dll"] = profile_data["global_config"].get("dll", config.get("dll", "")) - config["no_fp16"] = profile_data["global_config"].get("no_fp16", config.get("no_fp16", False)) - - lines = ["#!/bin/bash"] - lines.extend(get_script_generation_logic()(config)) - lines.extend( - [ - f"export LSFGVK_CONFIG={shlex.quote(str(self.config_file_path))}", - f"export LSFGVK_PROFILE={shlex.quote(current_profile)}", - 'exec "$@"', - ] - ) - return "\n".join(lines) + "\n" - - def _generate_script_content(self, config: ConfigurationData) -> str: - profile_data: ProfileData = { - "current_profile": DEFAULT_PROFILE_NAME, - "profiles": {DEFAULT_PROFILE_NAME: dict(config)}, - "global_config": { - "dll": config.get("dll", ""), - "no_fp16": config.get("no_fp16", False), - }, - } - return self._generate_script_content_for_profile(profile_data) + return self._error_response(dict, str(error), appid=str(appid), exists=False, config=None) - def _get_profile_data(self) -> ProfileData: - if not self.config_file_path.exists(): - default = ConfigurationManager.get_defaults() - return ProfileData( - current_profile=DEFAULT_PROFILE_NAME, - profiles={DEFAULT_PROFILE_NAME: dict(default)}, - global_config={ - "dll": default.get("dll", ""), - "no_fp16": default.get("no_fp16", False), - }, - ) - - profile_data = ConfigurationManager.parse_toml_content_multi_profile( - self.config_file_path.read_text(encoding="utf-8") - ) - if self.lsfg_script_path.exists(): - script_content = self.lsfg_script_path.read_text(encoding="utf-8") - selected = ConfigurationManager.parse_profile_selection(script_content) - if selected in profile_data["profiles"]: - profile_data["current_profile"] = selected - current_profile = profile_data["current_profile"] - profile_data["profiles"][current_profile] = ConfigurationManager.merge_config_with_script( - profile_data["profiles"][current_profile], - ConfigurationManager.parse_script_content(script_content), - ) - return profile_data - - def _save_profile_data(self, profile_data: ProfileData) -> None: - content = ConfigurationManager.generate_toml_content_multi_profile(profile_data) - self.runtime_service.validate_config_content(content) - self._write_file( - self.config_file_path, - content, - 0o644, - ) - - def get_profiles(self) -> ProfilesResponse: - try: - profile_data = self._get_profile_data() - return self._success_response( - ProfilesResponse, - "Profiles retrieved successfully", - profiles=list(profile_data["profiles"]), - current_profile=profile_data["current_profile"], - ) - except Exception as error: - return self._error_response( - ProfilesResponse, - str(error), - profiles=None, - current_profile=None, - ) - - def create_profile(self, profile_name: str, source_profile: str = None) -> ProfileResponse: + def update_game_config(self, appid: str, config: Dict[str, Any]) -> Dict[str, Any]: try: - profile_data = self._get_profile_data() - new_profile_data = ConfigurationManager.create_profile(profile_data, profile_name, source_profile) - self._save_profile_data(new_profile_data) - normalized = ConfigurationManager.normalize_profile_name(profile_name) - return self._success_response( - ProfileResponse, - f"Profile '{normalized}' created successfully", - profile_name=normalized, - ) + data = self._get_profile_data() + name = self._game_profile_name(appid) + validated = self._public_config(config) + validated["active_in"] = [str(appid)] + data["profiles"][name] = validated + self._save_profile_data(data) + return self._success_response(dict, appid=str(appid), config=validated) except Exception as error: - return self._error_response(ProfileResponse, str(error), profile_name=None) + return self._error_response(dict, str(error), appid=str(appid), config=None) - def delete_profile(self, profile_name: str) -> ProfileResponse: + def reset_game_config(self, appid: str) -> Dict[str, Any]: try: - profile_data = ConfigurationManager.delete_profile(self._get_profile_data(), profile_name) - self._save_profile_data(profile_data) - script_result = self.update_lsfg_script_from_profile_data(profile_data) - if not script_result["success"]: - raise OSError(script_result["error"]) - return self._success_response( - ProfileResponse, - f"Profile '{profile_name}' deleted successfully", - profile_name=profile_name, - ) + data = self._get_profile_data() + name = self._game_profile_name(appid) + data["profiles"].pop(name, None) + for profile_name, profile in list(data["profiles"].items()): + if profile_name != DEFAULT_PROFILE_NAME and str(appid) in profile.get("active_in", []): + data["profiles"].pop(profile_name) + self._save_profile_data(data) + return self._success_response(dict, appid=str(appid), config=self._public_config(data["profiles"][DEFAULT_PROFILE_NAME])) except Exception as error: - return self._error_response(ProfileResponse, str(error), profile_name=None) + return self._error_response(dict, str(error), appid=str(appid), config=None) - def rename_profile(self, old_name: str, new_name: str) -> ProfileResponse: + def reset_all_game_configs(self) -> Dict[str, Any]: try: - profile_data = ConfigurationManager.rename_profile(self._get_profile_data(), old_name, new_name) - self._save_profile_data(profile_data) - script_result = self.update_lsfg_script_from_profile_data(profile_data) - if not script_result["success"]: - raise OSError(script_result["error"]) - normalized = ConfigurationManager.normalize_profile_name(new_name) - return self._success_response( - ProfileResponse, - f"Profile renamed to '{normalized}' successfully", - profile_name=normalized, - ) + data = self._get_profile_data() + data["profiles"] = {DEFAULT_PROFILE_NAME: data["profiles"][DEFAULT_PROFILE_NAME]} + self._save_profile_data(data) + return self._success_response(dict, default=self._public_config(data["profiles"][DEFAULT_PROFILE_NAME]), games=[]) except Exception as error: - return self._error_response(ProfileResponse, str(error), profile_name=None) + return self._error_response(dict, str(error), default=None, games=[]) - def set_current_profile(self, profile_name: str) -> ProfileResponse: + def update_config_from_dict(self, config: Dict[str, Any]) -> Dict[str, Any]: try: - profile_data = ConfigurationManager.set_current_profile(self._get_profile_data(), profile_name) - script_result = self.update_lsfg_script_from_profile_data(profile_data) - if not script_result["success"]: - raise OSError(script_result["error"]) - return self._success_response( - ProfileResponse, - f"Current profile set to '{profile_name}' successfully", - profile_name=profile_name, - ) + data = self._get_profile_data() + validated = self._public_config(config) + validated["active_in"] = [] + data["profiles"][DEFAULT_PROFILE_NAME] = validated + data["global_config"] = {"dll": validated.get("dll", ""), "no_fp16": validated.get("no_fp16", False)} + self._save_profile_data(data) + return self._success_response(dict, config=validated) except Exception as error: - return self._error_response(ProfileResponse, str(error), profile_name=None) + return self._error_response(dict, str(error), config=None) - def update_profile_config(self, profile_name: str, config: ConfigurationData) -> ConfigurationResponse: - try: - profile_data = self._get_profile_data() - if profile_name not in profile_data["profiles"]: - raise ValueError(f"Profile '{profile_name}' does not exist") - - validated = ConfigurationManager.validate_config(config) - profile_data["profiles"][profile_name] = { - **profile_data["profiles"][profile_name], - **validated, - } - profile_data["global_config"]["dll"] = validated.get("dll", "") - profile_data["global_config"]["no_fp16"] = validated.get("no_fp16", False) - self._save_profile_data(profile_data) - - if profile_name == profile_data["current_profile"]: - script_result = self.update_lsfg_script_from_profile_data(profile_data) - if not script_result["success"]: - raise OSError(script_result["error"]) - - return self._success_response( - ConfigurationResponse, - f"Profile '{profile_name}' configuration updated successfully", - config=validated, - ) - except Exception as error: - return self._error_response(ConfigurationResponse, str(error), config=None) + def update_lsfg_script(self, config: Dict[str, Any]) -> Dict[str, Any]: + return self.update_config_from_dict(config) + + def _generate_script_content_for_profile(self, profile_data: ProfileData) -> str: + return "#!/bin/bash\n" f"export LSFGVK_CONFIG={shlex.quote(str(self.config_file_path))}\n" 'exec "$@"\n' + + def _generate_script_content(self, config: Dict[str, Any]) -> str: + return self._generate_script_content_for_profile(self._default_data()) - def update_lsfg_script_from_profile_data(self, profile_data: ProfileData) -> ConfigurationResponse: + def update_lsfg_script_from_profile_data(self, profile_data: ProfileData) -> Dict[str, Any]: try: - script_content = self._generate_script_content_for_profile(profile_data) - self._write_file(self.lsfg_script_path, script_content, 0o755) - current_config = profile_data["profiles"].get( - profile_data["current_profile"], - dict(ConfigurationManager.get_defaults()), - ) - return self._success_response( - ConfigurationResponse, - "Launch script updated successfully", - config=current_config, - ) + self._write_file(self.lsfg_script_path, self._generate_script_content_for_profile(profile_data), 0o755) + return self._success_response(dict) except Exception as error: - return self._error_response(ConfigurationResponse, str(error), config=None) + return self._error_response(dict, str(error)) -- cgit v1.2.3 From c9be32287ad5b72fcd86b10fa726d09dbd97d7fd Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Sun, 6 Sep 2026 17:09:54 -0400 Subject: refactor: organize plugin into native tabs --- py_modules/lsfg_vk/configuration.py | 64 ++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 33 deletions(-) (limited to 'py_modules/lsfg_vk/configuration.py') diff --git a/py_modules/lsfg_vk/configuration.py b/py_modules/lsfg_vk/configuration.py index 2626a66..a4ee2cc 100644 --- a/py_modules/lsfg_vk/configuration.py +++ b/py_modules/lsfg_vk/configuration.py @@ -1,5 +1,4 @@ import re -import shlex from typing import Any, Dict from .base_service import BaseService @@ -29,10 +28,27 @@ class ConfigurationService(BaseService): self._write_file(self.config_file_path, content, 0o644) @staticmethod - def _game_profile_name(appid: str) -> str: - if not re.fullmatch(r"[0-9]+", str(appid)): - raise ValueError("appid must be numeric") - return f"game-{appid}" + def _profile_name(data: ProfileData, appid: str, game_name: str) -> str: + name = str(game_name).strip() + if not name: + raise ValueError("game name is required") + if name == DEFAULT_PROFILE_NAME: + name = f"{name} ({appid})" + existing = data["profiles"].get(name) + if existing is not None and str(appid) not in existing.get("active_in", []): + name = f"{name} ({appid})" + return name + + @staticmethod + def _profile_for_appid(data: ProfileData, appid: str): + return next( + ( + (name, profile) + for name, profile in data["profiles"].items() + if str(appid) in profile.get("active_in", []) + ), + (None, None), + ) @staticmethod def _public_config(config: Dict[str, Any]) -> Dict[str, Any]: @@ -52,7 +68,7 @@ class ConfigurationService(BaseService): games = [] for name, raw in data["profiles"].items(): active_in = raw.get("active_in", []) - if len(active_in) != 1 or not str(active_in[0]).isdigit(): + if len(active_in) != 1 or not re.fullmatch(r"-?[0-9]+", str(active_in[0])): continue games.append({"appid": str(active_in[0]), "profile": name, "config": self._public_config(raw)}) return self._success_response(dict, default=self._public_config(data["profiles"][DEFAULT_PROFILE_NAME]), games=games) @@ -63,20 +79,20 @@ class ConfigurationService(BaseService): def get_game_config(self, appid: str) -> Dict[str, Any]: try: data = self._get_profile_data() - name = self._game_profile_name(appid) - profile = data["profiles"].get(name) - if profile is None: - profile = next((value for value in data["profiles"].values() if str(appid) in value.get("active_in", [])), None) + _, profile = self._profile_for_appid(data, appid) return self._success_response(dict, appid=str(appid), exists=profile is not None, config=self._public_config(profile or data["profiles"][DEFAULT_PROFILE_NAME])) except Exception as error: return self._error_response(dict, str(error), appid=str(appid), exists=False, config=None) - def update_game_config(self, appid: str, config: Dict[str, Any]) -> Dict[str, Any]: + def update_game_config(self, appid: str, game_name: str, config: Dict[str, Any]) -> Dict[str, Any]: try: data = self._get_profile_data() - name = self._game_profile_name(appid) + old_name, _ = self._profile_for_appid(data, appid) + name = self._profile_name(data, appid, game_name) validated = self._public_config(config) validated["active_in"] = [str(appid)] + if old_name and old_name != name: + data["profiles"].pop(old_name, None) data["profiles"][name] = validated self._save_profile_data(data) return self._success_response(dict, appid=str(appid), config=validated) @@ -86,11 +102,9 @@ class ConfigurationService(BaseService): def reset_game_config(self, appid: str) -> Dict[str, Any]: try: data = self._get_profile_data() - name = self._game_profile_name(appid) - data["profiles"].pop(name, None) - for profile_name, profile in list(data["profiles"].items()): - if profile_name != DEFAULT_PROFILE_NAME and str(appid) in profile.get("active_in", []): - data["profiles"].pop(profile_name) + name, _ = self._profile_for_appid(data, appid) + if name: + data["profiles"].pop(name, None) self._save_profile_data(data) return self._success_response(dict, appid=str(appid), config=self._public_config(data["profiles"][DEFAULT_PROFILE_NAME])) except Exception as error: @@ -116,19 +130,3 @@ class ConfigurationService(BaseService): return self._success_response(dict, config=validated) except Exception as error: return self._error_response(dict, str(error), config=None) - - def update_lsfg_script(self, config: Dict[str, Any]) -> Dict[str, Any]: - return self.update_config_from_dict(config) - - def _generate_script_content_for_profile(self, profile_data: ProfileData) -> str: - return "#!/bin/bash\n" f"export LSFGVK_CONFIG={shlex.quote(str(self.config_file_path))}\n" 'exec "$@"\n' - - def _generate_script_content(self, config: Dict[str, Any]) -> str: - return self._generate_script_content_for_profile(self._default_data()) - - def update_lsfg_script_from_profile_data(self, profile_data: ProfileData) -> Dict[str, Any]: - try: - self._write_file(self.lsfg_script_path, self._generate_script_content_for_profile(profile_data), 0o755) - return self._success_response(dict) - except Exception as error: - return self._error_response(dict, str(error)) -- cgit v1.2.3 From 7bc5f685186b1ff03ce0f7c99e7bec411beabaeb Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Sun, 6 Sep 2026 22:04:54 -0400 Subject: feat: make ui suck less, frfr --- py_modules/lsfg_vk/configuration.py | 56 +++++++++++-------------------------- 1 file changed, 16 insertions(+), 40 deletions(-) (limited to 'py_modules/lsfg_vk/configuration.py') diff --git a/py_modules/lsfg_vk/configuration.py b/py_modules/lsfg_vk/configuration.py index a4ee2cc..bec3828 100644 --- a/py_modules/lsfg_vk/configuration.py +++ b/py_modules/lsfg_vk/configuration.py @@ -2,7 +2,7 @@ import re from typing import Any, Dict from .base_service import BaseService -from .config_schema import ConfigurationManager, DEFAULT_PROFILE_NAME, ProfileData +from .config_schema import ConfigurationManager, ProfileData from .runtime_service import RuntimeService @@ -14,8 +14,7 @@ class ConfigurationService(BaseService): self.runtime_service = runtime_service or RuntimeService(logger=self.log) def _default_data(self) -> ProfileData: - defaults = ConfigurationManager.validate_config({}) - return {"current_profile": DEFAULT_PROFILE_NAME, "profiles": {DEFAULT_PROFILE_NAME: defaults}, "global_config": {"dll": "", "no_fp16": False}} + return {"profiles": {}, "global_config": {"dll": "", "no_fp16": False}} def _get_profile_data(self) -> ProfileData: if not self.config_file_path.exists(): @@ -32,8 +31,6 @@ class ConfigurationService(BaseService): name = str(game_name).strip() if not name: raise ValueError("game name is required") - if name == DEFAULT_PROFILE_NAME: - name = f"{name} ({appid})" existing = data["profiles"].get(name) if existing is not None and str(appid) not in existing.get("active_in", []): name = f"{name} ({appid})" @@ -54,14 +51,6 @@ class ConfigurationService(BaseService): def _public_config(config: Dict[str, Any]) -> Dict[str, Any]: return ConfigurationManager.validate_config(config) - def get_config(self) -> Dict[str, Any]: - try: - data = self._get_profile_data() - return self._success_response(dict, config=self._public_config(data["profiles"][DEFAULT_PROFILE_NAME])) - except Exception as error: - self.log.error(f"Error reading lsfg config: {error}") - return self._error_response(dict, str(error), config=None) - def get_game_configs(self) -> Dict[str, Any]: try: data = self._get_profile_data() @@ -71,26 +60,25 @@ class ConfigurationService(BaseService): if len(active_in) != 1 or not re.fullmatch(r"-?[0-9]+", str(active_in[0])): continue games.append({"appid": str(active_in[0]), "profile": name, "config": self._public_config(raw)}) - return self._success_response(dict, default=self._public_config(data["profiles"][DEFAULT_PROFILE_NAME]), games=games) + return self._success_response(dict, global_config=dict(data["global_config"]), games=games) except Exception as error: self.log.error(f"Error reading game configs: {error}") - return self._error_response(dict, str(error), default=None, games=[]) - - def get_game_config(self, appid: str) -> Dict[str, Any]: - try: - data = self._get_profile_data() - _, profile = self._profile_for_appid(data, appid) - return self._success_response(dict, appid=str(appid), exists=profile is not None, config=self._public_config(profile or data["profiles"][DEFAULT_PROFILE_NAME])) - except Exception as error: - return self._error_response(dict, str(error), appid=str(appid), exists=False, config=None) + return self._error_response(dict, str(error), games=[]) def update_game_config(self, appid: str, game_name: str, config: Dict[str, Any]) -> Dict[str, Any]: try: data = self._get_profile_data() old_name, _ = self._profile_for_appid(data, appid) name = self._profile_name(data, appid, game_name) - validated = self._public_config(config) + merged_config = {**data["global_config"], **config} + if not config.get("dll"): + merged_config["dll"] = data["global_config"].get("dll", "") + validated = self._public_config(merged_config) validated["active_in"] = [str(appid)] + data["global_config"] = { + "dll": validated["dll"], + "no_fp16": validated["no_fp16"], + } if old_name and old_name != name: data["profiles"].pop(old_name, None) data["profiles"][name] = validated @@ -106,27 +94,15 @@ class ConfigurationService(BaseService): if name: data["profiles"].pop(name, None) self._save_profile_data(data) - return self._success_response(dict, appid=str(appid), config=self._public_config(data["profiles"][DEFAULT_PROFILE_NAME])) + return self._success_response(dict, appid=str(appid), exists=False) except Exception as error: return self._error_response(dict, str(error), appid=str(appid), config=None) def reset_all_game_configs(self) -> Dict[str, Any]: try: data = self._get_profile_data() - data["profiles"] = {DEFAULT_PROFILE_NAME: data["profiles"][DEFAULT_PROFILE_NAME]} - self._save_profile_data(data) - return self._success_response(dict, default=self._public_config(data["profiles"][DEFAULT_PROFILE_NAME]), games=[]) - except Exception as error: - return self._error_response(dict, str(error), default=None, games=[]) - - def update_config_from_dict(self, config: Dict[str, Any]) -> Dict[str, Any]: - try: - data = self._get_profile_data() - validated = self._public_config(config) - validated["active_in"] = [] - data["profiles"][DEFAULT_PROFILE_NAME] = validated - data["global_config"] = {"dll": validated.get("dll", ""), "no_fp16": validated.get("no_fp16", False)} + data["profiles"] = {} self._save_profile_data(data) - return self._success_response(dict, config=validated) + return self._success_response(dict, global_config=dict(data["global_config"]), games=[]) except Exception as error: - return self._error_response(dict, str(error), config=None) + return self._error_response(dict, str(error), games=[]) -- cgit v1.2.3 From ba63c284631ba2aefe5906b0edecf51fab829ef3 Mon Sep 17 00:00:00 2001 From: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> Date: Thu, 10 Sep 2026 16:28:46 -0400 Subject: feat: add explicit flatpak profile configuration --- py_modules/lsfg_vk/configuration.py | 63 +++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) (limited to 'py_modules/lsfg_vk/configuration.py') diff --git a/py_modules/lsfg_vk/configuration.py b/py_modules/lsfg_vk/configuration.py index bec3828..7e68479 100644 --- a/py_modules/lsfg_vk/configuration.py +++ b/py_modules/lsfg_vk/configuration.py @@ -7,7 +7,7 @@ from .runtime_service import RuntimeService class ConfigurationService(BaseService): - """Controller-facing adapter over upstream lsfg-vk profiles.""" + FLATPAK_PROFILE_PREFIX = "flatpak:" def __init__(self, logger=None, runtime_service: RuntimeService = None): super().__init__(logger) @@ -47,6 +47,13 @@ class ConfigurationService(BaseService): (None, None), ) + @classmethod + def flatpak_profile_name(cls, app_id: str) -> str: + value = str(app_id).strip() + if not value: + raise ValueError("Flatpak application ID is required") + return f"{cls.FLATPAK_PROFILE_PREFIX}{value}" + @staticmethod def _public_config(config: Dict[str, Any]) -> Dict[str, Any]: return ConfigurationManager.validate_config(config) @@ -87,6 +94,51 @@ class ConfigurationService(BaseService): except Exception as error: return self._error_response(dict, str(error), appid=str(appid), config=None) + def get_flatpak_config(self, app_id: str) -> Dict[str, Any]: + try: + data = self._get_profile_data() + name = self.flatpak_profile_name(app_id) + raw = data["profiles"].get(name) + return self._success_response( + dict, + app_id=str(app_id), + profile=name, + exists=raw is not None, + config=self._public_config(raw) if raw is not None else None, + global_config=dict(data["global_config"]), + ) + except Exception as error: + return self._error_response(dict, str(error), app_id=str(app_id), config=None, exists=False) + + def update_flatpak_config(self, app_id: str, config: Dict[str, Any]) -> Dict[str, Any]: + try: + data = self._get_profile_data() + name = self.flatpak_profile_name(app_id) + merged_config = {**data["global_config"], **config} + if not config.get("dll"): + merged_config["dll"] = data["global_config"].get("dll", "") + validated = self._public_config(merged_config) + validated["active_in"] = [] + data["global_config"] = { + "dll": validated["dll"], + "no_fp16": validated["no_fp16"], + } + data["profiles"][name] = validated + self._save_profile_data(data) + return self._success_response(dict, app_id=str(app_id), profile=name, exists=True, config=validated) + except Exception as error: + return self._error_response(dict, str(error), app_id=str(app_id), config=None, exists=False) + + def reset_flatpak_config(self, app_id: str) -> Dict[str, Any]: + try: + data = self._get_profile_data() + name = self.flatpak_profile_name(app_id) + data["profiles"].pop(name, None) + self._save_profile_data(data) + return self._success_response(dict, app_id=str(app_id), profile=name, exists=False) + except Exception as error: + return self._error_response(dict, str(error), app_id=str(app_id), config=None, exists=False) + def reset_game_config(self, appid: str) -> Dict[str, Any]: try: data = self._get_profile_data() @@ -101,7 +153,14 @@ class ConfigurationService(BaseService): def reset_all_game_configs(self) -> Dict[str, Any]: try: data = self._get_profile_data() - data["profiles"] = {} + data["profiles"] = { + name: profile + for name, profile in data["profiles"].items() + if not ( + len(profile.get("active_in", [])) == 1 + and re.fullmatch(r"-?[0-9]+", str(profile.get("active_in", [""])[0])) + ) + } self._save_profile_data(data) return self._success_response(dict, global_config=dict(data["global_config"]), games=[]) except Exception as error: -- cgit v1.2.3 From b12e456585b60479d4e830a96590d18928a3aba7 Mon Sep 17 00:00:00 2001 From: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> Date: Thu, 10 Sep 2026 16:30:16 -0400 Subject: refactor: isolate flatpak profile cleanup --- py_modules/lsfg_vk/configuration.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'py_modules/lsfg_vk/configuration.py') diff --git a/py_modules/lsfg_vk/configuration.py b/py_modules/lsfg_vk/configuration.py index 7e68479..17dcaf3 100644 --- a/py_modules/lsfg_vk/configuration.py +++ b/py_modules/lsfg_vk/configuration.py @@ -139,6 +139,19 @@ class ConfigurationService(BaseService): except Exception as error: return self._error_response(dict, str(error), app_id=str(app_id), config=None, exists=False) + def reset_all_flatpak_configs(self) -> Dict[str, Any]: + try: + data = self._get_profile_data() + data["profiles"] = { + name: profile + for name, profile in data["profiles"].items() + if not name.startswith(self.FLATPAK_PROFILE_PREFIX) + } + self._save_profile_data(data) + return self._success_response(dict, global_config=dict(data["global_config"])) + except Exception as error: + return self._error_response(dict, str(error)) + def reset_game_config(self, appid: str) -> Dict[str, Any]: try: data = self._get_profile_data() -- cgit v1.2.3 From 954b2abc47d8772211cb8ecee523900f823184e8 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 11 Sep 2026 11:08:12 -0400 Subject: better uninstall cleanup --- py_modules/lsfg_vk/configuration.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'py_modules/lsfg_vk/configuration.py') diff --git a/py_modules/lsfg_vk/configuration.py b/py_modules/lsfg_vk/configuration.py index 17dcaf3..d1852a0 100644 --- a/py_modules/lsfg_vk/configuration.py +++ b/py_modules/lsfg_vk/configuration.py @@ -72,20 +72,30 @@ class ConfigurationService(BaseService): self.log.error(f"Error reading game configs: {error}") return self._error_response(dict, str(error), games=[]) + def update_global_config(self, config: Dict[str, Any]) -> Dict[str, Any]: + try: + data = self._get_profile_data() + merged_config = {**data["global_config"], **config} + validated = self._public_config(merged_config) + data["global_config"] = { + "dll": validated["dll"], + "no_fp16": validated["no_fp16"], + } + self._save_profile_data(data) + return self._success_response(dict, global_config=dict(data["global_config"])) + except Exception as error: + return self._error_response(dict, str(error), global_config=None) + def update_game_config(self, appid: str, game_name: str, config: Dict[str, Any]) -> Dict[str, Any]: try: data = self._get_profile_data() old_name, _ = self._profile_for_appid(data, appid) name = self._profile_name(data, appid, game_name) - merged_config = {**data["global_config"], **config} + merged_config = {**data["global_config"], **{key: value for key, value in config.items() if key != "no_fp16"}} if not config.get("dll"): merged_config["dll"] = data["global_config"].get("dll", "") validated = self._public_config(merged_config) validated["active_in"] = [str(appid)] - data["global_config"] = { - "dll": validated["dll"], - "no_fp16": validated["no_fp16"], - } if old_name and old_name != name: data["profiles"].pop(old_name, None) data["profiles"][name] = validated @@ -114,15 +124,11 @@ class ConfigurationService(BaseService): try: data = self._get_profile_data() name = self.flatpak_profile_name(app_id) - merged_config = {**data["global_config"], **config} + merged_config = {**data["global_config"], **{key: value for key, value in config.items() if key != "no_fp16"}} if not config.get("dll"): merged_config["dll"] = data["global_config"].get("dll", "") validated = self._public_config(merged_config) validated["active_in"] = [] - data["global_config"] = { - "dll": validated["dll"], - "no_fp16": validated["no_fp16"], - } data["profiles"][name] = validated self._save_profile_data(data) return self._success_response(dict, app_id=str(app_id), profile=name, exists=True, config=validated) -- cgit v1.2.3 From f3074fbe1427411dc3b5597d2e87918383299e3f Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 11 Sep 2026 13:47:52 -0400 Subject: feat: non steam tab, faster bulk actions --- py_modules/lsfg_vk/configuration.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'py_modules/lsfg_vk/configuration.py') diff --git a/py_modules/lsfg_vk/configuration.py b/py_modules/lsfg_vk/configuration.py index d1852a0..3c65972 100644 --- a/py_modules/lsfg_vk/configuration.py +++ b/py_modules/lsfg_vk/configuration.py @@ -169,6 +169,33 @@ class ConfigurationService(BaseService): except Exception as error: return self._error_response(dict, str(error), appid=str(appid), config=None) + def reset_game_configs(self, appids: list[str]) -> Dict[str, Any]: + try: + if not isinstance(appids, list): + raise ValueError("appids must be a list") + requested = set() + for appid in appids: + if isinstance(appid, bool) or not isinstance(appid, (str, int)): + raise ValueError("appids must contain only strings or integers") + value = str(appid) + if not re.fullmatch(r"-?[0-9]+", value): + raise ValueError("appids must contain only numeric App IDs") + requested.add(value) + + data = self._get_profile_data() + data["profiles"] = { + name: profile + for name, profile in data["profiles"].items() + if not ( + len(profile.get("active_in", [])) == 1 + and str(profile["active_in"][0]) in requested + ) + } + self._save_profile_data(data) + return self._success_response(dict, global_config=dict(data["global_config"]), games=[]) + except Exception as error: + return self._error_response(dict, str(error), games=[]) + def reset_all_game_configs(self) -> Dict[str, Any]: try: data = self._get_profile_data() -- cgit v1.2.3