mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2025-10-23 15:11:08 +00:00
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:
@@ -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)
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user