From dfa354bf74a5ad3c151fe8a803144400f977cca5 Mon Sep 17 00:00:00 2001 From: N-Pex Date: Wed, 10 Sep 2025 18:06:41 +0200 Subject: [PATCH] Fix sending audio and video files --- src/components/RoomExport.vue | 9 ++ src/components/chatMixin.js | 4 +- src/components/file_mode/ThumbnailView.vue | 6 +- .../messages/composition/MessageThread.vue | 6 +- src/plugins/utils.js | 97 +++++++++---------- src/services/audio.service.js | 9 +- 6 files changed, 66 insertions(+), 65 deletions(-) diff --git a/src/components/RoomExport.vue b/src/components/RoomExport.vue index c9728a4..eda85df 100644 --- a/src/components/RoomExport.vue +++ b/src/components/RoomExport.vue @@ -393,6 +393,7 @@ export default { break; case "image/gif": extension = ".gif"; + break; } let fileName = event.getId() + extension; @@ -407,6 +408,14 @@ export default { } } else if (mime.startsWith("audio/")) { var extension = ".webm"; + switch (mime) { + case "audio/mpeg": + extension = ".mp3"; + break; + case "audio/x-m4a": + extension = ".m4a"; + break; + } let fileName = event.getId() + extension; audioFolder.file(fileName, blob); // TODO calc bytes let elements = comp.el.getElementsByTagName("audio"); diff --git a/src/components/chatMixin.js b/src/components/chatMixin.js index 5ed5410..85bee75 100644 --- a/src/components/chatMixin.js +++ b/src/components/chatMixin.js @@ -208,7 +208,7 @@ export default { if (isForExport) { return MessageIncomingVideoExport; } - return MessageVideo; + return MessageThread; } else if (event.getContent().msgtype == "m.file") { if (isForExport) { return MessageIncomingFileExport; @@ -255,7 +255,7 @@ export default { if (isForExport) { return MessageOutgoingVideoExport; } - return MessageVideo; + return MessageThread; } else if (event.getContent().msgtype == "m.file") { if (isForExport) { return MessageOutgoingFileExport; diff --git a/src/components/file_mode/ThumbnailView.vue b/src/components/file_mode/ThumbnailView.vue index c405a3d..7922ee4 100644 --- a/src/components/file_mode/ThumbnailView.vue +++ b/src/components/file_mode/ThumbnailView.vue @@ -64,7 +64,7 @@ const poster: Ref = ref(undefined); const updateSource = () => { if (isEventAttachment(props.item)) { const eventAttachment = props.item; - if (isVideo.value || eventAttachment.src) { + if (eventAttachment.src) { source.value = eventAttachment.src; } else if (previewOnly) { eventAttachment.loadThumbnail().then((url) => { @@ -74,6 +74,10 @@ const updateSource = () => { eventAttachment.loadSrc().then((url) => { source.value = url.data; }) + } else if (isVideo.value) { + eventAttachment.loadSrc().then((url) => { + source.value = url.data; + }) } } else if (isAttachment(props.item)) { const attachment = props.item; diff --git a/src/components/messages/composition/MessageThread.vue b/src/components/messages/composition/MessageThread.vue index f85ca25..05b2f4b 100644 --- a/src/components/messages/composition/MessageThread.vue +++ b/src/components/messages/composition/MessageThread.vue @@ -128,7 +128,7 @@ watch( event, () => { if (event.value) { - if (event.value.getContent().msgtype == "m.image") { + if (["m.image", "m.video"].includes(event.value.getContent().msgtype ?? "")) { // Single image mode items.value = [event.value].map((e: MatrixEvent) => { let ea = $matrix.attachmentManager.getEventAttachment(e); @@ -157,14 +157,14 @@ onBeforeUnmount(() => { }); const showMessageText = computed((): boolean => { - if (event.value?.getContent().msgtype == "m.image") { + if (["m.image", "m.video"].includes(event.value?.getContent().msgtype ?? "")) { return false; } return true; }); const showMultiview = computed((): boolean => { - if (event.value?.getContent().msgtype == "m.image") { + if (["m.image", "m.video"].includes(event.value?.getContent().msgtype ?? "")) { return true; } return ( diff --git a/src/plugins/utils.js b/src/plugins/utils.js index 500b03b..a980545 100644 --- a/src/plugins/utils.js +++ b/src/plugins/utils.js @@ -47,21 +47,6 @@ class Util { return Thread.hasServerSideSupport ? "m.thread" : "io.element.thread"; } - getAttachmentUrlAndDuration(event) { - return new Promise((resolve, reject) => { - const content = event.getContent(); - if (content.url != null) { - resolve([content.url, content.info.duration]); - return; - } - if (content.file && content.file.url) { - resolve([content.file.url, content.info.duration]); - } else { - reject("No url found!"); - } - }); - } - getAttachment(matrixClient, useAuthedMedia, event, progressCallback, asBlob = false, abortController = undefined) { return new Promise((resolve, reject) => { const content = event.getContent(); @@ -577,7 +562,7 @@ class Util { // Generate audio waveforms if (msgtype == "m.audio") { - this.generateWaveform(fileContents, messageContent); + await this.generateWaveform(fileContents, messageContent); } const result = await this.sendMessage(matrixClient, roomId, "m.room.message", messageContent); @@ -594,46 +579,56 @@ class Util { return uploadPromise; } - generateWaveform(data, messageContent) { + async generateWaveform(data, messageContent) { if (!(window.AudioContext || window.webkitAudioContext)) { return; // No support } - const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); - if (audioCtx) { - return audioCtx.decodeAudioData(data).then((audioBuffer) => { - const rawData = audioBuffer.getChannelData(0); // TODO - currently using only 1 channel - const samples = 1000; // Number of samples - const blockSize = Math.floor(rawData.length / samples); - let filteredData = []; - for (let i = 0; i < samples; i++) { - let blockStart = blockSize * i; // the location of the first sample in the block - let sum = 0; - for (let j = 0; j < blockSize; j++) { - sum = sum + Math.abs(rawData[blockStart + j]); // find the sum of all the samples in the block + try { + const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); + if (audioCtx) { + const audioBuffer = await audioCtx.decodeAudioData(data); + if (audioBuffer) { + const rawData = audioBuffer.getChannelData(0); // TODO - currently using only 1 channel + const samples = 1000; // Number of samples + const blockSize = Math.floor(rawData.length / samples); + let filteredData = []; + for (let i = 0; i < samples; i++) { + let blockStart = blockSize * i; // the location of the first sample in the block + let sum = 0; + for (let j = 0; j < blockSize; j++) { + sum = sum + Math.abs(rawData[blockStart + j]); // find the sum of all the samples in the block + } + filteredData.push(sum / blockSize); // divide the sum by the block size to get the average + } + + // Normalize + const multiplier = Math.pow(Math.max(...filteredData), -1); + filteredData = filteredData.map((n) => n * multiplier); + + // Integerize + filteredData = filteredData.map((n) => parseInt((n * 255).toFixed())); + + // Generate SVG of waveform + let svg = ``; + svg += ``; + svg += ""; + + messageContent.format = "org.matrix.custom.html"; + messageContent.formatted_body = svg; + + // if duration is not set, do that here, since we have it + if (!messageContent.info.duration) { + messageContent.info.duration = parseInt((1000 * audioBuffer.duration).toFixed()); } - filteredData.push(sum / blockSize); // divide the sum by the block size to get the average } - - // Normalize - const multiplier = Math.pow(Math.max(...filteredData), -1); - filteredData = filteredData.map((n) => n * multiplier); - - // Integerize - filteredData = filteredData.map((n) => parseInt((n * 255).toFixed())); - - // Generate SVG of waveform - let svg = ``; - svg += ``; - svg += ""; - - messageContent.format = "org.matrix.custom.html"; - messageContent.formatted_body = svg; - }); + } + } catch (error) { + return; } } diff --git a/src/services/audio.service.js b/src/services/audio.service.js index 2c291f1..586df82 100644 --- a/src/services/audio.service.js +++ b/src/services/audio.service.js @@ -46,14 +46,7 @@ export default { this.infoMap.set(eventId, entry); // Get duration information - utils - .getAttachmentUrlAndDuration(event) - .then(([ignoredurl, duration]) => { - entry.duration = duration; - }) - .catch((err) => { - console.error("Failed to fetch attachment duration: ", err); - }); + entry.duration = event.getContent()?.info?.duration ?? 0; } entry.listeners.add(uid); return entry;