mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-22 21:29:05 +00:00
split recorder player
This commit is contained in:
@@ -6,11 +6,8 @@ import CustomRegionsPlugin from "../../lib/custom-plugins/regions";
|
||||
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faMicrophone } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faDownload } from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
import { formatTime } from "../../lib/time";
|
||||
import { Topic } from "./webSocketTypes";
|
||||
import { AudioWaveform } from "../../api";
|
||||
import AudioInputsDropdown from "./audioInputsDropdown";
|
||||
import { Option } from "react-dropdown";
|
||||
import { waveSurferStyles } from "../../styles/recorder";
|
||||
@@ -19,17 +16,8 @@ import { useError } from "../../(errors)/errorContext";
|
||||
type RecorderProps = {
|
||||
setStream?: React.Dispatch<React.SetStateAction<MediaStream | null>>;
|
||||
onStop?: () => void;
|
||||
topics: Topic[];
|
||||
getAudioStream?: (deviceId) => Promise<MediaStream | null>;
|
||||
audioDevices?: Option[];
|
||||
useActiveTopic: [
|
||||
Topic | null,
|
||||
React.Dispatch<React.SetStateAction<Topic | null>>,
|
||||
];
|
||||
waveform?: AudioWaveform | null;
|
||||
isPastMeeting: boolean;
|
||||
transcriptId?: string | null;
|
||||
media?: HTMLMediaElement | null;
|
||||
mediaDuration?: number | null;
|
||||
};
|
||||
|
||||
@@ -38,7 +26,7 @@ export default function Recorder(props: RecorderProps) {
|
||||
const [wavesurfer, setWavesurfer] = useState<WaveSurfer | null>(null);
|
||||
const [record, setRecord] = useState<RecordPlugin | null>(null);
|
||||
const [isRecording, setIsRecording] = useState<boolean>(false);
|
||||
const [hasRecorded, setHasRecorded] = useState<boolean>(props.isPastMeeting);
|
||||
const [hasRecorded, setHasRecorded] = useState<boolean>(false);
|
||||
const [isPlaying, setIsPlaying] = useState<boolean>(false);
|
||||
const [currentTime, setCurrentTime] = useState<number>(0);
|
||||
const [timeInterval, setTimeInterval] = useState<number | null>(null);
|
||||
@@ -48,8 +36,6 @@ export default function Recorder(props: RecorderProps) {
|
||||
);
|
||||
const [deviceId, setDeviceId] = useState<string | null>(null);
|
||||
const [recordStarted, setRecordStarted] = useState(false);
|
||||
const [activeTopic, setActiveTopic] = props.useActiveTopic;
|
||||
const topicsRef = useRef(props.topics);
|
||||
const [showDevices, setShowDevices] = useState(false);
|
||||
const { setError } = useError();
|
||||
|
||||
@@ -73,8 +59,6 @@ export default function Recorder(props: RecorderProps) {
|
||||
if (!record.isRecording()) return;
|
||||
handleRecClick();
|
||||
break;
|
||||
case "^":
|
||||
throw new Error("Unhandled Exception thrown by '^' shortcut");
|
||||
case "(":
|
||||
location.href = "/login";
|
||||
break;
|
||||
@@ -104,14 +88,8 @@ export default function Recorder(props: RecorderProps) {
|
||||
// Waveform setup
|
||||
useEffect(() => {
|
||||
if (waveformRef.current) {
|
||||
// XXX duration is required to prevent recomputing peaks from audio
|
||||
// However, the current waveform returns only the peaks, and no duration
|
||||
// And the backend does not save duration properly.
|
||||
// So at the moment, we deduct the duration from the topics.
|
||||
// This is not ideal, but it works for now.
|
||||
const _wavesurfer = WaveSurfer.create({
|
||||
container: waveformRef.current,
|
||||
peaks: props.waveform?.data,
|
||||
hideScrollbar: true,
|
||||
autoCenter: true,
|
||||
barWidth: 2,
|
||||
@@ -121,10 +99,8 @@ export default function Recorder(props: RecorderProps) {
|
||||
...waveSurferStyles.player,
|
||||
});
|
||||
|
||||
if (!props.transcriptId) {
|
||||
const _wshack: any = _wavesurfer;
|
||||
_wshack.renderer.renderSingleCanvas = () => {};
|
||||
}
|
||||
const _wshack: any = _wavesurfer;
|
||||
_wshack.renderer.renderSingleCanvas = () => {};
|
||||
|
||||
// styling
|
||||
const wsWrapper = _wavesurfer.getWrapper();
|
||||
@@ -144,12 +120,6 @@ export default function Recorder(props: RecorderProps) {
|
||||
setRecord(_wavesurfer.registerPlugin(RecordPlugin.create()));
|
||||
setWaveRegions(_wavesurfer.registerPlugin(CustomRegionsPlugin.create()));
|
||||
|
||||
if (props.isPastMeeting) _wavesurfer.toggleInteraction(true);
|
||||
|
||||
if (props.media) {
|
||||
_wavesurfer.setMediaElement(props.media);
|
||||
}
|
||||
|
||||
setWavesurfer(_wavesurfer);
|
||||
|
||||
return () => {
|
||||
@@ -161,58 +131,6 @@ export default function Recorder(props: RecorderProps) {
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!wavesurfer) return;
|
||||
if (!props.media) return;
|
||||
wavesurfer.setMediaElement(props.media);
|
||||
}, [props.media, wavesurfer]);
|
||||
|
||||
useEffect(() => {
|
||||
topicsRef.current = props.topics;
|
||||
if (!isRecording) renderMarkers();
|
||||
}, [props.topics, waveRegions]);
|
||||
|
||||
const renderMarkers = () => {
|
||||
if (!waveRegions) return;
|
||||
|
||||
waveRegions.clearRegions();
|
||||
|
||||
for (let topic of topicsRef.current) {
|
||||
const content = document.createElement("div");
|
||||
content.setAttribute("style", waveSurferStyles.marker);
|
||||
content.onmouseover = () => {
|
||||
content.style.backgroundColor =
|
||||
waveSurferStyles.markerHover.backgroundColor;
|
||||
content.style.zIndex = "999";
|
||||
content.style.width = "300px";
|
||||
};
|
||||
content.onmouseout = () => {
|
||||
content.setAttribute("style", waveSurferStyles.marker);
|
||||
};
|
||||
content.textContent = topic.title;
|
||||
|
||||
const region = waveRegions.addRegion({
|
||||
start: topic.timestamp,
|
||||
content,
|
||||
color: "f00",
|
||||
drag: false,
|
||||
});
|
||||
region.on("click", (e) => {
|
||||
e.stopPropagation();
|
||||
setActiveTopic(topic);
|
||||
wavesurfer?.setTime(region.start);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!record) return;
|
||||
|
||||
return record.on("stopRecording", () => {
|
||||
renderMarkers();
|
||||
});
|
||||
}, [record]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isRecording) {
|
||||
const interval = window.setInterval(() => {
|
||||
@@ -229,12 +147,6 @@ export default function Recorder(props: RecorderProps) {
|
||||
}
|
||||
}, [isRecording]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeTopic) {
|
||||
wavesurfer?.setTime(activeTopic.timestamp);
|
||||
}
|
||||
}, [activeTopic]);
|
||||
|
||||
const handleRecClick = async () => {
|
||||
if (!record) return console.log("no record");
|
||||
|
||||
@@ -320,7 +232,6 @@ export default function Recorder(props: RecorderProps) {
|
||||
if (!record) return;
|
||||
if (!destinationStream) return;
|
||||
if (props.setStream) props.setStream(destinationStream);
|
||||
waveRegions?.clearRegions();
|
||||
if (destinationStream) {
|
||||
record.startRecording(destinationStream);
|
||||
setIsRecording(true);
|
||||
@@ -379,23 +290,9 @@ export default function Recorder(props: RecorderProps) {
|
||||
} text-white ml-2 md:ml:4 md:h-[78px] md:min-w-[100px] text-lg`}
|
||||
id="play-btn"
|
||||
onClick={handlePlayClick}
|
||||
disabled={isRecording}
|
||||
>
|
||||
{isPlaying ? "Pause" : "Play"}
|
||||
</button>
|
||||
|
||||
{props.transcriptId && (
|
||||
<a
|
||||
title="Download recording"
|
||||
className="text-center cursor-pointer text-blue-400 hover:text-blue-700 ml-2 md:ml:4 p-2 rounded-lg outline-blue-400"
|
||||
download={`recording-${
|
||||
props.transcriptId?.split("-")[0] || "0000"
|
||||
}`}
|
||||
href={`${process.env.NEXT_PUBLIC_API_URL}/v1/transcripts/${props.transcriptId}/audio/mp3`}
|
||||
>
|
||||
<FontAwesomeIcon icon={faDownload} className="h-5 w-auto" />
|
||||
</a>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{!hasRecorded && (
|
||||
|
||||
Reference in New Issue
Block a user