From 345970410b5adb73f26ed989b5e6de54d9a77eb5 Mon Sep 17 00:00:00 2001 From: CN-JS-HuiBai Date: Thu, 2 Apr 2026 20:28:27 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86NEON=E7=A1=AC?= =?UTF-8?q?=E4=BB=B6=E7=BC=96=E7=A0=81=E5=99=A8=E9=80=89=E9=A1=B9=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=88=B7=E6=96=B0=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E3=80=81=E6=B8=85=E7=A9=BA=E4=B8=8B=E8=BD=BD=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E5=92=8C=E6=B8=85=E7=A9=BA=E8=BD=AC=E7=A0=81=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E7=9A=84=E6=8C=89=E9=92=AE=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/css/style.css | 30 ++++++++++++++++++++- public/index.html | 9 +++---- public/js/main.js | 54 ++++++++++++++++++++++++++++--------- server.js | 64 +++++++++++++++++++++++++++++++++++++++----- 4 files changed, 131 insertions(+), 26 deletions(-) diff --git a/public/css/style.css b/public/css/style.css index 267db5e..286b098 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -268,8 +268,36 @@ header p { .section-actions { display: flex; + flex-wrap: wrap; align-items: center; - gap: 0.5rem; + gap: 0.75rem; +} + +.action-btn { + border: none; + background: #2563eb; + color: #ffffff; + font-size: 0.95rem; + font-weight: 600; + padding: 0.75rem 1rem; + border-radius: 14px; + min-width: 140px; + cursor: pointer; + transition: background 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease; +} + +.action-btn:hover { + background: #1d4ed8; + transform: translateY(-1px); + box-shadow: 0 12px 24px rgba(37, 99, 235, 0.14); +} + +.action-btn.danger { + background: #dc2626; +} + +.action-btn.danger:hover { + background: #b91c1c; } .section-header h2 { diff --git a/public/index.html b/public/index.html index 9b82324..f525953 100644 --- a/public/index.html +++ b/public/index.html @@ -41,12 +41,9 @@

Available Videos

- - + + +
diff --git a/public/js/main.js b/public/js/main.js index 3501b87..549f997 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -2,7 +2,8 @@ document.addEventListener('DOMContentLoaded', () => { const videoListEl = document.getElementById('video-list'); const loadingSpinner = document.getElementById('loading-spinner'); const refreshBtn = document.getElementById('refresh-btn'); - const resetCacheBtn = document.getElementById('reset-cache-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'); @@ -196,16 +197,38 @@ document.addEventListener('DOMContentLoaded', () => { } }; - const resetCache = async () => { - if (!resetCacheBtn) return; - resetCacheBtn.disabled = true; - resetCacheBtn.title = 'Resetting cache...'; + const clearDownloadCache = async () => { + if (!clearDownloadCacheBtn) return; + clearDownloadCacheBtn.disabled = true; + clearDownloadCacheBtn.textContent = '清空中...'; + + try { + const res = await fetch('/api/clear-download-cache', { method: 'POST' }); + if (!res.ok) { + const data = await res.json().catch(() => ({})); + throw new Error(data.error || '清空下载缓存失败'); + } + alert('下载缓存已清空'); + } catch (err) { + console.error('Clear download cache failed:', err); + alert(`清空下载缓存失败: ${err.message}`); + } finally { + clearDownloadCacheBtn.disabled = false; + clearDownloadCacheBtn.textContent = '清空下载缓存'; + } + }; + + 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) { @@ -220,18 +243,19 @@ document.addEventListener('DOMContentLoaded', () => { resetProgress(); try { - const res = await fetch('/api/reset-cache', { method: 'POST' }); + const res = await fetch('/api/clear-transcode-cache', { method: 'POST' }); if (!res.ok) { const data = await res.json().catch(() => ({})); - throw new Error(data.error || 'Reset failed'); + throw new Error(data.error || '清空转码缓存失败'); } - await fetchVideos(); + await fetchVideos(selectedBucket); + alert('转码缓存已清空'); } catch (err) { - console.error('Reset cache failed:', err); - alert(`Reset cache failed: ${err.message}`); + console.error('Clear transcode cache failed:', err); + alert(`清空转码缓存失败: ${err.message}`); } finally { - resetCacheBtn.disabled = false; - resetCacheBtn.title = 'Reset Download Cache'; + clearTranscodeCacheBtn.disabled = false; + clearTranscodeCacheBtn.textContent = '清空转码缓存'; } }; @@ -481,6 +505,12 @@ document.addEventListener('DOMContentLoaded', () => { // Bind events refreshBtn.addEventListener('click', () => fetchVideos(selectedBucket)); + if (clearDownloadCacheBtn) { + clearDownloadCacheBtn.addEventListener('click', clearDownloadCache); + } + if (clearTranscodeCacheBtn) { + clearTranscodeCacheBtn.addEventListener('click', clearTranscodeCache); + } if (loginBtn) { loginBtn.addEventListener('click', login); } diff --git a/server.js b/server.js index ea8af31..670813d 100644 --- a/server.js +++ b/server.js @@ -148,20 +148,51 @@ wss.on('connection', (ws) => { ws.on('close', () => removeWsClient(ws)); }); -const clearMp4Cache = () => { - const mp4Dir = path.join(__dirname, 'public', 'mp4'); - if (!fs.existsSync(mp4Dir)) return; +const mp4BaseDir = path.join(__dirname, 'public', 'mp4'); + +const ensureDirectoryExists = (dirPath) => { + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); + } +}; + +const clearDownloadCache = () => { + const tmpDir = os.tmpdir(); try { - fs.rmSync(mp4Dir, { recursive: true, force: true }); + if (!fs.existsSync(tmpDir)) return; + const files = fs.readdirSync(tmpDir); + for (const file of files) { + if (file.startsWith('s3-input-') && file.endsWith('.tmp')) { + const filePath = path.join(tmpDir, file); + fs.rmSync(filePath, { force: true }); + } + } + } catch (err) { + console.error('Failed to clear download cache:', err); + throw err; + } +}; + +const clearMp4Cache = () => { + if (!fs.existsSync(mp4BaseDir)) return; + try { + fs.rmSync(mp4BaseDir, { recursive: true, force: true }); } catch (err) { if (typeof fs.rmdirSync === 'function') { - fs.rmdirSync(mp4Dir, { recursive: true }); + fs.rmdirSync(mp4BaseDir, { recursive: true }); } else { throw err; } } }; +const clearTranscodeCache = () => { + clearMp4Cache(); + Object.keys(progressMap).forEach((key) => delete progressMap[key]); +}; + +ensureDirectoryExists(mp4BaseDir); + // Endpoint to list available buckets app.get('/api/buckets', async (req, res) => { try { @@ -228,8 +259,7 @@ app.get('/api/config', (req, res) => { app.post('/api/reset-cache', (req, res) => { try { - clearMp4Cache(); - Object.keys(progressMap).forEach((key) => delete progressMap[key]); + clearTranscodeCache(); res.json({ message: 'Cache reset' }); } catch (error) { console.error('Error resetting cache:', error); @@ -237,6 +267,26 @@ app.post('/api/reset-cache', (req, res) => { } }); +app.post('/api/clear-download-cache', (req, res) => { + try { + clearDownloadCache(); + res.json({ message: 'Download cache cleared' }); + } catch (error) { + console.error('Error clearing download cache:', error); + res.status(500).json({ error: 'Failed to clear download cache', detail: error.message }); + } +}); + +app.post('/api/clear-transcode-cache', (req, res) => { + try { + clearTranscodeCache(); + res.json({ message: 'Transcode cache cleared' }); + } catch (error) { + console.error('Error clearing transcode cache:', error); + res.status(500).json({ error: 'Failed to clear transcode cache', detail: error.message }); + } +}); + // Endpoint to transcode S3 video streaming to MP4 app.post('/api/transcode', async (req, res) => { const { bucket, key, codec, encoder } = req.body;