From 637a1d974039274aef826b4f78f9d221e4dde3b2 Mon Sep 17 00:00:00 2001 From: Pinapelz Date: Wed, 15 Nov 2023 13:35:28 -0800 Subject: feat: add support for drag and drop files --- src/app/page.tsx | 466 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 463 insertions(+), 3 deletions(-) (limited to 'src/app/page.tsx') diff --git a/src/app/page.tsx b/src/app/page.tsx index e4a8169..95196d2 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,5 +1,465 @@ "use client" -import React from 'react'; -import App from './App'; +import React, { useEffect, useRef, useState } from "react"; +import styled from "styled-components"; +import KaraokePlayer from "./components/KaraokePlayer"; +import { toast, ToastContainer } from "react-toastify"; +import "react-toastify/dist/ReactToastify.css"; +import { FaPlay, FaPause, FaVolumeUp, FaVolumeMute } from "react-icons/fa"; +import { CaptionsRenderer } from "react-srv3"; -export default App; \ No newline at end of file +// Srtyled components +const Root = styled.div` + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + display: flex; + flex-direction: column; + align-items: center; + background-color: #f5f5f5; +`; + +const FileInputContainer = styled.div` + margin-bottom: 20px; + display: flex; + justify-content: center; + gap: 20px; + padding: 10px; + border-radius: 5px; + background-color: #ffffff; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +`; + +const FileInput = styled.input` + padding: 10px 15px; + border-radius: 5px; + border: 1px solid #ddd; + cursor: pointer; + display: none; + font-family: Arial; + &:hover, + &:focus { + background-color: #eaeaea; + outline: none; + } +`; + +const FileInputLabel = styled.label` + padding: 10px 15px; + border-radius: 5px; + border: 1px solid #ddd; + cursor: pointer; + &:hover, + &:focus { + background-color: #eaeaea; + outline: none; + } +`; + +const StyledLink = styled.a` + font-size: 20px; + font-family: Arial; + text-decoration: none; + text-color: black; + &:hover { + text-decoration: underline; + } + `; + +function KaraokePage() { + const [currentMillisecond, setCurrentMillisecond] = useState(0); + const [lrcContent, setLrcContent] = useState(""); + const [videoUrl, setVideoUrl] = useState(""); + const [isPlaying, setIsPlaying] = useState(false); + const [showVolume, setShowVolume] = useState(false); + const [scrubValue, setScrubValue] = useState(0); + const [showFileInputs, setShowFileInputs] = useState(true); + const videoRef = useRef(null); + const [captionsText, setCaptionsText] = useState(""); + const [offset, setOffset] = useState("0"); + const [dragOver, setDragOver] = useState(false); + const [statusText, setStatusText] = useState("No video selected"); + + // Functions for handling file input changes + const handleLrcFileChange = (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (e) => { + setLrcContent(e.target?.result as string); + if (videoUrl) setShowFileInputs(false); + }; + reader.readAsText(file); + toast.success("LRC file loaded successfully", { autoClose: 2000 }); + } + }; + + const handleVideoFileChange = ( + event: React.ChangeEvent + ) => { + const file = event.target.files?.[0]; + if (file) { + const url = URL.createObjectURL(file); + setVideoUrl(url); + setCurrentMillisecond(0); + setScrubValue(0); + setIsPlaying(false); + toast.success("Video file loaded successfully", { autoClose: 2000 }); + } + }; + + const handleSrvFileChange = (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (e) => { + setCaptionsText(e.target?.result as string); + }; + reader.readAsText(file); + toast.success("SRV file loaded successfully", { autoClose: 2000 }); + } + }; + + // Side effects for keyboard shortcuts + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.code === "Space") { + handlePlayPause(); + } + if (e.code === "ArrowRight") { + const video = videoRef.current; + if (!video) return; + video.currentTime += 5; + } + if (e.code === "ArrowLeft") { + const video = videoRef.current; + if (!video) return; + video.currentTime -= 5; + } + }; + document.addEventListener("keydown", handleKeyDown); + return () => { + document.removeEventListener("keydown", handleKeyDown); + }; + }); + + // Side effects for the video itself + useEffect(() => { + const video = videoRef.current; + if (!video) return; + const syncLrcWithVideo = () => { + setCurrentMillisecond(video.currentTime * 1000 + parseInt(offset)); // updates lrc position + setScrubValue((video.currentTime / video.duration) * 100); // update playhead position + }; + video.addEventListener("timeupdate", syncLrcWithVideo); + + return () => { + video.removeEventListener("timeupdate", syncLrcWithVideo); + }; + }); + + + // General video control functionality + + const handleVolumeToggle = () => { + setShowVolume(!showVolume); + }; + + const handlePlayPause = () => { + const video = videoRef.current; + if (!video) return; + + if (video.paused) { + video.play(); + setIsPlaying(true); + } else { + video.pause(); + setIsPlaying(false); + } + }; + + useEffect(() => { + if (videoUrl && lrcContent) { + setStatusText("Ready to play!"); + } else if (videoUrl) { + setStatusText("No lyrics file selected"); + } else if (lrcContent) { + setStatusText("No video file selected"); + } else { + setStatusText("No video or lyrics file selected"); + } + }, [videoUrl, lrcContent]); + + const handleScrub = (event: React.ChangeEvent) => { + const time = + (parseFloat(event.target.value) / 100) * videoRef.current!.duration; + videoRef.current!.currentTime = time; + setScrubValue(parseFloat(event.target.value)); + }; + + const handleVolumeChange = (event: React.ChangeEvent) => { + const video = videoRef.current; + if (!video) return; + video.volume = Number(event.target.value) / 100; + }; + + + // Handling drag and drop files + const handleDragOver = (event: React.DragEvent) => { + setDragOver(true); + event.preventDefault(); + }; + + const handleDragEnter = (event: React.DragEvent) => { + setDragOver(true); + event.preventDefault(); + }; + + const handleDragLeave = (event: React.DragEvent) => { + setDragOver(false); + event.preventDefault(); + }; + + const handleDrop = (event: React.DragEvent) => { + event.preventDefault(); + setDragOver(false); + const file = event.dataTransfer.files?.[0]; + if(file.name.endsWith(".lrc")) { + const reader = new FileReader(); + reader.onload = (e) => { + setLrcContent(e.target?.result as string); + if (videoUrl) setShowFileInputs(false); + }; + reader.readAsText(file); + toast.success("LRC file loaded successfully", { autoClose: 2000 }); + } + else if(file.name.endsWith(".srv3")) { + const reader = new FileReader(); + reader.onload = (e) => { + setCaptionsText(e.target?.result as string); + }; + reader.readAsText(file); + toast.success("SRV file loaded successfully", { autoClose: 2000 }); + } + else if(file.type.startsWith("video") || file.type.startsWith("audio") ) { + const url = URL.createObjectURL(file); + setVideoUrl(url); + setCurrentMillisecond(0); + setScrubValue(0); + setIsPlaying(false); + toast.success("Video/Audio file loaded successfully", { autoClose: 2000 }); + } + else{ + toast.error("Unsupported file type", { autoClose: 2000 }); + } + } + + + + return ( + + + {/*LRC viewer*/ } +
+ + + + {/* Ternary operation for if videoUrl has been set */} +
setShowFileInputs(true)} + onMouseLeave={() => setShowFileInputs(false)} + onDragOver={handleDragOver} + onDragEnter={handleDragEnter} + onDragLeave={handleDragLeave} + onDrop={handleDrop} + > + {videoUrl ? ( + <> +
+
+
+ ); +} + +export default KaraokePage; -- cgit v1.2.3