Skip to content

Commit 8bd89ce

Browse files
authored
Move to TypeScript (#955)
1 parent a1aed0a commit 8bd89ce

File tree

219 files changed

+12951
-8167
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

219 files changed

+12951
-8167
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ node_modules
33
/renderer/.next
44
/app/dist
55
/dist
6+
/dist-js
Lines changed: 57 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,53 @@
1-
'use strict';
1+
import {windowManager} from './windows/manager';
2+
import {setRecordingTray, disableTray, resetTray} from './tray';
3+
import {setCropperShortcutAction} from './global-accelerators';
4+
import {settings} from './common/settings';
5+
import {track} from './common/analytics';
6+
import {plugins} from './plugins';
7+
import {getAudioDevices, getSelectedInputDeviceId} from './utils/devices';
8+
import {showError} from './utils/errors';
9+
import {RecordServiceContext, RecordServiceState} from './plugins/service-context';
10+
import {setCurrentRecording, updatePluginState, stopCurrentRecording} from './recording-history';
11+
import {Recording} from './video';
12+
import {ApertureOptions, StartRecordingOptions} from './common/types';
13+
import {InstalledPlugin} from './plugins/plugin';
14+
import {RecordService, RecordServiceHook} from './plugins/service';
215

316
const createAperture = require('aperture');
4-
5-
const {openEditorWindow} = require('../editor');
6-
const {closePrefsWindow} = require('../preferences');
7-
const {setRecordingTray, disableTray, resetTray} = require('../tray');
8-
const {disableCroppers, setRecordingCroppers, closeAllCroppers} = require('../cropper');
9-
const {setCropperShortcutAction} = require('../global-accelerators');
10-
11-
// eslint-disable-next-line no-unused-vars
12-
const {convertToH264} = require('../utils/encoding');
13-
14-
const settings = require('./settings');
15-
const {track} = require('./analytics');
16-
const plugins = require('./plugins');
17-
const {getAudioDevices} = require('../utils/devices');
18-
const {showError} = require('../utils/errors');
19-
const {RecordServiceContext} = require('../service-context');
20-
const {setCurrentRecording, updatePluginState, stopCurrentRecording} = require('../recording-history');
21-
2217
const aperture = createAperture();
23-
const {videoCodecs} = createAperture;
24-
25-
// eslint-disable-next-line no-unused-vars
26-
const recordHevc = videoCodecs.has('hevc');
2718

28-
let lastUsedSettings;
29-
let recordingPlugins = [];
30-
const serviceState = new Map();
31-
let apertureOptions;
32-
let recordingName;
33-
let past;
19+
let recordingPlugins: Array<{plugin: InstalledPlugin; service: RecordService}> = [];
20+
const serviceState = new Map<string, RecordServiceState>();
21+
let apertureOptions: ApertureOptions;
22+
let recordingName: string | undefined;
23+
let past: number | undefined;
3424

35-
const setRecordingName = name => {
25+
const setRecordingName = (name: string) => {
3626
recordingName = name;
3727
};
3828

3929
const serializeEditPluginState = () => {
40-
const result = {};
30+
const result: Record<string, Record<string, Record<string, unknown> | undefined>> = {};
4131

4232
for (const {plugin, service} of recordingPlugins) {
4333
if (!result[plugin.name]) {
4434
result[plugin.name] = {};
4535
}
4636

47-
result[plugin.name][service.title] = serviceState.get(service.title).persistedState;
37+
result[plugin.name][service.title] = serviceState.get(service.title)?.persistedState;
4838
}
4939

5040
return result;
5141
};
5242

53-
const callPlugins = async method => Promise.all(recordingPlugins.map(async ({plugin, service}) => {
43+
const callPlugins = async (method: RecordServiceHook) => Promise.all(recordingPlugins.map(async ({plugin, service}) => {
5444
if (service[method] && typeof service[method] === 'function') {
5545
try {
56-
await service[method](
46+
await service[method]?.(
5747
new RecordServiceContext({
48+
plugin,
5849
apertureOptions,
59-
state: serviceState.get(service.title),
60-
config: plugin.config,
50+
state: serviceState.get(service.title) ?? {},
6151
setRecordingName
6252
})
6353
);
@@ -68,7 +58,7 @@ const callPlugins = async method => Promise.all(recordingPlugins.map(async ({plu
6858
}));
6959

7060
const cleanup = async () => {
71-
closeAllCroppers();
61+
windowManager.cropper?.close();
7262
resetTray();
7363

7464
await callPlugins('didStopRecording');
@@ -77,17 +67,17 @@ const cleanup = async () => {
7767
setCropperShortcutAction();
7868
};
7969

80-
const startRecording = async options => {
70+
export const startRecording = async (options: StartRecordingOptions) => {
8171
if (past) {
8272
return;
8373
}
8474

8575
past = Date.now();
8676
recordingName = undefined;
8777

88-
closePrefsWindow();
78+
windowManager.preferences?.close();
79+
windowManager.cropper?.disable();
8980
disableTray();
90-
disableCroppers();
9181

9282
const {cropperBounds, screenBounds, displayId} = options;
9383

@@ -108,19 +98,15 @@ const startRecording = async options => {
10898
screenId: displayId
10999
};
110100

111-
lastUsedSettings = {
112-
recordedFps: apertureOptions.fps
113-
};
114-
115-
if (recordAudio === true) {
101+
if (recordAudio) {
116102
// In case for some reason the default audio device is not set
117103
// use the first available device for recording
118-
const audioInputDeviceId = settings.getSelectedInputDeviceId();
104+
const audioInputDeviceId = getSelectedInputDeviceId();
119105
if (audioInputDeviceId) {
120106
apertureOptions.audioDeviceId = audioInputDeviceId;
121107
} else {
122108
const [defaultAudioDevice] = await getAudioDevices();
123-
apertureOptions.audioDeviceId = defaultAudioDevice && defaultAudioDevice.id;
109+
apertureOptions.audioDeviceId = defaultAudioDevice?.id;
124110
}
125111
}
126112

@@ -132,12 +118,15 @@ const startRecording = async options => {
132118
console.log(`Collected settings after ${(Date.now() - past) / 1000}s`);
133119

134120
recordingPlugins = plugins
135-
.getRecordingPlugins()
121+
.recordingPlugins
136122
.flatMap(
137-
plugin => plugin.recordServicesWithStatus
138-
// Make sure service is valid and enabled
139-
.filter(({title, isEnabled}) => isEnabled && plugin.config.validServices.includes(title))
140-
.map(service => ({plugin, service}))
123+
plugin => {
124+
const validServices = plugin.config.validServices;
125+
return plugin.recordServicesWithStatus
126+
// Make sure service is valid and enabled
127+
.filter(({title, isEnabled}) => isEnabled && validServices.includes(title))
128+
.map(service => ({plugin, service}));
129+
}
141130
);
142131

143132
for (const {service, plugin} of recordingPlugins) {
@@ -154,12 +143,12 @@ const startRecording = async options => {
154143
filePath,
155144
name: recordingName,
156145
apertureOptions,
157-
editPlugins: serializeEditPluginState()
146+
plugins: serializeEditPluginState()
158147
});
159148
} catch (error) {
160149
track('recording/stopped/error');
161-
showError(error, {title: 'Recording error'});
162-
past = null;
150+
showError(error, {title: 'Recording error', plugin: undefined});
151+
past = undefined;
163152
cleanup();
164153
return;
165154
}
@@ -172,18 +161,18 @@ const startRecording = async options => {
172161
}
173162

174163
console.log(`Started recording after ${startTime}s`);
175-
setRecordingCroppers();
164+
windowManager.cropper?.setRecording();
176165
setRecordingTray(stopRecording);
177166
setCropperShortcutAction(stopRecording);
178167
past = Date.now();
179168

180169
// Track aperture errors after recording has started, to avoid kap freezing if something goes wrong
181-
aperture.recorder.catch(error => {
170+
aperture.recorder.catch((error: any) => {
182171
// Make sure it doesn't catch the error of ending the recording
183172
if (past) {
184173
track('recording/stopped/error');
185-
showError(error, {title: 'Recording error'});
186-
past = null;
174+
showError(error, {title: 'Recording error', plugin: undefined});
175+
past = undefined;
187176
cleanup();
188177
}
189178
});
@@ -192,46 +181,38 @@ const startRecording = async options => {
192181
updatePluginState(serializeEditPluginState());
193182
};
194183

195-
const stopRecording = async () => {
184+
export const stopRecording = async () => {
196185
// Ensure we only stop recording once
197186
if (!past) {
198187
return;
199188
}
200189

201190
console.log(`Stopped recording after ${(Date.now() - past) / 1000}s`);
202-
past = null;
191+
past = undefined;
203192

204193
let filePath;
205194

206195
try {
207196
filePath = await aperture.stopRecording();
208197
} catch (error) {
209198
track('recording/stopped/error');
210-
showError(error, {title: 'Recording error'});
199+
showError(error, {title: 'Recording error', plugin: undefined});
211200
cleanup();
212201
return;
213202
}
214203

215-
const {recordedFps} = lastUsedSettings;
216-
217204
try {
218205
cleanup();
219206
} finally {
220207
track('editor/opened/recording');
221208

222-
// TODO: bring this back when we figure out how to convert hevc files
223-
// if (recordHevc) {
224-
// openEditorWindow(await convertToH264(filePath), {recordedFps, isNewRecording: true, originalFilePath: filePath});
225-
// } else {
226-
await openEditorWindow(filePath, {recordedFps, isNewRecording: true, recordingName});
227-
// }
209+
const recording = new Recording({
210+
filePath,
211+
title: recordingName,
212+
apertureOptions
213+
});
214+
await recording.openEditorWindow();
228215

229216
stopCurrentRecording(recordingName);
230217
}
231218
};
232-
233-
module.exports = {
234-
startRecording,
235-
stopRecording,
236-
getAudioDevices
237-
};
Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
'use strict';
2-
31
// The goal of this file is validating accelerator values we receive from the user
42
// to make sure that they are can be used with the electron api https://www.electronjs.org/docs/api/accelerator
53

@@ -46,9 +44,9 @@ const codes = [
4644
'numsub',
4745
'nummult',
4846
'numdiv'
49-
];
47+
] as const;
5048

51-
const keyCodeRegex = new RegExp('^([\\dA-Z~`!@#$%^&*()_+=.,<>?;:\'"\\-\\/\\\\\\[\\]\\{\\}\\|]|F([1-9]|1[\\d]|2[0-4])|' + codes.join('|') + ')$');
49+
const getKeyCodeRegex = () => new RegExp('^([\\dA-Z~`!@#$%^&*()_+=.,<>?;:\'"\\-\\/\\\\\\[\\]\\{\\}\\|]|F([1-9]|1[\\d]|2[0-4])|' + codes.join('|') + ')$');
5250

5351
const shiftKeyMap = new Map([
5452
['~', '`'],
@@ -102,7 +100,7 @@ const namedKeyCodeMap = new Map([
102100
['Clear', 'Numlock']
103101
]);
104102

105-
const checkAccelerator = accelerator => {
103+
export const checkAccelerator = (accelerator: string) => {
106104
if (!accelerator) {
107105
return true;
108106
}
@@ -113,23 +111,18 @@ const checkAccelerator = accelerator => {
113111
return false;
114112
}
115113

116-
if (!keyCodeRegex.test(parts[parts.length - 1])) {
114+
if (!getKeyCodeRegex().test(parts[parts.length - 1])) {
117115
return false;
118116
}
119117

120118
const metaKeys = parts.slice(0, -1);
121119
return metaKeys.every(part => modifiers.includes(part)) && metaKeys.some(part => part !== 'Shift');
122120
};
123121

124-
const eventKeyToAccelerator = (key, location) => {
122+
export const eventKeyToAccelerator = (key: string, location: number) => {
125123
if (location === 3) {
126124
return numpadKeyMap.get(key);
127125
}
128126

129-
return namedKeyCodeMap.get(key) || shiftKeyMap.get(key) || key.toUpperCase();
130-
};
131-
132-
module.exports = {
133-
checkAccelerator,
134-
eventKeyToAccelerator
127+
return namedKeyCodeMap.get(key) ?? shiftKeyMap.get(key) ?? key.toUpperCase();
135128
};
Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
'use strict';
22

3-
const util = require('electron-util');
3+
import util from 'electron-util';
4+
import {parse} from 'semver';
5+
import {settings} from './settings';
6+
47
const Insight = require('insight');
5-
const {parse} = require('semver');
68
const pkg = require('../../package');
7-
const settings = require('./settings');
89

910
const trackingCode = 'UA-84705099-2';
1011
const insight = new Insight({trackingCode, pkg});
1112
const version = parse(pkg.version);
1213

13-
const track = (...paths) => {
14+
export const track = (...paths: string[]) => {
1415
const allowAnalytics = settings.get('allowAnalytics');
1516

1617
if (allowAnalytics) {
17-
console.log('Tracking', `v${version.major}.${version.minor}`, ...paths);
18-
insight.track(`v${version.major}.${version.minor}`, ...paths);
18+
console.log('Tracking', `v${version?.major}.${version?.minor}`, ...paths);
19+
insight.track(`v${version?.major}.${version?.minor}`, ...paths);
1920
}
2021
};
2122

22-
const initializeAnalytics = () => {
23+
export const initializeAnalytics = () => {
2324
if (util.isFirstAppLaunch()) {
2425
insight.track('install');
2526
}
@@ -29,8 +30,3 @@ const initializeAnalytics = () => {
2930
settings.set('version', pkg.version);
3031
}
3132
};
32-
33-
module.exports = {
34-
initializeAnalytics,
35-
track
36-
};

main/common/constants.js

Lines changed: 0 additions & 15 deletions
This file was deleted.

main/common/constants.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import {Format} from './types';
2+
3+
export const supportedVideoExtensions = ['mp4', 'mov', 'm4v'];
4+
5+
const formatExtensions = new Map([
6+
['av1', 'mp4']
7+
]);
8+
9+
export const formats = [Format.mp4, Format.av1, Format.gif, Format.apng, Format.webm];
10+
11+
export const getFormatExtension = (format: Format) => formatExtensions.get(format) ?? format;
12+
13+
export const defaultInputDeviceId = 'SYSTEM_DEFAULT';
14+

0 commit comments

Comments
 (0)