이미지 업로드 관련 디버그 프린트 삭제
parent
266a51fe1d
commit
62dd681b83
|
|
@ -181,9 +181,9 @@ IMAGES_JSON_EXAMPLE = """[
|
||||||
@router.post(
|
@router.post(
|
||||||
"/image/upload/server/{task_id}",
|
"/image/upload/server/{task_id}",
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
summary="이미지 업로드",
|
summary="이미지 업로드 (로컬 서버)",
|
||||||
description="""
|
description="""
|
||||||
task_id에 연결된 이미지를 서버에 업로드합니다.
|
task_id에 연결된 이미지를 로컬 서버(media 폴더)에 업로드합니다.
|
||||||
|
|
||||||
## 요청 방식
|
## 요청 방식
|
||||||
multipart/form-data 형식으로 전송합니다.
|
multipart/form-data 형식으로 전송합니다.
|
||||||
|
|
@ -249,12 +249,15 @@ print(response.json())
|
||||||
## 반환 정보
|
## 반환 정보
|
||||||
- **task_id**: 작업 고유 식별자
|
- **task_id**: 작업 고유 식별자
|
||||||
- **total_count**: 총 업로드된 이미지 개수
|
- **total_count**: 총 업로드된 이미지 개수
|
||||||
- **url_count**: URL로 등록된 이미지 개수
|
- **url_count**: URL로 등록된 이미지 개수 (Image 테이블에 외부 URL 그대로 저장)
|
||||||
- **file_count**: 파일로 업로드된 이미지 개수
|
- **file_count**: 파일로 업로드된 이미지 개수 (media 폴더에 저장)
|
||||||
|
- **saved_count**: Image 테이블에 저장된 row 수
|
||||||
- **images**: 업로드된 이미지 목록
|
- **images**: 업로드된 이미지 목록
|
||||||
|
- **source**: "url" (외부 URL) 또는 "file" (로컬 서버 저장)
|
||||||
|
|
||||||
## 저장 경로
|
## 저장 경로
|
||||||
- 바이너리 파일: /media/image/{날짜}/{uuid7}/{파일명}
|
- 바이너리 파일: /media/image/{날짜}/{uuid7}/{파일명}
|
||||||
|
- URL 이미지: 외부 URL 그대로 Image 테이블에 저장
|
||||||
""",
|
""",
|
||||||
response_model=ImageUploadResponse,
|
response_model=ImageUploadResponse,
|
||||||
responses={
|
responses={
|
||||||
|
|
@ -276,20 +279,9 @@ async def upload_images(
|
||||||
session: AsyncSession = Depends(get_session),
|
session: AsyncSession = Depends(get_session),
|
||||||
) -> ImageUploadResponse:
|
) -> ImageUploadResponse:
|
||||||
"""이미지 업로드 (URL + 바이너리 파일)"""
|
"""이미지 업로드 (URL + 바이너리 파일)"""
|
||||||
print(f"[upload_images] START - task_id: {task_id}")
|
|
||||||
print(f"[upload_images] images_json: {images_json}")
|
|
||||||
print(f"[upload_images] files: {files}")
|
|
||||||
|
|
||||||
# 1. 진입 검증: images_json 또는 files 중 하나는 반드시 있어야 함
|
# 1. 진입 검증: images_json 또는 files 중 하나는 반드시 있어야 함
|
||||||
has_images_json = images_json is not None and images_json.strip() != ""
|
has_images_json = images_json is not None and images_json.strip() != ""
|
||||||
has_files = files is not None and len(files) > 0
|
has_files = files is not None and len(files) > 0
|
||||||
print(f"[upload_images] has_images_json: {has_images_json}, has_files: {has_files}")
|
|
||||||
|
|
||||||
if has_files and files:
|
|
||||||
for idx, f in enumerate(files):
|
|
||||||
print(
|
|
||||||
f"[upload_images] file[{idx}]: filename={f.filename}, size={f.size}, content_type={f.content_type}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if not has_images_json and not has_files:
|
if not has_images_json and not has_files:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|
@ -322,19 +314,11 @@ async def upload_images(
|
||||||
is_real_file = (
|
is_real_file = (
|
||||||
f.filename and f.filename != "filename"
|
f.filename and f.filename != "filename"
|
||||||
) # Swagger 빈 파일 체크
|
) # Swagger 빈 파일 체크
|
||||||
print(
|
|
||||||
f"[upload_images] Checking file: {f.filename}, size={f.size}, is_valid_ext={is_valid_ext}, is_real_file={is_real_file}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if f and is_real_file and is_valid_ext and is_not_empty:
|
if f and is_real_file and is_valid_ext and is_not_empty:
|
||||||
valid_files.append(f)
|
valid_files.append(f)
|
||||||
else:
|
else:
|
||||||
skipped_files.append(f.filename or "unknown")
|
skipped_files.append(f.filename or "unknown")
|
||||||
|
|
||||||
print(
|
|
||||||
f"[upload_images] valid_files count: {len(valid_files)}, skipped: {skipped_files}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 유효한 데이터가 하나도 없으면 에러
|
# 유효한 데이터가 하나도 없으면 에러
|
||||||
if not url_images and not valid_files:
|
if not url_images and not valid_files:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|
@ -357,7 +341,6 @@ async def upload_images(
|
||||||
)
|
)
|
||||||
session.add(image)
|
session.add(image)
|
||||||
await session.flush() # ID 생성을 위해 flush
|
await session.flush() # ID 생성을 위해 flush
|
||||||
print(f"[upload_images] URL image saved - id: {image.id}, img_name: {img_name}")
|
|
||||||
|
|
||||||
result_images.append(
|
result_images.append(
|
||||||
ImageUploadResultItem(
|
ImageUploadResultItem(
|
||||||
|
|
@ -398,7 +381,6 @@ async def upload_images(
|
||||||
# media 기준 URL 생성
|
# media 기준 URL 생성
|
||||||
img_url = f"/media/image/{today}/{batch_uuid}/{filename}"
|
img_url = f"/media/image/{today}/{batch_uuid}/{filename}"
|
||||||
img_name = file.filename or filename
|
img_name = file.filename or filename
|
||||||
print(f"[upload_images] File saved to media - path: {save_path}, url: {img_url}")
|
|
||||||
|
|
||||||
image = Image(
|
image = Image(
|
||||||
task_id=task_id,
|
task_id=task_id,
|
||||||
|
|
@ -421,9 +403,7 @@ async def upload_images(
|
||||||
img_order += 1
|
img_order += 1
|
||||||
|
|
||||||
saved_count = len(result_images)
|
saved_count = len(result_images)
|
||||||
print(f"[upload_images] Committing {saved_count} images to database...")
|
|
||||||
await session.commit()
|
await session.commit()
|
||||||
print("[upload_images] Commit successful!")
|
|
||||||
|
|
||||||
return ImageUploadResponse(
|
return ImageUploadResponse(
|
||||||
task_id=task_id,
|
task_id=task_id,
|
||||||
|
|
@ -437,9 +417,10 @@ async def upload_images(
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/image/upload/blob/{task_id}",
|
"/image/upload/blob/{task_id}",
|
||||||
summary="이미지 업로드 (Azure Blob)",
|
summary="이미지 업로드 (Azure Blob Storage)",
|
||||||
description="""
|
description="""
|
||||||
task_id에 연결된 이미지를 Azure Blob Storage에 업로드합니다.
|
task_id에 연결된 이미지를 Azure Blob Storage에 업로드합니다.
|
||||||
|
바이너리 파일은 로컬 서버에 저장하지 않고 Azure Blob에 직접 업로드됩니다.
|
||||||
|
|
||||||
## 요청 방식
|
## 요청 방식
|
||||||
multipart/form-data 형식으로 전송합니다.
|
multipart/form-data 형식으로 전송합니다.
|
||||||
|
|
@ -479,12 +460,15 @@ curl -X POST "http://localhost:8000/image/upload/blob/test-task-001" \\
|
||||||
## 반환 정보
|
## 반환 정보
|
||||||
- **task_id**: 작업 고유 식별자
|
- **task_id**: 작업 고유 식별자
|
||||||
- **total_count**: 총 업로드된 이미지 개수
|
- **total_count**: 총 업로드된 이미지 개수
|
||||||
- **url_count**: URL로 등록된 이미지 개수
|
- **url_count**: URL로 등록된 이미지 개수 (Image 테이블에 외부 URL 그대로 저장)
|
||||||
- **file_count**: 파일로 업로드된 이미지 개수 (Blob에 저장됨)
|
- **file_count**: 파일로 업로드된 이미지 개수 (Azure Blob Storage에 저장)
|
||||||
|
- **saved_count**: Image 테이블에 저장된 row 수
|
||||||
- **images**: 업로드된 이미지 목록
|
- **images**: 업로드된 이미지 목록
|
||||||
|
- **source**: "url" (외부 URL) 또는 "blob" (Azure Blob Storage)
|
||||||
|
|
||||||
## 저장 경로
|
## 저장 경로
|
||||||
- 바이너리 파일: Azure Blob Storage ({task_id}/{파일명})
|
- 바이너리 파일: Azure Blob Storage ({BASE_URL}/{task_id}/image/{파일명})
|
||||||
|
- URL 이미지: 외부 URL 그대로 Image 테이블에 저장
|
||||||
""",
|
""",
|
||||||
response_model=ImageUploadResponse,
|
response_model=ImageUploadResponse,
|
||||||
responses={
|
responses={
|
||||||
|
|
@ -506,8 +490,6 @@ async def upload_images_blob(
|
||||||
session: AsyncSession = Depends(get_session),
|
session: AsyncSession = Depends(get_session),
|
||||||
) -> ImageUploadResponse:
|
) -> ImageUploadResponse:
|
||||||
"""이미지 업로드 (URL + Azure Blob Storage)"""
|
"""이미지 업로드 (URL + Azure Blob Storage)"""
|
||||||
print(f"[upload_images_blob] START - task_id: {task_id}")
|
|
||||||
|
|
||||||
# 1. 진입 검증
|
# 1. 진입 검증
|
||||||
has_images_json = images_json is not None and images_json.strip() != ""
|
has_images_json = images_json is not None and images_json.strip() != ""
|
||||||
has_files = files is not None and len(files) > 0
|
has_files = files is not None and len(files) > 0
|
||||||
|
|
@ -545,8 +527,6 @@ async def upload_images_blob(
|
||||||
else:
|
else:
|
||||||
skipped_files.append(f.filename or "unknown")
|
skipped_files.append(f.filename or "unknown")
|
||||||
|
|
||||||
print(f"[upload_images_blob] valid_files: {len(valid_files)}, url_images: {len(url_images)}")
|
|
||||||
|
|
||||||
if not url_images and not valid_files:
|
if not url_images and not valid_files:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_400_BAD_REQUEST,
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
|
@ -568,7 +548,6 @@ async def upload_images_blob(
|
||||||
)
|
)
|
||||||
session.add(image)
|
session.add(image)
|
||||||
await session.flush()
|
await session.flush()
|
||||||
print(f"[upload_images_blob] URL saved - id: {image.id}, img_name: {img_name}")
|
|
||||||
|
|
||||||
result_images.append(
|
result_images.append(
|
||||||
ImageUploadResultItem(
|
ImageUploadResultItem(
|
||||||
|
|
@ -597,7 +576,6 @@ async def upload_images_blob(
|
||||||
|
|
||||||
# 파일 내용 읽기
|
# 파일 내용 읽기
|
||||||
file_content = await file.read()
|
file_content = await file.read()
|
||||||
print(f"[upload_images_blob] Uploading {filename} ({len(file_content)} bytes) to Blob...")
|
|
||||||
|
|
||||||
# Azure Blob Storage에 직접 업로드
|
# Azure Blob Storage에 직접 업로드
|
||||||
upload_success = await uploader.upload_image_bytes(file_content, filename)
|
upload_success = await uploader.upload_image_bytes(file_content, filename)
|
||||||
|
|
@ -614,7 +592,6 @@ async def upload_images_blob(
|
||||||
)
|
)
|
||||||
session.add(image)
|
session.add(image)
|
||||||
await session.flush()
|
await session.flush()
|
||||||
print(f"[upload_images_blob] Blob saved - id: {image.id}, blob_url: {blob_url}")
|
|
||||||
|
|
||||||
result_images.append(
|
result_images.append(
|
||||||
ImageUploadResultItem(
|
ImageUploadResultItem(
|
||||||
|
|
@ -627,13 +604,10 @@ async def upload_images_blob(
|
||||||
)
|
)
|
||||||
img_order += 1
|
img_order += 1
|
||||||
else:
|
else:
|
||||||
print(f"[upload_images_blob] Failed to upload {filename}")
|
|
||||||
skipped_files.append(filename)
|
skipped_files.append(filename)
|
||||||
|
|
||||||
saved_count = len(result_images)
|
saved_count = len(result_images)
|
||||||
print(f"[upload_images_blob] Committing {saved_count} images...")
|
|
||||||
await session.commit()
|
await session.commit()
|
||||||
print(f"[upload_images_blob] Done! saved_count: {saved_count}")
|
|
||||||
|
|
||||||
return ImageUploadResponse(
|
return ImageUploadResponse(
|
||||||
task_id=task_id,
|
task_id=task_id,
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,6 @@ async def upload_image_to_blob(
|
||||||
try:
|
try:
|
||||||
# 1. media에 파일 저장
|
# 1. media에 파일 저장
|
||||||
await save_upload_file(file, save_path)
|
await save_upload_file(file, save_path)
|
||||||
print(f"[upload_image_to_blob] File saved to media: {save_path}")
|
|
||||||
|
|
||||||
# 2. Azure Blob Storage에 업로드
|
# 2. Azure Blob Storage에 업로드
|
||||||
uploader = AzureBlobUploader(task_id=task_id)
|
uploader = AzureBlobUploader(task_id=task_id)
|
||||||
|
|
@ -58,5 +57,4 @@ async def upload_image_to_blob(
|
||||||
return False, f"Failed to upload {filename} to Blob", media_path
|
return False, f"Failed to upload {filename} to Blob", media_path
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[upload_image_to_blob] Error: {e}")
|
|
||||||
return False, str(e), media_path
|
return False, str(e), media_path
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ async def generate_song(
|
||||||
summary="노래 생성 상태 조회",
|
summary="노래 생성 상태 조회",
|
||||||
description="""
|
description="""
|
||||||
Suno API를 통해 노래 생성 작업의 상태를 조회합니다.
|
Suno API를 통해 노래 생성 작업의 상태를 조회합니다.
|
||||||
SUCCESS 상태인 경우 백그라운드에서 MP3 파일을 다운로드하고 Song 테이블을 업데이트합니다.
|
SUCCESS 상태인 경우 백그라운드에서 MP3 파일을 다운로드하고 Azure Blob Storage에 업로드한 뒤 Song 테이블을 업데이트합니다.
|
||||||
|
|
||||||
## 경로 파라미터
|
## 경로 파라미터
|
||||||
- **suno_task_id**: 노래 생성 시 반환된 Suno API 작업 ID (필수)
|
- **suno_task_id**: 노래 생성 시 반환된 Suno API 작업 ID (필수)
|
||||||
|
|
@ -208,7 +208,9 @@ GET /song/status/abc123...
|
||||||
## 참고
|
## 참고
|
||||||
- 스트림 URL: 30-40초 내 생성
|
- 스트림 URL: 30-40초 내 생성
|
||||||
- 다운로드 URL: 2-3분 내 생성
|
- 다운로드 URL: 2-3분 내 생성
|
||||||
- SUCCESS 시 백그라운드에서 MP3 다운로드 및 DB 업데이트 진행
|
- SUCCESS 시 백그라운드에서 MP3 다운로드 → Azure Blob Storage 업로드 → Song 테이블 업데이트 진행
|
||||||
|
- 저장 경로: Azure Blob Storage ({BASE_URL}/{task_id}/song/{store_name}.mp3)
|
||||||
|
- Song 테이블의 song_result_url에 Blob URL이 저장됩니다
|
||||||
""",
|
""",
|
||||||
response_model=PollingSongResponse,
|
response_model=PollingSongResponse,
|
||||||
responses={
|
responses={
|
||||||
|
|
@ -224,7 +226,8 @@ async def get_song_status(
|
||||||
"""suno_task_id로 노래 생성 작업의 상태를 조회합니다.
|
"""suno_task_id로 노래 생성 작업의 상태를 조회합니다.
|
||||||
|
|
||||||
SUCCESS 상태인 경우 백그라운드에서 MP3 파일을 다운로드하고
|
SUCCESS 상태인 경우 백그라운드에서 MP3 파일을 다운로드하고
|
||||||
Song 테이블의 status를 completed로, song_result_url을 업데이트합니다.
|
Azure Blob Storage에 업로드한 뒤 Song 테이블의 status를 completed로,
|
||||||
|
song_result_url을 Blob URL로 업데이트합니다.
|
||||||
"""
|
"""
|
||||||
print(f"[get_song_status] START - suno_task_id: {suno_task_id}")
|
print(f"[get_song_status] START - suno_task_id: {suno_task_id}")
|
||||||
try:
|
try:
|
||||||
|
|
@ -288,32 +291,33 @@ async def get_song_status(
|
||||||
"/download/{task_id}",
|
"/download/{task_id}",
|
||||||
summary="노래 다운로드 상태 조회",
|
summary="노래 다운로드 상태 조회",
|
||||||
description="""
|
description="""
|
||||||
task_id를 기반으로 Song 테이블의 상태를 polling하고,
|
task_id를 기반으로 Song 테이블의 상태를 polling하고,
|
||||||
completed인 경우 Project 정보와 노래 URL을 반환합니다.
|
completed인 경우 Project 정보와 노래 URL을 반환합니다.
|
||||||
|
|
||||||
## 경로 파라미터
|
## 경로 파라미터
|
||||||
- **task_id**: 프로젝트 task_id (필수)
|
- **task_id**: 프로젝트 task_id (필수)
|
||||||
|
|
||||||
## 반환 정보
|
## 반환 정보
|
||||||
- **success**: 조회 성공 여부
|
- **success**: 조회 성공 여부
|
||||||
- **status**: 처리 상태 (processing, completed, failed)
|
- **status**: 처리 상태 (processing, completed, failed, not_found)
|
||||||
- **message**: 응답 메시지
|
- **message**: 응답 메시지
|
||||||
- **store_name**: 업체명
|
- **store_name**: 업체명
|
||||||
- **region**: 지역명
|
- **region**: 지역명
|
||||||
- **detail_region_info**: 상세 지역 정보
|
- **detail_region_info**: 상세 지역 정보
|
||||||
- **task_id**: 작업 고유 식별자
|
- **task_id**: 작업 고유 식별자
|
||||||
- **language**: 언어
|
- **language**: 언어
|
||||||
- **song_result_url**: 노래 결과 URL (completed 시)
|
- **song_result_url**: 노래 결과 URL (completed 시, Azure Blob Storage URL)
|
||||||
- **created_at**: 생성 일시
|
- **created_at**: 생성 일시
|
||||||
|
|
||||||
## 사용 예시
|
## 사용 예시
|
||||||
```
|
```
|
||||||
GET /song/download/019123ab-cdef-7890-abcd-ef1234567890
|
GET /song/download/019123ab-cdef-7890-abcd-ef1234567890
|
||||||
```
|
```
|
||||||
|
|
||||||
## 참고
|
## 참고
|
||||||
- processing 상태인 경우 song_result_url은 null입니다.
|
- processing 상태인 경우 song_result_url은 null입니다.
|
||||||
- completed 상태인 경우 Project 정보와 함께 song_result_url을 반환합니다.
|
- completed 상태인 경우 Project 정보와 함께 song_result_url (Azure Blob URL)을 반환합니다.
|
||||||
|
- song_result_url 형식: {AZURE_BLOB_BASE_URL}/{task_id}/song/{store_name}.mp3
|
||||||
""",
|
""",
|
||||||
response_model=DownloadSongResponse,
|
response_model=DownloadSongResponse,
|
||||||
responses={
|
responses={
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue