Ver código fonte

feat(server): Support webm videos (#1365)

* feat(server): Support webm without transcoding.

Transcoding result doesn't appear to be used by anything expect for quicktime.

* feat(server): Fix the asset uploader for .avi

It needs to be transcoded.

* feat(server): Most browsers doesn't support avi so use mp4.

* feat(server): Address PR comments

* Addressed the PR comments

I moved the function that checks the mimetype to a central location in asset-utils and made tests for it.

* Rollbacked to the way transcoder was decising things to transcode.
Skyler Mäntysaari 2 anos atrás
pai
commit
8eb82836b9

+ 2 - 2
server/apps/immich/src/api-v1/asset/asset.service.ts

@@ -37,7 +37,7 @@ import {
 import { GetAssetCountByTimeBucketDto } from './dto/get-asset-count-by-time-bucket.dto';
 import { GetAssetByTimeBucketDto } from './dto/get-asset-by-time-bucket.dto';
 import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.dto';
-import { timeUtils } from '@app/common/utils';
+import { assetUtils, timeUtils } from '@app/common/utils';
 import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto';
 import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto';
 import { UpdateAssetDto } from './dto/update-asset.dto';
@@ -456,7 +456,7 @@ export class AssetService {
 
         await fs.access(videoPath, constants.R_OK | constants.W_OK);
 
-        if (query.isWeb && asset.mimeType == 'video/quicktime') {
+        if (query.isWeb && !assetUtils.isWebPlayable(asset.mimeType)) {
           videoPath = asset.encodedVideoPath == '' ? String(asset.originalPath) : String(asset.encodedVideoPath);
           mimeType = asset.encodedVideoPath == '' ? asset.mimeType : 'video/mp4';
         }

+ 6 - 0
server/apps/immich/src/config/asset-upload.config.spec.ts

@@ -60,6 +60,12 @@ describe('assetUploadOption', () => {
       expect(callback).toHaveBeenCalledWith(null, true);
     });
 
+    it('should allow webm videos', async () => {
+      const file = { mimetype: 'video/webm', originalname: 'test.webm' } as any;
+      fileFilter(mock.userRequest, file, callback);
+      expect(callback).toHaveBeenCalledWith(null, true);
+    });
+
     it('should not allow unknown types', async () => {
       const file = { mimetype: 'application/html', originalname: 'test.html' } as any;
       const callback = jest.fn();

+ 1 - 1
server/apps/immich/src/config/asset-upload.config.ts

@@ -28,7 +28,7 @@ function fileFilter(req: Request, file: any, cb: any) {
   }
   if (
     file.mimetype.match(
-      /\/(jpg|jpeg|png|gif|mp4|x-msvideo|quicktime|heic|heif|dng|x-adobe-dng|webp|tiff|3gpp|nef|x-nikon-nef)$/,
+      /\/(jpg|jpeg|png|gif|mp4|webm|x-msvideo|quicktime|heic|heif|dng|x-adobe-dng|webp|tiff|3gpp|nef|x-nikon-nef)$/,
     )
   ) {
     cb(null, true);

+ 20 - 0
server/libs/common/src/utils/asset-utils.spec.ts

@@ -0,0 +1,20 @@
+import { assetUtils } from './asset-utils';
+
+describe('Asset Utilities', () => {
+  describe('isWebPlayable', () => {
+    it('Check that it returns true with mimetype webm', () => {
+      const result = assetUtils.isWebPlayable('video/webm');
+      expect(result).toBeTruthy();
+    });
+
+    it('Check that returns true with mimetype mp4', () => {
+      const result = assetUtils.isWebPlayable('video/mp4');
+      expect(result).toBeTruthy();
+    });
+
+    it('Check that returns false with mimetype quicktime', () => {
+      const result = assetUtils.isWebPlayable('video/quicktime');
+      expect(result).toBeFalsy();
+    });
+  });
+});

+ 10 - 1
server/libs/common/src/utils/asset-utils.ts

@@ -36,4 +36,13 @@ const deleteFiles = (asset: AssetEntity | AssetResponseDto) => {
   }
 };
 
-export const assetUtils = { deleteFiles };
+const isWebPlayable = (mimeType: string | null): boolean => {
+  const WEB_PLAYABLE = ['video/webm', 'video/mp4'];
+
+  if (mimeType !== null) {
+    return WEB_PLAYABLE.includes(mimeType);
+  }
+  return false;
+};
+
+export const assetUtils = { deleteFiles, isWebPlayable };