fixed again
This commit is contained in:
@@ -40,6 +40,7 @@ const statusText = document.getElementById('status');
|
||||
const sentenceList = document.getElementById('sentences');
|
||||
let dubAudio = new Audio();
|
||||
dubAudio.preload = 'auto';
|
||||
dubAudio.playbackRate = 1;
|
||||
let segments = [];
|
||||
let selectedIndex = 0;
|
||||
let mode = null;
|
||||
@@ -58,39 +59,34 @@ function playOriginal() {
|
||||
mode = 'original';
|
||||
originalButton.classList.add('active');
|
||||
video.muted = false;
|
||||
playSelectedSentence();
|
||||
const sentence = segments[selectedIndex];
|
||||
if (!sentence) return;
|
||||
video.currentTime = sentence.start_ms / 1000;
|
||||
video.play().catch(() => {});
|
||||
}
|
||||
|
||||
function syncDub() {
|
||||
function highlightActiveSentence() {
|
||||
const timeMs = video.currentTime * 1000;
|
||||
const sentence = segments[selectedIndex];
|
||||
if (sentence && timeMs >= sentence.end_ms + PLAY_EXTEND_MS) {
|
||||
video.pause();
|
||||
dubAudio.pause();
|
||||
video.currentTime = (sentence.end_ms + PLAY_EXTEND_MS) / 1000;
|
||||
return;
|
||||
}
|
||||
const active = (sentence && timeMs >= sentence.start_ms && timeMs < sentence.end_ms + PLAY_EXTEND_MS)
|
||||
? sentence
|
||||
const selected = segments[selectedIndex];
|
||||
const active = (selected && timeMs >= selected.start_ms && timeMs < selected.end_ms + PLAY_EXTEND_MS)
|
||||
? selected
|
||||
: segments.find(item => timeMs >= item.start_ms && timeMs < item.end_ms);
|
||||
document.querySelectorAll('.sentence').forEach((node, index) => {
|
||||
node.classList.toggle('active-sentence', segments[index] === active);
|
||||
});
|
||||
if (mode !== 'dubbing') return;
|
||||
if (!active) {
|
||||
dubAudio.pause();
|
||||
return;
|
||||
return { timeMs, selected };
|
||||
}
|
||||
|
||||
function onVideoTimeUpdate() {
|
||||
const { timeMs, selected } = highlightActiveSentence();
|
||||
if (!selected) return;
|
||||
// Only bound the muted video window. Dubbing audio plays at its original speed/duration.
|
||||
if (timeMs >= selected.end_ms + PLAY_EXTEND_MS) {
|
||||
video.pause();
|
||||
if (mode === 'original') {
|
||||
video.currentTime = (selected.end_ms + PLAY_EXTEND_MS) / 1000;
|
||||
}
|
||||
}
|
||||
const source = active.audio_url;
|
||||
if (dubAudio.dataset.src !== source) {
|
||||
dubAudio.dataset.src = source;
|
||||
dubAudio.src = source;
|
||||
}
|
||||
const target = Math.max(0, timeMs - active.start_ms) / 1000;
|
||||
if (Math.abs(dubAudio.currentTime - target) > 0.25 && Number.isFinite(target)) {
|
||||
dubAudio.currentTime = target;
|
||||
}
|
||||
if (video.paused || dubAudio.paused) dubAudio.play().catch(() => {});
|
||||
}
|
||||
|
||||
function playDubbing() {
|
||||
@@ -98,17 +94,33 @@ function playDubbing() {
|
||||
mode = 'dubbing';
|
||||
dubbingButton.classList.add('active');
|
||||
video.muted = true;
|
||||
playSelectedSentence();
|
||||
const sentence = segments[selectedIndex];
|
||||
if (!sentence || !sentence.audio_url) return;
|
||||
video.currentTime = sentence.start_ms / 1000;
|
||||
dubAudio.pause();
|
||||
dubAudio.playbackRate = 1;
|
||||
dubAudio.src = sentence.audio_url;
|
||||
const startBoth = () => {
|
||||
try { dubAudio.currentTime = 0; } catch (_) {}
|
||||
video.play().catch(() => {});
|
||||
dubAudio.play().catch(() => {});
|
||||
};
|
||||
if (dubAudio.readyState >= 2) {
|
||||
startBoth();
|
||||
} else {
|
||||
dubAudio.onloadeddata = () => {
|
||||
dubAudio.onloadeddata = null;
|
||||
startBoth();
|
||||
};
|
||||
dubAudio.load();
|
||||
}
|
||||
}
|
||||
|
||||
function playSelectedSentence() {
|
||||
const sentence = segments[selectedIndex];
|
||||
if (!sentence) return;
|
||||
video.currentTime = sentence.start_ms / 1000;
|
||||
dubAudio.dataset.src = '';
|
||||
dubAudio.src = '';
|
||||
video.play().catch(() => {});
|
||||
}
|
||||
dubAudio.addEventListener('ended', () => {
|
||||
if (mode === 'dubbing') {
|
||||
video.pause();
|
||||
}
|
||||
});
|
||||
|
||||
async function load() {
|
||||
try {
|
||||
@@ -154,10 +166,9 @@ async function load() {
|
||||
});
|
||||
originalButton.onclick = playOriginal;
|
||||
dubbingButton.onclick = playDubbing;
|
||||
video.addEventListener('timeupdate', syncDub);
|
||||
video.addEventListener('play', () => { if (mode === 'dubbing') syncDub(); });
|
||||
video.addEventListener('pause', () => dubAudio.pause());
|
||||
video.addEventListener('seeked', syncDub);
|
||||
video.addEventListener('timeupdate', onVideoTimeUpdate);
|
||||
video.addEventListener('seeked', highlightActiveSentence);
|
||||
// Keep dubbing audio independent: do not pause/seek it to force video alignment.
|
||||
} catch (error) {
|
||||
statusText.textContent = `加载失败:${error.message}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user