From cda9d26e84519c776c0ee3c50b4bfd7e7b79ce68 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Thu, 11 Jul 2024 11:11:07 -0300 Subject: [PATCH 1/2] fix: AAC audio type not playing on web --- apps/meteor/app/api/server/lib/getUploadFormData.ts | 4 ++-- apps/meteor/app/utils/lib/mimeTypes.ts | 6 +++++- .../client/views/room/body/hooks/useFileUploadDropTarget.ts | 2 +- .../MessageBoxActionsToolbar/hooks/useFileUploadAction.ts | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/meteor/app/api/server/lib/getUploadFormData.ts b/apps/meteor/app/api/server/lib/getUploadFormData.ts index 85fc0658542d..3136a6c16e13 100644 --- a/apps/meteor/app/api/server/lib/getUploadFormData.ts +++ b/apps/meteor/app/api/server/lib/getUploadFormData.ts @@ -63,7 +63,7 @@ export async function getUploadFormData< function onFile( fieldname: string, file: Readable & { truncated: boolean }, - { filename, encoding }: { filename: string; encoding: string }, + { filename, encoding, mimeType: mimetype }: { filename: string; encoding: string; mimeType: string }, ) { if (options.field && fieldname !== options.field) { file.resume(); @@ -85,7 +85,7 @@ export async function getUploadFormData< file, filename, encoding, - mimetype: getMimeType(filename), + mimetype: getMimeType(mimetype, filename), fieldname, fields, fileBuffer: Buffer.concat(fileChunks), diff --git a/apps/meteor/app/utils/lib/mimeTypes.ts b/apps/meteor/app/utils/lib/mimeTypes.ts index 909a955d6724..542b7f94f8b6 100644 --- a/apps/meteor/app/utils/lib/mimeTypes.ts +++ b/apps/meteor/app/utils/lib/mimeTypes.ts @@ -12,7 +12,11 @@ const getExtension = (param: string): string => { return !extension || typeof extension === 'boolean' ? '' : extension; }; -const getMimeType = (fileName: string): string => { +const getMimeType = (mimetype: string, fileName: string): string => { + if (mimetype && mimetype !== 'application/octet-stream') { + return mimetype; + } + const fileMimeType = mime.lookup(fileName); return typeof fileMimeType === 'string' ? fileMimeType : 'application/octet-stream'; }; diff --git a/apps/meteor/client/views/room/body/hooks/useFileUploadDropTarget.ts b/apps/meteor/client/views/room/body/hooks/useFileUploadDropTarget.ts index 414b91c52493..314eb64304b5 100644 --- a/apps/meteor/client/views/room/body/hooks/useFileUploadDropTarget.ts +++ b/apps/meteor/client/views/room/body/hooks/useFileUploadDropTarget.ts @@ -54,7 +54,7 @@ export const useFileUploadDropTarget = (): readonly [ const uniqueFiles = getUniqueFiles(); const uploads = Array.from(uniqueFiles).map((file) => { - Object.defineProperty(file, 'type', { value: getMimeType(file.name) }); + Object.defineProperty(file, 'type', { value: getMimeType(file.type, file.name) }); return file; }); diff --git a/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/hooks/useFileUploadAction.ts b/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/hooks/useFileUploadAction.ts index 03229c5dceb3..f911b2b63b1f 100644 --- a/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/hooks/useFileUploadAction.ts +++ b/apps/meteor/client/views/room/composer/messageBox/MessageBoxActionsToolbar/hooks/useFileUploadAction.ts @@ -26,7 +26,7 @@ export const useFileUploadAction = (disabled: boolean): GenericMenuItemProps => const { getMimeType } = await import('../../../../../../../app/utils/lib/mimeTypes'); const filesToUpload = Array.from(fileInputRef?.current?.files ?? []).map((file) => { Object.defineProperty(file, 'type', { - value: getMimeType(file.name), + value: getMimeType(file.type, file.name), }); return file; }); From 649999a259b950000eaf93340de3dab6780467f4 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Thu, 11 Jul 2024 17:20:45 -0300 Subject: [PATCH 2/2] Improve logic and add tests --- apps/meteor/app/utils/lib/mimeTypes.spec.ts | 89 +++++++++++++++++++++ apps/meteor/app/utils/lib/mimeTypes.ts | 9 ++- apps/meteor/jest.config.ts | 1 + 3 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 apps/meteor/app/utils/lib/mimeTypes.spec.ts diff --git a/apps/meteor/app/utils/lib/mimeTypes.spec.ts b/apps/meteor/app/utils/lib/mimeTypes.spec.ts new file mode 100644 index 000000000000..d0fbd4360e24 --- /dev/null +++ b/apps/meteor/app/utils/lib/mimeTypes.spec.ts @@ -0,0 +1,89 @@ +import { expect } from 'chai'; + +import { getExtension, getMimeType } from './mimeTypes'; + +const mimeTypeToExtension = { + 'text/plain': 'txt', + 'image/x-icon': 'ico', + 'image/vnd.microsoft.icon': 'ico', + 'image/png': 'png', + 'image/jpeg': 'jpeg', + 'image/gif': 'gif', + 'image/webp': 'webp', + 'image/svg+xml': 'svg', + 'image/bmp': 'bmp', + 'image/tiff': 'tif', + 'audio/wav': 'wav', + 'audio/wave': 'wav', + 'audio/aac': 'aac', + 'audio/x-aac': 'aac', + 'audio/mp4': 'm4a', + 'audio/mpeg': 'mpga', + 'audio/ogg': 'oga', + 'application/octet-stream': 'bin', +}; + +const extensionToMimeType = { + lst: 'text/plain', + txt: 'text/plain', + ico: 'image/x-icon', + png: 'image/png', + jpeg: 'image/jpeg', + gif: 'image/gif', + webp: 'image/webp', + svg: 'image/svg+xml', + bmp: 'image/bmp', + tiff: 'image/tiff', + tif: 'image/tiff', + wav: 'audio/wav', + aac: 'audio/aac', + mp3: 'audio/mpeg', + ogg: 'audio/ogg', + oga: 'audio/ogg', + m4a: 'audio/mp4', + mpga: 'audio/mpeg', + mp4: 'video/mp4', + bin: 'application/octet-stream', +}; + +describe('mimeTypes', () => { + describe('getExtension', () => { + for (const [mimeType, extension] of Object.entries(mimeTypeToExtension)) { + it(`should return the correct extension ${extension} for the given mimeType ${mimeType}`, async () => { + expect(getExtension(mimeType)).to.be.eql(extension); + }); + } + + it('should return an empty string if the mimeType is not found', async () => { + expect(getExtension('application/unknown')).to.be.eql(''); + }); + }); + + describe('getMimeType', () => { + for (const [extension, mimeType] of Object.entries(extensionToMimeType)) { + it(`should return the correct mimeType ${mimeType} for the given fileName file.${extension} passing the correct mimeType`, async () => { + expect(getMimeType(mimeType, `file.${extension}`)).to.be.eql(mimeType); + }); + } + + it('should return the correct mimeType for the given fileName', async () => { + for (const [extension, mimeType] of Object.entries(extensionToMimeType)) { + expect(getMimeType('application/unknown', `file.${extension}`)).to.be.eql(mimeType); + } + }); + + it('should return the correct mimeType for the given fileName when informed mimeType is application/octet-stream', async () => { + for (const [extension, mimeType] of Object.entries(extensionToMimeType)) { + expect(getMimeType('application/octet-stream', `file.${extension}`)).to.be.eql(mimeType); + } + }); + + it('should return the mimeType if it is not application/octet-stream', async () => { + expect(getMimeType('audio/wav', 'file.wav')).to.be.eql('audio/wav'); + }); + + it('should return application/octet-stream if the mimeType is not found', async () => { + expect(getMimeType('application/octet-stream', 'file.unknown')).to.be.eql('application/octet-stream'); + }); + }); +}); diff --git a/apps/meteor/app/utils/lib/mimeTypes.ts b/apps/meteor/app/utils/lib/mimeTypes.ts index 542b7f94f8b6..df670145b494 100644 --- a/apps/meteor/app/utils/lib/mimeTypes.ts +++ b/apps/meteor/app/utils/lib/mimeTypes.ts @@ -3,8 +3,8 @@ import mime from 'mime-type/with-db'; mime.types.wav = 'audio/wav'; mime.types.lst = 'text/plain'; mime.define('image/vnd.microsoft.icon', { source: '', extensions: ['ico'] }, mime.dupAppend); -mime.define('image/x-icon', { source: '', extensions: ['ico'] }, mime.dupAppend); -mime.types.ico = 'image/x-icon'; +mime.define('image/x-icon', { source: '', extensions: ['ico'] }, mime.dupOverwrite); +mime.define('audio/aac', { source: '', extensions: ['aac'] }, mime.dupOverwrite); const getExtension = (param: string): string => { const extension = mime.extension(param); @@ -13,7 +13,10 @@ const getExtension = (param: string): string => { }; const getMimeType = (mimetype: string, fileName: string): string => { - if (mimetype && mimetype !== 'application/octet-stream') { + // If the extension from the mimetype is different from the file extension, the file + // extension may be wrong so use the informed mimetype + const extension = mime.extension(mimetype); + if (mimetype !== 'application/octet-stream' && extension && extension !== fileName.split('.').pop()) { return mimetype; } diff --git a/apps/meteor/jest.config.ts b/apps/meteor/jest.config.ts index 72538cf14d16..fb9c6f1247a9 100644 --- a/apps/meteor/jest.config.ts +++ b/apps/meteor/jest.config.ts @@ -34,6 +34,7 @@ const config: Config = { '/app/livechat/server/api/**/*.spec.ts', '/ee/app/authorization/server/validateUserRoles.spec.ts', '/app/cloud/server/functions/supportedVersionsToken/**.spec.ts', + '/app/utils/lib/**.spec.ts', ], transformIgnorePatterns: ['!/node_modules/jose'], errorOnDeprecated: true,