// getDownloadsFiles.js import fs from 'fs/promises'; import path from 'path'; import os from 'os'; const DOWNLOADS = path.join(os.homedir(), 'Downloads'); /** * Recursively get all files in Downloads with name, path, and inode. * @returns {Promise>} */ async function getAllDownloadsFiles() { const files = []; async function scan(dir) { let entries; try { entries = await fs.readdir(dir, { withFileTypes: true }); } catch (err) { console.warn(`Cannot read directory: ${dir}`, err.message); return; } for (const entry of entries) { const fullPath = path.join(dir, entry.name); try { // Get stats (inode = ino on Unix, null on Windows) // const stats = await fs.stat(fullPath); // const inode = stats.ino ?? null; if (entry.isDirectory()) { // Recurse into subdirectories await scan(fullPath); } else if (entry.isFile()) { const parsed = path.parse(entry.name); // splits name/ext const realExtension = path.extname(fullPath); // from full path (more reliable) // Use realExtension if available, fallback to parsed.ext const extension = realExtension || parsed.ext; // Build clean name + extension const nameWithExt = parsed.name + extension; files.push({ name: entry.name, // original (may hide ext) // nameWithExt: nameWithExt, // ALWAYS has correct ext // displayName: nameWithExt, // use this in UI // extension: extension.slice(1).toLowerCase() || null, // "jpg", null if none // path: fullPath, // inode: stats.ino ?? null, // size: stats.size, // mtime: stats.mtime.toISOString() }); } // Skip symlinks, sockets, etc. (or handle if needed) } catch (err) { console.warn(`Cannot access: ${fullPath}`, err.message); } } } await scan(DOWNLOADS); return files; } export default getAllDownloadsFiles;