| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697 |
- import logging
- import time
- from app.config import settings
- from app.service.task.base_task import BaseTask
- from app.service.task.task_param import TaskParam
- from app.service.task.task_result import TaskResult
- from app.util.http_util import logged_client
- from app.util.retry_util import retry_on_failure
- logger = logging.getLogger(__name__)
- class SeedanceVideoTask(BaseTask):
- def get_type(self) -> str:
- return "seedance_video"
- def _headers(self) -> dict[str, str]:
- return {
- "Content-Type": "application/json",
- "Authorization": f"Bearer {settings.seed_api_key}",
- }
- def _submit(self, url: str, inputs: dict) -> dict:
- with logged_client(timeout=60) as client:
- resp = client.post(url, headers=self._headers(), json=inputs)
- logger.info("[seedance_video] Submit response: status=%d", resp.status_code)
- resp.raise_for_status()
- data = resp.json()
- task_id = data.get("id")
- if not task_id:
- raise RuntimeError(f"No task ID in response: {data}")
- return data
- def execute(self, param: TaskParam) -> TaskResult:
- execute_url = settings.seedance_video_url
- t0 = time.time()
- logger.info("[seedance_video] Submitting task: POST %s", execute_url)
- try:
- data = retry_on_failure(
- lambda: self._submit(execute_url, param.inputs),
- task_name="seedance_video",
- )
- task_id = data.get("id")
- logger.info("[seedance_video] Task submitted, remote_id=%s", task_id)
- except Exception as e:
- logger.error("[seedance_video] Submit failed: %s", e)
- return TaskResult.failure(f"Failed to submit task: {e}")
- result = self._poll(task_id)
- if result is None:
- return TaskResult.failure("Failed to poll task status")
- if result.get("status") != "succeeded":
- return TaskResult.failure(f"Task failed with status: {result.get('status')}")
- videos: list[str] = []
- content = result.get("content", {})
- video_url = content.get("video_url")
- if video_url:
- videos.append(video_url)
- logger.info("[seedance_video] Completed: %d videos, total_elapsed=%.2fs",
- len(videos), time.time() - t0)
- return TaskResult.success({"videos": videos})
- def _poll(self, task_id: str) -> dict | None:
- search_url = f"{settings.seedance_video_url}/{task_id}"
- max_attempts = 30
- poll_t0 = time.time()
- for attempt in range(1, max_attempts + 1):
- try:
- with logged_client(timeout=60) as client:
- resp = client.get(search_url, headers=self._headers())
- data = resp.json()
- status = data.get("status")
- logger.info("[seedance_video] Poll #%d/%d: remote_id=%s, status=%s, elapsed=%.1fs",
- attempt, max_attempts, task_id, status, time.time() - poll_t0)
- if status == "succeeded":
- return data
- if status in ("failed", "cancelled"):
- logger.error("[seedance_video] Task %s terminated with status=%s", task_id, status)
- return None
- if attempt < max_attempts:
- time.sleep(2)
- except Exception as e:
- logger.error("[seedance_video] Poll #%d error for %s: %s", attempt, task_id, e)
- if attempt < max_attempts:
- time.sleep(2)
- logger.warning("[seedance_video] Max polling attempts (%d) reached for %s", max_attempts, task_id)
- return None
|