2021-10-08 14:26:49 +00:00
|
|
|
import downloadManager from 'services/downloadManager';
|
2021-10-29 08:50:45 +00:00
|
|
|
import { fileAttribute, getLocalFiles } from 'services/fileService';
|
2021-10-19 08:19:03 +00:00
|
|
|
import { generateThumbnail } from 'services/upload/thumbnailService';
|
2021-10-08 14:26:49 +00:00
|
|
|
import { getToken } from 'utils/common/key';
|
|
|
|
import { logError } from 'utils/sentry';
|
|
|
|
import { getEndpoint } from 'utils/common/apiUtil';
|
|
|
|
import HTTPService from 'services/HTTPService';
|
|
|
|
import CryptoWorker from 'utils/crypto';
|
|
|
|
import uploadHttpClient from 'services/upload/uploadHttpClient';
|
|
|
|
import { EncryptionResult, UploadURL } from 'services/upload/uploadService';
|
2021-10-18 05:37:59 +00:00
|
|
|
import { SetProgressTracker } from 'components/FixLargeThumbnail';
|
2021-10-29 08:50:45 +00:00
|
|
|
import { getFileType } from './upload/readFileService';
|
2021-11-02 15:29:31 +00:00
|
|
|
import { getLocalTrash, getTrashedFiles } from './trashService';
|
2021-10-08 14:26:49 +00:00
|
|
|
|
|
|
|
const ENDPOINT = getEndpoint();
|
2021-10-19 08:19:03 +00:00
|
|
|
const REPLACE_THUMBNAIL_THRESHOLD = 500 * 1024; // 500KB
|
2021-10-08 14:26:49 +00:00
|
|
|
export async function getLargeThumbnailFiles() {
|
|
|
|
try {
|
|
|
|
const token = getToken();
|
|
|
|
if (!token) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const resp = await HTTPService.get(
|
|
|
|
`${ENDPOINT}/files/large-thumbnails`,
|
|
|
|
{
|
2021-10-19 08:19:03 +00:00
|
|
|
threshold: REPLACE_THUMBNAIL_THRESHOLD,
|
2021-10-08 14:26:49 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
'X-Auth-Token': token,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
return resp.data.largeThumbnailFiles as number[];
|
|
|
|
} catch (e) {
|
|
|
|
logError(e, 'failed to get large thumbnail files');
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
2021-10-18 08:26:13 +00:00
|
|
|
export async function replaceThumbnail(
|
|
|
|
setProgressTracker: SetProgressTracker,
|
|
|
|
largeThumbnailFileIDs: Set<number>
|
|
|
|
) {
|
2021-10-18 06:19:57 +00:00
|
|
|
let completedWithError = false;
|
2021-10-08 14:26:49 +00:00
|
|
|
try {
|
|
|
|
const token = getToken();
|
|
|
|
const worker = await new CryptoWorker();
|
2021-10-18 05:37:59 +00:00
|
|
|
const files = await getLocalFiles();
|
2021-11-02 15:29:31 +00:00
|
|
|
const trash = await getLocalTrash();
|
|
|
|
const trashFiles = getTrashedFiles(trash);
|
|
|
|
const largeThumbnailFiles = [...files, ...trashFiles].filter((file) =>
|
2021-10-08 14:26:49 +00:00
|
|
|
largeThumbnailFileIDs.has(file.id)
|
|
|
|
);
|
2021-11-02 15:35:23 +00:00
|
|
|
if (largeThumbnailFileIDs.size !== largeThumbnailFiles.length) {
|
|
|
|
logError(Error(), 'all large thumbnail files not found locally');
|
|
|
|
}
|
2021-10-18 05:37:59 +00:00
|
|
|
if (largeThumbnailFiles.length === 0) {
|
2021-10-18 06:19:57 +00:00
|
|
|
return completedWithError;
|
2021-10-18 05:37:59 +00:00
|
|
|
}
|
|
|
|
setProgressTracker({ current: 0, total: largeThumbnailFiles.length });
|
2021-10-08 14:26:49 +00:00
|
|
|
const uploadURLs: UploadURL[] = [];
|
2021-11-05 12:57:33 +00:00
|
|
|
await uploadHttpClient.fetchUploadURLs(
|
2021-10-08 14:26:49 +00:00
|
|
|
largeThumbnailFiles.length,
|
|
|
|
uploadURLs
|
|
|
|
);
|
2021-10-18 05:37:59 +00:00
|
|
|
for (const [idx, file] of largeThumbnailFiles.entries()) {
|
2021-10-18 06:19:57 +00:00
|
|
|
try {
|
|
|
|
setProgressTracker({
|
|
|
|
current: idx,
|
|
|
|
total: largeThumbnailFiles.length,
|
|
|
|
});
|
2021-11-29 08:41:41 +00:00
|
|
|
const originalThumbnail = await downloadManager.downloadThumb(
|
2021-10-18 06:19:57 +00:00
|
|
|
token,
|
|
|
|
file
|
|
|
|
);
|
|
|
|
const dummyImageFile = new globalThis.File(
|
|
|
|
[originalThumbnail],
|
|
|
|
file.metadata.title
|
|
|
|
);
|
2021-10-29 08:50:45 +00:00
|
|
|
const fileTypeInfo = await getFileType(worker, dummyImageFile);
|
2021-10-18 06:19:57 +00:00
|
|
|
const { thumbnail: newThumbnail } = await generateThumbnail(
|
|
|
|
worker,
|
|
|
|
dummyImageFile,
|
2021-10-29 08:50:45 +00:00
|
|
|
fileTypeInfo
|
2021-10-18 06:19:57 +00:00
|
|
|
);
|
|
|
|
const newUploadedThumbnail = await uploadThumbnail(
|
|
|
|
worker,
|
|
|
|
file.key,
|
|
|
|
newThumbnail,
|
|
|
|
uploadURLs.pop()
|
|
|
|
);
|
|
|
|
await updateThumbnail(file.id, newUploadedThumbnail);
|
|
|
|
} catch (e) {
|
|
|
|
logError(e, 'failed to replace a thumbnail');
|
|
|
|
completedWithError = true;
|
|
|
|
}
|
2021-10-08 14:26:49 +00:00
|
|
|
}
|
|
|
|
} catch (e) {
|
2021-10-18 06:19:57 +00:00
|
|
|
logError(e, 'replace Thumbnail function failed');
|
|
|
|
completedWithError = true;
|
2021-10-08 14:26:49 +00:00
|
|
|
}
|
2021-10-18 06:19:57 +00:00
|
|
|
return completedWithError;
|
2021-10-08 14:26:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export async function uploadThumbnail(
|
|
|
|
worker,
|
|
|
|
fileKey: string,
|
|
|
|
updatedThumbnail: Uint8Array,
|
|
|
|
uploadURL: UploadURL
|
|
|
|
): Promise<fileAttribute> {
|
|
|
|
const { file: encryptedThumbnail }: EncryptionResult =
|
|
|
|
await worker.encryptThumbnail(updatedThumbnail, fileKey);
|
|
|
|
|
|
|
|
const thumbnailObjectKey = await uploadHttpClient.putFile(
|
|
|
|
uploadURL,
|
|
|
|
encryptedThumbnail.encryptedData as Uint8Array,
|
|
|
|
() => {}
|
|
|
|
);
|
|
|
|
return {
|
|
|
|
objectKey: thumbnailObjectKey,
|
|
|
|
decryptionHeader: encryptedThumbnail.decryptionHeader,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function updateThumbnail(
|
|
|
|
fileID: number,
|
|
|
|
newThumbnail: fileAttribute
|
|
|
|
) {
|
|
|
|
try {
|
|
|
|
const token = getToken();
|
|
|
|
if (!token) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
await HTTPService.put(
|
|
|
|
`${ENDPOINT}/files/thumbnail`,
|
|
|
|
{
|
|
|
|
fileID: fileID,
|
|
|
|
thumbnail: newThumbnail,
|
|
|
|
},
|
|
|
|
null,
|
|
|
|
{
|
|
|
|
'X-Auth-Token': token,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
} catch (e) {
|
|
|
|
logError(e, 'failed to update thumbnail');
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|