# -*- coding: utf-8 -*- """썰박스 산출물 Blob 업로드. castad `AzureBlobUploader` 를 그대로 재사용한다 — 원본 썰박스의 `blob_client.py` 는 이식하지 않는다. 같은 Azure 계정을 쓰므로 업로더를 두 벌 둘 이유가 없다. 경로는 `{user_uuid}/{task_id}/video/{file}` 형태가 되는데, ADO2 콘텐츠와 섞이지 않도록 task_id 자리에 `ssulbox-{id}` 접두를 붙인다. """ from pathlib import Path from typing import Optional from app.utils.logger import get_logger from app.utils.upload_blob_as_request import AzureBlobUploader from config import azure_blob_settings, ssulbox_settings logger = get_logger("ssulbox") #: 설정되지 않았을 때의 플레이스홀더 (config.py 기본값) _PLACEHOLDER_SAS = {"", "your-sas-token", "none"} def blob_enabled() -> bool: """Blob 업로드가 가능한 상태인지. 비활성이면 로컬 파일을 그대로 서빙한다(개발 환경). """ token = (azure_blob_settings.AZURE_BLOB_SAS_TOKEN or "").strip() return token.lower() not in _PLACEHOLDER_SAS async def upload_ssul_video( mp4_path: Path, user_uuid: Optional[str], content_id: int ) -> Optional[str]: """완성 영상을 Blob 에 올리고 공개 URL 을 반환. 실패·비활성이면 None. Args: mp4_path: 로컬 mp4 경로 user_uuid: 소유자. 탈퇴로 NULL 이면 업로드하지 않는다 content_id: `ssul_content.id` Returns: SAS 토큰이 제외된 공개 URL, 또는 None """ if not blob_enabled(): logger.info("[upload_ssul_video] Blob 비활성 — 로컬 서빙") return None if not user_uuid: logger.warning(f"[upload_ssul_video] user_uuid 없음 id={content_id}") return None if not mp4_path.exists(): logger.error(f"[upload_ssul_video] 파일 없음 {mp4_path}") return None # ADO2 영상과 경로를 분리한다 task_id = f"{ssulbox_settings.SSULBOX_BLOB_PREFIX}-{content_id}" uploader = AzureBlobUploader(user_uuid=user_uuid, task_id=task_id) success = await uploader.upload_video(file_path=str(mp4_path)) if not success: logger.error(f"[upload_ssul_video] 업로드 실패 id={content_id}") return None logger.info(f"[upload_ssul_video] OK id={content_id} url={uploader.public_url}") return uploader.public_url