使用流式视频传输
This commit is contained in:
@@ -3,7 +3,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const loadingSpinner = document.getElementById('loading-spinner');
|
||||
const refreshBtn = document.getElementById('refresh-btn');
|
||||
const clearDownloadCacheBtn = document.getElementById('clear-download-cache-btn');
|
||||
const clearTranscodeCacheBtn = document.getElementById('clear-transcode-cache-btn');
|
||||
const bucketSelect = document.getElementById('bucket-select');
|
||||
const loginScreen = document.getElementById('login-screen');
|
||||
const appContainer = document.getElementById('app-container');
|
||||
@@ -224,47 +223,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
};
|
||||
|
||||
const clearTranscodeCache = async () => {
|
||||
if (!clearTranscodeCacheBtn) return;
|
||||
clearTranscodeCacheBtn.disabled = true;
|
||||
clearTranscodeCacheBtn.textContent = '清空中...';
|
||||
|
||||
stopPolling();
|
||||
selectedKey = null;
|
||||
currentVideoKey = null;
|
||||
subscribedKey = null;
|
||||
if (transcodeBtn) {
|
||||
transcodeBtn.disabled = true;
|
||||
transcodeBtn.classList.add('hidden');
|
||||
}
|
||||
if (playBtn) {
|
||||
playBtn.classList.add('hidden');
|
||||
}
|
||||
if (playerOverlay) {
|
||||
playerOverlay.classList.remove('hidden');
|
||||
}
|
||||
if (nowPlaying) {
|
||||
nowPlaying.classList.add('hidden');
|
||||
}
|
||||
resetProgress();
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/clear-transcode-cache', { method: 'POST' });
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}));
|
||||
throw new Error(data.error || '清空转码缓存失败');
|
||||
}
|
||||
await fetchVideos(selectedBucket);
|
||||
alert('转码缓存已清空');
|
||||
} catch (err) {
|
||||
console.error('Clear transcode cache failed:', err);
|
||||
alert(`清空转码缓存失败: ${err.message}`);
|
||||
} finally {
|
||||
clearTranscodeCacheBtn.disabled = false;
|
||||
clearTranscodeCacheBtn.textContent = '清空转码缓存';
|
||||
}
|
||||
};
|
||||
|
||||
if (transcodeBtn) {
|
||||
transcodeBtn.addEventListener('click', () => {
|
||||
startTranscode();
|
||||
@@ -440,24 +398,24 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
stopPolling();
|
||||
transcodingOverlay.classList.remove('hidden');
|
||||
setProgress({ status: 'Starting download...', percent: 0, details: 'Starting download...' });
|
||||
setProgress({ status: 'Starting stream...', percent: 0, details: 'Starting stream...' });
|
||||
|
||||
try {
|
||||
const codec = codecSelect?.value || 'h264';
|
||||
const encoder = encoderSelect?.value || 'software';
|
||||
if (!selectedBucket) throw new Error('No bucket selected');
|
||||
const res = await fetch('/api/transcode', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', ...s3AuthHeaders },
|
||||
body: JSON.stringify({ bucket: selectedBucket, key: selectedKey, codec, encoder })
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (data.error) throw new Error(data.error);
|
||||
|
||||
if (!wsConnected) {
|
||||
pollForMp4Ready(selectedKey, data.mp4Url);
|
||||
}
|
||||
const streamUrl = `/api/stream?bucket=${encodeURIComponent(selectedBucket)}&key=${encodeURIComponent(selectedKey)}&codec=${encodeURIComponent(codec)}&encoder=${encodeURIComponent(encoder)}`;
|
||||
videoPlayer.src = streamUrl;
|
||||
videoPlayer.load();
|
||||
videoPlayer.addEventListener('loadedmetadata', () => {
|
||||
transcodingOverlay.classList.add('hidden');
|
||||
videoPlayer.classList.remove('hidden');
|
||||
if (playBtn) {
|
||||
playBtn.disabled = false;
|
||||
playBtn.textContent = 'Play';
|
||||
playBtn.classList.remove('hidden');
|
||||
}
|
||||
}, { once: true });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
transcodingOverlay.innerHTML = `<p style="color: #ef4444;">Transcode Failed: ${err.message}</p>`;
|
||||
@@ -496,36 +454,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Poll the backend to check if the generated MP4 file is accessible
|
||||
const pollForMp4Ready = (key, mp4Url) => {
|
||||
let attempts = 0;
|
||||
const maxAttempts = 120; // 60 seconds max wait for first segment
|
||||
const pollIntervalMs = 500;
|
||||
|
||||
currentPollInterval = setInterval(async () => {
|
||||
attempts++;
|
||||
try {
|
||||
const res = await fetch(`/api/status?key=${encodeURIComponent(key)}`);
|
||||
const data = await res.json();
|
||||
|
||||
if (data.progress) {
|
||||
setProgress(data.progress);
|
||||
}
|
||||
|
||||
if (data.ready) {
|
||||
stopPolling();
|
||||
setProgress({ status: 'Ready to play', percent: 100, details: 'Ready to play' });
|
||||
playMp4Stream(data.mp4Url);
|
||||
} else if (attempts >= maxAttempts) {
|
||||
stopPolling();
|
||||
transcodingOverlay.innerHTML = `<p style="color: #ef4444;">Timeout waiting for MP4 file.</p>`;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Poll Error:", err);
|
||||
}
|
||||
}, pollIntervalMs);
|
||||
};
|
||||
|
||||
const stopPolling = () => {
|
||||
if (currentPollInterval) {
|
||||
clearInterval(currentPollInterval);
|
||||
@@ -559,9 +487,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (clearDownloadCacheBtn) {
|
||||
clearDownloadCacheBtn.addEventListener('click', clearDownloadCache);
|
||||
}
|
||||
if (clearTranscodeCacheBtn) {
|
||||
clearTranscodeCacheBtn.addEventListener('click', clearTranscodeCache);
|
||||
}
|
||||
if (stopTranscodeBtn) {
|
||||
stopTranscodeBtn.addEventListener('click', stopTranscode);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user