131 lines
4.1 KiB
JavaScript
131 lines
4.1 KiB
JavaScript
const { ipcMain, app } = require('electron');
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
const crypto = require('crypto');
|
|
const {
|
|
fileExists,
|
|
listImages,
|
|
moveFileWithCopyFallback,
|
|
readImageAsDataUrl,
|
|
} = require('./file-utils');
|
|
const logger = require('../logger');
|
|
|
|
function register() {
|
|
ipcMain.handle('list-images', async (_event, folderPath) => {
|
|
logger.log('ipc:files', 'list-images requested', { folderPath });
|
|
try {
|
|
const files = listImages(folderPath);
|
|
logger.log('ipc:files', 'list-images result', { folderPath, count: files.length });
|
|
return files;
|
|
} catch (e) {
|
|
logger.error('ipc:files', `list-images failed: ${folderPath}`, e);
|
|
return [];
|
|
}
|
|
});
|
|
|
|
ipcMain.handle('read-image', async (_event, filePath) => {
|
|
try {
|
|
return readImageAsDataUrl(filePath);
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
});
|
|
|
|
ipcMain.handle('check-files-exist', (_event, folderPath, names) => {
|
|
return names.filter(name => fileExists(path.join(folderPath, name)));
|
|
});
|
|
|
|
ipcMain.handle('move-files', async (_event, { targetFolder, files }) => {
|
|
const errors = [];
|
|
const moved = [];
|
|
for (const file of files) {
|
|
const destName = file.newName || file.name;
|
|
const dest = path.join(targetFolder, destName);
|
|
try {
|
|
moveFileWithCopyFallback(file.path, dest);
|
|
moved.push({ src: file.path, dest, name: destName });
|
|
} catch (e) {
|
|
errors.push(`${file.name}: ${e.message}`);
|
|
}
|
|
}
|
|
return { moved, errors };
|
|
});
|
|
|
|
// 삭제 → 스테이징 폴더로 이동 (undo 가능)
|
|
ipcMain.handle('stage-delete-files', async (_event, filePaths) => {
|
|
const batchDir = path.join(app.getPath('userData'), 'deleted_staging', Date.now().toString());
|
|
fs.mkdirSync(batchDir, { recursive: true });
|
|
|
|
const staged = [];
|
|
const errors = [];
|
|
for (const fp of filePaths) {
|
|
const name = path.basename(fp);
|
|
const dest = path.join(batchDir, name);
|
|
try {
|
|
moveFileWithCopyFallback(fp, dest);
|
|
staged.push({ name, originalPath: fp, stagingPath: dest });
|
|
} catch (e) {
|
|
errors.push(`${name}: ${e.message}`);
|
|
}
|
|
}
|
|
if (!staged.length) try { fs.rmdirSync(batchDir); } catch {}
|
|
return { staged, errors };
|
|
});
|
|
|
|
// 스테이징 파일 원래 위치로 복원 (undo delete)
|
|
ipcMain.handle('restore-staged-files', async (_event, deletedEntries) => {
|
|
const restored = [];
|
|
const errors = [];
|
|
for (const { name, originalPath, stagingPath } of deletedEntries) {
|
|
if (fs.existsSync(originalPath)) {
|
|
errors.push(`${name}: 같은 이름의 파일이 이미 존재합니다`);
|
|
continue;
|
|
}
|
|
try {
|
|
fs.mkdirSync(path.dirname(originalPath), { recursive: true });
|
|
moveFileWithCopyFallback(stagingPath, originalPath);
|
|
restored.push(originalPath);
|
|
} catch (e) {
|
|
errors.push(`${name}: ${e.message}`);
|
|
}
|
|
}
|
|
return { restored, errors };
|
|
});
|
|
|
|
ipcMain.handle('rename-files', async (_event, { folderPath, files, prefix, digits, postfix = '' }) => {
|
|
const errors = [];
|
|
const pad = (n) => String(n + 1).padStart(digits, '0');
|
|
|
|
// 1단계: 임시 이름으로 이동 (충돌 방지)
|
|
const tmpMap = [];
|
|
for (let i = 0; i < files.length; i++) {
|
|
const src = files[i].path;
|
|
const ext = path.extname(files[i].name).toLowerCase();
|
|
const tmp = path.join(folderPath, `__tmp_${crypto.randomUUID()}${ext}`);
|
|
try {
|
|
fs.renameSync(src, tmp);
|
|
tmpMap.push({ tmp, ext, idx: i });
|
|
} catch (e) {
|
|
errors.push(`${files[i].name}: ${e.message}`);
|
|
}
|
|
}
|
|
|
|
// 2단계: 최종 이름으로 이동
|
|
const renamed = [];
|
|
for (const { tmp, ext, idx } of tmpMap) {
|
|
const newName = `${prefix}${pad(idx)}${postfix}${ext}`;
|
|
const dest = path.join(folderPath, newName);
|
|
try {
|
|
fs.renameSync(tmp, dest);
|
|
renamed.push({ oldPath: tmp, newPath: dest, newName });
|
|
} catch (e) {
|
|
errors.push(`${newName}: ${e.message}`);
|
|
}
|
|
}
|
|
|
|
return { renamed, errors };
|
|
});
|
|
}
|
|
|
|
module.exports = { register };
|