mirror of
https://github.com/immich-app/immich.git
synced 2025-05-31 12:15:47 -04:00
* use imagemagick and libraw for raw image support imagemagick and libraw have generally good support for raw images, including Sony's ARW format. These tools should also allow Immich to support many more image formats in future without any major code changes. https://www.libraw.org/supported-cameras I've tested and verified this change with .ARW files and other standard formats. Fixes: #2156 * Add additional type for awr * pr feedback --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
159 lines
4.7 KiB
TypeScript
159 lines
4.7 KiB
TypeScript
import { uploadAssetsStore } from '$lib/stores/upload';
|
|
import { addAssetsToAlbum, getFileMimeType, getFilenameExtension } from '$lib/utils/asset-utils';
|
|
import type { AssetFileUploadResponseDto } from '@api';
|
|
import axios from 'axios';
|
|
import { combineLatestAll, filter, firstValueFrom, from, mergeMap, of } from 'rxjs';
|
|
import type { UploadAsset } from '../models/upload-asset';
|
|
import {
|
|
notificationController,
|
|
NotificationType
|
|
} from './../components/shared-components/notification/notification';
|
|
|
|
export const openFileUploadDialog = async (
|
|
albumId: string | undefined = undefined,
|
|
sharedKey: string | undefined = undefined
|
|
) => {
|
|
return new Promise<(string | undefined)[]>((resolve, reject) => {
|
|
try {
|
|
const fileSelector = document.createElement('input');
|
|
|
|
fileSelector.type = 'file';
|
|
fileSelector.multiple = true;
|
|
|
|
// When adding a content type that is unsupported by browsers, make sure
|
|
// to also add it to getFileMimeType() otherwise the upload will fail.
|
|
fileSelector.accept = 'image/*,video/*,.heic,.heif,.dng,.3gp,.nef,.srw,.raf,.insp,.insv,.arw';
|
|
|
|
fileSelector.onchange = async (e: Event) => {
|
|
const target = e.target as HTMLInputElement;
|
|
if (!target.files) {
|
|
return;
|
|
}
|
|
const files = Array.from<File>(target.files);
|
|
|
|
resolve(await fileUploadHandler(files, albumId, sharedKey));
|
|
};
|
|
|
|
fileSelector.click();
|
|
} catch (e) {
|
|
console.log('Error selecting file', e);
|
|
reject(e);
|
|
}
|
|
});
|
|
};
|
|
|
|
export const fileUploadHandler = async (
|
|
files: File[],
|
|
albumId: string | undefined = undefined,
|
|
sharedKey: string | undefined = undefined
|
|
) => {
|
|
return firstValueFrom(
|
|
from(files).pipe(
|
|
filter((file) => {
|
|
const assetType = getFileMimeType(file).split('/')[0];
|
|
return assetType === 'video' || assetType === 'image';
|
|
}),
|
|
mergeMap(async (file) => of(await fileUploader(file, albumId, sharedKey)), 2),
|
|
combineLatestAll()
|
|
)
|
|
);
|
|
};
|
|
|
|
//TODO: should probably use the @api SDK
|
|
async function fileUploader(
|
|
asset: File,
|
|
albumId: string | undefined = undefined,
|
|
sharedKey: string | undefined = undefined
|
|
): Promise<string | undefined> {
|
|
const mimeType = getFileMimeType(asset);
|
|
const assetType = mimeType.split('/')[0].toUpperCase();
|
|
const fileExtension = getFilenameExtension(asset.name);
|
|
const formData = new FormData();
|
|
const fileCreatedAt = new Date(asset.lastModified).toISOString();
|
|
const deviceAssetId = 'web' + '-' + asset.name + '-' + asset.lastModified;
|
|
|
|
try {
|
|
// Create and add pseudo-unique ID of asset on the device
|
|
formData.append('deviceAssetId', deviceAssetId);
|
|
|
|
// Get device id - for web -> use WEB
|
|
formData.append('deviceId', 'WEB');
|
|
|
|
// Get asset type
|
|
formData.append('assetType', assetType);
|
|
|
|
// Get Asset Created Date
|
|
formData.append('fileCreatedAt', fileCreatedAt);
|
|
|
|
// Get Asset Modified At
|
|
formData.append('fileModifiedAt', new Date(asset.lastModified).toISOString());
|
|
|
|
// Set Asset is Favorite to false
|
|
formData.append('isFavorite', 'false');
|
|
|
|
// Get asset duration
|
|
formData.append('duration', '0:00:00.000000');
|
|
|
|
// Get asset file extension
|
|
formData.append('fileExtension', '.' + fileExtension);
|
|
|
|
// Get asset binary data with a custom MIME type, because browsers will
|
|
// use application/octet-stream for unsupported MIME types, leading to
|
|
// failed uploads.
|
|
formData.append('assetData', new File([asset], asset.name, { type: mimeType }));
|
|
|
|
const newUploadAsset: UploadAsset = {
|
|
id: deviceAssetId,
|
|
file: asset,
|
|
progress: 0,
|
|
fileExtension: fileExtension
|
|
};
|
|
|
|
uploadAssetsStore.addNewUploadAsset(newUploadAsset);
|
|
|
|
const response = await axios.post(`/api/asset/upload`, formData, {
|
|
params: {
|
|
key: sharedKey
|
|
},
|
|
onUploadProgress: (event) => {
|
|
const percentComplete = Math.floor((event.loaded / event.total) * 100);
|
|
uploadAssetsStore.updateProgress(deviceAssetId, percentComplete);
|
|
}
|
|
});
|
|
|
|
if (response.status == 200 || response.status == 201) {
|
|
const res: AssetFileUploadResponseDto = response.data;
|
|
|
|
if (albumId && res.id) {
|
|
await addAssetsToAlbum(albumId, [res.id], sharedKey);
|
|
}
|
|
|
|
setTimeout(() => {
|
|
uploadAssetsStore.removeUploadAsset(deviceAssetId);
|
|
}, 1000);
|
|
|
|
return res.id;
|
|
}
|
|
} catch (e) {
|
|
console.log('error uploading file ', e);
|
|
handleUploadError(asset, JSON.stringify(e));
|
|
uploadAssetsStore.removeUploadAsset(deviceAssetId);
|
|
}
|
|
}
|
|
|
|
function handleUploadError(asset: File, respBody = '{}', extraMessage?: string) {
|
|
try {
|
|
const res = JSON.parse(respBody);
|
|
|
|
const extraMsg = res ? ' ' + res?.message : '';
|
|
|
|
notificationController.show({
|
|
type: NotificationType.Error,
|
|
message: `Cannot upload file ${asset.name} ${extraMsg}${extraMessage}`,
|
|
timeout: 5000
|
|
});
|
|
} catch (e) {
|
|
console.error('ERROR parsing data JSON in handleUploadError');
|
|
}
|
|
}
|