fix(electron): streaming audio encoding not working (#12231)

fix AF-2612

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **Bug Fixes**
  - Improved handling of recording status updates to prevent unwanted side effects after closing the recording popup.
  - Enhanced error handling and logging for internal communication, ensuring errors are logged and do not disrupt the user experience.
  - Refined control flow for stream encoding to avoid unnecessary errors when encoders are closed.
- **Refactor**
  - Updated popup window readiness and lifecycle management for more reliable behavior and clearer logging.
  - Adjusted recording state management to allow more precise control over status updates and emissions.
- **Style**
  - Added additional logging for popup and recording operations to aid in troubleshooting and transparency.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
pengx17
2025-05-13 06:52:09 +00:00
parent 9d116426f8
commit 7e80cb336e
6 changed files with 40 additions and 18 deletions

View File

@@ -80,7 +80,12 @@ export const registerHandlers = () => {
};
ipcMain.handle(AFFINE_API_CHANNEL_NAME, async (e, ...args: any[]) => {
return handleIpcMessage(e, ...args);
try {
return await handleIpcMessage(e, ...args);
} catch (error) {
logger.error(`error in ipc handler when calling ${args[0]}`, error);
return null;
}
});
ipcMain.on(AFFINE_API_CHANNEL_NAME, (e, ...args: any[]) => {

View File

@@ -520,10 +520,13 @@ export function newRecording(
export function startRecording(
appGroup?: AppGroupInfo | number
): RecordingStatus | null {
const state = recordingStateMachine.dispatch({
type: 'START_RECORDING',
appGroup: normalizeAppGroupInfo(appGroup),
});
const state = recordingStateMachine.dispatch(
{
type: 'START_RECORDING',
appGroup: normalizeAppGroupInfo(appGroup),
},
false
);
if (state?.status === 'recording') {
createRecording(state);
@@ -542,6 +545,8 @@ export function startRecording(
}
}, MAX_DURATION_FOR_TRANSCRIPTION);
recordingStateMachine.status$.next(state);
return state;
}
@@ -654,6 +659,8 @@ export async function getRawAudioBuffers(
}
export async function readyRecording(id: number, buffer: Buffer) {
logger.info('readyRecording', id);
const recordingStatus = recordingStatus$.value;
const recording = recordings.get(id);
if (!recordingStatus || recordingStatus.id !== id || !recording) {

View File

@@ -63,7 +63,7 @@ export class RecordingStateMachine {
* @param event The event to dispatch
* @returns The new recording status after the event is processed
*/
dispatch(event: RecordingEvent): RecordingStatus | null {
dispatch(event: RecordingEvent, emit = true): RecordingStatus | null {
const currentStatus = this.recordingStatus$.value;
let newStatus: RecordingStatus | null = null;
@@ -105,7 +105,9 @@ export class RecordingStateMachine {
return currentStatus;
}
this.recordingStatus$.next(newStatus);
if (emit) {
this.recordingStatus$.next(newStatus);
}
return newStatus;
}

View File

@@ -61,10 +61,7 @@ abstract class PopupWindow {
abstract windowOptions: Partial<BrowserWindowConstructorOptions>;
resolveReady: () => void = () => {};
ready = new Promise<void>(resolve => {
this.resolveReady = resolve;
});
ready = Promise.withResolvers<void>();
private readonly showing$ = new BehaviorSubject<boolean>(false);
@@ -109,12 +106,12 @@ abstract class PopupWindow {
visibleOnFullScreen: true,
});
browserWindow.loadURL(popupViewUrl).catch(err => logger.error(err));
browserWindow.on('ready-to-show', () => {
browserWindow.webContents.on('did-finish-load', () => {
this.resolveReady();
});
logger.info('loading popup', this.name, popupViewUrl);
browserWindow.webContents.on('did-finish-load', () => {
this.ready.resolve();
logger.info('popup ready', this.name);
});
browserWindow.loadURL(popupViewUrl).catch(err => logger.error(err));
return browserWindow;
}
@@ -126,7 +123,7 @@ abstract class PopupWindow {
const workArea = getCurrentDisplay(browserWindow).workArea;
const popupSize = browserWindow.getSize();
await this.ready;
await this.ready.promise;
this.showing$.next(true);
@@ -141,6 +138,8 @@ abstract class PopupWindow {
// Set initial position
browserWindow.setPosition(startX, y);
logger.info('showing popup', this.name);
// First fade in, then slide
await Promise.all([
// Slide in animation
@@ -169,6 +168,7 @@ abstract class PopupWindow {
if (!this.browserWindow) {
return;
}
logger.info('hiding popup', this.name);
this.showing$.next(false);
await animate(this.browserWindow.getOpacity(), 0, opacity => {
this.browserWindow?.setOpacity(opacity);