forked from Mike/unshackle
103 lines
3.2 KiB
Python
103 lines
3.2 KiB
Python
"""Standalone worker process entry point for executing download jobs."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import logging
|
|
import sys
|
|
import traceback
|
|
from pathlib import Path
|
|
from typing import Any, Dict
|
|
|
|
from .download_manager import _perform_download
|
|
|
|
log = logging.getLogger("download_worker")
|
|
|
|
|
|
def _read_payload(path: Path) -> Dict[str, Any]:
|
|
with path.open("r", encoding="utf-8") as handle:
|
|
return json.load(handle)
|
|
|
|
|
|
def _write_result(path: Path, payload: Dict[str, Any]) -> None:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
with path.open("w", encoding="utf-8") as handle:
|
|
json.dump(payload, handle)
|
|
|
|
|
|
def main(argv: list[str]) -> int:
|
|
if len(argv) not in [3, 4]:
|
|
print(
|
|
"Usage: python -m unshackle.core.api.download_worker <payload_path> <result_path> [progress_path]",
|
|
file=sys.stderr,
|
|
)
|
|
return 2
|
|
|
|
payload_path = Path(argv[1])
|
|
result_path = Path(argv[2])
|
|
progress_path = Path(argv[3]) if len(argv) > 3 else None
|
|
|
|
result: Dict[str, Any] = {}
|
|
exit_code = 0
|
|
|
|
try:
|
|
payload = _read_payload(payload_path)
|
|
job_id = payload["job_id"]
|
|
service = payload["service"]
|
|
title_id = payload["title_id"]
|
|
params = payload.get("parameters", {})
|
|
|
|
log.info(f"Worker starting job {job_id} ({service}:{title_id})")
|
|
|
|
def progress_callback(progress_data: Dict[str, Any]) -> None:
|
|
"""Write progress updates to file for main process to read."""
|
|
if progress_path:
|
|
try:
|
|
log.info(f"Writing progress update: {progress_data}")
|
|
_write_result(progress_path, progress_data)
|
|
log.info(f"Progress update written to {progress_path}")
|
|
except Exception as e:
|
|
log.error(f"Failed to write progress update: {e}")
|
|
|
|
output_files = _perform_download(
|
|
job_id, service, title_id, params, cancel_event=None, progress_callback=progress_callback
|
|
)
|
|
|
|
result = {"status": "success", "output_files": output_files}
|
|
|
|
except Exception as exc: # noqa: BLE001 - capture for parent process
|
|
from unshackle.core.api.errors import categorize_exception
|
|
|
|
exit_code = 1
|
|
tb = traceback.format_exc()
|
|
log.error(f"Worker failed with error: {exc}")
|
|
|
|
api_error = categorize_exception(
|
|
exc,
|
|
context={
|
|
"service": payload.get("service") if "payload" in locals() else None,
|
|
"title_id": payload.get("title_id") if "payload" in locals() else None,
|
|
"job_id": payload.get("job_id") if "payload" in locals() else None,
|
|
},
|
|
)
|
|
|
|
result = {
|
|
"status": "error",
|
|
"message": str(exc),
|
|
"error_details": api_error.message,
|
|
"error_code": api_error.error_code.value,
|
|
"traceback": tb,
|
|
}
|
|
|
|
finally:
|
|
try:
|
|
_write_result(result_path, result)
|
|
except Exception as exc: # noqa: BLE001 - last resort logging
|
|
log.error(f"Failed to write worker result file: {exc}")
|
|
|
|
return exit_code
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv))
|