Skip to content

Commit 6640d1e

Browse files
committed
Initial Upload
0 parents  commit 6640d1e

File tree

6 files changed

+373
-0
lines changed

6 files changed

+373
-0
lines changed

.gitignore

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Node modules
2+
node_modules/
3+
4+
# Build directories
5+
.next/
6+
dist/
7+
out/
8+
lib/runners/models
9+
# System files
10+
.DS_Store
11+
12+
# Environment variables
13+
.env
14+
.env.local
15+
.env.development.local
16+
.env.test.local
17+
.env.production.local
18+
19+
# Logs
20+
npm-debug.log*
21+
yarn-debug.log*
22+
yarn-error.log*
23+
24+
# Lock files
25+
package-lock.json
26+
yarn.lock
27+
28+
# TypeScript
29+
*.tsbuildinfo
30+
31+
# Production
32+
build.js
33+
34+
# Vercel
35+
.vercel
36+
37+
# IDE files
38+
.idea/
39+
.vscode/
40+
41+
lib/src
42+
43+
# Miscellaneous
44+
*.swp
45+
*.swo
46+
*~

index.html

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>ReNameify</title>
5+
<style>
6+
body {
7+
font-family: Arial, sans-serif;
8+
padding: 20px;
9+
}
10+
button {
11+
padding: 10px 20px;
12+
margin: 10px 0;
13+
}
14+
#fileList {
15+
margin-top: 20px;
16+
}
17+
.file-item {
18+
margin: 5px 0;
19+
padding: 10px;
20+
background: #f0f0f0;
21+
border-radius: 4px;
22+
}
23+
</style>
24+
</head>
25+
<body>
26+
<h1>ReNameify</h1>
27+
<h3>Rename your Files based on its Content</h3>
28+
<p>Select a directory and organize all your files with a context-aware file-names. It is saved under <b>renamed_files</b> under the selected directory</p>
29+
<button id="selectDir">Select Directory</button>
30+
<button id="processFiles">Process Files</button>
31+
<div id="fileList"></div>
32+
<script src="renderer.js"></script>
33+
</body>
34+
</html>

logo.png

7.82 KB
Loading

main.js

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
2+
const path = require('path');
3+
const fs = require('fs-extra');
4+
const pdfParse = require('pdf-parse');
5+
6+
// import { app, BrowserWindow, ipcMain, dialog } from 'electron';
7+
// import path from 'path';
8+
// import fs from 'fs-extra';
9+
// import * as mupdfjs from 'mupdf/mupdfjs';
10+
// console.log("Encoding:", process.stdout.getEncoding());
11+
12+
process.stdout.setDefaultEncoding('utf-8');
13+
14+
let mainWindow;
15+
16+
function createWindow() {
17+
mainWindow = new BrowserWindow({
18+
icon: path.join(__dirname, 'logo.png'),
19+
width: 800,
20+
height: 600,
21+
webPreferences: {
22+
nodeIntegration: true,
23+
contextIsolation: false
24+
}
25+
});
26+
27+
mainWindow.loadFile('index.html');
28+
}
29+
30+
app.whenReady().then(createWindow);
31+
32+
app.on('window-all-closed', () => {
33+
if (process.platform !== 'darwin') {
34+
app.quit();
35+
}
36+
});
37+
38+
app.on('activate', () => {
39+
if (BrowserWindow.getAllWindows().length === 0) {
40+
createWindow();
41+
}
42+
});
43+
44+
ipcMain.handle('select-directory', async () => {
45+
const result = await dialog.showOpenDialog(mainWindow, {
46+
properties: ['openDirectory']
47+
});
48+
return result.filePaths;
49+
});
50+
51+
52+
function isValidLine(line) {
53+
// Skip if line is empty
54+
if (!line || line.length === 0) return false;
55+
56+
// Skip if line is too short (less than 5 characters)
57+
if (line.length < 5) return false;
58+
59+
// Skip if line is just numbers (including dots and spaces)
60+
if (/^[\d\s.]+$/.test(line)) return false;
61+
62+
// Skip if line contains common page number patterns
63+
// Including "page 78" and "- 78 -"
64+
if (/^page\s*\d+$/i.test(line) || /^-\s*\d+\s*-$/.test(line)) return false;
65+
66+
// Skip if line is mostly special characters (allow Korean characters)
67+
const specialCharRatio = (line.match(/[^a-zA-Z0-9-\s]/g) || []).length / line.length;
68+
if (specialCharRatio > 0.5) return false;
69+
70+
return true;
71+
}
72+
73+
74+
ipcMain.handle('process-files', async (event, dirPath) => {
75+
try {
76+
const outputDir = path.join(dirPath, 'renamed_files');
77+
await fs.ensureDir(outputDir);
78+
79+
const files = await fs.readdir(dirPath);
80+
const results = [];
81+
82+
for (const file of files) {
83+
const filePath = path.join(dirPath, file);
84+
const stats = await fs.stat(filePath);
85+
86+
if (stats.isFile()) {
87+
try {
88+
let firstLine = '';
89+
const fileExt = path.extname(file).toLowerCase();
90+
91+
if (fileExt === '.pdf') {
92+
93+
// let doc = mupdfjs.PDFDocument.openDocument(fs.readFileSync(filePath), "application/pdf");
94+
// let page = new mupdfjs.PDFPage(doc, 0); // returns the first page of the document
95+
// let extractedText = page.getText()
96+
97+
// // cleanup
98+
// doc.destroy()
99+
// page.destroy()
100+
101+
const dataBuffer = await fs.readFile(filePath);
102+
const pdfData = await pdfParse(dataBuffer);
103+
extractedText = pdfData.text
104+
105+
console.log(extractedText)
106+
// Get all lines and find the first non-empty one
107+
const lines = extractedText.split('\n')
108+
.map(line => line.trim())
109+
.filter(line => line.length > 0);
110+
111+
// Find first valid line
112+
firstLine = '';
113+
for (const line of lines) {
114+
if (isValidLine(line)) {
115+
firstLine = line.substring(0, 100); // Get first 100 characters
116+
break;
117+
}
118+
}
119+
120+
} else {
121+
const content = await fs.readFile(filePath, 'utf8');
122+
const lines = content.split('\n')
123+
.map(line => line.trim())
124+
.filter(line => line.length > 0);
125+
// Find first valid line
126+
firstLine = '';
127+
for (const line of lines) {
128+
if (isValidLine(line)) {
129+
firstLine = line.substring(0, 100); // Get first 100 characters
130+
break;
131+
}
132+
}
133+
134+
}
135+
136+
let newFileName;
137+
if (firstLine && firstLine.length > 0) {
138+
// Create safe filename from first non-empty line
139+
newFileName = firstLine
140+
.replace(/[^a-z0-9-]/gi, '_')
141+
+ fileExt;
142+
} else {
143+
// If no text found, use original filename
144+
newFileName = file;
145+
}
146+
147+
// Handle duplicate filenames
148+
let finalNewPath = path.join(outputDir, newFileName);
149+
// let counter = 1;
150+
// while (await fs.pathExists(finalNewPath)) {
151+
// const nameWithoutExt = newFileName.slice(0, -fileExt.length);
152+
// newFileName = `${nameWithoutExt}_${counter}${fileExt}`;
153+
// finalNewPath = path.join(outputDir, newFileName);
154+
// counter++;
155+
// }
156+
157+
await fs.copy(filePath, finalNewPath);
158+
159+
results.push({
160+
originalName: file,
161+
newName: newFileName,
162+
success: true,
163+
noTextFound: !firstLine
164+
});
165+
} catch (err) {
166+
// Handle PDF parsing errors (common with scanned PDFs)
167+
if (err.message.includes('PDF')) {
168+
// If PDF parsing fails, use original filename
169+
const newPath = path.join(outputDir, file);
170+
await fs.copy(filePath, newPath);
171+
results.push({
172+
originalName: file,
173+
newName: file,
174+
success: true,
175+
noTextFound: true
176+
});
177+
} else {
178+
results.push({
179+
originalName: file,
180+
error: err.message,
181+
success: false
182+
});
183+
}
184+
}
185+
}
186+
}
187+
188+
return results;
189+
} catch (err) {
190+
throw err;
191+
}
192+
});

package.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"name": "file-renamer-app",
3+
"version": "0.0.1",
4+
"description": "Organizes files based on the inside contents",
5+
"main": "main.js",
6+
"scripts": {
7+
"start": "electron .",
8+
"test": "echo \"Error: no test specified\" && exit 1",
9+
"pack:win": "electron-builder build --win"
10+
},
11+
"author": "Bibek K C <bibekyess@gmail.com>",
12+
"license": "MIT",
13+
"dependencies": {
14+
"fs-extra": "^11.2.0",
15+
"path": "^0.12.7",
16+
"pdf-parse": "^1.1.1"
17+
},
18+
"devDependencies": {
19+
"electron": "^33.2.0",
20+
"electron-builder": "^25.1.8"
21+
},
22+
"build": {
23+
"appId": "com.bibekyess.file-renamer",
24+
"productName": "ReNamify",
25+
"directories": {
26+
"output": "dist"
27+
},
28+
"win": {
29+
"target": [
30+
"nsis"
31+
],
32+
"icon": "logo.png"
33+
},
34+
"nsis": {
35+
"oneClick": false,
36+
"allowToChangeInstallationDirectory": true,
37+
"createDesktopShortcut": true,
38+
"createStartMenuShortcut": true,
39+
"shortcutName": "ReNamify",
40+
"uninstallDisplayName": "ReNamify",
41+
"artifactName": "ReNamify-${version}-installer.${ext}",
42+
"deleteAppDataOnUninstall": true,
43+
"displayLanguageSelector": true
44+
}
45+
}
46+
}

renderer.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
const { ipcRenderer } = require('electron');
2+
3+
let selectedDirectory = '';
4+
5+
document.getElementById('selectDir').addEventListener('click', async () => {
6+
const paths = await ipcRenderer.invoke('select-directory');
7+
if (paths && paths.length > 0) {
8+
selectedDirectory = paths[0];
9+
document.getElementById('fileList').innerHTML = `Selected directory: ${selectedDirectory}`;
10+
}
11+
});
12+
13+
document.getElementById('processFiles').addEventListener('click', async () => {
14+
if (!selectedDirectory) {
15+
alert('Please select a directory first');
16+
return;
17+
}
18+
19+
try {
20+
const results = await ipcRenderer.invoke('process-files', selectedDirectory);
21+
displayResults(results);
22+
} catch (err) {
23+
alert('Error processing files: ' + err.message);
24+
}
25+
});
26+
27+
function displayResults(results) {
28+
const fileList = document.getElementById('fileList');
29+
fileList.innerHTML = '<h3>Processing Results:</h3>';
30+
31+
results.forEach(result => {
32+
const div = document.createElement('div');
33+
div.className = 'file-item';
34+
35+
if (result.success) {
36+
let status = 'Success';
37+
if (result.noTextFound) {
38+
status = 'No readable text found - Original filename kept';
39+
}
40+
41+
div.innerHTML = `
42+
Original: ${result.originalName}<br>
43+
New name: ${result.newName}<br>
44+
Status: ${status}
45+
`;
46+
} else {
47+
div.innerHTML = `
48+
Original: ${result.originalName}<br>
49+
Status: Failed - ${result.error}
50+
`;
51+
}
52+
53+
fileList.appendChild(div);
54+
});
55+
}

0 commit comments

Comments
 (0)