feat: enable transcription automatically (#21691)

* fest: Add enableAutomaticTranscription field to CalVideoSettings schema

* fest: Update backend logic for enableAutomaticTranscription in event types

* fest: Add frontend support for enableAutomaticTranscription in video meetings and event types

* fest: Add localization strings for enableAutomaticTranscription feature

* fix: enable transcription feature

* Update apps/web/lib/video/[uid]/getServerSideProps.ts

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: akarsh-jain-790 <akarsh.jain.790@gmail.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
This commit is contained in:
Udit Takkar
2025-06-13 07:51:57 +00:00
committed by GitHub
co-authored by cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> akarsh-jain-790 cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
parent 6de6f8e697
commit 8de4897042
14 changed files with 73 additions and 3 deletions
@@ -22,6 +22,7 @@ const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true }
type CalVideoSettings = {
disableRecordingForGuests: boolean;
disableRecordingForOrganizer: boolean;
enableAutomaticTranscription: boolean;
};
const shouldEnableRecordButton = ({
@@ -43,6 +44,19 @@ const shouldEnableRecordButton = ({
return !calVideoSettings.disableRecordingForGuests;
};
const shouldEnableAutomaticTranscription = ({
hasTeamPlan,
calVideoSettings,
}: {
hasTeamPlan: boolean;
calVideoSettings?: CalVideoSettings | null;
}) => {
if (!hasTeamPlan) return false;
if (!calVideoSettings) return false;
return !!calVideoSettings.enableAutomaticTranscription;
};
const checkIfUserIsHost = async ({
booking,
sessionUserId,
@@ -204,6 +218,11 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
isOrganizer: sessionUserId === bookingObj.user?.id,
});
const enableAutomaticTranscription = shouldEnableAutomaticTranscription({
hasTeamPlan: !!hasTeamPlan,
calVideoSettings: bookingObj.eventType?.calVideoSettings,
});
return {
props: {
meetingUrl: videoReference.meetingUrl ?? "",
@@ -225,6 +244,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
displayLogInOverlay,
loggedInUserName: sessionUserId ? session?.user?.name : undefined,
showRecordingButton,
enableAutomaticTranscription,
rediectAttendeeToOnExit: isOrganizer
? undefined
: bookingObj.eventType?.calVideoSettings?.redirectUrlOnExit,
+19 -1
View File
@@ -70,7 +70,13 @@ export interface DailyCustomTrayButton {
tooltip: string;
visualState?: DailyCustomTrayButtonVisualState;
}
export const CalAiTranscribe = ({ showRecordingButton }: { showRecordingButton: boolean }) => {
export const CalAiTranscribe = ({
showRecordingButton,
enableAutomaticTranscription,
}: {
showRecordingButton: boolean;
enableAutomaticTranscription: boolean;
}) => {
const daily = useDaily();
const { t } = useLocale();
@@ -107,6 +113,18 @@ export const CalAiTranscribe = ({ showRecordingButton }: { showRecordingButton:
}, [])
);
useDailyEvent("joined-meeting", (ev) => {
if (enableAutomaticTranscription) {
daily?.startTranscription();
updateCustomTrayButtons({
recording: BUTTONS.START_RECORDING,
transcription: transcription?.isTranscribing
? BUTTONS.STOP_TRANSCRIPTION
: BUTTONS.START_TRANSCRIPTION,
});
}
});
useDailyEvent("transcription-started", (ev) => {
updateCustomTrayButtons({
recording: recording?.isRecording ? BUTTONS.STOP_RECORDING : BUTTONS.START_RECORDING,
@@ -36,6 +36,7 @@ export default function JoinCall(props: PageProps) {
loggedInUserName,
overrideName,
showRecordingButton,
enableAutomaticTranscription,
rediectAttendeeToOnExit,
} = props;
const [daily, setDaily] = useState<DailyCall | null>(null);
@@ -96,7 +97,6 @@ export default function JoinCall(props: PageProps) {
callFrame = DailyIframe.getCallInstance();
} finally {
setDaily(callFrame ?? null);
callFrame?.join();
}
@@ -111,7 +111,10 @@ export default function JoinCall(props: PageProps) {
<div
className="mx-auto hidden sm:block"
style={{ zIndex: 2, left: "30%", position: "absolute", bottom: 100, width: "auto" }}>
<CalAiTranscribe showRecordingButton={showRecordingButton} />
<CalAiTranscribe
showRecordingButton={showRecordingButton}
enableAutomaticTranscription={enableAutomaticTranscription}
/>
</div>
<div style={{ zIndex: 2, position: "relative" }}>
{calVideoLogo ? (
@@ -47,6 +47,7 @@
"customize_calvideo_settings": "Customize Cal Video Settings",
"calvideo_settings_description": "Configure video meeting preferences including recording permissions for hosts and attendees.",
"disable_recording_for_guests": "Disable recording for guests",
"enable_automatic_transcription": "Enable automatic transcription after joining the meeting",
"disable_recording_for_organizer": "Disable recording for organizer",
"event_request_declined_recurring": "Your recurring event request has been declined",
"event_request_cancelled": "Your scheduled event was canceled",
@@ -375,6 +375,22 @@ const Locations: React.FC<LocationsProps> = ({
}}
/>
)}
{!isPlatform && (
<Controller
name="calVideoSettings.enableAutomaticTranscription"
defaultValue={!!eventType.calVideoSettings?.enableAutomaticTranscription}
render={({ field: { onChange, value } }) => {
return (
<SettingsToggle
title={t("enable_automatic_transcription")}
labelClassName="text-sm"
checked={value}
onCheckedChange={onChange}
/>
);
}}
/>
)}
<TextField
label={t("enter_redirect_url_on_exit_description")}
@@ -154,6 +154,7 @@ export type FormValues = {
calVideoSettings?: {
disableRecordingForOrganizer?: boolean;
disableRecordingForGuests?: boolean;
enableAutomaticTranscription?: boolean;
redirectUrlOnExit?: string;
};
};
@@ -393,6 +393,7 @@ export class BookingRepository {
select: {
disableRecordingForGuests: true,
disableRecordingForOrganizer: true,
enableAutomaticTranscription: true,
redirectUrlOnExit: true,
},
},
@@ -15,6 +15,7 @@ export class CalVideoSettingsRepository {
calVideoSettings: {
disableRecordingForGuests?: boolean | null;
disableRecordingForOrganizer?: boolean | null;
enableAutomaticTranscription?: boolean | null;
redirectUrlOnExit?: string | null;
};
}) {
@@ -23,12 +24,14 @@ export class CalVideoSettingsRepository {
update: {
disableRecordingForGuests: calVideoSettings.disableRecordingForGuests ?? false,
disableRecordingForOrganizer: calVideoSettings.disableRecordingForOrganizer ?? false,
enableAutomaticTranscription: calVideoSettings.enableAutomaticTranscription ?? false,
redirectUrlOnExit: calVideoSettings.redirectUrlOnExit ?? null,
updatedAt: new Date(),
},
create: {
disableRecordingForGuests: calVideoSettings.disableRecordingForGuests ?? false,
disableRecordingForOrganizer: calVideoSettings.disableRecordingForOrganizer ?? false,
enableAutomaticTranscription: calVideoSettings.enableAutomaticTranscription ?? false,
redirectUrlOnExit: calVideoSettings.redirectUrlOnExit ?? null,
eventTypeId,
},
@@ -692,6 +692,7 @@ export class EventTypeRepository {
select: {
disableRecordingForGuests: true,
disableRecordingForOrganizer: true,
enableAutomaticTranscription: true,
redirectUrlOnExit: true,
},
},
@@ -174,6 +174,7 @@ export const useEventTypeForm = ({
redirectUrlOnExit: z.string().url().nullish(),
disableRecordingForOrganizer: z.boolean().nullable(),
disableRecordingForGuests: z.boolean().nullable(),
enableAutomaticTranscription: z.boolean().nullable(),
})
.optional()
.nullable(),
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "CalVideoSettings" ADD COLUMN "enableAutomaticTranscription" BOOLEAN NOT NULL DEFAULT false;
+1
View File
@@ -74,6 +74,7 @@ model CalVideoSettings {
disableRecordingForOrganizer Boolean @default(false)
disableRecordingForGuests Boolean @default(false)
enableAutomaticTranscription Boolean @default(false)
redirectUrlOnExit String?
createdAt DateTime @default(now())
@@ -29,6 +29,7 @@ const calVideoSettingsSchema = z
.object({
disableRecordingForGuests: z.boolean().optional().nullable(),
disableRecordingForOrganizer: z.boolean().optional().nullable(),
enableAutomaticTranscription: z.boolean().optional().nullable(),
redirectUrlOnExit: z.string().url().optional().nullable(),
})
.optional()
@@ -124,6 +124,7 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
select: {
disableRecordingForOrganizer: true,
disableRecordingForGuests: true,
enableAutomaticTranscription: true,
redirectUrlOnExit: true,
},
},