Try another factoring
This commit is contained in:
parent
6ca3eb55af
commit
6ff41db939
4 changed files with 152 additions and 11 deletions
|
@ -994,6 +994,7 @@ class ExportService {
|
|||
file,
|
||||
);
|
||||
await writeStream(
|
||||
electron,
|
||||
`${collectionExportPath}/${fileExportName}`,
|
||||
updatedFileStream,
|
||||
);
|
||||
|
@ -1047,6 +1048,7 @@ class ExportService {
|
|||
file,
|
||||
);
|
||||
await writeStream(
|
||||
electron,
|
||||
`${collectionExportPath}/${imageExportName}`,
|
||||
imageStream,
|
||||
);
|
||||
|
@ -1061,6 +1063,7 @@ class ExportService {
|
|||
);
|
||||
try {
|
||||
await writeStream(
|
||||
electron,
|
||||
`${collectionExportPath}/${videoExportName}`,
|
||||
videoStream,
|
||||
);
|
||||
|
|
|
@ -430,10 +430,122 @@ const moduleState = new ModuleState();
|
|||
const readFileOrPath = async (
|
||||
fileOrPath: File | string,
|
||||
fileTypeInfo: FileTypeInfo,
|
||||
): Promise<FileInMemory> =>
|
||||
fileOrPath instanceof File
|
||||
? _readFile(fileOrPath, fileTypeInfo)
|
||||
: _readPath(fileOrPath, fileTypeInfo);
|
||||
): Promise<FileInMemory> => {
|
||||
let file: File | undefined;
|
||||
let dataOrStream: Uint8Array | DataStream;
|
||||
let fileSize: number;
|
||||
|
||||
if (fileOrPath instanceof File) {
|
||||
file = fileOrPath;
|
||||
fileSize = file.size;
|
||||
dataOrStream =
|
||||
fileSize > MULTIPART_PART_SIZE
|
||||
? getFileStream(file, FILE_READER_CHUNK_SIZE)
|
||||
: new Uint8Array(await file.arrayBuffer());
|
||||
} else {
|
||||
const path = fileOrPath;
|
||||
const { response, size } = await readStream(ensureElectron(), path);
|
||||
fileSize = size;
|
||||
if (size > MULTIPART_PART_SIZE) {
|
||||
const chunkCount = Math.ceil(size / FILE_READER_CHUNK_SIZE);
|
||||
dataOrStream = { stream: response.body, chunkCount };
|
||||
} else {
|
||||
dataOrStream = new Uint8Array(await response.arrayBuffer());
|
||||
}
|
||||
}
|
||||
|
||||
let thumbnail: Uint8Array | undefined;
|
||||
let hasStaticThumbnail = false;
|
||||
|
||||
const electron = globalThis.electron;
|
||||
if (electron) {
|
||||
// On Windows native thumbnail creation for images is not yet implemented.
|
||||
const notAvailable =
|
||||
fileTypeInfo.fileType == FILE_TYPE.IMAGE &&
|
||||
moduleState.isNativeImageThumbnailCreationNotAvailable;
|
||||
|
||||
try {
|
||||
if (!notAvailable) {
|
||||
if (fileOrPath instanceof File) {
|
||||
if (dataOrStream instanceof Uint8Array) {
|
||||
thumbnail = await generateThumbnailNative(
|
||||
electron,
|
||||
dataOrStream,
|
||||
fileTypeInfo,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
thumbnail = await generateThumbnailNative(
|
||||
electron,
|
||||
fileOrPath,
|
||||
fileTypeInfo,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.message == CustomErrorMessage.NotAvailable) {
|
||||
moduleState.isNativeImageThumbnailCreationNotAvailable = true;
|
||||
} else {
|
||||
log.error("Native thumbnail creation failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If needed, fallback to browser based thumbnail generation. First, see if
|
||||
// we already have a file (which is also a blob).
|
||||
if (!thumbnail && file) {
|
||||
try {
|
||||
thumbnail = await generateThumbnailWeb(file, fileTypeInfo);
|
||||
} catch (e) {
|
||||
log.error(
|
||||
`Failed to generate ${fileTypeInfo.exactType} thumbnail`,
|
||||
e,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise see if the data is small enough to read in memory.
|
||||
if (!thumbnail) {
|
||||
let data: Uint8Array | undefined;
|
||||
if (dataOrStream instanceof Uint8Array) {
|
||||
data = dataOrStream;
|
||||
} else {
|
||||
// Read the stream into memory, since the our web based thumbnail
|
||||
// generation methods need the entire file in memory. Don't try this
|
||||
// fallback for huge files though lest we run out of memory.
|
||||
if (fileSize < 100 * 1024 * 1024 /* 100 MB */) {
|
||||
data = new Uint8Array(
|
||||
await new Response(dataOrStream.stream).arrayBuffer(),
|
||||
);
|
||||
// The Readable stream cannot be read twice, so also overwrite
|
||||
// the stream with the data we read.
|
||||
dataOrStream = data;
|
||||
}
|
||||
}
|
||||
if (data) {
|
||||
const blob = new Blob([data]);
|
||||
try {
|
||||
thumbnail = await generateThumbnailWeb(blob, fileTypeInfo);
|
||||
} catch (e) {
|
||||
log.error(
|
||||
`Failed to generate ${fileTypeInfo.exactType} thumbnail`,
|
||||
e,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!thumbnail) {
|
||||
thumbnail = fallbackThumbnail();
|
||||
hasStaticThumbnail = true;
|
||||
}
|
||||
|
||||
return {
|
||||
filedata: dataOrStream,
|
||||
thumbnail,
|
||||
hasStaticThumbnail,
|
||||
};
|
||||
};
|
||||
|
||||
const _readFile = async (file: File, fileTypeInfo: FileTypeInfo) => {
|
||||
const dataOrStream =
|
||||
|
|
|
@ -646,7 +646,11 @@ async function downloadFileDesktop(
|
|||
fs.exists,
|
||||
);
|
||||
const imageStream = generateStreamFromArrayBuffer(imageData);
|
||||
await writeStream(`${downloadDir}/${imageExportName}`, imageStream);
|
||||
await writeStream(
|
||||
electron,
|
||||
`${downloadDir}/${imageExportName}`,
|
||||
imageStream,
|
||||
);
|
||||
try {
|
||||
const videoExportName = await safeFileName(
|
||||
downloadDir,
|
||||
|
@ -654,7 +658,11 @@ async function downloadFileDesktop(
|
|||
fs.exists,
|
||||
);
|
||||
const videoStream = generateStreamFromArrayBuffer(videoData);
|
||||
await writeStream(`${downloadDir}/${videoExportName}`, videoStream);
|
||||
await writeStream(
|
||||
electron,
|
||||
`${downloadDir}/${videoExportName}`,
|
||||
videoStream,
|
||||
);
|
||||
} catch (e) {
|
||||
await fs.rm(`${downloadDir}/${imageExportName}`);
|
||||
throw e;
|
||||
|
@ -665,7 +673,11 @@ async function downloadFileDesktop(
|
|||
file.metadata.title,
|
||||
fs.exists,
|
||||
);
|
||||
await writeStream(`${downloadDir}/${fileExportName}`, updatedStream);
|
||||
await writeStream(
|
||||
electron,
|
||||
`${downloadDir}/${fileExportName}`,
|
||||
updatedStream,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,13 +4,18 @@
|
|||
* NOTE: These functions only work when we're running in our desktop app.
|
||||
*/
|
||||
|
||||
import type { Electron } from "@/next/types/ipc";
|
||||
|
||||
/**
|
||||
* Stream the given file from the user's local filesystem.
|
||||
*
|
||||
* **This only works when we're running in our desktop app**. It uses the
|
||||
* This only works when we're running in our desktop app since it uses the
|
||||
* "stream://" protocol handler exposed by our custom code in the Node.js layer.
|
||||
* See: [Note: IPC streams].
|
||||
*
|
||||
* To avoid accidentally invoking it in a non-desktop app context, it requires
|
||||
* the {@link Electron} object as a parameter (even though it doesn't use it).
|
||||
*
|
||||
* @param path The path on the file on the user's local filesystem whose
|
||||
* contents we want to stream.
|
||||
*
|
||||
|
@ -23,6 +28,7 @@
|
|||
* * The size is the size of the file that we'll be reading from disk.
|
||||
*/
|
||||
export const readStream = async (
|
||||
_: Electron,
|
||||
path: string,
|
||||
): Promise<{ response: Response; size: number }> => {
|
||||
const req = new Request(`stream://read${path}`, {
|
||||
|
@ -47,14 +53,22 @@ export const readStream = async (
|
|||
/**
|
||||
* Write the given stream to a file on the local machine.
|
||||
*
|
||||
* **This only works when we're running in our desktop app**. It uses the
|
||||
* This only works when we're running in our desktop app since it uses the
|
||||
* "stream://" protocol handler exposed by our custom code in the Node.js layer.
|
||||
* See: [Note: IPC streams].
|
||||
*
|
||||
* To avoid accidentally invoking it in a non-desktop app context, it requires
|
||||
* the {@link Electron} object as a parameter (even though it doesn't use it).
|
||||
*
|
||||
* @param path The path on the local machine where to write the file to.
|
||||
*
|
||||
* @param stream The stream which should be written into the file.
|
||||
* */
|
||||
export const writeStream = async (path: string, stream: ReadableStream) => {
|
||||
*/
|
||||
export const writeStream = async (
|
||||
_: Electron,
|
||||
path: string,
|
||||
stream: ReadableStream,
|
||||
) => {
|
||||
// TODO(MR): This doesn't currently work.
|
||||
//
|
||||
// Not sure what I'm doing wrong here; I've opened an issue upstream
|
||||
|
|
Loading…
Add table
Reference in a new issue