UNPKG

tidyup

Version:

A simple CLI tool to organize files in a directory into categorized subfolders based on file types.

122 lines (118 loc) 3.98 kB
#!/usr/bin/env node // src/index.ts import { Command } from "commander"; import fs from "fs"; import path from "path"; import { promisify } from "util"; // package.json var version = "0.0.7"; // src/index.ts var readdir = promisify(fs.readdir); var mkdir = promisify(fs.mkdir); var rename = promisify(fs.rename); var stat = promisify(fs.stat); var FILE_TYPE_MAP = { ".png": "images-png", ".jpg": "images-jpg", ".jpeg": "images-jpeg", ".mp4": "videos-mp4", ".avi": "videos-avi", ".pdf": "documents-pdf" }; var program = new Command(); program.version(version).argument("[directory]", "Directory to tidy up", ".").description("Organize files in a directory based on their extensions").option("--ext", "Use the file extetions as folder names").option("--name", "Group files by starting name").action(async (inputDir, option) => { const dirPath = path.resolve(inputDir); try { if (option.ext && option.name) { console.error("Only one of --ext or --name can be used at a time"); process.exit(1); } const dirStat = await stat(dirPath); if (!dirStat.isDirectory()) { console.error(`The provided path is not a directory: ${dirPath}`); process.exit(1); } await organizeFiles(dirPath, option); } catch (error) { console.error("An error occurred:", error.message); process.exit(1); } }); program.parse(process.argv); async function getFileTypes(dirPath) { const files = await readdir(dirPath); const fileTypes = {}; for (const file of files) { const filePath = path.join(dirPath, file); const fileStat = await stat(filePath); if (fileStat.isFile()) { const ext = path.extname(file).toLowerCase(); if (!fileTypes[ext]) { fileTypes[ext] = []; } fileTypes[ext].push(filePath); } } return fileTypes; } async function getFileNameGroups(dirPath) { const files = await readdir(dirPath); const nameGroups = {}; for (const file of files) { const filePath = path.join(dirPath, file); const fileStat = await stat(filePath); if (fileStat.isFile()) { const baseName = path.parse(file).name.slice(0, 10); if (!nameGroups[baseName]) { nameGroups[baseName] = []; } nameGroups[baseName].push(filePath); } } return nameGroups; } async function organizeFiles(dirPath, options) { let fileTypes; if (options.name) { fileTypes = await getFileNameGroups(dirPath); } else { fileTypes = await getFileTypes(dirPath); } const summary = []; for (const [ext, filePaths] of Object.entries(fileTypes)) { const newFolder = ext.split(".")[1]; const folderName = options.ext ? newFolder : FILE_TYPE_MAP[ext] || `others-${ext.replace(".", "")}`; const folderPath = path.join(dirPath, folderName); let folderCreated = false; if (!fs.existsSync(folderPath)) { await mkdir(folderPath); folderCreated = true; } let filesAdded = 0; for (const filePath of filePaths) { const fileName = path.basename(filePath); let newFilePath = path.join(folderPath, fileName); if (fs.existsSync(newFilePath)) { const fileBase = path.parse(fileName).name; const fileExt = path.extname(fileName); let counter = 1; while (fs.existsSync(newFilePath)) { const newFileName = `${fileBase}(${counter})${fileExt}`; newFilePath = path.join(folderPath, newFileName); counter++; } } await rename(filePath, newFilePath); filesAdded++; } summary.push({ folder: folderName, created: folderCreated, filesAdded }); } const lastPath = dirPath.split("/"); const lastDir = lastPath[lastPath.length - 1]; console.log(`Organization Summary for '${lastDir}':`); for (const { folder, created, filesAdded } of summary) { console.log(`- Folder: ${folder}`); console.log(` - ${created ? "Created" : "Already existed"}`); console.log(` - Files added: ${filesAdded}`); } }