fix(rework): AI 자막 - 음성 없음은 오류 아닌 안내(회색)로 표시
말소리 없는 영상(음악·화면자막만)의 음성 패널을 빨강 오류가 아니라 회색 안내
('음성 없음 — 말소리가 없거나 전사 미실행')로 표시. renderAiPanel에 note 인자 추가.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6380a28a6f
commit
7b94515180
@ -470,14 +470,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ===== AI 자막 (음성 Whisper / 화면 Gemini) =====
|
// ===== AI 자막 (음성 Whisper / 화면 Gemini) =====
|
||||||
function renderAiPanel(elId, segs, error){
|
// segs 있으면 목록, 없으면 error(빨강) 또는 note(회색 안내) 표시.
|
||||||
|
function renderAiPanel(elId, segs, error, note){
|
||||||
const box = document.getElementById(elId);
|
const box = document.getElementById(elId);
|
||||||
|
if(segs && segs.length){
|
||||||
|
box.innerHTML = segs.map(sg =>
|
||||||
|
'<div class="ai-row" data-start="' + sg.start + '" onclick="seekTo(' + sg.start + ')">'
|
||||||
|
+ '<span class="seg-time">' + mmss(sg.start) + '</span>'
|
||||||
|
+ '<span class="seg-text">' + esc(sg.text) + '</span></div>').join('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
if(error){ box.innerHTML = '<div class="ai-empty" style="color:#f87171;">' + esc(error) + '</div>'; return; }
|
if(error){ box.innerHTML = '<div class="ai-empty" style="color:#f87171;">' + esc(error) + '</div>'; return; }
|
||||||
if(!segs || !segs.length){ box.innerHTML = '<div class="ai-empty">자막 없음</div>'; return; }
|
box.innerHTML = '<div class="ai-empty">' + esc(note || '자막 없음') + '</div>';
|
||||||
box.innerHTML = segs.map(sg =>
|
|
||||||
'<div class="ai-row" data-start="' + sg.start + '" onclick="seekTo(' + sg.start + ')">'
|
|
||||||
+ '<span class="seg-time">' + mmss(sg.start) + '</span>'
|
|
||||||
+ '<span class="seg-text">' + esc(sg.text) + '</span></div>').join('');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 음성(Whisper 정밀+한국어) + 화면(Gemini 한국어)을 동시에 생성해 좌우 패널에 표시.
|
// 음성(Whisper 정밀+한국어) + 화면(Gemini 한국어)을 동시에 생성해 좌우 패널에 표시.
|
||||||
@ -490,7 +494,8 @@
|
|||||||
|
|
||||||
// 왼쪽: 저장된 Whisper 세그먼트 → 한국어 번역(LibreTranslate)
|
// 왼쪽: 저장된 Whisper 세그먼트 → 한국어 번역(LibreTranslate)
|
||||||
const audioP = (async () => {
|
const audioP = (async () => {
|
||||||
if(!SEGMENTS || !SEGMENTS.length) return { error: '음성 자막 없음 — 이 영상에 말소리가 없거나(음악·화면자막만) 아직 ‘전사’ 미실행' };
|
// 말소리가 없는(음악·화면자막만) 영상은 오류가 아니라 '음성 없음' 안내.
|
||||||
|
if(!SEGMENTS || !SEGMENTS.length) return { note: '음성 없음 — 말소리가 없거나(음악·화면자막만) ‘전사’ 미실행' };
|
||||||
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 || '')) })) };
|
||||||
@ -504,8 +509,8 @@
|
|||||||
const [audio, screen] = await Promise.all([audioP, screenP]);
|
const [audio, screen] = await Promise.all([audioP, screenP]);
|
||||||
AI_AUDIO = audio.segments || [];
|
AI_AUDIO = audio.segments || [];
|
||||||
AI_SCREEN = screen.segments || [];
|
AI_SCREEN = screen.segments || [];
|
||||||
renderAiPanel('aiAudioList', AI_AUDIO, audio.error);
|
renderAiPanel('aiAudioList', AI_AUDIO, audio.error, audio.note);
|
||||||
renderAiPanel('aiScreenList', AI_SCREEN, screen.error);
|
renderAiPanel('aiScreenList', AI_SCREEN, screen.error, screen.note);
|
||||||
|
|
||||||
status.style.color = '#4ade80';
|
status.style.color = '#4ade80';
|
||||||
status.textContent = '완료 · 음성 ' + AI_AUDIO.length + '개 · 화면 ' + AI_SCREEN.length + '개'
|
status.textContent = '완료 · 음성 ' + AI_AUDIO.length + '개 · 화면 ' + AI_SCREEN.length + '개'
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user