Kaynağa Gözat

Refactor and extract

Manav Rathi 1 yıl önce
ebeveyn
işleme
297ca09535

+ 4 - 4
web/apps/photos/src/services/export/index.ts

@@ -34,7 +34,7 @@ import {
     mergeMetadata,
     mergeMetadata,
     splitFilenameAndExtension,
     splitFilenameAndExtension,
 } from "utils/file";
 } from "utils/file";
-import { safeDirectoryName, sanitizedUniqueFileName } from "utils/native-fs";
+import { safeDirectoryName, safeFileName } from "utils/native-fs";
 import { getAllLocalCollections } from "../collectionService";
 import { getAllLocalCollections } from "../collectionService";
 import downloadManager from "../download";
 import downloadManager from "../download";
 import { getAllLocalFiles } from "../fileService";
 import { getAllLocalFiles } from "../fileService";
@@ -1057,7 +1057,7 @@ class ExportService {
                     file,
                     file,
                 );
                 );
             } else {
             } else {
-                const fileExportName = await sanitizedUniqueFileName(
+                const fileExportName = await safeFileName(
                     collectionExportPath,
                     collectionExportPath,
                     file.metadata.title,
                     file.metadata.title,
                 );
                 );
@@ -1096,11 +1096,11 @@ class ExportService {
     ) {
     ) {
         const fileBlob = await new Response(fileStream).blob();
         const fileBlob = await new Response(fileStream).blob();
         const livePhoto = await decodeLivePhoto(file, fileBlob);
         const livePhoto = await decodeLivePhoto(file, fileBlob);
-        const imageExportName = await sanitizedUniqueFileName(
+        const imageExportName = await safeFileName(
             collectionExportPath,
             collectionExportPath,
             livePhoto.imageNameTitle,
             livePhoto.imageNameTitle,
         );
         );
-        const videoExportName = await sanitizedUniqueFileName(
+        const videoExportName = await safeFileName(
             collectionExportPath,
             collectionExportPath,
             livePhoto.videoNameTitle,
             livePhoto.videoNameTitle,
         );
         );

+ 4 - 4
web/apps/photos/src/utils/file/index.ts

@@ -52,7 +52,7 @@ import {
 import { VISIBILITY_STATE } from "types/magicMetadata";
 import { VISIBILITY_STATE } from "types/magicMetadata";
 import { FileTypeInfo } from "types/upload";
 import { FileTypeInfo } from "types/upload";
 import { isArchivedFile, updateMagicMetadata } from "utils/magicMetadata";
 import { isArchivedFile, updateMagicMetadata } from "utils/magicMetadata";
-import { sanitizedUniqueFileName } from "utils/native-fs";
+import { safeFileName } from "utils/native-fs";
 
 
 const WAIT_TIME_IMAGE_CONVERSION = 30 * 1000;
 const WAIT_TIME_IMAGE_CONVERSION = 30 * 1000;
 
 
@@ -812,7 +812,7 @@ async function downloadFileDesktop(
     if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
     if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
         const fileBlob = await new Response(updatedFileStream).blob();
         const fileBlob = await new Response(updatedFileStream).blob();
         const livePhoto = await decodeLivePhoto(file, fileBlob);
         const livePhoto = await decodeLivePhoto(file, fileBlob);
-        const imageExportName = await sanitizedUniqueFileName(
+        const imageExportName = await safeFileName(
             downloadPath,
             downloadPath,
             livePhoto.imageNameTitle,
             livePhoto.imageNameTitle,
         );
         );
@@ -822,7 +822,7 @@ async function downloadFileDesktop(
             imageStream,
             imageStream,
         );
         );
         try {
         try {
-            const videoExportName = await sanitizedUniqueFileName(
+            const videoExportName = await safeFileName(
                 downloadPath,
                 downloadPath,
                 livePhoto.videoNameTitle,
                 livePhoto.videoNameTitle,
             );
             );
@@ -836,7 +836,7 @@ async function downloadFileDesktop(
             throw e;
             throw e;
         }
         }
     } else {
     } else {
-        const fileExportName = await sanitizedUniqueFileName(
+        const fileExportName = await safeFileName(
             downloadPath,
             downloadPath,
             file.metadata.title,
             file.metadata.title,
         );
         );

+ 22 - 20
web/apps/photos/src/utils/native-fs.ts

@@ -1,7 +1,10 @@
 import { ensureElectron } from "@/next/electron";
 import { ensureElectron } from "@/next/electron";
+import { nameAndExtension } from "@/next/file";
 import sanitize from "sanitize-filename";
 import sanitize from "sanitize-filename";
-import { exportTrashDirectoryName } from "services/export";
-import { splitFilenameAndExtension } from "utils/file";
+import {
+    exportMetadataDirectoryName,
+    exportTrashDirectoryName,
+} from "services/export";
 
 
 /**
 /**
  * Sanitize string for use as file or directory name.
  * Sanitize string for use as file or directory name.
@@ -14,8 +17,8 @@ export const sanitizeFilename = (s: string) =>
     sanitize(s, { replacement: "_" });
     sanitize(s, { replacement: "_" });
 
 
 /**
 /**
- * Return a new unique directory name based on {@link name} that is not the same
- * as any existing directory in the given {@link directoryPath}.
+ * Return a new sanitized and unique directory name based on {@link name} that
+ * is not the same as any existing item in the given {@link directoryPath}.
  *
  *
  * We also ensure we don't return names which might collide with our own special
  * We also ensure we don't return names which might collide with our own special
  * directories.
  * directories.
@@ -30,11 +33,16 @@ export const safeDirectoryName = async (
     directoryPath: string,
     directoryPath: string,
     name: string,
     name: string,
 ): Promise<string> => {
 ): Promise<string> => {
+    const specialDirectoryNames = [
+        exportTrashDirectoryName,
+        exportMetadataDirectoryName,
+    ];
+
     let result = sanitizeFilename(name);
     let result = sanitizeFilename(name);
     let count = 1;
     let count = 1;
     while (
     while (
         (await exists(`${directoryPath}/${result}`)) ||
         (await exists(`${directoryPath}/${result}`)) ||
-        result == exportTrashDirectoryName
+        specialDirectoryNames.includes(result)
     ) {
     ) {
         result = `${sanitizeFilename(name)}(${count})`;
         result = `${sanitizeFilename(name)}(${count})`;
         count++;
         count++;
@@ -43,28 +51,22 @@ export const safeDirectoryName = async (
 };
 };
 
 
 /**
 /**
- * Return a new unique file name based on {@link name} that is not the same as
- * any existing directory in the given {@link directoryPath}.
+ * Return a new sanitized and unique file name based on {@link name} that is not
+ * the same as any existing item in the given {@link directoryPath}.
  *
  *
  * This function only works when we are running inside an electron app.
  * This function only works when we are running inside an electron app.
  * @see {@link safeDirectoryName}.
  * @see {@link safeDirectoryName}.
  */
  */
-export const sanitizedUniqueFileName = async (
-    directoryPath: string,
-    name: string,
-) => {
-    let fileExportName = sanitizeFilename(name);
+export const safeFileName = async (directoryPath: string, name: string) => {
+    let result = sanitizeFilename(name);
     let count = 1;
     let count = 1;
-    while (await exists(`${directoryPath}/${fileExportName}`)) {
-        const filenameParts = splitFilenameAndExtension(sanitizeFilename(name));
-        if (filenameParts[1]) {
-            fileExportName = `${filenameParts[0]}(${count}).${filenameParts[1]}`;
-        } else {
-            fileExportName = `${filenameParts[0]}(${count})`;
-        }
+    while (await exists(`${directoryPath}/${result}`)) {
+        const [fn, ext] = nameAndExtension(sanitizeFilename(name));
+        if (ext) result = `${fn}(${count}).${ext}`;
+        else result = `${fn}(${count})`;
         count++;
         count++;
     }
     }
-    return fileExportName;
+    return result;
 };
 };
 
 
 const exists = (path: string) => ensureElectron().fs.exists(path);
 const exists = (path: string) => ensureElectron().fs.exists(path);

+ 14 - 0
web/packages/next/file.ts

@@ -1,5 +1,19 @@
 import type { ElectronFile } from "./types/file";
 import type { ElectronFile } from "./types/file";
 
 
+/**
+ * Split a filename into its components - the name itself, and the extension (if
+ * any) - returning both. The dot is not included in either.
+ *
+ * For example, `foo-bar.png` will be split into ["foo-bar", "png"].
+ */
+export const nameAndExtension = (
+    fileName: string,
+): [string, string | undefined] => {
+    const i = fileName.lastIndexOf(".");
+    if (i == -1) return [fileName, undefined];
+    else return [fileName.slice(0, i), fileName.slice(i + 1)];
+};
+
 export function getFileNameSize(file: File | ElectronFile) {
 export function getFileNameSize(file: File | ElectronFile) {
     return `${file.name}_${convertBytesToHumanReadable(file.size)}`;
     return `${file.name}_${convertBytesToHumanReadable(file.size)}`;
 }
 }