mirror of
https://github.com/LinoSchmidt/StickExporterTX.git
synced 2026-03-21 01:51:15 +01:00
Slightly better ui, added react and typescript
This commit is contained in:
50
src/components/logger.ts
Normal file
50
src/components/logger.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import electronLog from 'electron-log';
|
||||
import {dataPath} from './paths';
|
||||
import {dialog} from '@electron/remote';
|
||||
import path from 'path';
|
||||
import {exec} from "child_process";
|
||||
|
||||
electronLog.transports.console.format = "{h}:{i}:{s} {text}";
|
||||
electronLog.transports.file.getFile();
|
||||
electronLog.transports.file.resolvePath = () => path.join(dataPath, "logs", "main.log");
|
||||
|
||||
const logger = {
|
||||
info: function (message:string) {
|
||||
electronLog.info(message);
|
||||
},
|
||||
error: function (message:string) {
|
||||
electronLog.error(message);
|
||||
},
|
||||
warning: function (message:string) {
|
||||
electronLog.warn(message);
|
||||
},
|
||||
errorMSG: function (message:string) {
|
||||
logger.error(message);
|
||||
|
||||
dialog.showMessageBox({
|
||||
type: 'error',
|
||||
buttons: ['Open Log', 'OK'],
|
||||
defaultId: 1,
|
||||
title: 'Something went wrong!',
|
||||
message: 'An error has occurred:',
|
||||
detail: message
|
||||
}).then(res => {
|
||||
if(res.response === 0) {
|
||||
exec('start "" "' + path.join(dataPath, "logs") + '"');
|
||||
}
|
||||
});
|
||||
},
|
||||
warningMSG: function (message:string) {
|
||||
logger.warning(message);
|
||||
|
||||
dialog.showMessageBox({
|
||||
type: 'warning',
|
||||
buttons: ['OK'],
|
||||
defaultId: 1,
|
||||
title: 'Warning!',
|
||||
message: message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default logger;
|
||||
9
src/components/paths.ts
Normal file
9
src/components/paths.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import path from 'path';
|
||||
import {app} from '@electron/remote';
|
||||
|
||||
export const dataPath = app.getPath('userData');
|
||||
export const SettingPath = path.join(dataPath, "settings.xml");
|
||||
|
||||
export const blenderPath = path.join("assets", "blender-win", "blender");
|
||||
export const templatePath = path.join("assets", "template.blend");
|
||||
export const blenderScriptPath = path.join("assets", "blenderScript.py");
|
||||
43
src/components/render.ts
Normal file
43
src/components/render.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import {blenderPath, blenderScriptPath, SettingPath, templatePath} from "./paths";
|
||||
import {exec} from "child_process";
|
||||
import logger from "./logger";
|
||||
import React from "react";
|
||||
|
||||
function Render(setStatusDisplay:React.Dispatch<React.SetStateAction<string>>, setLogNumber:React.Dispatch<React.SetStateAction<string>>) {
|
||||
const blenderCons = exec('"' + blenderPath + '" "' + templatePath + '" --background --python "' + blenderScriptPath + '" -- "' + SettingPath.replaceAll('\\', '/') + '"', {maxBuffer: Infinity});
|
||||
|
||||
let frames = "0";
|
||||
let lastFrame = "0";
|
||||
let lastData = "Initializing...";
|
||||
|
||||
setStatusDisplay(lastData);
|
||||
|
||||
blenderCons.stdout?.on("data", (data:string) => {
|
||||
logger.info(data);
|
||||
|
||||
if(data.startsWith("Frames:")) {
|
||||
frames = data.split(":")[1];
|
||||
} else if(data.startsWith("Fra:")) {
|
||||
lastFrame = data.split(":")[1].split(" ")[0]
|
||||
lastData = "Render Frame " + lastFrame + "/" + frames;
|
||||
} else if(data.startsWith("Finished")) {
|
||||
if(lastFrame == frames) {
|
||||
lastData = "Finished Render Successfully!"
|
||||
} else {
|
||||
logger.errorMSG("Render Failed!");
|
||||
}
|
||||
} else if(data.includes("Blender quit")) {
|
||||
if(lastData != "Finished Render Successfully!") {
|
||||
logger.errorMSG("Render Failed!");
|
||||
}
|
||||
} else if(data.startsWith("Init:")) {
|
||||
lastData = "Initialize Frame " + data.split(":")[1] + "/" + frames;
|
||||
} else if(data.startsWith("Lognr:")) {
|
||||
setLogNumber(data.split(":")[1]);
|
||||
}
|
||||
|
||||
setStatusDisplay(lastData);
|
||||
});
|
||||
}
|
||||
|
||||
export default Render;
|
||||
98
src/components/settings.ts
Normal file
98
src/components/settings.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import formatXML from "xml-formatter";
|
||||
import {SettingPath} from './paths';
|
||||
import fs from "fs";
|
||||
import logger from "./logger";
|
||||
|
||||
function getXMLChild(doc:Document, child:string) {
|
||||
return String(doc.getElementsByTagName(child)[0].childNodes[0].nodeValue);
|
||||
}
|
||||
|
||||
const settingList = await fetch(SettingPath).then(function(response){
|
||||
return response.text();
|
||||
}).then(function(data){
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(data, 'text/xml');
|
||||
|
||||
return {
|
||||
fps: parseInt(getXMLChild(xmlDoc, "fps")),
|
||||
width: parseInt(getXMLChild(xmlDoc, "width")),
|
||||
stickDistance: parseInt(getXMLChild(xmlDoc, "stickDistance")),
|
||||
stickMode2: (getXMLChild(xmlDoc, "stickMode2") === "true"),
|
||||
log: getXMLChild(xmlDoc, "log"),
|
||||
output: getXMLChild(xmlDoc, "output")
|
||||
}
|
||||
|
||||
}).catch(function(error) {
|
||||
logger.errorMSG(error);
|
||||
|
||||
return {
|
||||
fps: 30,
|
||||
width: 540,
|
||||
stickDistance: 5,
|
||||
stickMode2: true,
|
||||
log: '"None"',
|
||||
output: "None"
|
||||
}
|
||||
});
|
||||
|
||||
function settingListLoadDefault() {
|
||||
updateSettings({
|
||||
fps: 30,
|
||||
width: 540,
|
||||
stickDistance: 5,
|
||||
stickMode2: true
|
||||
});
|
||||
}
|
||||
|
||||
function updateSettings(optiones:{fps?:number, width?:number, stickDistance?:number, stickMode2?:boolean, log?:string, output?:string}) {
|
||||
if(optiones.fps === undefined) {
|
||||
optiones.fps = settingList.fps;
|
||||
} else {
|
||||
settingList.fps = optiones.fps;
|
||||
}
|
||||
if(optiones.width === undefined) {
|
||||
optiones.width = settingList.width;
|
||||
} else {
|
||||
settingList.width = optiones.width;
|
||||
}
|
||||
if(optiones.stickDistance === undefined) {
|
||||
optiones.stickDistance = settingList.stickDistance;
|
||||
} else {
|
||||
settingList.stickDistance = optiones.stickDistance;
|
||||
}
|
||||
if(optiones.stickMode2 === undefined) {
|
||||
optiones.stickMode2 = settingList.stickMode2;
|
||||
} else {
|
||||
settingList.stickMode2 = optiones.stickMode2;
|
||||
}
|
||||
if(optiones.log === undefined) {
|
||||
optiones.log = settingList.log;
|
||||
} else {
|
||||
settingList.log = optiones.log;
|
||||
}
|
||||
if(optiones.output === undefined) {
|
||||
optiones.output = settingList.output;
|
||||
} else {
|
||||
settingList.output = optiones.output;
|
||||
}
|
||||
|
||||
const xmlStr = '<?xml version="1.0"?><settings><fps>' + optiones.fps +
|
||||
'</fps><width>' + optiones.width +
|
||||
'</width><stickDistance>' + optiones.stickDistance +
|
||||
'</stickDistance><stickMode2>' + ((optiones.stickMode2)?"true":"false") +
|
||||
'</stickMode2><log>' + optiones.log +
|
||||
'</log><output>' + optiones.output +
|
||||
'</output></settings>';
|
||||
|
||||
fs.writeFile(SettingPath, formatXML(xmlStr, {collapseContent: true}), function(err) {
|
||||
if(err) {
|
||||
logger.errorMSG(String(err));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
updateSettings,
|
||||
settingListLoadDefault,
|
||||
settingList
|
||||
}
|
||||
98
src/components/ui/mainSide.tsx
Normal file
98
src/components/ui/mainSide.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import React, {useState, useEffect} from "react";
|
||||
import { dialog } from "@electron/remote";
|
||||
import { settingList, updateSettings } from "../settings";
|
||||
import logger from "../logger";
|
||||
import {exec} from "child_process";
|
||||
import Render from "../render";
|
||||
|
||||
function MainSide() {
|
||||
const [status, setStatus] = useState("Idle");
|
||||
const [logNumber, setLogNumber] = useState("0");
|
||||
const [logs, setLogs] = useState(settingList.log);
|
||||
const [output, setOutput] = useState(settingList.output);
|
||||
const [logTable, setLogTable] = useState(logs.substring(1).slice(0, -1).split('""').map((log, index) => {
|
||||
return <tr key={index}>
|
||||
<td>{log}</td>
|
||||
</tr>
|
||||
}));
|
||||
useEffect(() => {
|
||||
setLogTable(logs.substring(1).slice(0, -1).split('""').map((log, index) => {
|
||||
return <tr key={index}>
|
||||
<td>{log}</td>
|
||||
</tr>
|
||||
}));
|
||||
}, [logs]);
|
||||
|
||||
return (
|
||||
<div id="content">
|
||||
<button onClick={() => Render(setStatus, setLogNumber)}>Start Render</button>
|
||||
<p>{"Log " + logNumber + "/" + String(settingList.log.split("\"\"").length)}</p>
|
||||
<div className="dataDiv">
|
||||
<p>{status}</p>
|
||||
<button onClick={() => openOutputFolder()}>Open Output Folder</button>
|
||||
</div>
|
||||
<hr/>
|
||||
<div className="dataDiv">
|
||||
<p>Logs:</p>
|
||||
<table>
|
||||
<tbody>
|
||||
{logTable}
|
||||
</tbody>
|
||||
</table>
|
||||
<button onClick={() => openLog(setLogs)}>Open Log</button>
|
||||
</div>
|
||||
<div className="dataDiv">
|
||||
<p id="output">{"Output Folder: " + output}</p>
|
||||
<button onClick={() => openVid(setOutput)}>Open Video</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function openLog(updateHook:React.Dispatch<React.SetStateAction<string>>) {
|
||||
dialog.showOpenDialog({
|
||||
properties: [
|
||||
"multiSelections"
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: "TX-Logs",
|
||||
extensions: [
|
||||
"csv"
|
||||
]
|
||||
}
|
||||
]
|
||||
}).then(result => {
|
||||
let logStr = "";
|
||||
result.filePaths.forEach(value => {
|
||||
logStr += "\"" + String(value) + "\"";
|
||||
});
|
||||
updateSettings({log:logStr});
|
||||
updateHook(logStr);
|
||||
}).catch(err => {
|
||||
logger.errorMSG(err);
|
||||
});
|
||||
}
|
||||
|
||||
function openVid(updateHook:React.Dispatch<React.SetStateAction<string>>) {
|
||||
dialog.showOpenDialog({
|
||||
properties: [
|
||||
"openDirectory"
|
||||
]
|
||||
}).then(result => {
|
||||
updateSettings({output:String(result.filePaths)});
|
||||
updateHook(String(result.filePaths));
|
||||
}).catch(err => {
|
||||
logger.errorMSG(err);
|
||||
});
|
||||
}
|
||||
|
||||
function openOutputFolder() {
|
||||
if(settingList.output == "None") {
|
||||
logger.warningMSG("No output folder set!");
|
||||
} else {
|
||||
exec('start "" "' + settingList.output + '"');
|
||||
}
|
||||
}
|
||||
|
||||
export default MainSide;
|
||||
36
src/components/ui/menu.tsx
Normal file
36
src/components/ui/menu.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import React from "react";
|
||||
import { openSide, Side } from "../../renderer";
|
||||
|
||||
const UpdateButton = () => (
|
||||
<div id="update-available">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
{/* Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. */}
|
||||
<path
|
||||
d="M480 352h-133.5l-45.25 45.25C289.2 409.3 273.1 416 256 416s-33.16-6.656-45.25-18.75L165.5 352H32c-17.67 0-32 14.33-32 32v96c0 17.67 14.33 32 32 32h448c17.67 0 32-14.33 32-32v-96C512 366.3 497.7 352 480 352zM432 456c-13.2 0-24-10.8-24-24c0-13.2 10.8-24 24-24s24 10.8 24 24C456 445.2 445.2 456 432 456zM233.4 374.6C239.6 380.9 247.8 384 256 384s16.38-3.125 22.62-9.375l128-128c12.49-12.5 12.49-32.75 0-45.25c-12.5-12.5-32.76-12.5-45.25 0L288 274.8V32c0-17.67-14.33-32-32-32C238.3 0 224 14.33 224 32v242.8L150.6 201.4c-12.49-12.5-32.75-12.5-45.25 0c-12.49 12.5-12.49 32.75 0 45.25L233.4 374.6z" />
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
|
||||
const MainSideButtons = () => (
|
||||
<div id="settings-button" onClick={() => openSide(Side.Settings)}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
{/* Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. */}
|
||||
<path
|
||||
d="M495.9 166.6C499.2 175.2 496.4 184.9 489.6 191.2L446.3 230.6C447.4 238.9 448 247.4 448 256C448 264.6 447.4 273.1 446.3 281.4L489.6 320.8C496.4 327.1 499.2 336.8 495.9 345.4C491.5 357.3 486.2 368.8 480.2 379.7L475.5 387.8C468.9 398.8 461.5 409.2 453.4 419.1C447.4 426.2 437.7 428.7 428.9 425.9L373.2 408.1C359.8 418.4 344.1 427 329.2 433.6L316.7 490.7C314.7 499.7 307.7 506.1 298.5 508.5C284.7 510.8 270.5 512 255.1 512C241.5 512 227.3 510.8 213.5 508.5C204.3 506.1 197.3 499.7 195.3 490.7L182.8 433.6C167 427 152.2 418.4 138.8 408.1L83.14 425.9C74.3 428.7 64.55 426.2 58.63 419.1C50.52 409.2 43.12 398.8 36.52 387.8L31.84 379.7C25.77 368.8 20.49 357.3 16.06 345.4C12.82 336.8 15.55 327.1 22.41 320.8L65.67 281.4C64.57 273.1 64 264.6 64 256C64 247.4 64.57 238.9 65.67 230.6L22.41 191.2C15.55 184.9 12.82 175.3 16.06 166.6C20.49 154.7 25.78 143.2 31.84 132.3L36.51 124.2C43.12 113.2 50.52 102.8 58.63 92.95C64.55 85.8 74.3 83.32 83.14 86.14L138.8 103.9C152.2 93.56 167 84.96 182.8 78.43L195.3 21.33C197.3 12.25 204.3 5.04 213.5 3.51C227.3 1.201 241.5 0 256 0C270.5 0 284.7 1.201 298.5 3.51C307.7 5.04 314.7 12.25 316.7 21.33L329.2 78.43C344.1 84.96 359.8 93.56 373.2 103.9L428.9 86.14C437.7 83.32 447.4 85.8 453.4 92.95C461.5 102.8 468.9 113.2 475.5 124.2L480.2 132.3C486.2 143.2 491.5 154.7 495.9 166.6V166.6zM256 336C300.2 336 336 300.2 336 255.1C336 211.8 300.2 175.1 256 175.1C211.8 175.1 176 211.8 176 255.1C176 300.2 211.8 336 256 336z" />
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
|
||||
const OtherSideButtons = () => (
|
||||
<button id="settings-back" onClick={() => openSide(Side.Main)}>Back</button>
|
||||
)
|
||||
|
||||
const Menu = ({updateAvailable, side}:{updateAvailable:boolean, side:Side}) => (
|
||||
<header>
|
||||
<h1 id="main-headline">{(side == Side.Main)? "StickExporterTX" : "Settings"}</h1>
|
||||
{updateAvailable? <UpdateButton/> : null}
|
||||
{(side == Side.Main)? <MainSideButtons/> : <OtherSideButtons/>}
|
||||
</header>
|
||||
)
|
||||
|
||||
export default Menu;
|
||||
58
src/components/ui/settingsSide.tsx
Normal file
58
src/components/ui/settingsSide.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import React, {useState} from "react";
|
||||
import { settingList, updateSettings, settingListLoadDefault } from "../settings";
|
||||
|
||||
function SettingsSide() {
|
||||
|
||||
const [fps, setFps] = useState(settingList.fps);
|
||||
const [width, setWidth] = useState(settingList.width);
|
||||
const [stickDistance, setStickDistance] = useState(settingList.stickDistance);
|
||||
const [stickMode2, setStickMode2] = useState(settingList.stickMode2);
|
||||
|
||||
return (
|
||||
<div id="content">
|
||||
<div className="dataDiv">
|
||||
<p>FPS: </p>
|
||||
<input id="fpsInput" type="number" value={fps.toString()} min="1" step="1" onChange={e => {
|
||||
updateSettings({fps:parseInt(e.target.value)});
|
||||
setFps(settingList.fps);
|
||||
}}/>
|
||||
</div>
|
||||
<div className="dataDiv">
|
||||
<p>Width: </p>
|
||||
<input id="widthInput" type="number" value={width.toString()} min="1" step="1" onChange={e => {
|
||||
updateSettings({width:parseInt(e.target.value)});
|
||||
setWidth(settingList.width);
|
||||
}}/>
|
||||
</div>
|
||||
<div className="dataDiv">
|
||||
<p>Stick Distance: </p>
|
||||
<input id="stickDistanceInput" type="number" value={stickDistance.toString()} min="0" step="1" onChange={e => {
|
||||
updateSettings({stickDistance:parseInt(e.target.value)});
|
||||
setStickDistance(settingList.stickDistance);
|
||||
}}/>
|
||||
</div>
|
||||
<div className="dataDiv">
|
||||
<p>Stick Mode:</p>
|
||||
<label htmlFor="stickMode" className="toggle-switchy" data-style="rounded" data-text="12">
|
||||
<input checked={stickMode2} type="checkbox" id="stickMode" onChange={e => {
|
||||
updateSettings({stickMode2:e.target.checked});
|
||||
setStickMode2(settingList.stickMode2);
|
||||
}}/>
|
||||
<span className="toggle">
|
||||
<span className="switch"></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<button onClick={() => {
|
||||
settingListLoadDefault();
|
||||
|
||||
setFps(settingList.fps);
|
||||
setWidth(settingList.width);
|
||||
setStickDistance(settingList.stickDistance);
|
||||
setStickMode2(settingList.stickMode2);
|
||||
}}>Reset Settings</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SettingsSide;
|
||||
@@ -1,14 +0,0 @@
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background-color: #172336;
|
||||
color: white;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.dataDiv {
|
||||
display: flex;
|
||||
}
|
||||
.dataDiv p {
|
||||
margin-right: 10px;
|
||||
}
|
||||
104
src/index.css
Normal file
104
src/index.css
Normal file
@@ -0,0 +1,104 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #172336;
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.dataDiv {
|
||||
display: flex;
|
||||
}
|
||||
.dataDiv p {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #9DA8B9;
|
||||
border-radius: 5px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: #0d131e;
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding: 20px 25px;
|
||||
background-color: #0d131e;
|
||||
height: 20px;
|
||||
color: #9DA8B9;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
header h1 {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#settings-back {
|
||||
cursor: pointer;
|
||||
padding: 10px;
|
||||
background-color: #2196F3;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
display: flex;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
#settings-back:hover {
|
||||
transform: scale(1.05);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#content {
|
||||
padding-left: 25px;
|
||||
padding-right: 25px;
|
||||
}
|
||||
|
||||
#settings-button {
|
||||
cursor: pointer;
|
||||
background-color: #2196F3;
|
||||
border-radius: 50%;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
align-items: center;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
#settings-button svg {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
fill: white;
|
||||
margin-left: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
#settings-button:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
#update-available {
|
||||
cursor: pointer;
|
||||
background-color: #21f3ad;
|
||||
border-radius: 20%;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
align-items: center;
|
||||
border: none;
|
||||
outline: none;
|
||||
margin-right: 15px;
|
||||
display: none;
|
||||
}
|
||||
#update-available svg {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
fill: white;
|
||||
margin-left: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
#update-available:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
@@ -1,50 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
|
||||
<meta charset="UTF-8" />
|
||||
<title>StickExporterTX</title>
|
||||
|
||||
<link rel="stylesheet" href="./css/style.css"/>
|
||||
<link rel="stylesheet" href="./css/toggle-switchy.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<button onclick="startRender()">Start Render</button>
|
||||
<p id="logNumber">Log 0/0</p>
|
||||
<div class="dataDiv">
|
||||
<p id="status">Idle</p>
|
||||
<button onclick="openOutputFolder()">Open Output Folder</button>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="dataDiv">
|
||||
<p>FPS: </p>
|
||||
<input id="fpsInput" type="number" value="25" min="1" step="1" onchange="setFPS();">
|
||||
</div>
|
||||
<div class="dataDiv">
|
||||
<p>Width: </p>
|
||||
<input id="widthInput" type="number" value="540" min="1" step="1" onchange="setWidth();">
|
||||
</div>
|
||||
<div class="dataDiv">
|
||||
<p>Stick Distance: </p>
|
||||
<input id="stickDistanceInput" type="number" value="540" min="0" step="1" onchange="setStickDistance();">
|
||||
</div>
|
||||
<div class="dataDiv">
|
||||
<p>Stick Mode:</p>
|
||||
<label for="stickMode" class="toggle-switchy" data-style="rounded" data-text="12">
|
||||
<input checked type="checkbox" id="stickMode" onchange="setStickMode();">
|
||||
<span class="toggle">
|
||||
<span class="switch"></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="dataDiv">
|
||||
<p id="log">Logs:<br/></p>
|
||||
<button onclick="openLog();">Open Log</button>
|
||||
</div>
|
||||
<div class="dataDiv">
|
||||
<p id="output">Output Folder: </p>
|
||||
<button onclick="openVid();">Open Video</button>
|
||||
</div>
|
||||
<script src="./js/render.js"></script>
|
||||
<body style="background-color: #172336;margin:0;padding:0;color:white;font-family:sans-serif;">
|
||||
<div id="root"></div>
|
||||
|
||||
<script src="./.webpack/renderer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,5 +1,5 @@
|
||||
const { app, BrowserWindow } = require('electron');
|
||||
const path = require('path');
|
||||
import {app, BrowserWindow} from 'electron';
|
||||
import {initialize as remoteInitialize, enable as remoteEnable} from '@electron/remote/main';
|
||||
|
||||
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
|
||||
if (require('electron-squirrel-startup')) {
|
||||
@@ -14,21 +14,20 @@ const createWindow = () => {
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
enableRemoteModule: true
|
||||
contextIsolation: false
|
||||
}
|
||||
});
|
||||
|
||||
mainWindow.setMenu(null);
|
||||
if(app.isPackaged) mainWindow.setMenu(null);
|
||||
|
||||
require('@electron/remote/main').initialize();
|
||||
require("@electron/remote/main").enable(mainWindow.webContents);
|
||||
remoteInitialize();
|
||||
remoteEnable(mainWindow.webContents);
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile(path.join(__dirname, 'index.html'));
|
||||
mainWindow.loadFile("src/index.html");
|
||||
|
||||
// Open the DevTools.
|
||||
// mainWindow.webContents.openDevTools();
|
||||
if(!app.isPackaged) mainWindow.webContents.openDevTools();
|
||||
};
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
@@ -54,4 +53,4 @@ app.on('activate', () => {
|
||||
});
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and import them here.
|
||||
// code. You can also put them in separate files and import them here.
|
||||
205
src/js/render.js
205
src/js/render.js
@@ -1,205 +0,0 @@
|
||||
var lastData = "Initialise..."
|
||||
var frames = "0";
|
||||
var lastFrame = "0";
|
||||
var logCount = 0;
|
||||
|
||||
var fps = 25
|
||||
var width = 540
|
||||
var stickDistance = 5
|
||||
var stickMode2 = true
|
||||
var log = '"None"'
|
||||
var output = "None"
|
||||
|
||||
const statusDisplay = document.getElementById("status");
|
||||
const fpsDisplay = document.getElementById("fpsInput");
|
||||
const widthDistplay = document.getElementById("widthInput");
|
||||
const stickDistanceDisplay = document.getElementById("stickDistanceInput");
|
||||
const stickModeDisplay = document.getElementById("stickMode");
|
||||
const logDisplay = document.getElementById("log");
|
||||
const outputDisplay = document.getElementById("output");
|
||||
const logNumberDisplay = document.getElementById("logNumber");
|
||||
|
||||
const logger = require('electron-log');
|
||||
const fs = require("fs");
|
||||
const formatXml = require("xml-formatter");
|
||||
const {dialog, app} = require("@electron/remote");
|
||||
const path = require('path');
|
||||
|
||||
const dataFolder = app.getPath('userData');
|
||||
const SettingFolder = path.join(dataFolder, "settings.xml");
|
||||
|
||||
const blenderPath = path.join("assets", "blender", "blender");
|
||||
const templatePath = path.join("assets", "template.blend");
|
||||
const blenderScriptPath = path.join("assets", "blenderScript.py");
|
||||
|
||||
logger.transports.console.format = "{h}:{i}:{s} {text}";
|
||||
logger.transports.file.getFile();
|
||||
logger.transports.file.resolvePath = () => path.join(dataFolder, "logs", "main.log");
|
||||
|
||||
function startRender() {
|
||||
const {exec} = require("child_process");
|
||||
var blenderCons = exec('"' + blenderPath + '" "' + templatePath + '" --background --python "' + blenderScriptPath + '" -- "' + SettingFolder.replaceAll('\\', '/') + '"', {maxBuffer: Infinity});
|
||||
|
||||
frames = "0";
|
||||
lastFrame = "0";
|
||||
statusDisplay.innerHTML = lastData = "Initialise...";
|
||||
statusDisplay.style.color = "white";
|
||||
logNumberDisplay.innerHTML = "Log 0/" + String(logCount);
|
||||
blenderCons.stdout.on("data", (data) => {
|
||||
var dataStr = String(data);
|
||||
logger.info(dataStr);
|
||||
|
||||
if(dataStr.startsWith("Frames:")) {
|
||||
frames = dataStr.split(":")[1];
|
||||
} else if(dataStr.startsWith("Fra:")) {
|
||||
lastFrame = dataStr.split(":")[1].split(" ")[0]
|
||||
lastData = "Render Frame " + lastFrame + "/" + frames;
|
||||
} else if(dataStr.startsWith("Finished")) {
|
||||
if(lastFrame == frames) {
|
||||
lastData = "Finished Render Successfully!"
|
||||
statusDisplay.style.color = "white";
|
||||
} else {
|
||||
lastData = "Something went wrong! Check Logs."
|
||||
statusDisplay.style.color = "red";
|
||||
}
|
||||
} else if(dataStr.includes("Blender quit")) {
|
||||
if(lastData != "Finished Render Successfully!") {
|
||||
lastData = "Something went wrong! Check Logs.";
|
||||
statusDisplay.style.color = "red";
|
||||
}
|
||||
} else if(dataStr.startsWith("Init:")) {
|
||||
lastData = "Initialize Frame " + dataStr.split(":")[1] + "/" + frames;
|
||||
} else if(dataStr.startsWith("Lognr:")) {
|
||||
logNumberDisplay.innerHTML = "Log " + dataStr.split(":")[1] + "/" + String(logCount);
|
||||
}
|
||||
|
||||
if(statusDisplay.innerHTML != lastData) {
|
||||
statusDisplay.innerHTML = lastData;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getXMLChild(doc, child) {
|
||||
return String(doc.getElementsByTagName(child)[0].childNodes[0].nodeValue);
|
||||
}
|
||||
|
||||
function updateSettingDisplay() {
|
||||
fpsDisplay.value = String(fps);
|
||||
widthDistplay.value = String(width);
|
||||
stickDistanceDisplay.value = String(stickDistance);
|
||||
stickModeDisplay.checked = stickMode2;
|
||||
logDisplay.innerHTML = "Logs:<br/>" + log.substring(1).slice(0, -1).replaceAll("\"\"", "<br/>");
|
||||
outputDisplay.innerHTML = "Output Folder: " + output;
|
||||
logCount = log.split("\"\"").length;
|
||||
logNumberDisplay.innerHTML = "Log 0/" + String(logCount);
|
||||
}
|
||||
|
||||
function updateSettings() {
|
||||
var xmlStr = '<?xml version="1.0"?><settings><fps>' + String(fps) +
|
||||
'</fps><width>' + String(width) +
|
||||
'</width><stickDistance>' + String(stickDistance) +
|
||||
'</stickDistance><stickMode2>' + ((stickMode2)?"true":"false") +
|
||||
'</stickMode2><log>' + log +
|
||||
'</log><output>' + output +
|
||||
'</output></settings>';
|
||||
|
||||
fs.writeFile(SettingFolder, formatXml(xmlStr, {collapseContent: true}), function(err) {
|
||||
if(err) {
|
||||
statusDisplay.innerHTML = "Couldn't write Log! Check Logs.";
|
||||
statusDisplay.style.color = "red";
|
||||
logger.error(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fetch(SettingFolder).then(function(response){
|
||||
return response.text();
|
||||
}).then(function(data){
|
||||
let parser = new DOMParser();
|
||||
let xmlDoc = parser.parseFromString(data, 'text/xml');
|
||||
|
||||
fps = parseInt(getXMLChild(xmlDoc, "fps"));
|
||||
width = parseInt(getXMLChild(xmlDoc, "width"));
|
||||
stickDistance = parseInt(getXMLChild(xmlDoc, "stickDistance"));
|
||||
if(getXMLChild(xmlDoc, "stickMode2") == "false") {
|
||||
stickMode2 = false;
|
||||
} else {
|
||||
stickMode2 = true;
|
||||
}
|
||||
log = getXMLChild(xmlDoc, "log");
|
||||
output = getXMLChild(xmlDoc, "output");
|
||||
|
||||
updateSettingDisplay();
|
||||
}).catch(function(error) {
|
||||
logger.error(error);
|
||||
updateSettingDisplay();
|
||||
updateSettings();
|
||||
});
|
||||
|
||||
function openLog() {
|
||||
dialog.showOpenDialog({
|
||||
properties: [
|
||||
"multiSelections"
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: "TX-Logs",
|
||||
extensions: [
|
||||
"csv"
|
||||
]
|
||||
}
|
||||
]
|
||||
}).then(result => {
|
||||
logStr = "";
|
||||
result.filePaths.forEach(value => {
|
||||
logStr += "\"" + String(value) + "\"";
|
||||
});
|
||||
log = logStr;
|
||||
updateSettingDisplay();
|
||||
updateSettings();
|
||||
}).catch(err => {
|
||||
statusDisplay.innerHTML = "Something went wrong! Check Logs.";
|
||||
statusDisplay.style.color = "red";
|
||||
logger.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
function openVid() {
|
||||
dialog.showOpenDialog({
|
||||
properties: [
|
||||
"openDirectory"
|
||||
]
|
||||
}).then(result => {
|
||||
output = String(result.filePaths);
|
||||
updateSettingDisplay();
|
||||
updateSettings();
|
||||
}).catch(err => {
|
||||
statusDisplay.innerHTML = "Something went wrong! Check Logs.";
|
||||
statusDisplay.style.color = "red";
|
||||
logger.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
function setFPS() {
|
||||
fps = parseInt(fpsDisplay.value);
|
||||
updateSettings();
|
||||
}
|
||||
|
||||
function setWidth() {
|
||||
width = parseInt(widthDistplay.value);
|
||||
updateSettings();
|
||||
}
|
||||
|
||||
function setStickDistance() {
|
||||
stickDistance = parseInt(stickDistanceDisplay.value);
|
||||
updateSettings();
|
||||
}
|
||||
|
||||
function setStickMode() {
|
||||
stickMode2 = stickModeDisplay.checked;
|
||||
updateSettings();
|
||||
}
|
||||
|
||||
function openOutputFolder() {
|
||||
require("child_process").exec('start "" "' + output + '"');
|
||||
}
|
||||
30
src/renderer.tsx
Normal file
30
src/renderer.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import Menu from "./components/ui/menu";
|
||||
import MainSide from "./components/ui/mainSide";
|
||||
import SettingsSite from "./components/ui/settingsSide";
|
||||
import "./index.css";
|
||||
import "./toggle-switchy.css";
|
||||
|
||||
enum Side {
|
||||
Main,
|
||||
Settings
|
||||
}
|
||||
|
||||
function openSide(side:Side) {
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<Menu updateAvailable={true} side={side}/>
|
||||
{(side == Side.Main)? <MainSide/> : <SettingsSite/>}
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root'));
|
||||
}
|
||||
|
||||
openSide(Side.Main);
|
||||
|
||||
|
||||
|
||||
export {
|
||||
openSide,
|
||||
Side,
|
||||
}
|
||||
Reference in New Issue
Block a user