feat(audio): play m.file audio messages inline like m.audio
Audio frequently arrives as m.file (bridges, other clients, or when the browser reported a non-audio/* mime on upload) and only got a download button. Detect audio in the m.file branch (by info.mimetype or filename extension) and render the existing MAudio inline player, falling back to the file card otherwise. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -31,7 +31,29 @@ import { ImageViewer } from './image-viewer';
|
||||
import { PdfViewer } from './Pdf-viewer';
|
||||
import { TextViewer } from './text-viewer';
|
||||
import { testMatrixTo } from '../plugins/matrix-to';
|
||||
import { IImageContent } from '../../types/matrix/common';
|
||||
import { IAudioContent, IFileContent, IImageContent } from '../../types/matrix/common';
|
||||
|
||||
// Audio is frequently sent as m.file (bridges/other clients, or when the browser
|
||||
// reported a non-audio/* mime on upload). Detect that so we can play it inline
|
||||
// like m.audio instead of showing only a download button.
|
||||
const AUDIO_EXT_MIME: Record<string, string> = {
|
||||
mp3: 'audio/mpeg',
|
||||
m4a: 'audio/mp4',
|
||||
aac: 'audio/aac',
|
||||
oga: 'audio/ogg',
|
||||
ogg: 'audio/ogg',
|
||||
opus: 'audio/ogg',
|
||||
wav: 'audio/wav',
|
||||
flac: 'audio/flac',
|
||||
weba: 'audio/webm',
|
||||
};
|
||||
const resolveInlineAudioMime = (content: IFileContent): string | undefined => {
|
||||
const mime = content.info?.mimetype;
|
||||
if (typeof mime === 'string' && mime.startsWith('audio')) return mime;
|
||||
const name = content.filename ?? content.body ?? '';
|
||||
const ext = name.split('.').pop()?.toLowerCase();
|
||||
return ext ? AUDIO_EXT_MIME[ext] : undefined;
|
||||
};
|
||||
|
||||
type RenderMessageContentProps = {
|
||||
displayName: string;
|
||||
@@ -276,6 +298,29 @@ export function RenderMessageContent({
|
||||
}
|
||||
|
||||
if (msgType === MsgType.File) {
|
||||
// If an m.file is actually audio, play it inline (like m.audio) instead of
|
||||
// only offering a download. MAudio falls back to renderFile if playback fails.
|
||||
const audioMime = resolveInlineAudioMime(getContent<IFileContent>());
|
||||
if (audioMime) {
|
||||
const fileContent = getContent<IFileContent>();
|
||||
const audioContent = {
|
||||
...fileContent,
|
||||
info: { ...(fileContent.info ?? {}), mimetype: audioMime },
|
||||
} as unknown as IAudioContent;
|
||||
return (
|
||||
<>
|
||||
<MAudio
|
||||
content={audioContent}
|
||||
renderAsFile={renderFile}
|
||||
renderAudioContent={(props) => (
|
||||
<AudioContent {...props} renderMediaControl={(p) => <MediaControl {...p} />} />
|
||||
)}
|
||||
outlined={outlineAttachment}
|
||||
/>
|
||||
{renderCaption()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
return renderFile();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user