mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-16 09:36:17 +08:00
fix: client indexing & outdated scheme (#14160)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Optimized storage handling with platform-specific
implementations—SQLite for Electron and IndexedDB for other environments
for improved performance.
* **Bug Fixes**
* Enhanced recording file access and retrieval functionality for better
reliability.
* Strengthened local file protocol handling and security restrictions.
<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -2,6 +2,8 @@ import { DebugLogger } from '@affine/debug';
|
||||
import { apis } from '@affine/electron-api';
|
||||
import { ArrayBufferTarget, Muxer } from 'mp4-muxer';
|
||||
|
||||
import { isLink } from '../modules/navigation/utils';
|
||||
|
||||
interface AudioEncodingConfig {
|
||||
sampleRate: number;
|
||||
numberOfChannels: number;
|
||||
@@ -14,6 +16,7 @@ interface AudioEncodingResult {
|
||||
}
|
||||
|
||||
const logger = new DebugLogger('opus-encoding');
|
||||
const LOCAL_FILE_ASSET_URL = 'assets://local-file';
|
||||
|
||||
// Constants
|
||||
const DEFAULT_BITRATE = 64000;
|
||||
@@ -30,12 +33,61 @@ async function blobToArrayBuffer(
|
||||
if (blob instanceof Blob) {
|
||||
return await blob.arrayBuffer();
|
||||
} else if (blob instanceof Uint8Array) {
|
||||
return blob.buffer instanceof ArrayBuffer
|
||||
? blob.buffer
|
||||
: blob.slice().buffer;
|
||||
} else {
|
||||
return blob;
|
||||
return toArrayBuffer(blob);
|
||||
}
|
||||
return toArrayBuffer(blob);
|
||||
}
|
||||
|
||||
function toArrayBuffer(data: ArrayBuffer | ArrayBufferView): ArrayBuffer {
|
||||
if (data instanceof ArrayBuffer) {
|
||||
return data;
|
||||
}
|
||||
return data.buffer.slice(
|
||||
data.byteOffset,
|
||||
data.byteOffset + data.byteLength
|
||||
) as ArrayBuffer;
|
||||
}
|
||||
|
||||
function getRecordingFileUrl(filepath: string): URL {
|
||||
const base =
|
||||
typeof location !== 'undefined' && location.protocol === 'assets:'
|
||||
? LOCAL_FILE_ASSET_URL
|
||||
: typeof location !== 'undefined'
|
||||
? location.origin
|
||||
: LOCAL_FILE_ASSET_URL;
|
||||
|
||||
// If filepath already contains a protocol, use it directly
|
||||
const fileUrl = isLink(filepath)
|
||||
? new URL(filepath)
|
||||
: new URL(filepath, base);
|
||||
|
||||
if (fileUrl.protocol === 'assets:') {
|
||||
// Force requests to go through the local-file host so the protocol handler
|
||||
// can validate paths correctly.
|
||||
fileUrl.hostname = 'local-file';
|
||||
}
|
||||
|
||||
return fileUrl;
|
||||
}
|
||||
|
||||
async function readRecordingFileBuffer(filepath: string): Promise<ArrayBuffer> {
|
||||
if (apis?.recording?.readRecordingFile) {
|
||||
try {
|
||||
const buffer = await apis.recording.readRecordingFile(filepath);
|
||||
return toArrayBuffer(buffer);
|
||||
} catch (error) {
|
||||
logger.error('Failed to read recording file via IPC', error);
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch(getRecordingFileUrl(filepath));
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to fetch recording file: ${response.status} ${response.statusText}`
|
||||
);
|
||||
}
|
||||
|
||||
return await response.arrayBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,6 +123,10 @@ export function createOpusEncoder(config: AudioEncodingConfig): {
|
||||
encoder: AudioEncoder;
|
||||
encodedChunks: EncodedAudioChunk[];
|
||||
} {
|
||||
if (typeof AudioEncoder === 'undefined') {
|
||||
throw new Error('AudioEncoder is not available in this environment');
|
||||
}
|
||||
|
||||
const encodedChunks: EncodedAudioChunk[] = [];
|
||||
const encoder = new AudioEncoder({
|
||||
output: chunk => {
|
||||
@@ -198,38 +254,14 @@ export async function encodeRawBufferToOpus({
|
||||
numberOfChannels: number;
|
||||
}): Promise<Uint8Array> {
|
||||
logger.debug('Encoding raw buffer to Opus');
|
||||
const response = await fetch(new URL(filepath, location.origin));
|
||||
if (!response.body) {
|
||||
throw new Error('Response body is null');
|
||||
}
|
||||
|
||||
const { encoder, encodedChunks } = createOpusEncoder({
|
||||
sampleRate,
|
||||
numberOfChannels,
|
||||
});
|
||||
|
||||
// Process the stream
|
||||
const reader = response.body.getReader();
|
||||
const chunks: Float32Array[] = [];
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
chunks.push(new Float32Array(value.buffer));
|
||||
}
|
||||
} finally {
|
||||
reader.releaseLock();
|
||||
}
|
||||
|
||||
// Combine all chunks into a single Float32Array
|
||||
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
||||
const audioData = new Float32Array(totalLength);
|
||||
let offset = 0;
|
||||
for (const chunk of chunks) {
|
||||
audioData.set(chunk, offset);
|
||||
offset += chunk.length;
|
||||
}
|
||||
const rawBuffer = await readRecordingFileBuffer(filepath);
|
||||
const audioData = new Float32Array(rawBuffer);
|
||||
|
||||
await encodeAudioFrames({
|
||||
audioData,
|
||||
|
||||
Reference in New Issue
Block a user