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
JavaScript
// 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}`);
}
}