# 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, }