Skip to content

Commit 4f7dfe8

Browse files
committed
Fix disk usage display: use native du/PowerShell instead of recursive stat
Previous async Promise.all approach had two problems: 1. Created tens of thousands of concurrent stat() calls for large directories (venv has 100k+ files), overwhelming the OS I/O queue on Windows and returning 0 or hanging indefinitely 2. Math.round(bytes/1048576) silently showed '0 MB' for dirs < 0.5 MB Fix: - Linux/macOS: use 'du -sm <dir>' via exec() — fast, non-blocking, OS-native - Windows: use PowerShell Get-ChildItem | Measure-Object — reliable, async - For excludeDirs: compute total minus excluded subdirs separately - Display: show '< 1 MB' for small dirs, 'not installed' for missing venv
1 parent a88dc6f commit 4f7dfe8

3 files changed

Lines changed: 44 additions & 32 deletions

File tree

electron/main.js

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const os = require('os');
66
const fs = require('fs');
77
const https = require('https');
88
const http = require('http');
9-
const { spawnSync, spawn } = require('child_process');
9+
const { spawnSync, spawn, exec } = require('child_process');
1010

1111
// ── Optional: node-pty for real tty on Unix (tqdm in-place refresh) ──────────
1212
let nodePty = null;
@@ -627,37 +627,44 @@ function discoverLectures(courseId) {
627627
}
628628

629629
// ── Uninstall helpers ─────────────────────────────────────────────────────────
630-
async function getDirSizeBytes(dirPath) {
631-
let bytes = 0;
632-
try {
633-
const entries = await fs.promises.readdir(dirPath, { withFileTypes: true });
634-
await Promise.all(entries.map(async e => {
635-
const full = path.join(dirPath, e.name);
636-
if (e.isDirectory()) {
637-
bytes += await getDirSizeBytes(full);
638-
} else {
639-
try { bytes += (await fs.promises.stat(full)).size; } catch {}
640-
}
641-
}));
642-
} catch {}
643-
return bytes;
630+
// Returns directory size in MB using native OS tools (non-blocking, fast).
631+
function getDirSizeMBNative(dirPath) {
632+
return new Promise(resolve => {
633+
if (!fs.existsSync(dirPath)) { resolve(0); return; }
634+
if (process.platform === 'win32') {
635+
// PowerShell: sum all file lengths under the directory
636+
const esc = dirPath.replace(/'/g, "''");
637+
const cmd = `powershell -NoProfile -NonInteractive -Command ` +
638+
`"(Get-ChildItem -LiteralPath '${esc}' -Recurse -File ` +
639+
`-ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum"`;
640+
exec(cmd, { timeout: 30000, windowsHide: true }, (_, stdout) => {
641+
const bytes = parseInt((stdout || '').trim()) || 0;
642+
resolve(Math.round(bytes / 1048576));
643+
});
644+
} else {
645+
// Linux / macOS: du -sm gives megabytes directly
646+
const esc = dirPath.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
647+
exec(`du -sm -- "${esc}"`, { timeout: 30000 }, (err, stdout) => {
648+
if (err) { resolve(0); return; }
649+
const mb = parseInt((stdout || '').trim().split(/\s+/)[0]) || 0;
650+
resolve(mb);
651+
});
652+
}
653+
});
644654
}
645655

646656
async function getDirSizeMB(dirPath, excludeDirs = []) {
647-
let bytes = 0;
648-
try {
649-
const entries = await fs.promises.readdir(dirPath, { withFileTypes: true });
650-
await Promise.all(entries.map(async e => {
651-
if (excludeDirs.includes(e.name)) return;
652-
const full = path.join(dirPath, e.name);
653-
if (e.isDirectory()) {
654-
bytes += await getDirSizeBytes(full);
655-
} else {
656-
try { bytes += (await fs.promises.stat(full)).size; } catch {}
657-
}
658-
}));
659-
} catch {}
660-
return Math.round(bytes / (1024 * 1024));
657+
if (!fs.existsSync(dirPath)) return 0;
658+
const total = await getDirSizeMBNative(dirPath);
659+
if (excludeDirs.length === 0) return total;
660+
// Subtract excluded subdirs from the total
661+
const excluded = await Promise.all(
662+
excludeDirs
663+
.map(d => path.join(dirPath, d))
664+
.filter(p => fs.existsSync(p))
665+
.map(getDirSizeMBNative)
666+
);
667+
return Math.max(0, total - excluded.reduce((a, b) => a + b, 0));
661668
}
662669

663670
function rmRecursive(dirPath) {

electron/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "auto-note",
3-
"version": "0.9.6",
3+
"version": "0.9.7",
44
"description": "AutoNote — lecture notes generator from Canvas recordings",
55
"homepage": "https://github.com/nodeeeeee/Auto-Note",
66
"author": {

electron/renderer/app.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,9 +1303,14 @@ async function attachPageHandlers() {
13031303
if (!el) return;
13041304
try {
13051305
const s = await window.api.getUninstallSizes();
1306-
const fmt = mb => mb >= 1024 ? `${(mb/1024).toFixed(1)} GB` : `${mb} MB`;
1306+
const fmt = mb => {
1307+
if (mb >= 1024) return `${(mb / 1024).toFixed(1)} GB`;
1308+
if (mb >= 1) return `${mb} MB`;
1309+
return '< 1 MB';
1310+
};
1311+
const venvLabel = s.venv === 0 ? '<span style="color:var(--c-white-45)">not installed</span>' : `<b>${fmt(s.venv)}</b>`;
13071312
let html = `<b>Disk usage (controlled by checkboxes below):</b><br>`;
1308-
html += `&nbsp;&nbsp;• ML Environment (~/.auto_note/venv): <b>${fmt(s.venv)}</b><br>`;
1313+
html += `&nbsp;&nbsp;• ML Environment (~/.auto_note/venv): ${venvLabel}<br>`;
13091314
html += `&nbsp;&nbsp;• Settings &amp; config (~/.auto_note/): <b>${fmt(s.settings)}</b><br>`;
13101315
if (s.content !== null) {
13111316
html += `&nbsp;&nbsp;• Generated content (${s.outputDir}): <b>${fmt(s.content)}</b>`;

0 commit comments

Comments
 (0)