新增 TinyPanda 图片压缩工具项目,基于 Electron 和 Tinify API 构建
This commit is contained in:
205
src/main/index.js
Normal file
205
src/main/index.js
Normal file
@@ -0,0 +1,205 @@
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
|
||||
const path = require('path');
|
||||
const tinify = require('tinify');
|
||||
const fs = require('fs');
|
||||
const JSZip = require('jszip');
|
||||
require('dotenv').config();
|
||||
|
||||
let mainWindow;
|
||||
let apiKey = process.env.TINIFY_API_KEY || '';
|
||||
|
||||
function createWindow() {
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
frame: false,
|
||||
transparent: false,
|
||||
backgroundColor: '#FFFFFF',
|
||||
vibrancy: 'acrylic',
|
||||
resizable: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false
|
||||
},
|
||||
icon: path.join(__dirname, '../../assets/icon.png')
|
||||
});
|
||||
|
||||
mainWindow.loadFile('src/renderer/index.html');
|
||||
|
||||
// 开发模式下打开开发者工具
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
mainWindow.webContents.openDevTools();
|
||||
}
|
||||
}
|
||||
|
||||
app.whenReady().then(createWindow);
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// 获取当前 API Key
|
||||
ipcMain.handle('get-api-key', async () => {
|
||||
return { apiKey: apiKey || '' };
|
||||
});
|
||||
|
||||
// 设置 API Key
|
||||
ipcMain.handle('set-api-key', async (event, key) => {
|
||||
apiKey = key;
|
||||
tinify.key = key;
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
// 验证 API Key
|
||||
ipcMain.handle('validate-api-key', async (event, key) => {
|
||||
try {
|
||||
tinify.key = key;
|
||||
await tinify.fromBuffer(Buffer.from('fake')).toBuffer();
|
||||
return { valid: false };
|
||||
} catch (error) {
|
||||
if (error.message && error.message.includes('Credentials')) {
|
||||
return { valid: false };
|
||||
}
|
||||
// 其他错误说明格式正确但不是真实图片
|
||||
return { valid: true };
|
||||
}
|
||||
});
|
||||
|
||||
// 压缩单张图片
|
||||
ipcMain.handle('compress-image', async (event, filePath) => {
|
||||
try {
|
||||
const source = tinify.fromFile(filePath);
|
||||
const compressed = await source.toBuffer();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
compressedData: compressed.toString('base64')
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 获取图片信息
|
||||
ipcMain.handle('get-image-info', async (event, filePath) => {
|
||||
try {
|
||||
const stats = fs.statSync(filePath);
|
||||
return {
|
||||
size: stats.size,
|
||||
success: true
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 保存压缩后的图片
|
||||
ipcMain.handle('save-compressed-image', async (event, savePath, base64Data) => {
|
||||
try {
|
||||
const buffer = Buffer.from(base64Data, 'base64');
|
||||
fs.writeFileSync(savePath, buffer);
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 创建压缩包
|
||||
ipcMain.handle('create-zip', async (event, files) => {
|
||||
try {
|
||||
const zip = new JSZip();
|
||||
|
||||
for (const file of files) {
|
||||
const buffer = Buffer.from(file.data, 'base64');
|
||||
zip.file(file.name, buffer);
|
||||
}
|
||||
|
||||
const content = await zip.generateAsync({ type: 'base64' });
|
||||
|
||||
return {
|
||||
success: true,
|
||||
zipData: content
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 选择保存目录
|
||||
ipcMain.handle('select-save-directory', async () => {
|
||||
const result = await dialog.showOpenDialog({
|
||||
properties: ['openDirectory']
|
||||
});
|
||||
|
||||
if (result.canceled) {
|
||||
return { canceled: true };
|
||||
}
|
||||
|
||||
return { canceled: false, path: result.filePaths[0] };
|
||||
});
|
||||
|
||||
// 选择文件
|
||||
ipcMain.handle('select-files', async () => {
|
||||
const result = await dialog.showOpenDialog({
|
||||
properties: ['openFile', 'multiSelections'],
|
||||
filters: [
|
||||
{ name: 'Images', extensions: ['jpg', 'jpeg', 'png', 'webp'] }
|
||||
]
|
||||
});
|
||||
|
||||
if (result.canceled) {
|
||||
return { canceled: true };
|
||||
}
|
||||
|
||||
return { canceled: false, files: result.filePaths };
|
||||
});
|
||||
|
||||
// 窗口控制
|
||||
ipcMain.handle('window-minimize', async () => {
|
||||
if (mainWindow) {
|
||||
mainWindow.minimize();
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('window-maximize', async () => {
|
||||
if (mainWindow) {
|
||||
if (mainWindow.isMaximized()) {
|
||||
mainWindow.unmaximize();
|
||||
} else {
|
||||
mainWindow.maximize();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('window-close', async () => {
|
||||
if (mainWindow) {
|
||||
mainWindow.close();
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('window-is-maximized', async () => {
|
||||
if (mainWindow) {
|
||||
return mainWindow.isMaximized();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
Reference in New Issue
Block a user