Files
script.service.ultimate/lib/streaming_providers/base/drm/plugin_manager.py
T

635 lines
25 KiB
Python
Raw Normal View History

2025-10-29 20:23:50 +01:00
# streaming_providers/base/drm/plugin_manager.py
2026-01-26 16:32:30 +01:00
"""
Manager for DRM configuration plugins with two-phase processing.
Handles plugin registration, discovery, and processing of DRM configs with PSSH data.
"""
2026-01-06 16:41:03 +01:00
import traceback
2025-10-29 20:23:50 +01:00
from typing import Dict, List, Optional
2026-01-06 16:41:03 +01:00
2026-02-13 17:07:25 +01:00
from ..models.drm import DRMConfig, DRMSystem, PSSHData
2025-10-29 20:23:50 +01:00
from ..utils.logger import logger
2026-01-06 16:41:03 +01:00
from .drm_plugin import DRMPlugin
2025-10-29 20:23:50 +01:00
class DRMPluginManager:
"""
Manager for DRM configuration plugins.
2026-01-06 16:41:03 +01:00
2026-01-26 16:32:30 +01:00
Supports two-phase processing:
- Phase 1: GENERIC plugins (config generators, run before provider)
- Phase 2: System-specific plugins (config transformers, run after provider)
2025-10-29 20:23:50 +01:00
"""
def __init__(self, auto_discover: bool = True):
"""Initialize with empty plugin registry and optionally auto-discover plugins"""
self.plugins: Dict[DRMSystem, DRMPlugin] = {}
logger.debug("DRMPluginManager: Initialized with empty plugin registry")
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
if auto_discover:
2026-01-16 12:24:41 +01:00
logger.debug("DRMPluginManager: Auto-discovery enabled, discovering plugins...")
2025-10-29 20:23:50 +01:00
discovered = self.discover_plugins()
if discovered:
2026-01-06 16:41:03 +01:00
logger.debug(
f"DRMPluginManager: Auto-discovery completed, {len(discovered)} plugins ready"
)
2025-10-29 20:23:50 +01:00
else:
2026-01-16 12:24:41 +01:00
logger.debug("DRMPluginManager: Auto-discovery completed, no plugins found")
2025-10-29 20:23:50 +01:00
def register_plugin(self, plugin: DRMPlugin) -> None:
"""
Register a single plugin instance.
Args:
plugin: Configured plugin instance to register
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
Raises:
ValueError: If plugin is invalid or DRM system already has a plugin
"""
if not isinstance(plugin, DRMPlugin):
2026-01-06 16:41:03 +01:00
logger.warning(
f"DRMPluginManager: Registration failed - invalid plugin type: {type(plugin)}"
)
2025-10-29 20:23:50 +01:00
raise ValueError("Only DRMPlugin instances can be registered")
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
drm_system = plugin.supported_drm_system
plugin_name = plugin.plugin_name
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
if drm_system in self.plugins:
existing_plugin = self.plugins[drm_system].plugin_name
2026-01-06 16:41:03 +01:00
logger.warning(
2026-01-26 16:32:30 +01:00
f"DRMPluginManager: Overwriting existing plugin '{existing_plugin}' "
f"with '{plugin_name}' for DRM system {drm_system}"
2026-01-06 16:41:03 +01:00
)
2025-10-29 20:23:50 +01:00
self.plugins[drm_system] = plugin
2026-01-26 16:32:30 +01:00
phase = "1-GENERIC" if drm_system == DRMSystem.GENERIC else "2-System-specific"
logger.info(
f"DRMPluginManager: Successfully registered plugin '{plugin_name}' "
f"for DRM system {drm_system} (Phase: {phase})"
2026-01-06 16:41:03 +01:00
)
2025-10-29 20:23:50 +01:00
def discover_plugins(self) -> List[str]:
"""
Discover and register all available DRM plugins by scanning filesystem.
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
Scans only the plugins directory (not subdirectories) for Python files
containing classes that inherit from DRMPlugin.
Returns:
List of discovered plugin names
"""
import importlib.util
import inspect
2026-01-06 16:41:03 +01:00
import os
2025-10-29 20:23:50 +01:00
logger.debug("DRMPluginManager: Starting filesystem-based plugin autodiscovery")
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
# Get the directory where this plugin manager is located
current_dir = os.path.dirname(os.path.abspath(__file__))
plugins_dir = os.path.join(current_dir, "plugins") # Scan the plugins subfolder
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
# Check if plugins directory exists
if not os.path.exists(plugins_dir):
2026-01-16 12:24:41 +01:00
logger.debug(f"DRMPluginManager: Plugins directory does not exist: {plugins_dir}")
2025-10-29 20:23:50 +01:00
return []
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
logger.debug(f"DRMPluginManager: Scanning plugins directory: {plugins_dir}")
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
registered = []
failed_plugins = []
scanned_files = []
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
# Scan only the plugins directory (no subdirectories)
try:
files = os.listdir(plugins_dir)
except OSError as e:
logger.warning(f"DRMPluginManager: Error reading plugins directory: {e}")
return []
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
for filename in files:
file_path = os.path.join(plugins_dir, filename)
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
# Only process Python files (not directories or other files)
2026-01-06 16:41:03 +01:00
if (
2026-01-26 16:32:30 +01:00
filename.endswith(".py")
and not filename.startswith("__")
and os.path.isfile(file_path)
2026-01-06 16:41:03 +01:00
):
2025-10-29 20:23:50 +01:00
scanned_files.append(filename)
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
logger.debug(f"DRMPluginManager: Scanning file: {filename}")
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
try:
# Create module name from filename
module_name = os.path.splitext(filename)[0]
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
# Import the module dynamically
2026-01-16 12:24:41 +01:00
spec = importlib.util.spec_from_file_location(module_name, file_path)
2025-10-29 20:23:50 +01:00
if spec is None or spec.loader is None:
2026-01-06 16:41:03 +01:00
logger.debug(
f"DRMPluginManager: Could not create module spec for {filename}"
)
2025-10-29 20:23:50 +01:00
continue
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
# Find all classes in the module that inherit from DRMPlugin
plugin_classes = []
for name, obj in inspect.getmembers(module, inspect.isclass):
# Check if it's a DRMPlugin subclass (but not DRMPlugin itself)
2026-01-06 16:41:03 +01:00
if (
2026-01-26 16:32:30 +01:00
issubclass(obj, DRMPlugin)
and obj is not DRMPlugin
and obj.__module__ == module.__name__
2026-01-06 16:41:03 +01:00
):
2025-10-29 20:23:50 +01:00
plugin_classes.append((name, obj))
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
if not plugin_classes:
2026-01-16 12:24:41 +01:00
logger.debug(f"DRMPluginManager: No DRMPlugin classes found in {filename}")
2025-10-29 20:23:50 +01:00
continue
2026-01-06 16:41:03 +01:00
logger.debug(
f"DRMPluginManager: Found {len(plugin_classes)} plugin class(es) in {filename}: {[name for name, _ in plugin_classes]}"
)
2025-10-29 20:23:50 +01:00
# Instantiate and register each plugin class found
for class_name, plugin_class in plugin_classes:
try:
2026-01-06 16:41:03 +01:00
logger.debug(
f"DRMPluginManager: Attempting to instantiate {class_name} from {filename}"
)
2025-10-29 20:23:50 +01:00
# Create plugin instance
plugin = plugin_class()
plugin_name = plugin.plugin_name
drm_system = plugin.supported_drm_system
2026-01-06 16:41:03 +01:00
logger.debug(
f"DRMPluginManager: Successfully created plugin '{plugin_name}' (class: {class_name}) supporting {drm_system}"
)
2025-10-29 20:23:50 +01:00
# Register the plugin
self.register_plugin(plugin)
registered.append(plugin_name)
2026-01-06 16:41:03 +01:00
logger.debug(
f"DRMPluginManager: Plugin '{plugin_name}' from {filename} successfully registered"
)
2025-10-29 20:23:50 +01:00
except Exception as e:
2026-01-16 12:24:41 +01:00
error_msg = (
f"Failed to instantiate {class_name} from {filename}: {str(e)}"
2026-01-06 16:41:03 +01:00
)
2026-01-16 12:24:41 +01:00
failed_plugins.append((f"{filename}::{class_name}", error_msg))
2025-10-29 20:23:50 +01:00
logger.warning(f"DRMPluginManager: {error_msg}")
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
except Exception as e:
traceback_str = traceback.format_exc()
2026-01-16 12:24:41 +01:00
error_msg = f"Failed to process file {filename}: {str(e)}\n{traceback_str}"
2025-10-29 20:23:50 +01:00
failed_plugins.append((filename, error_msg))
logger.warning(f"DRMPluginManager: {error_msg}")
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
# Log scanning summary
2026-01-06 16:41:03 +01:00
logger.debug(
f"DRMPluginManager: Filesystem scan completed - scanned {len(scanned_files)} Python files"
)
2025-10-29 20:23:50 +01:00
if scanned_files:
logger.debug(f"DRMPluginManager: Scanned files: {scanned_files}")
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
# Log final discovery results
if registered:
2026-01-26 16:32:30 +01:00
logger.info(
f"DRMPluginManager: Filesystem autodiscovery completed - {len(registered)} plugins registered: {registered}"
2026-01-06 16:41:03 +01:00
)
2025-10-29 20:23:50 +01:00
else:
2026-01-06 16:41:03 +01:00
logger.debug(
"DRMPluginManager: Filesystem autodiscovery completed - no plugins were registered"
)
2025-10-29 20:23:50 +01:00
if failed_plugins:
2026-01-16 12:24:41 +01:00
logger.debug(f"DRMPluginManager: {len(failed_plugins)} plugins/files failed to load:")
2025-10-29 20:23:50 +01:00
for plugin_name, error in failed_plugins:
logger.debug(f" - {plugin_name}: {error}")
return registered
2026-01-26 16:32:30 +01:00
def has_generic_plugins(self) -> bool:
"""Check if any GENERIC plugins are registered"""
return DRMSystem.GENERIC in self.plugins
def has_system_specific_plugins(self) -> bool:
"""Check if any system-specific (non-GENERIC) plugins are registered"""
return any(sys != DRMSystem.GENERIC for sys in self.plugins.keys())
def process_generic_plugins(
self,
dummy_configs: List[DRMConfig],
pssh_data_list: List[PSSHData],
**kwargs
) -> List[DRMConfig]:
"""
PHASE 1: Process through GENERIC plugins only.
GENERIC plugins can CREATE configs from PSSH data.
They receive a dummy config (DRMSystem.NONE) and PSSH data,
and should return valid DRM configs or None.
Args:
dummy_configs: List with dummy config [DRMConfig(system=DRMSystem.NONE)]
pssh_data_list: PSSH data extracted from manifest
**kwargs: Additional context
Returns:
List of generated DRM configs, or empty list if generation failed
"""
if not self.has_generic_plugins():
logger.debug("Phase 1: No GENERIC plugins registered")
return []
if not pssh_data_list:
logger.debug("Phase 1: No PSSH data available for GENERIC plugins")
return []
plugin = self.plugins[DRMSystem.GENERIC]
logger.debug(f"Phase 1: Processing with GENERIC plugin '{plugin.plugin_name}'")
generated_configs = []
try:
2026-02-23 13:17:38 +01:00
# Generic plugins are system-agnostic: key IDs are the same across all DRM systems,
# so we only need to call the plugin once with the first PSSH entry.
pssh_data = pssh_data_list[0]
logger.debug(
f"Phase 1: Processing PSSH for system {pssh_data.system_id} "
f"with plugin '{plugin.plugin_name}'"
)
# Pass dummy config - plugin should return real config(s) or None
result = plugin.process_drm_config(
dummy_configs[0], # Dummy config
pssh_data,
**kwargs
)
2026-01-26 16:32:30 +01:00
2026-02-23 13:17:38 +01:00
if result:
generated_configs.append(result)
logger.debug(
f"Phase 1: Plugin '{plugin.plugin_name}' generated "
f"{result.system.value} config"
2026-01-26 16:32:30 +01:00
)
2026-02-23 13:17:38 +01:00
# If ClearKey found, return immediately
if result.system == DRMSystem.CLEARKEY:
logger.info(
f"Phase 1: ClearKey config found from GENERIC plugin, "
f"returning immediately"
2026-01-26 16:32:30 +01:00
)
2026-02-23 13:17:38 +01:00
return [result]
2026-01-26 16:32:30 +01:00
except Exception as e:
logger.error(
f"Phase 1: GENERIC plugin '{plugin.plugin_name}' failed: {e}",
exc_info=True
)
return []
if generated_configs:
logger.info(
f"Phase 1: GENERIC plugin generated {len(generated_configs)} configs"
)
else:
logger.debug("Phase 1: GENERIC plugin generated no configs")
return generated_configs
def process_system_specific_plugins(
self,
drm_configs: List[DRMConfig],
pssh_data_list: List[PSSHData],
**kwargs
) -> List[DRMConfig]:
"""
PHASE 2: Process through system-specific plugins (EXCLUDE GENERIC).
2026-02-25 20:33:36 +01:00
For each DRM config, the matching plugin is called once per PSSH entry
for that system (n PSSHs → n plugin calls). ClearKey results from
multiple calls are merged into a single DRMConfig by combining their
keyids dicts. For all other systems, all non-None results are collected
(single PSSH expected in practice, so behaviour is unchanged).
2026-01-26 16:32:30 +01:00
"""
if not drm_configs:
logger.debug("Phase 2: No DRM configs to process")
return []
2026-04-27 17:26:35 +02:00
drm_configs = sorted(drm_configs, key=lambda c: c.priority)
2026-01-26 16:32:30 +01:00
# Get system-specific plugins only (exclude GENERIC)
2026-02-25 20:33:36 +01:00
from typing import Any
2026-02-13 17:07:25 +01:00
system_plugins: dict[DRMSystem, Any] = {
2026-01-26 16:32:30 +01:00
sys: plugin for sys, plugin in self.plugins.items()
if sys != DRMSystem.GENERIC
}
if not system_plugins:
logger.debug("Phase 2: No system-specific plugins registered, returning provider configs")
return drm_configs
logger.debug(
f"Phase 2: Processing {len(drm_configs)} configs through "
f"{len(system_plugins)} system-specific plugins"
)
2026-02-25 20:33:36 +01:00
# Group all PSSHs by DRM system (preserve all entries, not just last)
pssh_by_system: dict[DRMSystem, List[PSSHData]] = {}
2026-01-26 16:32:30 +01:00
for pssh_data in pssh_data_list:
2026-02-13 17:07:25 +01:00
drm_sys = pssh_data.drm_system
if drm_sys is not None:
2026-02-25 20:33:36 +01:00
pssh_by_system.setdefault(drm_sys, []).append(pssh_data)
logger.debug(f"Phase 2: Mapped PSSH data for DRM system: {drm_sys}")
2026-01-26 16:32:30 +01:00
processed_configs = []
# Process each config through matching plugin
for config in drm_configs:
logger.debug(f"Phase 2: Processing DRM config for system: {config.system}")
2026-02-25 20:33:36 +01:00
if config.system not in system_plugins:
# No plugin for this system, keep original
logger.debug(
f"Phase 2: No plugin registered for DRM system {config.system}, "
f"passing through unchanged"
)
processed_configs.append(config)
continue
2026-01-26 16:32:30 +01:00
2026-02-25 20:33:36 +01:00
plugin = system_plugins[config.system]
# Fall back to [None] so the plugin is still called once when no PSSH is available
pssh_entries = pssh_by_system.get(config.system) or [None]
2026-01-26 16:32:30 +01:00
2026-02-25 20:33:36 +01:00
logger.debug(
f"Phase 2: Calling plugin '{plugin.plugin_name}' "
f"{len(pssh_entries)} time(s) for {config.system}"
)
2026-01-26 16:32:30 +01:00
2026-02-25 20:33:36 +01:00
# Call plugin once per PSSH entry, collect all non-None results
plugin_results: List[DRMConfig] = []
for pssh_data in pssh_entries:
try:
2026-01-26 16:32:30 +01:00
result = plugin.process_drm_config(config, pssh_data, **kwargs)
if result is not None:
2026-02-25 20:33:36 +01:00
plugin_results.append(result)
2026-01-26 16:32:30 +01:00
logger.debug(
2026-02-25 20:33:36 +01:00
f"Phase 2: Plugin '{plugin.plugin_name}' returned "
f"{result.system.value} config for PSSH "
f"'{pssh_data.system_id if pssh_data else 'None'}'"
2026-01-26 16:32:30 +01:00
)
else:
logger.debug(
2026-02-25 20:33:36 +01:00
f"Phase 2: Plugin '{plugin.plugin_name}' returned None "
f"for PSSH '{pssh_data.system_id if pssh_data else 'None'}'"
2026-01-26 16:32:30 +01:00
)
except Exception as e:
logger.warning(
2026-02-25 20:33:36 +01:00
f"Phase 2: Plugin '{plugin.plugin_name}' failed for PSSH "
f"'{pssh_data.system_id if pssh_data else 'None'}': {e}"
2026-01-26 16:32:30 +01:00
)
2026-02-25 20:33:36 +01:00
if not plugin_results:
# All calls returned None or errored — fall back to original config
2026-01-26 16:32:30 +01:00
logger.debug(
2026-02-25 20:33:36 +01:00
f"Phase 2: Plugin '{plugin.plugin_name}' produced no results, "
f"keeping original config"
2026-01-26 16:32:30 +01:00
)
processed_configs.append(config)
2026-02-25 20:33:36 +01:00
continue
# Separate ClearKey results from others
clearkey_results = [r for r in plugin_results if r.system == DRMSystem.CLEARKEY]
other_results = [r for r in plugin_results if r.system != DRMSystem.CLEARKEY]
# Merge all ClearKey results into one config by combining keyids
if clearkey_results:
merged_keyids: dict[str, str] = {}
2026-03-10 20:48:04 +01:00
highest_priority = 1
2026-02-25 20:33:36 +01:00
for ck in clearkey_results:
if ck.license and ck.license.keyids:
merged_keyids.update(ck.license.keyids)
if ck.priority > highest_priority:
highest_priority = ck.priority
merged_clearkey = DRMConfig.create_clearkey(
keyids=merged_keyids,
priority=highest_priority,
)
logger.info(
f"Phase 2: Merged {len(clearkey_results)} ClearKey result(s) into "
f"one config with {len(merged_keyids)} key(s)"
)
return [merged_clearkey]
# For non-ClearKey: collect all results (single PSSH expected in practice)
processed_configs.extend(other_results)
logger.debug(
f"Phase 2: Plugin '{plugin.plugin_name}' produced "
f"{len(other_results)} non-ClearKey config(s)"
)
2026-01-26 16:32:30 +01:00
logger.info(
f"Phase 2: Processed {len(drm_configs)} configs → {len(processed_configs)} configs"
)
return processed_configs
2026-01-06 16:41:03 +01:00
def process_drm_configs(
2026-01-26 16:32:30 +01:00
self,
drm_configs: List[DRMConfig],
pssh_data_list: List[PSSHData],
**kwargs
2026-01-06 16:41:03 +01:00
) -> List[DRMConfig]:
2025-10-29 20:23:50 +01:00
"""
2026-01-26 16:32:30 +01:00
Legacy method: Process configs through all plugins (generic first, then specific).
This maintains backward compatibility but uses single-phase processing.
For new code using two-phase flow, use process_generic_plugins() and
process_system_specific_plugins() separately via DRMOperations.
2025-10-29 20:23:50 +01:00
Args:
drm_configs: List of DRM configs to process
pssh_data_list: List of PSSH data extracted from manifest
**kwargs: Additional context from the original method call
Returns:
List of processed DRM configs (may be modified, filtered, or unchanged)
"""
if not drm_configs:
logger.debug("DRMPluginManager: No DRM configs to process")
return drm_configs
2026-01-06 16:41:03 +01:00
logger.debug(
2026-01-26 16:32:30 +01:00
f"DRMPluginManager: Processing {len(drm_configs)} DRM configs with "
f"{len(pssh_data_list)} PSSH data entries (legacy single-phase mode)"
2026-01-06 16:41:03 +01:00
)
2025-10-29 20:23:50 +01:00
# Create a mapping of DRM system to PSSH data for quick lookup
pssh_by_system = {}
for pssh_data in pssh_data_list:
if pssh_data.drm_system:
pssh_by_system[pssh_data.drm_system] = pssh_data
2026-01-06 16:41:03 +01:00
logger.debug(
f"DRMPluginManager: Mapped PSSH data for DRM system: {pssh_data.drm_system}"
)
2025-10-29 20:23:50 +01:00
# Separate generic and specific plugins
generic_plugins = []
specific_plugins = []
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
for drm_system, plugin in self.plugins.items():
if drm_system == DRMSystem.GENERIC:
generic_plugins.append(plugin)
2026-01-16 12:24:41 +01:00
logger.debug(f"DRMPluginManager: Found generic plugin '{plugin.plugin_name}'")
2025-10-29 20:23:50 +01:00
else:
specific_plugins.append((drm_system, plugin))
# Process generic plugins first
processed_configs = list(drm_configs)
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
for plugin in generic_plugins:
2026-01-06 16:41:03 +01:00
logger.debug(
f"DRMPluginManager: Processing through generic plugin '{plugin.plugin_name}'"
)
2025-10-29 20:23:50 +01:00
try:
# Generic plugins process all configs at once
temp_configs = []
for config in processed_configs:
pssh_data = pssh_by_system.get(config.system)
result = plugin.process_drm_config(config, pssh_data, **kwargs)
if result is not None:
temp_configs.append(result)
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
# Check for ClearKey and return immediately if found
for config in temp_configs:
if config.system == DRMSystem.CLEARKEY:
2026-01-26 16:32:30 +01:00
logger.info(
f"DRMPluginManager: ClearKey config found from generic plugin, "
f"returning immediately"
2026-01-06 16:41:03 +01:00
)
2025-10-29 20:23:50 +01:00
return [config]
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
processed_configs = temp_configs
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
except Exception as e:
2026-01-06 16:41:03 +01:00
logger.warning(
f"DRMPluginManager: Generic plugin '{plugin.plugin_name}' failed: {str(e)}"
)
2025-10-29 20:23:50 +01:00
continue
# Process specific plugins
final_configs = []
for config in processed_configs:
2026-01-16 12:24:41 +01:00
logger.debug(f"DRMPluginManager: Processing DRM config for system: {config.system}")
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
# Find specific plugin for this DRM system
plugin = self.plugins.get(config.system)
2026-01-06 16:41:03 +01:00
2026-01-26 16:32:30 +01:00
if plugin and plugin.supported_drm_system != DRMSystem.GENERIC:
2026-01-06 16:41:03 +01:00
logger.debug(
2026-01-26 16:32:30 +01:00
f"DRMPluginManager: Found plugin '{plugin.plugin_name}' for "
f"DRM system {config.system}"
2026-01-06 16:41:03 +01:00
)
2025-10-29 20:23:50 +01:00
try:
pssh_data = pssh_by_system.get(config.system)
if pssh_data:
2026-01-06 16:41:03 +01:00
logger.debug(
f"DRMPluginManager: Using PSSH data for DRM system {config.system}"
)
2025-10-29 20:23:50 +01:00
else:
2026-01-06 16:41:03 +01:00
logger.debug(
2026-01-26 16:32:30 +01:00
f"DRMPluginManager: No PSSH data available for "
f"DRM system {config.system}"
2026-01-06 16:41:03 +01:00
)
2026-01-16 12:24:41 +01:00
processed_config = plugin.process_drm_config(config, pssh_data, **kwargs)
2025-10-29 20:23:50 +01:00
if processed_config is not None:
# Check for ClearKey and return immediately if found
if processed_config.system == DRMSystem.CLEARKEY:
2026-01-26 16:32:30 +01:00
logger.info(
f"DRMPluginManager: ClearKey config found from plugin "
f"'{plugin.plugin_name}', returning immediately"
2026-01-06 16:41:03 +01:00
)
2025-10-29 20:23:50 +01:00
return [processed_config]
2026-01-06 16:41:03 +01:00
2025-10-29 20:23:50 +01:00
final_configs.append(processed_config)
2026-01-06 16:41:03 +01:00
logger.debug(
2026-01-26 16:32:30 +01:00
f"DRMPluginManager: Plugin '{plugin.plugin_name}' successfully "
f"processed config"
2026-01-06 16:41:03 +01:00
)
2025-10-29 20:23:50 +01:00
else:
2026-01-06 16:41:03 +01:00
logger.debug(
2026-01-26 16:32:30 +01:00
f"DRMPluginManager: Plugin '{plugin.plugin_name}' filtered out "
f"config (returned None)"
2026-01-06 16:41:03 +01:00
)
2025-10-29 20:23:50 +01:00
except Exception as e:
2026-01-06 16:41:03 +01:00
logger.warning(
2026-01-26 16:32:30 +01:00
f"DRMPluginManager: Plugin '{plugin.plugin_name}' failed to process "
f"config: {str(e)}"
2026-01-06 16:41:03 +01:00
)
2025-10-29 20:23:50 +01:00
final_configs.append(config)
logger.debug(f"DRMPluginManager: Using original config as fallback")
else:
2026-01-06 16:41:03 +01:00
logger.debug(
2026-01-26 16:32:30 +01:00
f"DRMPluginManager: No plugin registered for DRM system {config.system}, "
f"passing through unchanged"
2026-01-06 16:41:03 +01:00
)
2025-10-29 20:23:50 +01:00
final_configs.append(config)
2026-01-06 16:41:03 +01:00
logger.debug(
f"DRMPluginManager: Completed processing - {len(final_configs)} configs returned"
)
2025-10-29 20:23:50 +01:00
return final_configs
def get_plugin(self, drm_system: DRMSystem) -> Optional[DRMPlugin]:
"""
Get registered plugin for a DRM system.
Args:
drm_system: DRM system to get plugin for
Returns:
The plugin instance or None if not found
"""
plugin = self.plugins.get(drm_system)
if plugin:
2026-01-06 16:41:03 +01:00
logger.debug(
2026-01-26 16:32:30 +01:00
f"DRMPluginManager: Retrieved plugin '{plugin.plugin_name}' for "
f"DRM system {drm_system}"
2026-01-06 16:41:03 +01:00
)
2025-10-29 20:23:50 +01:00
else:
2026-01-16 12:24:41 +01:00
logger.debug(f"DRMPluginManager: No plugin found for DRM system {drm_system}")
2025-10-29 20:23:50 +01:00
return plugin
2026-01-26 16:32:30 +01:00
def list_plugins(self) -> Dict:
2025-10-29 20:23:50 +01:00
"""
2026-01-26 16:32:30 +01:00
List all registered plugins with phase information.
2025-10-29 20:23:50 +01:00
Returns:
2026-01-26 16:32:30 +01:00
Dictionary mapping DRM system values to plugin info
2025-10-29 20:23:50 +01:00
"""
2026-01-06 16:41:03 +01:00
plugin_list = {
2026-01-26 16:32:30 +01:00
drm_system.value: {
"name": plugin.plugin_name,
"system": drm_system.value,
"phase": "1-GENERIC" if drm_system == DRMSystem.GENERIC else "2-System-specific"
}
for drm_system, plugin in self.plugins.items()
2026-01-06 16:41:03 +01:00
}
2025-10-29 20:23:50 +01:00
logger.debug(f"DRMPluginManager: Currently registered plugins: {plugin_list}")
return plugin_list
def clear_plugins(self) -> None:
"""Clear all registered plugins"""
plugin_count = len(self.plugins)
self.plugins.clear()
2026-01-26 16:32:30 +01:00
logger.info(f"DRMPluginManager: Cleared {plugin_count} registered plugins")