diff --git a/src/frontend/src/features/rooms/livekit/api/recordRoom.ts b/src/frontend/src/features/rooms/livekit/api/recordRoom.ts
new file mode 100644
index 00000000..c463c5d0
--- /dev/null
+++ b/src/frontend/src/features/rooms/livekit/api/recordRoom.ts
@@ -0,0 +1,56 @@
+import { fetchServerApi } from './fetchServerApi'
+import { buildServerApiUrl } from './buildServerApiUrl'
+import { useRoomData } from '../hooks/useRoomData'
+import { useParams } from 'wouter'
+
+export const useRecordRoom = () => {
+ const data = useRoomData()
+ const { roomId: roomSlug } = useParams()
+
+ const recordRoom = () => {
+ if (!data || !data?.livekit) {
+ throw new Error('Room data is not available')
+ }
+ if (!roomSlug) {
+ throw new Error('Room ID is not available')
+ }
+ return fetchServerApi(
+ buildServerApiUrl(
+ data.livekit.url,
+ '/twirp/livekit.Egress/StartRoomCompositeEgress'
+ ),
+ data.livekit.token,
+ {
+ method: 'POST',
+ body: JSON.stringify({
+ room_name: data.livekit.room,
+ audio_only: true,
+ file_outputs: [
+ {
+ file_extension: 'ogg',
+ filepath: `{room_name}_{time}_${roomSlug}`,
+ },
+ ],
+ }),
+ }
+ )
+ }
+
+ const stopRecordingRoom = (egressId: string) => {
+ if (!data || !data?.livekit) {
+ throw new Error('Room data is not available')
+ }
+ return fetchServerApi(
+ buildServerApiUrl(data.livekit.url, '/twirp/livekit.Egress/StopEgress'),
+ data.livekit.token,
+ {
+ method: 'POST',
+ body: JSON.stringify({
+ egressId,
+ }),
+ }
+ )
+ }
+
+ return { recordRoom, stopRecordingRoom }
+}
diff --git a/src/frontend/src/features/rooms/livekit/components/controls/Options/OptionsMenuItems.tsx b/src/frontend/src/features/rooms/livekit/components/controls/Options/OptionsMenuItems.tsx
index d84e4cb6..e7dc868c 100644
--- a/src/frontend/src/features/rooms/livekit/components/controls/Options/OptionsMenuItems.tsx
+++ b/src/frontend/src/features/rooms/livekit/components/controls/Options/OptionsMenuItems.tsx
@@ -10,6 +10,7 @@ import { Dispatch, SetStateAction } from 'react'
import { DialogState } from './OptionsButton'
import { Separator } from '@/primitives/Separator'
import { useSidePanel } from '../../../hooks/useSidePanel'
+import { RecordingMenuItem } from '@/features/rooms/livekit/components/controls/Options/RecordingMenuItem'
// @todo try refactoring it to use MenuList component
export const OptionsMenuItems = ({
@@ -34,6 +35,7 @@ export const OptionsMenuItems = ({
{t('effects')}
+
diff --git a/src/frontend/src/features/rooms/livekit/components/controls/Options/RecordingMenuItem.tsx b/src/frontend/src/features/rooms/livekit/components/controls/Options/RecordingMenuItem.tsx
new file mode 100644
index 00000000..935e8e8a
--- /dev/null
+++ b/src/frontend/src/features/rooms/livekit/components/controls/Options/RecordingMenuItem.tsx
@@ -0,0 +1,52 @@
+import { MenuItem } from 'react-aria-components'
+import { menuItemRecipe } from '@/primitives/menuItemRecipe.ts'
+import { RiPauseCircleLine, RiRecordCircleLine } from '@remixicon/react'
+import { useRecordRoom } from '@/features/rooms/livekit/api/recordRoom.ts'
+import { useState } from 'react'
+import { egressStore } from '@/stores/egress.ts'
+import { useSnapshot } from 'valtio'
+
+export const RecordingMenuItem = () => {
+ const { recordRoom, stopRecordingRoom } = useRecordRoom()
+
+ const egressSnap = useSnapshot(egressStore)
+ const egressId = egressSnap.egressId
+
+ const [isPending, setIsPending] = useState(false)
+
+ const handleAction = async () => {
+ if (egressId) {
+ setIsPending(true)
+ egressStore.egressIsStopping = true
+ const response = await stopRecordingRoom(egressId)
+ console.log(response)
+ egressStore.egressId = undefined
+ setIsPending(false)
+ } else {
+ setIsPending(true)
+ const response = await recordRoom()
+ egressStore.egressId = response['egress_id'] as string
+ setIsPending(false)
+ }
+ }
+
+ return (
+
+ )
+}
diff --git a/src/frontend/src/stores/egress.ts b/src/frontend/src/stores/egress.ts
new file mode 100644
index 00000000..a06270bc
--- /dev/null
+++ b/src/frontend/src/stores/egress.ts
@@ -0,0 +1,11 @@
+import { proxy } from 'valtio'
+
+type State = {
+ egressId: string | undefined
+ egressIsStopping: boolean
+}
+
+export const egressStore = proxy({
+ egressId: undefined,
+ egressIsStopping: false,
+})