diff --git a/electron/backend.js b/electron/backend.js index fe125f5..5c699d8 100644 --- a/electron/backend.js +++ b/electron/backend.js @@ -1,5 +1,5 @@ const {app, BrowserWindow, ipcMain, Menu, MenuItem, dialog, session} = require('electron/main') -const {exec} = require('child_process') +const {exec, spawn} = require('child_process') const path = require('path') // const http = require("http"); const fs = require("fs"); @@ -81,6 +81,7 @@ const createWindow = (log) => { app.whenReady().then(() => { let log = { + name: 'app', error: '' } if (!fs.existsSync(process.env.CACHE_PATH)) { @@ -93,15 +94,15 @@ app.whenReady().then(() => { createWindow(log) session.defaultSession.webRequest.onBeforeRequest({urls: ['file://*']}, (details, callback) => { - let reqUrl = details.url - const filePath = decodeURIComponent(reqUrl.slice(8)) - - if (filePath.endsWith('.atlas') && !fs.existsSync(filePath)) { - reqUrl = reqUrl + '.txt' - callback({redirectURL: reqUrl}) - } else { - callback({cancel: false}) + const reqUrl = details.url + if (reqUrl.endsWith('.atlas')) { + const filePath = decodeURIComponent(reqUrl.slice(8)) + if (!fs.existsSync(filePath)) { + callback({redirectURL: reqUrl + '.txt'}) + return + } } + callback({cancel: false}) }) ipcMain.handle('port', () => server.address().port) @@ -151,18 +152,29 @@ app.whenReady().then(() => { ipcMain.handle('save-image-cache', (ev, image) => saveBase64Image(image)) ipcMain.handle('ffmpeg', (ev, options) => { const imagePath = path.join(process.env.CACHE_PATH, filename) - const outputPath = options.path - let instruction; + const inputPath = path.join(imagePath, '%05d.png') + const outputPath = path.join(options.path, options.filename) + const ffmpegArgs = ['-y', '-r', options.framerate.toString(), '-i', inputPath] switch (options.format) { case 'MP4': - instruction = `"${process.env.FFMPEG_PATH}" -y -r ${options.framerate} -i "${path.join(imagePath, '%05d.png')}" -vf "crop=if(mod(iw\\,2)\\,iw-1\\,iw):if(mod(ih\\,2)\\,ih-1\\,ih)" -crf 17 -pix_fmt yuv420p "${path.join(outputPath, options.filename + '.mp4')}"` + ffmpegArgs.push(...['-vf', 'crop=if(mod(iw\\,2)\\,iw-1\\,iw):if(mod(ih\\,2)\\,ih-1\\,ih)', '-crf', '17', '-pix_fmt', 'yuv420p', `${outputPath}.mp4`]) + break + case 'GIF-HQ': + ffmpegArgs.push(...['-vf', 'split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse', `${outputPath}.gif`]) break case 'GIF': default: - instruction = `"${process.env.FFMPEG_PATH}" -y -r ${options.framerate} -i "${path.join(imagePath, '%05d.png')}" "${path.join(outputPath, options.filename + '.gif')}"` + ffmpegArgs.push(`${outputPath}.gif`) break } - exec(instruction, (error, stdout, stderr) => { + const ffmpegProcess = spawn(process.env.FFMPEG_PATH, ffmpegArgs) + ffmpegProcess.stdout.on('data', (data) => { + win.webContents.send('logs-out', {name: 'ffmpeg', stdout: data.toString()}) + }) + ffmpegProcess.stderr.on('data', (data) => { + win.webContents.send('logs-out', {name: 'ffmpeg', stderr: data.toString()}) + }) + ffmpegProcess.on('close', (code) => { fs.readdir(imagePath, (err, files) => { files.forEach(file => { const filePath = path.join(imagePath, file); @@ -172,7 +184,7 @@ app.whenReady().then(() => { } ) win.webContents.send('export-complete') - win.webContents.send('logs-out', {stdout, stderr}) + win.webContents.send('logs-out', {name: 'export', code}) }) }) diff --git a/package.json b/package.json index 09a44eb..2d3697d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "spine-viewer-vue", - "version": "1.0.2", + "version": "1.0.3", "main": "electron/backend.js", "private": true, "scripts": { diff --git a/src/App.vue b/src/App.vue index 049b380..c4e23ff 100644 --- a/src/App.vue +++ b/src/App.vue @@ -25,7 +25,7 @@ import {useAppStore} from "@/stores/app"; import useApp from "@/hooks/useApp"; ipcRenderer.on('logs-out', (_ev, logs) => { - console.log('logs: ', logs) + console.log('[INFO] ', logs) }) // 子组件 @@ -74,12 +74,13 @@ const exportAnimation = () => { standard.index = i standard.duration = info.totalDuration } - c.setAutoUpdate(false) }) let {format, framerate, filename, path} = exportStore.options if (!standard.duration || !path) return + + appStore.containers.forEach(c => c.setAutoUpdate(false)) if (Number.isNaN(framerate) || framerate < 1 || framerate > 60) { framerate = format === 'MP4' ? 30 : 20 } @@ -140,11 +141,13 @@ ipcRenderer.on('export-complete', () => { window.onerror = function (message) { if (message.includes('Texture Error')) { - console.error(message) alert('贴图尺寸与图集不符合!') location.reload() + } else if (message.includes('Region not found')) { + alert('Spine文件错误') + location.reload() } -}; +} function loadFiles(fileUrls) { pixiApp.loader diff --git a/src/components/Export.vue b/src/components/Export.vue index a3040f4..d685ba1 100644 --- a/src/components/Export.vue +++ b/src/components/Export.vue @@ -9,7 +9,7 @@
{ store.options.path = await ipcRenderer.invoke('select-export-path') } +const formats = { + 'GIF': 'GIF', + 'MP4': 'MP4', + 'GIF-HQ': 'GIF (HQ)', +} +