Encrypt the voice message file if needed
Fixes https://github.com/vector-im/element-web/issues/17729 "oops, should have done that"
This commit is contained in:
parent
6d6995e6fb
commit
cb0d2a2c4f
3 changed files with 21 additions and 20 deletions
|
@ -307,7 +307,7 @@ function readFileAsArrayBuffer(file: File | Blob): Promise<ArrayBuffer> {
|
||||||
* If the file is unencrypted then the object will have a "url" key.
|
* If the file is unencrypted then the object will have a "url" key.
|
||||||
* If the file is encrypted then the object will have a "file" key.
|
* If the file is encrypted then the object will have a "file" key.
|
||||||
*/
|
*/
|
||||||
function uploadFile(
|
export function uploadFile(
|
||||||
matrixClient: MatrixClient,
|
matrixClient: MatrixClient,
|
||||||
roomId: string,
|
roomId: string,
|
||||||
file: File | Blob,
|
file: File | Blob,
|
||||||
|
|
|
@ -65,12 +65,13 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.state.recorder.stop();
|
await this.state.recorder.stop();
|
||||||
const mxc = await this.state.recorder.upload();
|
const upload = await this.state.recorder.upload(this.props.room.roomId);
|
||||||
MatrixClientPeg.get().sendMessage(this.props.room.roomId, {
|
MatrixClientPeg.get().sendMessage(this.props.room.roomId, {
|
||||||
"body": "Voice message",
|
"body": "Voice message",
|
||||||
//"msgtype": "org.matrix.msc2516.voice",
|
//"msgtype": "org.matrix.msc2516.voice",
|
||||||
"msgtype": MsgType.Audio,
|
"msgtype": MsgType.Audio,
|
||||||
"url": mxc,
|
"url": upload.mxc,
|
||||||
|
"file": upload.encrypted,
|
||||||
"info": {
|
"info": {
|
||||||
duration: Math.round(this.state.recorder.durationSeconds * 1000),
|
duration: Math.round(this.state.recorder.durationSeconds * 1000),
|
||||||
mimetype: this.state.recorder.contentType,
|
mimetype: this.state.recorder.contentType,
|
||||||
|
@ -81,7 +82,8 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
|
||||||
// https://github.com/matrix-org/matrix-doc/pull/3245
|
// https://github.com/matrix-org/matrix-doc/pull/3245
|
||||||
"org.matrix.msc1767.text": "Voice message",
|
"org.matrix.msc1767.text": "Voice message",
|
||||||
"org.matrix.msc1767.file": {
|
"org.matrix.msc1767.file": {
|
||||||
url: mxc,
|
"url": upload.mxc,
|
||||||
|
"file": upload.encrypted,
|
||||||
name: "Voice message.ogg",
|
name: "Voice message.ogg",
|
||||||
mimetype: this.state.recorder.contentType,
|
mimetype: this.state.recorder.contentType,
|
||||||
size: this.state.recorder.contentLength,
|
size: this.state.recorder.contentLength,
|
||||||
|
|
|
@ -27,6 +27,8 @@ import {PayloadEvent, WORKLET_NAME} from "./consts";
|
||||||
import {UPDATE_EVENT} from "../stores/AsyncStore";
|
import {UPDATE_EVENT} from "../stores/AsyncStore";
|
||||||
import {Playback} from "./Playback";
|
import {Playback} from "./Playback";
|
||||||
import {createAudioContext} from "./compat";
|
import {createAudioContext} from "./compat";
|
||||||
|
import { IEncryptedFile } from "matrix-js-sdk/src/@types/event";
|
||||||
|
import { uploadFile } from "../ContentMessages";
|
||||||
|
|
||||||
const CHANNELS = 1; // stereo isn't important
|
const CHANNELS = 1; // stereo isn't important
|
||||||
export const SAMPLE_RATE = 48000; // 48khz is what WebRTC uses. 12khz is where we lose quality.
|
export const SAMPLE_RATE = 48000; // 48khz is what WebRTC uses. 12khz is where we lose quality.
|
||||||
|
@ -49,6 +51,11 @@ export enum RecordingState {
|
||||||
Uploaded = "uploaded",
|
Uploaded = "uploaded",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IUpload {
|
||||||
|
mxc?: string; // for unencrypted uploads
|
||||||
|
encrypted?: IEncryptedFile;
|
||||||
|
}
|
||||||
|
|
||||||
export class VoiceRecording extends EventEmitter implements IDestroyable {
|
export class VoiceRecording extends EventEmitter implements IDestroyable {
|
||||||
private recorder: Recorder;
|
private recorder: Recorder;
|
||||||
private recorderContext: AudioContext;
|
private recorderContext: AudioContext;
|
||||||
|
@ -58,7 +65,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
|
||||||
private recorderWorklet: AudioWorkletNode;
|
private recorderWorklet: AudioWorkletNode;
|
||||||
private recorderProcessor: ScriptProcessorNode;
|
private recorderProcessor: ScriptProcessorNode;
|
||||||
private buffer = new Uint8Array(0); // use this.audioBuffer to access
|
private buffer = new Uint8Array(0); // use this.audioBuffer to access
|
||||||
private mxc: string;
|
private lastUpload: IUpload;
|
||||||
private recording = false;
|
private recording = false;
|
||||||
private observable: SimpleObservable<IRecordingUpdate>;
|
private observable: SimpleObservable<IRecordingUpdate>;
|
||||||
private amplitudes: number[] = []; // at each second mark, generated
|
private amplitudes: number[] = []; // at each second mark, generated
|
||||||
|
@ -214,13 +221,6 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
|
||||||
return this.buffer.length > 0;
|
return this.buffer.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get mxcUri(): string {
|
|
||||||
if (!this.mxc) {
|
|
||||||
throw new Error("Recording has not been uploaded yet");
|
|
||||||
}
|
|
||||||
return this.mxc;
|
|
||||||
}
|
|
||||||
|
|
||||||
private onAudioProcess = (ev: AudioProcessingEvent) => {
|
private onAudioProcess = (ev: AudioProcessingEvent) => {
|
||||||
this.processAudioUpdate(ev.playbackTime);
|
this.processAudioUpdate(ev.playbackTime);
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
|
||||||
};
|
};
|
||||||
|
|
||||||
public async start(): Promise<void> {
|
public async start(): Promise<void> {
|
||||||
if (this.mxc || this.hasRecording) {
|
if (this.lastUpload || this.hasRecording) {
|
||||||
throw new Error("Recording already prepared");
|
throw new Error("Recording already prepared");
|
||||||
}
|
}
|
||||||
if (this.recording) {
|
if (this.recording) {
|
||||||
|
@ -362,20 +362,19 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
|
||||||
this.observable.close();
|
this.observable.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async upload(): Promise<string> {
|
public async upload(inRoomId: string): Promise<IUpload> {
|
||||||
if (!this.hasRecording) {
|
if (!this.hasRecording) {
|
||||||
throw new Error("No recording available to upload");
|
throw new Error("No recording available to upload");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.mxc) return this.mxc;
|
if (this.lastUpload) return this.lastUpload;
|
||||||
|
|
||||||
this.emit(RecordingState.Uploading);
|
this.emit(RecordingState.Uploading);
|
||||||
this.mxc = await this.client.uploadContent(new Blob([this.audioBuffer], {
|
const { url: mxc, file: encrypted } = await uploadFile(this.client, inRoomId, new Blob([this.audioBuffer], {
|
||||||
type: this.contentType,
|
type: this.contentType,
|
||||||
}), {
|
}));
|
||||||
onlyContentUri: false, // to stop the warnings in the console
|
this.lastUpload = {mxc, encrypted};
|
||||||
}).then(r => r['content_uri']);
|
|
||||||
this.emit(RecordingState.Uploaded);
|
this.emit(RecordingState.Uploaded);
|
||||||
return this.mxc;
|
return this.lastUpload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue