diff --git a/docs/dev/rest-api/endpoints.md b/docs/dev/rest-api/endpoints.md index 1f04afd9..10963597 100644 --- a/docs/dev/rest-api/endpoints.md +++ b/docs/dev/rest-api/endpoints.md @@ -397,6 +397,8 @@ Create a download job. Requires `service` and `title_id`; every other field is a | `proxy` / `no_proxy` / `no_proxy_download` | string / bool / bool | `null` / `false` / `false` | Proxy controls. | | `no_folder` / `no_source` / `no_mux` | boolean | `false` | Output/mux controls. | | `workers` | int | `null` | Threads per track. | +| `adaptive_workers` | boolean | `false` | Scale segment workers to measured CDN throughput, up to the `workers` cap. | +| `download_processes` | int | `1` | Split a track's segments across this many processes. Only engages for large batches. | | `downloads` | int | `1` | Concurrent tracks. | | `worst` | boolean | `false` | Lowest bitrate within quality. | | `best_available` | boolean | `false` | Fall back to best available. | diff --git a/unshackle/core/api/download_manager.py b/unshackle/core/api/download_manager.py index 147133fa..5a614ad2 100644 --- a/unshackle/core/api/download_manager.py +++ b/unshackle/core/api/download_manager.py @@ -586,6 +586,8 @@ def perform_download( no_source=params.get("no_source", False), no_mux=params.get("no_mux", False), workers=params.get("workers"), + adaptive_workers=params.get("adaptive_workers", False), + download_processes=params.get("download_processes", 1), downloads=params.get("downloads", 1), worst=params.get("worst", False), best_available=params.get("best_available", False), diff --git a/unshackle/core/api/handlers.py b/unshackle/core/api/handlers.py index 771c2bc6..9e2db1c2 100644 --- a/unshackle/core/api/handlers.py +++ b/unshackle/core/api/handlers.py @@ -70,6 +70,8 @@ DEFAULT_DOWNLOAD_PARAMS = { "no_source": False, "no_mux": False, "workers": None, + "adaptive_workers": False, + "download_processes": 1, "downloads": 1, "worst": False, "best_available": False, @@ -984,6 +986,10 @@ def validate_download_parameters(data: Dict[str, Any]) -> Optional[str]: if not isinstance(data["workers"], int) or data["workers"] <= 0: return "workers must be a positive integer" + if "download_processes" in data and data["download_processes"] is not None: + if not isinstance(data["download_processes"], int) or data["download_processes"] <= 0: + return "download_processes must be a positive integer" + if "downloads" in data and data["downloads"] is not None: if not isinstance(data["downloads"], int) or data["downloads"] <= 0: return "downloads must be a positive integer" diff --git a/unshackle/core/api/routes.py b/unshackle/core/api/routes.py index 9a841931..9a2606a3 100644 --- a/unshackle/core/api/routes.py +++ b/unshackle/core/api/routes.py @@ -781,6 +781,12 @@ async def download(request: web.Request) -> web.Response: workers: type: integer description: Max workers/threads per track download (default - None) + adaptive_workers: + type: boolean + description: Scale per-track segment workers to measured CDN throughput, up to the workers cap (default - false) + download_processes: + type: integer + description: Split a track's segments across this many processes; only engages for large batches (default - 1) downloads: type: integer description: Amount of tracks to download concurrently (default - 1)