| 1 | from typing import Any, Dict |
| 2 | |
| 3 | from core.downloader_base import BaseDownloader, DownloadResult |
| 4 | from utils.logger import setup_logger |
| 5 | |
| 6 | logger = setup_logger("VideoDownloader") |
| 7 | |
| 8 | |
| 9 | class VideoDownloader(BaseDownloader): |
| 10 | async def download(self, parsed_url: Dict[str, Any]) -> DownloadResult: |
| 11 | result = DownloadResult() |
| 12 | |
| 13 | aweme_id = parsed_url.get("aweme_id") |
| 14 | if not aweme_id: |
| 15 | logger.error("No aweme_id found in parsed URL") |
| 16 | return result |
| 17 | |
| 18 | result.total = 1 |
| 19 | self._progress_set_item_total(1, "单作品下载") |
| 20 | self._progress_update_step("下载作品", "单作品资源下载中") |
| 21 | |
| 22 | if not await self._should_download(aweme_id): |
| 23 | logger.info("Video %s already downloaded, skipping", aweme_id) |
| 24 | result.skipped += 1 |
| 25 | self._progress_advance_item("skipped", str(aweme_id)) |
| 26 | return result |
| 27 | |
| 28 | await self.rate_limiter.acquire() |
| 29 | |
| 30 | aweme_data = await self.api_client.get_video_detail(aweme_id) |
| 31 | if not aweme_data: |
| 32 | logger.error("Failed to get video detail: %s", aweme_id) |
| 33 | result.failed += 1 |
| 34 | self._progress_advance_item("failed", str(aweme_id)) |
| 35 | return result |
| 36 | |
| 37 | success = await self._download_aweme(aweme_data) |
| 38 | if success: |
| 39 | result.success += 1 |
| 40 | self._progress_advance_item("success", str(aweme_id)) |
| 41 | else: |
| 42 | result.failed += 1 |
| 43 | self._progress_advance_item("failed", str(aweme_id)) |
| 44 | |
| 45 | return result |
| 46 | |
| 47 | async def _download_aweme(self, aweme_data: Dict[str, Any]) -> bool: |
| 48 | author = aweme_data.get("author", {}) or {} |
| 49 | author_name = author.get("nickname", "unknown") |
| 50 | # Cache author on the hosting job so JobRow can display the nickname |
| 51 | # and `retry_failed_awemes` doesn't need to re-fetch user info. |
| 52 | self._progress_report_author( |
| 53 | nickname=author_name if author_name != "unknown" else None, |
| 54 | sec_uid=author.get("sec_uid"), |
| 55 | ) |
| 56 | return await self._download_aweme_assets(aweme_data, author_name) |
| 57 |