mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-20 20:29:06 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| af921ce927 | |||
|
|
bd5df1ce2e | ||
| c8024484b3 | |||
| 28f87c09dc |
14
CHANGELOG.md
14
CHANGELOG.md
@@ -1,5 +1,19 @@
|
||||
# Changelog
|
||||
|
||||
## [0.22.4](https://github.com/Monadical-SAS/reflector/compare/v0.22.3...v0.22.4) (2025-12-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Multitrack mixdown optimisation 2 ([#764](https://github.com/Monadical-SAS/reflector/issues/764)) ([bd5df1c](https://github.com/Monadical-SAS/reflector/commit/bd5df1ce2ebf35d7f3413b295e56937a9a28ef7b))
|
||||
|
||||
## [0.22.3](https://github.com/Monadical-SAS/reflector/compare/v0.22.2...v0.22.3) (2025-12-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* align daily room settings ([#759](https://github.com/Monadical-SAS/reflector/issues/759)) ([28f87c0](https://github.com/Monadical-SAS/reflector/commit/28f87c09dc459846873d0dde65b03e3d7b2b9399))
|
||||
|
||||
## [0.22.2](https://github.com/Monadical-SAS/reflector/compare/v0.22.1...v0.22.2) (2025-12-02)
|
||||
|
||||
|
||||
|
||||
@@ -40,6 +40,10 @@ class RoomProperties(BaseModel):
|
||||
)
|
||||
enable_chat: bool = Field(default=True, description="Enable in-meeting chat")
|
||||
enable_screenshare: bool = Field(default=True, description="Enable screen sharing")
|
||||
enable_knocking: bool = Field(
|
||||
default=False,
|
||||
description="Enable knocking for private rooms (allows participants to request access)",
|
||||
)
|
||||
start_video_off: bool = Field(
|
||||
default=False, description="Start with video off for all participants"
|
||||
)
|
||||
|
||||
@@ -31,7 +31,6 @@ from reflector.processors import AudioFileWriterProcessor
|
||||
from reflector.processors.audio_waveform_processor import AudioWaveformProcessor
|
||||
from reflector.processors.types import TitleSummary
|
||||
from reflector.processors.types import Transcript as TranscriptType
|
||||
from reflector.settings import settings
|
||||
from reflector.storage import Storage, get_transcripts_storage
|
||||
from reflector.utils.daily import (
|
||||
filter_cam_audio_tracks,
|
||||
@@ -423,7 +422,15 @@ class PipelineMainMultitrack(PipelineMainBase):
|
||||
# Open all containers with cleanup guaranteed
|
||||
for i, url in enumerate(valid_track_urls):
|
||||
try:
|
||||
c = av.open(url)
|
||||
c = av.open(
|
||||
url,
|
||||
options={
|
||||
# it's trying to stream from s3 by default
|
||||
"reconnect": "1",
|
||||
"reconnect_streamed": "1",
|
||||
"reconnect_delay_max": "5",
|
||||
},
|
||||
)
|
||||
containers.append(c)
|
||||
except Exception as e:
|
||||
self.logger.warning(
|
||||
@@ -452,6 +459,8 @@ class PipelineMainMultitrack(PipelineMainBase):
|
||||
frame = next(dec)
|
||||
except StopIteration:
|
||||
active[i] = False
|
||||
# causes stream to move on / unclogs memory
|
||||
inputs[i].push(None)
|
||||
continue
|
||||
|
||||
if frame.sample_rate != target_sample_rate:
|
||||
@@ -471,8 +480,6 @@ class PipelineMainMultitrack(PipelineMainBase):
|
||||
mixed.time_base = Fraction(1, target_sample_rate)
|
||||
await writer.push(mixed)
|
||||
|
||||
for in_ctx in inputs:
|
||||
in_ctx.push(None)
|
||||
while True:
|
||||
try:
|
||||
mixed = sink.pull()
|
||||
@@ -632,55 +639,43 @@ class PipelineMainMultitrack(PipelineMainBase):
|
||||
|
||||
transcript.data_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if settings.SKIP_MIXDOWN:
|
||||
self.logger.warning(
|
||||
"SKIP_MIXDOWN enabled: Skipping mixdown and waveform generation. "
|
||||
"UI will have no audio playback or waveform.",
|
||||
num_tracks=len(padded_track_urls),
|
||||
transcript_id=transcript.id,
|
||||
)
|
||||
else:
|
||||
mp3_writer = AudioFileWriterProcessor(
|
||||
path=str(transcript.audio_mp3_filename),
|
||||
on_duration=self.on_duration,
|
||||
)
|
||||
await self.mixdown_tracks(
|
||||
padded_track_urls, mp3_writer, offsets_seconds=None
|
||||
)
|
||||
await mp3_writer.flush()
|
||||
mp3_writer = AudioFileWriterProcessor(
|
||||
path=str(transcript.audio_mp3_filename),
|
||||
on_duration=self.on_duration,
|
||||
)
|
||||
await self.mixdown_tracks(padded_track_urls, mp3_writer, offsets_seconds=None)
|
||||
await mp3_writer.flush()
|
||||
|
||||
if not transcript.audio_mp3_filename.exists():
|
||||
raise Exception(
|
||||
"Mixdown failed - no MP3 file generated. Cannot proceed without playable audio."
|
||||
)
|
||||
|
||||
storage_path = f"{transcript.id}/audio.mp3"
|
||||
# Use file handle streaming to avoid loading entire MP3 into memory
|
||||
mp3_size = transcript.audio_mp3_filename.stat().st_size
|
||||
with open(transcript.audio_mp3_filename, "rb") as mp3_file:
|
||||
await transcript_storage.put_file(storage_path, mp3_file)
|
||||
mp3_url = await transcript_storage.get_file_url(storage_path)
|
||||
|
||||
await transcripts_controller.update(
|
||||
transcript, {"audio_location": "storage"}
|
||||
if not transcript.audio_mp3_filename.exists():
|
||||
raise Exception(
|
||||
"Mixdown failed - no MP3 file generated. Cannot proceed without playable audio."
|
||||
)
|
||||
|
||||
self.logger.info(
|
||||
f"Uploaded mixed audio to storage",
|
||||
storage_path=storage_path,
|
||||
size=mp3_size,
|
||||
url=mp3_url,
|
||||
)
|
||||
storage_path = f"{transcript.id}/audio.mp3"
|
||||
# Use file handle streaming to avoid loading entire MP3 into memory
|
||||
mp3_size = transcript.audio_mp3_filename.stat().st_size
|
||||
with open(transcript.audio_mp3_filename, "rb") as mp3_file:
|
||||
await transcript_storage.put_file(storage_path, mp3_file)
|
||||
mp3_url = await transcript_storage.get_file_url(storage_path)
|
||||
|
||||
self.logger.info("Generating waveform from mixed audio")
|
||||
waveform_processor = AudioWaveformProcessor(
|
||||
audio_path=transcript.audio_mp3_filename,
|
||||
waveform_path=transcript.audio_waveform_filename,
|
||||
on_waveform=self.on_waveform,
|
||||
)
|
||||
waveform_processor.set_pipeline(self.empty_pipeline)
|
||||
await waveform_processor.flush()
|
||||
self.logger.info("Waveform generated successfully")
|
||||
await transcripts_controller.update(transcript, {"audio_location": "storage"})
|
||||
|
||||
self.logger.info(
|
||||
f"Uploaded mixed audio to storage",
|
||||
storage_path=storage_path,
|
||||
size=mp3_size,
|
||||
url=mp3_url,
|
||||
)
|
||||
|
||||
self.logger.info("Generating waveform from mixed audio")
|
||||
waveform_processor = AudioWaveformProcessor(
|
||||
audio_path=transcript.audio_mp3_filename,
|
||||
waveform_path=transcript.audio_waveform_filename,
|
||||
on_waveform=self.on_waveform,
|
||||
)
|
||||
waveform_processor.set_pipeline(self.empty_pipeline)
|
||||
await waveform_processor.flush()
|
||||
self.logger.info("Waveform generated successfully")
|
||||
|
||||
speaker_transcripts: list[TranscriptType] = []
|
||||
for idx, padded_url in enumerate(padded_track_urls):
|
||||
|
||||
@@ -138,14 +138,6 @@ class Settings(BaseSettings):
|
||||
DAILY_WEBHOOK_UUID: str | None = (
|
||||
None # Webhook UUID for this environment. Not used by production code
|
||||
)
|
||||
|
||||
# Multitrack processing
|
||||
# SKIP_MIXDOWN: When True, skips audio mixdown and waveform generation.
|
||||
# Transcription still works using individual tracks. Useful for:
|
||||
# - Diagnosing OOM issues in mixdown
|
||||
# - Fast processing when audio playback is not needed
|
||||
# Note: UI will have no audio playback or waveform when enabled.
|
||||
SKIP_MIXDOWN: bool = True
|
||||
# Platform Configuration
|
||||
DEFAULT_VIDEO_PLATFORM: Platform = WHEREBY_PLATFORM
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ class DailyClient(VideoPlatformClient):
|
||||
PLATFORM_NAME: Platform = "daily"
|
||||
TIMESTAMP_FORMAT = "%Y%m%d%H%M%S"
|
||||
RECORDING_NONE: RecordingType = "none"
|
||||
RECORDING_LOCAL: RecordingType = "local"
|
||||
RECORDING_CLOUD: RecordingType = "cloud"
|
||||
|
||||
def __init__(self, config: VideoPlatformConfig):
|
||||
@@ -54,19 +55,23 @@ class DailyClient(VideoPlatformClient):
|
||||
timestamp = datetime.now().strftime(self.TIMESTAMP_FORMAT)
|
||||
room_name = f"{room_name_prefix}{ROOM_PREFIX_SEPARATOR}{timestamp}"
|
||||
|
||||
enable_recording = None
|
||||
if room.recording_type == self.RECORDING_LOCAL:
|
||||
enable_recording = "local"
|
||||
elif room.recording_type == self.RECORDING_CLOUD:
|
||||
enable_recording = "raw-tracks"
|
||||
|
||||
properties = RoomProperties(
|
||||
enable_recording="raw-tracks"
|
||||
if room.recording_type != self.RECORDING_NONE
|
||||
else False,
|
||||
enable_recording=enable_recording,
|
||||
enable_chat=True,
|
||||
enable_screenshare=True,
|
||||
enable_knocking=room.is_locked,
|
||||
start_video_off=False,
|
||||
start_audio_off=False,
|
||||
exp=int(end_date.timestamp()),
|
||||
)
|
||||
|
||||
# Only configure recordings_bucket if recording is enabled
|
||||
if room.recording_type != self.RECORDING_NONE:
|
||||
if room.recording_type == self.RECORDING_CLOUD:
|
||||
daily_storage = get_dailyco_storage()
|
||||
assert daily_storage.bucket_name, "S3 bucket must be configured"
|
||||
properties.recordings_bucket = RecordingsBucketConfig(
|
||||
@@ -172,15 +177,16 @@ class DailyClient(VideoPlatformClient):
|
||||
async def create_meeting_token(
|
||||
self,
|
||||
room_name: DailyRoomName,
|
||||
enable_recording: bool,
|
||||
start_cloud_recording: bool,
|
||||
enable_recording_ui: bool,
|
||||
user_id: NonEmptyString | None = None,
|
||||
is_owner: bool = False,
|
||||
) -> NonEmptyString:
|
||||
properties = MeetingTokenProperties(
|
||||
room_name=room_name,
|
||||
user_id=user_id,
|
||||
start_cloud_recording=enable_recording,
|
||||
enable_recording_ui=False,
|
||||
start_cloud_recording=start_cloud_recording,
|
||||
enable_recording_ui=enable_recording_ui,
|
||||
is_owner=is_owner,
|
||||
)
|
||||
request = CreateMeetingTokenRequest(properties=properties)
|
||||
|
||||
@@ -89,7 +89,7 @@ class CreateRoom(BaseModel):
|
||||
ics_url: Optional[str] = None
|
||||
ics_fetch_interval: int = 300
|
||||
ics_enabled: bool = False
|
||||
platform: Optional[Platform] = None
|
||||
platform: Platform
|
||||
|
||||
|
||||
class UpdateRoom(BaseModel):
|
||||
@@ -248,7 +248,7 @@ async def rooms_create(
|
||||
ics_url=room.ics_url,
|
||||
ics_fetch_interval=room.ics_fetch_interval,
|
||||
ics_enabled=room.ics_enabled,
|
||||
platform=room.platform or settings.DEFAULT_VIDEO_PLATFORM,
|
||||
platform=room.platform,
|
||||
)
|
||||
|
||||
|
||||
@@ -310,6 +310,22 @@ async def rooms_create_meeting(
|
||||
room=room, current_time=current_time
|
||||
)
|
||||
|
||||
if meeting is not None:
|
||||
settings_match = (
|
||||
meeting.is_locked == room.is_locked
|
||||
and meeting.room_mode == room.room_mode
|
||||
and meeting.recording_type == room.recording_type
|
||||
and meeting.recording_trigger == room.recording_trigger
|
||||
and meeting.platform == room.platform
|
||||
)
|
||||
if not settings_match:
|
||||
logger.info(
|
||||
f"Room settings changed for {room_name}, creating new meeting",
|
||||
room_id=room.id,
|
||||
old_meeting_id=meeting.id,
|
||||
)
|
||||
meeting = None
|
||||
|
||||
if meeting is None:
|
||||
end_date = current_time + timedelta(hours=8)
|
||||
|
||||
@@ -549,21 +565,16 @@ async def rooms_join_meeting(
|
||||
if meeting.end_date <= current_time:
|
||||
raise HTTPException(status_code=400, detail="Meeting has ended")
|
||||
|
||||
if meeting.platform == "daily":
|
||||
if meeting.platform == "daily" and user_id is not None:
|
||||
client = create_platform_client(meeting.platform)
|
||||
enable_recording = room.recording_trigger != "none"
|
||||
token = await client.create_meeting_token(
|
||||
meeting.room_name,
|
||||
enable_recording=enable_recording,
|
||||
start_cloud_recording=meeting.recording_type == "cloud",
|
||||
enable_recording_ui=meeting.recording_type == "local",
|
||||
user_id=user_id,
|
||||
is_owner=user_id == room.user_id,
|
||||
)
|
||||
meeting = meeting.model_copy()
|
||||
meeting.room_url = add_query_param(meeting.room_url, "t", token)
|
||||
if meeting.host_room_url:
|
||||
meeting.host_room_url = add_query_param(meeting.host_room_url, "t", token)
|
||||
|
||||
if user_id != room.user_id and meeting.platform == "whereby":
|
||||
meeting.host_room_url = ""
|
||||
|
||||
return meeting
|
||||
|
||||
@@ -67,6 +67,11 @@ const recordingTypeOptions: SelectOption[] = [
|
||||
{ label: "Cloud", value: "cloud" },
|
||||
];
|
||||
|
||||
const platformOptions: SelectOption[] = [
|
||||
{ label: "Whereby", value: "whereby" },
|
||||
{ label: "Daily", value: "daily" },
|
||||
];
|
||||
|
||||
const roomInitialState = {
|
||||
name: "",
|
||||
zulipAutoPost: false,
|
||||
@@ -82,6 +87,7 @@ const roomInitialState = {
|
||||
icsUrl: "",
|
||||
icsEnabled: false,
|
||||
icsFetchInterval: 5,
|
||||
platform: "whereby",
|
||||
};
|
||||
|
||||
export default function RoomsList() {
|
||||
@@ -99,6 +105,11 @@ export default function RoomsList() {
|
||||
const recordingTypeCollection = createListCollection({
|
||||
items: recordingTypeOptions,
|
||||
});
|
||||
|
||||
const platformCollection = createListCollection({
|
||||
items: platformOptions,
|
||||
});
|
||||
|
||||
const [roomInput, setRoomInput] = useState<null | typeof roomInitialState>(
|
||||
null,
|
||||
);
|
||||
@@ -143,15 +154,24 @@ export default function RoomsList() {
|
||||
zulipStream: detailedEditedRoom.zulip_stream,
|
||||
zulipTopic: detailedEditedRoom.zulip_topic,
|
||||
isLocked: detailedEditedRoom.is_locked,
|
||||
roomMode: detailedEditedRoom.room_mode,
|
||||
roomMode:
|
||||
detailedEditedRoom.platform === "daily"
|
||||
? "group"
|
||||
: detailedEditedRoom.room_mode,
|
||||
recordingType: detailedEditedRoom.recording_type,
|
||||
recordingTrigger: detailedEditedRoom.recording_trigger,
|
||||
recordingTrigger:
|
||||
detailedEditedRoom.platform === "daily"
|
||||
? detailedEditedRoom.recording_type === "cloud"
|
||||
? "automatic-2nd-participant"
|
||||
: "none"
|
||||
: detailedEditedRoom.recording_trigger,
|
||||
isShared: detailedEditedRoom.is_shared,
|
||||
webhookUrl: detailedEditedRoom.webhook_url || "",
|
||||
webhookSecret: detailedEditedRoom.webhook_secret || "",
|
||||
icsUrl: detailedEditedRoom.ics_url || "",
|
||||
icsEnabled: detailedEditedRoom.ics_enabled || false,
|
||||
icsFetchInterval: detailedEditedRoom.ics_fetch_interval || 5,
|
||||
platform: detailedEditedRoom.platform,
|
||||
}
|
||||
: null,
|
||||
[detailedEditedRoom],
|
||||
@@ -277,21 +297,32 @@ export default function RoomsList() {
|
||||
return;
|
||||
}
|
||||
|
||||
const platform: "whereby" | "daily" | null =
|
||||
room.platform === "whereby" || room.platform === "daily"
|
||||
? room.platform
|
||||
: null;
|
||||
|
||||
const roomData = {
|
||||
name: room.name,
|
||||
zulip_auto_post: room.zulipAutoPost,
|
||||
zulip_stream: room.zulipStream,
|
||||
zulip_topic: room.zulipTopic,
|
||||
is_locked: room.isLocked,
|
||||
room_mode: room.roomMode,
|
||||
room_mode: platform === "daily" ? "group" : room.roomMode,
|
||||
recording_type: room.recordingType,
|
||||
recording_trigger: room.recordingTrigger,
|
||||
recording_trigger:
|
||||
platform === "daily"
|
||||
? room.recordingType === "cloud"
|
||||
? "automatic-2nd-participant"
|
||||
: "none"
|
||||
: room.recordingTrigger,
|
||||
is_shared: room.isShared,
|
||||
webhook_url: room.webhookUrl,
|
||||
webhook_secret: room.webhookSecret,
|
||||
ics_url: room.icsUrl,
|
||||
ics_enabled: room.icsEnabled,
|
||||
ics_fetch_interval: room.icsFetchInterval,
|
||||
platform,
|
||||
};
|
||||
|
||||
if (isEditing) {
|
||||
@@ -339,15 +370,21 @@ export default function RoomsList() {
|
||||
zulipStream: roomData.zulip_stream,
|
||||
zulipTopic: roomData.zulip_topic,
|
||||
isLocked: roomData.is_locked,
|
||||
roomMode: roomData.room_mode,
|
||||
roomMode: roomData.platform === "daily" ? "group" : roomData.room_mode, // Daily always uses 2-200
|
||||
recordingType: roomData.recording_type,
|
||||
recordingTrigger: roomData.recording_trigger,
|
||||
recordingTrigger:
|
||||
roomData.platform === "daily"
|
||||
? roomData.recording_type === "cloud"
|
||||
? "automatic-2nd-participant"
|
||||
: "none"
|
||||
: roomData.recording_trigger,
|
||||
isShared: roomData.is_shared,
|
||||
webhookUrl: roomData.webhook_url || "",
|
||||
webhookSecret: roomData.webhook_secret || "",
|
||||
icsUrl: roomData.ics_url || "",
|
||||
icsEnabled: roomData.ics_enabled || false,
|
||||
icsFetchInterval: roomData.ics_fetch_interval || 5,
|
||||
platform: roomData.platform,
|
||||
});
|
||||
setEditRoomId(roomId);
|
||||
setIsEditing(true);
|
||||
@@ -482,6 +519,48 @@ export default function RoomsList() {
|
||||
)}
|
||||
</Field.Root>
|
||||
|
||||
<Field.Root mt={4}>
|
||||
<Field.Label>Platform</Field.Label>
|
||||
<Select.Root
|
||||
value={[room.platform]}
|
||||
onValueChange={(e) => {
|
||||
const newPlatform = e.value[0] as "whereby" | "daily";
|
||||
const updates: Partial<typeof room> = {
|
||||
platform: newPlatform,
|
||||
};
|
||||
if (newPlatform === "daily") {
|
||||
updates.roomMode = "group";
|
||||
updates.recordingTrigger =
|
||||
room.recordingType === "cloud"
|
||||
? "automatic-2nd-participant"
|
||||
: "none";
|
||||
}
|
||||
setRoomInput({ ...room, ...updates });
|
||||
}}
|
||||
collection={platformCollection}
|
||||
>
|
||||
<Select.HiddenSelect />
|
||||
<Select.Control>
|
||||
<Select.Trigger>
|
||||
<Select.ValueText placeholder="Select platform" />
|
||||
</Select.Trigger>
|
||||
<Select.IndicatorGroup>
|
||||
<Select.Indicator />
|
||||
</Select.IndicatorGroup>
|
||||
</Select.Control>
|
||||
<Select.Positioner>
|
||||
<Select.Content>
|
||||
{platformOptions.map((option) => (
|
||||
<Select.Item key={option.value} item={option}>
|
||||
{option.label}
|
||||
<Select.ItemIndicator />
|
||||
</Select.Item>
|
||||
))}
|
||||
</Select.Content>
|
||||
</Select.Positioner>
|
||||
</Select.Root>
|
||||
</Field.Root>
|
||||
|
||||
<Field.Root mt={4}>
|
||||
<Checkbox.Root
|
||||
name="isLocked"
|
||||
@@ -512,6 +591,7 @@ export default function RoomsList() {
|
||||
setRoomInput({ ...room, roomMode: e.value[0] })
|
||||
}
|
||||
collection={roomModeCollection}
|
||||
disabled={room.platform === "daily"}
|
||||
>
|
||||
<Select.HiddenSelect />
|
||||
<Select.Control>
|
||||
@@ -538,16 +618,26 @@ export default function RoomsList() {
|
||||
<Field.Label>Recording type</Field.Label>
|
||||
<Select.Root
|
||||
value={[room.recordingType]}
|
||||
onValueChange={(e) =>
|
||||
setRoomInput({
|
||||
...room,
|
||||
recordingType: e.value[0],
|
||||
recordingTrigger:
|
||||
e.value[0] !== "cloud"
|
||||
onValueChange={(e) => {
|
||||
const newRecordingType = e.value[0];
|
||||
const updates: Partial<typeof room> = {
|
||||
recordingType: newRecordingType,
|
||||
};
|
||||
// For Daily: if cloud, use automatic; otherwise none
|
||||
if (room.platform === "daily") {
|
||||
updates.recordingTrigger =
|
||||
newRecordingType === "cloud"
|
||||
? "automatic-2nd-participant"
|
||||
: "none";
|
||||
} else {
|
||||
// For Whereby: if not cloud, set to none
|
||||
updates.recordingTrigger =
|
||||
newRecordingType !== "cloud"
|
||||
? "none"
|
||||
: room.recordingTrigger,
|
||||
})
|
||||
}
|
||||
: room.recordingTrigger;
|
||||
}
|
||||
setRoomInput({ ...room, ...updates });
|
||||
}}
|
||||
collection={recordingTypeCollection}
|
||||
>
|
||||
<Select.HiddenSelect />
|
||||
@@ -572,7 +662,7 @@ export default function RoomsList() {
|
||||
</Select.Root>
|
||||
</Field.Root>
|
||||
<Field.Root mt={4}>
|
||||
<Field.Label>Cloud recording start trigger</Field.Label>
|
||||
<Field.Label>Recording start trigger</Field.Label>
|
||||
<Select.Root
|
||||
value={[room.recordingTrigger]}
|
||||
onValueChange={(e) =>
|
||||
@@ -582,7 +672,11 @@ export default function RoomsList() {
|
||||
})
|
||||
}
|
||||
collection={recordingTriggerCollection}
|
||||
disabled={room.recordingType !== "cloud"}
|
||||
disabled={
|
||||
room.recordingType !== "cloud" ||
|
||||
(room.platform === "daily" &&
|
||||
room.recordingType === "cloud")
|
||||
}
|
||||
>
|
||||
<Select.HiddenSelect />
|
||||
<Select.Control>
|
||||
|
||||
@@ -52,7 +52,7 @@ export default function DailyRoom({ meeting }: DailyRoomProps) {
|
||||
join();
|
||||
}, [meeting?.id, roomName, authLastUserId]);
|
||||
|
||||
const roomUrl = joinedMeeting?.host_room_url || joinedMeeting?.room_url;
|
||||
const roomUrl = joinedMeeting?.room_url;
|
||||
|
||||
const handleLeave = useCallback(() => {
|
||||
router.push("/browse");
|
||||
@@ -89,14 +89,17 @@ export default function DailyRoom({ meeting }: DailyRoomProps) {
|
||||
|
||||
frame.on("left-meeting", handleLeave);
|
||||
|
||||
// TODO this method must not ignore no-recording option
|
||||
// TODO this method is here to make dev environments work in some cases (not examined which)
|
||||
frame.on("joined-meeting", async () => {
|
||||
try {
|
||||
assertExists(
|
||||
const frameInstance = assertExists(
|
||||
frame,
|
||||
"frame object got lost somewhere after frame.on was called",
|
||||
).startRecording({ type: "raw-tracks" });
|
||||
);
|
||||
|
||||
if (meeting.recording_type === "cloud") {
|
||||
console.log("Starting cloud recording");
|
||||
await frameInstance.startRecording({ type: "raw-tracks" });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to start recording:", error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user