From 7b8c8b7f56f56b3b76209f6f01551420488f8c8d Mon Sep 17 00:00:00 2001 From: kromskii2 Date: Sun, 5 Jan 2025 12:17:22 +0700 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=81=D0=B8=D0=BD=D1=85=D1=80=D0=BE=D0=BD=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D1=82=D0=B5=D0=BA=D1=83=D1=89?= =?UTF-8?q?=D0=B5=D0=B3=D0=BE=20=D0=B2=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=20=D0=B2=D0=BE=D1=81=D0=BF=D1=80=D0=BE=D0=B8=D0=B7=D0=B2=D0=B5?= =?UTF-8?q?=D0=B4=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B2=D0=B8=D0=B4=D0=B5=D0=BE?= =?UTF-8?q?=20=D1=81=20=D1=81=D0=B5=D1=80=D0=B2=D0=B5=D1=80=D0=BE=D0=BC=20?= =?UTF-8?q?=D0=B8=20=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B0=20=D0=BF?= =?UTF-8?q?=D0=BE=D1=82=D0=BE=D0=BA=D0=BE=D0=B2=D0=BE=D0=B9=20=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=B4=D0=B0=D1=87=D0=B8=20=D0=B2=D0=B8=D0=B4=D0=B5?= =?UTF-8?q?=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server.js | 87 +++++++++++++++++++++++++++++++++++------------------ src/main.js | 48 ++++++++++------------------- 2 files changed, 73 insertions(+), 62 deletions(-) diff --git a/server.js b/server.js index b78bcb5..1fd5b94 100644 --- a/server.js +++ b/server.js @@ -5,44 +5,71 @@ const path = require('path'); const app = express(); const PORT = 3000; -// Путь к видеофайлу -const videoPath = path.join(__dirname, 'movie.mp4'); // Замените 'movie.mp4' на ваш фильм -const serverStartTime = Date.now(); // Время запуска сервера +// Укажите путь к фильму +const videoPath = path.join(__dirname, 'movie.mp4'); -// Маршрут для главной страницы -app.use('/', express.static(path.join(__dirname, ''))); +// Определите битрейт (бит в секунду) фильма +const videoBitrate = 1000000; // Например, 1 Мбит/с (проверьте, используя ffmpeg) -// Маршрут для видео +// Рассчитаем продолжительность фильма (в секундах) +const videoFileSize = fs.statSync(videoPath).size; // Размер файла в байтах +const videoDurationInSeconds = videoFileSize / (videoBitrate / 8); // Длительность = Размер файла / Байт/секунду + +// Фиксируем момент запуска фильма +const serverStartTime = Date.now(); + +app.use(express.static(path.join(__dirname))); + +// Маршрут для передачи видео app.get('/video', (req, res) => { + const stat = fs.statSync(videoPath); + const fileSize = stat.size; + + // Рассчитываем текущую позицию фильма + const timeElapsedSinceStart = (Date.now() - serverStartTime) / 1000; // В секундах + const currentPlaybackTime = Math.min(timeElapsedSinceStart, videoDurationInSeconds); + + // Рассчитываем начальную позицию в байтах + const startByte = Math.floor((currentPlaybackTime / videoDurationInSeconds) * fileSize); + const range = req.headers.range; - if (!range) { - return res.status(400).send('Requires Range header'); + if (range) { + const parts = range.replace(/bytes=/, '').split('-'); + const start = parseInt(parts[0], 10); + const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1; + + const chunkSize = end - start + 1; + const file = fs.createReadStream(videoPath, { start, end }); + const head = { + 'Content-Range': `bytes ${start}-${end}/${fileSize}`, + 'Accept-Ranges': 'bytes', + 'Content-Length': chunkSize, + 'Content-Type': 'video/mp4', + }; + + res.writeHead(206, head); + file.pipe(res); + } else { + const file = fs.createReadStream(videoPath, { start: startByte }); + const head = { + 'Content-Length': fileSize - startByte, + 'Content-Type': 'video/mp4', + }; + + res.writeHead(200, head); + file.pipe(res); } - - const videoSize = fs.statSync(videoPath).size; - const CHUNK_SIZE = 10 ** 6; // 1MB - const start = Number(range.replace(/\D/g, '')); - const end = Math.min(start + CHUNK_SIZE, videoSize - 1); - - const videoStream = fs.createReadStream(videoPath, { start, end }); - - res.writeHead(206, { - 'Content-Range': `bytes ${start}-${end}/${videoSize}`, - 'Accept-Ranges': 'bytes', - 'Content-Length': end - start + 1, - 'Content-Type': 'video/mp4', - }); - - videoStream.pipe(res); }); -// API для получения текущего времени воспроизведения -app.get('/current-time', (req, res) => { - const elapsedTime = (Date.now() - serverStartTime) / 1000; // Время с момента запуска сервера в секундах - res.json({ currentTime: elapsedTime }); +// Маршрут для передачи текущего времени +app.get('/time', (req, res) => { + const timeElapsedSinceStart = (Date.now() - serverStartTime) / 1000; // В секундах + const currentPlaybackTime = Math.min(timeElapsedSinceStart, videoDurationInSeconds); + + res.json({ currentPlaybackTime }); }); -// Запуск сервера -app.listen(PORT, '0.0.0.0', () => { +app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); + console.log(`Video duration is approximately ${Math.round(videoDurationInSeconds)} seconds.`); }); diff --git a/src/main.js b/src/main.js index 40a3ca9..7c2730c 100644 --- a/src/main.js +++ b/src/main.js @@ -1,4 +1,4 @@ -document.addEventListener('DOMContentLoaded', async () => { +document.addEventListener('DOMContentLoaded', () => { const video = document.getElementById('video'); const playPauseBtn = document.getElementById('playPauseBtn'); const muteBtn = document.getElementById('muteBtn'); @@ -7,33 +7,14 @@ document.addEventListener('DOMContentLoaded', async () => { const volumeSlider = document.querySelector('.volume-slider'); const videoContainer = document.querySelector('.video-frame'); - // Получение текущего времени воспроизведения с сервера - async function fetchCurrentTime() { - try { - const response = await fetch('/current-time'); - if (!response.ok) { - throw new Error('Failed to fetch current time'); - } - const data = await response.json(); - return data.currentTime; // Сервер возвращает время в секундах - } catch (error) { - console.error('Error fetching current time:', error); - return 0; // Если ошибка, начать воспроизведение с начала - } + // Синхронизация времени + async function syncTime() { + const response = await fetch('/time'); // Запрос текущего времени + const data = await response.json(); + video.currentTime = data.currentPlaybackTime; // Устанавливаем время + video.play(); // Автоматически начинаем воспроизведение } - // Синхронизация времени видео с сервером - async function syncVideo() { - const currentTime = await fetchCurrentTime(); // Получаем время от сервера - video.currentTime = currentTime; // Устанавливаем текущее время видео - } - - // Событие, когда метаданные видео загружены - video.addEventListener('loadedmetadata', async () => { - await syncVideo(); // Синхронизация видео с сервера - video.play(); // Запускаем видео - }); - // Play/Pause function togglePlay() { if (video.paused) { @@ -45,18 +26,18 @@ document.addEventListener('DOMContentLoaded', async () => { } } - // Обновление прогресса + // Update progress bar function updateProgress() { const progress = (video.currentTime / video.duration) * 100; progressBar.value = progress; } - // Установка времени видео через прогресс-бар + // Set video time based on progress bar function setVideoProgress() { video.currentTime = (progressBar.value * video.duration) / 100; } - // Громкость + // Volume controls function updateVolume() { video.volume = volumeSlider.value; updateVolumeIcon(); @@ -89,7 +70,7 @@ document.addEventListener('DOMContentLoaded', async () => { } } - // Слушатели событий + // Event listeners playPauseBtn.addEventListener('click', togglePlay); video.addEventListener('click', togglePlay); video.addEventListener('timeupdate', updateProgress); @@ -98,7 +79,10 @@ document.addEventListener('DOMContentLoaded', async () => { volumeSlider.addEventListener('input', updateVolume); fullscreenBtn.addEventListener('click', toggleFullscreen); - // Поддержка клавиш на клавиатуре + // Синхронизация времени при загрузке страницы + syncTime(); + + // Keyboard controls document.addEventListener('keydown', (e) => { if (e.code === 'Space') { e.preventDefault(); @@ -106,5 +90,5 @@ document.addEventListener('DOMContentLoaded', async () => { } }); - console.log('Video player initialized and synced with the server.'); + console.log('Video player initialized'); });