Skip to content

Commit 7a0b209

Browse files
Moved over to Electron Builder
1 parent 6676ea2 commit 7a0b209

19 files changed

+1167
-146
lines changed

package-lock.json

Lines changed: 36 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"description": "A foundation for scalable desktop apps",
2+
"description": "An application for creating and managing clips for What the Dub and Rifftrax",
33
"keywords": [
44
"electron",
55
"boilerplate",
@@ -21,17 +21,10 @@
2121
},
2222
"license": "MIT",
2323
"author": {
24-
"name": "Electron React Boilerplate Maintainers",
25-
"email": "electronreactboilerplate@gmail.com",
26-
"url": "https://electron-react-boilerplate.js.org"
24+
"name": "Michael C. Main",
25+
"email": "deusprogrammer@gmail.com",
26+
"url": "https://deusprogrammer.com/util/wtd-tool"
2727
},
28-
"contributors": [
29-
{
30-
"name": "Amila Welihinda",
31-
"email": "[email protected]",
32-
"url": "https://github.com/amilajack"
33-
}
34-
],
3528
"main": "./src/main/main.ts",
3629
"scripts": {
3730
"build": "concurrently \"npm run build:main\" \"npm run build:renderer\"",
@@ -113,7 +106,8 @@
113106
"electron-updater": "^5.0.1",
114107
"react": "^18.1.0",
115108
"react-dom": "^18.1.0",
116-
"react-router-dom": "^6.3.0"
109+
"react-router-dom": "^6.3.0",
110+
"react-toastify": "^9.0.1"
117111
},
118112
"devDependencies": {
119113
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.6",
@@ -183,8 +177,8 @@
183177
"webpack-merge": "^5.8.0"
184178
},
185179
"build": {
186-
"productName": "ElectronReact",
187-
"appId": "org.erb.ElectronReact",
180+
"productName": "Dub Editor",
181+
"appId": "com.trinary.dub-editor",
188182
"asar": true,
189183
"asarUnpack": "**\\*.{node,dll}",
190184
"files": [

src/main/config.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"whatTheDubDirectory": "~/Library/Application Support/Steam/steamapps/common/WhatTheDub/WhatTheDub.app/Contents/Resources/Data",
3+
"rifftraxDirectory": "~/Library/Application Support/Steam/steamapps/common/RiffTraxTheGame/RiffTraxTheGame.app/Contents/Resources/Data",
4+
"isMac": true
5+
}

src/main/defaultConfig.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
type Config = {
2+
whatTheDubDirectory: string | null,
3+
rifftraxDirectory: string | null,
4+
isMac: boolean
5+
}
6+
7+
const defaultConfig : Config = {
8+
whatTheDubDirectory: null,
9+
rifftraxDirectory: null,
10+
isMac: false
11+
}
12+
13+
export default defaultConfig;

src/main/main.ts

Lines changed: 158 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,18 @@
99
* `./src/main.js` using webpack. This gives us some performance wins.
1010
*/
1111
import path from 'path';
12-
import { app, BrowserWindow, shell, ipcMain } from 'electron';
12+
import fs from 'fs';
13+
import { app, BrowserWindow, dialog, shell, ipcMain } from 'electron';
1314
import { autoUpdater } from 'electron-updater';
1415
import log from 'electron-log';
1516
import MenuBuilder from './menu';
1617
import { resolveHtmlPath } from './util';
1718

19+
import defaultConfig from './defaultConfig';
20+
21+
const HOME : string = process.env.HOME || "/";
22+
const CONFIG_FILE : string = `${HOME}/.dub-editor-config.json`;
23+
1824
export default class AppUpdater {
1925
constructor() {
2026
log.transports.file.level = 'info';
@@ -25,11 +31,18 @@ export default class AppUpdater {
2531

2632
let mainWindow: BrowserWindow | null = null;
2733

28-
ipcMain.on('ipc-example', async (event, arg) => {
29-
const msgTemplate = (pingPong: string) => `IPC test: ${pingPong}`;
30-
console.log(msgTemplate(arg));
31-
event.reply('ipc-example', msgTemplate('pong'));
32-
});
34+
let config = defaultConfig;
35+
if (process.platform === 'darwin') {
36+
config.whatTheDubDirectory = "~/Library/Application Support/Steam/steamapps/common/WhatTheDub/WhatTheDub.app/Contents/Resources/Data";
37+
config.rifftraxDirectory = "~/Library/Application Support/Steam/steamapps/common/RiffTraxTheGame/RiffTraxTheGame.app/Contents/Resources/Data";
38+
config.isMac = true;
39+
}
40+
41+
if (!fs.existsSync(CONFIG_FILE)) {
42+
fs.writeFileSync(CONFIG_FILE, Buffer.from(JSON.stringify(config, null, 5)));
43+
} else {
44+
config = JSON.parse(fs.readFileSync(CONFIG_FILE, {}).toString());
45+
}
3346

3447
if (process.env.NODE_ENV === 'production') {
3548
const sourceMapSupport = require('source-map-support');
@@ -71,8 +84,8 @@ const createWindow = async () => {
7184

7285
mainWindow = new BrowserWindow({
7386
show: false,
74-
width: 1024,
75-
height: 728,
87+
width: 1920,
88+
height: 1080,
7689
icon: getAssetPath('icon.png'),
7790
webPreferences: {
7891
preload: app.isPackaged
@@ -135,3 +148,140 @@ app
135148
});
136149
})
137150
.catch(console.log);
151+
152+
// Bridged functionality
153+
154+
ipcMain.handle('updateConfig', (event, newConfig) => {
155+
console.log("CONFIG: " + JSON.stringify(newConfig));
156+
config = newConfig;
157+
fs.writeFileSync(CONFIG_FILE, Buffer.from(JSON.stringify(config, null, 5)));
158+
return;
159+
});
160+
161+
ipcMain.handle('getConfig', () => {
162+
return config;
163+
});
164+
165+
ipcMain.handle('getVideos', (event, game) => {
166+
let clipsDirectory = null;
167+
if (game === "rifftrax") {
168+
clipsDirectory = `${config.rifftraxDirectory}/StreamingAssets/VideoClips`.replace("~", HOME);
169+
} else if (game === "whatthedub") {
170+
clipsDirectory = `${config.whatTheDubDirectory}/StreamingAssets/VideoClips`.replace("~", HOME);
171+
} else {
172+
return [];
173+
}
174+
175+
const files = fs.readdirSync(clipsDirectory);
176+
const fileObjects = files.filter(file => file.endsWith(".mp4") || file.endsWith(".mp4.disabled")).map((file) => {return {_id: file.substring(0, file.lastIndexOf(".mp4")), name: file.replace(/_/g, " ").substring(0, file.lastIndexOf(".mp4")), game, disabled: file.endsWith(".disabled")}});
177+
return fileObjects;
178+
});
179+
180+
ipcMain.handle('getVideo', (event, {id, game}) => {
181+
console.log("Opening: " + id + " from game " + game);
182+
183+
let directory = null;
184+
if (game === "rifftrax") {
185+
directory = config.rifftraxDirectory;
186+
} else if (game === "whatthedub") {
187+
directory = config.whatTheDubDirectory;
188+
} else {
189+
return [];
190+
}
191+
192+
const clipsDirectory = `${directory}/StreamingAssets/VideoClips`.replace("~", HOME);
193+
const subsDirectory = `${directory}/StreamingAssets/Subtitles`.replace("~", HOME);
194+
const videoFilePath = `${clipsDirectory}/${id}.mp4`;
195+
const subFilePath = `${subsDirectory}/${id}.srt`;
196+
197+
const videoBase64 = fs.readFileSync(videoFilePath, {encoding: 'base64'});
198+
const subtitles = fs.readFileSync(subFilePath, {encoding: 'base64'});
199+
200+
return {
201+
name: id.replace(/_/g, " "),
202+
videoUrl: `data:video/mp4;base64,${videoBase64}`,
203+
subtitles: [],
204+
srtBase64: subtitles
205+
}
206+
});
207+
208+
ipcMain.handle('storeVideo', (event, {base64ByteStream, subtitles, title, clipNumber, game}) => {
209+
console.log(`STORING ${title}-${clipNumber} for game ${game} with subtitles ${subtitles}`);
210+
211+
let directory = null;
212+
if (game === "rifftrax") {
213+
directory = config.rifftraxDirectory;
214+
} else if (game === "whatthedub") {
215+
directory = config.whatTheDubDirectory;
216+
} else {
217+
return;
218+
}
219+
220+
let baseFileName = title.replace(" ", "_") + `-Clip${`${clipNumber}`.padStart(3, "0")}`;
221+
222+
const clipsDirectory = `${directory}/StreamingAssets/VideoClips`.replace("~", HOME);
223+
const subsDirectory = `${directory}/StreamingAssets/Subtitles`.replace("~", HOME);
224+
const videoFilePath = `${clipsDirectory}/_${baseFileName}.mp4`;
225+
const subFilePath = `${subsDirectory}/_${baseFileName}.srt`;
226+
227+
console.log("SAVING TO " + videoFilePath + "\n" + subFilePath);
228+
229+
fs.writeFileSync(videoFilePath, Buffer.from(base64ByteStream, "base64"));
230+
fs.writeFileSync(subFilePath, subtitles);
231+
});
232+
233+
ipcMain.handle('deleteVideo', (event, {id, game}) => {
234+
console.log("DELETING " + id + " FOR GAME " + game);
235+
236+
let directory = null;
237+
if (game === "rifftrax") {
238+
directory = config.rifftraxDirectory;
239+
} else if (game === "whatthedub") {
240+
directory = config.whatTheDubDirectory;
241+
} else {
242+
return;
243+
}
244+
const clipsDirectory = `${directory}/StreamingAssets/VideoClips`.replace("~", HOME);
245+
const subsDirectory = `${directory}/StreamingAssets/Subtitles`.replace("~", HOME);
246+
const videoFilePath = `${clipsDirectory}/${id}.mp4`;
247+
const subFilePath = `${subsDirectory}/${id}.srt`;
248+
249+
console.log("DELETING " + videoFilePath + "\n" + subFilePath);
250+
251+
fs.unlinkSync(videoFilePath);
252+
fs.unlinkSync(subFilePath);
253+
});
254+
255+
ipcMain.handle('openDialog', async () => {
256+
const response = await dialog.showOpenDialog({properties: ['openDirectory', 'createDirectory'] });
257+
if (!response.canceled) {
258+
return response.filePaths[0];
259+
} else {
260+
return null;
261+
}
262+
});
263+
264+
ipcMain.handle('setActive', async (event, {id, game, isActive}) => {
265+
console.log("TOGGLING " + id + " in game " + game + " to " + isActive);
266+
267+
let directory = null;
268+
if (game === "rifftrax") {
269+
directory = config.rifftraxDirectory;
270+
} else if (game === "whatthedub") {
271+
directory = config.whatTheDubDirectory;
272+
} else {
273+
return;
274+
}
275+
const clipsDirectory = `${directory}/StreamingAssets/VideoClips`.replace("~", HOME);
276+
const subsDirectory = `${directory}/StreamingAssets/Subtitles`.replace("~", HOME);
277+
const videoFilePath = `${clipsDirectory}/${id}.mp4`;
278+
const subFilePath = `${subsDirectory}/${id}.srt`;
279+
280+
if(isActive) {
281+
fs.renameSync(`${videoFilePath}.disabled`, videoFilePath);
282+
fs.renameSync(`${subFilePath}.disabled`, subFilePath);
283+
} else {
284+
fs.renameSync(videoFilePath, `${videoFilePath}.disabled`);
285+
fs.renameSync(subFilePath, `${subFilePath}.disabled`);
286+
}
287+
});

src/main/preload.ts

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
1-
import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron';
1+
import { contextBridge, ipcRenderer } from 'electron';
22

33
export type Channels = 'ipc-example';
44

5-
contextBridge.exposeInMainWorld('electron', {
6-
ipcRenderer: {
7-
sendMessage(channel: Channels, args: unknown[]) {
8-
ipcRenderer.send(channel, args);
9-
},
10-
on(channel: Channels, func: (...args: unknown[]) => void) {
11-
const subscription = (_event: IpcRendererEvent, ...args: unknown[]) =>
12-
func(...args);
13-
ipcRenderer.on(channel, subscription);
14-
15-
return () => ipcRenderer.removeListener(channel, subscription);
16-
},
17-
once(channel: Channels, func: (...args: unknown[]) => void) {
18-
ipcRenderer.once(channel, (_event, ...args) => func(...args));
19-
},
20-
},
5+
contextBridge.exposeInMainWorld('api', {
6+
send: async (channel: string, args: any) => {
7+
// whitelist channels
8+
let validChannels = [
9+
"updateConfig",
10+
"getConfig",
11+
"getVideos",
12+
"getVideo",
13+
"storeVideo",
14+
"setActive",
15+
"deleteVideo",
16+
"openDialog"
17+
];
18+
if (validChannels.includes(channel)) {
19+
return await ipcRenderer.invoke(channel, args);
20+
} else {
21+
throw `Invalid channel: ${channel}`;
22+
}
23+
}
2124
});

0 commit comments

Comments
 (0)