aboutsummaryrefslogtreecommitdiffstats
path: root/src/app/page.tsx
diff options
context:
space:
mode:
authorPinapelz <yukais@pinapelz.com>2023-11-16 21:49:11 -0800
committerPinapelz <yukais@pinapelz.com>2023-11-16 21:49:11 -0800
commit1caebf3d8a8ba9deb7fb77fcc17b02c46652f9f1 (patch)
tree4e58da6feea914c074334f883182724c98e1691c /src/app/page.tsx
parent63950ac21a046c582cbaa9aad86304a136e27310 (diff)
feat: add supplementary audio upload option
Diffstat (limited to 'src/app/page.tsx')
-rw-r--r--src/app/page.tsx240
1 files changed, 190 insertions, 50 deletions
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 4be028c..457610d 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,4 +1,4 @@
-"use client"
+"use client";
import React, { useEffect, useRef, useState } from "react";
import styled from "styled-components";
import KaraokePlayer from "./components/KaraokePlayer";
@@ -35,6 +35,7 @@ const FileInput = styled.input`
padding: 10px 15px;
border-radius: 5px;
border: 1px solid #ddd;
+ justify-content: center;
cursor: pointer;
display: none;
font-family: Arial;
@@ -57,29 +58,46 @@ const FileInputLabel = styled.label`
}
`;
+const ControlBarButton = styled.button`
+ padding: 10px 15px;
+ border-radius: 5px;
+ border: 1px solid #ddd;
+ align-items: center;
+ 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;
- }
- `;
+ 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 [lrcContent, setLrcContent] = useState<string>("");
+ const [videoUrl, setVideoUrl] = useState<string>("");
+ const [supplementAudioUrl, setSupplementAudioUrl] = useState<string>("");
+ const [isPlaying, setIsPlaying] = useState<boolean>(false);
+ const [showVolume, setShowVolume] = useState<boolean>(false);
+ const [scrubValue, setScrubValue] = useState<number>(0);
+ const [showFileInputs, setShowFileInputs] = useState<boolean>(true);
const videoRef = useRef<HTMLVideoElement>(null);
- const [captionsText, setCaptionsText] = useState("");
- const [offset, setOffset] = useState("0");
- const [dragOver, setDragOver] = useState(false);
- const [statusText, setStatusText] = useState("No video selected");
+ const supplementAudioRef = useRef<HTMLAudioElement>(null);
+ const [captionsText, setCaptionsText] = useState<string>("");
+ const [offset, setOffset] = useState<string>("0");
+ const [dragOver, setDragOver] = useState<boolean>(false);
+ const [statusText, setStatusText] = useState<string>("No video selected");
+ const [balance, setBalance] = useState<number>(0);
+ const [supplementAudioOffset, setSupplementAudioOffset] = useState<string>("0");
// Functions for handling file input changes
const handleLrcFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
@@ -118,7 +136,26 @@ function KaraokePage() {
};
reader.readAsText(file);
toast.success("SRV file loaded successfully", { autoClose: 2000 });
- }
+ }
+ };
+
+ const handleSupplementAudioFileChange = (
+ event: React.ChangeEvent<HTMLInputElement>
+ ) => {
+ const file = event.target.files?.[0];
+ const video = videoRef.current;
+ if (file) {
+ const url = URL.createObjectURL(file);
+ setSupplementAudioUrl(url);
+ setCurrentMillisecond(0);
+ setScrubValue(0);
+ setIsPlaying(false);
+ if (video)
+ video.pause();
+ toast.success("Supplemental Audio file loaded successfully", {
+ autoClose: 2000,
+ });
+ }
};
// Side effects for keyboard shortcuts
@@ -161,6 +198,26 @@ function KaraokePage() {
};
});
+ useEffect(() => {
+ const video = videoRef.current;
+ const audio = supplementAudioRef.current;
+ if (!video || !audio) return;
+
+ if (balance < 0) {
+ video.volume = (1 + balance);
+ } else {
+ video.volume = 1;
+ audio.volume = (1 - balance);
+ }
+ }, [balance]);
+
+ useEffect(() => {
+ const video = videoRef.current;
+ const audio = supplementAudioRef.current;
+ if (!video || !audio) return;
+ if (supplementAudioOffset === "" || supplementAudioOffset == null) return;
+ audio.currentTime = video.currentTime + parseInt(supplementAudioOffset)/1000;
+ },[supplementAudioOffset]);
// General video control functionality
@@ -174,13 +231,16 @@ function KaraokePage() {
if (video.paused) {
video.play();
+ if (supplementAudioUrl) supplementAudioRef.current?.play();
setIsPlaying(true);
} else {
video.pause();
+ if (supplementAudioUrl) supplementAudioRef.current?.pause();
setIsPlaying(false);
}
};
+ // Status text styling depending on whats loaded. Not all visible
useEffect(() => {
if (videoUrl && lrcContent) {
setStatusText("Ready to play!");
@@ -193,19 +253,47 @@ function KaraokePage() {
}
}, [videoUrl, lrcContent]);
+ // Video Control Bar functionality
const handleScrub = (event: React.ChangeEvent<HTMLInputElement>) => {
const time =
(parseFloat(event.target.value) / 100) * videoRef.current!.duration;
videoRef.current!.currentTime = time;
+ if (supplementAudioOffset === "" || supplementAudioOffset == null){
+ supplementAudioRef.current!.currentTime = time;
+ }
+ else {
+ supplementAudioRef.current!.currentTime = time + parseInt(supplementAudioOffset)/1000;
+ }
setScrubValue(parseFloat(event.target.value));
};
const handleVolumeChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+ const volume = Number(event.target.value) / 100;
const video = videoRef.current;
- if (!video) return;
- video.volume = Number(event.target.value) / 100;
+ const audio = supplementAudioRef.current;
+ if (!video || !audio) return;
+
+ if (balance < 0) {
+ video.volume = volume * (1 + balance);
+ audio.volume = volume;
+ } else {
+ video.volume = volume;
+ audio.volume = volume * (1 - balance);
+ }
+ };
+
+ const handleVideoEnded = () => {
+ setIsPlaying(false);
+ supplementAudioRef.current?.pause();
};
+ const syncSupplementAudioWithVideo = () => {
+ const video = videoRef.current;
+ const audio = supplementAudioRef.current;
+ if (!video || !audio) return;
+ if (supplementAudioOffset === "" || supplementAudioOffset == null) return;
+ audio.currentTime = video.currentTime + parseInt(supplementAudioOffset)/1000;
+ }
// Handling drag and drop files
const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => {
@@ -227,7 +315,7 @@ function KaraokePage() {
event.preventDefault();
setDragOver(false);
const file = event.dataTransfer.files?.[0];
- if(file.name.endsWith(".lrc")) {
+ if (file.name.endsWith(".lrc")) {
const reader = new FileReader();
reader.onload = (e) => {
setLrcContent(e.target?.result as string);
@@ -235,44 +323,44 @@ function KaraokePage() {
};
reader.readAsText(file);
toast.success("LRC file loaded successfully", { autoClose: 2000 });
- }
- else if(file.name.endsWith(".srv3")) {
+ } 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") ) {
+ } 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.success("Video/Audio file loaded successfully", {
+ autoClose: 2000,
+ });
+ } else {
toast.error("Unsupported file type", { autoClose: 2000 });
}
- }
-
-
+ };
return (
<Root>
<ToastContainer />
- {/*LRC viewer*/ }
+ {/*LRC viewer*/}
<div style={{ display: "flex", width: "100%", height: "100vh" }}>
<KaraokePlayer
lrc={lrcContent}
currentMillisecond={currentMillisecond}
/>
-
{/* Ternary operation for if videoUrl has been set */}
<div
- style={{ flex: 1, position: "relative", backgroundColor: dragOver ? 'lightblue' : 'white', }}
+ style={{
+ flex: 1,
+ position: "relative",
+ backgroundColor: dragOver ? "lightblue" : "white",
+ }}
onMouseEnter={() => setShowFileInputs(true)}
onMouseLeave={() => setShowFileInputs(false)}
onDragOver={handleDragOver}
@@ -286,17 +374,23 @@ function KaraokePage() {
ref={videoRef}
src={videoUrl}
style={{ position: "absolute", width: "100%", height: "100%" }}
+ onEnded={handleVideoEnded}
/>
- <div
- style={{ width: '90%', height: '90%', margin: 'auto' }}
- onClick={() => handlePlayPause()}
+ <audio
+ ref={supplementAudioRef}
+ src={supplementAudioUrl}
+ style={{ display: "none" }}
+ />
+ <div
+ style={{ width: "90%", height: "90%", margin: "auto" }}
+ onClick={() => handlePlayPause()}
>
<CaptionsRenderer
srv3={captionsText}
currentTime={currentMillisecond / 1000}
/>
</div>
- { /*Video control bar*/ }
+ {/*Video control bar*/}
<div
style={{
position: "absolute",
@@ -382,13 +476,19 @@ function KaraokePage() {
display: "flex",
justifyContent: "center",
alignItems: "center",
- flexDirection: "column"
+ flexDirection: "column",
}}
>
- <h1 style={{ fontFamily: "Arial", fontWeight: "bold", fontSize: "32px" }}>
+ <h1
+ style={{
+ fontFamily: "Arial",
+ fontWeight: "bold",
+ fontSize: "32px",
+ }}
+ >
{statusText}
</h1>
- {/* Show a placeholder while no video selected */}
+ {/* Show a placeholder while no video selected */}
<p
style={{
@@ -397,10 +497,9 @@ function KaraokePage() {
fontFamily: "Arial",
}}
>
-
Please select the video and lrc (lyrics) file <br />
(Drag and Drop them here, or use the menus below!)
- <br/>
+ <br />
<StyledLink href="/about>">About</StyledLink>
</p>
</div>
@@ -432,13 +531,28 @@ function KaraokePage() {
accept="video/*"
onChange={handleVideoFileChange}
/>
- <FileInputLabel
- htmlFor="srvUpload"
+ <FileInputLabel htmlFor="srvUpload" style={{ cursor: "pointer" }}>
+ SRV
+ </FileInputLabel>
+ <FileInput
+ id="srvUpload"
+ type="file"
+ accept=".srv3"
+ onChange={handleSrvFileChange}
+ />
+ <FileInputLabel
+ htmlFor="supplementAudioUpload"
style={{ cursor: "pointer" }}
- >
- SRV
+ >
+ Audio #2
</FileInputLabel>
- <FileInput id="srvUpload" type="file" accept=".srv3" onChange={handleSrvFileChange}/>
+ <FileInput
+ id="supplementAudioUpload"
+ type="file"
+ accept="audio/*"
+ onChange={handleSupplementAudioFileChange}
+ />
+ <ControlBarButton onClick={syncSupplementAudioWithVideo}>Sync Audio</ControlBarButton>
<div
style={{
display: "flex",
@@ -446,6 +560,15 @@ function KaraokePage() {
fontFamily: "Arial",
}}
>
+ <label>Audio/Video Balance</label>
+ <input
+ type="range"
+ min="-1"
+ max="1"
+ step="0.01"
+ value={balance}
+ onChange={(e) => setBalance(Number(e.target.value))}
+ />
<label>Offset (±ms) </label>
<input
type="number"
@@ -456,6 +579,23 @@ function KaraokePage() {
step="100"
/>
</div>
+ <div
+ style={{
+ display: "flex",
+ flexDirection: "column",
+ fontFamily: "Arial",
+ }}
+ >
+ <label>Audio 2 Offset (±ms) </label>
+ <input
+ type="number"
+ style={{ fontSize: "14px" }}
+ id="numberInput"
+ value={supplementAudioOffset}
+ onChange={(e) => setSupplementAudioOffset(e.target.value)}
+ step="25"
+ />
+ </div>
</FileInputContainer>
)}
</div>
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage