bcup2

import React, { useEffect, useRef } from 'react'; import Hls from 'hls.js'; import './Player.css'; // Class để quản lý subtitle SRT class SubtitleManager { constructor(videoElement, subtitleDisplay) { this.video = videoElement; this.display = subtitleDisplay; this.subtitles = []; this.currentSubIndex = -1; this.isEnabled = false; } async loadSRT(url) { try { const response = await fetch(url); if (!response.ok) throw new Error('Không tải được file SRT'); const text = await response.text(); this.subtitles = this.parseSRT(text); this.updateSubtitles(); return true; } catch (error) { console.error(error.message); return false; } } parseSRT(srtText) { const cues = []; const blocks = srtText.trim().split(/\n\s*\n/); for (const block of blocks) { const lines = block.split('\n').map(line => line.trim()); if (lines.length < 2 || !lines[1].includes(' --> ')) continue; const timeLine = lines[1]; const [startStr, endStr] = timeLine.split(' --> '); const text = lines.slice(2).join('\n'); const start = this.timeToSeconds(startStr); const end = this.timeToSeconds(endStr); if (!isNaN(start) && !isNaN(end) && start < end) { cues.push({ start, end, text }); } } return cues.sort((a, b) => a.start - b.start); } timeToSeconds(timeStr) { try { const [hours, minutes, seconds] = timeStr.replace(',', '.').split(':'); return (+hours * 3600) + (+minutes * 60) + (+seconds); } catch (e) { return NaN; } } updateSubtitles() { if (!this.isEnabled) { this.display.innerHTML = ''; return; } const currentTime = this.video.currentTime; let subtitleShown = false; for (let i = 0; i < this.subtitles.length; i++) { const cue = this.subtitles[i]; if (currentTime >= cue.start && currentTime < cue.end) { this.display.innerHTML = ''; const subElement = document.createElement('span'); subElement.className = 'subtitle-text'; subElement.textContent = cue.text; this.display.appendChild(subElement); this.currentSubIndex = i; subtitleShown = true; break; } } if (!subtitleShown) { this.display.innerHTML = ''; this.currentSubIndex = -1; } } toggleSubtitles() { this.isEnabled = !this.isEnabled; this.updateSubtitles(); } initialize() { this.video.addEventListener('timeupdate', () => this.updateSubtitles()); this.video.addEventListener('seeked', () => this.updateSubtitles()); this.video.addEventListener('play', () => this.updateSubtitles()); this.video.addEventListener('pause', () => this.updateSubtitles()); } } const Playerconcak = () => { const playPauseBtnRef = useRef(null); const theaterBtnRef = useRef(null); const fullScreenBtnRef = useRef(null); const miniPlayerBtnRef = useRef(null); const muteBtnRef = useRef(null); const captionsBtnRef = useRef(null); const speedBtnRef = useRef(null); const currentTimeElemRef = useRef(null); const totalTimeElemRef = useRef(null); const previewImgRef = useRef(null); const thumbnailImgRef = useRef(null); const volumeSliderRef = useRef(null); const videoContainerRef = useRef(null); const timelineContainerRef = useRef(null); const videoRef = useRef(null); const videoControlsContainerRef = useRef(null); const videoListRef = useRef(null); const titleeRef = useRef(null); const subtitleDisplayRef = useRef(null); let isScrubbing = false; let wasPaused; let timeout; // Hàm cập nhật kích thước font subtitle const updateFontSize = () => { const videoContainer = videoContainerRef.current; if (!videoContainer) return; const width = window.innerWidth; let fontSize = '24px'; // Default font size const breakpoints = { 359: '14px', 360: '14px', 375: '14px', 412: '15px', 480: '16px', 640: '17px', 768: '18px', 800: '18px', 1024: '28px', 1280: '30px', 1366: '35px', 1440: '55px', 1920: '65px', }; for (let breakpoint in breakpoints) { if (width <= parseInt(breakpoint)) { fontSize = breakpoints[breakpoint]; break; } } // Cập nhật CSS variable trên video-container videoContainer.style.setProperty('--subtitle-font-size', fontSize); }; useEffect(() => { const playPauseBtn = playPauseBtnRef.current; const theaterBtn = theaterBtnRef.current; const fullScreenBtn = fullScreenBtnRef.current; const miniPlayerBtn = miniPlayerBtnRef.current; const muteBtn = muteBtnRef.current; const captionsBtn = captionsBtnRef.current; const speedBtn = speedBtnRef.current; const currentTimeElem = currentTimeElemRef.current; const totalTimeElem = totalTimeElemRef.current; const previewImg = previewImgRef.current; const thumbnailImg = thumbnailImgRef.current; const volumeSlider = volumeSliderRef.current; const videoContainer = videoContainerRef.current; const timelineContainer = timelineContainerRef.current; const video = videoRef.current; const videoControlsContainer = videoControlsContainerRef.current; const videoList = videoListRef.current; const titlee = titleeRef.current; const subtitleDisplay = subtitleDisplayRef.current; let hls = null; if (Hls.isSupported()) { hls = new Hls(); hls.loadSource(video.src); hls.attachMedia(video); } else if (video.canPlayType('application/vnd.apple.mpegurl')) { video.src = video.src; } const subtitleManager = new SubtitleManager(video, subtitleDisplay); subtitleManager.loadSRT( 'https://raw.githubusercontent.com/Huyenuiio/Data-src/main/video/yoru%20no%20kurage/tap1/%5BSubtitleTools.com%5D%20%5BErai-raws%5D%20Yoru%20no%20Kurage%20wa%20Oyogenai%20-%2001%20%5B480p%5D%5B26196FA3%5D.srt' ).then(() => subtitleManager.initialize()); // Gọi updateFontSize lần đầu updateFontSize(); // Thêm listener cho resize window.addEventListener('resize', updateFontSize); const handleKeydown = (e) => { const tagName = document.activeElement.tagName.toLowerCase(); if (tagName === 'input') return; switch (e.key.toLowerCase()) { case ' ': if (tagName === 'button') return; case 'k': togglePlay(); break; case 'f': toggleFullScreenMode(); break; case 't': toggleTheaterMode(); break; case 'i': toggleMiniPlayerMode(); break; case 'm': toggleMute(); break; case 'arrowleft': case 'j': skip(-5); break; case 'arrowright': case 'l': skip(5); break; case 'c': toggleCaptions(); break; default: break; } }; const handleTimelineUpdate = (e) => { const rect = timelineContainer.getBoundingClientRect(); const percent = Math.min(Math.max(0, e.x - rect.x), rect.width) / rect.width; const previewImgNumber = Math.max(1, Math.floor((percent * video.duration) / 10)); const previewImgSrc = `assets/previewImgs/preview${previewImgNumber}.jpg`; if (previewImg) { previewImg.src = previewImgSrc; } timelineContainer.style.setProperty('--preview-position', percent); if (isScrubbing && thumbnailImg) { e.preventDefault(); thumbnailImg.src = previewImgSrc; timelineContainer.style.setProperty('--progress-position', percent); } }; const toggleScrubbing = (e) => { const rect = timelineContainer.getBoundingClientRect(); const percent = Math.min(Math.max(0, e.x - rect.x), rect.width) / rect.width; isScrubbing = (e.buttons & 1) === 1; videoContainer.classList.toggle('scrubbing', isScrubbing); if (isScrubbing) { wasPaused = video.paused; video.pause(); } else { video.currentTime = percent * video.duration; if (!wasPaused) video.play(); } handleTimelineUpdate(e); }; const changePlaybackSpeed = () => { let newPlaybackRate = video.playbackRate + 0.25; if (newPlaybackRate > 2) newPlaybackRate = 0.25; video.playbackRate = newPlaybackRate; speedBtn.textContent = `${newPlaybackRate}x`; }; const toggleCaptions = () => { subtitleManager.toggleSubtitles(); videoContainer.classList.toggle('captions', subtitleManager.isEnabled); }; const formatDuration = (time) => { const seconds = Math.floor(time % 60); const minutes = Math.floor(time / 60) % 60; const hours = Math.floor(time / 3600); const leadingZeroFormatter = new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }); return hours === 0 ? `${minutes}:${leadingZeroFormatter.format(seconds)}` : `${hours}:${leadingZeroFormatter.format(minutes)}:${leadingZeroFormatter.format(seconds)}`; }; const skip = (duration) => { video.currentTime += duration; }; const toggleMute = () => { video.muted = !video.muted; }; const handleVolumeChange = () => { volumeSlider.value = video.volume; let volumeLevel; if (video.muted || video.volume === 0) { volumeSlider.value = 0; volumeLevel = 'muted'; } else if (video.volume >= 0.5) { volumeLevel = 'high'; } else { volumeLevel = 'low'; } videoContainer.dataset.volumeLevel = volumeLevel; }; const toggleTheaterMode = () => { videoContainer.classList.toggle('theater'); }; const toggleFullScreenMode = () => { if (!document.fullscreenElement) { videoContainer.requestFullscreen(); } else { document.exitFullscreen(); } }; const toggleMiniPlayerMode = () => { if (videoContainer.classList.contains('mini-player')) { document.exitPictureInPicture(); } else { video.requestPictureInPicture(); } }; const togglePlay = () => { video.paused ? video.play() : video.pause(); }; const showControls = () => { videoControlsContainer.style.opacity = '1'; clearTimeout(timeout); }; const hideControls = () => { timeout = setTimeout(() => { videoControlsContainer.style.opacity = '0'; if (subtitleManager.isEnabled) { subtitleDisplay.style.opacity = '1'; } }, 2000); }; const handleVideoSelect = (e) => { const vidElement = e.currentTarget; const videoElement = vidElement.querySelector('video'); const newSrc = videoElement.getAttribute('src'); const newTitle = vidElement.querySelector('.title').textContent; const newSubtitle = videoElement.getAttribute('data-subtitle'); if (Hls.isSupported() && hls) { hls.destroy(); hls = new Hls(); hls.loadSource(newSrc); hls.attachMedia(video); video.play(); } else if (video.canPlayType('application/vnd.apple.mpegurl')) { video.src = newSrc; video.load(); video.play(); } const allVidElements = videoList.querySelectorAll('.vid'); allVidElements.forEach((vid) => vid.classList.remove('active')); vidElement.classList.add('active'); if (titlee) { titlee.textContent = newTitle; } if (newSubtitle) { subtitleManager.loadSRT(newSubtitle); } else { subtitleManager.subtitles = []; subtitleManager.updateSubtitles(); } }; document.addEventListener('keydown', handleKeydown); timelineContainer.addEventListener('mousemove', handleTimelineUpdate); timelineContainer.addEventListener('mousedown', toggleScrubbing); document.addEventListener('mouseup', (e) => { if (isScrubbing) toggleScrubbing(e); }); document.addEventListener('mousemove', (e) => { if (isScrubbing) handleTimelineUpdate(e); }); speedBtn.addEventListener('click', changePlaybackSpeed); captionsBtn.addEventListener('click', toggleCaptions); video.addEventListener('loadeddata', () => { totalTimeElem.textContent = formatDuration(video.duration); }); video.addEventListener('timeupdate', () => { currentTimeElem.textContent = formatDuration(video.currentTime); const percent = video.currentTime / video.duration; timelineContainer.style.setProperty('--progress-position', percent); }); muteBtn.addEventListener('click', toggleMute); volumeSlider.addEventListener('input', (e) => { video.volume = e.target.value; video.muted = e.target.value === 0; }); video.addEventListener('volumechange', handleVolumeChange); theaterBtn.addEventListener('click', toggleTheaterMode); fullScreenBtn.addEventListener('click', toggleFullScreenMode); miniPlayerBtn.addEventListener('click', toggleMiniPlayerMode); document.addEventListener('fullscreenchange', () => { videoContainer.classList.toggle('full-screen', !!document.fullscreenElement); }); video.addEventListener('enterpictureinpicture', () => { videoContainer.classList.add('mini-player'); }); video.addEventListener('leavepictureinpicture', () => { videoContainer.classList.remove('mini-player'); }); playPauseBtn.addEventListener('click', togglePlay); video.addEventListener('click', togglePlay); video.addEventListener('play', () => { videoContainer.classList.remove('paused'); }); video.addEventListener('pause', () => { videoContainer.classList.add('paused'); }); document.addEventListener('mousemove', showControls); document.addEventListener('keydown', showControls); videoControlsContainer.addEventListener('mouseover', showControls); document.addEventListener('mouseout', hideControls); document.addEventListener('keyup', hideControls); videoControlsContainer.addEventListener('mouseleave', hideControls); const vidElements = videoList.querySelectorAll('.vid'); vidElements.forEach((vid) => { vid.addEventListener('click', handleVideoSelect); }); return () => { document.removeEventListener('keydown', handleKeydown); timelineContainer.removeEventListener('mousemove', handleTimelineUpdate); timelineContainer.removeEventListener('mousedown', toggleScrubbing); document.removeEventListener('mouseup', toggleScrubbing); document.removeEventListener('mousemove', handleTimelineUpdate); speedBtn.removeEventListener('click', changePlaybackSpeed); captionsBtn.removeEventListener('click', toggleCaptions); video.removeEventListener('loadeddata', () => {}); video.removeEventListener('timeupdate', () => {}); muteBtn.removeEventListener('click', toggleMute); volumeSlider.removeEventListener('input', () => {}); video.removeEventListener('volumechange', handleVolumeChange); theaterBtn.removeEventListener('click', toggleTheaterMode); fullScreenBtn.removeEventListener('click', toggleFullScreenMode); miniPlayerBtn.removeEventListener('click', toggleMiniPlayerMode); document.removeEventListener('fullscreenchange', () => {}); video.removeEventListener('enterpictureinpicture', () => {}); video.removeEventListener('leavepictureinpicture', () => {}); playPauseBtn.removeEventListener('click', togglePlay); video.removeEventListener('click', togglePlay); video.removeEventListener('play', () => {}); video.removeEventListener('pause', () => {}); document.removeEventListener('mousemove', showControls); document.removeEventListener('keydown', showControls); videoControlsContainer.removeEventListener('mouseover', showControls); document.removeEventListener('mouseout', hideControls); document.removeEventListener('keyup', hideControls); videoControlsContainer.removeEventListener('mouseleave', hideControls); vidElements.forEach((vid) => { vid.removeEventListener('click', handleVideoSelect); }); window.removeEventListener('resize', updateFontSize); // Cleanup resize listener if (hls) { hls.destroy(); } }; }, []); return (
thumbnail preview
0:00
/
{/* Các video khác giữ nguyên và thêm data-subtitle nếu cần */}
Tập 1 - "Ta yêu em" và Tự động thủ kí nhân hình
); }; export default Playerconcak;
Huyền

Một Blog Anime chia sẻ những bộ anime hay download về để xem chất lượng cao nhất. neyuhv.blogspot.com does not host any files, it merely links to 3rd party services. Legal issues should be taken up with the file hosts and providers. neyuhv.blogspot.com is not responsible for any media files shown by the video providers.

Mới hơn Cũ hơn