Skip to content

Commit

Permalink
refactor(server): calculate asset type server side (#3200)
Browse files Browse the repository at this point in the history
* refactor(server): calculate asset type server-side

* chore: open api

* chore: remove comments

* fix: linting

* update

* Revert "update"

This reverts commit dc58702.

* fix: upload LivePhotos

* chore: remove unused request fields for upload

* remove unused method

* mobile-fix: livePhoto filename

* fix: revert check for livephotos filename and extension

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
  • Loading branch information
jrasm91 and alextran1502 authored Jul 12, 2023
1 parent 93462aa commit b71d7e3
Show file tree
Hide file tree
Showing 24 changed files with 97 additions and 580 deletions.
48 changes: 5 additions & 43 deletions cli/src/api/open-api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1375,12 +1375,6 @@ export interface GetAssetCountByTimeBucketDto {
* @interface ImportAssetDto
*/
export interface ImportAssetDto {
/**
*
* @type {AssetTypeEnum}
* @memberof ImportAssetDto
*/
'assetType': AssetTypeEnum;
/**
*
* @type {boolean}
Expand Down Expand Up @@ -1448,8 +1442,6 @@ export interface ImportAssetDto {
*/
'duration'?: string;
}


/**
*
* @export
Expand Down Expand Up @@ -5690,9 +5682,7 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
},
/**
*
* @param {AssetTypeEnum} assetType
* @param {File} assetData
* @param {string} fileExtension
* @param {string} deviceAssetId
* @param {string} deviceId
* @param {string} fileCreatedAt
Expand All @@ -5708,13 +5698,9 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
uploadFile: async (assetType: AssetTypeEnum, assetData: File, fileExtension: string, deviceAssetId: string, deviceId: string, fileCreatedAt: string, fileModifiedAt: string, isFavorite: boolean, key?: string, livePhotoData?: File, sidecarData?: File, isReadOnly?: boolean, isArchived?: boolean, isVisible?: boolean, duration?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'assetType' is not null or undefined
assertParamExists('uploadFile', 'assetType', assetType)
uploadFile: async (assetData: File, deviceAssetId: string, deviceId: string, fileCreatedAt: string, fileModifiedAt: string, isFavorite: boolean, key?: string, livePhotoData?: File, sidecarData?: File, isReadOnly?: boolean, isArchived?: boolean, isVisible?: boolean, duration?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'assetData' is not null or undefined
assertParamExists('uploadFile', 'assetData', assetData)
// verify required parameter 'fileExtension' is not null or undefined
assertParamExists('uploadFile', 'fileExtension', fileExtension)
// verify required parameter 'deviceAssetId' is not null or undefined
assertParamExists('uploadFile', 'deviceAssetId', deviceAssetId)
// verify required parameter 'deviceId' is not null or undefined
Expand Down Expand Up @@ -5752,10 +5738,6 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
}


if (assetType !== undefined) {
localVarFormParams.append('assetType', new Blob([JSON.stringify(assetType)], { type: "application/json", }));
}

if (assetData !== undefined) {
localVarFormParams.append('assetData', assetData as any);
}
Expand All @@ -5772,10 +5754,6 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
localVarFormParams.append('isReadOnly', isReadOnly as any);
}

if (fileExtension !== undefined) {
localVarFormParams.append('fileExtension', fileExtension as any);
}

if (deviceAssetId !== undefined) {
localVarFormParams.append('deviceAssetId', deviceAssetId as any);
}
Expand Down Expand Up @@ -6089,9 +6067,7 @@ export const AssetApiFp = function(configuration?: Configuration) {
},
/**
*
* @param {AssetTypeEnum} assetType
* @param {File} assetData
* @param {string} fileExtension
* @param {string} deviceAssetId
* @param {string} deviceId
* @param {string} fileCreatedAt
Expand All @@ -6107,8 +6083,8 @@ export const AssetApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async uploadFile(assetType: AssetTypeEnum, assetData: File, fileExtension: string, deviceAssetId: string, deviceId: string, fileCreatedAt: string, fileModifiedAt: string, isFavorite: boolean, key?: string, livePhotoData?: File, sidecarData?: File, isReadOnly?: boolean, isArchived?: boolean, isVisible?: boolean, duration?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AssetFileUploadResponseDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.uploadFile(assetType, assetData, fileExtension, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, isFavorite, key, livePhotoData, sidecarData, isReadOnly, isArchived, isVisible, duration, options);
async uploadFile(assetData: File, deviceAssetId: string, deviceId: string, fileCreatedAt: string, fileModifiedAt: string, isFavorite: boolean, key?: string, livePhotoData?: File, sidecarData?: File, isReadOnly?: boolean, isArchived?: boolean, isVisible?: boolean, duration?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AssetFileUploadResponseDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.uploadFile(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, isFavorite, key, livePhotoData, sidecarData, isReadOnly, isArchived, isVisible, duration, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
}
Expand Down Expand Up @@ -6339,7 +6315,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
* @throws {RequiredError}
*/
uploadFile(requestParameters: AssetApiUploadFileRequest, options?: AxiosRequestConfig): AxiosPromise<AssetFileUploadResponseDto> {
return localVarFp.uploadFile(requestParameters.assetType, requestParameters.assetData, requestParameters.fileExtension, requestParameters.deviceAssetId, requestParameters.deviceId, requestParameters.fileCreatedAt, requestParameters.fileModifiedAt, requestParameters.isFavorite, requestParameters.key, requestParameters.livePhotoData, requestParameters.sidecarData, requestParameters.isReadOnly, requestParameters.isArchived, requestParameters.isVisible, requestParameters.duration, options).then((request) => request(axios, basePath));
return localVarFp.uploadFile(requestParameters.assetData, requestParameters.deviceAssetId, requestParameters.deviceId, requestParameters.fileCreatedAt, requestParameters.fileModifiedAt, requestParameters.isFavorite, requestParameters.key, requestParameters.livePhotoData, requestParameters.sidecarData, requestParameters.isReadOnly, requestParameters.isArchived, requestParameters.isVisible, requestParameters.duration, options).then((request) => request(axios, basePath));
},
};
};
Expand Down Expand Up @@ -6763,27 +6739,13 @@ export interface AssetApiUpdateAssetRequest {
* @interface AssetApiUploadFileRequest
*/
export interface AssetApiUploadFileRequest {
/**
*
* @type {AssetTypeEnum}
* @memberof AssetApiUploadFile
*/
readonly assetType: AssetTypeEnum

/**
*
* @type {File}
* @memberof AssetApiUploadFile
*/
readonly assetData: File

/**
*
* @type {string}
* @memberof AssetApiUploadFile
*/
readonly fileExtension: string

/**
*
* @type {string}
Expand Down Expand Up @@ -7143,7 +7105,7 @@ export class AssetApi extends BaseAPI {
* @memberof AssetApi
*/
public uploadFile(requestParameters: AssetApiUploadFileRequest, options?: AxiosRequestConfig) {
return AssetApiFp(this.configuration).uploadFile(requestParameters.assetType, requestParameters.assetData, requestParameters.fileExtension, requestParameters.deviceAssetId, requestParameters.deviceId, requestParameters.fileCreatedAt, requestParameters.fileModifiedAt, requestParameters.isFavorite, requestParameters.key, requestParameters.livePhotoData, requestParameters.sidecarData, requestParameters.isReadOnly, requestParameters.isArchived, requestParameters.isVisible, requestParameters.duration, options).then((request) => request(this.axios, this.basePath));
return AssetApiFp(this.configuration).uploadFile(requestParameters.assetData, requestParameters.deviceAssetId, requestParameters.deviceId, requestParameters.fileCreatedAt, requestParameters.fileModifiedAt, requestParameters.isFavorite, requestParameters.key, requestParameters.livePhotoData, requestParameters.sidecarData, requestParameters.isReadOnly, requestParameters.isArchived, requestParameters.isVisible, requestParameters.duration, options).then((request) => request(this.axios, this.basePath));
}
}

Expand Down
3 changes: 0 additions & 3 deletions cli/src/commands/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ export default class Upload extends BaseCommand {
const importData = {
assetPath: asset.path,
deviceAssetId: asset.deviceAssetId,
assetType: asset.assetType,
deviceId: this.deviceId,
fileCreatedAt: asset.fileCreatedAt,
fileModifiedAt: asset.fileModifiedAt,
Expand Down Expand Up @@ -157,8 +156,6 @@ export default class Upload extends BaseCommand {
uploadFormData.append('fileCreatedAt', asset.fileCreatedAt);
uploadFormData.append('fileModifiedAt', asset.fileModifiedAt);
uploadFormData.append('isFavorite', String(false));
uploadFormData.append('fileExtension', asset.fileExtension);
uploadFormData.append('assetType', asset.assetType);
uploadFormData.append('assetData', asset.assetData, { filename: asset.path });

if (asset.sidecarData) {
Expand Down
13 changes: 0 additions & 13 deletions cli/src/cores/models/crawled-asset.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
import * as fs from 'fs';
import * as mime from 'mime-types';
import { basename } from 'node:path';
import * as path from 'path';
import crypto from 'crypto';
import { AssetTypeEnum } from 'src/api/open-api';

export class CrawledAsset {
public path: string;

public assetType?: AssetTypeEnum;
public assetData?: fs.ReadStream;
public deviceAssetId?: string;
public fileCreatedAt?: string;
public fileModifiedAt?: string;
public fileExtension?: string;
public sidecarData?: Buffer;
public sidecarPath?: string;
public fileSize!: number;
Expand All @@ -30,16 +25,8 @@ export class CrawledAsset {
async process() {
const stats = await fs.promises.stat(this.path);
this.deviceAssetId = `${basename(this.path)}-${stats.size}`.replace(/\s+/g, '');

// TODO: Determine file type from extension only
const mimeType = mime.lookup(this.path);
if (!mimeType) {
throw Error('Cannot determine mime type of asset: ' + this.path);
}
this.assetType = mimeType.split('/')[0].toUpperCase() as AssetTypeEnum;
this.fileCreatedAt = stats.ctime.toISOString();
this.fileModifiedAt = stats.mtime.toISOString();
this.fileExtension = path.extname(this.path);
this.fileSize = stats.size;

// TODO: doesn't xmp replace the file extension? Will need investigation
Expand Down
1 change: 0 additions & 1 deletion cli/src/services/upload.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ describe('UploadService', () => {

it('should upload a single file', async () => {
const data = new FormData();
data.append('assetType', 'image');

uploadService.upload(data);

Expand Down
22 changes: 3 additions & 19 deletions mobile/lib/modules/backup/services/backup.service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@ import 'package:immich_mobile/shared/models/store.dart';
import 'package:immich_mobile/shared/providers/api.provider.dart';
import 'package:immich_mobile/shared/providers/db.provider.dart';
import 'package:immich_mobile/shared/services/api.service.dart';
import 'package:immich_mobile/utils/files_helper.dart';
import 'package:isar/isar.dart';
import 'package:logging/logging.dart';
import 'package:openapi/api.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:photo_manager/photo_manager.dart';
import 'package:http_parser/http_parser.dart';
import 'package:path/path.dart' as p;
import 'package:cancellation_token_http/http.dart' as http;
import 'package:path/path.dart' as p;

final backupServiceProvider = Provider(
(ref) => BackupService(
Expand Down Expand Up @@ -230,18 +228,12 @@ class BackupService {

if (file != null) {
String originalFileName = await entity.titleAsync;
var fileExtension = p.extension(file.path);
var mimeType = FileHelper.getMimeType(file.path);
var fileStream = file.openRead();
var assetRawUploadData = http.MultipartFile(
"assetData",
fileStream,
file.lengthSync(),
filename: originalFileName,
contentType: MediaType(
mimeType["type"],
mimeType["subType"],
),
);

var req = MultipartRequest(
Expand All @@ -256,12 +248,10 @@ class BackupService {

req.fields['deviceAssetId'] = entity.id;
req.fields['deviceId'] = deviceId;
req.fields['assetType'] = _getAssetType(entity.type);
req.fields['fileCreatedAt'] = entity.createDateTime.toIso8601String();
req.fields['fileModifiedAt'] =
entity.modifiedDateTime.toIso8601String();
req.fields['isFavorite'] = entity.isFavorite.toString();
req.fields['fileExtension'] = fileExtension;
req.fields['duration'] = entity.videoDuration.toString();

req.files.add(assetRawUploadData);
Expand Down Expand Up @@ -342,18 +332,12 @@ class BackupService {
var validPath = motionFilePath.replaceAll('file://', '');
var motionFile = File(validPath);
var fileStream = motionFile.openRead();
String originalFileName = await entity.titleAsync;
var mimeType = FileHelper.getMimeType(validPath);

String fileName = p.basename(motionFile.path);
return http.MultipartFile(
"livePhotoData",
fileStream,
motionFile.lengthSync(),
filename: originalFileName,
contentType: MediaType(
mimeType["type"],
mimeType["subType"],
),
filename: fileName,
);
}

Expand Down
8 changes: 0 additions & 8 deletions mobile/lib/shared/services/user.service.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'package:collection/collection.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:http/http.dart';
import 'package:http_parser/http_parser.dart';
import 'package:image_picker/image_picker.dart';
import 'package:immich_mobile/modules/partner/services/partner.service.dart';
import 'package:immich_mobile/shared/models/store.dart';
Expand All @@ -11,7 +10,6 @@ import 'package:immich_mobile/shared/providers/db.provider.dart';
import 'package:immich_mobile/shared/services/api.service.dart';
import 'package:immich_mobile/shared/services/sync.service.dart';
import 'package:immich_mobile/utils/diff.dart';
import 'package:immich_mobile/utils/files_helper.dart';
import 'package:isar/isar.dart';
import 'package:logging/logging.dart';
import 'package:openapi/api.dart';
Expand Down Expand Up @@ -59,17 +57,11 @@ class UserService {

Future<CreateProfileImageResponseDto?> uploadProfileImage(XFile image) async {
try {
var mimeType = FileHelper.getMimeType(image.path);

return await _apiService.userApi.createProfileImage(
MultipartFile.fromBytes(
'file',
await image.readAsBytes(),
filename: image.name,
contentType: MediaType(
mimeType["type"],
mimeType["subType"],
),
),
);
} catch (e) {
Expand Down
Loading

0 comments on commit b71d7e3

Please sign in to comment.