mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
feat(web/server) public album sharing (#1266)
This commit is contained in:
parent
fd15cdbf40
commit
10789503c1
101 changed files with 4879 additions and 347 deletions
|
|
@ -1,21 +1,106 @@
|
|||
import { api, AddAssetsResponseDto } from '@api';
|
||||
import { api, AddAssetsResponseDto, AssetResponseDto } from '@api';
|
||||
import {
|
||||
notificationController,
|
||||
NotificationType
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import { downloadAssets } from '$lib/stores/download';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
export const addAssetsToAlbum = async (
|
||||
albumId: string,
|
||||
assetIds: Array<string>
|
||||
assetIds: Array<string>,
|
||||
key: string | undefined = undefined
|
||||
): Promise<AddAssetsResponseDto> =>
|
||||
api.albumApi.addAssetsToAlbum(albumId, { assetIds }).then(({ data: dto }) => {
|
||||
if (dto.successfullyAdded > 0) {
|
||||
// This might be 0 if the user tries to add an asset that is already in the album
|
||||
notificationController.show({
|
||||
message: `Added ${dto.successfullyAdded} to ${dto.album?.albumName}`,
|
||||
type: NotificationType.Info
|
||||
});
|
||||
}
|
||||
api.albumApi
|
||||
.addAssetsToAlbum(albumId, { assetIds }, { params: { key } })
|
||||
.then(({ data: dto }) => {
|
||||
if (dto.successfullyAdded > 0) {
|
||||
// This might be 0 if the user tries to add an asset that is already in the album
|
||||
notificationController.show({
|
||||
message: `Added ${dto.successfullyAdded} to ${dto.album?.albumName}`,
|
||||
type: NotificationType.Info
|
||||
});
|
||||
}
|
||||
|
||||
return dto;
|
||||
});
|
||||
return dto;
|
||||
});
|
||||
|
||||
export async function bulkDownload(
|
||||
fileName: string,
|
||||
assets: AssetResponseDto[],
|
||||
onDone: () => void,
|
||||
key?: string
|
||||
) {
|
||||
const assetIds = assets.map((asset) => asset.id);
|
||||
|
||||
try {
|
||||
let skip = 0;
|
||||
let count = 0;
|
||||
let done = false;
|
||||
|
||||
while (!done) {
|
||||
count++;
|
||||
|
||||
const downloadFileName = fileName + `${count === 1 ? '' : count}.zip`;
|
||||
downloadAssets.set({ [downloadFileName]: 0 });
|
||||
|
||||
let total = 0;
|
||||
|
||||
const { data, status, headers } = await api.assetApi.downloadFiles(
|
||||
{ assetIds },
|
||||
{
|
||||
params: { key },
|
||||
responseType: 'blob',
|
||||
onDownloadProgress: function (progressEvent) {
|
||||
const request = this as XMLHttpRequest;
|
||||
if (!total) {
|
||||
total = Number(request.getResponseHeader('X-Immich-Content-Length-Hint')) || 0;
|
||||
}
|
||||
|
||||
if (total) {
|
||||
const current = progressEvent.loaded;
|
||||
downloadAssets.set({ [downloadFileName]: Math.floor((current / total) * 100) });
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const isNotComplete = headers['x-immich-archive-complete'] === 'false';
|
||||
const fileCount = Number(headers['x-immich-archive-file-count']) || 0;
|
||||
if (isNotComplete && fileCount > 0) {
|
||||
skip += fileCount;
|
||||
} else {
|
||||
onDone();
|
||||
done = true;
|
||||
}
|
||||
|
||||
if (!(data instanceof Blob)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (status === 201) {
|
||||
const fileUrl = URL.createObjectURL(data);
|
||||
const anchor = document.createElement('a');
|
||||
anchor.href = fileUrl;
|
||||
anchor.download = downloadFileName;
|
||||
|
||||
document.body.appendChild(anchor);
|
||||
anchor.click();
|
||||
document.body.removeChild(anchor);
|
||||
|
||||
URL.revokeObjectURL(fileUrl);
|
||||
|
||||
// Remove item from download list
|
||||
setTimeout(() => {
|
||||
downloadAssets.set({});
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error downloading file ', e);
|
||||
notificationController.show({
|
||||
type: NotificationType.Error,
|
||||
message: 'Error downloading file, check console for more details.'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { addAssetsToAlbum } from '$lib/utils/asset-utils';
|
|||
|
||||
export const openFileUploadDialog = (
|
||||
albumId: string | undefined = undefined,
|
||||
sharedKey: string | undefined = undefined,
|
||||
callback?: () => void
|
||||
) => {
|
||||
try {
|
||||
|
|
@ -27,7 +28,7 @@ export const openFileUploadDialog = (
|
|||
}
|
||||
const files = Array.from<File>(target.files);
|
||||
|
||||
await fileUploadHandler(files, albumId);
|
||||
await fileUploadHandler(files, albumId, sharedKey);
|
||||
callback && callback();
|
||||
};
|
||||
|
||||
|
|
@ -37,7 +38,11 @@ export const openFileUploadDialog = (
|
|||
}
|
||||
};
|
||||
|
||||
export const fileUploadHandler = async (files: File[], albumId: string | undefined = undefined) => {
|
||||
export const fileUploadHandler = async (
|
||||
files: File[],
|
||||
albumId: string | undefined = undefined,
|
||||
sharedKey: string | undefined = undefined
|
||||
) => {
|
||||
if (files.length > 50) {
|
||||
notificationController.show({
|
||||
type: NotificationType.Error,
|
||||
|
|
@ -49,18 +54,22 @@ export const fileUploadHandler = async (files: File[], albumId: string | undefin
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('fileUploadHandler');
|
||||
const acceptedFile = files.filter(
|
||||
(e) => e.type.split('/')[0] === 'video' || e.type.split('/')[0] === 'image'
|
||||
);
|
||||
|
||||
for (const asset of acceptedFile) {
|
||||
await fileUploader(asset, albumId);
|
||||
await fileUploader(asset, albumId, sharedKey);
|
||||
}
|
||||
};
|
||||
|
||||
//TODO: should probably use the @api SDK
|
||||
async function fileUploader(asset: File, albumId: string | undefined = undefined) {
|
||||
async function fileUploader(
|
||||
asset: File,
|
||||
albumId: string | undefined = undefined,
|
||||
sharedKey: string | undefined = undefined
|
||||
) {
|
||||
const assetType = asset.type.split('/')[0].toUpperCase();
|
||||
const temp = asset.name.split('.');
|
||||
const fileExtension = temp[temp.length - 1];
|
||||
|
|
@ -108,10 +117,17 @@ async function fileUploader(asset: File, albumId: string | undefined = undefined
|
|||
formData.append('assetData', asset);
|
||||
|
||||
// Check if asset upload on server before performing upload
|
||||
const { data, status } = await api.assetApi.checkDuplicateAsset({
|
||||
deviceAssetId: String(deviceAssetId),
|
||||
deviceId: 'WEB'
|
||||
});
|
||||
const { data, status } = await api.assetApi.checkDuplicateAsset(
|
||||
{
|
||||
deviceAssetId: String(deviceAssetId),
|
||||
deviceId: 'WEB'
|
||||
},
|
||||
{
|
||||
params: {
|
||||
key: sharedKey
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (status === 200) {
|
||||
if (data.isExist) {
|
||||
|
|
@ -124,7 +140,6 @@ async function fileUploader(asset: File, albumId: string | undefined = undefined
|
|||
}
|
||||
|
||||
const request = new XMLHttpRequest();
|
||||
|
||||
request.upload.onloadstart = () => {
|
||||
const newUploadAsset: UploadAsset = {
|
||||
id: deviceAssetId,
|
||||
|
|
@ -144,7 +159,7 @@ async function fileUploader(asset: File, albumId: string | undefined = undefined
|
|||
try {
|
||||
const res: AssetFileUploadResponseDto = JSON.parse(request.response || '{}');
|
||||
if (res.id) {
|
||||
addAssetsToAlbum(albumId, [res.id]);
|
||||
addAssetsToAlbum(albumId, [res.id], sharedKey);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('ERROR parsing data JSON in upload onload');
|
||||
|
|
@ -171,7 +186,7 @@ async function fileUploader(asset: File, albumId: string | undefined = undefined
|
|||
uploadAssetsStore.updateProgress(deviceAssetId, percentComplete);
|
||||
};
|
||||
|
||||
request.open('POST', `/api/asset/upload`);
|
||||
request.open('POST', `/api/asset/upload?key=${sharedKey ?? ''}`);
|
||||
|
||||
request.send(formData);
|
||||
} catch (e) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue