243 lines
10 KiB
Python
243 lines
10 KiB
Python
"""
|
|
Video Background Tasks
|
|
|
|
영상 생성 관련 백그라운드 태스크를 정의합니다.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
|
|
import aiofiles
|
|
import httpx
|
|
from sqlalchemy import select
|
|
|
|
from app.database.session import AsyncSessionLocal
|
|
from app.video.models import Video
|
|
from app.utils.upload_blob_as_request import AzureBlobUploader
|
|
|
|
|
|
async def download_and_upload_video_to_blob(
|
|
task_id: str,
|
|
video_url: str,
|
|
store_name: str,
|
|
) -> None:
|
|
"""백그라운드에서 영상을 다운로드하고 Azure Blob Storage에 업로드한 뒤 Video 테이블을 업데이트합니다.
|
|
|
|
Args:
|
|
task_id: 프로젝트 task_id
|
|
video_url: 다운로드할 영상 URL
|
|
store_name: 저장할 파일명에 사용할 업체명
|
|
"""
|
|
print(f"[download_and_upload_video_to_blob] START - task_id: {task_id}, store_name: {store_name}")
|
|
temp_file_path: Path | None = None
|
|
|
|
try:
|
|
# 파일명에 사용할 수 없는 문자 제거
|
|
safe_store_name = "".join(
|
|
c for c in store_name if c.isalnum() or c in (" ", "_", "-")
|
|
).strip()
|
|
safe_store_name = safe_store_name or "video"
|
|
file_name = f"{safe_store_name}.mp4"
|
|
|
|
# 임시 저장 경로 생성
|
|
temp_dir = Path("media") / "temp" / task_id
|
|
temp_dir.mkdir(parents=True, exist_ok=True)
|
|
temp_file_path = temp_dir / file_name
|
|
print(f"[download_and_upload_video_to_blob] Temp directory created - path: {temp_file_path}")
|
|
|
|
# 영상 파일 다운로드
|
|
print(f"[download_and_upload_video_to_blob] Downloading video - task_id: {task_id}, url: {video_url}")
|
|
async with httpx.AsyncClient() as client:
|
|
response = await client.get(video_url, timeout=180.0)
|
|
response.raise_for_status()
|
|
|
|
async with aiofiles.open(str(temp_file_path), "wb") as f:
|
|
await f.write(response.content)
|
|
print(f"[download_and_upload_video_to_blob] File downloaded - task_id: {task_id}, path: {temp_file_path}")
|
|
|
|
# Azure Blob Storage에 업로드
|
|
uploader = AzureBlobUploader(task_id=task_id)
|
|
upload_success = await uploader.upload_video(file_path=str(temp_file_path))
|
|
|
|
if not upload_success:
|
|
raise Exception("Azure Blob Storage 업로드 실패")
|
|
|
|
# SAS 토큰이 제외된 public_url 사용
|
|
blob_url = uploader.public_url
|
|
print(f"[download_and_upload_video_to_blob] Uploaded to Blob - task_id: {task_id}, url: {blob_url}")
|
|
|
|
# Video 테이블 업데이트 (새 세션 사용)
|
|
async with AsyncSessionLocal() as session:
|
|
# 여러 개 있을 경우 가장 최근 것 선택
|
|
result = await session.execute(
|
|
select(Video)
|
|
.where(Video.task_id == task_id)
|
|
.order_by(Video.created_at.desc())
|
|
.limit(1)
|
|
)
|
|
video = result.scalar_one_or_none()
|
|
|
|
if video:
|
|
video.status = "completed"
|
|
video.result_movie_url = blob_url
|
|
await session.commit()
|
|
print(f"[download_and_upload_video_to_blob] SUCCESS - task_id: {task_id}, status: completed")
|
|
else:
|
|
print(f"[download_and_upload_video_to_blob] Video NOT FOUND in DB - task_id: {task_id}")
|
|
|
|
except Exception as e:
|
|
print(f"[download_and_upload_video_to_blob] EXCEPTION - task_id: {task_id}, error: {e}")
|
|
# 실패 시 Video 테이블 업데이트
|
|
async with AsyncSessionLocal() as session:
|
|
result = await session.execute(
|
|
select(Video)
|
|
.where(Video.task_id == task_id)
|
|
.order_by(Video.created_at.desc())
|
|
.limit(1)
|
|
)
|
|
video = result.scalar_one_or_none()
|
|
|
|
if video:
|
|
video.status = "failed"
|
|
await session.commit()
|
|
print(f"[download_and_upload_video_to_blob] FAILED - task_id: {task_id}, status updated to failed")
|
|
|
|
finally:
|
|
# 임시 파일 삭제
|
|
if temp_file_path and temp_file_path.exists():
|
|
try:
|
|
temp_file_path.unlink()
|
|
print(f"[download_and_upload_video_to_blob] Temp file deleted - path: {temp_file_path}")
|
|
except Exception as e:
|
|
print(f"[download_and_upload_video_to_blob] Failed to delete temp file: {e}")
|
|
|
|
# 임시 디렉토리 삭제 시도
|
|
temp_dir = Path("media") / "temp" / task_id
|
|
if temp_dir.exists():
|
|
try:
|
|
temp_dir.rmdir()
|
|
except Exception:
|
|
pass # 디렉토리가 비어있지 않으면 무시
|
|
|
|
|
|
async def download_and_upload_video_by_creatomate_render_id(
|
|
creatomate_render_id: str,
|
|
video_url: str,
|
|
store_name: str,
|
|
) -> None:
|
|
"""creatomate_render_id로 Video를 조회하여 영상을 다운로드하고 Azure Blob Storage에 업로드한 뒤 Video 테이블을 업데이트합니다.
|
|
|
|
Args:
|
|
creatomate_render_id: Creatomate API 렌더 ID
|
|
video_url: 다운로드할 영상 URL
|
|
store_name: 저장할 파일명에 사용할 업체명
|
|
"""
|
|
print(f"[download_and_upload_video_by_creatomate_render_id] START - creatomate_render_id: {creatomate_render_id}, store_name: {store_name}")
|
|
temp_file_path: Path | None = None
|
|
task_id: str | None = None
|
|
|
|
try:
|
|
# creatomate_render_id로 Video 조회하여 task_id 가져오기
|
|
async with AsyncSessionLocal() as session:
|
|
result = await session.execute(
|
|
select(Video)
|
|
.where(Video.creatomate_render_id == creatomate_render_id)
|
|
.order_by(Video.created_at.desc())
|
|
.limit(1)
|
|
)
|
|
video = result.scalar_one_or_none()
|
|
|
|
if not video:
|
|
print(f"[download_and_upload_video_by_creatomate_render_id] Video NOT FOUND - creatomate_render_id: {creatomate_render_id}")
|
|
return
|
|
|
|
task_id = video.task_id
|
|
print(f"[download_and_upload_video_by_creatomate_render_id] Video found - creatomate_render_id: {creatomate_render_id}, task_id: {task_id}")
|
|
|
|
# 파일명에 사용할 수 없는 문자 제거
|
|
safe_store_name = "".join(
|
|
c for c in store_name if c.isalnum() or c in (" ", "_", "-")
|
|
).strip()
|
|
safe_store_name = safe_store_name or "video"
|
|
file_name = f"{safe_store_name}.mp4"
|
|
|
|
# 임시 저장 경로 생성
|
|
temp_dir = Path("media") / "temp" / task_id
|
|
temp_dir.mkdir(parents=True, exist_ok=True)
|
|
temp_file_path = temp_dir / file_name
|
|
print(f"[download_and_upload_video_by_creatomate_render_id] Temp directory created - path: {temp_file_path}")
|
|
|
|
# 영상 파일 다운로드
|
|
print(f"[download_and_upload_video_by_creatomate_render_id] Downloading video - creatomate_render_id: {creatomate_render_id}, url: {video_url}")
|
|
async with httpx.AsyncClient() as client:
|
|
response = await client.get(video_url, timeout=180.0)
|
|
response.raise_for_status()
|
|
|
|
async with aiofiles.open(str(temp_file_path), "wb") as f:
|
|
await f.write(response.content)
|
|
print(f"[download_and_upload_video_by_creatomate_render_id] File downloaded - creatomate_render_id: {creatomate_render_id}, path: {temp_file_path}")
|
|
|
|
# Azure Blob Storage에 업로드
|
|
uploader = AzureBlobUploader(task_id=task_id)
|
|
upload_success = await uploader.upload_video(file_path=str(temp_file_path))
|
|
|
|
if not upload_success:
|
|
raise Exception("Azure Blob Storage 업로드 실패")
|
|
|
|
# SAS 토큰이 제외된 public_url 사용
|
|
blob_url = uploader.public_url
|
|
print(f"[download_and_upload_video_by_creatomate_render_id] Uploaded to Blob - creatomate_render_id: {creatomate_render_id}, url: {blob_url}")
|
|
|
|
# Video 테이블 업데이트 (새 세션 사용)
|
|
async with AsyncSessionLocal() as session:
|
|
result = await session.execute(
|
|
select(Video)
|
|
.where(Video.creatomate_render_id == creatomate_render_id)
|
|
.order_by(Video.created_at.desc())
|
|
.limit(1)
|
|
)
|
|
video = result.scalar_one_or_none()
|
|
|
|
if video:
|
|
video.status = "completed"
|
|
video.result_movie_url = blob_url
|
|
await session.commit()
|
|
print(f"[download_and_upload_video_by_creatomate_render_id] SUCCESS - creatomate_render_id: {creatomate_render_id}, status: completed")
|
|
else:
|
|
print(f"[download_and_upload_video_by_creatomate_render_id] Video NOT FOUND in DB - creatomate_render_id: {creatomate_render_id}")
|
|
|
|
except Exception as e:
|
|
print(f"[download_and_upload_video_by_creatomate_render_id] EXCEPTION - creatomate_render_id: {creatomate_render_id}, error: {e}")
|
|
# 실패 시 Video 테이블 업데이트
|
|
if task_id:
|
|
async with AsyncSessionLocal() as session:
|
|
result = await session.execute(
|
|
select(Video)
|
|
.where(Video.creatomate_render_id == creatomate_render_id)
|
|
.order_by(Video.created_at.desc())
|
|
.limit(1)
|
|
)
|
|
video = result.scalar_one_or_none()
|
|
|
|
if video:
|
|
video.status = "failed"
|
|
await session.commit()
|
|
print(f"[download_and_upload_video_by_creatomate_render_id] FAILED - creatomate_render_id: {creatomate_render_id}, status updated to failed")
|
|
|
|
finally:
|
|
# 임시 파일 삭제
|
|
if temp_file_path and temp_file_path.exists():
|
|
try:
|
|
temp_file_path.unlink()
|
|
print(f"[download_and_upload_video_by_creatomate_render_id] Temp file deleted - path: {temp_file_path}")
|
|
except Exception as e:
|
|
print(f"[download_and_upload_video_by_creatomate_render_id] Failed to delete temp file: {e}")
|
|
|
|
# 임시 디렉토리 삭제 시도
|
|
if task_id:
|
|
temp_dir = Path("media") / "temp" / task_id
|
|
if temp_dir.exists():
|
|
try:
|
|
temp_dir.rmdir()
|
|
except Exception:
|
|
pass # 디렉토리가 비어있지 않으면 무시
|