db status 조회로 변경 .
parent
662b6b80bc
commit
e3ed840d12
|
|
@ -22,7 +22,6 @@ type GenerationStatus = 'idle' | 'generating_lyric' | 'generating_song' | 'polli
|
||||||
|
|
||||||
interface SavedGenerationState {
|
interface SavedGenerationState {
|
||||||
taskId: string;
|
taskId: string;
|
||||||
songId: string;
|
|
||||||
lyrics: string;
|
lyrics: string;
|
||||||
status: GenerationStatus;
|
status: GenerationStatus;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
|
|
@ -71,10 +70,9 @@ const SoundStudioContent: React.FC<SoundStudioContentProps> = ({
|
||||||
const audioRef = useRef<HTMLAudioElement>(null);
|
const audioRef = useRef<HTMLAudioElement>(null);
|
||||||
const languageDropdownRef = useRef<HTMLDivElement>(null);
|
const languageDropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const saveToStorage = (taskId: string, songId: string, currentLyrics: string, currentStatus: GenerationStatus) => {
|
const saveToStorage = (taskId: string, currentLyrics: string, currentStatus: GenerationStatus) => {
|
||||||
const data: SavedGenerationState = {
|
const data: SavedGenerationState = {
|
||||||
taskId,
|
taskId,
|
||||||
songId,
|
|
||||||
lyrics: currentLyrics,
|
lyrics: currentLyrics,
|
||||||
status: currentStatus,
|
status: currentStatus,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
|
|
@ -114,7 +112,7 @@ const SoundStudioContent: React.FC<SoundStudioContentProps> = ({
|
||||||
}
|
}
|
||||||
setStatus('polling');
|
setStatus('polling');
|
||||||
setStatusMessage('노래를 생성하고 있습니다... (새로고침 후 복구됨)');
|
setStatusMessage('노래를 생성하고 있습니다... (새로고침 후 복구됨)');
|
||||||
resumePolling(savedState.taskId, savedState.songId, savedState.lyrics, 0);
|
resumePolling(savedState.taskId, savedState.lyrics, 0);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
@ -146,16 +144,15 @@ const SoundStudioContent: React.FC<SoundStudioContentProps> = ({
|
||||||
}
|
}
|
||||||
}, [videoGenerationStatus, songTaskId, onNext]);
|
}, [videoGenerationStatus, songTaskId, onNext]);
|
||||||
|
|
||||||
const resumePolling = async (taskId: string, songId: string, currentLyrics: string, currentRetryCount: number = 0) => {
|
const resumePolling = async (taskId: string, currentLyrics: string, currentRetryCount: number = 0) => {
|
||||||
try {
|
try {
|
||||||
const downloadResponse = await waitForSongComplete(
|
const downloadResponse = await waitForSongComplete(
|
||||||
taskId,
|
taskId,
|
||||||
songId,
|
|
||||||
(pollStatus: string) => {
|
(pollStatus: string) => {
|
||||||
if (pollStatus === 'pending') {
|
if (pollStatus === 'processing') {
|
||||||
setStatusMessage('노래를 생성하고 있습니다...');
|
|
||||||
} else if (pollStatus === 'processing') {
|
|
||||||
setStatusMessage('노래를 생성하고 있습니다...');
|
setStatusMessage('노래를 생성하고 있습니다...');
|
||||||
|
} else if (pollStatus === 'uploading') {
|
||||||
|
setStatusMessage('노래를 업로드하고 있습니다...');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -221,8 +218,8 @@ const SoundStudioContent: React.FC<SoundStudioContentProps> = ({
|
||||||
throw new Error(songResponse.error_message || '음악 생성 요청에 실패했습니다.');
|
throw new Error(songResponse.error_message || '음악 생성 요청에 실패했습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
saveToStorage(songResponse.task_id, songResponse.song_id, currentLyrics, 'polling');
|
saveToStorage(songResponse.task_id, currentLyrics, 'polling');
|
||||||
await resumePolling(songResponse.task_id, songResponse.song_id, currentLyrics, currentRetryCount);
|
await resumePolling(songResponse.task_id, currentLyrics, currentRetryCount);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Song regeneration failed:', error);
|
console.error('Song regeneration failed:', error);
|
||||||
|
|
@ -396,9 +393,9 @@ const SoundStudioContent: React.FC<SoundStudioContentProps> = ({
|
||||||
|
|
||||||
setStatus('polling');
|
setStatus('polling');
|
||||||
setStatusMessage('노래를 생성하고 있습니다...');
|
setStatusMessage('노래를 생성하고 있습니다...');
|
||||||
saveToStorage(songResponse.task_id, songResponse.song_id, lyricDetailResponse.lyric_result, 'polling');
|
saveToStorage(songResponse.task_id, lyricDetailResponse.lyric_result, 'polling');
|
||||||
|
|
||||||
await resumePolling(songResponse.task_id, songResponse.song_id, lyricDetailResponse.lyric_result, 0);
|
await resumePolling(songResponse.task_id, lyricDetailResponse.lyric_result, 0);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Music generation failed:', error);
|
console.error('Music generation failed:', error);
|
||||||
|
|
|
||||||
|
|
@ -83,23 +83,7 @@ export interface SongGenerateResponse {
|
||||||
error_message: string | null;
|
error_message: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 노래 상태 확인 응답
|
// 노래 다운로드 상태 조회 응답 (DB Polling)
|
||||||
export interface SongStatusResponse {
|
|
||||||
success: boolean;
|
|
||||||
status: string;
|
|
||||||
message?: string;
|
|
||||||
clips?: Array<{
|
|
||||||
id: string;
|
|
||||||
audio_url: string;
|
|
||||||
stream_audio_url: string;
|
|
||||||
image_url: string;
|
|
||||||
title: string;
|
|
||||||
status: string | null;
|
|
||||||
duration: number;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 노래 다운로드 응답
|
|
||||||
export interface SongDownloadResponse {
|
export interface SongDownloadResponse {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
status: string;
|
status: string;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import {
|
||||||
LyricDetailResponse,
|
LyricDetailResponse,
|
||||||
SongGenerateRequest,
|
SongGenerateRequest,
|
||||||
SongGenerateResponse,
|
SongGenerateResponse,
|
||||||
SongStatusResponse,
|
|
||||||
SongDownloadResponse,
|
SongDownloadResponse,
|
||||||
VideoGenerateResponse,
|
VideoGenerateResponse,
|
||||||
VideoStatusResponse,
|
VideoStatusResponse,
|
||||||
|
|
@ -154,21 +153,9 @@ export async function generateSong(taskId: string, request: SongGenerateRequest)
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 노래 상태 확인 API (song_id 사용)
|
// 노래 다운로드 상태 조회 API (DB Polling)
|
||||||
export async function getSongStatus(songId: string): Promise<SongStatusResponse> {
|
// task_id를 기반으로 Song 테이블의 상태를 조회하고, completed인 경우 Project 정보와 노래 URL을 반환
|
||||||
const response = await fetch(`${API_URL}/song/status/${songId}`, {
|
export async function getSongDownloadStatus(taskId: string): Promise<SongDownloadResponse> {
|
||||||
method: 'GET',
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.json();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 노래 다운로드 API
|
|
||||||
export async function downloadSong(taskId: string): Promise<SongDownloadResponse> {
|
|
||||||
const response = await fetch(`${API_URL}/song/download/${taskId}`, {
|
const response = await fetch(`${API_URL}/song/download/${taskId}`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
|
|
@ -180,40 +167,44 @@ export async function downloadSong(taskId: string): Promise<SongDownloadResponse
|
||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 노래 생성 완료까지 폴링 (2분 타임아웃)
|
// 노래 생성 완료까지 폴링 (5분 타임아웃, 3초 간격)
|
||||||
const POLL_TIMEOUT = 2 * 60 * 1000; // 2분
|
// 새로운 API는 DB 상태를 직접 조회: processing, uploading, completed, failed, not_found, error
|
||||||
const POLL_INTERVAL = 5000; // 5초
|
const SONG_POLL_TIMEOUT = 5 * 60 * 1000; // 5분
|
||||||
|
const SONG_POLL_INTERVAL = 3000; // 3초
|
||||||
|
|
||||||
export async function waitForSongComplete(
|
export async function waitForSongComplete(
|
||||||
taskId: string,
|
taskId: string,
|
||||||
songId: string,
|
|
||||||
onStatusChange?: (status: string) => void
|
onStatusChange?: (status: string) => void
|
||||||
): Promise<SongDownloadResponse> {
|
): Promise<SongDownloadResponse> {
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
|
|
||||||
// 재귀적으로 폴링하는 방식으로 변경 (async/await 제대로 동작)
|
|
||||||
const poll = async (): Promise<SongDownloadResponse> => {
|
const poll = async (): Promise<SongDownloadResponse> => {
|
||||||
// 2분 타임아웃 체크
|
// 5분 타임아웃 체크
|
||||||
if (Date.now() - startTime > POLL_TIMEOUT) {
|
if (Date.now() - startTime > SONG_POLL_TIMEOUT) {
|
||||||
throw new Error('TIMEOUT');
|
throw new Error('TIMEOUT');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 상태 확인은 song_id 사용
|
const response = await getSongDownloadStatus(taskId);
|
||||||
const statusResponse = await getSongStatus(songId);
|
onStatusChange?.(response.status);
|
||||||
onStatusChange?.(statusResponse.status);
|
|
||||||
|
|
||||||
// status가 "SUCCESS" (대문자)인 경우 완료
|
// completed: 모든 작업 완료, Blob URL 사용 가능
|
||||||
if (statusResponse.status === 'SUCCESS' && statusResponse.success) {
|
if (response.status === 'completed' && response.success) {
|
||||||
// 다운로드는 task_id 사용
|
return response;
|
||||||
const downloadResponse = await downloadSong(taskId);
|
|
||||||
return downloadResponse;
|
|
||||||
} else if (statusResponse.status === 'FAILED' || statusResponse.status === 'failed') {
|
|
||||||
throw new Error('Song generation failed');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PENDING, PROCESSING 등은 대기 후 재시도
|
// failed: 노래 생성 또는 업로드 실패
|
||||||
await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL));
|
if (response.status === 'failed' || response.status === 'error') {
|
||||||
|
throw new Error(response.error_message || '노래 생성에 실패했습니다.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// not_found: task_id에 해당하는 Song 없음
|
||||||
|
if (response.status === 'not_found') {
|
||||||
|
throw new Error('노래를 찾을 수 없습니다.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// processing, uploading 등은 대기 후 재시도
|
||||||
|
await new Promise(resolve => setTimeout(resolve, SONG_POLL_INTERVAL));
|
||||||
return poll();
|
return poll();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue