mirror of
https://github.com/nirvana-7777/script.service.ultimate.git
synced 2026-09-16 14:12:20 +02:00
520 lines
18 KiB
Python
520 lines
18 KiB
Python
# streaming_providers/base/settings/provider_enable_manager.py
|
|
"""
|
|
Provider Enable Manager - Handles provider enable/disable status with precedence:
|
|
1. Kodi settings (if in Kodi environment and setting exists)
|
|
2. providers_enabled.json file
|
|
3. Default to enabled (True)
|
|
|
|
Works in both Kodi and standalone environments.
|
|
"""
|
|
|
|
import time
|
|
from enum import Enum
|
|
from typing import Any, Dict, Optional, Tuple
|
|
|
|
from ..utils.environment import get_vfs_instance, is_kodi_environment
|
|
from ..utils.logger import logger
|
|
|
|
|
|
class EnableSource(Enum):
|
|
"""Source of the enable setting"""
|
|
|
|
KODI = "kodi"
|
|
FILE = "file"
|
|
DEFAULT = "default"
|
|
|
|
|
|
class ProviderEnableManager:
|
|
"""
|
|
Manages provider enable/disable status with proper precedence.
|
|
Uses VFS for file operations to work in both Kodi and standalone.
|
|
"""
|
|
|
|
DEFAULT_FILENAME = "providers_enabled.json"
|
|
DEFAULT_VERSION = "1.0"
|
|
CACHE_TTL = 30 # seconds
|
|
|
|
def __init__(self, config_dir: Optional[str] = None):
|
|
"""
|
|
Initialize the enable manager.
|
|
|
|
Args:
|
|
config_dir: Optional configuration directory (uses VFS default if None)
|
|
"""
|
|
self.vfs = get_vfs_instance(config_dir=config_dir)
|
|
self._cache: Optional[Dict[str, Any]] = None
|
|
self._cache_time: float = 0
|
|
self._kodi_bridge = None
|
|
|
|
# Try to initialize Kodi bridge if in Kodi environment
|
|
if is_kodi_environment():
|
|
try:
|
|
# Fixed import path - kodi_settings_bridge is in the same directory
|
|
from .kodi_settings_bridge import KodiSettingsBridge
|
|
|
|
self._kodi_bridge = KodiSettingsBridge(config_dir=config_dir)
|
|
logger.debug("ProviderEnableManager: Kodi bridge initialized")
|
|
except ImportError as e:
|
|
logger.debug(f"ProviderEnableManager: Kodi bridge not available: {e}")
|
|
self._kodi_bridge = None
|
|
except Exception as e:
|
|
logger.error(f"ProviderEnableManager: Error initializing Kodi bridge: {e}")
|
|
self._kodi_bridge = None
|
|
|
|
def _load_file(self, force_reload: bool = False) -> Dict[str, Any]:
|
|
"""
|
|
Load providers_enabled.json file with caching.
|
|
|
|
Args:
|
|
force_reload: Force reload from disk ignoring cache
|
|
|
|
Returns:
|
|
Dictionary with file contents
|
|
"""
|
|
current_time = time.time()
|
|
|
|
# Check cache first
|
|
if not force_reload and self._cache and (current_time - self._cache_time) < self.CACHE_TTL:
|
|
return self._cache
|
|
|
|
# Initialize default structure
|
|
default_data = {"version": self.DEFAULT_VERSION, "providers": {}}
|
|
|
|
# Check if file exists
|
|
if not self.vfs.exists(self.DEFAULT_FILENAME):
|
|
self._cache = default_data
|
|
self._cache_time = current_time
|
|
logger.debug(
|
|
f"ProviderEnableManager: File {self.DEFAULT_FILENAME} not found, using defaults"
|
|
)
|
|
return default_data
|
|
|
|
try:
|
|
# Read file via VFS
|
|
data = self.vfs.read_json(self.DEFAULT_FILENAME)
|
|
|
|
if not isinstance(data, dict):
|
|
logger.warning(f"ProviderEnableManager: Invalid file format, using defaults")
|
|
self._cache = default_data
|
|
self._cache_time = current_time
|
|
return default_data
|
|
|
|
# Ensure required structure
|
|
if "version" not in data:
|
|
data["version"] = self.DEFAULT_VERSION
|
|
|
|
if "providers" not in data or not isinstance(data["providers"], dict):
|
|
data["providers"] = {}
|
|
|
|
# Ensure all values are booleans
|
|
for provider, value in list(data["providers"].items()):
|
|
if not isinstance(value, bool):
|
|
try:
|
|
data["providers"][provider] = bool(value)
|
|
except (ValueError, TypeError):
|
|
logger.warning(
|
|
f"ProviderEnableManager: Invalid value for {provider}, removing"
|
|
)
|
|
del data["providers"][provider]
|
|
|
|
# Add last_updated if missing
|
|
if "last_updated" not in data:
|
|
data["last_updated"] = current_time
|
|
|
|
# Update cache
|
|
self._cache = data
|
|
self._cache_time = current_time
|
|
|
|
logger.debug(
|
|
f"ProviderEnableManager: Loaded {len(data['providers'])} providers from file"
|
|
)
|
|
return data
|
|
|
|
except Exception as e:
|
|
logger.error(f"ProviderEnableManager: Error loading {self.DEFAULT_FILENAME}: {e}")
|
|
self._cache = default_data
|
|
self._cache_time = current_time
|
|
return default_data
|
|
|
|
def _save_file(self, data: Dict[str, Any]) -> bool:
|
|
"""
|
|
Save data to providers_enabled.json file.
|
|
|
|
Args:
|
|
data: Data to save
|
|
|
|
Returns:
|
|
True if successful, False otherwise
|
|
"""
|
|
try:
|
|
# Add/update metadata
|
|
data["last_updated"] = time.time()
|
|
if "version" not in data:
|
|
data["version"] = self.DEFAULT_VERSION
|
|
|
|
# Write via VFS
|
|
success = self.vfs.write_json(self.DEFAULT_FILENAME, data)
|
|
|
|
if success:
|
|
# Update cache
|
|
self._cache = data
|
|
self._cache_time = time.time()
|
|
logger.debug(
|
|
f"ProviderEnableManager: Saved {len(data.get('providers', {}))} providers to file"
|
|
)
|
|
else:
|
|
logger.error(f"ProviderEnableManager: Failed to write to {self.DEFAULT_FILENAME}")
|
|
|
|
return success
|
|
|
|
except Exception as e:
|
|
logger.error(f"ProviderEnableManager: Error saving {self.DEFAULT_FILENAME}: {e}")
|
|
return False
|
|
|
|
def _get_kodi_enabled_status(self, provider_name: str) -> Optional[bool]:
|
|
"""
|
|
Get enabled status from Kodi settings.
|
|
|
|
Args:
|
|
provider_name: Provider name (e.g., "joyn_de", "rtlplus")
|
|
|
|
Returns:
|
|
True/False if setting exists in Kodi, None if not set
|
|
"""
|
|
if not is_kodi_environment() or not self._kodi_bridge:
|
|
return None
|
|
|
|
try:
|
|
# Parse provider_name for potential country suffix
|
|
parts = provider_name.rsplit("_", 1)
|
|
|
|
# Check country-specific setting first (e.g., "enable_joyn_de")
|
|
if len(parts) == 2 and len(parts[1]) in (2, 3): # Country code length
|
|
provider, country = parts
|
|
country_setting = f"enable_{provider}_{country}"
|
|
|
|
# Try country-specific setting
|
|
country_value = self._kodi_bridge.get_setting(country_setting)
|
|
if country_value:
|
|
enabled = country_value.lower() in ["true", "1", "yes"]
|
|
logger.debug(
|
|
f"ProviderEnableManager: Found Kodi setting {country_setting}={enabled}"
|
|
)
|
|
return enabled
|
|
|
|
# Try general provider setting (e.g., "enable_joyn")
|
|
general_setting = f"enable_{provider_name}"
|
|
general_value = self._kodi_bridge.get_setting(general_setting)
|
|
|
|
if general_value:
|
|
enabled = general_value.lower() in ["true", "1", "yes"]
|
|
logger.debug(
|
|
f"ProviderEnableManager: Found Kodi setting {general_setting}={enabled}"
|
|
)
|
|
return enabled
|
|
|
|
# No Kodi setting found
|
|
logger.debug(f"ProviderEnableManager: No Kodi setting found for {provider_name}")
|
|
return None
|
|
|
|
except Exception as e:
|
|
logger.warning(
|
|
f"ProviderEnableManager: Error reading Kodi setting for {provider_name}: {e}"
|
|
)
|
|
return None
|
|
|
|
def is_provider_enabled(self, provider_name: str) -> bool:
|
|
"""
|
|
Check if provider is enabled using precedence rules.
|
|
|
|
Precedence:
|
|
1. Kodi settings (if in Kodi and setting exists)
|
|
2. providers_enabled.json file
|
|
3. Default to True
|
|
|
|
Args:
|
|
provider_name: Provider name (e.g., "joyn_de", "rtlplus")
|
|
|
|
Returns:
|
|
True if enabled, False if disabled
|
|
"""
|
|
# 1. Check Kodi settings first
|
|
kodi_enabled = self._get_kodi_enabled_status(provider_name)
|
|
if kodi_enabled is not None:
|
|
return kodi_enabled
|
|
|
|
# 2. Check file
|
|
data = self._load_file()
|
|
file_enabled = data["providers"].get(provider_name)
|
|
|
|
if file_enabled is not None:
|
|
return file_enabled
|
|
|
|
# 3. Default to enabled
|
|
return True
|
|
|
|
def get_enabled_source(self, provider_name: str) -> EnableSource:
|
|
"""
|
|
Determine where the enabled status comes from.
|
|
|
|
Args:
|
|
provider_name: Provider name
|
|
|
|
Returns:
|
|
EnableSource enum (KODI, FILE, or DEFAULT)
|
|
"""
|
|
# Check Kodi first
|
|
kodi_enabled = self._get_kodi_enabled_status(provider_name)
|
|
if kodi_enabled is not None:
|
|
return EnableSource.KODI
|
|
|
|
# Check file
|
|
data = self._load_file()
|
|
file_enabled = data["providers"].get(provider_name)
|
|
|
|
if file_enabled is not None:
|
|
return EnableSource.FILE
|
|
|
|
return EnableSource.DEFAULT
|
|
|
|
def set_provider_enabled(self, provider_name: str, enabled: bool) -> Tuple[bool, str]:
|
|
"""
|
|
Set enabled status for a provider (writes to file only).
|
|
|
|
Note: Cannot override Kodi settings. If provider is controlled by
|
|
Kodi settings, this will return False.
|
|
|
|
Args:
|
|
provider_name: Provider name
|
|
enabled: True to enable, False to disable
|
|
|
|
Returns:
|
|
Tuple of (success: bool, message: str)
|
|
"""
|
|
# Check if controlled by Kodi
|
|
source = self.get_enabled_source(provider_name)
|
|
|
|
if source == EnableSource.KODI:
|
|
message = (
|
|
f"Provider '{provider_name}' is controlled by Kodi settings. "
|
|
f"Change the setting in Kodi addon settings."
|
|
)
|
|
logger.warning(f"ProviderEnableManager: {message}")
|
|
return False, message
|
|
|
|
# Load current data
|
|
data = self._load_file()
|
|
|
|
# Update provider status
|
|
data["providers"][provider_name] = bool(enabled)
|
|
|
|
# Save to file
|
|
success = self._save_file(data)
|
|
|
|
if success:
|
|
action = "enabled" if enabled else "disabled"
|
|
message = f"Provider '{provider_name}' {action} in file"
|
|
logger.info(f"ProviderEnableManager: {message}")
|
|
return True, message
|
|
else:
|
|
message = f"Failed to save enabled status for '{provider_name}'"
|
|
logger.error(f"ProviderEnableManager: {message}")
|
|
return False, message
|
|
|
|
def get_all_enabled_status(self) -> Dict[str, Dict[str, Any]]:
|
|
"""
|
|
Get enabled status for all providers in file.
|
|
|
|
Returns:
|
|
Dictionary mapping provider names to their status info
|
|
"""
|
|
data = self._load_file()
|
|
result = {}
|
|
|
|
for provider_name, enabled in data["providers"].items():
|
|
source = self.get_enabled_source(provider_name)
|
|
|
|
result[provider_name] = {
|
|
"enabled": enabled,
|
|
"source": source.value, # Convert enum to string
|
|
"can_modify": source != EnableSource.KODI,
|
|
}
|
|
|
|
return result
|
|
|
|
def get_provider_status(self, provider_name: str) -> Optional[Dict[str, Any]]:
|
|
"""
|
|
Get detailed status for a specific provider.
|
|
|
|
Args:
|
|
provider_name: Provider name
|
|
|
|
Returns:
|
|
Dictionary with status info, or None if provider not in file
|
|
"""
|
|
data = self._load_file()
|
|
|
|
if provider_name not in data["providers"]:
|
|
return None
|
|
|
|
enabled = data["providers"][provider_name]
|
|
source = self.get_enabled_source(provider_name)
|
|
|
|
return {
|
|
"provider": provider_name,
|
|
"enabled": enabled,
|
|
"source": source.value, # Convert enum to string
|
|
"can_modify": source != EnableSource.KODI,
|
|
"in_file": True,
|
|
}
|
|
|
|
def delete_provider_setting(self, provider_name: str) -> Tuple[bool, str]:
|
|
"""
|
|
Delete provider setting from file (reset to default).
|
|
|
|
Args:
|
|
provider_name: Provider name
|
|
|
|
Returns:
|
|
Tuple of (success: bool, message: str)
|
|
"""
|
|
# Check if controlled by Kodi
|
|
source = self.get_enabled_source(provider_name)
|
|
|
|
if source == EnableSource.KODI:
|
|
message = f"Cannot delete setting for '{provider_name}' - controlled by Kodi"
|
|
logger.warning(f"ProviderEnableManager: {message}")
|
|
return False, message
|
|
|
|
# Load current data
|
|
data = self._load_file()
|
|
|
|
# Remove if exists
|
|
if provider_name in data["providers"]:
|
|
del data["providers"][provider_name]
|
|
|
|
# Save to file
|
|
success = self._save_file(data)
|
|
|
|
if success:
|
|
message = f"Deleted setting for '{provider_name}' from file"
|
|
logger.info(f"ProviderEnableManager: {message}")
|
|
return True, message
|
|
else:
|
|
message = f"Failed to delete setting for '{provider_name}'"
|
|
logger.error(f"ProviderEnableManager: {message}")
|
|
return False, message
|
|
|
|
# Provider not in file
|
|
message = f"Setting for '{provider_name}' not found in file"
|
|
logger.debug(f"ProviderEnableManager: {message}")
|
|
return True, message # Already not in file, so success
|
|
|
|
def reset_all_to_defaults(self) -> Tuple[bool, str]:
|
|
"""
|
|
Reset all provider settings to defaults (empty file).
|
|
|
|
Returns:
|
|
Tuple of (success: bool, message: str)
|
|
"""
|
|
try:
|
|
# Create default structure
|
|
default_data = {
|
|
"version": self.DEFAULT_VERSION,
|
|
"last_updated": time.time(),
|
|
"providers": {},
|
|
}
|
|
|
|
# Save empty file
|
|
success = self._save_file(default_data)
|
|
|
|
if success:
|
|
message = "All provider settings reset to defaults"
|
|
logger.info(f"ProviderEnableManager: {message}")
|
|
return True, message
|
|
else:
|
|
message = "Failed to reset provider settings"
|
|
logger.error(f"ProviderEnableManager: {message}")
|
|
return False, message
|
|
|
|
except Exception as e:
|
|
message = f"Error resetting provider settings: {e}"
|
|
logger.error(f"ProviderEnableManager: {message}")
|
|
return False, message
|
|
|
|
def get_file_info(self) -> Dict[str, Any]:
|
|
"""
|
|
Get information about the providers_enabled.json file.
|
|
|
|
Returns:
|
|
Dictionary with file information
|
|
"""
|
|
data = self._load_file()
|
|
|
|
return {
|
|
"filename": self.DEFAULT_FILENAME,
|
|
"version": data.get("version", self.DEFAULT_VERSION),
|
|
"last_updated": data.get("last_updated"),
|
|
"provider_count": len(data.get("providers", {})),
|
|
"file_exists": self.vfs.exists(self.DEFAULT_FILENAME),
|
|
"file_path": (
|
|
self.vfs.get_file_path(self.DEFAULT_FILENAME)
|
|
if hasattr(self.vfs, "get_file_path")
|
|
else "vfs"
|
|
),
|
|
}
|
|
|
|
def migrate_from_kodi(self) -> Tuple[bool, str, int]:
|
|
"""
|
|
Migrate all Kodi enable settings to the file.
|
|
Only copies settings that are explicitly set in Kodi.
|
|
|
|
Returns:
|
|
Tuple of (success: bool, message: str, migrated_count: int)
|
|
"""
|
|
if not is_kodi_environment() or not self._kodi_bridge:
|
|
return False, "Not in Kodi environment or Kodi bridge not available", 0
|
|
|
|
try:
|
|
# Load current file data
|
|
data = self._load_file()
|
|
migrated_count = 0
|
|
|
|
# Discover all providers from Kodi settings
|
|
try:
|
|
kodi_providers = self._kodi_bridge.discover_all_providers()
|
|
except AttributeError:
|
|
# Fallback if discover_all_providers not available
|
|
kodi_providers = {}
|
|
|
|
# Check each provider
|
|
for provider_name in kodi_providers:
|
|
kodi_enabled = self._get_kodi_enabled_status(provider_name)
|
|
|
|
if kodi_enabled is not None:
|
|
# Kodi has explicit setting, migrate to file
|
|
data["providers"][provider_name] = kodi_enabled
|
|
migrated_count += 1
|
|
logger.debug(f"ProviderEnableManager: Migrated {provider_name}={kodi_enabled}")
|
|
|
|
if migrated_count > 0:
|
|
# Save migrated data
|
|
success = self._save_file(data)
|
|
if success:
|
|
message = f"Migrated {migrated_count} provider settings from Kodi to file"
|
|
logger.info(f"ProviderEnableManager: {message}")
|
|
return True, message, migrated_count
|
|
else:
|
|
message = "Failed to save migrated settings"
|
|
logger.error(f"ProviderEnableManager: {message}")
|
|
return False, message, 0
|
|
else:
|
|
message = "No Kodi provider settings found to migrate"
|
|
logger.info(f"ProviderEnableManager: {message}")
|
|
return True, message, 0
|
|
|
|
except Exception as e:
|
|
message = f"Error migrating from Kodi: {e}"
|
|
logger.error(f"ProviderEnableManager: {message}")
|
|
return False, message, 0
|