""" YouTube Publish Router Handles video upload/publishing to YouTube via the Data API v3. Uses stored OAuth credentials for authentication. """ from typing import Optional, List from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks, Query from pydantic import BaseModel, Field from loguru import logger from middleware.auth_middleware import get_current_user from services.youtube.youtube_oauth_service import YouTubeOAuthService from services.youtube.youtube_publish_service import YouTubePublishService from .oauth_router import get_oauth_service from .task_manager import task_manager router = APIRouter(prefix="/youtube/publish", tags=["youtube-publish"]) class PublishRequest(BaseModel): token_id: int = Field(..., description="YouTube OAuth token row ID (which channel to publish to)") video_source: str = Field(..., description="URL or local file path to the video") title: str = Field(..., min_length=1, max_length=100, description="Video title (max 100 chars)") description: str = Field("", description="Video description") tags: List[str] = Field(default_factory=list, description="Video tags") privacy_status: str = Field("unlisted", pattern="^(public|private|unlisted)$", description="Privacy status") category_id: str = Field("22", description="YouTube category ID (default: People & Blogs)") made_for_kids: bool = Field(False, description="Whether content is made for children") class PublishResponse(BaseModel): success: bool task_id: Optional[str] = None video_id: Optional[str] = None video_url: Optional[str] = None error: Optional[str] = None message: str = "" def get_publish_service( oauth_service: YouTubeOAuthService = Depends(get_oauth_service), ) -> YouTubePublishService: return YouTubePublishService(oauth_service) @router.post("", response_model=PublishResponse) def start_publish( request: PublishRequest, background_tasks: BackgroundTasks, user: dict = Depends(get_current_user), publish_service: YouTubePublishService = Depends(get_publish_service), ): """Start publishing a video to YouTube as a background task.""" try: user_id = user.get("id") if not user_id: raise HTTPException(status_code=401, detail="Authentication required") # Verify token belongs to user oauth_service = publish_service.oauth_service status = oauth_service.get_connection_status(user_id) tokens = [c for c in status.get("channels", []) if c["token_id"] == request.token_id and c["is_active"]] if not tokens: raise HTTPException(status_code=400, detail="Invalid or inactive token_id") # Create background task task_id = task_manager.create_task("youtube_publish") logger.info( f"YouTube publish: created task {task_id} for user {user_id}, " f"title='{request.title[:50]}', channel={tokens[0].get('channel_name', 'unknown')}" ) background_tasks.add_task( _execute_publish_task, task_id=task_id, user_id=user_id, token_id=request.token_id, video_source=request.video_source, title=request.title, description=request.description, tags=request.tags, privacy_status=request.privacy_status, category_id=request.category_id, made_for_kids=request.made_for_kids, publish_service=publish_service, ) return PublishResponse( success=True, task_id=task_id, message="Publishing to YouTube started. Poll task_id for progress.", ) except HTTPException: raise except Exception as e: logger.error(f"YouTube publish: error starting task: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.get("/{task_id}", response_model=PublishResponse) def get_publish_status( task_id: str, user: dict = Depends(get_current_user), ): """Check the status of a YouTube publish task.""" try: user_id = user.get("id") if not user_id: raise HTTPException(status_code=401, detail="Authentication required") task_status = task_manager.get_task_status(task_id) if not task_status: return PublishResponse( success=False, error="Task not found", message="Publish task not found (may have expired).", ) status = task_status.get("status", "unknown") result = task_status.get("result") or {} error = task_status.get("error") if status == "completed": return PublishResponse( success=True, task_id=task_id, video_id=result.get("video_id"), video_url=result.get("video_url"), message=task_status.get("message", "Published successfully"), ) elif status == "failed": return PublishResponse( success=False, task_id=task_id, error=error or result.get("error", "Publish failed"), message=task_status.get("message", "Publish failed"), ) else: return PublishResponse( success=False, task_id=task_id, message=task_status.get("message", "Publishing in progress..."), ) except HTTPException: raise except Exception as e: logger.error(f"YouTube publish: status check error: {e}") raise HTTPException(status_code=500, detail=str(e)) def _execute_publish_task( task_id: str, user_id: str, token_id: int, video_source: str, title: str, description: str, tags: List[str], privacy_status: str, category_id: str, made_for_kids: bool, publish_service: YouTubePublishService, ): """Background task to execute video publish.""" logger.info(f"YouTube publish: background task {task_id} starting for user {user_id}") try: task_manager.update_task_status( task_id, "processing", progress=10.0, message="Preparing video for upload..." ) result = publish_service.publish_video( user_id=user_id, token_id=token_id, video_source=video_source, title=title, description=description, tags=tags, privacy_status=privacy_status, category_id=category_id, made_for_kids=made_for_kids, ) if result.get("success"): task_manager.update_task_status( task_id, "completed", progress=100.0, message=f"Published successfully: {result.get('video_url', '')}", result=result, ) logger.info( f"YouTube publish: task {task_id} completed — " f"video_id={result.get('video_id')}, url={result.get('video_url')}" ) else: error_msg = result.get("error", "Unknown publish error") logger.error(f"YouTube publish: task {task_id} failed: {error_msg}") task_manager.update_task_status( task_id, "failed", error=error_msg, message="Publish failed", result=result, ) except Exception as e: logger.error(f"YouTube publish: background task {task_id} error: {e}") task_manager.update_task_status( task_id, "failed", error=str(e), message="Publish error", result={"error": str(e)}, )