fix(rework): Gemini 실패 사유를 사용자에게 명확히 + 음성 없음 메시지 정정
- GeminiSubtitleService: 403(프로젝트 차단)/429(한도)/400(키 무효)을 한국어 메시지로 IllegalArgumentException throw → 'Internal Server Error' 대신 실제 사유 노출 - rework.html: 음성 패널 빈 경우 '말소리 없음(음악/화면자막만) 또는 전사 미실행'으로 정정 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
447645c19a
commit
6380a28a6f
@ -46,7 +46,7 @@ public class GeminiSubtitleService {
|
|||||||
public List<ScriptResponseDto.Segment> fetchKoreanScreenSubtitles(String videoId) {
|
public List<ScriptResponseDto.Segment> fetchKoreanScreenSubtitles(String videoId) {
|
||||||
String key = sanitizeApiKey(apiKey);
|
String key = sanitizeApiKey(apiKey);
|
||||||
if (key.isEmpty()) {
|
if (key.isEmpty()) {
|
||||||
throw new IllegalStateException("GEMINI_API_KEY 가 설정되지 않았습니다.");
|
throw new IllegalArgumentException("GEMINI_API_KEY 가 설정되지 않았습니다.");
|
||||||
}
|
}
|
||||||
String videoUrl = "https://www.youtube.com/watch?v=" + videoId;
|
String videoUrl = "https://www.youtube.com/watch?v=" + videoId;
|
||||||
String apiUrl = "https://generativelanguage.googleapis.com/v1beta/models/"
|
String apiUrl = "https://generativelanguage.googleapis.com/v1beta/models/"
|
||||||
@ -66,13 +66,25 @@ public class GeminiSubtitleService {
|
|||||||
return segs;
|
return segs;
|
||||||
} catch (HttpStatusCodeException e) {
|
} catch (HttpStatusCodeException e) {
|
||||||
// 키는 URL 쿼리에 있으므로 메시지에 apiUrl 을 넣지 않는다(노출 방지).
|
// 키는 URL 쿼리에 있으므로 메시지에 apiUrl 을 넣지 않는다(노출 방지).
|
||||||
|
int code = e.getStatusCode().value();
|
||||||
String body = e.getResponseBodyAsString();
|
String body = e.getResponseBodyAsString();
|
||||||
String tail = body.length() > 300 ? body.substring(0, 300) : body;
|
String msg;
|
||||||
throw new RuntimeException("Gemini 호출 실패(" + e.getStatusCode() + "): " + tail, e);
|
if (code == 403) {
|
||||||
} catch (RuntimeException e) {
|
msg = "Gemini 접근 거부(403): 이 API 키의 프로젝트가 차단되었습니다. 다른 프로젝트의 새 API 키가 필요합니다.";
|
||||||
|
} else if (code == 429) {
|
||||||
|
msg = "Gemini 무료 한도 초과(429): 잠시 후 다시 시도하세요.";
|
||||||
|
} else if (code == 400 && body.contains("API key not valid")) {
|
||||||
|
msg = "Gemini API 키가 유효하지 않습니다(GEMINI_API_KEY 값을 확인하세요).";
|
||||||
|
} else {
|
||||||
|
String tail = body.length() > 200 ? body.substring(0, 200) : body;
|
||||||
|
msg = "Gemini 호출 실패(" + code + "): " + tail;
|
||||||
|
}
|
||||||
|
log.error("Gemini 호출 실패({}) for video {}", code, videoId);
|
||||||
|
throw new IllegalArgumentException(msg, e); // GlobalExceptionHandler 가 메시지를 그대로 노출
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Gemini 자막 추출 실패: " + e.getMessage(), e);
|
throw new IllegalArgumentException("Gemini 자막 추출 실패: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -490,7 +490,7 @@
|
|||||||
|
|
||||||
// 왼쪽: 저장된 Whisper 세그먼트 → 한국어 번역(LibreTranslate)
|
// 왼쪽: 저장된 Whisper 세그먼트 → 한국어 번역(LibreTranslate)
|
||||||
const audioP = (async () => {
|
const audioP = (async () => {
|
||||||
if(!SEGMENTS || !SEGMENTS.length) return { error: '음성 자막: 먼저 ‘전사’를 실행하세요' };
|
if(!SEGMENTS || !SEGMENTS.length) return { error: '음성 자막 없음 — 이 영상에 말소리가 없거나(음악·화면자막만) 아직 ‘전사’ 미실행' };
|
||||||
const r = await api(API + '/' + VIDEO_ID + '/translate?target=ko&format=segments', { method:'POST' });
|
const r = await api(API + '/' + VIDEO_ID + '/translate?target=ko&format=segments', { method:'POST' });
|
||||||
const ko = (r && r.texts) || [];
|
const ko = (r && r.texts) || [];
|
||||||
return { segments: SEGMENTS.map((sg, i) => ({ start: sg.start, end: sg.end, text: (ko[i] != null ? ko[i] : (sg.text || '')) })) };
|
return { segments: SEGMENTS.map((sg, i) => ({ start: sg.start, end: sg.end, text: (ko[i] != null ? ko[i] : (sg.text || '')) })) };
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user