Files
calendar/apps/web/modules/videos/ai/ai-transcribe.tsx
T

201 lines
6.0 KiB
TypeScript

import { useTranscription, useRecording } from "@daily-co/daily-react";
import { useDaily, useDailyEvent } from "@daily-co/daily-react";
import React, { Fragment, useCallback, useRef, useState, useLayoutEffect, useEffect } from "react";
import {
TRANSCRIPTION_STARTED_ICON,
RECORDING_IN_PROGRESS_ICON,
TRANSCRIPTION_STOPPED_ICON,
RECORDING_DEFAULT_ICON,
} from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
const BUTTONS = {
STOP_TRANSCRIPTION: {
label: "Stop",
tooltip: "Stop transcription",
iconPath: TRANSCRIPTION_STARTED_ICON,
iconPathDarkMode: TRANSCRIPTION_STARTED_ICON,
},
START_TRANSCRIPTION: {
label: "Cal.ai",
tooltip: "Transcription powered by AI",
iconPath: TRANSCRIPTION_STOPPED_ICON,
iconPathDarkMode: TRANSCRIPTION_STOPPED_ICON,
},
WAIT_FOR_TRANSCRIPTION_TO_START: {
label: "Starting..",
tooltip: "Please wait while we start transcription",
iconPath: TRANSCRIPTION_STOPPED_ICON,
iconPathDarkMode: TRANSCRIPTION_STOPPED_ICON,
},
WAIT_FOR_TRANSCRIPTION_TO_STOP: {
label: "Stopping..",
tooltip: "Please wait while we stop transcription",
iconPath: TRANSCRIPTION_STOPPED_ICON,
iconPathDarkMode: TRANSCRIPTION_STOPPED_ICON,
},
START_RECORDING: {
label: "Record",
tooltip: "Start recording",
iconPath: RECORDING_DEFAULT_ICON,
iconPathDarkMode: RECORDING_DEFAULT_ICON,
},
WAIT_FOR_RECORDING_TO_START: {
label: "Starting..",
tooltip: "Please wait while we start recording",
iconPath: RECORDING_DEFAULT_ICON,
iconPathDarkMode: RECORDING_DEFAULT_ICON,
},
WAIT_FOR_RECORDING_TO_STOP: {
label: "Stopping..",
tooltip: "Please wait while we stop recording",
iconPath: RECORDING_DEFAULT_ICON,
iconPathDarkMode: RECORDING_DEFAULT_ICON,
},
STOP_RECORDING: {
label: "Stop",
tooltip: "Stop recording",
iconPath: RECORDING_IN_PROGRESS_ICON,
iconPathDarkMode: RECORDING_IN_PROGRESS_ICON,
},
};
export const CalAiTranscribe = () => {
const daily = useDaily();
const { t } = useLocale();
const [transcript, setTranscript] = useState("");
const [transcriptHeight, setTranscriptHeight] = useState(0);
const transcriptRef = useRef<HTMLDivElement | null>(null);
const transcription = useTranscription();
const recording = useRecording();
useDailyEvent(
"app-message",
useCallback((ev) => {
const data = ev?.data;
if (data.user_name && data.text) setTranscript(`${data.user_name}: ${data.text}`);
}, [])
);
useDailyEvent("transcription-started", (ev) => {
daily?.updateCustomTrayButtons({
recording: recording?.isRecording ? BUTTONS.STOP_RECORDING : BUTTONS.START_RECORDING,
transcription: BUTTONS.STOP_TRANSCRIPTION,
});
});
useDailyEvent("recording-started", (ev) => {
daily?.updateCustomTrayButtons({
recording: BUTTONS.STOP_RECORDING,
transcription: transcription?.isTranscribing ? BUTTONS.STOP_TRANSCRIPTION : BUTTONS.START_TRANSCRIPTION,
});
});
useDailyEvent("transcription-stopped", (ev) => {
daily?.updateCustomTrayButtons({
recording: recording?.isRecording ? BUTTONS.STOP_RECORDING : BUTTONS.START_RECORDING,
transcription: BUTTONS.START_TRANSCRIPTION,
});
});
useDailyEvent("recording-stopped", (ev) => {
daily?.updateCustomTrayButtons({
recording: BUTTONS.START_RECORDING,
transcription: transcription?.isTranscribing ? BUTTONS.STOP_TRANSCRIPTION : BUTTONS.START_TRANSCRIPTION,
});
});
const toggleRecording = async () => {
if (recording?.isRecording) {
daily?.updateCustomTrayButtons({
recording: BUTTONS.WAIT_FOR_RECORDING_TO_STOP,
transcription: transcription?.isTranscribing
? BUTTONS.STOP_TRANSCRIPTION
: BUTTONS.START_TRANSCRIPTION,
});
await daily?.stopRecording();
} else {
daily?.updateCustomTrayButtons({
recording: BUTTONS.WAIT_FOR_RECORDING_TO_START,
transcription: transcription?.isTranscribing
? BUTTONS.STOP_TRANSCRIPTION
: BUTTONS.START_TRANSCRIPTION,
});
await daily?.startRecording({
// 480p
videoBitrate: 2000,
});
}
};
const toggleTranscription = async () => {
if (transcription?.isTranscribing) {
daily?.updateCustomTrayButtons({
recording: recording?.isRecording ? BUTTONS.STOP_RECORDING : BUTTONS.START_RECORDING,
transcription: BUTTONS.WAIT_FOR_TRANSCRIPTION_TO_STOP,
});
daily?.stopTranscription();
} else {
daily?.updateCustomTrayButtons({
recording: recording?.isRecording ? BUTTONS.STOP_RECORDING : BUTTONS.START_RECORDING,
transcription: BUTTONS.WAIT_FOR_TRANSCRIPTION_TO_START,
});
daily?.startTranscription();
}
};
useDailyEvent("custom-button-click", async (ev) => {
if (ev?.button_id === "recording") {
toggleRecording();
} else if (ev?.button_id === "transcription") {
toggleTranscription();
}
});
useLayoutEffect(() => {
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
setTranscriptHeight(entry.target.scrollHeight);
}
});
if (transcriptRef.current) {
observer.observe(transcriptRef.current);
}
return () => observer.disconnect();
}, []);
useEffect(() => {
transcriptRef.current?.scrollTo({
top: transcriptRef.current?.scrollHeight,
behavior: "smooth",
});
}, [transcriptHeight]);
return transcript ? (
<div
id="cal-ai-transcription"
style={{
textShadow: "0 0 20px black, 0 0 20px black, 0 0 20px black",
backgroundColor: "rgba(0,0,0,0.6)",
}}
ref={transcriptRef}
className="flex max-h-full justify-center overflow-x-hidden overflow-y-scroll p-2 text-center text-white">
{transcript
? transcript.split("\n").map((line, i) => (
<Fragment key={`transcript-${i}`}>
{i > 0 && <br />}
{line}
</Fragment>
))
: ""}
</div>
) : null;
};