123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- import {
- notificationController,
- NotificationType
- } from './../components/shared-components/notification/notification';
- /* @vite-ignore */
- import * as exifr from 'exifr';
- import { uploadAssetsStore } from '$lib/stores/upload';
- import type { UploadAsset } from '../models/upload-asset';
- import { api, AssetFileUploadResponseDto } from '@api';
- import { addAssetsToAlbum, getFileMimeType, getFilenameExtension } from '$lib/utils/asset-utils';
- export const openFileUploadDialog = (
- albumId: string | undefined = undefined,
- sharedKey: string | undefined = undefined,
- onDone?: (id: string) => void
- ) => {
- 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';
- fileSelector.onchange = async (e: Event) => {
- const target = e.target as HTMLInputElement;
- if (!target.files) {
- return;
- }
- const files = Array.from<File>(target.files);
- await fileUploadHandler(files, albumId, sharedKey, onDone);
- };
- fileSelector.click();
- } catch (e) {
- console.log('Error selecting file', e);
- }
- };
- export const fileUploadHandler = async (
- files: File[],
- albumId: string | undefined = undefined,
- sharedKey: string | undefined = undefined,
- onDone?: (id: string) => void
- ) => {
- if (files.length > 50) {
- notificationController.show({
- type: NotificationType.Error,
- message: `Cannot upload more than 50 files at a time - you are uploading ${files.length} files.
- Please check out <u>the bulk upload documentation</u> if you need to upload more than 50 files.`,
- timeout: 10000,
- action: { type: 'link', target: 'https://immich.app/docs/features/bulk-upload' }
- });
- return;
- }
- const acceptedFile = files.filter((file) => {
- const assetType = getFileMimeType(file).split('/')[0];
- return assetType === 'video' || assetType === 'image';
- });
- for (const asset of acceptedFile) {
- await fileUploader(asset, albumId, sharedKey, onDone);
- }
- };
- //TODO: should probably use the @api SDK
- async function fileUploader(
- asset: File,
- albumId: string | undefined = undefined,
- sharedKey: string | undefined = undefined,
- onDone?: (id: string) => void
- ) {
- const mimeType = getFileMimeType(asset);
- const assetType = mimeType.split('/')[0].toUpperCase();
- const fileExtension = getFilenameExtension(asset.name);
- const formData = new FormData();
- try {
- let exifData = null;
- if (assetType !== 'VIDEO') {
- exifData = await exifr.parse(asset).catch((e) => console.log('error parsing exif', e));
- }
- const createdAt =
- exifData && exifData.DateTimeOriginal != null
- ? new Date(exifData.DateTimeOriginal).toISOString()
- : new Date(asset.lastModified).toISOString();
- const deviceAssetId = 'web' + '-' + asset.name + '-' + asset.lastModified;
- // Create and add 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('createdAt', createdAt);
- // Get Asset Modified At
- formData.append('modifiedAt', 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 }));
- // Check if asset upload on server before performing upload
- const { data, status } = await api.assetApi.checkDuplicateAsset(
- {
- deviceAssetId: String(deviceAssetId),
- deviceId: 'WEB'
- },
- {
- params: {
- key: sharedKey
- }
- }
- );
- if (status === 200) {
- if (data.isExist) {
- const dataId = data.id;
- if (albumId && dataId) {
- addAssetsToAlbum(albumId, [dataId], sharedKey);
- }
- onDone && dataId && onDone(dataId);
- return;
- }
- }
- const request = new XMLHttpRequest();
- request.upload.onloadstart = () => {
- const newUploadAsset: UploadAsset = {
- id: deviceAssetId,
- file: asset,
- progress: 0,
- fileExtension: fileExtension
- };
- uploadAssetsStore.addNewUploadAsset(newUploadAsset);
- };
- request.upload.onload = () => {
- setTimeout(() => {
- uploadAssetsStore.removeUploadAsset(deviceAssetId);
- const res: AssetFileUploadResponseDto = JSON.parse(request.response || '{}');
- if (albumId) {
- try {
- if (res.id) {
- addAssetsToAlbum(albumId, [res.id], sharedKey);
- }
- } catch (e) {
- console.error('ERROR parsing data JSON in upload onload');
- }
- }
- onDone && onDone(res.id);
- }, 1000);
- };
- // listen for `error` event
- request.upload.onerror = () => {
- uploadAssetsStore.removeUploadAsset(deviceAssetId);
- handleUploadError(asset, request.response);
- };
- // listen for `abort` event
- request.upload.onabort = () => {
- uploadAssetsStore.removeUploadAsset(deviceAssetId);
- handleUploadError(asset, request.response);
- };
- // listen for `progress` event
- request.upload.onprogress = (event) => {
- const percentComplete = Math.floor((event.loaded / event.total) * 100);
- uploadAssetsStore.updateProgress(deviceAssetId, percentComplete);
- };
- request.open('POST', `/api/asset/upload?key=${sharedKey ?? ''}`);
- request.send(formData);
- } catch (e) {
- console.log('error uploading file ', e);
- }
- }
- 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');
- }
- }
|