Добавлена синхронизация текущего времени воспроизведения видео с сервером и улучшена обработка потоковой передачи видео

This commit is contained in:
kromskii2 committed 2025-01-05 12:17:22 +07:00
1 parent ea08781fb6
commit 7b8c8b7f56
2 files changed
+73 -62

No files matched your search

+57 -30
View File
@@ -5,44 +5,71 @@ const path = require('path');
const app = express(); const app = express();
const PORT = 3000; const PORT = 3000;
// Путь к видеофайлу // Укажите путь к фильму
const videoPath = path.join(__dirname, 'movie.mp4'); // Замените 'movie.mp4' на ваш фильм const videoPath = path.join(__dirname, 'movie.mp4');
const serverStartTime = Date.now(); // Время запуска сервера
// Маршрут для главной страницы // Определите битрейт (бит в секунду) фильма
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) => { 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; const range = req.headers.range;
if (!range) { if (range) {
return res.status(400).send('Requires Range header'); 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) => { app.get('/time', (req, res) => {
const elapsedTime = (Date.now() - serverStartTime) / 1000; // Время с момента запуска сервера в секундах const timeElapsedSinceStart = (Date.now() - serverStartTime) / 1000; // В секундах
res.json({ currentTime: elapsedTime }); const currentPlaybackTime = Math.min(timeElapsedSinceStart, videoDurationInSeconds);
res.json({ currentPlaybackTime });
}); });
// Запуск сервера app.listen(PORT, () => {
app.listen(PORT, '0.0.0.0', () => {
console.log(`Server is running on http://localhost:${PORT}`); console.log(`Server is running on http://localhost:${PORT}`);
console.log(`Video duration is approximately ${Math.round(videoDurationInSeconds)} seconds.`);
}); });
+16 -32
View File
@@ -1,4 +1,4 @@
document.addEventListener('DOMContentLoaded', async () => { document.addEventListener('DOMContentLoaded', () => {
const video = document.getElementById('video'); const video = document.getElementById('video');
const playPauseBtn = document.getElementById('playPauseBtn'); const playPauseBtn = document.getElementById('playPauseBtn');
const muteBtn = document.getElementById('muteBtn'); const muteBtn = document.getElementById('muteBtn');
@@ -7,33 +7,14 @@ document.addEventListener('DOMContentLoaded', async () => {
const volumeSlider = document.querySelector('.volume-slider'); const volumeSlider = document.querySelector('.volume-slider');
const videoContainer = document.querySelector('.video-frame'); const videoContainer = document.querySelector('.video-frame');
// Получение текущего времени воспроизведения с сервера // Синхронизация времени
async function fetchCurrentTime() { async function syncTime() {
try { const response = await fetch('/time'); // Запрос текущего времени
const response = await fetch('/current-time'); const data = await response.json();
if (!response.ok) { video.currentTime = data.currentPlaybackTime; // Устанавливаем время
throw new Error('Failed to fetch current time'); video.play(); // Автоматически начинаем воспроизведение
}
const data = await response.json();
return data.currentTime; // Сервер возвращает время в секундах
} catch (error) {
console.error('Error fetching current time:', error);
return 0; // Если ошибка, начать воспроизведение с начала
}
} }
// Синхронизация времени видео с сервером
async function syncVideo() {
const currentTime = await fetchCurrentTime(); // Получаем время от сервера
video.currentTime = currentTime; // Устанавливаем текущее время видео
}
// Событие, когда метаданные видео загружены
video.addEventListener('loadedmetadata', async () => {
await syncVideo(); // Синхронизация видео с сервера
video.play(); // Запускаем видео
});
// Play/Pause // Play/Pause
function togglePlay() { function togglePlay() {
if (video.paused) { if (video.paused) {
@@ -45,18 +26,18 @@ document.addEventListener('DOMContentLoaded', async () => {
} }
} }
// Обновление прогресса // Update progress bar
function updateProgress() { function updateProgress() {
const progress = (video.currentTime / video.duration) * 100; const progress = (video.currentTime / video.duration) * 100;
progressBar.value = progress; progressBar.value = progress;
} }
// Установка времени видео через прогресс-бар // Set video time based on progress bar
function setVideoProgress() { function setVideoProgress() {
video.currentTime = (progressBar.value * video.duration) / 100; video.currentTime = (progressBar.value * video.duration) / 100;
} }
// Громкость // Volume controls
function updateVolume() { function updateVolume() {
video.volume = volumeSlider.value; video.volume = volumeSlider.value;
updateVolumeIcon(); updateVolumeIcon();
@@ -89,7 +70,7 @@ document.addEventListener('DOMContentLoaded', async () => {
} }
} }
// Слушатели событий // Event listeners
playPauseBtn.addEventListener('click', togglePlay); playPauseBtn.addEventListener('click', togglePlay);
video.addEventListener('click', togglePlay); video.addEventListener('click', togglePlay);
video.addEventListener('timeupdate', updateProgress); video.addEventListener('timeupdate', updateProgress);
@@ -98,7 +79,10 @@ document.addEventListener('DOMContentLoaded', async () => {
volumeSlider.addEventListener('input', updateVolume); volumeSlider.addEventListener('input', updateVolume);
fullscreenBtn.addEventListener('click', toggleFullscreen); fullscreenBtn.addEventListener('click', toggleFullscreen);
// Поддержка клавиш на клавиатуре // Синхронизация времени при загрузке страницы
syncTime();
// Keyboard controls
document.addEventListener('keydown', (e) => { document.addEventListener('keydown', (e) => {
if (e.code === 'Space') { if (e.code === 'Space') {
e.preventDefault(); 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');
}); });