Kopie van https://gitlab.com/studieverenigingvia/ict/centurion met een paar aanpassingen
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

211 lines
5.4 KiB

import connection, {
calculateRoomTime,
useRoomRunningAndReadyChanged,
useTimelineSongFileChanged,
} from "../lib/Connection";
import { SyntheticEvent, useEffect, useRef, useState } from "react";
import { Button, Slider } from "antd";
import { SoundFilled, SoundOutlined } from "@ant-design/icons";
import "../css/player.css";
import { Room } from "../types/types";
const Player = () => {
const room = useRoomRunningAndReadyChanged();
const timeline = useTimelineSongFileChanged();
const player = useRef<HTMLAudioElement>(null);
const defaultVolume = parseInt(localStorage.getItem("volume") ?? "100");
const [volume, setVolume] = useState(defaultVolume);
const [muted, setMuted] = useState(false);
const [finishedLoading, setFinishedLoading] = useState(false);
const [timesSeeked, setTimesSeeked] = useState(0);
const [hadError, setHadError] = useState(false);
// If our time synchronisation algorithm thing thinks the time is off by more
// than this value, we seek the running player to correct it.
const diffSecondsRequiredToSeekRunningPlayer = 0.2;
// Hard cap we are allowed to seek this player. Some browsers are slow or inaccurate
// and will always be off. To avoid endless skipping of the song this cap stops seeking the
// player.
const maxTimesSeekAllow = 25;
useEffect(() => {
// Need to use an effect since 'player' will only contain a reference after first render.
if (!timeline) {
throw new Error("Player without active timeline.");
}
if (!player.current) {
throw new Error("No player after mount.");
}
player.current.src = timeline.songFile;
}, [timeline]);
function handlePlayerOnPlay(e: SyntheticEvent) {
e.preventDefault();
// For when the user manually started the player for when autoplay is off.
setHadError(false);
}
async function handlePlayerPause(e: SyntheticEvent) {
if (!shouldPlay()) {
// We should not be playing, pausing is fine.
console.log("should not play, paused");
return;
}
e.preventDefault();
if (room) {
setPlayerTime(room, true);
}
await player.current?.play();
}
function handlePlayerCanPlayThrough() {
if (!finishedLoading) {
setFinishedLoading(true);
connection.requestStart();
}
}
function shouldPlay() {
return (
player.current &&
timeline &&
room &&
room.running &&
room.readyToParticipate &&
!player.current.ended
);
}
async function startPlaying(manual: boolean) {
if (!player.current) {
return;
}
if (player.current.paused && !hadError) {
setPlayerVolume(volume);
try {
await player.current.play();
setHadError(false);
} catch (e) {
console.error("Error playing", e);
setHadError(true);
}
}
if (!hadError && room) {
setPlayerTime(room, manual);
}
}
function setPlayerTime(room: Room, manualAdjustment: boolean) {
if (!player.current) {
return;
}
// Player's currentTime is in seconds, not ms.
const targetTime = calculateRoomTime() / 1000;
const diff = Math.abs(player.current.currentTime - targetTime);
// console.log('PLAYER DIFF', diff,
// 'min req to seek: ', diffSecondsRequiredToSeekRunningPlayer,
// `(${timesSeeked} / ${maxTimesSeekAllow})`);
if (diff <= diffSecondsRequiredToSeekRunningPlayer) {
return;
}
if (timesSeeked >= maxTimesSeekAllow && !manualAdjustment) {
// If we are adjusting manually we always allow a seek.
console.warn(
"The running player is off, but we've changed the time " +
"too often, skipping synchronizing the player."
);
return;
}
player.current.currentTime = targetTime;
player.current.playbackRate = Math.min(room.speedFactor, 5);
if (!manualAdjustment) {
setTimesSeeked(timesSeeked + 1);
}
console.log(
`Player seeked: diff: ${diff}, target: ${targetTime}, (${timesSeeked} / ${maxTimesSeekAllow})`
);
}
function toggleMute() {
if (player.current != null) {
player.current.muted = !player.current.muted;
setMuted(!muted);
}
}
function changeVolume(sliderValue: number) {
setVolume(sliderValue);
localStorage["volume"] = sliderValue;
setPlayerVolume(sliderValue);
}
function setPlayerVolume(value: number) {
if (player.current) {
player.current.volume =
value === 0.0 ? 0.0 : Math.pow(10, (value / 100 - 1) * 2);
}
}
if (shouldPlay()) {
startPlaying(false)
.then(() => {
//
})
.catch((e) => {
console.error(e);
});
} else {
if (player.current) {
player.current.pause();
}
}
return (
<div id="audio">
<audio
ref={player}
className="player"
controls={true}
loop={false}
hidden={!hadError}
onPause={handlePlayerPause}
onPlay={handlePlayerOnPlay}
onCanPlayThrough={handlePlayerCanPlayThrough}
/>
<div id="volume-control">
<Button onClick={toggleMute} shape="circle">
{muted ? <SoundOutlined /> : <SoundFilled />}
</Button>
<Slider
id="volume-slider"
defaultValue={volume}
onChange={changeVolume}
trackStyle={{ backgroundColor: "var(--secondary-color)" }}
/>
</div>
</div>
);
};
export default Player;