feat: add service-specific configuration overrides

Implement comprehensive per-service config override system that allows any configuration section (dl, n_m3u8dl_re, aria2c, subtitle, etc.) to be customized on a per-service basis.

Fixes #13
This commit is contained in:
Andy
2025-11-03 16:56:58 +00:00
parent f979e94235
commit f00790f31b
2 changed files with 70 additions and 23 deletions

View File

@@ -318,6 +318,38 @@ class dl:
self.log = logging.getLogger("download") self.log = logging.getLogger("download")
self.service = Services.get_tag(ctx.invoked_subcommand) self.service = Services.get_tag(ctx.invoked_subcommand)
service_dl_config = config.services.get(self.service, {}).get("dl", {})
if service_dl_config:
param_types = {param.name: param.type for param in ctx.command.params if param.name}
for param_name, service_value in service_dl_config.items():
if param_name not in ctx.params:
continue
current_value = ctx.params[param_name]
global_default = config.dl.get(param_name)
param_type = param_types.get(param_name)
try:
if param_type and global_default is not None:
global_default = param_type.convert(global_default, None, ctx)
except Exception as e:
self.log.debug(f"Failed to convert global default for '{param_name}': {e}")
if current_value == global_default or (current_value is None and global_default is None):
try:
converted_value = service_value
if param_type and service_value is not None:
converted_value = param_type.convert(service_value, None, ctx)
ctx.params[param_name] = converted_value
self.log.debug(f"Applied service-specific '{param_name}' override: {converted_value}")
except Exception as e:
self.log.warning(
f"Failed to apply service-specific '{param_name}' override: {e}. "
f"Check that the value '{service_value}' is valid for this parameter."
)
self.profile = profile self.profile = profile
self.tmdb_id = tmdb_id self.tmdb_id = tmdb_id
self.tmdb_name = tmdb_name self.tmdb_name = tmdb_name
@@ -383,31 +415,34 @@ class dl:
config.decryption = config.decryption_map.get(self.service, config.decryption) config.decryption = config.decryption_map.get(self.service, config.decryption)
service_config = config.services.get(self.service, {}) service_config = config.services.get(self.service, {})
if service_config:
reserved_keys = {
"profiles",
"api_key",
"certificate",
"api_endpoint",
"region",
"device",
"endpoints",
"client",
"dl",
}
reserved_keys = { for config_key, override_value in service_config.items():
"profiles", if config_key in reserved_keys or not isinstance(override_value, dict):
"api_key", continue
"certificate",
"api_endpoint",
"region",
"device",
"endpoints",
"client",
}
for config_key, override_value in service_config.items(): if hasattr(config, config_key):
if config_key in reserved_keys: current_config = getattr(config, config_key, {})
continue
if isinstance(override_value, dict) and hasattr(config, config_key): if isinstance(current_config, dict):
current_config = getattr(config, config_key, {}) merged_config = deepcopy(current_config)
if isinstance(current_config, dict): merge_dict(override_value, merged_config)
merged_config = {**current_config, **override_value} setattr(config, config_key, merged_config)
setattr(config, config_key, merged_config)
self.log.debug( self.log.debug(
f"Applied service-specific '{config_key}' overrides for {self.service}: {override_value}" f"Applied service-specific '{config_key}' overrides for {self.service}: {override_value}"
) )
with console.status("Loading Key Vaults...", spinner="dots"): with console.status("Loading Key Vaults...", spinner="dots"):
self.vaults = Vaults(self.service) self.vaults = Vaults(self.service)

View File

@@ -450,12 +450,24 @@ services:
region: "GB" region: "GB"
api_endpoint: "https://api.uk.service.com" api_endpoint: "https://api.uk.service.com"
# Example: Rate-limited service
RATE_LIMITED_SERVICE:
dl:
downloads: 2 # Limit concurrent downloads
workers: 4 # Reduce workers to avoid rate limits
n_m3u8dl_re:
thread_count: 4 # Very low thread count
retry_count: 20 # More retries for flaky service
aria2c:
max_concurrent_downloads: 1 # Download tracks one at a time
max_connection_per_server: 1 # Single connection only
# Notes on service-specific overrides: # Notes on service-specific overrides:
# - Overrides are merged with global config, not replaced # - Overrides are merged with global config, not replaced
# - Only specified keys are overridden, others use global defaults # - Only specified keys are overridden, others use global defaults
# - Reserved keys (profiles, api_key, certificate, etc.) are NOT treated as overrides # - Reserved keys (profiles, api_key, certificate, etc.) are NOT treated as overrides
# - Any dict-type config option can be overridden (dl, aria2c, n_m3u8dl_re, etc.) # - Any dict-type config option can be overridden (dl, aria2c, n_m3u8dl_re, subtitle, etc.)
# - Use --debug flag to see which overrides are applied during downloads # - CLI arguments always take priority over service-specific config
# External proxy provider services # External proxy provider services
proxy_providers: proxy_providers: