mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-16 01:26:37 +08:00
feat(server): refactor record schema (#14729)
#### PR Dependency Tree * **PR #14729** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Transcriptions now produce structured meeting summaries (strict JSON) and a normalized, speaker‑tagged, non‑overlapping transcript with legacy projection support. * **API** * Submission accepts richer transcription input; results return source‑audio metadata, slice manifest, quality indicators, normalized segments/transcript, and structured summary JSON. * **Frontend** * Recording flow stores transcription metadata and uploads preprocessed audio slices with slice/quality info; UI-side result normalization applied. * **Tests** * Expanded unit, contract, and e2e coverage for normalization, payload parsing, persistence/retry, and end‑to‑end transcription flows. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -16,6 +16,21 @@ import { getCurrentWorkspace, isAiEnabled } from './utils';
|
||||
const logger = new DebugLogger('electron-renderer:recording');
|
||||
const RECORDING_IMPORT_RETRY_MS = 1000;
|
||||
const NATIVE_RECORDING_MIME_TYPE = 'audio/ogg';
|
||||
const TRANSCRIPTION_BLOCK_FLAVOUR = 'affine:transcription';
|
||||
|
||||
type TranscriptionBlockModel = {
|
||||
props: {
|
||||
transcription: {
|
||||
sourceAudio?: {
|
||||
mimeType?: string;
|
||||
durationMs?: number;
|
||||
sampleRate?: number;
|
||||
channels?: number;
|
||||
};
|
||||
quality?: { degraded?: boolean; overflowCount?: number };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
type RecordingImportStatus = {
|
||||
id: number;
|
||||
@@ -115,6 +130,29 @@ function findExistingAttachment(docStore: Store, attachmentName: string) {
|
||||
);
|
||||
}
|
||||
|
||||
function ensureTranscriptionBlock(model: AttachmentBlockModel) {
|
||||
for (const key of model.childMap.value.keys()) {
|
||||
const block = model.store.getBlock$(key);
|
||||
if (block?.flavour === TRANSCRIPTION_BLOCK_FLAVOUR) {
|
||||
return block.model as unknown as TranscriptionBlockModel;
|
||||
}
|
||||
}
|
||||
|
||||
const blockId = model.store.addBlock(
|
||||
TRANSCRIPTION_BLOCK_FLAVOUR,
|
||||
{ transcription: {} },
|
||||
model.id
|
||||
);
|
||||
|
||||
const block = model.store.getBlock(blockId)?.model as
|
||||
| TranscriptionBlockModel
|
||||
| undefined;
|
||||
if (!block) {
|
||||
throw new Error('Failed to create transcription block');
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
async function createRecordingDoc(
|
||||
frameworkProvider: FrameworkProvider,
|
||||
workspace: WorkspaceHandle['workspace'],
|
||||
@@ -187,6 +225,20 @@ async function createRecordingDoc(
|
||||
attachmentCreated = true;
|
||||
}
|
||||
|
||||
const transcriptionBlock = ensureTranscriptionBlock(model);
|
||||
transcriptionBlock.props.transcription = {
|
||||
sourceAudio: {
|
||||
mimeType: model.props.type,
|
||||
durationMs: status.durationMs,
|
||||
sampleRate: status.sampleRate,
|
||||
channels: status.numberOfChannels,
|
||||
},
|
||||
quality: {
|
||||
degraded: status.degraded,
|
||||
overflowCount: status.overflowCount,
|
||||
},
|
||||
};
|
||||
|
||||
workspace.scope.get(WorkbenchService).workbench.openDoc(targetDocId);
|
||||
|
||||
if (!aiEnabled || !attachmentCreated) {
|
||||
|
||||
Reference in New Issue
Block a user