Skip to content

Commit bb94a05

Browse files
author
SAANN3
committed
first commit
0 parents  commit bb94a05

29 files changed

Lines changed: 21603 additions & 0 deletions

.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
BROWSER=none

.gitignore

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# production
12+
/build
13+
/dist
14+
15+
# misc
16+
.DS_Store
17+
.env.local
18+
.env.development.local
19+
.env.test.local
20+
.env.production.local
21+
22+
npm-debug.log*
23+
yarn-debug.log*
24+
yarn-error.log*

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Folder scanner
2+
Program written in electron , that shows size of your folders on drive.
3+
### Screenshot
4+
!![Untitled](https://github.com/SAANN3/FolderScan/assets/95036865/122f3a21-96a1-4b29-b827-6a7c2da3c52f)
5+
|
6+
## Installation
7+
[Download](https://github.com/SAANN3/FolderScan/releases) a linux binary or windows installer
8+
## Usage
9+
Run,select disk and let it scan (3~10 minutes), then navigate using mouse and buttons
10+
11+
Also while scanning program will be showing itself as not responing, due to heavy task and ui is still going to update.
12+
13+
### Or if you prefer to compile from source
14+
#### For linux
15+
```
16+
https://github.com/SAANN3/FolderScan
17+
cd FolderScan
18+
npm install
19+
npm run build
20+
npm run build-linux
21+
npm run electron-pack
22+
```
23+
Appimage will be placed in dist folder
24+
#### For windows
25+
```
26+
https://github.com/SAANN3/FolderScan
27+
cd FolderScan
28+
npm install
29+
npm run build
30+
npm run build-windows
31+
npm run electron-pack
32+
```
33+
Installer will be placed in dist folder
34+
35+

electron/DiskLists.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const os = require('os');
2+
const { execSync } = require('node:child_process');
3+
class DiskLists{
4+
// name spareName mountpoints[]
5+
static getDisks(){
6+
switch(os.platform()){
7+
case "linux":
8+
return this.#getDisksLinux();
9+
case "win32":
10+
return this.#getDisksWindows();
11+
default:
12+
console.log("currently unsupported");
13+
}
14+
}
15+
static #getDisksLinux(){
16+
let returnVal = [];
17+
const command = "lsblk -I 8 --json -o NAME,LABEL,MOUNTPOINTS";
18+
let output = execSync(command).toString();
19+
output = JSON.parse(output);
20+
output['blockdevices'].forEach(disk => {
21+
disk['children'].forEach(partition => {
22+
if(partition["mountpoints"][0]!=null && partition["mountpoints"][0]!="[SWAP]"){
23+
returnVal.push({name:partition["label"],spareName:partition["name"],mountpoints:partition["mountpoints"]});
24+
}
25+
});
26+
});
27+
return returnVal;
28+
}
29+
static #getDisksWindows(){
30+
let returnVal = [];
31+
const command = "Get-WmiObject Win32_LogicalDisk |select Name,VolumeName |ConvertTo-JSON";
32+
let output = execSync(command,{'shell':'powershell.exe'}).toString();
33+
output = JSON.parse(output);
34+
output.forEach(disk => {
35+
returnVal.push({name:disk["VolumeName"],spareName:disk["Name"],mountpoints:[disk["Name"]+"\\"]});
36+
});
37+
return returnVal;
38+
}
39+
};
40+
module.exports = DiskLists;

electron/abstractClass.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
const fs = require('fs');
2+
const nodePath = require('path');
3+
module.exports = class AbstractFile{
4+
_size = 0;
5+
#parent = null;
6+
#path = "";
7+
#type = "";
8+
#readable = false;
9+
#isReadable(){
10+
let out = false;
11+
try{
12+
fs.accessSync(this.#path, fs.constants.R_OK);
13+
out = true;
14+
}catch (err){
15+
out = false;
16+
console.log("Can't read " + this.#path);
17+
}
18+
this.#readable = out;
19+
}
20+
21+
constructor(parent,path,type){
22+
this.#type = type;
23+
this.#parent = parent;
24+
this.#path = path;
25+
this.#isReadable();
26+
}
27+
_setParent(parent){
28+
this.#parent = parent;
29+
}
30+
get parent(){
31+
return this.#parent;
32+
}
33+
get type(){
34+
return this.#type;
35+
}
36+
get currentSize(){
37+
return this._size;
38+
};
39+
get currentName(){
40+
return nodePath.basename(this.#path);
41+
}
42+
get canRead(){
43+
return this.#readable;
44+
}
45+
get currentPath(){
46+
return this.#path;
47+
};
48+
}

electron/fileClass.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const AbstractClass = require("./abstractClass");
2+
const fs = require('fs');
3+
module.exports = class FileClass extends AbstractClass{
4+
constructor(parent,path){
5+
super(parent,path,"file");
6+
if(this.canRead){
7+
try {
8+
this._size += fs.statSync(path).size;
9+
parent._size += this._size;
10+
} catch (error) {
11+
console.log("Can't read " + path);
12+
}
13+
14+
}
15+
16+
}
17+
18+
19+
}

electron/folderClass.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
const AbstractFile = require("./abstractClass");
2+
const FileClass = require("./fileClass");
3+
const nodePath = require('path');
4+
const globalVariables = require('./globalVar');
5+
const path = require('path');
6+
const fs = require('fs');
7+
const os = require('os');
8+
module.exports = class FolderClass extends AbstractFile{
9+
#childrens = [];
10+
#setChildrens(){
11+
globalVariables.window.webContents.send('fileUpdate', this.currentPath);
12+
let openedDir = [];
13+
try{
14+
openedDir = fs.readdirSync(this.currentPath,{withFileTypes: true});
15+
}
16+
catch(err){
17+
console.log("Can't open directory " + this.currentPath);
18+
}
19+
if(openedDir.length!=0){
20+
for(let i = 0;i<openedDir.length;i++){
21+
let path = nodePath.join(openedDir[i].path,openedDir[i].name);
22+
if(openedDir[i].isDirectory()){
23+
this.#childrens.push(new FolderClass(this,path));
24+
}
25+
if(openedDir[i].isFile()){
26+
this.#childrens.push(new FileClass(this,path));
27+
}
28+
}
29+
}
30+
}
31+
findChild(path){
32+
if(path === this.currentPath){
33+
return this.#childrens;
34+
}
35+
let cuttedPath;
36+
if(this.currentPath == "/"){
37+
cuttedPath = path.replace(this.currentPath,"");
38+
}
39+
else{
40+
if(this.currentPath[this.currentPath.length-1] != globalVariables.separator){
41+
cuttedPath = path.replace(this.currentPath + globalVariables.separator,"");
42+
}
43+
else{
44+
cuttedPath = path.replace(this.currentPath,"");
45+
}
46+
}
47+
let arrFolders = cuttedPath.split(globalVariables.separator);
48+
for(let i = 0;i<this.#childrens.length;i++){
49+
if(this.#childrens[i].currentName === (arrFolders[0])){
50+
return this.#childrens[i].findChild(path);
51+
}
52+
}
53+
}
54+
openFolder(path) {
55+
let openedChildrens = this.findChild(path);
56+
let arr = [];
57+
for(let i = 0;i<openedChildrens.length;i++){
58+
arr.push({
59+
name:openedChildrens[i].currentName,
60+
path:openedChildrens[i].currentPath,
61+
size:openedChildrens[i]._size,
62+
type:openedChildrens[i].type,
63+
64+
});
65+
}
66+
let isLast = false;
67+
if(path==this.currentPath && this.parent==null){
68+
isLast = true;
69+
}
70+
globalVariables.window.webContents.send('getFileList',path,arr,isLast);
71+
}
72+
get getChildrens(){
73+
return this.#childrens;
74+
}
75+
constructor(parent,path){
76+
super(parent,path,"folder");
77+
if(os.platform=="linux"){
78+
//Linux mount system can let you mount one drive in another
79+
//For example /media/PATH are mounted inside system partition(/)
80+
//So why do we need to rescan already scanned folders?
81+
if(globalVariables.alreadyOpened.length!=0 && parent!=null){
82+
for(let i = 0; i<globalVariables.alreadyOpened.length;i++){
83+
if(globalVariables.alreadyOpened[i].currentPath==path){
84+
globalVariables.alreadyOpened[i]._setParent(parent);
85+
this._size = globalVariables.alreadyOpened[i]._size;
86+
parent._size += this._size;
87+
this.#childrens = globalVariables.alreadyOpened[i].getChildrens;
88+
return;
89+
}
90+
}
91+
}
92+
}
93+
if(this.canRead){
94+
this.#setChildrens();
95+
}
96+
if(parent!=null){
97+
parent._size += this._size;
98+
}
99+
else{
100+
globalVariables.window.webContents.send('fileEnd');
101+
this.openFolder(this.currentPath);
102+
}
103+
}
104+
105+
106+
}

electron/globalVar.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
var globalVariables = {
2+
window: null,
3+
alreadyOpened: [],
4+
separator: "",
5+
os:"",
6+
}
7+
module.exports = globalVariables;

electron/main.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
const { app, BrowserWindow, ipcMain } = require('electron');
2+
const path = require('node:path');
3+
const os = require('os');
4+
const serve = require('electron-serve');
5+
const loadURL = serve({ directory: 'build' });
6+
const FolderClass = require('./folderClass.js');
7+
const globalVariables = require('./globalVar');
8+
const DiskLists = require("./DiskLists.js")
9+
var win;
10+
11+
function isDev() {
12+
return !app.isPackaged;
13+
}
14+
const createWindow = () => {
15+
win = new BrowserWindow({
16+
width: 800,
17+
height: 600,
18+
title:"Folder Scan",
19+
webPreferences: {
20+
preload: path.join(__dirname, './preload.js'),
21+
devTools: false
22+
}
23+
});
24+
25+
if (isDev()) {
26+
win.loadURL('http://localhost:3000/');
27+
} else {
28+
loadURL(win);
29+
}
30+
globalVariables.window = win;
31+
win.setMenu(null);
32+
switch(os.platform()){
33+
case "linux":
34+
globalVariables.separator = "/";
35+
break;
36+
case "win32":
37+
globalVariables.separator = "\\";
38+
break;
39+
}
40+
globalVariables.os = os.platform();
41+
}
42+
app.on('window-all-closed', () => {
43+
if (process.platform !== 'darwin') app.quit()
44+
})
45+
app.whenReady().then(() => {
46+
createWindow()
47+
app.on('activate', () => {
48+
if (BrowserWindow.getAllWindows().length === 0) createWindow()
49+
})
50+
})
51+
52+
ipcMain.on("wantDisks",(event)=>{
53+
globalVariables.window.webContents.send('getDisks',DiskLists.getDisks());
54+
})
55+
ipcMain.on("wantOs",(event)=>{
56+
globalVariables.window.webContents.send('getOs',globalVariables.os);
57+
});
58+
ipcMain.on("startScanning",(event,data)=>{
59+
let finded = false;
60+
if(globalVariables.alreadyOpened.length!=0){
61+
for(let i = 0; i<globalVariables.alreadyOpened.length;i++){
62+
if(globalVariables.alreadyOpened[i].currentPath==data){
63+
globalVariables.alreadyOpened[i].openFolder(data);
64+
finded = true;
65+
globalVariables.window.webContents.send('fileEnd');
66+
break;
67+
}
68+
if(globalVariables.alreadyOpened[i].findChild(data)!=undefined){
69+
globalVariables.alreadyOpened[i].openFolder(data);
70+
finded = true;
71+
globalVariables.window.webContents.send('fileEnd');
72+
break;
73+
}
74+
}
75+
}
76+
if(!finded){
77+
let mainDisk = new FolderClass(null,data);
78+
globalVariables.alreadyOpened.push(mainDisk);
79+
}
80+
81+
})
82+
ipcMain.on("openFolder",(event,path)=>{
83+
for(let i = 0; i<globalVariables.alreadyOpened.length;i++){
84+
if(path.includes(globalVariables.alreadyOpened[i].currentPath)){
85+
globalVariables.alreadyOpened[i].openFolder(path);
86+
return;
87+
}
88+
}
89+
})

electron/preload.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const { contextBridge, ipcRenderer } = require('electron');
2+
contextBridge.exposeInMainWorld('electronAPI', {
3+
getDisks: (callback) => ipcRenderer.on('getDisks', (_event,disks) => callback(disks)),
4+
wantDisks: () => ipcRenderer.send("wantDisks"),
5+
getPlatform: () => ipcRenderer.send("getPlatform",platform),
6+
startScanning: (path) => ipcRenderer.send('startScanning',path),
7+
openFolder: (path) => ipcRenderer.send('openFolder',path),
8+
onFileUpdate: (callback) => ipcRenderer.on('fileUpdate', (_event, value) => callback(value)),
9+
onFileEnd: (callback) => ipcRenderer.on('fileEnd', (_event) => callback()),
10+
onOSget: (callback) => ipcRenderer.on('getOs', (_event,os) => callback(os)),
11+
wantOS: () => ipcRenderer.send("wantOs"),
12+
getFileList: (callback) => ipcRenderer.on('getFileList',(_event,path,data,isLast) => callback(path,data,isLast)),
13+
removeThisListener: (str,func) => ipcRenderer.removeAllListeners(str)
14+
})

0 commit comments

Comments
 (0)