diff --git a/packages/frontend/apps/electron-renderer/src/app/effects/recording.ts b/packages/frontend/apps/electron-renderer/src/app/effects/recording.ts index 3b940c2e99..bdb87c23a4 100644 --- a/packages/frontend/apps/electron-renderer/src/app/effects/recording.ts +++ b/packages/frontend/apps/electron-renderer/src/app/effects/recording.ts @@ -12,7 +12,7 @@ import { Text } from '@blocksuite/affine/store'; import type { BlobEngine } from '@blocksuite/affine/sync'; import type { FrameworkProvider } from '@toeverything/infra'; -import { getCurrentWorkspace } from './utils'; +import { getCurrentWorkspace, isAiEnabled } from './utils'; const logger = new DebugLogger('electron-renderer:recording'); @@ -44,6 +44,7 @@ export function setupRecordingEvents(frameworkProvider: FrameworkProvider) { frameworkProvider.get(EditorSettingService); const docsService = workspace.scope.get(DocsService); const editorSetting = editorSettingService.editorSetting; + const aiEnabled = isAiEnabled(frameworkProvider); const timestamp = i18nTime(status.startTime, { absolute: { @@ -96,7 +97,10 @@ export function setupRecordingEvents(frameworkProvider: FrameworkProvider) { MeetingSettingsService ); - if (!meetingSettingsService.settings.autoTranscription) { + if ( + !meetingSettingsService.settings.autoTranscription || + !aiEnabled + ) { // auto transcription is disabled, // so we don't need to transcribe the recording by default return; diff --git a/packages/frontend/apps/electron-renderer/src/app/effects/utils.ts b/packages/frontend/apps/electron-renderer/src/app/effects/utils.ts index ff7424b4f6..e15592c8b9 100644 --- a/packages/frontend/apps/electron-renderer/src/app/effects/utils.ts +++ b/packages/frontend/apps/electron-renderer/src/app/effects/utils.ts @@ -1,3 +1,5 @@ +import { ServersService } from '@affine/core/modules/cloud'; +import { FeatureFlagService } from '@affine/core/modules/feature-flag'; import { GlobalContextService } from '@affine/core/modules/global-context'; import { WorkspacesService } from '@affine/core/modules/workspace'; import type { FrameworkProvider } from '@toeverything/infra'; @@ -21,3 +23,23 @@ export function getCurrentWorkspace(frameworkProvider: FrameworkProvider) { [Symbol.dispose]: dispose, }; } + +export function getCurrentServerService(frameworkProvider: FrameworkProvider) { + const currentServerId = frameworkProvider + .get(GlobalContextService) + .globalContext.serverId.get(); + const serversService = frameworkProvider.get(ServersService); + const serverRef = currentServerId + ? serversService.servers$.value.find( + server => server.id === currentServerId + ) + : null; + return serverRef; +} + +export function isAiEnabled(frameworkProvider: FrameworkProvider) { + const featureFlagService = frameworkProvider.get(FeatureFlagService); + const serverService = getCurrentServerService(frameworkProvider); + const aiConfig = serverService?.features$.value.copilot; + return featureFlagService.flags.enable_ai.$ && aiConfig; +} diff --git a/packages/frontend/apps/electron/src/main/recording/feature.ts b/packages/frontend/apps/electron/src/main/recording/feature.ts index 1787d1c8ec..6f53c9f658 100644 --- a/packages/frontend/apps/electron/src/main/recording/feature.ts +++ b/packages/frontend/apps/electron/src/main/recording/feature.ts @@ -517,14 +517,17 @@ export function startRecording( appGroup: normalizeAppGroupInfo(appGroup), }); - if (state?.status === 'recording') { - // set a timeout to stop the recording after MAX_DURATION_FOR_TRANSCRIPTION - setTimeout(() => { + // set a timeout to stop the recording after MAX_DURATION_FOR_TRANSCRIPTION + setTimeout(() => { + if ( + state?.status === 'recording' && + state.id === recordingStatus$.value?.id + ) { stopRecording(state.id).catch(err => { logger.error('failed to stop recording', err); }); - }, MAX_DURATION_FOR_TRANSCRIPTION); - } + } + }, MAX_DURATION_FOR_TRANSCRIPTION); return state; } @@ -549,30 +552,48 @@ export async function stopRecording(id: number) { return; } - const recordingStatus = recordingStateMachine.dispatch({ - type: 'STOP_RECORDING', - id, - filepath: String(recording.file.path), - sampleRate: recording.stream.sampleRate, - numberOfChannels: recording.stream.channels, - }); - - if (!recordingStatus) { - logger.error('No recording status to stop'); - return; - } - const { file } = recording; file.end(); // Wait for file to finish writing - await new Promise(resolve => { - file.on('finish', () => { - resolve(); + try { + await new Promise((resolve, reject) => { + file.on('finish', () => { + // check if the file is empty + const stats = fs.statSync(file.path); + if (stats.size === 0) { + logger.error(`Recording ${id} is empty`); + reject(new Error('Recording is empty')); + } + resolve(); + }); + }); + const recordingStatus = recordingStateMachine.dispatch({ + type: 'STOP_RECORDING', + id, + filepath: String(recording.file.path), + sampleRate: recording.stream.sampleRate, + numberOfChannels: recording.stream.channels, }); - }); - return serializeRecordingStatus(recordingStatus); + if (!recordingStatus) { + logger.error('No recording status to stop'); + return; + } + return serializeRecordingStatus(recordingStatus); + } catch (error: unknown) { + logger.error('Failed to stop recording', error); + const recordingStatus = recordingStateMachine.dispatch({ + type: 'CREATE_BLOCK_FAILED', + id, + error: error instanceof Error ? error : undefined, + }); + if (!recordingStatus) { + logger.error('No recording status to stop'); + return; + } + return serializeRecordingStatus(recordingStatus); + } } export async function readyRecording(id: number, buffer: Buffer) {