feat: add service-specific configuration overrides

Add support for per-service configuration overrides allowing fine-tuned control of downloader and command options on a service-by-service basis.

Fixes #13
This commit is contained in:
Andy
2025-10-18 07:32:17 +00:00
parent 3dd12b0cbe
commit 9921690339
2 changed files with 158 additions and 82 deletions

View File

@@ -380,6 +380,33 @@ class dl:
if getattr(config, "decryption_map", None): if getattr(config, "decryption_map", None):
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, {})
reserved_keys = {
"profiles",
"api_key",
"certificate",
"api_endpoint",
"region",
"device",
"endpoints",
"client",
}
for config_key, override_value in service_config.items():
if config_key in reserved_keys:
continue
if isinstance(override_value, dict) and hasattr(config, config_key):
current_config = getattr(config, config_key, {})
if isinstance(current_config, dict):
merged_config = {**current_config, **override_value}
setattr(config, config_key, merged_config)
self.log.debug(
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)
total_vaults = len(config.key_vaults) total_vaults = len(config.key_vaults)

View File

@@ -34,21 +34,23 @@ title_cache_max_retention: 86400 # Maximum cache retention for fallback when API
# Debug logging configuration # Debug logging configuration
# Comprehensive JSON-based debug logging for troubleshooting and service development # Comprehensive JSON-based debug logging for troubleshooting and service development
debug: false # Enable structured JSON debug logging (default: false) debug:
# When enabled with --debug flag or set to true: false # Enable structured JSON debug logging (default: false)
# - Creates JSON Lines (.jsonl) log files with complete debugging context # When enabled with --debug flag or set to true:
# - Logs: session info, CLI params, service config, CDM details, authentication, # - Creates JSON Lines (.jsonl) log files with complete debugging context
# titles, tracks metadata, DRM operations, vault queries, errors with stack traces # - Logs: session info, CLI params, service config, CDM details, authentication,
# - File location: logs/unshackle_debug_{service}_{timestamp}.jsonl # titles, tracks metadata, DRM operations, vault queries, errors with stack traces
# - Also creates text log: logs/unshackle_root_{timestamp}.log # - File location: logs/unshackle_debug_{service}_{timestamp}.jsonl
# - Also creates text log: logs/unshackle_root_{timestamp}.log
debug_keys: false # Log decryption keys in debug logs (default: false) debug_keys:
# Set to true to include actual decryption keys in logs false # Log decryption keys in debug logs (default: false)
# Useful for debugging key retrieval and decryption issues # Set to true to include actual decryption keys in logs
# SECURITY NOTE: Passwords, tokens, cookies, and session tokens # Useful for debugging key retrieval and decryption issues
# are ALWAYS redacted regardless of this setting # SECURITY NOTE: Passwords, tokens, cookies, and session tokens
# Only affects: content_key, key fields (the actual CEKs) # are ALWAYS redacted regardless of this setting
# Never affects: kid, keys_count, key_id (metadata is always logged) # Only affects: content_key, key fields (the actual CEKs)
# Never affects: kid, keys_count, key_id (metadata is always logged)
# Muxing configuration # Muxing configuration
muxing: muxing:
@@ -128,72 +130,72 @@ cdm:
# Use pywidevine Serve-compliant Remote CDMs # Use pywidevine Serve-compliant Remote CDMs
# Example: Custom CDM API Configuration # Example: Custom CDM API Configuration
# This demonstrates the highly configurable custom_api type that can adapt to any CDM API format # This demonstrates the highly configurable custom_api type that can adapt to any CDM API format
# - name: "chrome" # - name: "chrome"
# type: "custom_api" # type: "custom_api"
# host: "http://remotecdm.test/" # host: "http://remotecdm.test/"
# timeout: 30 # timeout: 30
# device: # device:
# name: "ChromeCDM" # name: "ChromeCDM"
# type: "CHROME" # type: "CHROME"
# system_id: 34312 # system_id: 34312
# security_level: 3 # security_level: 3
# auth: # auth:
# type: "header" # type: "header"
# header_name: "x-api-key" # header_name: "x-api-key"
# key: "YOUR_API_KEY_HERE" # key: "YOUR_API_KEY_HERE"
# custom_headers: # custom_headers:
# User-Agent: "Unshackle/2.0.0" # User-Agent: "Unshackle/2.0.0"
# endpoints: # endpoints:
# get_request: # get_request:
# path: "/get-challenge" # path: "/get-challenge"
# method: "POST" # method: "POST"
# timeout: 30 # timeout: 30
# decrypt_response: # decrypt_response:
# path: "/get-keys" # path: "/get-keys"
# method: "POST" # method: "POST"
# timeout: 30 # timeout: 30
# request_mapping: # request_mapping:
# get_request: # get_request:
# param_names: # param_names:
# scheme: "device" # scheme: "device"
# init_data: "init_data" # init_data: "init_data"
# static_params: # static_params:
# scheme: "Widevine" # scheme: "Widevine"
# decrypt_response: # decrypt_response:
# param_names: # param_names:
# scheme: "device" # scheme: "device"
# license_request: "license_request" # license_request: "license_request"
# license_response: "license_response" # license_response: "license_response"
# static_params: # static_params:
# scheme: "Widevine" # scheme: "Widevine"
# response_mapping: # response_mapping:
# get_request: # get_request:
# fields: # fields:
# challenge: "challenge" # challenge: "challenge"
# session_id: "session_id" # session_id: "session_id"
# message: "message" # message: "message"
# message_type: "message_type" # message_type: "message_type"
# response_types: # response_types:
# - condition: "message_type == 'license-request'" # - condition: "message_type == 'license-request'"
# type: "license_request" # type: "license_request"
# success_conditions: # success_conditions:
# - "message == 'success'" # - "message == 'success'"
# decrypt_response: # decrypt_response:
# fields: # fields:
# keys: "keys" # keys: "keys"
# message: "message" # message: "message"
# key_fields: # key_fields:
# kid: "kid" # kid: "kid"
# key: "key" # key: "key"
# type: "type" # type: "type"
# success_conditions: # success_conditions:
# - "message == 'success'" # - "message == 'success'"
# caching: # caching:
# enabled: true # enabled: true
# use_vaults: true # use_vaults: true
# check_cached_first: true # check_cached_first: true
remote_cdm: remote_cdm:
- name: "chrome" - name: "chrome"
@@ -360,9 +362,13 @@ services:
# Service-specific configuration goes here # Service-specific configuration goes here
# Profile-specific configurations can be nested under service names # Profile-specific configurations can be nested under service names
# Example: with profile-specific device configs # You can override ANY global configuration option on a per-service basis
# This allows fine-tuned control for services with special requirements
# Supported overrides: dl, aria2c, n_m3u8dl_re, curl_impersonate, subtitle, muxing, headers, etc.
# Example: Comprehensive service configuration showing all features
EXAMPLE: EXAMPLE:
# Global service config # Standard service config
api_key: "service_api_key" api_key: "service_api_key"
# Service certificate for Widevine L1/L2 (base64 encoded) # Service certificate for Widevine L1/L2 (base64 encoded)
@@ -383,6 +389,42 @@ services:
app_name: "AIV" app_name: "AIV"
device_model: "Fire TV Stick 4K" device_model: "Fire TV Stick 4K"
# NEW: Configuration overrides (can be combined with profiles and certificates)
# Override dl command defaults for this service
dl:
downloads: 4 # Limit concurrent track downloads (global default: 6)
workers: 8 # Reduce workers per track (global default: 16)
lang: ["en", "es-419"] # Different language priority for this service
sub_format: srt # Force SRT subtitle format
# Override n_m3u8dl_re downloader settings
n_m3u8dl_re:
thread_count: 8 # Lower thread count for rate-limited service (global default: 16)
use_proxy: true # Force proxy usage for this service
retry_count: 10 # More retries for unstable connections
ad_keyword: "advertisement" # Service-specific ad filtering
# Override aria2c downloader settings
aria2c:
max_concurrent_downloads: 2 # Limit concurrent downloads (global default: 4)
max_connection_per_server: 1 # Single connection per server
split: 3 # Fewer splits (global default: 5)
file_allocation: none # Faster allocation for this service
# Override subtitle processing for this service
subtitle:
conversion_method: pycaption # Use specific subtitle converter
sdh_method: auto
# Service-specific headers
headers:
User-Agent: "Service-specific user agent string"
Accept-Language: "en-US,en;q=0.9"
# Override muxing options
muxing:
set_title: true
# Example: Service with different regions per profile # Example: Service with different regions per profile
SERVICE_NAME: SERVICE_NAME:
profiles: profiles:
@@ -393,6 +435,13 @@ services:
region: "GB" region: "GB"
api_endpoint: "https://api.uk.service.com" api_endpoint: "https://api.uk.service.com"
# Notes on service-specific overrides:
# - Overrides are merged with global config, not replaced
# - Only specified keys are overridden, others use global defaults
# - 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.)
# - Use --debug flag to see which overrides are applied during downloads
# External proxy provider services # External proxy provider services
proxy_providers: proxy_providers:
nordvpn: nordvpn: