diff --git a/public/js/main.js b/public/js/main.js index df96d0e..aee13f7 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -465,10 +465,12 @@ document.addEventListener('DOMContentLoaded', () => { showTranscodePhase(); }, 600); } else if (status === 'transcoding') { - if (transcodingOverlay) transcodingOverlay.classList.remove('hidden'); - if (videoPlayer) videoPlayer.classList.add('hidden'); hasDownloadCompleted = true; - showTranscodePhase(); + if (!isStreamActive) { + if (transcodingOverlay) transcodingOverlay.classList.remove('hidden'); + if (videoPlayer) videoPlayer.classList.add('hidden'); + showTranscodePhase(); + } const percent = Math.min(Math.max(Math.round(progress.percent || 0), 0), 100); updateTranscodeProgressBar(percent); transcodeProgressText.textContent = `${percent}%`; @@ -482,9 +484,11 @@ document.addEventListener('DOMContentLoaded', () => { statTime.textContent = progress.timemark ? `${progress.timemark}` : ''; } } else if (status === 'finished') { - if (transcodingOverlay) transcodingOverlay.classList.remove('hidden'); hasDownloadCompleted = true; - showTranscodePhase(); + if (!isStreamActive) { + if (transcodingOverlay) transcodingOverlay.classList.remove('hidden'); + showTranscodePhase(); + } updateTranscodeProgressBar(100); transcodeProgressText.textContent = '100%'; transcodeProgressFill.style.width = '100%'; @@ -1241,6 +1245,7 @@ document.addEventListener('DOMContentLoaded', () => { stopPolling(); resetPhases(); handleProgress({ status: 'downloading', percent: 0, downloadedBytes: 0, totalBytes: 0 }); + startPolling(); seekOffset = 0; videoDuration = 0; isStreamActive = false; @@ -1300,6 +1305,7 @@ document.addEventListener('DOMContentLoaded', () => { videoPlayer.pause(); videoPlayer.removeAttribute('src'); videoPlayer.load(); + stopPolling(); hideSeekBar(); hideCustomControls(); resetSubtitleTracks(); @@ -1329,6 +1335,35 @@ document.addEventListener('DOMContentLoaded', () => { } }; + const pollProgress = async () => { + if (!currentVideoKey) return; + try { + const res = await fetch(`/api/progress?key=${encodeURIComponent(currentVideoKey)}`); + if (!res.ok) return; + const data = await res.json(); + if (data?.progress) { + handleProgress(data.progress); + if (typeof data.progress.duration === 'number' && data.progress.duration > 0) { + videoDuration = data.progress.duration; + if (seekTotalTime) seekTotalTime.textContent = formatTime(videoDuration); + } + if (['finished', 'failed', 'cancelled'].includes(data.progress.status)) { + stopPolling(); + } + } + } catch (error) { + console.error('Progress polling failed:', error); + } + }; + + const startPolling = () => { + stopPolling(); + currentPollInterval = setInterval(() => { + pollProgress(); + }, 500); + pollProgress(); + }; + const playMp4Stream = (url) => { transcodingOverlay.classList.add('hidden'); videoPlayer.classList.remove('hidden'); diff --git a/server.js b/server.js index 187948f..b31fccd 100644 --- a/server.js +++ b/server.js @@ -803,6 +803,22 @@ app.post('/api/stop-transcode', (req, res) => { } }); +app.get('/api/progress', (req, res) => { + try { + const key = typeof req.query.key === 'string' ? req.query.key.trim() : ''; + if (!key) { + return res.status(400).json({ error: 'Video key is required' }); + } + + const progressKey = getProgressKey(key); + const progress = progressMap[progressKey] || null; + res.json({ progress }); + } catch (error) { + console.error('Error fetching progress:', error); + res.status(500).json({ error: 'Failed to fetch progress', detail: error.message }); + } +}); + app.get('/api/stream', async (req, res) => { const bucket = req.query.bucket; const key = req.query.key;