upload bug fix .
parent
e1386b891e
commit
e89709ce87
|
|
@ -304,11 +304,10 @@ def add_exception_handlers(app: FastAPI):
|
||||||
|
|
||||||
@app.exception_handler(status.HTTP_500_INTERNAL_SERVER_ERROR)
|
@app.exception_handler(status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
def internal_server_error_handler(request, exception):
|
def internal_server_error_handler(request, exception):
|
||||||
|
# 에러 메시지 로깅 (한글 포함 가능)
|
||||||
|
logger.error(f"Internal Server Error: {exception}")
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
content={"detail": "Something went wrong..."},
|
content={"detail": "Something went wrong..."},
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
headers={
|
|
||||||
"X-Error": f"{exception}",
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -112,36 +112,45 @@ async def upload_to_social(
|
||||||
)
|
)
|
||||||
raise SocialAccountNotFoundError()
|
raise SocialAccountNotFoundError()
|
||||||
|
|
||||||
# 3. 기존 업로드 확인 (동일 video + account 조합)
|
# 3. 진행 중인 업로드 확인 (pending 또는 uploading 상태만)
|
||||||
existing_result = await session.execute(
|
in_progress_result = await session.execute(
|
||||||
select(SocialUpload).where(
|
select(SocialUpload).where(
|
||||||
SocialUpload.video_id == body.video_id,
|
SocialUpload.video_id == body.video_id,
|
||||||
SocialUpload.social_account_id == account.id,
|
SocialUpload.social_account_id == account.id,
|
||||||
SocialUpload.status.in_(
|
SocialUpload.status.in_([UploadStatus.PENDING.value, UploadStatus.UPLOADING.value]),
|
||||||
[UploadStatus.PENDING.value, UploadStatus.UPLOADING.value]
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
existing_upload = existing_result.scalar_one_or_none()
|
in_progress_upload = in_progress_result.scalar_one_or_none()
|
||||||
|
|
||||||
if existing_upload:
|
if in_progress_upload:
|
||||||
logger.info(
|
logger.info(
|
||||||
f"[UPLOAD_API] 진행 중인 업로드 존재 - upload_id: {existing_upload.id}"
|
f"[UPLOAD_API] 진행 중인 업로드 존재 - upload_id: {in_progress_upload.id}"
|
||||||
)
|
)
|
||||||
return SocialUploadResponse(
|
return SocialUploadResponse(
|
||||||
success=True,
|
success=True,
|
||||||
upload_id=existing_upload.id,
|
upload_id=in_progress_upload.id,
|
||||||
platform=account.platform,
|
platform=account.platform,
|
||||||
status=existing_upload.status,
|
status=in_progress_upload.status,
|
||||||
message="이미 업로드가 진행 중입니다.",
|
message="이미 업로드가 진행 중입니다.",
|
||||||
)
|
)
|
||||||
|
|
||||||
# 4. 새 업로드 레코드 생성
|
# 4. 업로드 순번 계산 (동일 video + account 조합에서 최대 순번 + 1)
|
||||||
|
max_seq_result = await session.execute(
|
||||||
|
select(func.coalesce(func.max(SocialUpload.upload_seq), 0)).where(
|
||||||
|
SocialUpload.video_id == body.video_id,
|
||||||
|
SocialUpload.social_account_id == account.id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
max_seq = max_seq_result.scalar() or 0
|
||||||
|
next_seq = max_seq + 1
|
||||||
|
|
||||||
|
# 5. 새 업로드 레코드 생성 (항상 새로 생성하여 이력 보존)
|
||||||
social_upload = SocialUpload(
|
social_upload = SocialUpload(
|
||||||
user_uuid=current_user.user_uuid,
|
user_uuid=current_user.user_uuid,
|
||||||
video_id=body.video_id,
|
video_id=body.video_id,
|
||||||
social_account_id=account.id,
|
social_account_id=account.id,
|
||||||
platform=account.platform, # 계정의 플랫폼 정보 사용
|
upload_seq=next_seq,
|
||||||
|
platform=account.platform,
|
||||||
status=UploadStatus.PENDING.value,
|
status=UploadStatus.PENDING.value,
|
||||||
upload_progress=0,
|
upload_progress=0,
|
||||||
title=body.title,
|
title=body.title,
|
||||||
|
|
@ -161,10 +170,11 @@ async def upload_to_social(
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"[UPLOAD_API] 업로드 레코드 생성 - "
|
f"[UPLOAD_API] 업로드 레코드 생성 - "
|
||||||
f"upload_id: {social_upload.id}, video_id: {body.video_id}, platform: {account.platform}"
|
f"upload_id: {social_upload.id}, video_id: {body.video_id}, "
|
||||||
|
f"account_id: {account.id}, upload_seq: {next_seq}, platform: {account.platform}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 5. 백그라운드 태스크 등록
|
# 6. 백그라운드 태스크 등록
|
||||||
background_tasks.add_task(process_social_upload, social_upload.id)
|
background_tasks.add_task(process_social_upload, social_upload.id)
|
||||||
|
|
||||||
return SocialUploadResponse(
|
return SocialUploadResponse(
|
||||||
|
|
@ -211,6 +221,8 @@ async def get_upload_status(
|
||||||
return SocialUploadStatusResponse(
|
return SocialUploadStatusResponse(
|
||||||
upload_id=upload.id,
|
upload_id=upload.id,
|
||||||
video_id=upload.video_id,
|
video_id=upload.video_id,
|
||||||
|
social_account_id=upload.social_account_id,
|
||||||
|
upload_seq=upload.upload_seq,
|
||||||
platform=upload.platform,
|
platform=upload.platform,
|
||||||
status=UploadStatus(upload.status),
|
status=UploadStatus(upload.status),
|
||||||
upload_progress=upload.upload_progress,
|
upload_progress=upload.upload_progress,
|
||||||
|
|
@ -282,6 +294,8 @@ async def get_upload_history(
|
||||||
SocialUploadHistoryItem(
|
SocialUploadHistoryItem(
|
||||||
upload_id=upload.id,
|
upload_id=upload.id,
|
||||||
video_id=upload.video_id,
|
video_id=upload.video_id,
|
||||||
|
social_account_id=upload.social_account_id,
|
||||||
|
upload_seq=upload.upload_seq,
|
||||||
platform=upload.platform,
|
platform=upload.platform,
|
||||||
status=upload.status,
|
status=upload.status,
|
||||||
title=upload.title,
|
title=upload.title,
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ class SocialUpload(Base):
|
||||||
user_uuid: 사용자 UUID (User.user_uuid 참조)
|
user_uuid: 사용자 UUID (User.user_uuid 참조)
|
||||||
video_id: Video 외래키
|
video_id: Video 외래키
|
||||||
social_account_id: SocialAccount 외래키
|
social_account_id: SocialAccount 외래키
|
||||||
|
upload_seq: 업로드 순번 (동일 영상+채널 조합 내 순번, 관리자 추적용)
|
||||||
platform: 플랫폼 구분 (youtube, instagram, facebook, tiktok)
|
platform: 플랫폼 구분 (youtube, instagram, facebook, tiktok)
|
||||||
status: 업로드 상태 (pending, uploading, processing, completed, failed)
|
status: 업로드 상태 (pending, uploading, processing, completed, failed)
|
||||||
upload_progress: 업로드 진행률 (0-100)
|
upload_progress: 업로드 진행률 (0-100)
|
||||||
|
|
@ -58,12 +59,10 @@ class SocialUpload(Base):
|
||||||
Index("idx_social_upload_platform", "platform"),
|
Index("idx_social_upload_platform", "platform"),
|
||||||
Index("idx_social_upload_status", "status"),
|
Index("idx_social_upload_status", "status"),
|
||||||
Index("idx_social_upload_created_at", "created_at"),
|
Index("idx_social_upload_created_at", "created_at"),
|
||||||
Index(
|
# 동일 영상+채널 조합 조회용 인덱스 (유니크 아님 - 여러 번 업로드 가능)
|
||||||
"uq_social_upload_video_platform",
|
Index("idx_social_upload_video_account", "video_id", "social_account_id"),
|
||||||
"video_id",
|
# 순번 조회용 인덱스
|
||||||
"social_account_id",
|
Index("idx_social_upload_seq", "video_id", "social_account_id", "upload_seq"),
|
||||||
unique=True,
|
|
||||||
),
|
|
||||||
{
|
{
|
||||||
"mysql_engine": "InnoDB",
|
"mysql_engine": "InnoDB",
|
||||||
"mysql_charset": "utf8mb4",
|
"mysql_charset": "utf8mb4",
|
||||||
|
|
@ -106,6 +105,16 @@ class SocialUpload(Base):
|
||||||
comment="SocialAccount 외래키",
|
comment="SocialAccount 외래키",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# 업로드 순번 (관리자 추적용)
|
||||||
|
# ==========================================================================
|
||||||
|
upload_seq: Mapped[int] = mapped_column(
|
||||||
|
Integer,
|
||||||
|
nullable=False,
|
||||||
|
default=1,
|
||||||
|
comment="업로드 순번 (동일 영상+채널 조합 내 순번, 1부터 시작)",
|
||||||
|
)
|
||||||
|
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
# 플랫폼 정보
|
# 플랫폼 정보
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
|
|
@ -238,8 +247,10 @@ class SocialUpload(Base):
|
||||||
return (
|
return (
|
||||||
f"<SocialUpload("
|
f"<SocialUpload("
|
||||||
f"id={self.id}, "
|
f"id={self.id}, "
|
||||||
|
f"video_id={self.video_id}, "
|
||||||
|
f"account_id={self.social_account_id}, "
|
||||||
|
f"seq={self.upload_seq}, "
|
||||||
f"platform='{self.platform}', "
|
f"platform='{self.platform}', "
|
||||||
f"status='{self.status}', "
|
f"status='{self.status}'"
|
||||||
f"video_id={self.video_id}"
|
|
||||||
f")>"
|
f")>"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,8 @@ class SocialUploadStatusResponse(BaseModel):
|
||||||
|
|
||||||
upload_id: int = Field(..., description="업로드 작업 ID")
|
upload_id: int = Field(..., description="업로드 작업 ID")
|
||||||
video_id: int = Field(..., description="영상 ID")
|
video_id: int = Field(..., description="영상 ID")
|
||||||
|
social_account_id: int = Field(..., description="소셜 계정 ID")
|
||||||
|
upload_seq: int = Field(..., description="업로드 순번 (동일 영상+채널 조합 내 순번)")
|
||||||
platform: str = Field(..., description="플랫폼명")
|
platform: str = Field(..., description="플랫폼명")
|
||||||
status: UploadStatus = Field(..., description="업로드 상태")
|
status: UploadStatus = Field(..., description="업로드 상태")
|
||||||
upload_progress: int = Field(..., description="업로드 진행률 (0-100)")
|
upload_progress: int = Field(..., description="업로드 진행률 (0-100)")
|
||||||
|
|
@ -210,6 +212,8 @@ class SocialUploadStatusResponse(BaseModel):
|
||||||
"example": {
|
"example": {
|
||||||
"upload_id": 456,
|
"upload_id": 456,
|
||||||
"video_id": 123,
|
"video_id": 123,
|
||||||
|
"social_account_id": 1,
|
||||||
|
"upload_seq": 2,
|
||||||
"platform": "youtube",
|
"platform": "youtube",
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"upload_progress": 100,
|
"upload_progress": 100,
|
||||||
|
|
@ -230,6 +234,8 @@ class SocialUploadHistoryItem(BaseModel):
|
||||||
|
|
||||||
upload_id: int = Field(..., description="업로드 작업 ID")
|
upload_id: int = Field(..., description="업로드 작업 ID")
|
||||||
video_id: int = Field(..., description="영상 ID")
|
video_id: int = Field(..., description="영상 ID")
|
||||||
|
social_account_id: int = Field(..., description="소셜 계정 ID")
|
||||||
|
upload_seq: int = Field(..., description="업로드 순번 (동일 영상+채널 조합 내 순번)")
|
||||||
platform: str = Field(..., description="플랫폼명")
|
platform: str = Field(..., description="플랫폼명")
|
||||||
status: str = Field(..., description="업로드 상태")
|
status: str = Field(..., description="업로드 상태")
|
||||||
title: str = Field(..., description="영상 제목")
|
title: str = Field(..., description="영상 제목")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
-- ===================================================================
|
||||||
|
-- social_upload 테이블 수정 마이그레이션
|
||||||
|
-- 동일 영상 + 동일 채널 조합으로 여러 번 업로드 가능하도록 변경
|
||||||
|
-- 관리자 추적을 위한 upload_seq 컬럼 추가
|
||||||
|
-- 생성일: 2026-02-02
|
||||||
|
-- ===================================================================
|
||||||
|
|
||||||
|
-- 1. 기존 유니크 인덱스 제거
|
||||||
|
DROP INDEX uq_social_upload_video_platform ON social_upload;
|
||||||
|
|
||||||
|
-- 2. 업로드 순번 컬럼 추가 (관리자 추적용)
|
||||||
|
-- upload_seq: 동일 video_id + social_account_id 조합 내에서의 업로드 순번
|
||||||
|
ALTER TABLE social_upload
|
||||||
|
ADD COLUMN upload_seq INT NOT NULL DEFAULT 1 COMMENT '업로드 순번 (동일 영상+채널 조합 내 순번)' AFTER social_account_id;
|
||||||
|
|
||||||
|
-- 3. 추적을 위한 복합 인덱스 추가 (유니크 아님)
|
||||||
|
CREATE INDEX idx_social_upload_video_account ON social_upload(video_id, social_account_id);
|
||||||
|
|
||||||
|
-- 4. 순번 조회를 위한 인덱스 추가
|
||||||
|
CREATE INDEX idx_social_upload_seq ON social_upload(video_id, social_account_id, upload_seq);
|
||||||
|
|
||||||
|
-- ===================================================================
|
||||||
|
-- 확인 쿼리 (실행 후 검증용)
|
||||||
|
-- ===================================================================
|
||||||
|
-- 테이블 구조 확인
|
||||||
|
-- DESCRIBE social_upload;
|
||||||
|
|
||||||
|
-- 인덱스 확인
|
||||||
|
-- SHOW INDEX FROM social_upload;
|
||||||
|
|
||||||
|
-- 특정 영상의 업로드 이력 조회 예시
|
||||||
|
-- SELECT id, video_id, social_account_id, upload_seq, title, status, platform_url, created_at
|
||||||
|
-- FROM social_upload
|
||||||
|
-- WHERE video_id = 17
|
||||||
|
-- ORDER BY upload_seq DESC;
|
||||||
Loading…
Reference in New Issue