将视频字幕嵌入

This commit is contained in:
CN-JS-HuiBai
2026-04-09 23:31:32 +08:00
parent 116db4cb0f
commit be953b1621
4 changed files with 216 additions and 17 deletions

View File

@@ -23,6 +23,8 @@ document.addEventListener('DOMContentLoaded', () => {
const themeSelector = document.getElementById('theme-selector');
const videoListHeader = document.getElementById('video-list-header');
const logoutBtn = document.getElementById('logout-btn');
const subtitlePanel = document.getElementById('subtitle-panel');
const subtitleSelector = document.getElementById('subtitle-selector');
const topBannerTitle = document.getElementById('top-banner-title');
const playBtn = document.getElementById('play-btn');
const topBanner = document.getElementById('top-banner');
@@ -143,6 +145,12 @@ document.addEventListener('DOMContentLoaded', () => {
const decoder = 'auto';
const encoder = encoderSelect?.value || 'h264_rkmpp';
let streamUrl = `/api/hls/playlist.m3u8?bucket=${encodeURIComponent(selectedBucket)}&key=${encodeURIComponent(selectedKey)}&decoder=${encodeURIComponent(decoder)}&encoder=${encodeURIComponent(encoder)}`;
const subIndex = subtitleSelector?.value;
if (subIndex && subIndex !== '-1') {
streamUrl += `&subtitleIndex=${encodeURIComponent(subIndex)}`;
}
const sessionId = localStorage.getItem('sessionId');
if (sessionId) {
streamUrl += `&sessionId=${encodeURIComponent(sessionId)}`;
@@ -265,16 +273,22 @@ document.addEventListener('DOMContentLoaded', () => {
};
const subscribeToKey = (key) => {
subscribedKey = key;
let subscriptionKey = key;
const subIndex = subtitleSelector?.value;
if (subIndex && subIndex !== '-1') {
subscriptionKey = `${key}-sub${subIndex}`;
}
subscribedKey = subscriptionKey;
if (wsConnected) {
sendWsMessage({ type: 'subscribe', key });
sendWsMessage({ type: 'subscribe', key: subscriptionKey });
}
};
const handleWsMessage = (event) => {
try {
const message = JSON.parse(event.data);
if (message.key !== currentVideoKey) return;
if (message.key !== subscribedKey) return;
if (message.type === 'duration' && message.duration) {
videoDuration = message.duration;
@@ -891,6 +905,51 @@ document.addEventListener('DOMContentLoaded', () => {
nowPlaying.classList.remove('hidden');
currentVideoTitle.textContent = key.split('/').pop();
// Fetch subtitle metadata
fetchVideoMetadata(selectedBucket, key);
};
const handleSubtitleChange = () => {
if (!selectedKey) return;
subscribeToKey(selectedKey);
};
if (subtitleSelector) {
subtitleSelector.addEventListener('change', handleSubtitleChange);
}
const fetchVideoMetadata = async (bucket, key) => {
if (!subtitlePanel || !subtitleSelector) return;
subtitlePanel.classList.add('hidden');
subtitleSelector.innerHTML = '<option value="-1">正在加载字幕...</option>';
subtitleSelector.disabled = true;
try {
const res = await fetch(`/api/video-metadata?bucket=${encodeURIComponent(bucket)}&key=${encodeURIComponent(key)}`, { headers: s3AuthHeaders });
if (!res.ok) throw new Error('Failed to fetch metadata');
const data = await res.json();
subtitleSelector.innerHTML = '<option value="-1">无字幕</option>';
if (data.subtitleStreams && data.subtitleStreams.length > 0) {
data.subtitleStreams.forEach(sub => {
const option = document.createElement('option');
option.value = sub.subIndex;
option.textContent = `[${sub.language}] ${sub.title}`;
subtitleSelector.appendChild(option);
});
subtitlePanel.classList.remove('hidden');
} else {
subtitleSelector.innerHTML = '<option value="-1">无嵌入字幕</option>';
subtitlePanel.classList.remove('hidden');
}
} catch (err) {
console.error('Fetch metadata failed:', err);
subtitleSelector.innerHTML = '<option value="-1">无法加载字幕</option>';
subtitlePanel.classList.remove('hidden');
} finally {
subtitleSelector.disabled = false;
}
};
const startTranscode = async () => {