From 04a4f9b29688405b6ddecb2dfe5ac10fe822abce Mon Sep 17 00:00:00 2001 From: an-lee Date: Thu, 15 Aug 2024 17:18:12 +0800 Subject: [PATCH] display library disk usage (#976) --- enjoy/src/i18n/en.json | 15 ++- enjoy/src/i18n/zh-CN.json | 15 ++- enjoy/src/main/window.ts | 37 ++++++++ enjoy/src/preload.ts | 3 + .../preferences/library-settings.tsx | 93 ++++++++++++++++--- enjoy/src/types/enjoy-app.d.ts | 1 + enjoy/src/types/index.d.ts | 6 ++ enjoy/src/utils.ts | 21 +++++ 8 files changed, 176 insertions(+), 15 deletions(-) diff --git a/enjoy/src/i18n/en.json b/enjoy/src/i18n/en.json index b3523f1c..0f7afccf 100644 --- a/enjoy/src/i18n/en.json +++ b/enjoy/src/i18n/en.json @@ -723,5 +723,18 @@ "cannotFindSourceFile": "Cannot find source file", "cleanUp": "Clean up", "cleanUpConfirmation": "Are you sure to remove resources without source file?", - "cleanedUpSuccessfully": "Cleaned up successfully" + "cleanedUpSuccessfully": "Cleaned up successfully", + "libraryDescriptions": { + "library": "Contains all files created by Enjoy while you are using the app.", + "database": "Records all your activities and settings.", + "audios": "Contains all audio files you added.", + "videos": "Contains all video files you added.", + "segments": "Contains all segments you make recording or note.", + "speeches": "Contains all speeches created by AI TTS.", + "recordings": "Contains all recordings you made.", + "whisper": "Contains all whisper models you downloaded.", + "waveforms": "Contains all waveforms decoded from audio/videos. They're for caching. It's save to delete them.", + "logs": "Contains some logs helpful for debugging.", + "cache": "Contains cached files. They will be cleaned up automatically." + } } diff --git a/enjoy/src/i18n/zh-CN.json b/enjoy/src/i18n/zh-CN.json index ccfc2bd4..becd5ec7 100644 --- a/enjoy/src/i18n/zh-CN.json +++ b/enjoy/src/i18n/zh-CN.json @@ -723,5 +723,18 @@ "cannotFindSourceFile": "无法找到源文件,可能已经被删除", "cleanUp": "清理", "cleanUpConfirmation": "您确定要移除所有找不到源文件的资源吗?", - "cleanedUpSuccessfully": "清理成功" + "cleanedUpSuccessfully": "清理成功", + "libraryDescriptions": { + "library": "资源库总目录,包含所有您使用 Enjoy 过程产生的文件", + "database": "数据库文件,记录您使用 Enjoy 的所有数据", + "audios": "您添加的所有音频文件", + "videos": "您添加的所有视频文件", + "segments": "音频/视频文件的段落,跟读过程中产生", + "speeches": "AI TTS 服务生成的语音文件", + "recordings": "您的所有录音文件", + "whisper": "Whisper 模型文件,用于语音转文本", + "waveforms": "波形文件,用于显示音频波形。用作缓存,可以删除", + "logs": "日志文件,帮助开发者调试问题", + "cache": "缓存文件,会自动清理。" + } } diff --git a/enjoy/src/main/window.ts b/enjoy/src/main/window.ts index 249278d0..4bb41b6a 100644 --- a/enjoy/src/main/window.ts +++ b/enjoy/src/main/window.ts @@ -384,6 +384,43 @@ ${log} } ); + ipcMain.handle("app-disk-usage", () => { + const paths: { [key: string]: string } = { + library: settings.libraryPath(), + database: settings.dbPath(), + audios: path.join(settings.userDataPath(), "audios"), + videos: path.join(settings.userDataPath(), "videos"), + segments: path.join(settings.userDataPath(), "segments"), + speeches: path.join(settings.userDataPath(), "speeches"), + recordings: path.join(settings.userDataPath(), "recordings"), + whisper: path.join(settings.libraryPath(), "whisper"), + waveforms: path.join(settings.libraryPath(), "waveforms"), + logs: path.join(settings.libraryPath(), "logs"), + cache: settings.cachePath(), + }; + + const sizeSync = (p: string): number => { + const stat = fs.statSync(p); + if (stat.isFile()) return stat.size; + else if (stat.isDirectory()) + return fs + .readdirSync(p) + .reduce((a, e) => a + sizeSync(path.join(p, e)), 0); + else return 0; // can't take size of a stream/symlink/socket/ + }; + + return Object.keys(paths).map((key) => { + const p = paths[key]; + const size = sizeSync(p); + + return { + name: key, + path: p, + size, + }; + }); + }); + // Shell ipcMain.handle("shell-open-external", (_event, url) => { shell.openExternal(url); diff --git a/enjoy/src/preload.ts b/enjoy/src/preload.ts index f8629b92..096c621d 100644 --- a/enjoy/src/preload.ts +++ b/enjoy/src/preload.ts @@ -47,6 +47,9 @@ contextBridge.exposeInMainWorld("__ENJOY_APP__", { removeCmdOutputListeners: () => { ipcRenderer.removeAllListeners("app-on-cmd-output"); }, + diskUsage: () => { + return ipcRenderer.invoke("app-disk-usage"); + }, version, }, window: { diff --git a/enjoy/src/renderer/components/preferences/library-settings.tsx b/enjoy/src/renderer/components/preferences/library-settings.tsx index 0ef9c816..1141033c 100644 --- a/enjoy/src/renderer/components/preferences/library-settings.tsx +++ b/enjoy/src/renderer/components/preferences/library-settings.tsx @@ -1,12 +1,20 @@ import { t } from "i18next"; import { + Badge, Button, + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, + ScrollArea, + Separator, } from "@renderer/components/ui"; -import { - AppSettingsProviderContext, -} from "@renderer/context"; -import { useContext } from "react"; +import { AppSettingsProviderContext } from "@renderer/context"; +import { useContext, useEffect, useState } from "react"; import { InfoIcon } from "lucide-react"; +import { humanFileSize } from "@/utils"; export const LibrarySettings = () => { const { libraryPath, EnjoyApp } = useContext(AppSettingsProviderContext); @@ -25,12 +33,6 @@ export const LibrarySettings = () => { } }; - const openLibraryPath = async () => { - if (libraryPath) { - await EnjoyApp.shell.openPath(libraryPath); - } - }; - return (
@@ -40,9 +42,26 @@ export const LibrarySettings = () => {
- + + + + + + + {t("usage")} + + Disk usage of Enjoy + + +
+ + + +
+
+
); }; + +const DiskUsage = () => { + const [usage, setUsage] = useState([]); + const { EnjoyApp } = useContext(AppSettingsProviderContext); + + const openPath = async (path: string) => { + if (path) { + await EnjoyApp.shell.openPath(path); + } + }; + + useEffect(() => { + EnjoyApp.app.diskUsage().then((usage) => { + console.log(usage); + setUsage(usage); + }); + }, []); + + return ( +
+ {usage.map((item) => ( +
+
+
+
+ /{item.path.split("/").pop()} +
+ {humanFileSize(item.size)} +
+
+
+ {t(`libraryDescriptions.${item.name}`)} +
+
+ +
+ +
+ ))} +
+ ); +}; diff --git a/enjoy/src/types/enjoy-app.d.ts b/enjoy/src/types/enjoy-app.d.ts index 696e6d43..63d63747 100644 --- a/enjoy/src/types/enjoy-app.d.ts +++ b/enjoy/src/types/enjoy-app.d.ts @@ -13,6 +13,7 @@ type EnjoyAppType = { createIssue: (title: string, body: string) => Promise; onCmdOutput: (callback: (event, output: string) => void) => void; removeCmdOutputListeners: () => void; + diskUsage: () => Promise; version: string; }; window: { diff --git a/enjoy/src/types/index.d.ts b/enjoy/src/types/index.d.ts index f04312c6..45d2f0a3 100644 --- a/enjoy/src/types/index.d.ts +++ b/enjoy/src/types/index.d.ts @@ -183,3 +183,9 @@ type PlatformInfo = { arch: string; version: string; }; + +type DiskUsageType = { + name: string; + path: string; + size: number; +}[]; diff --git a/enjoy/src/utils.ts b/enjoy/src/utils.ts index c9aa3348..0e6edea4 100644 --- a/enjoy/src/utils.ts +++ b/enjoy/src/utils.ts @@ -127,3 +127,24 @@ export const convertIpaToNormal = ( return converted; } }; + +// make size of bytes human readable +export const humanFileSize = (bytes: number, si: boolean = false) => { + const thresh = si ? 1000 : 1024; + if (Math.abs(bytes) < thresh) { + return bytes + " B"; + } + const units = si + ? ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] + : ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"]; + let u = -1; + const r = 10; + do { + bytes /= thresh; + ++u; + } while ( + Math.round(Math.abs(bytes) * r) / r >= thresh && + u < units.length - 1 + ); + return bytes.toFixed(1) + " " + units[u]; +};