Добавлена поддержка потоковой передачи видео с сервера и синхронизация текущего времени воспроизведения
This commit is contained in:
1 parent
d931cd50da
commit
0b6b6e23da
3 files changed
+141
-86
No files matched your search
+2
-2
@@ -29,9 +29,9 @@
|
||||
|
||||
<div class="video-container">
|
||||
<div class="video-frame">
|
||||
<video id="video" src="https://www.w3schools.com/html/mov_bbb.mp4">
|
||||
<video id="video" src="/video" controls>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</video>
|
||||
|
||||
<div class="video-controls">
|
||||
<div class="progress-container">
|
||||
|
||||
@@ -1,19 +1,48 @@
|
||||
const express = require('express');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const app = express();
|
||||
const PORT = 3000;
|
||||
|
||||
// Middleware для обслуживания статических файлов из папки "src" и корня приложения
|
||||
app.use('/src', express.static(path.join(__dirname, 'src')));
|
||||
// Путь к видеофайлу
|
||||
const videoPath = path.join(__dirname, 'movie.mp4'); // Замените 'movie.mp4' на ваш фильм
|
||||
const serverStartTime = Date.now(); // Время запуска сервера
|
||||
|
||||
// Маршрут для главной страницы
|
||||
app.use('/', express.static(path.join(__dirname, '')));
|
||||
|
||||
// Главная страница
|
||||
app.get('/', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, 'index.html'));
|
||||
// Маршрут для видео
|
||||
app.get('/video', (req, res) => {
|
||||
const range = req.headers.range;
|
||||
if (!range) {
|
||||
return res.status(400).send('Requires Range header');
|
||||
}
|
||||
|
||||
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.listen(PORT, '0.0.0.0', () => {
|
||||
console.log(`Server is running on http://0.0.0.0:${PORT}`);
|
||||
console.log(`Server is running on http://localhost:${PORT}`);
|
||||
});
|
||||
+104
-78
@@ -1,84 +1,110 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const video = document.getElementById('video');
|
||||
const playPauseBtn = document.getElementById('playPauseBtn');
|
||||
const muteBtn = document.getElementById('muteBtn');
|
||||
const fullscreenBtn = document.getElementById('fullscreenBtn');
|
||||
const progressBar = document.querySelector('.progress-bar');
|
||||
const volumeSlider = document.querySelector('.volume-slider');
|
||||
const videoContainer = document.querySelector('.video-frame');
|
||||
|
||||
// Play/Pause
|
||||
function togglePlay() {
|
||||
if (video.paused) {
|
||||
video.play();
|
||||
playPauseBtn.innerHTML = '<i class="fas fa-pause"></i>';
|
||||
} else {
|
||||
video.pause();
|
||||
playPauseBtn.innerHTML = '<i class="fas fa-play"></i>';
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const video = document.getElementById('video');
|
||||
const playPauseBtn = document.getElementById('playPauseBtn');
|
||||
const muteBtn = document.getElementById('muteBtn');
|
||||
const fullscreenBtn = document.getElementById('fullscreenBtn');
|
||||
const progressBar = document.querySelector('.progress-bar');
|
||||
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; // Если ошибка, начать воспроизведение с начала
|
||||
}
|
||||
|
||||
// Update progress bar
|
||||
function updateProgress() {
|
||||
const progress = (video.currentTime / video.duration) * 100;
|
||||
progressBar.value = progress;
|
||||
}
|
||||
|
||||
// Синхронизация времени видео с сервером
|
||||
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) {
|
||||
video.play();
|
||||
playPauseBtn.innerHTML = '<i class="fas fa-pause"></i>';
|
||||
} else {
|
||||
video.pause();
|
||||
playPauseBtn.innerHTML = '<i class="fas fa-play"></i>';
|
||||
}
|
||||
|
||||
// Set video time based on progress bar
|
||||
function setVideoProgress() {
|
||||
video.currentTime = (progressBar.value * video.duration) / 100;
|
||||
}
|
||||
|
||||
// Обновление прогресса
|
||||
function updateProgress() {
|
||||
const progress = (video.currentTime / video.duration) * 100;
|
||||
progressBar.value = progress;
|
||||
}
|
||||
|
||||
// Установка времени видео через прогресс-бар
|
||||
function setVideoProgress() {
|
||||
video.currentTime = (progressBar.value * video.duration) / 100;
|
||||
}
|
||||
|
||||
// Громкость
|
||||
function updateVolume() {
|
||||
video.volume = volumeSlider.value;
|
||||
updateVolumeIcon();
|
||||
}
|
||||
|
||||
function toggleMute() {
|
||||
video.muted = !video.muted;
|
||||
updateVolumeIcon();
|
||||
}
|
||||
|
||||
function updateVolumeIcon() {
|
||||
const icon = muteBtn.querySelector('i');
|
||||
if (video.muted || video.volume === 0) {
|
||||
icon.className = 'fas fa-volume-mute';
|
||||
volumeSlider.value = 0;
|
||||
} else {
|
||||
icon.className = 'fas fa-volume-up';
|
||||
volumeSlider.value = video.volume;
|
||||
}
|
||||
|
||||
// Volume controls
|
||||
function updateVolume() {
|
||||
video.volume = volumeSlider.value;
|
||||
updateVolumeIcon();
|
||||
}
|
||||
|
||||
// Fullscreen
|
||||
function toggleFullscreen() {
|
||||
if (!document.fullscreenElement) {
|
||||
videoContainer.requestFullscreen();
|
||||
fullscreenBtn.innerHTML = '<i class="fas fa-compress"></i>';
|
||||
} else {
|
||||
document.exitFullscreen();
|
||||
fullscreenBtn.innerHTML = '<i class="fas fa-expand"></i>';
|
||||
}
|
||||
|
||||
function toggleMute() {
|
||||
video.muted = !video.muted;
|
||||
updateVolumeIcon();
|
||||
}
|
||||
|
||||
// Слушатели событий
|
||||
playPauseBtn.addEventListener('click', togglePlay);
|
||||
video.addEventListener('click', togglePlay);
|
||||
video.addEventListener('timeupdate', updateProgress);
|
||||
progressBar.addEventListener('change', setVideoProgress);
|
||||
muteBtn.addEventListener('click', toggleMute);
|
||||
volumeSlider.addEventListener('input', updateVolume);
|
||||
fullscreenBtn.addEventListener('click', toggleFullscreen);
|
||||
|
||||
// Поддержка клавиш на клавиатуре
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.code === 'Space') {
|
||||
e.preventDefault();
|
||||
togglePlay();
|
||||
}
|
||||
|
||||
function updateVolumeIcon() {
|
||||
const icon = muteBtn.querySelector('i');
|
||||
if (video.muted || video.volume === 0) {
|
||||
icon.className = 'fas fa-volume-mute';
|
||||
volumeSlider.value = 0;
|
||||
} else {
|
||||
icon.className = 'fas fa-volume-up';
|
||||
volumeSlider.value = video.volume;
|
||||
}
|
||||
}
|
||||
|
||||
// Fullscreen
|
||||
function toggleFullscreen() {
|
||||
if (!document.fullscreenElement) {
|
||||
videoContainer.requestFullscreen();
|
||||
fullscreenBtn.innerHTML = '<i class="fas fa-compress"></i>';
|
||||
} else {
|
||||
document.exitFullscreen();
|
||||
fullscreenBtn.innerHTML = '<i class="fas fa-expand"></i>';
|
||||
}
|
||||
}
|
||||
|
||||
// Event listeners
|
||||
playPauseBtn.addEventListener('click', togglePlay);
|
||||
video.addEventListener('click', togglePlay);
|
||||
video.addEventListener('timeupdate', updateProgress);
|
||||
progressBar.addEventListener('change', setVideoProgress);
|
||||
muteBtn.addEventListener('click', toggleMute);
|
||||
volumeSlider.addEventListener('input', updateVolume);
|
||||
fullscreenBtn.addEventListener('click', toggleFullscreen);
|
||||
|
||||
// Keyboard controls
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.code === 'Space') {
|
||||
e.preventDefault();
|
||||
togglePlay();
|
||||
}
|
||||
});
|
||||
|
||||
// Log ready state
|
||||
console.log('Video player initialized');
|
||||
});
|
||||
});
|
||||
|
||||
console.log('Video player initialized and synced with the server.');
|
||||
});
|
||||
Reference in new issue
Block a user