diff --git a/src/main/java/com/hlab/yanalyst/service/GeminiSubtitleService.java b/src/main/java/com/hlab/yanalyst/service/GeminiSubtitleService.java index b14dfcb..f20ee6a 100644 --- a/src/main/java/com/hlab/yanalyst/service/GeminiSubtitleService.java +++ b/src/main/java/com/hlab/yanalyst/service/GeminiSubtitleService.java @@ -46,7 +46,7 @@ public class GeminiSubtitleService { public List fetchKoreanScreenSubtitles(String videoId) { String key = sanitizeApiKey(apiKey); 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 apiUrl = "https://generativelanguage.googleapis.com/v1beta/models/" @@ -66,13 +66,25 @@ public class GeminiSubtitleService { return segs; } catch (HttpStatusCodeException e) { // 키는 URL 쿼리에 있으므로 메시지에 apiUrl 을 넣지 않는다(노출 방지). + int code = e.getStatusCode().value(); String body = e.getResponseBodyAsString(); - String tail = body.length() > 300 ? body.substring(0, 300) : body; - throw new RuntimeException("Gemini 호출 실패(" + e.getStatusCode() + "): " + tail, e); - } catch (RuntimeException e) { + String msg; + if (code == 403) { + 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; } catch (Exception e) { - throw new RuntimeException("Gemini 자막 추출 실패: " + e.getMessage(), e); + throw new IllegalArgumentException("Gemini 자막 추출 실패: " + e.getMessage(), e); } } diff --git a/src/main/resources/templates/rework.html b/src/main/resources/templates/rework.html index 442a4a1..496bdb3 100644 --- a/src/main/resources/templates/rework.html +++ b/src/main/resources/templates/rework.html @@ -490,7 +490,7 @@ // 왼쪽: 저장된 Whisper 세그먼트 → 한국어 번역(LibreTranslate) 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 ko = (r && r.texts) || []; return { segments: SEGMENTS.map((sg, i) => ({ start: sg.start, end: sg.end, text: (ko[i] != null ? ko[i] : (sg.text || '')) })) };