Spaces:
Runtime error
Runtime error
| # FILE: bot/youtube/uploader.py | |
| # PATH: bot/youtube/uploader.py | |
| from __future__ import annotations | |
| import os | |
| from typing import Any, Awaitable, Callable, Dict, Optional | |
| from bot.youtube.metadata import build_metadata | |
| from bot.youtube.resume import start_resumable_session, upload_resumable | |
| ProgressCB = Callable[[int, int], Awaitable[None]] | |
| def _watch_url(video_id: str) -> str: | |
| vid = (video_id or "").strip() | |
| return f"https://youtu.be/{vid}" if vid else "" | |
| async def upload_video( | |
| *, | |
| access_token: str, | |
| file_path: str, | |
| title: str, | |
| description: str, | |
| privacy: str = "private", | |
| progress_cb: Optional[ProgressCB] = None, | |
| ) -> Dict[str, Any]: | |
| """Upload a video to YouTube via resumable upload. | |
| Returns: | |
| {"ok": True, "video_id": "...", "url": "...", "response": {...}} | |
| {"ok": False, "err": "...", "detail": "..."} | |
| """ | |
| if not access_token: | |
| return {"ok": False, "err": "missing_access_token"} | |
| if not file_path or not os.path.exists(file_path): | |
| return {"ok": False, "err": "file_not_found"} | |
| try: | |
| total_bytes = int(os.path.getsize(file_path)) | |
| except Exception: | |
| total_bytes = 0 | |
| metadata = build_metadata(title=title, description=description, privacy=privacy) | |
| # 1) Start session | |
| start = await start_resumable_session( | |
| access_token=access_token, | |
| metadata=metadata, | |
| total_bytes=total_bytes, | |
| ) | |
| if not (isinstance(start, dict) and start.get("ok") and start.get("session_url")): | |
| err = start.get("err", "start_failed") if isinstance(start, dict) else "start_failed" | |
| detail = start.get("detail") if isinstance(start, dict) else None | |
| out: Dict[str, Any] = {"ok": False, "err": err} | |
| if detail: | |
| out["detail"] = str(detail)[:700] | |
| return out | |
| session_url = str(start["session_url"]) | |
| # 2) Upload chunks | |
| up = await upload_resumable( | |
| session_url=session_url, | |
| file_path=file_path, | |
| access_token=access_token, | |
| total_bytes=total_bytes, | |
| progress_cb=progress_cb, | |
| ) | |
| if not (isinstance(up, dict) and up.get("ok")): | |
| err = up.get("err", "upload_failed") if isinstance(up, dict) else "upload_failed" | |
| detail = up.get("detail") if isinstance(up, dict) else None | |
| out2: Dict[str, Any] = {"ok": False, "err": err} | |
| if detail: | |
| out2["detail"] = str(detail)[:900] | |
| return out2 | |
| resp = up.get("response") if isinstance(up, dict) else None | |
| if not isinstance(resp, dict): | |
| resp = {} | |
| video_id = str(resp.get("id") or resp.get("videoId") or "").strip() | |
| url = _watch_url(video_id) | |
| return { | |
| "ok": True, | |
| "video_id": video_id, | |
| "url": url, | |
| "response": resp, | |
| } |