mirror of
https://github.com/LinoSchmidt/StickExporterTX.git
synced 2026-03-21 10:00:47 +01:00
Compare commits
59 Commits
v0.8.1-alp
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6c4ab37f7 | ||
|
|
c095f57f98 | ||
|
|
a28b678719 | ||
|
|
5b1445d507 | ||
|
|
476359e4ce | ||
|
|
7fefef0ea9 | ||
|
|
7b7560743f | ||
|
|
b2a44cec24 | ||
|
|
f9072092b5 | ||
|
|
0e526df376 | ||
|
|
8b1d5ae04f | ||
|
|
b081149759 | ||
|
ac8368adf4
|
|||
|
d90c811e83
|
|||
|
05d195f9d1
|
|||
|
d2d3ed696b
|
|||
|
59e5c4e150
|
|||
|
244d4c94b4
|
|||
|
2e9731e69a
|
|||
|
248f676185
|
|||
|
1cf9ca8541
|
|||
|
b72a44fd3b
|
|||
|
fd1d8e2b51
|
|||
|
|
f24ea21d8c | ||
|
|
365f7d8b34 | ||
|
|
8dff49751b | ||
|
|
2e55a6dc52 | ||
|
|
fb9dcabec2 | ||
|
|
7ac92ffef6 | ||
|
|
db48a8b04d | ||
|
|
6223624ff9 | ||
|
|
119066d649 | ||
|
|
88d56c0197 | ||
|
f496cad846
|
|||
|
|
4dc3bac6bf | ||
|
|
5aa61e4290 | ||
|
|
bb5ca04fd2 | ||
|
|
3a78bfdee3 | ||
|
477fccdb2e
|
|||
|
fac8118b28
|
|||
|
a4d504962d
|
|||
|
c9c3316437
|
|||
|
f2318cccf0
|
|||
|
efeea62def
|
|||
|
367f91fcb4
|
|||
|
c8397308a3
|
|||
|
ce0f03cff7
|
|||
|
13706757ee
|
|||
|
377e26c124
|
|||
|
57a6d7479b
|
|||
|
c768cf225f
|
|||
|
|
f585c81d38 | ||
|
|
b6b94e67b8 | ||
|
|
2938b98ab8 | ||
|
|
3a83fb77ca | ||
|
|
8f32025238 | ||
|
|
2541d5ff9a | ||
|
|
f5d45c3b9a | ||
|
|
a013bced01 |
20
README.md
20
README.md
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
StickExporterTX is a 3D Stick Exporter for EdgeTX/OpenTX logs.
|
StickExporterTX is a 3D Stick Exporter for EdgeTX/OpenTX logs.
|
||||||
|
|
||||||
## Quick Start Guide
|
## Controller Setup Guide
|
||||||
|
|
||||||
To log your sticks on each model, go to `settings -> global functions` in your RC controller.
|
To log your sticks on each model, go to `settings -> global functions` in your RC controller.
|
||||||
|
|
||||||
@@ -18,6 +18,24 @@ If you only want to set up logging for one model, go to `model -> special functi
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## How to use
|
||||||
|
### Importing logs
|
||||||
|
1. Connect your RC controller to your computer via USB.
|
||||||
|
2. Select `USB Storage (SD)` on the controller.
|
||||||
|
3. Open the StickExporterTX app and click on `Add Log(s)`.
|
||||||
|
4. Select the log files you want to import. They should be located in the `LOGS` folder on your controller.
|
||||||
|
|
||||||
|
## How to build (for developers)
|
||||||
|
The following software is required to build the project:
|
||||||
|
- [Node.js](https://nodejs.org/)
|
||||||
|
- [Python 3](https://www.python.org/)
|
||||||
|
|
||||||
|
To build the project, run the following commands in the project directory:
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run bundle
|
||||||
|
```
|
||||||
|
If the commands were successful, a new folder called `dist` should have been created in the project directory containing the compiled files.
|
||||||
## Licence:
|
## Licence:
|
||||||
|
|
||||||
This project is released under the MIT license, for more information, check the [LICENSE](LICENSE) file.
|
This project is released under the MIT license, for more information, check the [LICENSE](LICENSE) file.
|
||||||
73
dependencies/blenderScript.py
vendored
73
dependencies/blenderScript.py
vendored
@@ -5,10 +5,7 @@ import math
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import bpy
|
import bpy
|
||||||
import xml.etree.ElementTree as ET
|
import json
|
||||||
|
|
||||||
argv = sys.argv
|
|
||||||
argv = argv[argv.index("--") + 1:]
|
|
||||||
|
|
||||||
logger = logging.getLogger('simple_example')
|
logger = logging.getLogger('simple_example')
|
||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.INFO)
|
||||||
@@ -43,28 +40,22 @@ def _map(x, in_min, in_max, out_min, out_max):
|
|||||||
logger.info("Blender started successfully!")
|
logger.info("Blender started successfully!")
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
command = input("Waiting for command: ")
|
command = input("Waiting for command: ").split(" -- ")
|
||||||
|
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
|
|
||||||
settingsRoot = ET.parse(argv[0]+"/settings.xml").getroot()
|
settings = json.loads(command[1])
|
||||||
|
|
||||||
StickMode = settingsRoot[3].text
|
stickMode2 = settings["stickMode2"]
|
||||||
if(StickMode == "true"):
|
width = settings["width"]
|
||||||
StickMode = 2
|
stickDistance = _map(settings["stickDistance"], 0, 100, 5, 105)
|
||||||
else:
|
fps = settings["fps"]
|
||||||
StickMode = 1
|
videoFormat = settings["videoFormat"]
|
||||||
|
logs = settings["logs"]
|
||||||
width = int(settingsRoot[1].text)
|
output = settings["output"]
|
||||||
StickDistance = _map(int(settingsRoot[2].text), 0, 100, 5, 105)
|
dataPath = settings["dataPath"]
|
||||||
|
|
||||||
if(command == "startRendering"):
|
|
||||||
|
|
||||||
fps = int(settingsRoot[0].text)
|
|
||||||
videoFormat = settingsRoot[4].text
|
|
||||||
logs = settingsRoot[5].text[1:][:-1].split("\"\"")
|
|
||||||
output = settingsRoot[6].text
|
|
||||||
|
|
||||||
|
if(command[0] == "startRendering"):
|
||||||
logCount = len(logs)
|
logCount = len(logs)
|
||||||
logNumber = 1
|
logNumber = 1
|
||||||
|
|
||||||
@@ -129,14 +120,14 @@ while True:
|
|||||||
bpy.context.scene.render.image_settings.color_mode = 'RGBA'
|
bpy.context.scene.render.image_settings.color_mode = 'RGBA'
|
||||||
|
|
||||||
scn.render.resolution_x = width
|
scn.render.resolution_x = width
|
||||||
GimbalCoverR.location[0] = StickDistance
|
GimbalCoverR.location[0] = stickDistance
|
||||||
GimbalR.location[0] = StickDistance
|
GimbalR.location[0] = stickDistance
|
||||||
TrailR.location[0] = StickDistance
|
TrailR.location[0] = stickDistance
|
||||||
Plane.location[0] = StickDistance
|
Plane.location[0] = stickDistance
|
||||||
Camera.location[0] = StickDistance/2
|
Camera.location[0] = stickDistance/2
|
||||||
Camera.data.ortho_scale = StickDistance+5
|
Camera.data.ortho_scale = stickDistance+5
|
||||||
scn.render.resolution_y = int(width/_map(StickDistance, 5, 105, 2, 21.6))
|
scn.render.resolution_y = int(width/_map(stickDistance, 5, 105, 2, 21.6))
|
||||||
bpy.context.scene.render.filepath = output + "\\" + log.split("/")[-1].split("\\")[-1].replace(".csv", "."+videoFormat)
|
bpy.context.scene.render.filepath = output[logNumber-1]
|
||||||
|
|
||||||
scn.render.fps = 1000
|
scn.render.fps = 1000
|
||||||
scn.render.fps_base = FPSxxx
|
scn.render.fps_base = FPSxxx
|
||||||
@@ -168,7 +159,7 @@ while True:
|
|||||||
StickR.rotation_euler=[0,0,0]
|
StickR.rotation_euler=[0,0,0]
|
||||||
GimbalR.rotation_euler=[0,0,0]
|
GimbalR.rotation_euler=[0,0,0]
|
||||||
|
|
||||||
if StickMode == "1":
|
if stickMode2 == False:
|
||||||
StickL.rotation_euler.rotate_axis("Y", ailP)
|
StickL.rotation_euler.rotate_axis("Y", ailP)
|
||||||
GimbalL.rotation_euler.rotate_axis("X", eleP)
|
GimbalL.rotation_euler.rotate_axis("X", eleP)
|
||||||
StickR.rotation_euler.rotate_axis("Y", rudP)
|
StickR.rotation_euler.rotate_axis("Y", rudP)
|
||||||
@@ -194,19 +185,19 @@ while True:
|
|||||||
|
|
||||||
logNumber+=1
|
logNumber+=1
|
||||||
|
|
||||||
elif(command == "getRender"):
|
elif(command[0] == "getRender"):
|
||||||
|
|
||||||
bpy.context.scene.render.image_settings.file_format = 'PNG'
|
bpy.context.scene.render.image_settings.file_format = 'PNG'
|
||||||
bpy.context.scene.render.filepath = argv[0] + "\\render.png"
|
bpy.context.scene.render.filepath = dataPath + "\\render.png"
|
||||||
|
|
||||||
scn.render.resolution_x = width
|
scn.render.resolution_x = width
|
||||||
GimbalCoverR.location[0] = StickDistance
|
GimbalCoverR.location[0] = stickDistance
|
||||||
GimbalR.location[0] = StickDistance
|
GimbalR.location[0] = stickDistance
|
||||||
TrailR.location[0] = StickDistance
|
TrailR.location[0] = stickDistance
|
||||||
Plane.location[0] = StickDistance
|
Plane.location[0] = stickDistance
|
||||||
Camera.location[0] = StickDistance/2
|
Camera.location[0] = stickDistance/2
|
||||||
Camera.data.ortho_scale = StickDistance+5
|
Camera.data.ortho_scale = stickDistance+5
|
||||||
scn.render.resolution_y = int(width/_map(StickDistance, 5, 105, 2, 21.6))
|
scn.render.resolution_y = int(width/_map(stickDistance, 5, 105, 2, 21.6))
|
||||||
|
|
||||||
bpy.context.scene.frame_set(0)
|
bpy.context.scene.frame_set(0)
|
||||||
|
|
||||||
@@ -215,7 +206,7 @@ while True:
|
|||||||
StickR.rotation_euler=[0,0,0]
|
StickR.rotation_euler=[0,0,0]
|
||||||
GimbalR.rotation_euler=[0,0,0]
|
GimbalR.rotation_euler=[0,0,0]
|
||||||
|
|
||||||
if(StickMode == 2):
|
if(stickMode2 == True):
|
||||||
StickL.rotation_euler.rotate_axis("Y", 0)
|
StickL.rotation_euler.rotate_axis("Y", 0)
|
||||||
GimbalL.rotation_euler.rotate_axis("X", 0.436)
|
GimbalL.rotation_euler.rotate_axis("X", 0.436)
|
||||||
StickR.rotation_euler.rotate_axis("Y", 0)
|
StickR.rotation_euler.rotate_axis("Y", 0)
|
||||||
@@ -238,7 +229,7 @@ while True:
|
|||||||
StickR.rotation_euler=[0,0,0]
|
StickR.rotation_euler=[0,0,0]
|
||||||
GimbalR.rotation_euler=[0,0,0]
|
GimbalR.rotation_euler=[0,0,0]
|
||||||
|
|
||||||
if(StickMode == 2):
|
if(stickMode2 == True):
|
||||||
StickL.rotation_euler.rotate_axis("Y", 0)
|
StickL.rotation_euler.rotate_axis("Y", 0)
|
||||||
GimbalL.rotation_euler.rotate_axis("X", 0.436)
|
GimbalL.rotation_euler.rotate_axis("X", 0.436)
|
||||||
StickR.rotation_euler.rotate_axis("Y", 0)
|
StickR.rotation_euler.rotate_axis("Y", 0)
|
||||||
|
|||||||
4115
package-lock.json
generated
4115
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -134,8 +134,8 @@
|
|||||||
"babel-loader": "^8.2.5",
|
"babel-loader": "^8.2.5",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"css-loader": "^6.7.1",
|
"css-loader": "^6.7.1",
|
||||||
"electron": "18.1.0",
|
"electron": "22.3.25",
|
||||||
"electron-builder": "^23.0.3",
|
"electron-builder": "^24.0.0",
|
||||||
"eslint": "^8.16.0",
|
"eslint": "^8.16.0",
|
||||||
"eslint-plugin-import": "^2.26.0",
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"eslint-plugin-react": "^7.30.0",
|
"eslint-plugin-react": "^7.30.0",
|
||||||
@@ -150,7 +150,7 @@
|
|||||||
"ts-loader": "^9.3.0",
|
"ts-loader": "^9.3.0",
|
||||||
"ts-node": "^10.8.0",
|
"ts-node": "^10.8.0",
|
||||||
"typescript": "^4.7.2",
|
"typescript": "^4.7.2",
|
||||||
"webpack": "^5.72.1",
|
"webpack": "^5.76.0",
|
||||||
"webpack-cli": "^4.9.2",
|
"webpack-cli": "^4.9.2",
|
||||||
"webpack-dev-server": "^4.9.1",
|
"webpack-dev-server": "^4.9.1",
|
||||||
"webpack-merge": "^5.8.0"
|
"webpack-merge": "^5.8.0"
|
||||||
|
|||||||
@@ -34,7 +34,26 @@ if(platform.system() == 'Linux'):
|
|||||||
urllib.request.urlretrieve(linuxURL, './dependencies/linux/blender.tar.xz')
|
urllib.request.urlretrieve(linuxURL, './dependencies/linux/blender.tar.xz')
|
||||||
print("Extracting linux version")
|
print("Extracting linux version")
|
||||||
with tarfile.open('./dependencies/linux/blender.tar.xz') as tfile:
|
with tarfile.open('./dependencies/linux/blender.tar.xz') as tfile:
|
||||||
tfile.extractall('./dependencies/linux')
|
def is_within_directory(directory, target):
|
||||||
|
|
||||||
|
abs_directory = os.path.abspath(directory)
|
||||||
|
abs_target = os.path.abspath(target)
|
||||||
|
|
||||||
|
prefix = os.path.commonprefix([abs_directory, abs_target])
|
||||||
|
|
||||||
|
return prefix == abs_directory
|
||||||
|
|
||||||
|
def safe_extract(tar, path=".", members=None, *, numeric_owner=False):
|
||||||
|
|
||||||
|
for member in tar.getmembers():
|
||||||
|
member_path = os.path.join(path, member.name)
|
||||||
|
if not is_within_directory(path, member_path):
|
||||||
|
raise Exception("Attempted Path Traversal in Tar File")
|
||||||
|
|
||||||
|
tar.extractall(path, members, numeric_owner=numeric_owner)
|
||||||
|
|
||||||
|
|
||||||
|
safe_extract(tfile, "./dependencies/linux")
|
||||||
|
|
||||||
print("Adjust linux version")
|
print("Adjust linux version")
|
||||||
oldLinuxName = linuxURL.split('/')[-1].replace('.tar.xz', '')
|
oldLinuxName = linuxURL.split('/')[-1].replace('.tar.xz', '')
|
||||||
|
|||||||
@@ -1,28 +1,22 @@
|
|||||||
import { blenderPath, blenderScriptPath, dataPath, templatePath, finsishedIconPath } from "./paths";
|
import { blenderPath, blenderScriptPath, templatePath, finsishedIconPath, dataPath } from "./paths";
|
||||||
import {spawn} from "child_process";
|
import {spawn} from "child_process";
|
||||||
import logger from "./logger";
|
import logger from "./logger";
|
||||||
import { setBlenderLoading, setBlenderStatus } from "./ui/menu";
|
import { setBlenderLoading, setBlenderStatus } from "./ui/menu";
|
||||||
import { setLogNumber, setPastTime, setRemainingTime, setRenderDisplayProgress, setStatus, setPastTimeNow, setRemainingTimeNow } from "./ui/renderingPage";
|
import { setLogNumber, setStatus, addTerminalLine } from "./ui/renderingPage";
|
||||||
import {imageLoading, imageLoaded} from "./ui/settingsPage";
|
import {imageLoading, imageLoaded} from "./ui/settingsPage";
|
||||||
import { getLogList, getLogSize, settingList } from "./settings";
|
import { getInOutSettings, getActiveProfile } from "./settings";
|
||||||
import isValid from "is-valid-path";
|
|
||||||
import { pageSetRendering, setProgress, openPage, Page } from "../renderer";
|
import { pageSetRendering, setProgress, openPage, Page } from "../renderer";
|
||||||
|
import { setLog, setRenderProgress, startProgress, stopProgress } from "./progressController";
|
||||||
import { ipcRenderer } from "electron";
|
import { ipcRenderer } from "electron";
|
||||||
|
import path from 'path';
|
||||||
|
import fs from "fs";
|
||||||
// import { getDoNotDisturb } from "electron-notification-state";
|
// import { getDoNotDisturb } from "electron-notification-state";
|
||||||
|
|
||||||
export const renderInfo = {
|
|
||||||
time: "0min 0sec",
|
|
||||||
startTime: 0,
|
|
||||||
endTime: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const blenderStartString = [
|
const blenderStartString = [
|
||||||
templatePath,
|
templatePath,
|
||||||
"--background",
|
"--background",
|
||||||
"--python",
|
"--python",
|
||||||
blenderScriptPath,
|
blenderScriptPath
|
||||||
"--",
|
|
||||||
dataPath.replaceAll("\\", "/")
|
|
||||||
]
|
]
|
||||||
|
|
||||||
let blenderConsole = spawn(blenderPath, blenderStartString).on('error', function(err) {
|
let blenderConsole = spawn(blenderPath, blenderStartString).on('error', function(err) {
|
||||||
@@ -33,41 +27,40 @@ let renderingPicture = false;
|
|||||||
let renderingVideo = false;
|
let renderingVideo = false;
|
||||||
let waitingForRender = false;
|
let waitingForRender = false;
|
||||||
|
|
||||||
let logPortionList:number[] = [];
|
function getOutPath(log:string) {
|
||||||
let currentLogPortion = 0;
|
let fullOutPath = path.join(getInOutSettings().output, log.substring(log.lastIndexOf("\\")).replace(".csv", "."+getActiveProfile().videoFormat));
|
||||||
|
|
||||||
const estimatedRenderPortion = 0.97;
|
if(fs.existsSync(fullOutPath)) {
|
||||||
function setRenderProgress(log:number, init:boolean, frameCount:number, frame:number) {
|
let i = 1;
|
||||||
let progress = 0;
|
while(fs.existsSync(fullOutPath.replace("."+getActiveProfile().videoFormat, " ("+i+")."+getActiveProfile().videoFormat))) {
|
||||||
if(init) {
|
i++;
|
||||||
progress = logPortionList[log-1] * (frame / frameCount * (1 - estimatedRenderPortion)) + currentLogPortion;
|
}
|
||||||
} else {
|
fullOutPath = fullOutPath.replace("."+getActiveProfile().videoFormat, " ("+i+")."+getActiveProfile().videoFormat);
|
||||||
progress = logPortionList[log-1] * (frame / frameCount * estimatedRenderPortion + (1 - estimatedRenderPortion)) + currentLogPortion;
|
|
||||||
}
|
}
|
||||||
setProgress(progress);
|
|
||||||
setRenderDisplayProgress(parseFloat((progress*100).toFixed(2)));
|
|
||||||
|
|
||||||
const timeNow = new Date().getTime();
|
return fullOutPath;
|
||||||
const timeDiff = timeNow - renderInfo.startTime;
|
|
||||||
let timeDiffSeconds = timeDiff / 1000;
|
|
||||||
let timeDiffMinutes = 0;
|
|
||||||
while(timeDiffSeconds > 60) {
|
|
||||||
timeDiffMinutes++;
|
|
||||||
timeDiffSeconds -= 60;
|
|
||||||
}
|
}
|
||||||
renderInfo.time = timeDiffMinutes + "m " + timeDiffSeconds.toFixed(0) + "s";
|
|
||||||
setPastTimeNow(renderInfo.time);
|
|
||||||
|
|
||||||
if(progress > 0) {
|
let outputArgs:string[] = [];
|
||||||
const timeRemaining = (timeDiff / progress) * (1 - progress);
|
|
||||||
timeDiffSeconds = timeRemaining / 1000;
|
function blenderArgs() {
|
||||||
timeDiffMinutes = 0;
|
const outputList:string[] = [];
|
||||||
while(timeDiffSeconds > 60) {
|
getInOutSettings().logs.forEach(log => {
|
||||||
timeDiffMinutes++;
|
outputList.push(getOutPath(log));
|
||||||
timeDiffSeconds -= 60;
|
});
|
||||||
}
|
|
||||||
setRemainingTimeNow(timeDiffMinutes + "m " + timeDiffSeconds.toFixed(0) + "s");
|
outputArgs = outputList;
|
||||||
}
|
|
||||||
|
return JSON.stringify({
|
||||||
|
stickMode2:getActiveProfile().stickMode2,
|
||||||
|
width:getActiveProfile().width,
|
||||||
|
stickDistance:getActiveProfile().stickDistance,
|
||||||
|
fps:getActiveProfile().fps,
|
||||||
|
videoFormat:getActiveProfile().videoFormat,
|
||||||
|
logs:getInOutSettings().logs,
|
||||||
|
output:outputList,
|
||||||
|
dataPath:dataPath
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function startBlender() {
|
function startBlender() {
|
||||||
@@ -80,6 +73,10 @@ function startBlender() {
|
|||||||
|
|
||||||
logger.info("Blender: " + dataStr);
|
logger.info("Blender: " + dataStr);
|
||||||
|
|
||||||
|
if(renderingVideo) {
|
||||||
|
addTerminalLine(dataStr);
|
||||||
|
}
|
||||||
|
|
||||||
if (dataStr.includes("Blender started successfully")) {
|
if (dataStr.includes("Blender started successfully")) {
|
||||||
renderingPicture = false;
|
renderingPicture = false;
|
||||||
renderingVideo = false;
|
renderingVideo = false;
|
||||||
@@ -108,13 +105,13 @@ function startBlender() {
|
|||||||
setRenderProgress(parseInt(log), true, 0, 0);
|
setRenderProgress(parseInt(log), true, 0, 0);
|
||||||
}
|
}
|
||||||
if(dataStr.includes("Fra:") && renderingVideo) {
|
if(dataStr.includes("Fra:") && renderingVideo) {
|
||||||
lastFrame = dataStr.split(":")[1].split(" ")[0];
|
lastFrame = dataStr.split("Fra:")[1].split(" ")[0];
|
||||||
setStatus("Rendering Frame " + lastFrame + "/" + frames);
|
setStatus("Rendering Frame " + lastFrame + "/" + frames);
|
||||||
setRenderProgress(parseInt(log), false, parseInt(frames), parseInt(lastFrame));
|
setRenderProgress(parseInt(log), false, parseInt(frames), parseInt(lastFrame));
|
||||||
}
|
}
|
||||||
if(dataStr.includes("Finished") && renderingVideo) {
|
if(dataStr.includes("Finished") && renderingVideo) {
|
||||||
pageSetRendering(false);
|
pageSetRendering(false);
|
||||||
renderInfo.endTime = new Date().getTime();
|
stopProgress();
|
||||||
if(lastFrame == frames) {
|
if(lastFrame == frames) {
|
||||||
openPage(Page.RenderFinish);
|
openPage(Page.RenderFinish);
|
||||||
ipcRenderer.send("renderFinished");
|
ipcRenderer.send("renderFinished");
|
||||||
@@ -136,7 +133,7 @@ function startBlender() {
|
|||||||
if(dataStr.includes("Lognr:") && renderingVideo) {
|
if(dataStr.includes("Lognr:") && renderingVideo) {
|
||||||
log = dataStr.split(":")[1];
|
log = dataStr.split(":")[1];
|
||||||
if(log !== "1") {
|
if(log !== "1") {
|
||||||
currentLogPortion += logPortionList[parseInt(log)-2];
|
setLog(parseInt(log));
|
||||||
}
|
}
|
||||||
setLogNumber(log);
|
setLogNumber(log);
|
||||||
}
|
}
|
||||||
@@ -158,7 +155,7 @@ function startBlender() {
|
|||||||
} else {
|
} else {
|
||||||
waitingForRender = false;
|
waitingForRender = false;
|
||||||
renderingPicture = true;
|
renderingPicture = true;
|
||||||
blenderConsole.stdin.write("getRender\n");
|
blenderConsole.stdin.write("getRender -- "+blenderArgs()+"\n");
|
||||||
setBlenderStatus("Rendering");
|
setBlenderStatus("Rendering");
|
||||||
setBlenderLoading(true);
|
setBlenderLoading(true);
|
||||||
imageLoading();
|
imageLoading();
|
||||||
@@ -192,44 +189,23 @@ function blender(command:blenderCmd) {
|
|||||||
imageLoading();
|
imageLoading();
|
||||||
setBlenderStatus("Rendering");
|
setBlenderStatus("Rendering");
|
||||||
setBlenderLoading(true);
|
setBlenderLoading(true);
|
||||||
blenderConsole.stdin.write("getRender\n");
|
blenderConsole.stdin.write("getRender -- "+blenderArgs()+"\n");
|
||||||
} else {
|
} else {
|
||||||
waitingForRender = true;
|
waitingForRender = true;
|
||||||
}
|
}
|
||||||
} else if(command === blenderCmd.startRendering) {
|
} else if(command === blenderCmd.startRendering) {
|
||||||
if(readyToAcceptCommand) {
|
if(readyToAcceptCommand) {
|
||||||
if(settingList.log == "") {
|
if(getInOutSettings().logs.length === 0) {
|
||||||
logger.warningMSG("No log selected!");
|
logger.warningMSG("No log selected!");
|
||||||
} else if(!isValid(settingList.log)) {
|
|
||||||
logger.warningMSG("Output path is invalid!");
|
|
||||||
} else {
|
} else {
|
||||||
currentLogPortion = 0;
|
|
||||||
|
|
||||||
const logSizeList:number[] = [];
|
|
||||||
getLogList().forEach(function (value, index) {
|
|
||||||
logSizeList.push(getLogSize(index));
|
|
||||||
});
|
|
||||||
|
|
||||||
let fullLogSize = 0;
|
|
||||||
for(let i = 0; i < logSizeList.length; i++) {
|
|
||||||
fullLogSize += logSizeList[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
logPortionList = [];
|
|
||||||
logSizeList.forEach(function (value) {
|
|
||||||
logPortionList.push(value / fullLogSize);
|
|
||||||
});
|
|
||||||
|
|
||||||
readyToAcceptCommand = false;
|
readyToAcceptCommand = false;
|
||||||
renderingVideo = true;
|
renderingVideo = true;
|
||||||
pageSetRendering(true);
|
pageSetRendering(true);
|
||||||
setBlenderStatus("Rendering");
|
setBlenderStatus("Rendering");
|
||||||
setBlenderLoading(true);
|
setBlenderLoading(true);
|
||||||
blenderConsole.stdin.write("startRendering\n");
|
blenderConsole.stdin.write("startRendering -- "+blenderArgs()+"\n");
|
||||||
|
|
||||||
renderInfo.startTime = new Date().getTime();
|
startProgress();
|
||||||
setPastTime("0min 0sec");
|
|
||||||
setRemainingTime("calculating...");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(command === blenderCmd.stopRendering) {
|
} else if(command === blenderCmd.stopRendering) {
|
||||||
@@ -252,5 +228,6 @@ export {
|
|||||||
blender,
|
blender,
|
||||||
blenderCmd,
|
blenderCmd,
|
||||||
startBlender,
|
startBlender,
|
||||||
renderingPicture
|
renderingPicture,
|
||||||
|
outputArgs
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import logger from "./logger";
|
import logger from "./logger";
|
||||||
import {parse as csvParse} from "csv-parse";
|
import {parse as csvParse} from "csv-parse";
|
||||||
import {settingList} from "./settings";
|
import {getInOutSettings} from "./settings";
|
||||||
import {platformCharacter} from "./paths";
|
import {platformCharacter} from "./paths";
|
||||||
import {formatDate} from "./dateFormat";
|
import {formatDate} from "./dateFormat";
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
async function openLogFile(filePath:string, rawData:boolean) {
|
async function openLogFile(filePath:string, rawData:boolean) {
|
||||||
const data = await fetch(filePath).then(function(response) {
|
const data = await fetch(filePath).then(function(response) {
|
||||||
@@ -20,7 +21,7 @@ async function openLogFile(filePath:string, rawData:boolean) {
|
|||||||
let logData:string[]|undefined = undefined;
|
let logData:string[]|undefined = undefined;
|
||||||
csvParse(data, {}, (err, output:string[]) => {
|
csvParse(data, {}, (err, output:string[]) => {
|
||||||
if(err) {
|
if(err) {
|
||||||
logger.errorMSG(`Error parsing csv file: ${err}`);
|
logger.errorMSG(`Error parsing file "${filePath}": ${err}`);
|
||||||
logData = [];
|
logData = [];
|
||||||
} else {
|
} else {
|
||||||
logData = output;
|
logData = output;
|
||||||
@@ -162,10 +163,8 @@ async function getLogTime(filePath:string) {
|
|||||||
async function getAllLogs() {
|
async function getAllLogs() {
|
||||||
const loadList = [];
|
const loadList = [];
|
||||||
|
|
||||||
if(settingList.log.length > 0) {
|
if(getInOutSettings().logs.length > 0) {
|
||||||
const logs = settingList.log.substring(1).slice(0, -1).split('""');
|
for(const log of getInOutSettings().logs) {
|
||||||
|
|
||||||
for(const log of logs) {
|
|
||||||
loadList.push({
|
loadList.push({
|
||||||
name: log.split(platformCharacter())[log.split(platformCharacter()).length - 1].replace(".csv", ""),
|
name: log.split(platformCharacter())[log.split(platformCharacter()).length - 1].replace(".csv", ""),
|
||||||
path: log,
|
path: log,
|
||||||
@@ -184,9 +183,8 @@ async function reloadAllLogs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function updateLogs() {
|
async function updateLogs() {
|
||||||
if(settingList.log.length > 0) {
|
if(getInOutSettings().logs.length > 0) {
|
||||||
const logs = settingList.log.substring(1).slice(0, -1).split('""');
|
for(const log of getInOutSettings().logs) {
|
||||||
for(const log of logs) {
|
|
||||||
if(!logList.some(x => x.path === log)) {
|
if(!logList.some(x => x.path === log)) {
|
||||||
logList.push({
|
logList.push({
|
||||||
name: log.split(platformCharacter())[log.split(platformCharacter()).length - 1].replace(".csv", ""),
|
name: log.split(platformCharacter())[log.split(platformCharacter()).length - 1].replace(".csv", ""),
|
||||||
@@ -197,7 +195,7 @@ async function updateLogs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(const log of logList) {
|
for(const log of logList) {
|
||||||
if(!logs.some(x => x === log.path)) {
|
if(!getInOutSettings().logs.some(x => x === log.path)) {
|
||||||
logList.splice(logList.indexOf(log), 1);
|
logList.splice(logList.indexOf(log), 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,8 +204,13 @@ async function updateLogs() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLogSize(index:number) {
|
||||||
|
return fs.statSync(getInOutSettings().logs[index]).size;
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
reloadAllLogs,
|
reloadAllLogs,
|
||||||
logList,
|
logList,
|
||||||
updateLogs
|
updateLogs,
|
||||||
|
getLogSize
|
||||||
};
|
};
|
||||||
@@ -4,7 +4,8 @@ import { platformFolder, platform, Platform } from './platform';
|
|||||||
|
|
||||||
export const dataPath = app.getPath('userData');
|
export const dataPath = app.getPath('userData');
|
||||||
export const appPath = app.getAppPath().replace("app.asar", "");
|
export const appPath = app.getAppPath().replace("app.asar", "");
|
||||||
export const SettingPath = path.join(dataPath, "settings.xml");
|
export const SettingPath = path.join(dataPath, "settings.json");
|
||||||
|
export const OLDSettingPath = path.join(dataPath, "settings.xml");
|
||||||
|
|
||||||
export const defaultOutputPath = path.join(app.getPath('videos'), "StickExporterTX");
|
export const defaultOutputPath = path.join(app.getPath('videos'), "StickExporterTX");
|
||||||
|
|
||||||
|
|||||||
92
src/components/progressController.ts
Normal file
92
src/components/progressController.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import {setProgress} from "../renderer";
|
||||||
|
import {getLogSize} from "./logReader";
|
||||||
|
import {getInOutSettings} from "./settings";
|
||||||
|
import { setRenderDisplayProgress, setPastTimeNow, setRemainingTimeNow } from "./ui/renderingPage";
|
||||||
|
|
||||||
|
const estimatedRenderPortion = 0.97;
|
||||||
|
|
||||||
|
const renderInfo = {
|
||||||
|
time: "0min 0sec",
|
||||||
|
startTime: 0,
|
||||||
|
endTime: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
let logPortionList:number[] = [];
|
||||||
|
let currentLogPortion = 0;
|
||||||
|
|
||||||
|
function setRenderProgress(log:number, init:boolean, frameCount:number, frame:number) {
|
||||||
|
let progress = 0;
|
||||||
|
if(init) {
|
||||||
|
progress = logPortionList[log-1] * (frame / frameCount * (1 - estimatedRenderPortion)) + currentLogPortion;
|
||||||
|
} else {
|
||||||
|
progress = logPortionList[log-1] * (frame / frameCount * estimatedRenderPortion + (1 - estimatedRenderPortion)) + currentLogPortion;
|
||||||
|
}
|
||||||
|
setProgress(progress);
|
||||||
|
setRenderDisplayProgress(parseFloat((progress*100).toFixed(2)));
|
||||||
|
|
||||||
|
const timeNow = new Date().getTime();
|
||||||
|
const timeDiff = timeNow - renderInfo.startTime;
|
||||||
|
let timeDiffSeconds = timeDiff / 1000;
|
||||||
|
let timeDiffMinutes = 0;
|
||||||
|
while(timeDiffSeconds > 60) {
|
||||||
|
timeDiffMinutes++;
|
||||||
|
timeDiffSeconds -= 60;
|
||||||
|
}
|
||||||
|
renderInfo.time = timeDiffMinutes + "m " + timeDiffSeconds.toFixed(0) + "s";
|
||||||
|
setPastTimeNow(renderInfo.time);
|
||||||
|
|
||||||
|
if(progress > 0) {
|
||||||
|
const timeRemaining = (timeDiff / progress) * (1 - progress);
|
||||||
|
timeDiffSeconds = timeRemaining / 1000;
|
||||||
|
timeDiffMinutes = 0;
|
||||||
|
while(timeDiffSeconds > 60) {
|
||||||
|
timeDiffMinutes++;
|
||||||
|
timeDiffSeconds -= 60;
|
||||||
|
}
|
||||||
|
setRemainingTimeNow(timeDiffMinutes + "m " + timeDiffSeconds.toFixed(0) + "s");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetRenderProgress() {
|
||||||
|
logPortionList = [];
|
||||||
|
currentLogPortion = 0;
|
||||||
|
|
||||||
|
const logSizeList:number[] = [];
|
||||||
|
getInOutSettings().logs.forEach(function (value, index) {
|
||||||
|
logSizeList.push(getLogSize(index));
|
||||||
|
});
|
||||||
|
|
||||||
|
let fullLogSize = 0;
|
||||||
|
for(let i = 0; i < logSizeList.length; i++) {
|
||||||
|
fullLogSize += logSizeList[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
logSizeList.forEach(function (value) {
|
||||||
|
logPortionList.push(value / fullLogSize);
|
||||||
|
});
|
||||||
|
|
||||||
|
setPastTimeNow("0min 0sec");
|
||||||
|
setRemainingTimeNow("calculating...");
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLog(log:number) {
|
||||||
|
currentLogPortion += logPortionList[log-2];
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopProgress() {
|
||||||
|
renderInfo.endTime = new Date().getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
function startProgress() {
|
||||||
|
renderInfo.startTime = new Date().getTime();
|
||||||
|
resetRenderProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
setRenderProgress,
|
||||||
|
resetRenderProgress,
|
||||||
|
setLog,
|
||||||
|
stopProgress,
|
||||||
|
startProgress,
|
||||||
|
renderInfo
|
||||||
|
}
|
||||||
@@ -1,11 +1,7 @@
|
|||||||
import formatXML from "xml-formatter";
|
import {SettingPath, defaultOutputPath, OLDSettingPath} from './paths';
|
||||||
import {SettingPath, defaultOutputPath} from './paths';
|
import {dialog} from '@electron/remote';
|
||||||
import fs from "fs";
|
|
||||||
import logger from "./logger";
|
import logger from "./logger";
|
||||||
|
import fs from "fs";
|
||||||
function getXMLChild(doc:Document, child:string) {
|
|
||||||
return String(doc.getElementsByTagName(child)[0].childNodes[0].nodeValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum VideoFormat {
|
enum VideoFormat {
|
||||||
mp4="mp4",
|
mp4="mp4",
|
||||||
@@ -15,138 +11,470 @@ enum VideoFormat {
|
|||||||
mkv="mkv",
|
mkv="mkv",
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultSettings = {
|
type JSONProfile = {
|
||||||
|
profileName: string,
|
||||||
|
fps: number,
|
||||||
|
width: number,
|
||||||
|
stickDistance: number,
|
||||||
|
stickMode2: boolean,
|
||||||
|
videoFormat: VideoFormat
|
||||||
|
};
|
||||||
|
|
||||||
|
type JSONSettings = {
|
||||||
|
activeProfile: string,
|
||||||
|
profiles: JSONProfile[],
|
||||||
|
logs: string[],
|
||||||
|
output: string,
|
||||||
|
showRenderTerminal: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultSettings:JSONSettings = {
|
||||||
|
activeProfile: "Default",
|
||||||
|
profiles: [
|
||||||
|
{
|
||||||
|
profileName: "Default",
|
||||||
fps: 30,
|
fps: 30,
|
||||||
width: 540,
|
width: 540,
|
||||||
stickDistance: 5,
|
stickDistance: 5,
|
||||||
stickMode2: true,
|
stickMode2: true,
|
||||||
videoFormat: VideoFormat.webm,
|
videoFormat: VideoFormat.webm
|
||||||
log: '',
|
|
||||||
output: defaultOutputPath
|
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
logs: [],
|
||||||
|
output: defaultOutputPath,
|
||||||
|
showRenderTerminal: false
|
||||||
|
};
|
||||||
|
|
||||||
let allSettingsFound = true;
|
|
||||||
function catchSetting(tryFunc:()=>string, catchFunc:()=>string) {
|
function catchSetting(tryFunc:()=>string, catchFunc:()=>string) {
|
||||||
let val;
|
let val;
|
||||||
try {
|
try {
|
||||||
val = tryFunc();
|
val = tryFunc();
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
logger.info("Failed to get setting value. Using default value:" + String(err));
|
logger.info("Failed to get setting value. Using default value:" + String(err));
|
||||||
allSettingsFound = false;
|
|
||||||
val = catchFunc();
|
val = catchFunc();
|
||||||
}
|
}
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getXMLChild(doc:Document, child:string) {
|
||||||
|
return String(doc.getElementsByTagName(child)[0].childNodes[0].nodeValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
let fetchFailed = "";
|
||||||
const settingList = await fetch(SettingPath).then(function(response) {
|
const settingList = await fetch(SettingPath).then(function(response) {
|
||||||
return response.text();
|
return response.text();
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
logger.info(err);
|
logger.info(err);
|
||||||
return "fileLoadFailed";
|
return "fileLoadFailed";
|
||||||
|
}).then(async function(data) {
|
||||||
|
if(data === "fileLoadFailed") {
|
||||||
|
return await fetch(OLDSettingPath).then(function(response) {
|
||||||
|
return response.text();
|
||||||
|
}).catch(function(err) {
|
||||||
|
logger.info(err);
|
||||||
|
return "fileLoadFailed";
|
||||||
}).then(function(data) {
|
}).then(function(data) {
|
||||||
if(data === "fileLoadFailed") {
|
if(data === "fileLoadFailed") {
|
||||||
allSettingsFound = false;
|
fetchFailed = "fileLoadFailed";
|
||||||
return defaultSettings;
|
return defaultSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
const xmlDoc = parser.parseFromString(data, 'text/xml');
|
const xmlDoc = parser.parseFromString(data, 'text/xml');
|
||||||
|
|
||||||
return {
|
const allLogs = catchSetting(function() {return (getXMLChild(xmlDoc, "log") === "None")? "":getXMLChild(xmlDoc, "log");},function() {
|
||||||
fps: parseInt(catchSetting(function() {return getXMLChild(xmlDoc, "fps");},function() {return defaultSettings.fps.toString();})),
|
fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
|
||||||
width: parseInt(catchSetting(function() {return getXMLChild(xmlDoc, "width");},function() {return defaultSettings.width.toString();})),
|
return ""
|
||||||
stickDistance: parseInt(catchSetting(function() {return getXMLChild(xmlDoc, "stickDistance");},function() {return defaultSettings.stickDistance.toString();})),
|
});
|
||||||
stickMode2: catchSetting(function() {return getXMLChild(xmlDoc, "stickMode2");},function() {return defaultSettings.stickMode2.toString();}) === "true",
|
const newLogs = defaultSettings.logs;
|
||||||
videoFormat: catchSetting(function() {return getXMLChild(xmlDoc, "videoFormat");},function() {return defaultSettings.videoFormat.toString();}) as VideoFormat as VideoFormat,
|
if(allLogs !== "") {
|
||||||
log: catchSetting(function() {return (getXMLChild(xmlDoc, "log") === "None")? "":getXMLChild(xmlDoc, "log");},function() {return defaultSettings.log;}),
|
const allLogsList = allLogs.split("\"\"");
|
||||||
output: catchSetting(function() {return getXMLChild(xmlDoc, "output");},function() {return defaultSettings.output;})
|
allLogsList.forEach(log => {
|
||||||
|
if(log !== "") {
|
||||||
|
newLogs.push(log.replace('"', ''));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if(!allSettingsFound) {
|
|
||||||
updateSettings({});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSettings(optiones:{fps?:number, width?:number, stickDistance?:number, stickMode2?:boolean, videoFormat?:VideoFormat, log?:string, output?:string}) {
|
return {
|
||||||
if(optiones.fps === undefined) {
|
activeProfile: defaultSettings.activeProfile,
|
||||||
optiones.fps = settingList.fps;
|
profiles: [
|
||||||
} else {
|
{
|
||||||
settingList.fps = optiones.fps;
|
profileName: defaultSettings.profiles[0].profileName,
|
||||||
|
fps: parseInt(catchSetting(function() {return getXMLChild(xmlDoc, "fps");},function() {
|
||||||
|
fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
|
||||||
|
return defaultSettings.profiles[0].fps.toString();
|
||||||
|
})),
|
||||||
|
width: parseInt(catchSetting(function() {return getXMLChild(xmlDoc, "width");},function() {
|
||||||
|
fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
|
||||||
|
return defaultSettings.profiles[0].width.toString();
|
||||||
|
})),
|
||||||
|
stickDistance: parseInt(catchSetting(function() {return getXMLChild(xmlDoc, "stickDistance");},function() {
|
||||||
|
fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
|
||||||
|
return defaultSettings.profiles[0].stickDistance.toString();
|
||||||
|
})),
|
||||||
|
stickMode2: catchSetting(function() {return getXMLChild(xmlDoc, "stickMode2");},function() {
|
||||||
|
fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
|
||||||
|
return defaultSettings.profiles[0].stickMode2.toString();
|
||||||
|
}) === "true",
|
||||||
|
videoFormat: catchSetting(function() {return getXMLChild(xmlDoc, "videoFormat");},function() {
|
||||||
|
return defaultSettings.profiles[0].videoFormat.toString();
|
||||||
|
}) as VideoFormat as VideoFormat
|
||||||
}
|
}
|
||||||
if(optiones.width === undefined) {
|
],
|
||||||
optiones.width = settingList.width;
|
logs: newLogs,
|
||||||
} else {
|
output: catchSetting(function() {return getXMLChild(xmlDoc, "output");},function() {
|
||||||
settingList.width = optiones.width;
|
fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
|
||||||
|
return defaultSettings.output;
|
||||||
|
}),
|
||||||
|
showRenderTerminal: defaultSettings.showRenderTerminal
|
||||||
|
} as JSONSettings;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if(optiones.stickDistance === undefined) {
|
const parsedData = JSON.parse(data);
|
||||||
optiones.stickDistance = settingList.stickDistance;
|
|
||||||
} else {
|
let profiles:JSONProfile[] = [];
|
||||||
settingList.stickDistance = optiones.stickDistance;
|
if(parsedData.profiles !== undefined) {
|
||||||
|
parsedData.profiles.forEach((profile:JSONProfile) => {
|
||||||
|
if(typeof profile.profileName !== "string") {
|
||||||
|
fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
|
||||||
|
profile.profileName = defaultSettings.profiles[0].profileName;
|
||||||
}
|
}
|
||||||
if(optiones.stickMode2 === undefined) {
|
if(typeof profile.fps !== "number") {
|
||||||
optiones.stickMode2 = settingList.stickMode2;
|
fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
|
||||||
} else {
|
profile.fps = defaultSettings.profiles[0].fps;
|
||||||
settingList.stickMode2 = optiones.stickMode2;
|
|
||||||
}
|
}
|
||||||
if(optiones.videoFormat === undefined) {
|
if(typeof profile.width !== "number") {
|
||||||
optiones.videoFormat = settingList.videoFormat;
|
fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
|
||||||
} else {
|
profile.width = defaultSettings.profiles[0].width;
|
||||||
settingList.videoFormat = optiones.videoFormat;
|
|
||||||
}
|
}
|
||||||
if(optiones.log === undefined) {
|
if(typeof profile.stickDistance !== "number") {
|
||||||
optiones.log = settingList.log;
|
fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
|
||||||
} else {
|
profile.stickDistance = defaultSettings.profiles[0].stickDistance;
|
||||||
settingList.log = optiones.log;
|
|
||||||
}
|
}
|
||||||
if(optiones.output === undefined) {
|
if(typeof profile.stickMode2 !== "boolean") {
|
||||||
optiones.output = settingList.output;
|
fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
|
||||||
} else {
|
profile.stickMode2 = defaultSettings.profiles[0].stickMode2;
|
||||||
settingList.output = optiones.output;
|
}
|
||||||
|
if(typeof profile.videoFormat !== "string") {
|
||||||
|
fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
|
||||||
|
profile.videoFormat = defaultSettings.profiles[0].videoFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
const xmlStr = `
|
profiles.push({
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
profileName: profile.profileName,
|
||||||
<settings>
|
fps: profile.fps,
|
||||||
<fps>${optiones.fps}</fps>
|
width: profile.width,
|
||||||
<width>${optiones.width}</width>
|
stickDistance: profile.stickDistance,
|
||||||
<stickDistance>${optiones.stickDistance}</stickDistance>
|
stickMode2: profile.stickMode2,
|
||||||
<stickMode2>${optiones.stickMode2}</stickMode2>
|
videoFormat: profile.videoFormat
|
||||||
<videoFormat>${optiones.videoFormat}</videoFormat>
|
});
|
||||||
<log>${(optiones.log === "")?"None":optiones.log}</log>
|
});
|
||||||
<output>${optiones.output}</output>
|
} else {
|
||||||
</settings>
|
fetchFailed = "multiSetting";
|
||||||
`;
|
profiles = defaultSettings.profiles;
|
||||||
|
}
|
||||||
|
|
||||||
fs.writeFile(SettingPath, formatXML(xmlStr, {collapseContent: true}), function(err) {
|
const logs:string[] = [];
|
||||||
|
if(parsedData.logs !== undefined) {
|
||||||
|
parsedData.logs.forEach((log:string) => {
|
||||||
|
if(typeof log === "string") {
|
||||||
|
logs.push(log);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
activeProfile: catchSetting(function() {return parsedData.activeProfile;},function() {
|
||||||
|
fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
|
||||||
|
return defaultSettings.activeProfile;
|
||||||
|
}),
|
||||||
|
profiles,
|
||||||
|
logs,
|
||||||
|
output: catchSetting(function() {return parsedData.output;},function() {
|
||||||
|
fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
|
||||||
|
return defaultSettings.output;
|
||||||
|
}),
|
||||||
|
showRenderTerminal: function() {
|
||||||
|
if(typeof parsedData.showRenderTerminal === "boolean") {
|
||||||
|
return parsedData.showRenderTerminal;
|
||||||
|
} else {
|
||||||
|
fetchFailed === "singleSetting"? fetchFailed = "multiSetting":fetchFailed = "singleSetting";
|
||||||
|
return defaultSettings.showRenderTerminal;
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if(fetchFailed !== "") {
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProfiles() {
|
||||||
|
return settingList.profiles.map((profile) => {
|
||||||
|
return profile.profileName;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createProfile(profileName:string, clone:boolean) {
|
||||||
|
settingList.profiles.push({
|
||||||
|
profileName: profileName,
|
||||||
|
fps: clone? getActiveProfile().fps:defaultSettings.profiles[0].fps,
|
||||||
|
width: clone? getActiveProfile().width:defaultSettings.profiles[0].width,
|
||||||
|
stickDistance: clone? getActiveProfile().stickDistance:defaultSettings.profiles[0].stickDistance,
|
||||||
|
stickMode2: clone? getActiveProfile().stickMode2:defaultSettings.profiles[0].stickMode2,
|
||||||
|
videoFormat: clone? getActiveProfile().videoFormat:defaultSettings.profiles[0].videoFormat
|
||||||
|
});
|
||||||
|
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
function ProfileLoadDefault(reset:{fps?:boolean, width?:boolean, stickDistance?:boolean, stickMode2?:boolean, videoFormat?:boolean, all?:boolean}, profileName?:string) {
|
||||||
|
if(profileName === undefined) {
|
||||||
|
profileName = getActiveProfile().profileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
settingList.profiles.forEach(profile => {
|
||||||
|
if(profile.profileName === profileName) {
|
||||||
|
if(reset.all || reset.fps) {
|
||||||
|
profile.fps = defaultSettings.profiles[0].fps;
|
||||||
|
}
|
||||||
|
if(reset.all || reset.width) {
|
||||||
|
profile.width = defaultSettings.profiles[0].width;
|
||||||
|
}
|
||||||
|
if(reset.all || reset.stickDistance) {
|
||||||
|
profile.stickDistance = defaultSettings.profiles[0].stickDistance;
|
||||||
|
}
|
||||||
|
if(reset.all || reset.stickMode2) {
|
||||||
|
profile.stickMode2 = defaultSettings.profiles[0].stickMode2;
|
||||||
|
}
|
||||||
|
if(reset.all || reset.videoFormat) {
|
||||||
|
profile.videoFormat = defaultSettings.profiles[0].videoFormat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
function editProfile(optiones:{profileName?:string, fps?:number, width?:number, stickDistance?:number, stickMode2?:boolean, videoFormat?:VideoFormat}, profileName?:string) {
|
||||||
|
if(profileName === undefined) {
|
||||||
|
profileName = getActiveProfile().profileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
settingList.profiles.forEach(profile => {
|
||||||
|
if(profile.profileName === profileName) {
|
||||||
|
if(optiones.profileName !== undefined) {
|
||||||
|
profile.profileName = optiones.profileName;
|
||||||
|
}
|
||||||
|
if(optiones.fps !== undefined) {
|
||||||
|
profile.fps = optiones.fps;
|
||||||
|
}
|
||||||
|
if(optiones.width !== undefined) {
|
||||||
|
profile.width = optiones.width;
|
||||||
|
}
|
||||||
|
if(optiones.stickDistance !== undefined) {
|
||||||
|
profile.stickDistance = optiones.stickDistance;
|
||||||
|
}
|
||||||
|
if(optiones.stickMode2 !== undefined) {
|
||||||
|
profile.stickMode2 = optiones.stickMode2;
|
||||||
|
}
|
||||||
|
if(optiones.videoFormat !== undefined) {
|
||||||
|
profile.videoFormat = optiones.videoFormat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setInOutSettings(args:{logs?:string[], output?:string}) {
|
||||||
|
if(args.logs !== undefined) {
|
||||||
|
settingList.logs = args.logs;
|
||||||
|
}
|
||||||
|
if(args.output !== undefined) {
|
||||||
|
settingList.output = args.output;
|
||||||
|
}
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
function getInOutSettings() {
|
||||||
|
return {logs: settingList.logs, output: settingList.output};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setShowRenderTerminal(show:boolean) {
|
||||||
|
settingList.showRenderTerminal = show;
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
function getShowRenderTerminal() {
|
||||||
|
return settingList.showRenderTerminal;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeProfile(profileName?:string) {
|
||||||
|
if(profileName === undefined) {
|
||||||
|
profileName = getActiveProfile().profileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
settingList.profiles.forEach(profile => {
|
||||||
|
if(profile.profileName === profileName) {
|
||||||
|
settingList.profiles.splice(settingList.profiles.indexOf(profile), 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setActiveProfile(profileName:string) {
|
||||||
|
settingList.profiles.forEach(profile => {
|
||||||
|
if(profile.profileName === profileName) {
|
||||||
|
settingList.activeProfile = profile.profileName;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeSettings(settings?:JSONSettings) {
|
||||||
|
if(settings === undefined) {
|
||||||
|
settings = settingList;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFile(SettingPath, JSON.stringify(settings), function(err) {
|
||||||
if(err) {
|
if(err) {
|
||||||
logger.errorMSG(String(err));
|
logger.errorMSG(String(err));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function settingListLoadDefault() {
|
function getActiveProfile() {
|
||||||
updateSettings({
|
let activeProfile;
|
||||||
fps:defaultSettings.fps,
|
settingList.profiles.forEach(profile => {
|
||||||
width:defaultSettings.width,
|
if(profile.profileName === settingList.activeProfile) {
|
||||||
stickDistance:defaultSettings.stickDistance,
|
activeProfile = profile;
|
||||||
stickMode2:defaultSettings.stickMode2,
|
}
|
||||||
videoFormat:defaultSettings.videoFormat,
|
});
|
||||||
|
|
||||||
|
if(activeProfile === undefined) {
|
||||||
|
activeProfile = defaultSettings.profiles[0];
|
||||||
|
logger.error("Active profile not found, using default profile");
|
||||||
|
}
|
||||||
|
|
||||||
|
return activeProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
function importProfile(importSucces:CallableFunction) {
|
||||||
|
dialog.showOpenDialog({
|
||||||
|
properties: [
|
||||||
|
"openFile"
|
||||||
|
],
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
name: "StickExporterTX-Profile",
|
||||||
|
extensions: [
|
||||||
|
"setp"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}).then(async result => {
|
||||||
|
if(result.filePaths.length === 1) {
|
||||||
|
const rawData = await fs.promises.readFile(result.filePaths[0], "utf8");
|
||||||
|
const jsonData = JSON.parse(rawData) as JSONProfile[];
|
||||||
|
const importProfiles:JSONProfile[] = [];
|
||||||
|
jsonData.forEach(profile => {
|
||||||
|
importProfiles.push({
|
||||||
|
profileName: profile.profileName,
|
||||||
|
fps: profile.fps,
|
||||||
|
width: profile.width,
|
||||||
|
stickDistance: profile.stickDistance,
|
||||||
|
stickMode2: profile.stickMode2,
|
||||||
|
videoFormat: profile.videoFormat
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
importProfiles.forEach(importProfile => {
|
||||||
|
while(settingList.profiles.find(profile => profile.profileName === importProfile.profileName) !== undefined) {
|
||||||
|
importProfile.profileName = importProfile.profileName + " (imported)";
|
||||||
|
}
|
||||||
|
settingList.profiles.push(importProfile);
|
||||||
|
});
|
||||||
|
writeSettings();
|
||||||
|
importSucces();
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
logger.errorMSG("Import faulty: "+err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLogList() {
|
function exportProfile() {
|
||||||
return settingList.log.split("\"\"");
|
dialog.showMessageBox({
|
||||||
|
type: "question",
|
||||||
|
noLink: true,
|
||||||
|
buttons: ["This", "All", "Cancel"],
|
||||||
|
title: "Export profile",
|
||||||
|
message: "Do you want to export all profiles or just the active one?"
|
||||||
|
}).then(result => {
|
||||||
|
if(result.response === 0) {
|
||||||
|
dialog.showSaveDialog({
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
name: "StickExporterTX-Profile",
|
||||||
|
extensions: [
|
||||||
|
"setp"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
]
|
||||||
function getLogSize(index:number) {
|
}).then(result => {
|
||||||
const logList = settingList.log.substring(1).slice(0, -1).split('""');
|
if(result.filePath !== undefined) {
|
||||||
|
fs.writeFile(result.filePath, JSON.stringify([getActiveProfile()]), function(err) {
|
||||||
return fs.statSync(logList[index]).size;
|
if(err) {
|
||||||
|
logger.errorMSG(String(err));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
logger.errorMSG("Export faulty: "+err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if(result.response === 1) {
|
||||||
|
dialog.showSaveDialog({
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
name: "StickExporterTX-Profile",
|
||||||
|
extensions: [
|
||||||
|
"setp"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}).then(result => {
|
||||||
|
if(result.filePath !== undefined) {
|
||||||
|
fs.writeFile(result.filePath, JSON.stringify(settingList.profiles), function(err) {
|
||||||
|
if(err) {
|
||||||
|
logger.errorMSG(String(err));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
logger.errorMSG("Export faulty: "+err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
logger.errorMSG("Export faulty: "+err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
updateSettings,
|
getProfiles,
|
||||||
settingListLoadDefault,
|
createProfile,
|
||||||
settingList,
|
ProfileLoadDefault,
|
||||||
getLogList,
|
editProfile,
|
||||||
getLogSize,
|
removeProfile,
|
||||||
|
setActiveProfile,
|
||||||
|
getActiveProfile,
|
||||||
|
setInOutSettings,
|
||||||
|
getInOutSettings,
|
||||||
|
setShowRenderTerminal,
|
||||||
|
getShowRenderTerminal,
|
||||||
|
importProfile,
|
||||||
|
exportProfile,
|
||||||
VideoFormat
|
VideoFormat
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import { dialog } from "@electron/remote";
|
import { dialog } from "@electron/remote";
|
||||||
import { settingList, updateSettings } from "../settings";
|
import { setInOutSettings, getInOutSettings } from "../settings";
|
||||||
import logger from "../logger";
|
import logger from "../logger";
|
||||||
import {blender, blenderCmd} from "../blenderController";
|
import {blender, blenderCmd} from "../blenderController";
|
||||||
import openFolder from "../openFolder";
|
import openFolder from "../openFolder";
|
||||||
@@ -8,7 +8,7 @@ import {platformCharacter} from "../paths";
|
|||||||
import {logList, reloadAllLogs, updateLogs} from "../logReader";
|
import {logList, reloadAllLogs, updateLogs} from "../logReader";
|
||||||
|
|
||||||
function MainPage() {
|
function MainPage() {
|
||||||
const [output, setOutput] = useState(settingList.output);
|
const [output, setOutput] = useState(getInOutSettings().output);
|
||||||
const [logTable, setLogTable] = useState([<tr key={0}></tr>]);
|
const [logTable, setLogTable] = useState([<tr key={0}></tr>]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -32,14 +32,14 @@ function MainPage() {
|
|||||||
<div className="dataDiv">
|
<div className="dataDiv">
|
||||||
<button id="openLogButton" onClick={() => addLog(setLogTable)}>Add Log(s)</button>
|
<button id="openLogButton" onClick={() => addLog(setLogTable)}>Add Log(s)</button>
|
||||||
<button id="deleteLogsButton" onClick={async () => {
|
<button id="deleteLogsButton" onClick={async () => {
|
||||||
updateSettings({log:""});
|
setInOutSettings({logs:[]});
|
||||||
await reloadAllLogs();
|
await reloadAllLogs();
|
||||||
updateLogTable(setLogTable);
|
updateLogTable(setLogTable);
|
||||||
}}>Delete All</button>
|
}}>Delete All</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="dataDiv" id="outputDiv">
|
<div className="dataDiv" id="outputDiv">
|
||||||
<h4>Output Folder:</h4>
|
<h4>Output Folder:</h4>
|
||||||
<p id="output" onClick={() => openFolder(settingList.output)}>{output}</p>
|
<p id="output" onClick={() => openFolder(getInOutSettings().output)}>{output}</p>
|
||||||
<button onClick={() => openVid(setOutput)}>Select Folder</button>
|
<button onClick={() => openVid(setOutput)}>Select Folder</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -60,8 +60,8 @@ function updateLogTable(setLogTable:React.Dispatch<React.SetStateAction<JSX.Elem
|
|||||||
fontWeight: "lighter"
|
fontWeight: "lighter"
|
||||||
}}>({log.time.length.formatted})</td>
|
}}>({log.time.length.formatted})</td>
|
||||||
<td><button className="listButton" onClick={async () => {
|
<td><button className="listButton" onClick={async () => {
|
||||||
const newLogs = settingList.log.replace('"'+log.path+'"', "");
|
const newLogs = getInOutSettings().logs.filter(value => value !== log.path);
|
||||||
updateSettings({log:newLogs});
|
setInOutSettings({logs:newLogs});
|
||||||
await updateLogs();
|
await updateLogs();
|
||||||
updateLogTable(setLogTable);
|
updateLogTable(setLogTable);
|
||||||
}}>Delete</button></td>
|
}}>Delete</button></td>
|
||||||
@@ -69,7 +69,7 @@ function updateLogTable(setLogTable:React.Dispatch<React.SetStateAction<JSX.Elem
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(settingList.log == "") {
|
if(getInOutSettings().logs.length === 0) {
|
||||||
setLogTable([]);
|
setLogTable([]);
|
||||||
} else {
|
} else {
|
||||||
getData();
|
getData();
|
||||||
@@ -90,17 +90,15 @@ function addLog(setLogTable:React.Dispatch<React.SetStateAction<JSX.Element[]>>)
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}).then(async result => {
|
}).then(async result => {
|
||||||
let logStr = "";
|
const newLogs = getInOutSettings().logs;
|
||||||
result.filePaths.forEach(value => {
|
result.filePaths.forEach(value => {
|
||||||
const logToAdd = "\"" + value + "\"";
|
if(getInOutSettings().logs.includes(value)) {
|
||||||
if(settingList.log.includes(logToAdd)) {
|
logger.warningMSG("Log \"" + value + "\" already added.");
|
||||||
logger.warningMSG("Log " + logToAdd + " already added.");
|
|
||||||
} else {
|
} else {
|
||||||
logStr += "\"" + String(value) + "\"";
|
newLogs.push(value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const newLogs = settingList.log + logStr;
|
setInOutSettings({logs:newLogs});
|
||||||
updateSettings({log:newLogs});
|
|
||||||
await updateLogs();
|
await updateLogs();
|
||||||
updateLogTable(setLogTable);
|
updateLogTable(setLogTable);
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
@@ -115,7 +113,7 @@ function openVid(updateHook:React.Dispatch<React.SetStateAction<string>>) {
|
|||||||
]
|
]
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
if(result.filePaths.length > 0) {
|
if(result.filePaths.length > 0) {
|
||||||
updateSettings({output:String(result.filePaths)});
|
setInOutSettings({output:String(result.filePaths)});
|
||||||
updateHook(String(result.filePaths));
|
updateHook(String(result.filePaths));
|
||||||
}
|
}
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import React, { CSSProperties } from "react";
|
import React, { CSSProperties } from "react";
|
||||||
import {openPage, Page} from "../../renderer";
|
import {openPage, Page} from "../../renderer";
|
||||||
import {renderInfo} from "../blenderController";
|
import {outputArgs} from "../blenderController";
|
||||||
import openFolder from "../openFolder";
|
import openFolder from "../openFolder";
|
||||||
import {settingList} from "../settings";
|
import {getInOutSettings, getActiveProfile} from "../settings";
|
||||||
import VideoPlayer from "./videoPlayer";
|
import VideoPlayer from "./videoPlayer";
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {platformCharacter} from "../paths";
|
|
||||||
import {VideoJsPlayerOptions} from "video.js";
|
import {VideoJsPlayerOptions} from "video.js";
|
||||||
import {logList} from "../logReader";
|
import {logList} from "../logReader";
|
||||||
|
import {renderInfo} from "../progressController";
|
||||||
|
|
||||||
const detailsStyle:CSSProperties = {
|
const detailsStyle:CSSProperties = {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@@ -18,10 +18,21 @@ const detailsInnerStyle:CSSProperties = {
|
|||||||
marginRight: "5px",
|
marginRight: "5px",
|
||||||
}
|
}
|
||||||
|
|
||||||
function RenderFinishPage() {
|
const VideoSpanStyle:CSSProperties = {
|
||||||
const [logPlaying, setLogPlaying] = React.useState(path.join(settingList.output, settingList.log.substring(1).slice(0, -1).split('""')[0].split(platformCharacter())[settingList.log.substring(1).slice(0, -1).split('""')[0].split(platformCharacter()).length - 1].replace(".csv", "."+settingList.videoFormat)));
|
display: "flex",
|
||||||
|
alignItems: "center"
|
||||||
|
}
|
||||||
|
const videoSelectStyle:CSSProperties = {
|
||||||
|
padding: "4px 0",
|
||||||
|
border: "0",
|
||||||
|
borderRadius: "14px",
|
||||||
|
cursor: "pointer",
|
||||||
|
textAlign: "center",
|
||||||
|
fontSize: "large"
|
||||||
|
}
|
||||||
|
|
||||||
const [outputList, setOutputList] = React.useState([<li key={0}></li>]);
|
function RenderFinishPage() {
|
||||||
|
const [logPlaying, setLogPlaying] = React.useState(logList[0].name);
|
||||||
|
|
||||||
const videoPlayerOptions:VideoJsPlayerOptions = {
|
const videoPlayerOptions:VideoJsPlayerOptions = {
|
||||||
controls: true,
|
controls: true,
|
||||||
@@ -32,25 +43,17 @@ function RenderFinishPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const [videoSource, setVideoSource] = React.useState({src: logPlaying, type: 'video/'+settingList.videoFormat.toUpperCase()});
|
const [videoSource, setVideoSource] = React.useState({src: path.join(getInOutSettings().output, logPlaying+"."+getActiveProfile().videoFormat), type: 'video/'+getActiveProfile().videoFormat.toUpperCase()});
|
||||||
|
|
||||||
|
const OutputList = outputArgs.map((output, index) => {
|
||||||
|
const outputName = output.substring(output.lastIndexOf("\\")+1);
|
||||||
|
return <option key={index} value={outputName} title={output}>{outputName}</option>
|
||||||
|
});
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setOutputList(logList.map((inputLog, index) => {
|
|
||||||
const outputLogPath = path.join(settingList.output, inputLog.name+"."+settingList.videoFormat);
|
|
||||||
|
|
||||||
return <li key={index}>
|
|
||||||
<p style={{
|
|
||||||
textDecoration: logPlaying === outputLogPath ? "underline" : "none",
|
|
||||||
cursor: logPlaying === outputLogPath ? "default" : "pointer",
|
|
||||||
}} onClick={() => {
|
|
||||||
setLogPlaying(outputLogPath);
|
|
||||||
}} title={outputLogPath}>{inputLog.name}</p>
|
|
||||||
</li>
|
|
||||||
}));
|
|
||||||
|
|
||||||
setVideoSource({
|
setVideoSource({
|
||||||
src: logPlaying,
|
src: path.join(getInOutSettings().output, logPlaying.replace(".csv", "."+getActiveProfile().videoFormat)),
|
||||||
type: 'video/'+settingList.videoFormat.toUpperCase()
|
type: 'video/'+getActiveProfile().videoFormat.toUpperCase()
|
||||||
});
|
});
|
||||||
}, [logPlaying]);
|
}, [logPlaying]);
|
||||||
|
|
||||||
@@ -78,14 +81,20 @@ function RenderFinishPage() {
|
|||||||
}} onClick={() => {
|
}} onClick={() => {
|
||||||
openPage(Page.Main);
|
openPage(Page.Main);
|
||||||
}}>Finish</button>
|
}}>Finish</button>
|
||||||
<button onClick={() => openFolder(settingList.output)}>Open Output Folder</button>
|
<button onClick={() => openFolder(getInOutSettings().output)}>Open Output Folder</button>
|
||||||
<div style={{
|
<div style={{
|
||||||
marginTop: "10px"
|
marginTop: "10px"
|
||||||
}}>
|
}}>
|
||||||
|
<span style={VideoSpanStyle}>
|
||||||
|
<label htmlFor="vslct">
|
||||||
|
<select style={videoSelectStyle} id="vslct" required={true} value={logPlaying} onChange={e => {
|
||||||
|
setLogPlaying(e.target.value);
|
||||||
|
}}>
|
||||||
|
{OutputList}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</span>
|
||||||
<VideoPlayer options={videoPlayerOptions} src={videoSource} />
|
<VideoPlayer options={videoPlayerOptions} src={videoSource} />
|
||||||
<ol>
|
|
||||||
{outputList}
|
|
||||||
</ol>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useRef, useState} from "react";
|
||||||
import { settingList, getLogList } from "../settings";
|
import {getInOutSettings, getShowRenderTerminal, setShowRenderTerminal} from "../settings";
|
||||||
import openFolder from "../openFolder";
|
import openFolder from "../openFolder";
|
||||||
import { blenderCmd, blender } from "../blenderController";
|
import { blenderCmd, blender } from "../blenderController";
|
||||||
|
|
||||||
@@ -8,11 +8,15 @@ let setStatus:React.Dispatch<React.SetStateAction<string>>;
|
|||||||
let setRenderDisplayProgress:React.Dispatch<React.SetStateAction<number>>;
|
let setRenderDisplayProgress:React.Dispatch<React.SetStateAction<number>>;
|
||||||
let setPastTime:React.Dispatch<React.SetStateAction<string>>;
|
let setPastTime:React.Dispatch<React.SetStateAction<string>>;
|
||||||
let setRemainingTime:React.Dispatch<React.SetStateAction<string>>;
|
let setRemainingTime:React.Dispatch<React.SetStateAction<string>>;
|
||||||
|
let setTerminalLines:React.Dispatch<React.SetStateAction<JSX.Element[]>>;
|
||||||
|
|
||||||
let pastTimeNow = "0m 0s";
|
let pastTimeNow = "0m 0s";
|
||||||
let remainingTimeNow = "calculating...";
|
let remainingTimeNow = "calculating...";
|
||||||
|
|
||||||
function RenderingPage() {
|
function RenderingPage() {
|
||||||
|
const [terminalHidden, setTerminalHidden] = useState(getShowRenderTerminal() ? "block" : "none");
|
||||||
|
const [terminalScroll, setTerminalScroll] = useState(true);
|
||||||
|
const [scrollButtonText, setScrollButtonText] = useState("pause scroll");
|
||||||
const [logNumber, setLogNumberInner] = useState("0");
|
const [logNumber, setLogNumberInner] = useState("0");
|
||||||
setLogNumber = setLogNumberInner;
|
setLogNumber = setLogNumberInner;
|
||||||
const [status, setStatusInner] = useState("Idle");
|
const [status, setStatusInner] = useState("Idle");
|
||||||
@@ -23,6 +27,24 @@ function RenderingPage() {
|
|||||||
setPastTime = setPastTimeInner;
|
setPastTime = setPastTimeInner;
|
||||||
const [remainingTime, setRemainingTimeInner] = useState("calculating...");
|
const [remainingTime, setRemainingTimeInner] = useState("calculating...");
|
||||||
setRemainingTime = setRemainingTimeInner;
|
setRemainingTime = setRemainingTimeInner;
|
||||||
|
const [terminalLines, setTerminalLinesInner] = useState([
|
||||||
|
<tr key={0}>
|
||||||
|
<td className="terminalTableNumber">0:</td>
|
||||||
|
<td>==== Start ====</td>
|
||||||
|
</tr>
|
||||||
|
]);
|
||||||
|
setTerminalLines = setTerminalLinesInner;
|
||||||
|
|
||||||
|
const messagesEndRef = useRef() as React.MutableRefObject<HTMLDivElement>;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(terminalScroll) {
|
||||||
|
messagesEndRef.current?.scrollIntoView();
|
||||||
|
setScrollButtonText("pause scroll");
|
||||||
|
} else {
|
||||||
|
setScrollButtonText("continue scroll");
|
||||||
|
}
|
||||||
|
}, [terminalLines, messagesEndRef, terminalScroll]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
@@ -34,7 +56,7 @@ function RenderingPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<p>{"Log " + logNumber + "/" + getLogList().length}</p>
|
<p>{"Log " + logNumber + "/" + getInOutSettings().logs.length}</p>
|
||||||
<p>{status}</p>
|
<p>{status}</p>
|
||||||
<div className="progress">
|
<div className="progress">
|
||||||
<div className="progress-done" style={{
|
<div className="progress-done" style={{
|
||||||
@@ -69,7 +91,27 @@ function RenderingPage() {
|
|||||||
}>{remainingTime}</p>
|
}>{remainingTime}</p>
|
||||||
</div>
|
</div>
|
||||||
<button id="stopRenderButton" onClick={() => blender(blenderCmd.stopRendering)}>Stop</button>
|
<button id="stopRenderButton" onClick={() => blender(blenderCmd.stopRendering)}>Stop</button>
|
||||||
<button onClick={() => openFolder(settingList.output)}>Open Output Folder</button>
|
<button onClick={() => openFolder(getInOutSettings().output)}>Open Output Folder</button>
|
||||||
|
<button onClick={() => {
|
||||||
|
if (getShowRenderTerminal()) {
|
||||||
|
setTerminalHidden("none");
|
||||||
|
setShowRenderTerminal(false);
|
||||||
|
} else {
|
||||||
|
setTerminalHidden("block");
|
||||||
|
setShowRenderTerminal(true);
|
||||||
|
}
|
||||||
|
}} style={{marginLeft:"10px"}}>Details</button>
|
||||||
|
<div style={{display: terminalHidden}}>
|
||||||
|
<div id="outerTerminal">
|
||||||
|
<div id="innerTerminal">
|
||||||
|
<table id="terminalTable">
|
||||||
|
{terminalLines}
|
||||||
|
</table>
|
||||||
|
<div ref={messagesEndRef}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button style={{padding:"4px"}} onClick={() => setTerminalScroll(!terminalScroll)}>{scrollButtonText}</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -82,6 +124,17 @@ function setRemainingTimeNow(time:string) {
|
|||||||
remainingTimeNow = time;
|
remainingTimeNow = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addTerminalLine(line:string) {
|
||||||
|
setTerminalLines((prev:JSX.Element[]) => {
|
||||||
|
return [...prev,
|
||||||
|
<tr key={prev.length}>
|
||||||
|
<td className="terminalTableNumber">{prev.length}:</td>
|
||||||
|
<td>{line}</td>
|
||||||
|
</tr>
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default RenderingPage;
|
export default RenderingPage;
|
||||||
export {
|
export {
|
||||||
setLogNumber,
|
setLogNumber,
|
||||||
@@ -90,5 +143,6 @@ export {
|
|||||||
setPastTime,
|
setPastTime,
|
||||||
setRemainingTime,
|
setRemainingTime,
|
||||||
setPastTimeNow,
|
setPastTimeNow,
|
||||||
setRemainingTimeNow
|
setRemainingTimeNow,
|
||||||
|
addTerminalLine
|
||||||
};
|
};
|
||||||
@@ -1,17 +1,162 @@
|
|||||||
import React, {useState, useEffect, CSSProperties} from "react";
|
import React, {useState, useEffect, CSSProperties} from "react";
|
||||||
import { settingList, updateSettings, settingListLoadDefault, VideoFormat } from "../settings";
|
import { VideoFormat, editProfile, getActiveProfile, ProfileLoadDefault, getProfiles, setActiveProfile, createProfile, removeProfile, exportProfile, importProfile } from "../settings";
|
||||||
import {blender, blenderCmd, renderingPicture} from "../blenderController";
|
import {blender, blenderCmd, renderingPicture} from "../blenderController";
|
||||||
import {dataPath} from "../paths";
|
import {dataPath} from "../paths";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import logger from "../logger";
|
||||||
|
|
||||||
let setRenderImg:React.Dispatch<React.SetStateAction<string>>;
|
let setRenderImg:React.Dispatch<React.SetStateAction<string>>;
|
||||||
let setRenderLoading:React.Dispatch<React.SetStateAction<boolean>>;
|
let setRenderLoading:React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
let pageLoaded = false;
|
let pageLoaded = false;
|
||||||
|
|
||||||
|
const VideoFormatOptions = Object.keys(VideoFormat).filter((el) => { return isNaN(Number(el)) }).map(key => {
|
||||||
|
return <option key={key} value={key}>{key}</option>;
|
||||||
|
});
|
||||||
|
|
||||||
function picturePath() {
|
function picturePath() {
|
||||||
return path.join(dataPath, "render.png?t="+Date.now());
|
return path.join(dataPath, "render.png?t="+Date.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const overlayArrowStyle:CSSProperties = {
|
||||||
|
width: "50%",
|
||||||
|
height: "50%",
|
||||||
|
fill: "white"
|
||||||
|
}
|
||||||
|
const settingLabelStyle:CSSProperties = {
|
||||||
|
position: "relative",
|
||||||
|
paddingLeft: "10px",
|
||||||
|
paddingRight: "10px",
|
||||||
|
height: "100%",
|
||||||
|
background: "#00c24a",
|
||||||
|
borderTopLeftRadius: "19px",
|
||||||
|
borderBottomLeftRadius: "19px",
|
||||||
|
fontWeight: "bolder",
|
||||||
|
overflow: "hidden",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center"
|
||||||
|
}
|
||||||
|
|
||||||
|
function InputSpan({name, value, min, step, onChange, onReset}:{name:string, value:number, min:number, step:number, onChange:React.ChangeEventHandler<HTMLInputElement>, onReset:CallableFunction}) {
|
||||||
|
const [dispayOverlay, setDisplayOverlay] = useState("none");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="inputSelectSpan">
|
||||||
|
<div style={settingLabelStyle} onMouseEnter={() => {setDisplayOverlay("flex");}} onMouseLeave={() => {setDisplayOverlay("none");}}>
|
||||||
|
{name}
|
||||||
|
<div className="overlay" style={{display: dispayOverlay}} onClick={() => {onReset()}}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style={overlayArrowStyle}>
|
||||||
|
{/* <!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --> */}
|
||||||
|
<path d="M125.7 160H176c17.7 0 32 14.3 32 32s-14.3 32-32 32H48c-17.7 0-32-14.3-32-32V64c0-17.7 14.3-32 32-32s32 14.3 32 32v51.2L97.6 97.6c87.5-87.5 229.3-87.5 316.8 0s87.5 229.3 0 316.8s-229.3 87.5-316.8 0c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0c62.5 62.5 163.8 62.5 226.3 0s62.5-163.8 0-226.3s-163.8-62.5-226.3 0L125.7 160z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input id={name+" Input"} type="number" value={value.toString()} min={min.toString()} step={step.toString()} onChange={onChange}/>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TextSpan({name, value, placeholder, onChange, onReset}:{name:string, value:string, placeholder:string, onChange:React.ChangeEventHandler<HTMLInputElement>, onReset?:CallableFunction}) {
|
||||||
|
const [dispayOverlay, setDisplayOverlay] = useState("none");
|
||||||
|
|
||||||
|
const Overlay = (
|
||||||
|
<div className="overlay" style={{display: dispayOverlay}} onClick={() => {
|
||||||
|
if(onReset !== undefined) {
|
||||||
|
onReset()
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style={overlayArrowStyle}>
|
||||||
|
{/* <!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --> */}
|
||||||
|
<path d="M125.7 160H176c17.7 0 32 14.3 32 32s-14.3 32-32 32H48c-17.7 0-32-14.3-32-32V64c0-17.7 14.3-32 32-32s32 14.3 32 32v51.2L97.6 97.6c87.5-87.5 229.3-87.5 316.8 0s87.5 229.3 0 316.8s-229.3 87.5-316.8 0c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0c62.5 62.5 163.8 62.5 226.3 0s62.5-163.8 0-226.3s-163.8-62.5-226.3 0L125.7 160z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="inputSelectSpan">
|
||||||
|
<div style={settingLabelStyle} onMouseEnter={() => {setDisplayOverlay("flex");}} onMouseLeave={() => {setDisplayOverlay("none");}}>
|
||||||
|
{name}
|
||||||
|
{onReset !== undefined ? Overlay : null}
|
||||||
|
</div>
|
||||||
|
<input id={name+" Input"} type="text" value={value.toString()} placeholder={placeholder} onChange={onChange}/>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectSpan({name, value, optiones, onChange, onReset}:{name:string, value:string, optiones:JSX.Element[], onChange:React.ChangeEventHandler<HTMLSelectElement>, onReset?:CallableFunction}) {
|
||||||
|
const [dispayOverlay, setDisplayOverlay] = useState("none");
|
||||||
|
|
||||||
|
const Overlay = (
|
||||||
|
<div className="overlay" style={{display: dispayOverlay}} onClick={() => {
|
||||||
|
if(onReset !== undefined) {
|
||||||
|
onReset()
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style={overlayArrowStyle}>
|
||||||
|
{/* <!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --> */}
|
||||||
|
<path d="M125.7 160H176c17.7 0 32 14.3 32 32s-14.3 32-32 32H48c-17.7 0-32-14.3-32-32V64c0-17.7 14.3-32 32-32s32 14.3 32 32v51.2L97.6 97.6c87.5-87.5 229.3-87.5 316.8 0s87.5 229.3 0 316.8s-229.3 87.5-316.8 0c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0c62.5 62.5 163.8 62.5 226.3 0s62.5-163.8 0-226.3s-163.8-62.5-226.3 0L125.7 160z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="inputSelectSpan">
|
||||||
|
<div style={settingLabelStyle} onMouseEnter={() => {setDisplayOverlay("flex");}} onMouseLeave={() => {setDisplayOverlay("none");}}>
|
||||||
|
{name}
|
||||||
|
{onReset !== undefined ? Overlay : null}
|
||||||
|
</div>
|
||||||
|
<select id={name+" slct"} required={true} value={value} onChange={onChange}>
|
||||||
|
{optiones}
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ToggleSpan({name, state, checkedValue, uncheckedValue, onChange, onReset}:{name:string, state:boolean, checkedValue:string, uncheckedValue:string, onChange(checked:boolean):void, onReset:CallableFunction}) {
|
||||||
|
const [dispayOverlay, setDisplayOverlay] = useState("none");
|
||||||
|
const [checked, setChecked] = useState(state);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setChecked(state);
|
||||||
|
}, [state]);
|
||||||
|
|
||||||
|
const toggleStyle:CSSProperties = {
|
||||||
|
height: "100%",
|
||||||
|
border: "0",
|
||||||
|
borderTopRightRadius: "18px",
|
||||||
|
borderBottomRightRadius: "18px",
|
||||||
|
textAlign: "center",
|
||||||
|
fontSize: "large",
|
||||||
|
paddingLeft: "15px",
|
||||||
|
paddingRight: "15px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
backgroundColor: checked ? "#2196F3" : "white",
|
||||||
|
color: checked ? "white" : "black",
|
||||||
|
cursor: "pointer",
|
||||||
|
userSelect: "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="inputSelectSpan">
|
||||||
|
<div style={settingLabelStyle} onMouseEnter={() => {setDisplayOverlay("flex");}} onMouseLeave={() => {setDisplayOverlay("none");}}>
|
||||||
|
{name}
|
||||||
|
<div className="overlay" style={{display: dispayOverlay}} onClick={() => {onReset()}}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style={overlayArrowStyle}>
|
||||||
|
{/* <!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --> */}
|
||||||
|
<path d="M125.7 160H176c17.7 0 32 14.3 32 32s-14.3 32-32 32H48c-17.7 0-32-14.3-32-32V64c0-17.7 14.3-32 32-32s32 14.3 32 32v51.2L97.6 97.6c87.5-87.5 229.3-87.5 316.8 0s87.5 229.3 0 316.8s-229.3 87.5-316.8 0c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0c62.5 62.5 163.8 62.5 226.3 0s62.5-163.8 0-226.3s-163.8-62.5-226.3 0L125.7 160z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={toggleStyle} onClick={() => {
|
||||||
|
onChange(!checked);
|
||||||
|
setChecked(!checked);
|
||||||
|
}}>
|
||||||
|
{checked ? checkedValue : uncheckedValue}
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const RenderLoadingSpinner = () => (
|
const RenderLoadingSpinner = () => (
|
||||||
<div id="renderLoadingDiv">
|
<div id="renderLoadingDiv">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||||
@@ -40,9 +185,9 @@ function VideoFormatWarning({videoFormat}:{videoFormat:VideoFormat}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const style:CSSProperties = {
|
const style:CSSProperties = {
|
||||||
height: "30px",
|
height: "35px",
|
||||||
width: "30px",
|
width: "35px",
|
||||||
fill: "orange",
|
fill: "yellow",
|
||||||
paddingLeft: "5px",
|
paddingLeft: "5px",
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -57,21 +202,91 @@ function VideoFormatWarning({videoFormat}:{videoFormat:VideoFormat}) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ProfileSettings({setNameProfile, setProfileName, setNewProfileName, profileName, setNewProfileType, setProfileOptions}:{setNameProfile:CallableFunction, setProfileName:CallableFunction, setNewProfileName:CallableFunction, profileName:string, setNewProfileType:CallableFunction, setProfileOptions:CallableFunction}) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button title="Rename" style={{width:"35px", height:"35px", marginLeft:"5px"}} onClick={() => {
|
||||||
|
setNewProfileName(profileName);
|
||||||
|
setNameProfile(true);
|
||||||
|
setNewProfileType("rename");
|
||||||
|
}}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style={{fill:"white"}}>
|
||||||
|
{/* <!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --> */}
|
||||||
|
<path d="M471.6 21.7c-21.9-21.9-57.3-21.9-79.2 0L362.3 51.7l97.9 97.9 30.1-30.1c21.9-21.9 21.9-57.3 0-79.2L471.6 21.7zm-299.2 220c-6.1 6.1-10.8 13.6-13.5 21.9l-29.6 88.8c-2.9 8.6-.6 18.1 5.8 24.6s15.9 8.7 24.6 5.8l88.8-29.6c8.2-2.8 15.7-7.4 21.9-13.5L437.7 172.3 339.7 74.3 172.4 241.7zM96 64C43 64 0 107 0 160V416c0 53 43 96 96 96H352c53 0 96-43 96-96V320c0-17.7-14.3-32-32-32s-32 14.3-32 32v96c0 17.7-14.3 32-32 32H96c-17.7 0-32-14.3-32-32V160c0-17.7 14.3-32 32-32h96c17.7 0 32-14.3 32-32s-14.3-32-32-32H96z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button title="Create" style={{width:"35px", height:"35px", marginLeft:"5px"}} onClick={() => {
|
||||||
|
setNewProfileName("");
|
||||||
|
setNameProfile(true);
|
||||||
|
setNewProfileType("create");
|
||||||
|
}}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" style={{fill:"white"}}>
|
||||||
|
{/* <!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --> */}
|
||||||
|
<path d="M256 80c0-17.7-14.3-32-32-32s-32 14.3-32 32V224H48c-17.7 0-32 14.3-32 32s14.3 32 32 32H192V432c0 17.7 14.3 32 32 32s32-14.3 32-32V288H400c17.7 0 32-14.3 32-32s-14.3-32-32-32H256V80z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button title="Duplicate" style={{width:"35px", height:"35px", marginLeft:"5px"}} onClick={() => {
|
||||||
|
setNewProfileName("");
|
||||||
|
setNameProfile(true);
|
||||||
|
setNewProfileType("duplicate");
|
||||||
|
}}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style={{fill:"white"}}>
|
||||||
|
{/* <!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --> */}
|
||||||
|
<path d="M502.6 70.63l-61.25-61.25C435.4 3.371 427.2 0 418.7 0H255.1c-35.35 0-64 28.66-64 64l.0195 256C192 355.4 220.7 384 256 384h192c35.2 0 64-28.8 64-64V93.25C512 84.77 508.6 76.63 502.6 70.63zM464 320c0 8.836-7.164 16-16 16H255.1c-8.838 0-16-7.164-16-16L239.1 64.13c0-8.836 7.164-16 16-16h128L384 96c0 17.67 14.33 32 32 32h47.1V320zM272 448c0 8.836-7.164 16-16 16H63.1c-8.838 0-16-7.164-16-16L47.98 192.1c0-8.836 7.164-16 16-16H160V128H63.99c-35.35 0-64 28.65-64 64l.0098 256C.002 483.3 28.66 512 64 512h192c35.2 0 64-28.8 64-64v-32h-47.1L272 448z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button title="Delete" style={{width:"35px", height:"35px", backgroundColor:"#e1334e", marginLeft:"5px"}} onClick={() => {
|
||||||
|
if (getProfiles().length === 1) {
|
||||||
|
logger.warningMSG("You can't delete the last profile!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
removeProfile(profileName);
|
||||||
|
const newActiveProfile = getProfiles()[getProfiles().length - 1];
|
||||||
|
setActiveProfile(newActiveProfile);
|
||||||
|
setProfileName(newActiveProfile);
|
||||||
|
setProfileOptions(getProfiles().map(p => {
|
||||||
|
return <option key={p} value={p}>{p}</option>;
|
||||||
|
}));
|
||||||
|
}}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" style={{fill:"white"}}>
|
||||||
|
{/* <!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --> */}
|
||||||
|
<path d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function SettingsPage() {
|
function SettingsPage() {
|
||||||
|
|
||||||
const [fps, setFps] = useState(settingList.fps);
|
const [fps, setFps] = useState(getActiveProfile().fps);
|
||||||
const [width, setWidth] = useState(settingList.width);
|
const [width, setWidth] = useState(getActiveProfile().width);
|
||||||
const [stickDistance, setStickDistance] = useState(settingList.stickDistance);
|
const [stickDistance, setStickDistance] = useState(getActiveProfile().stickDistance);
|
||||||
const [stickMode2, setStickMode2] = useState(settingList.stickMode2);
|
const [stickMode2, setStickMode2] = useState(getActiveProfile().stickMode2);
|
||||||
const [videoFormat, setVideoFormat] = useState(settingList.videoFormat);
|
const [videoFormat, setVideoFormat] = useState(getActiveProfile().videoFormat);
|
||||||
const [renderImg, setRenderImgInner] = useState(picturePath());
|
const [renderImg, setRenderImgInner] = useState(picturePath());
|
||||||
setRenderImg = setRenderImgInner;
|
setRenderImg = setRenderImgInner;
|
||||||
const [renderLoading, setRenderLoadingInner] = useState(renderingPicture);
|
const [renderLoading, setRenderLoadingInner] = useState(renderingPicture);
|
||||||
setRenderLoading = setRenderLoadingInner;
|
setRenderLoading = setRenderLoadingInner;
|
||||||
|
const [profileOptions, setProfileOptions] = useState(getProfiles().map(p => {
|
||||||
|
return <option key={p} value={p}>{p}</option>;
|
||||||
|
}));
|
||||||
|
const [profileName, setProfileName] = useState(getActiveProfile().profileName);
|
||||||
|
const [newProfileName, setNewProfileName] = useState("");
|
||||||
|
const [nameProfile, setNameProfile] = useState(false);
|
||||||
|
const [newProfileType, setNewProfileType] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setFps(getActiveProfile().fps);
|
||||||
|
setWidth(getActiveProfile().width);
|
||||||
|
setStickDistance(getActiveProfile().stickDistance);
|
||||||
|
setStickMode2(getActiveProfile().stickMode2);
|
||||||
|
setVideoFormat(getActiveProfile().videoFormat);
|
||||||
|
}, [profileName]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
updateSettings({width, stickDistance, stickMode2});
|
editProfile({width, stickDistance, stickMode2});
|
||||||
blender(blenderCmd.getRender);
|
blender(blenderCmd.getRender);
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
@@ -80,7 +295,7 @@ function SettingsPage() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
updateSettings({fps, videoFormat});
|
editProfile({fps, videoFormat});
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
@@ -88,66 +303,133 @@ function SettingsPage() {
|
|||||||
|
|
||||||
pageLoaded = true;
|
pageLoaded = true;
|
||||||
|
|
||||||
const VideoFormatOptions = Object.keys(VideoFormat).filter((el) => { return isNaN(Number(el)) }).map(key => {
|
|
||||||
return <option key={key} value={key}>{key}</option>;
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<div id="settingRow">
|
<div id="settingRow">
|
||||||
<span className="inputSpan">
|
<div style={{
|
||||||
<label>FPS</label>
|
display: "flex",
|
||||||
<input id="fpsInput" type="number" value={fps.toString()} min="1" step="1" onChange={e => {
|
}}>
|
||||||
if(e.target.value.trim().length !== 0) setFps(parseInt(e.target.value));
|
{nameProfile? <TextSpan name="Set Profile Name" value={newProfileName} placeholder="Enter Profile Name Here" onChange={e => {
|
||||||
|
setNewProfileName(e.target.value);
|
||||||
|
}}/> : <SelectSpan name="Select Profile" value={profileName} optiones={profileOptions} onChange={ e => {
|
||||||
|
setActiveProfile(e.target.value);
|
||||||
|
setProfileName(e.target.value);
|
||||||
}}/>
|
}}/>
|
||||||
</span>
|
}
|
||||||
<span className="inputSpan">
|
{nameProfile? <button title="Save" style={{width:"35px", height:"35px", marginLeft:"5px"}} onClick={() => {
|
||||||
<label>Width</label>
|
let profileExists = false;
|
||||||
<input id="widthInput" type="number" value={width.toString()} min="1" step="1" onChange={e => {
|
getProfiles().forEach(profile => {
|
||||||
if(e.target.value.trim().length !== 0) setWidth(parseInt(e.target.value));
|
if (profile === newProfileName) {
|
||||||
}}/>
|
profileExists = true;
|
||||||
</span>
|
}
|
||||||
<span className="inputSpan">
|
});
|
||||||
<label>Stick Distance</label>
|
|
||||||
<input id="stickDistanceInput" type="number" value={stickDistance.toString()} min="0" step="1" onChange={e => {
|
if (profileExists) {
|
||||||
if(e.target.value.trim().length !== 0) setStickDistance(parseInt(e.target.value));
|
logger.warningMSG("Profile with the name \""+newProfileName+"\" already exists");
|
||||||
}}/>
|
} else {
|
||||||
</span>
|
setProfileName(newProfileName);
|
||||||
|
if(newProfileType === "rename") {
|
||||||
|
editProfile({profileName: newProfileName});
|
||||||
|
} else if (newProfileType === "create") {
|
||||||
|
createProfile(newProfileName, false);
|
||||||
|
} else if (newProfileType === "duplicate") {
|
||||||
|
createProfile(newProfileName, true);
|
||||||
|
}
|
||||||
|
setActiveProfile(newProfileName);
|
||||||
|
}
|
||||||
|
setProfileOptions(getProfiles().map(p => {
|
||||||
|
return <option key={p} value={p}>{p}</option>;
|
||||||
|
}));
|
||||||
|
setNameProfile(false);
|
||||||
|
}}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" style={{fill:"white"}}>
|
||||||
|
{/* <!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --> */}
|
||||||
|
<path d="M224 256c-35.2 0-64 28.8-64 64c0 35.2 28.8 64 64 64c35.2 0 64-28.8 64-64C288 284.8 259.2 256 224 256zM433.1 129.1l-83.9-83.9C341.1 37.06 328.8 32 316.1 32H64C28.65 32 0 60.65 0 96v320c0 35.35 28.65 64 64 64h320c35.35 0 64-28.65 64-64V163.9C448 151.2 442.9 138.9 433.1 129.1zM128 80h144V160H128V80zM400 416c0 8.836-7.164 16-16 16H64c-8.836 0-16-7.164-16-16V96c0-8.838 7.164-16 16-16h16v104c0 13.25 10.75 24 24 24h192C309.3 208 320 197.3 320 184V83.88l78.25 78.25C399.4 163.2 400 164.8 400 166.3V416z"/>
|
||||||
|
</svg>
|
||||||
|
</button> : <ProfileSettings setNameProfile={setNameProfile} setProfileName={setProfileName} setNewProfileName={setNewProfileName} profileName={profileName} setNewProfileType={setNewProfileType} setProfileOptions={setProfileOptions}/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div style={{
|
||||||
|
display: "flex",
|
||||||
|
}}>
|
||||||
|
<button title="Import" style={{width:"35px", height:"35px"}} onClick={() => {
|
||||||
|
importProfile(() => {
|
||||||
|
setProfileOptions(getProfiles().map(p => {
|
||||||
|
return <option key={p} value={p}>{p}</option>;
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style={{fill:"white"}}>
|
||||||
|
{/* <!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --> */}
|
||||||
|
<path d="M128 64c0-35.3 28.7-64 64-64H352V128c0 17.7 14.3 32 32 32H512V448c0 35.3-28.7 64-64 64H192c-35.3 0-64-28.7-64-64V336H302.1l-39 39c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l80-80c9.4-9.4 9.4-24.6 0-33.9l-80-80c-9.4-9.4-24.6-9.4-33.9 0s-9.4 24.6 0 33.9l39 39H128V64zm0 224v48H24c-13.3 0-24-10.7-24-24s10.7-24 24-24H128zM512 128H384V0L512 128z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button title="Export" style={{width:"35px", height:"35px", marginLeft:"5px"}} onClick={() => {
|
||||||
|
exportProfile();
|
||||||
|
}}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" style={{fill:"white"}}>
|
||||||
|
{/* <!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --> */}
|
||||||
|
<path d="M32 64C32 28.7 60.7 0 96 0H256V128c0 17.7 14.3 32 32 32H416V288H248c-13.3 0-24 10.7-24 24s10.7 24 24 24H416V448c0 35.3-28.7 64-64 64H96c-35.3 0-64-28.7-64-64V64zM416 336V288H526.1l-39-39c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l80 80c9.4 9.4 9.4 24.6 0 33.9l-80 80c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l39-39H416zm0-208H288V0L416 128z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="settingRow">
|
<div id="settingRow">
|
||||||
<div className="dataDiv">
|
{<InputSpan name={"FPS"} value={fps} min={1} step={1} onChange={
|
||||||
<p>Stick Mode</p>
|
e => {
|
||||||
<label htmlFor="stickMode" className="toggle-switchy" data-style="rounded" data-text="12">
|
if(e.target.value.trim().length !== 0) setFps(parseInt(e.target.value));
|
||||||
<input checked={stickMode2} type="checkbox" id="stickMode" onChange={e => {
|
}
|
||||||
setStickMode2(e.target.checked);
|
} onReset={() => {
|
||||||
}}/>
|
ProfileLoadDefault({fps: true});
|
||||||
<span className="toggle">
|
setFps(getActiveProfile().fps);
|
||||||
<span className="switch"></span>
|
}}/>}
|
||||||
</span>
|
{<InputSpan name="Width" value={width} min={1} step={1} onChange={
|
||||||
</label>
|
e => {
|
||||||
|
if(e.target.value.trim().length !== 0) setWidth(parseInt(e.target.value));
|
||||||
|
}
|
||||||
|
} onReset={() => {
|
||||||
|
ProfileLoadDefault({width: true});
|
||||||
|
setWidth(getActiveProfile().width);
|
||||||
|
}}/>}
|
||||||
|
{<InputSpan name="Stick Distance" value={stickDistance} min={0} step={1} onChange={
|
||||||
|
e => {
|
||||||
|
if(e.target.value.trim().length !== 0) setStickDistance(parseInt(e.target.value));
|
||||||
|
}
|
||||||
|
} onReset={() => {
|
||||||
|
ProfileLoadDefault({stickDistance: true});
|
||||||
|
setStickDistance(getActiveProfile().stickDistance);
|
||||||
|
}}/>}
|
||||||
</div>
|
</div>
|
||||||
<span className="selectSpan">
|
<div id="settingRow">
|
||||||
<label className="selectSpanLabel">Format</label>
|
{<ToggleSpan name="Stick Mode" state={stickMode2} checkedValue={"2"} uncheckedValue={"1"} onChange={checked => {
|
||||||
<label className="selectSpanSelect" htmlFor="slct">
|
setStickMode2(checked);
|
||||||
<select id="slct" required={true} value={videoFormat} onChange={e => {
|
}} onReset={() => {
|
||||||
setVideoFormat(e.target.value as unknown as VideoFormat);
|
ProfileLoadDefault({stickMode2: true});
|
||||||
|
setStickMode2(getActiveProfile().stickMode2);
|
||||||
|
}}/>}
|
||||||
|
<div style={{
|
||||||
|
display: "flex",
|
||||||
}}>
|
}}>
|
||||||
{VideoFormatOptions}
|
{<SelectSpan name="Format" value={videoFormat} optiones={VideoFormatOptions} onChange={ e => {
|
||||||
</select>
|
setVideoFormat(e.target.value as unknown as VideoFormat);
|
||||||
</label>
|
}} onReset={() => {
|
||||||
|
ProfileLoadDefault({videoFormat: true});
|
||||||
|
setVideoFormat(getActiveProfile().videoFormat);
|
||||||
|
}}/>}
|
||||||
{videoFormat === VideoFormat.mov? <VideoFormatWarning videoFormat={videoFormat}/> : null}
|
{videoFormat === VideoFormat.mov? <VideoFormatWarning videoFormat={videoFormat}/> : null}
|
||||||
{videoFormat === VideoFormat.mp4? <VideoFormatWarning videoFormat={videoFormat}/> : null}
|
{videoFormat === VideoFormat.mp4? <VideoFormatWarning videoFormat={videoFormat}/> : null}
|
||||||
{videoFormat === VideoFormat.avi? <VideoFormatWarning videoFormat={videoFormat}/> : null}
|
{videoFormat === VideoFormat.avi? <VideoFormatWarning videoFormat={videoFormat}/> : null}
|
||||||
</span>
|
{videoFormat === VideoFormat.mkv? <VideoFormatWarning videoFormat={videoFormat}/> : null}
|
||||||
|
</div>
|
||||||
<button id="resetSettingsButton" onClick={() => {
|
<button id="resetSettingsButton" onClick={() => {
|
||||||
settingListLoadDefault();
|
ProfileLoadDefault({all: true});
|
||||||
|
|
||||||
setFps(settingList.fps);
|
setFps(getActiveProfile().fps);
|
||||||
setWidth(settingList.width);
|
setWidth(getActiveProfile().width);
|
||||||
setStickDistance(settingList.stickDistance);
|
setStickDistance(getActiveProfile().stickDistance);
|
||||||
setStickMode2(settingList.stickMode2);
|
setStickMode2(getActiveProfile().stickMode2);
|
||||||
setVideoFormat(settingList.videoFormat);
|
setVideoFormat(getActiveProfile().videoFormat);
|
||||||
}}>Reset Settings</button>
|
}}>Reset Profile</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="renderImgDiv">
|
<div id="renderImgDiv">
|
||||||
<img id="render-ex" src={renderImg}></img>
|
<img id="render-ex" src={renderImg}></img>
|
||||||
|
|||||||
108
src/index.css
108
src/index.css
@@ -1,3 +1,12 @@
|
|||||||
|
body {
|
||||||
|
background-color: #172336;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.dataDiv {
|
.dataDiv {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
@@ -249,16 +258,18 @@ header h1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#render-ex {
|
#render-ex {
|
||||||
width: 100%;
|
width: auto;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
max-width: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
#renderImgDiv {
|
#renderImgDiv {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
width: 100%;
|
|
||||||
image-rendering: pixelated;
|
image-rendering: pixelated;
|
||||||
|
width: fit-content;
|
||||||
|
height: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.noMarginBottom {
|
.noMarginBottom {
|
||||||
@@ -310,38 +321,64 @@ button:hover {
|
|||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputSpan {
|
.inputSelectSpan {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-bottom: 15px;
|
height: 35px;
|
||||||
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
.inputSpan input {
|
.inputSelectSpan input {
|
||||||
width: 75px;
|
width: 75px;
|
||||||
padding: 8px 0;
|
height: 100%;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-top-right-radius: 18px;
|
border-top-right-radius: 18px;
|
||||||
border-bottom-right-radius: 18px;
|
border-bottom-right-radius: 18px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: large;
|
font-size: large;
|
||||||
line-height: 0px;
|
padding: 0;
|
||||||
}
|
}
|
||||||
.inputSpan label {
|
.inputSelectSpan label {
|
||||||
padding: 10px 10px;
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
height: 100%;
|
||||||
background: #00c24a;
|
background: #00c24a;
|
||||||
border-top-left-radius: 18px;
|
border-top-left-radius: 18px;
|
||||||
border-bottom-left-radius: 18px;
|
border-bottom-left-radius: 18px;
|
||||||
font-weight: bolder;
|
font-weight: bolder;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.inputSelectSpan select {
|
||||||
|
height: 100%;
|
||||||
|
border: 0;
|
||||||
|
border-top-right-radius: 18px;
|
||||||
|
border-bottom-right-radius: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
font-size: large;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0,0,0,0.8);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#settingRow {
|
#settingRow {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#resetSettingsButton {
|
#resetSettingsButton {
|
||||||
height: 35px;
|
height: 35px;
|
||||||
|
background-color: #e1334e;
|
||||||
}
|
}
|
||||||
|
|
||||||
#logList-Name:hover {
|
#logList-Name:hover {
|
||||||
@@ -410,22 +447,39 @@ button:hover {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.selectSpanSelect select {
|
|
||||||
padding: 8px 0;
|
|
||||||
border: 0;
|
|
||||||
border-top-right-radius: 18px;
|
|
||||||
border-bottom-right-radius: 18px;
|
|
||||||
cursor: pointer;
|
|
||||||
text-align: center;
|
|
||||||
font-size: large;
|
|
||||||
}
|
|
||||||
.selectSpanLabel {
|
|
||||||
padding: 10px 10px;
|
|
||||||
background: #00c24a;
|
|
||||||
border-top-left-radius: 18px;
|
|
||||||
border-bottom-left-radius: 18px;
|
|
||||||
font-weight: bolder;
|
|
||||||
}
|
|
||||||
#videoFormatWarning:hover {
|
#videoFormatWarning:hover {
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#outerTerminal {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
width: 100%;
|
||||||
|
height: 300px;
|
||||||
|
background-color: #0d131e;
|
||||||
|
color: white;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
#outerTerminal p {
|
||||||
|
margin: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#innerTerminal {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#terminalTable tr {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.terminalTableNumber {
|
||||||
|
color: #9DA8B9;
|
||||||
|
background-color: #172336;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>StickExporterTX</title>
|
<title>StickExporterTX</title>
|
||||||
</head>
|
</head>
|
||||||
<body style="background-color: #172336;margin:0;padding:0;color:white;font-family:sans-serif;">
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import RenderingPage from "./components/ui/renderingPage";
|
|||||||
import RenderFinishPage from "./components/ui/renderFinishPage";
|
import RenderFinishPage from "./components/ui/renderFinishPage";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import "./toggle-switchy.css";
|
import "./toggle-switchy.css";
|
||||||
import { startBlender } from "./components/blenderController";
|
import { blender, blenderCmd, startBlender } from "./components/blenderController";
|
||||||
import {ipcRenderer} from "electron";
|
import {ipcRenderer} from "electron";
|
||||||
|
|
||||||
enum Page {
|
enum Page {
|
||||||
@@ -40,6 +40,18 @@ function openPage(page:Page) {
|
|||||||
|
|
||||||
openPage(currentPage);
|
openPage(currentPage);
|
||||||
|
|
||||||
|
window.addEventListener("keydown", (e:KeyboardEvent) => {
|
||||||
|
if(e.key === "Escape") {
|
||||||
|
if(currentPage === Page.Main) {
|
||||||
|
ipcRenderer.send("closeApp");
|
||||||
|
} else if(currentPage === Page.Rendering) {
|
||||||
|
blender(blenderCmd.stopRendering);
|
||||||
|
} else {
|
||||||
|
openPage(Page.Main);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
startBlender();
|
startBlender();
|
||||||
|
|
||||||
function pageSetRendering(value:boolean) {
|
function pageSetRendering(value:boolean) {
|
||||||
|
|||||||
Reference in New Issue
Block a user