WIP: Fix builds
This commit is contained in:
parent
18586644e1
commit
5f8adf787d
|
@ -23,14 +23,11 @@ jobs:
|
||||||
yarn
|
yarn
|
||||||
yarn devInstall
|
yarn devInstall
|
||||||
yarn compile
|
yarn compile
|
||||||
|
yarn build
|
||||||
|
|
||||||
- name: Build/release Electron app
|
- name: Release
|
||||||
uses: samuelmeuli/action-electron-builder@v1
|
uses: softprops/action-gh-release@v1
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
with:
|
with:
|
||||||
# GitHub token, automatically provided to the action
|
files:
|
||||||
# (No need to define this secret in the repo settings)
|
- 'builds/*'
|
||||||
github_token: ${{ secrets.github_token }}
|
|
||||||
|
|
||||||
# If the commit is tagged with a version (e.g. "v1.0.0"),
|
|
||||||
# release the app after building
|
|
||||||
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
|
42
.travis.yml
42
.travis.yml
|
@ -1,42 +0,0 @@
|
||||||
language: node_js
|
|
||||||
node_js: "12"
|
|
||||||
before_install:
|
|
||||||
- npm i
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
include:
|
|
||||||
- stage: Linux & Mac Build
|
|
||||||
os: osx
|
|
||||||
osx_image: xcode10.2
|
|
||||||
env:
|
|
||||||
- ELECTRON_CACHE=$HOME/.cache/electron
|
|
||||||
- ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
|
|
||||||
before_cache:
|
|
||||||
- rm -rf $ELECTRON_BUILDER_CACHE/wine
|
|
||||||
script:
|
|
||||||
- npm run devInstall
|
|
||||||
- npm run build
|
|
||||||
# - stage: Windows Build
|
|
||||||
# os: windows
|
|
||||||
# script:
|
|
||||||
# - export NPM_CONFIG_PREFIX=C:\\npm_prefix
|
|
||||||
# - export PATH="/c/npm_prefix:$PATH"
|
|
||||||
# - npm i -g npm@latest
|
|
||||||
# - npm run devInstall
|
|
||||||
# - npm run build
|
|
||||||
# - stage: GitHub Release
|
|
||||||
# script:
|
|
||||||
# - export TRAVIS_TAG=${TRAVIS_TAG:-$(date +'%Y%m%d%H%M%S')-$(git log --format=%h -1)}
|
|
||||||
# - git tag $TRAVIS_TAG
|
|
||||||
# deploy:
|
|
||||||
# provider: releases
|
|
||||||
# prerelease: true
|
|
||||||
# api_key: "$GH_TOKEN"
|
|
||||||
# cleanup: false
|
|
||||||
# file:
|
|
||||||
# - builds/lightcord-win32-ia32.zip
|
|
||||||
# - builds/lightcord-win32.exe
|
|
||||||
# - builds/lightcord-linux-x64.zip
|
|
||||||
# - builds/lightcord-darwin.zip
|
|
||||||
# on:
|
|
||||||
# tags: true
|
|
|
@ -80,271 +80,270 @@ if (process.arch === 'arm64') {
|
||||||
module.exports = {
|
module.exports = {
|
||||||
consoleLog = (...args) => {},
|
consoleLog = (...args) => {},
|
||||||
}
|
}
|
||||||
return;
|
} else {
|
||||||
}
|
features.declareSupported('voice_panning');
|
||||||
|
features.declareSupported('voice_multiple_connections');
|
||||||
|
features.declareSupported('media_devices');
|
||||||
|
features.declareSupported('media_video');
|
||||||
|
features.declareSupported('debug_logging');
|
||||||
|
features.declareSupported('set_audio_device_by_id');
|
||||||
|
features.declareSupported('set_video_device_by_id');
|
||||||
|
features.declareSupported('loopback');
|
||||||
|
features.declareSupported('experiment_config');
|
||||||
|
features.declareSupported('remote_locus_network_control');
|
||||||
|
features.declareSupported('connection_replay');
|
||||||
|
features.declareSupported('simulcast');
|
||||||
|
features.declareSupported('direct_video');
|
||||||
|
|
||||||
features.declareSupported('voice_panning');
|
if (process.platform === 'win32') {
|
||||||
features.declareSupported('voice_multiple_connections');
|
features.declareSupported('voice_legacy_subsystem');
|
||||||
features.declareSupported('media_devices');
|
features.declareSupported('soundshare');
|
||||||
features.declareSupported('media_video');
|
features.declareSupported('wumpus_video');
|
||||||
features.declareSupported('debug_logging');
|
features.declareSupported('hybrid_video');
|
||||||
features.declareSupported('set_audio_device_by_id');
|
features.declareSupported('elevated_hook');
|
||||||
features.declareSupported('set_video_device_by_id');
|
features.declareSupported('soundshare_loopback');
|
||||||
features.declareSupported('loopback');
|
features.declareSupported('screen_previews');
|
||||||
features.declareSupported('experiment_config');
|
features.declareSupported('window_previews');
|
||||||
features.declareSupported('remote_locus_network_control');
|
features.declareSupported('audio_debug_state');
|
||||||
features.declareSupported('connection_replay');
|
features.declareSupported('video_effects');
|
||||||
features.declareSupported('simulcast');
|
// NOTE(jvass): currently there's no experimental encoders! Add this back if you
|
||||||
features.declareSupported('direct_video');
|
// add one and want to re-enable the UI for them.
|
||||||
|
// features.declareSupported('experimental_encoders');
|
||||||
if (process.platform === 'win32') {
|
|
||||||
features.declareSupported('voice_legacy_subsystem');
|
|
||||||
features.declareSupported('soundshare');
|
|
||||||
features.declareSupported('wumpus_video');
|
|
||||||
features.declareSupported('hybrid_video');
|
|
||||||
features.declareSupported('elevated_hook');
|
|
||||||
features.declareSupported('soundshare_loopback');
|
|
||||||
features.declareSupported('screen_previews');
|
|
||||||
features.declareSupported('window_previews');
|
|
||||||
features.declareSupported('audio_debug_state');
|
|
||||||
features.declareSupported('video_effects');
|
|
||||||
// NOTE(jvass): currently there's no experimental encoders! Add this back if you
|
|
||||||
// add one and want to re-enable the UI for them.
|
|
||||||
// features.declareSupported('experimental_encoders');
|
|
||||||
}
|
|
||||||
|
|
||||||
function bindConnectionInstance(instance) {
|
|
||||||
return {
|
|
||||||
destroy: () => instance.destroy(),
|
|
||||||
|
|
||||||
setTransportOptions: (options) => instance.setTransportOptions(options),
|
|
||||||
setSelfMute: (mute) => instance.setSelfMute(mute),
|
|
||||||
setSelfDeafen: (deaf) => instance.setSelfDeafen(deaf),
|
|
||||||
|
|
||||||
mergeUsers: (users) => instance.mergeUsers(users),
|
|
||||||
destroyUser: (userId) => instance.destroyUser(userId),
|
|
||||||
|
|
||||||
setLocalVolume: (userId, volume) => instance.setLocalVolume(userId, volume),
|
|
||||||
setLocalMute: (userId, mute) => instance.setLocalMute(userId, mute),
|
|
||||||
setLocalPan: (userId, left, right) => instance.setLocalPan(userId, left, right),
|
|
||||||
setDisableLocalVideo: (userId, disabled) => instance.setDisableLocalVideo(userId, disabled),
|
|
||||||
|
|
||||||
setMinimumOutputDelay: (delay) => instance.setMinimumOutputDelay(delay),
|
|
||||||
getEncryptionModes: (callback) => instance.getEncryptionModes(callback),
|
|
||||||
configureConnectionRetries: (baseDelay, maxDelay, maxAttempts) =>
|
|
||||||
instance.configureConnectionRetries(baseDelay, maxDelay, maxAttempts),
|
|
||||||
setOnSpeakingCallback: (callback) => instance.setOnSpeakingCallback(callback),
|
|
||||||
setOnSpeakingWhileMutedCallback: (callback) => instance.setOnSpeakingWhileMutedCallback(callback),
|
|
||||||
setPingInterval: (interval) => instance.setPingInterval(interval),
|
|
||||||
setPingCallback: (callback) => instance.setPingCallback(callback),
|
|
||||||
setPingTimeoutCallback: (callback) => instance.setPingTimeoutCallback(callback),
|
|
||||||
setRemoteUserSpeakingStatus: (userId, speaking) => instance.setRemoteUserSpeakingStatus(userId, speaking),
|
|
||||||
setRemoteUserCanHavePriority: (userId, canHavePriority) =>
|
|
||||||
instance.setRemoteUserCanHavePriority(userId, canHavePriority),
|
|
||||||
|
|
||||||
setOnVideoCallback: (callback) => instance.setOnVideoCallback(callback),
|
|
||||||
setVideoBroadcast: (broadcasting) => instance.setVideoBroadcast(broadcasting),
|
|
||||||
setDesktopSource: (id, videoHook, type) => instance.setDesktopSource(id, videoHook, type),
|
|
||||||
setDesktopSourceStatusCallback: (callback) => instance.setDesktopSourceStatusCallback(callback),
|
|
||||||
setOnDesktopSourceEnded: (callback) => instance.setOnDesktopSourceEnded(callback),
|
|
||||||
setOnSoundshare: (callback) => instance.setOnSoundshare(callback),
|
|
||||||
setOnSoundshareEnded: (callback) => instance.setOnSoundshareEnded(callback),
|
|
||||||
setOnSoundshareFailed: (callback) => instance.setOnSoundshareFailed(callback),
|
|
||||||
setPTTActive: (active, priority) => instance.setPTTActive(active, priority),
|
|
||||||
getStats: (callback) => instance.getStats(callback),
|
|
||||||
getFilteredStats: (filter, callback) => instance.getFilteredStats(filter, callback),
|
|
||||||
startReplay: () => instance.startReplay(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
VoiceEngine.createTransport = VoiceEngine._createTransport;
|
|
||||||
|
|
||||||
if (isElectronRenderer) {
|
|
||||||
VoiceEngine.setImageDataAllocator((width, height) => new window.ImageData(width, height));
|
|
||||||
}
|
|
||||||
|
|
||||||
VoiceEngine.createVoiceConnection = function (audioSSRC, userId, address, port, onConnectCallback, experiments, rids) {
|
|
||||||
let instance = null;
|
|
||||||
if (rids != null) {
|
|
||||||
instance = new VoiceEngine.VoiceConnection(audioSSRC, userId, address, port, onConnectCallback, experiments, rids);
|
|
||||||
} else if (experiments != null) {
|
|
||||||
instance = new VoiceEngine.VoiceConnection(audioSSRC, userId, address, port, onConnectCallback, experiments);
|
|
||||||
} else {
|
|
||||||
instance = new VoiceEngine.VoiceConnection(audioSSRC, userId, address, port, onConnectCallback);
|
|
||||||
}
|
|
||||||
return bindConnectionInstance(instance);
|
|
||||||
};
|
|
||||||
VoiceEngine.createOwnStreamConnection = VoiceEngine.createVoiceConnection;
|
|
||||||
|
|
||||||
VoiceEngine.createReplayConnection = function (audioEngineId, callback, replayLog) {
|
|
||||||
if (replayLog == null) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bindConnectionInstance(new VoiceEngine.VoiceReplayConnection(replayLog, audioEngineId, callback));
|
function bindConnectionInstance(instance) {
|
||||||
};
|
return {
|
||||||
|
destroy: () => instance.destroy(),
|
||||||
|
|
||||||
VoiceEngine.setAudioSubsystem = function (subsystem) {
|
setTransportOptions: (options) => instance.setTransportOptions(options),
|
||||||
if (appSettings == null) {
|
setSelfMute: (mute) => instance.setSelfMute(mute),
|
||||||
console.warn('Unable to access app settings.');
|
setSelfDeafen: (deaf) => instance.setSelfDeafen(deaf),
|
||||||
return;
|
|
||||||
|
mergeUsers: (users) => instance.mergeUsers(users),
|
||||||
|
destroyUser: (userId) => instance.destroyUser(userId),
|
||||||
|
|
||||||
|
setLocalVolume: (userId, volume) => instance.setLocalVolume(userId, volume),
|
||||||
|
setLocalMute: (userId, mute) => instance.setLocalMute(userId, mute),
|
||||||
|
setLocalPan: (userId, left, right) => instance.setLocalPan(userId, left, right),
|
||||||
|
setDisableLocalVideo: (userId, disabled) => instance.setDisableLocalVideo(userId, disabled),
|
||||||
|
|
||||||
|
setMinimumOutputDelay: (delay) => instance.setMinimumOutputDelay(delay),
|
||||||
|
getEncryptionModes: (callback) => instance.getEncryptionModes(callback),
|
||||||
|
configureConnectionRetries: (baseDelay, maxDelay, maxAttempts) =>
|
||||||
|
instance.configureConnectionRetries(baseDelay, maxDelay, maxAttempts),
|
||||||
|
setOnSpeakingCallback: (callback) => instance.setOnSpeakingCallback(callback),
|
||||||
|
setOnSpeakingWhileMutedCallback: (callback) => instance.setOnSpeakingWhileMutedCallback(callback),
|
||||||
|
setPingInterval: (interval) => instance.setPingInterval(interval),
|
||||||
|
setPingCallback: (callback) => instance.setPingCallback(callback),
|
||||||
|
setPingTimeoutCallback: (callback) => instance.setPingTimeoutCallback(callback),
|
||||||
|
setRemoteUserSpeakingStatus: (userId, speaking) => instance.setRemoteUserSpeakingStatus(userId, speaking),
|
||||||
|
setRemoteUserCanHavePriority: (userId, canHavePriority) =>
|
||||||
|
instance.setRemoteUserCanHavePriority(userId, canHavePriority),
|
||||||
|
|
||||||
|
setOnVideoCallback: (callback) => instance.setOnVideoCallback(callback),
|
||||||
|
setVideoBroadcast: (broadcasting) => instance.setVideoBroadcast(broadcasting),
|
||||||
|
setDesktopSource: (id, videoHook, type) => instance.setDesktopSource(id, videoHook, type),
|
||||||
|
setDesktopSourceStatusCallback: (callback) => instance.setDesktopSourceStatusCallback(callback),
|
||||||
|
setOnDesktopSourceEnded: (callback) => instance.setOnDesktopSourceEnded(callback),
|
||||||
|
setOnSoundshare: (callback) => instance.setOnSoundshare(callback),
|
||||||
|
setOnSoundshareEnded: (callback) => instance.setOnSoundshareEnded(callback),
|
||||||
|
setOnSoundshareFailed: (callback) => instance.setOnSoundshareFailed(callback),
|
||||||
|
setPTTActive: (active, priority) => instance.setPTTActive(active, priority),
|
||||||
|
getStats: (callback) => instance.getStats(callback),
|
||||||
|
getFilteredStats: (filter, callback) => instance.getFilteredStats(filter, callback),
|
||||||
|
startReplay: () => instance.startReplay(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: With experiment controlling ADM selection, this may be incorrect since
|
VoiceEngine.createTransport = VoiceEngine._createTransport;
|
||||||
// audioSubsystem is read from settings (or default if does not exists)
|
|
||||||
// and not the actual ADM used.
|
|
||||||
if (subsystem === audioSubsystem) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
appSettings.set('audioSubsystem', subsystem);
|
|
||||||
appSettings.set('useLegacyAudioDevice', false);
|
|
||||||
|
|
||||||
if (isElectronRenderer) {
|
if (isElectronRenderer) {
|
||||||
window.DiscordNative.app.relaunch();
|
VoiceEngine.setImageDataAllocator((width, height) => new window.ImageData(width, height));
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
VoiceEngine.setDebugLogging = function (enable) {
|
|
||||||
if (appSettings == null) {
|
|
||||||
console.warn('Unable to access app settings.');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debugLogging === enable) {
|
VoiceEngine.createVoiceConnection = function (audioSSRC, userId, address, port, onConnectCallback, experiments, rids) {
|
||||||
return;
|
let instance = null;
|
||||||
}
|
if (rids != null) {
|
||||||
|
instance = new VoiceEngine.VoiceConnection(audioSSRC, userId, address, port, onConnectCallback, experiments, rids);
|
||||||
appSettings.set('debugLogging', enable);
|
} else if (experiments != null) {
|
||||||
|
instance = new VoiceEngine.VoiceConnection(audioSSRC, userId, address, port, onConnectCallback, experiments);
|
||||||
if (isElectronRenderer) {
|
} else {
|
||||||
window.DiscordNative.app.relaunch();
|
instance = new VoiceEngine.VoiceConnection(audioSSRC, userId, address, port, onConnectCallback);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
VoiceEngine.getDebugLogging = function () {
|
|
||||||
return debugLogging;
|
|
||||||
};
|
|
||||||
|
|
||||||
const videoStreams = {};
|
|
||||||
|
|
||||||
const ensureCanvasContext = function (sinkId) {
|
|
||||||
let canvas = document.getElementById(sinkId);
|
|
||||||
if (canvas == null) {
|
|
||||||
for (const popout of window.popouts.values()) {
|
|
||||||
const element = popout.document != null && popout.document.getElementById(sinkId);
|
|
||||||
if (element != null) {
|
|
||||||
canvas = element;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return bindConnectionInstance(instance);
|
||||||
|
};
|
||||||
|
VoiceEngine.createOwnStreamConnection = VoiceEngine.createVoiceConnection;
|
||||||
|
|
||||||
if (canvas == null) {
|
VoiceEngine.createReplayConnection = function (audioEngineId, callback, replayLog) {
|
||||||
|
if (replayLog == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const context = canvas.getContext('2d');
|
return bindConnectionInstance(new VoiceEngine.VoiceReplayConnection(replayLog, audioEngineId, callback));
|
||||||
if (context == null) {
|
};
|
||||||
console.log(`Failed to initialize context for sinkId ${sinkId}`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return context;
|
VoiceEngine.setAudioSubsystem = function (subsystem) {
|
||||||
};
|
if (appSettings == null) {
|
||||||
|
console.warn('Unable to access app settings.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// [adill] NB: with context isolation it has become extremely costly (both memory & performance) to provide the image
|
// TODO: With experiment controlling ADM selection, this may be incorrect since
|
||||||
// data directly to clients at any reasonably fast interval so we've replaced setVideoOutputSink with a direct canvas
|
// audioSubsystem is read from settings (or default if does not exists)
|
||||||
// renderer via addVideoOutputSink
|
// and not the actual ADM used.
|
||||||
const setVideoOutputSink = VoiceEngine.setVideoOutputSink;
|
if (subsystem === audioSubsystem) {
|
||||||
const clearVideoOutputSink = (streamId) => {
|
return;
|
||||||
// [adill] NB: if you don't pass a frame callback setVideoOutputSink clears the sink
|
}
|
||||||
setVideoOutputSink(streamId);
|
|
||||||
};
|
|
||||||
const signalVideoOutputSinkReady = VoiceEngine.signalVideoOutputSinkReady;
|
|
||||||
delete VoiceEngine.setVideoOutputSink;
|
|
||||||
delete VoiceEngine.signalVideoOutputSinkReady;
|
|
||||||
|
|
||||||
function addVideoOutputSinkInternal(sinkId, streamId, frameCallback) {
|
appSettings.set('audioSubsystem', subsystem);
|
||||||
let sinks = videoStreams[streamId];
|
appSettings.set('useLegacyAudioDevice', false);
|
||||||
if (sinks == null) {
|
|
||||||
sinks = videoStreams[streamId] = new Map();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sinks.size === 0) {
|
if (isElectronRenderer) {
|
||||||
console.log(`Subscribing to frames for streamId ${streamId}`);
|
window.DiscordNative.app.relaunch();
|
||||||
const onFrame = (imageData) => {
|
}
|
||||||
const sinks = videoStreams[streamId];
|
};
|
||||||
if (sinks != null) {
|
|
||||||
for (const callback of sinks.values()) {
|
VoiceEngine.setDebugLogging = function (enable) {
|
||||||
if (callback != null) {
|
if (appSettings == null) {
|
||||||
callback(imageData);
|
console.warn('Unable to access app settings.');
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debugLogging === enable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
appSettings.set('debugLogging', enable);
|
||||||
|
|
||||||
|
if (isElectronRenderer) {
|
||||||
|
window.DiscordNative.app.relaunch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
VoiceEngine.getDebugLogging = function () {
|
||||||
|
return debugLogging;
|
||||||
|
};
|
||||||
|
|
||||||
|
const videoStreams = {};
|
||||||
|
|
||||||
|
const ensureCanvasContext = function (sinkId) {
|
||||||
|
let canvas = document.getElementById(sinkId);
|
||||||
|
if (canvas == null) {
|
||||||
|
for (const popout of window.popouts.values()) {
|
||||||
|
const element = popout.document != null && popout.document.getElementById(sinkId);
|
||||||
|
if (element != null) {
|
||||||
|
canvas = element;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
signalVideoOutputSinkReady(streamId);
|
|
||||||
};
|
|
||||||
setVideoOutputSink(streamId, onFrame, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
sinks.set(sinkId, frameCallback);
|
if (canvas == null) {
|
||||||
}
|
return null;
|
||||||
|
|
||||||
VoiceEngine.addVideoOutputSink = function (sinkId, streamId, frameCallback) {
|
|
||||||
let canvasContext = null;
|
|
||||||
addVideoOutputSinkInternal(sinkId, streamId, (imageData) => {
|
|
||||||
if (canvasContext == null) {
|
|
||||||
canvasContext = ensureCanvasContext(sinkId);
|
|
||||||
if (canvasContext == null) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (frameCallback != null) {
|
|
||||||
frameCallback(imageData.width, imageData.height);
|
|
||||||
}
|
|
||||||
// [adill] NB: Electron 9+ on macOS would show massive leaks in the the GPU helper process when a non-Discord
|
|
||||||
// window completely occludes the Discord window. Adding this tiny readback ameliorates the issue. We tried WebGL
|
|
||||||
// rendering which did not exhibit the issue, however, the context limit of 16 was too small to be a real
|
|
||||||
// alternative.
|
|
||||||
const leak = canvasContext.getImageData(0, 0, 1, 1);
|
|
||||||
canvasContext.putImageData(imageData, 0, 0);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
VoiceEngine.removeVideoOutputSink = function (sinkId, streamId) {
|
const context = canvas.getContext('2d');
|
||||||
const sinks = videoStreams[streamId];
|
if (context == null) {
|
||||||
if (sinks != null) {
|
console.log(`Failed to initialize context for sinkId ${sinkId}`);
|
||||||
sinks.delete(sinkId);
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
// [adill] NB: with context isolation it has become extremely costly (both memory & performance) to provide the image
|
||||||
|
// data directly to clients at any reasonably fast interval so we've replaced setVideoOutputSink with a direct canvas
|
||||||
|
// renderer via addVideoOutputSink
|
||||||
|
const setVideoOutputSink = VoiceEngine.setVideoOutputSink;
|
||||||
|
const clearVideoOutputSink = (streamId) => {
|
||||||
|
// [adill] NB: if you don't pass a frame callback setVideoOutputSink clears the sink
|
||||||
|
setVideoOutputSink(streamId);
|
||||||
|
};
|
||||||
|
const signalVideoOutputSinkReady = VoiceEngine.signalVideoOutputSinkReady;
|
||||||
|
delete VoiceEngine.setVideoOutputSink;
|
||||||
|
delete VoiceEngine.signalVideoOutputSinkReady;
|
||||||
|
|
||||||
|
function addVideoOutputSinkInternal(sinkId, streamId, frameCallback) {
|
||||||
|
let sinks = videoStreams[streamId];
|
||||||
|
if (sinks == null) {
|
||||||
|
sinks = videoStreams[streamId] = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
if (sinks.size === 0) {
|
if (sinks.size === 0) {
|
||||||
delete videoStreams[streamId];
|
console.log(`Subscribing to frames for streamId ${streamId}`);
|
||||||
console.log(`Unsubscribing from frames for streamId ${streamId}`);
|
const onFrame = (imageData) => {
|
||||||
clearVideoOutputSink(streamId);
|
const sinks = videoStreams[streamId];
|
||||||
|
if (sinks != null) {
|
||||||
|
for (const callback of sinks.values()) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback(imageData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
signalVideoOutputSinkReady(streamId);
|
||||||
|
};
|
||||||
|
setVideoOutputSink(streamId, onFrame, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sinks.set(sinkId, frameCallback);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let sinkId = 0;
|
VoiceEngine.addVideoOutputSink = function (sinkId, streamId, frameCallback) {
|
||||||
VoiceEngine.getNextVideoOutputFrame = function (streamId) {
|
let canvasContext = null;
|
||||||
const nextVideoFrameSinkId = `getNextVideoFrame_${++sinkId}`;
|
addVideoOutputSinkInternal(sinkId, streamId, (imageData) => {
|
||||||
|
if (canvasContext == null) {
|
||||||
|
canvasContext = ensureCanvasContext(sinkId);
|
||||||
|
if (canvasContext == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (frameCallback != null) {
|
||||||
|
frameCallback(imageData.width, imageData.height);
|
||||||
|
}
|
||||||
|
// [adill] NB: Electron 9+ on macOS would show massive leaks in the the GPU helper process when a non-Discord
|
||||||
|
// window completely occludes the Discord window. Adding this tiny readback ameliorates the issue. We tried WebGL
|
||||||
|
// rendering which did not exhibit the issue, however, the context limit of 16 was too small to be a real
|
||||||
|
// alternative.
|
||||||
|
const leak = canvasContext.getImageData(0, 0, 1, 1);
|
||||||
|
canvasContext.putImageData(imageData, 0, 0);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
VoiceEngine.removeVideoOutputSink = function (sinkId, streamId) {
|
||||||
setTimeout(() => {
|
const sinks = videoStreams[streamId];
|
||||||
VoiceEngine.removeVideoOutputSink(nextVideoFrameSinkId, streamId);
|
if (sinks != null) {
|
||||||
reject(new Error('getNextVideoOutputFrame timeout'));
|
sinks.delete(sinkId);
|
||||||
}, 5000);
|
if (sinks.size === 0) {
|
||||||
|
delete videoStreams[streamId];
|
||||||
|
console.log(`Unsubscribing from frames for streamId ${streamId}`);
|
||||||
|
clearVideoOutputSink(streamId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
addVideoOutputSinkInternal(nextVideoFrameSinkId, streamId, (imageData) => {
|
let sinkId = 0;
|
||||||
VoiceEngine.removeVideoOutputSink(nextVideoFrameSinkId, streamId);
|
VoiceEngine.getNextVideoOutputFrame = function (streamId) {
|
||||||
resolve({
|
const nextVideoFrameSinkId = `getNextVideoFrame_${++sinkId}`;
|
||||||
width: imageData.width,
|
|
||||||
height: imageData.height,
|
return new Promise((resolve, reject) => {
|
||||||
data: new Uint8ClampedArray(imageData.data.buffer),
|
setTimeout(() => {
|
||||||
|
VoiceEngine.removeVideoOutputSink(nextVideoFrameSinkId, streamId);
|
||||||
|
reject(new Error('getNextVideoOutputFrame timeout'));
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
addVideoOutputSinkInternal(nextVideoFrameSinkId, streamId, (imageData) => {
|
||||||
|
VoiceEngine.removeVideoOutputSink(nextVideoFrameSinkId, streamId);
|
||||||
|
resolve({
|
||||||
|
width: imageData.width,
|
||||||
|
height: imageData.height,
|
||||||
|
data: new Uint8ClampedArray(imageData.data.buffer),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
};
|
|
||||||
|
|
||||||
console.log(`Initializing voice engine with audio subsystem: ${audioSubsystem}`);
|
console.log(`Initializing voice engine with audio subsystem: ${audioSubsystem}`);
|
||||||
VoiceEngine.initialize({audioSubsystem, logLevel, dataDirectory});
|
VoiceEngine.initialize({audioSubsystem, logLevel, dataDirectory});
|
||||||
|
|
||||||
module.exports = VoiceEngine;
|
module.exports = VoiceEngine;
|
||||||
|
}
|
747
scripts/build.js
747
scripts/build.js
|
@ -1,286 +1,505 @@
|
||||||
const child_process = require("child_process")
|
const child_process = require("child_process");
|
||||||
const path = require("path")
|
const path = require("path");
|
||||||
const terser = require("terser")
|
const terser = require("terser");
|
||||||
const util = require("util")
|
const util = require("util");
|
||||||
|
|
||||||
const production = true
|
const production = true;
|
||||||
const includeSourcesMaps = true
|
const includeSourcesMaps = true;
|
||||||
|
|
||||||
let fs = require("fs")
|
let fs = require("fs");
|
||||||
|
|
||||||
exports.default = async function beforeBuild(context){
|
exports.default = async function beforeBuild(context) {
|
||||||
await main()
|
await main();
|
||||||
return true
|
return true;
|
||||||
}
|
};
|
||||||
const PROJECT_DIR = path.resolve(__dirname, "..");
|
const PROJECT_DIR = path.resolve(__dirname, "..");
|
||||||
|
|
||||||
console.log = (...args) => {
|
console.log = (...args) => {
|
||||||
process.stdout.write(Buffer.from(util.formatWithOptions({colors: true}, ...args)+"\n", "binary").toString("utf8"))
|
process.stdout.write(
|
||||||
}
|
Buffer.from(
|
||||||
|
util.formatWithOptions({ colors: true }, ...args) + "\n",
|
||||||
|
"binary"
|
||||||
|
).toString("utf8")
|
||||||
|
);
|
||||||
|
};
|
||||||
console.info = (...args) => {
|
console.info = (...args) => {
|
||||||
console.log(`\x1b[34m[INFO]\x1b[0m`, ...args)
|
console.log(`\x1b[34m[INFO]\x1b[0m`, ...args);
|
||||||
}
|
};
|
||||||
let commit = child_process.execSync("git rev-parse HEAD").toString().split("\n")[0].trim()
|
let commit = child_process
|
||||||
console.info(`Obtained commit ${commit} for the build`)
|
.execSync("git rev-parse HEAD")
|
||||||
|
.toString()
|
||||||
async function processNextDir(folder, folders, predicate, compile, ignoreModules){
|
.split("\n")[0]
|
||||||
if(typeof ignoreModules === "undefined")ignoreModules = false
|
.trim();
|
||||||
let files = fs.readdirSync(folder, {withFileTypes: true})
|
console.info(`Obtained commit ${commit} for the build`);
|
||||||
for(let file of files){
|
|
||||||
if(file.isFile()){
|
async function processNextDir(
|
||||||
let isMinified = file.name.endsWith(".min.js") || file.name.endsWith(".min.css")
|
folder,
|
||||||
let filepath = path.join(folder, file.name)
|
folders,
|
||||||
let type = file.name.split(".").pop().toLowerCase()
|
predicate,
|
||||||
if(type === file.name)type = ""
|
compile,
|
||||||
if([
|
ignoreModules
|
||||||
"ts",
|
) {
|
||||||
"md",
|
if (typeof ignoreModules === "undefined") ignoreModules = false;
|
||||||
"gitignore",
|
let files = fs.readdirSync(folder, { withFileTypes: true });
|
||||||
"map"
|
for (let file of files) {
|
||||||
].includes(type)){
|
if (file.isFile()) {
|
||||||
console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because of type ${type}\x1b[0m`)
|
let isMinified =
|
||||||
continue
|
file.name.endsWith(".min.js") || file.name.endsWith(".min.css");
|
||||||
}
|
let filepath = path.join(folder, file.name);
|
||||||
if([
|
let type = file.name.split(".").pop().toLowerCase();
|
||||||
"tsconfig.json",
|
if (type === file.name) type = "";
|
||||||
"webpack.config.js"
|
if (["ts", "md", "gitignore", "map"].includes(type)) {
|
||||||
].includes(file.name)){
|
console.warn(
|
||||||
console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because of name ${file.name}\x1b[0m`)
|
`\x1b[33mIgnored file ${path.relative(
|
||||||
continue
|
folders.startDir,
|
||||||
}
|
filepath
|
||||||
if(folders.exclude && folders.exclude.test(filepath)){
|
)} because of type ${type}\x1b[0m`
|
||||||
console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because regex\x1b[0m`)
|
);
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
let hasMinifiedVersion = (type === "js" || type === "css") && !isMinified && files.find(f => {
|
if (["tsconfig.json", "webpack.config.js"].includes(file.name)) {
|
||||||
return f.name === file.name.split(".").slice(0, -1).join(".")+".min."+type
|
console.warn(
|
||||||
})
|
`\x1b[33mIgnored file ${path.relative(
|
||||||
if(hasMinifiedVersion){
|
folders.startDir,
|
||||||
console.warn(`\x1b[33mIgnored file ${path.relative(folders.startDir, filepath)} because it has a minified version.\x1b[0m`)
|
filepath
|
||||||
continue
|
)} because of name ${file.name}\x1b[0m`
|
||||||
}
|
);
|
||||||
if(!isMinified && predicate(filepath) && filepath.split(/[\\/]+/).reverse()[1] !== "js"){
|
continue;
|
||||||
await compile(filepath, path.join(filepath.replace(folders.startDir, folders.newDir)), "..")
|
}
|
||||||
}else{
|
if (folders.exclude && folders.exclude.test(filepath)) {
|
||||||
if(["js", "css"].includes(type)){
|
console.warn(
|
||||||
if(!includeSourcesMaps){
|
`\x1b[33mIgnored file ${path.relative(
|
||||||
console.log(`We don't include sourcemap for this build. Skipping ${file.name}.`)
|
folders.startDir,
|
||||||
return await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir))
|
filepath
|
||||||
}
|
)} because regex\x1b[0m`
|
||||||
let fileContent = (await fs.promises.readFile(filepath, "utf8"))
|
);
|
||||||
let sourceMap = fileContent.split(/[\n\r]+/g).pop()
|
continue;
|
||||||
if(!sourceMap || !sourceMap.startsWith("//# sourceMappingURL=")){
|
}
|
||||||
console.log(`This file doesn't have sourcemap. ${file.name}.`)
|
let hasMinifiedVersion =
|
||||||
await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir))
|
(type === "js" || type === "css") &&
|
||||||
continue
|
!isMinified &&
|
||||||
}
|
files.find((f) => {
|
||||||
let sourceMapContent
|
return (
|
||||||
if(sourceMap.slice(21).startsWith("data:")){
|
f.name ===
|
||||||
console.log(`Extracting sourcemap from data uri. From file ${file.name}.`)
|
file.name.split(".").slice(0, -1).join(".") + ".min." + type
|
||||||
sourceMapContent = Buffer.from(sourceMap.split("=").slice(1).join("="), "base64").toString("utf-8")
|
);
|
||||||
}else{
|
});
|
||||||
console.log(`Extracting sourcemap from file ${file.name}.map.`)
|
if (hasMinifiedVersion) {
|
||||||
await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir))
|
console.warn(
|
||||||
sourceMapContent = await fs.promises.readFile(path.join(folder, sourceMap.slice(21)), "utf8")
|
`\x1b[33mIgnored file ${path.relative(
|
||||||
}
|
folders.startDir,
|
||||||
sourceMapContent = JSON.parse(sourceMapContent)
|
filepath
|
||||||
sourceMapContent.sourcesContent = []
|
)} because it has a minified version.\x1b[0m`
|
||||||
let sourceMapPath = filepath + ".map"
|
);
|
||||||
fileContent = fileContent
|
continue;
|
||||||
// source map
|
}
|
||||||
.replace(sourceMap, "//# sourceMappingURL="+filepath.split(/[\\\/]+/g).pop()+".map")
|
if (
|
||||||
await fs.promises.writeFile(filepath.replace(folders.startDir, folders.newDir), fileContent)
|
!isMinified &&
|
||||||
await fs.promises.writeFile(filepath.replace(folders.startDir, folders.newDir)+".map", JSON.stringify(sourceMapContent))
|
predicate(filepath) &&
|
||||||
}else{
|
filepath.split(/[\\/]+/).reverse()[1] !== "js"
|
||||||
await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir))
|
) {
|
||||||
}
|
await compile(
|
||||||
}
|
filepath,
|
||||||
}else if(file.isDirectory()){
|
path.join(filepath.replace(folders.startDir, folders.newDir)),
|
||||||
if(ignoreModules && file.name === "node_modules")continue
|
".."
|
||||||
if(folders.exclude && folders.exclude.test(path.join(folder, file.name)))continue
|
);
|
||||||
await fs.promises.mkdir(path.join(folder, file.name).replace(folders.startDir, folders.newDir), {recursive: true})
|
} else {
|
||||||
await processNextDir(path.join(folder, file.name), ...Array.from(arguments).slice(1))
|
if (["js", "css"].includes(type)) {
|
||||||
|
if (!includeSourcesMaps) {
|
||||||
|
console.log(
|
||||||
|
`We don't include sourcemap for this build. Skipping ${file.name}.`
|
||||||
|
);
|
||||||
|
return await fs.promises.copyFile(
|
||||||
|
filepath,
|
||||||
|
filepath.replace(folders.startDir, folders.newDir)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let fileContent = await fs.promises.readFile(filepath, "utf8");
|
||||||
|
let sourceMap = fileContent.split(/[\n\r]+/g).pop();
|
||||||
|
if (!sourceMap || !sourceMap.startsWith("//# sourceMappingURL=")) {
|
||||||
|
console.log(`This file doesn't have sourcemap. ${file.name}.`);
|
||||||
|
await fs.promises.copyFile(
|
||||||
|
filepath,
|
||||||
|
filepath.replace(folders.startDir, folders.newDir)
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let sourceMapContent;
|
||||||
|
if (sourceMap.slice(21).startsWith("data:")) {
|
||||||
|
console.log(
|
||||||
|
`Extracting sourcemap from data uri. From file ${file.name}.`
|
||||||
|
);
|
||||||
|
sourceMapContent = Buffer.from(
|
||||||
|
sourceMap.split("=").slice(1).join("="),
|
||||||
|
"base64"
|
||||||
|
).toString("utf-8");
|
||||||
|
} else {
|
||||||
|
console.log(`Extracting sourcemap from file ${file.name}.map.`);
|
||||||
|
await fs.promises.copyFile(
|
||||||
|
filepath,
|
||||||
|
filepath.replace(folders.startDir, folders.newDir)
|
||||||
|
);
|
||||||
|
sourceMapContent = await fs.promises.readFile(
|
||||||
|
path.join(folder, sourceMap.slice(21)),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
sourceMapContent = JSON.parse(sourceMapContent);
|
||||||
|
sourceMapContent.sourcesContent = [];
|
||||||
|
let sourceMapPath = filepath + ".map";
|
||||||
|
fileContent = fileContent
|
||||||
|
// source map
|
||||||
|
.replace(
|
||||||
|
sourceMap,
|
||||||
|
"//# sourceMappingURL=" +
|
||||||
|
filepath.split(/[\\\/]+/g).pop() +
|
||||||
|
".map"
|
||||||
|
);
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
filepath.replace(folders.startDir, folders.newDir),
|
||||||
|
fileContent
|
||||||
|
);
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
filepath.replace(folders.startDir, folders.newDir) + ".map",
|
||||||
|
JSON.stringify(sourceMapContent)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await fs.promises.copyFile(
|
||||||
|
filepath,
|
||||||
|
filepath.replace(folders.startDir, folders.newDir)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else if (file.isDirectory()) {
|
||||||
|
if (ignoreModules && file.name === "node_modules") continue;
|
||||||
|
if (folders.exclude && folders.exclude.test(path.join(folder, file.name)))
|
||||||
|
continue;
|
||||||
|
await fs.promises.mkdir(
|
||||||
|
path.join(folder, file.name).replace(folders.startDir, folders.newDir),
|
||||||
|
{ recursive: true }
|
||||||
|
);
|
||||||
|
await processNextDir(
|
||||||
|
path.join(folder, file.name),
|
||||||
|
...Array.from(arguments).slice(1)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main(){
|
async function main() {
|
||||||
let startTimestamp = Date.now()
|
let startTimestamp = Date.now();
|
||||||
console.info("Starting build")
|
console.info("Starting build");
|
||||||
|
|
||||||
console.info("Reseting existent directory...")
|
|
||||||
try{
|
|
||||||
await fs.promises.rm("./distApp", {"recursive": true})
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
await fs.promises.mkdir(PROJECT_DIR+"/distApp/dist", {"recursive": true})
|
console.info("Reseting existent directory...");
|
||||||
|
try {
|
||||||
console.info("Executing command `yarn compile`")
|
await fs.promises.rm("./distApp", { recursive: true });
|
||||||
child_process.execSync("yarn compile", {
|
} catch (error) {
|
||||||
encoding: "binary",
|
console.error(error);
|
||||||
stdio: "inherit"
|
}
|
||||||
})
|
|
||||||
|
|
||||||
let startDir = path.join(PROJECT_DIR, "./dist")
|
|
||||||
let newDir = path.join(PROJECT_DIR, "./distApp/dist")
|
|
||||||
console.info("No error detected. Copying files from "+startDir+".")
|
|
||||||
await fs.promises.mkdir(startDir, {recursive: true})
|
|
||||||
|
|
||||||
await processNextDir(startDir, {
|
await fs.promises.mkdir(PROJECT_DIR + "/distApp/dist", { recursive: true });
|
||||||
startDir,
|
|
||||||
newDir
|
|
||||||
}, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => {
|
|
||||||
console.info(`Minifying ${filepath} to ${newpath}`)
|
|
||||||
|
|
||||||
if(filepath.endsWith("git.js")){
|
console.info("Executing command `yarn compile`");
|
||||||
await fs.promises.writeFile(newpath, terser.minify(fs.readFileSync(filepath, "utf8").replace(/"{commit}"/g, `"${commit}"`)).code, "utf8")
|
child_process.execSync("yarn compile", {
|
||||||
}else{
|
encoding: "binary",
|
||||||
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
|
stdio: "inherit",
|
||||||
|
});
|
||||||
|
|
||||||
|
let startDir = path.join(PROJECT_DIR, "./dist");
|
||||||
|
let newDir = path.join(PROJECT_DIR, "./distApp/dist");
|
||||||
|
console.info("No error detected. Copying files from " + startDir + ".");
|
||||||
|
await fs.promises.mkdir(startDir, { recursive: true });
|
||||||
|
|
||||||
|
await processNextDir(
|
||||||
|
startDir,
|
||||||
|
{
|
||||||
|
startDir,
|
||||||
|
newDir,
|
||||||
|
},
|
||||||
|
(filepath) => filepath.endsWith(".js"),
|
||||||
|
async (filepath, newpath) => {
|
||||||
|
console.info(`Minifying ${filepath} to ${newpath}`);
|
||||||
|
|
||||||
|
if (filepath.endsWith("git.js")) {
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
newpath,
|
||||||
|
(
|
||||||
|
await terser.minify(
|
||||||
|
fs
|
||||||
|
.readFileSync(filepath, "utf8")
|
||||||
|
.replace(/"{commit}"/g, `"${commit}"`)
|
||||||
|
)
|
||||||
|
).code,
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
newpath,
|
||||||
|
(
|
||||||
|
await terser.minify(await fs.promises.readFile(filepath, "utf8"))
|
||||||
|
).code,
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true
|
||||||
|
).then(() => {
|
||||||
|
console.info(`Copied files and minified them from ${startDir}.`);
|
||||||
|
});
|
||||||
|
|
||||||
|
await processNextDir(
|
||||||
|
path.join(PROJECT_DIR, "modules"),
|
||||||
|
{
|
||||||
|
startDir: path.join(PROJECT_DIR, "modules"),
|
||||||
|
newDir: path.join(PROJECT_DIR, "distApp", "modules"),
|
||||||
|
exclude: /discord_spellcheck/g,
|
||||||
|
},
|
||||||
|
(filepath) => filepath.endsWith(".js"),
|
||||||
|
async (filepath, newpath) => {
|
||||||
|
console.info(`Minifying ${filepath} to ${newpath}`);
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
newpath,
|
||||||
|
(await terser.minify(await fs.promises.readFile(filepath, "utf8"))).code,
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
true
|
||||||
|
).then(() => {
|
||||||
|
console.info(
|
||||||
|
`Copied files and minified them from ${path.join(
|
||||||
|
PROJECT_DIR,
|
||||||
|
"modules"
|
||||||
|
)}.`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
(
|
||||||
|
await fs.promises.readdir(path.join(PROJECT_DIR, "distApp", "modules"))
|
||||||
|
).map(async (mdl) => {
|
||||||
|
let dir = path.join(PROJECT_DIR, "distApp", "modules", mdl);
|
||||||
|
|
||||||
|
if (!fs.existsSync(path.join(dir, "package.json"))) {
|
||||||
|
if (mdl === "discord_desktop_core") {
|
||||||
|
dir = path.join(dir, "core");
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}, true).then(() => {
|
}
|
||||||
console.info(`Copied files and minified them from ${startDir}.`)
|
|
||||||
})
|
|
||||||
|
|
||||||
await processNextDir(path.join(PROJECT_DIR, "modules"), {
|
|
||||||
startDir: path.join(PROJECT_DIR, "modules"),
|
|
||||||
newDir: path.join(PROJECT_DIR, "distApp", "modules"),
|
|
||||||
exclude: /discord_spellcheck/g
|
|
||||||
}, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => {
|
|
||||||
console.info(`Minifying ${filepath} to ${newpath}`)
|
|
||||||
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
|
|
||||||
}, true).then(() => {
|
|
||||||
console.info(`Copied files and minified them from ${path.join(PROJECT_DIR, "modules")}.`)
|
|
||||||
})
|
|
||||||
|
|
||||||
await Promise.all((await fs.promises.readdir(path.join(PROJECT_DIR, "distApp", "modules"))).map(async mdl => {
|
console.info(`Installing modules for ${mdl}`);
|
||||||
let dir = path.join(PROJECT_DIR, "distApp", "modules", mdl)
|
child_process.execSync("yarn --production", {
|
||||||
|
|
||||||
if(!fs.existsSync(path.join(dir, "package.json"))){
|
|
||||||
if(mdl === "discord_desktop_core"){
|
|
||||||
dir = path.join(dir, "core")
|
|
||||||
}else{
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.info(`Installing modules for ${mdl}`)
|
|
||||||
child_process.execSync("yarn --production", {
|
|
||||||
encoding: "binary",
|
|
||||||
cwd: dir,
|
|
||||||
stdio: "inherit"
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
|
|
||||||
await fs.promises.mkdir(path.join(PROJECT_DIR, "distApp", "modules", "discord_spellcheck"), {recursive: true})
|
|
||||||
await processNextDir(path.join(PROJECT_DIR, "modules", "discord_spellcheck"), {
|
|
||||||
startDir: path.join(PROJECT_DIR, "modules", "discord_spellcheck"),
|
|
||||||
newDir: path.join(PROJECT_DIR, "distApp", "modules", "discord_spellcheck")
|
|
||||||
}, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => {
|
|
||||||
console.info(`Minifying ${filepath} to ${newpath}`)
|
|
||||||
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
|
|
||||||
}, false).then(() => {
|
|
||||||
console.info(`Copied files and minified them from ${path.join(PROJECT_DIR, "modules")}.`)
|
|
||||||
})
|
|
||||||
|
|
||||||
await processNextDir(path.join(PROJECT_DIR, "LightcordApi"), {
|
|
||||||
startDir: path.join(PROJECT_DIR, "LightcordApi"),
|
|
||||||
newDir: path.join(PROJECT_DIR, "distApp", "LightcordApi"),
|
|
||||||
exclude: /(src|webpack\.config\.js|tsconfig\.json|dist|docs)/g
|
|
||||||
}, ((filepath) => filepath.endsWith(".js") && (!production ? !filepath.includes("node_modules") : true)), async (filepath, newpath) => {
|
|
||||||
await fs.promises.copyFile(filepath, newpath)
|
|
||||||
}, true).then(() => {
|
|
||||||
console.info(`Copied files and minified them from ${path.join(PROJECT_DIR, "LightcordApi")}.`)
|
|
||||||
})
|
|
||||||
|
|
||||||
child_process.execSync("yarn --production", {
|
|
||||||
encoding: "binary",
|
encoding: "binary",
|
||||||
cwd: path.join(PROJECT_DIR, "distApp", "LightcordApi"),
|
cwd: dir,
|
||||||
stdio: "inherit"
|
stdio: "inherit",
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
);
|
||||||
|
|
||||||
function processDJS(dir){
|
await fs.promises.mkdir(
|
||||||
fs.mkdirSync(path.join(PROJECT_DIR, "distApp", "DiscordJS", dir), {recursive: true})
|
path.join(PROJECT_DIR, "distApp", "modules", "discord_spellcheck"),
|
||||||
return processNextDir(path.join(PROJECT_DIR, "DiscordJS", dir), {
|
{ recursive: true }
|
||||||
startDir: path.join(PROJECT_DIR, "DiscordJS", dir),
|
);
|
||||||
newDir: path.join(PROJECT_DIR, "distApp", "DiscordJS", dir),
|
await processNextDir(
|
||||||
exclude: /node_modules/g
|
path.join(PROJECT_DIR, "modules", "discord_spellcheck"),
|
||||||
}, ((filepath) => filepath.endsWith(".js")), async (filepath, newpath) => {
|
{
|
||||||
console.info(`Minifying ${filepath} to ${newpath}`)
|
startDir: path.join(PROJECT_DIR, "modules", "discord_spellcheck"),
|
||||||
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
|
newDir: path.join(
|
||||||
}).then(() => {
|
PROJECT_DIR,
|
||||||
console.info(`Copied files and minified them from ${path.join(PROJECT_DIR, "DiscordJS", dir)}.`)
|
"distApp",
|
||||||
})
|
"modules",
|
||||||
}
|
"discord_spellcheck"
|
||||||
async function copyFileDJS(file){
|
),
|
||||||
await fs.promises.writeFile(path.join(PROJECT_DIR, "distApp", "DiscordJS", file), await fs.promises.readFile(path.join(PROJECT_DIR, "DiscordJS", file)))
|
},
|
||||||
|
(filepath) => filepath.endsWith(".js"),
|
||||||
|
async (filepath, newpath) => {
|
||||||
|
console.info(`Minifying ${filepath} to ${newpath}`);
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
newpath,
|
||||||
|
(await terser.minify(await fs.promises.readFile(filepath, "utf8"))).code,
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
false
|
||||||
|
).then(() => {
|
||||||
|
console.info(
|
||||||
|
`Copied files and minified them from ${path.join(
|
||||||
|
PROJECT_DIR,
|
||||||
|
"modules"
|
||||||
|
)}.`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await processNextDir(
|
||||||
|
path.join(PROJECT_DIR, "LightcordApi"),
|
||||||
|
{
|
||||||
|
startDir: path.join(PROJECT_DIR, "LightcordApi"),
|
||||||
|
newDir: path.join(PROJECT_DIR, "distApp", "LightcordApi"),
|
||||||
|
exclude: /(src|webpack\.config\.js|tsconfig\.json|dist|docs)/g,
|
||||||
|
},
|
||||||
|
(filepath) =>
|
||||||
|
filepath.endsWith(".js") &&
|
||||||
|
(!production ? !filepath.includes("node_modules") : true),
|
||||||
|
async (filepath, newpath) => {
|
||||||
|
await fs.promises.copyFile(filepath, newpath);
|
||||||
|
},
|
||||||
|
true
|
||||||
|
).then(() => {
|
||||||
|
console.info(
|
||||||
|
`Copied files and minified them from ${path.join(
|
||||||
|
PROJECT_DIR,
|
||||||
|
"LightcordApi"
|
||||||
|
)}.`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
child_process.execSync("yarn --production", {
|
||||||
|
encoding: "binary",
|
||||||
|
cwd: path.join(PROJECT_DIR, "distApp", "LightcordApi"),
|
||||||
|
stdio: "inherit",
|
||||||
|
});
|
||||||
|
|
||||||
|
function processDJS(dir) {
|
||||||
|
fs.mkdirSync(path.join(PROJECT_DIR, "distApp", "DiscordJS", dir), {
|
||||||
|
recursive: true,
|
||||||
|
});
|
||||||
|
return processNextDir(
|
||||||
|
path.join(PROJECT_DIR, "DiscordJS", dir),
|
||||||
|
{
|
||||||
|
startDir: path.join(PROJECT_DIR, "DiscordJS", dir),
|
||||||
|
newDir: path.join(PROJECT_DIR, "distApp", "DiscordJS", dir),
|
||||||
|
exclude: /node_modules/g,
|
||||||
|
},
|
||||||
|
(filepath) => filepath.endsWith(".js"),
|
||||||
|
async (filepath, newpath) => {
|
||||||
|
console.info(`Minifying ${filepath} to ${newpath}`);
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
newpath,
|
||||||
|
(await terser.minify(await fs.promises.readFile(filepath, "utf8"))).code,
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
console.info(
|
||||||
|
`Copied files and minified them from ${path.join(
|
||||||
|
PROJECT_DIR,
|
||||||
|
"DiscordJS",
|
||||||
|
dir
|
||||||
|
)}.`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async function copyFileDJS(file) {
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
path.join(PROJECT_DIR, "distApp", "DiscordJS", file),
|
||||||
|
await fs.promises.readFile(path.join(PROJECT_DIR, "DiscordJS", file))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await processDJS("dist");
|
||||||
|
await copyFileDJS("package.json");
|
||||||
|
|
||||||
|
child_process.execSync("yarn --production", {
|
||||||
|
encoding: "binary",
|
||||||
|
cwd: path.join(PROJECT_DIR, "distApp", "DiscordJS"),
|
||||||
|
stdio: "inherit",
|
||||||
|
});
|
||||||
|
|
||||||
|
fs.mkdirSync(path.join(PROJECT_DIR, "distApp", "BetterDiscordApp", "dist"), {
|
||||||
|
recursive: true,
|
||||||
|
});
|
||||||
|
const BDPackageJSON = require("../BetterDiscordApp/package.json");
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(PROJECT_DIR, "distApp", "BetterDiscordApp", "package.json"),
|
||||||
|
JSON.stringify(BDPackageJSON),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
const files = ["index.min.js", "style.min.css"];
|
||||||
|
files.forEach((e) => {
|
||||||
|
files.push(e + ".map");
|
||||||
|
});
|
||||||
|
files.forEach((e) => {
|
||||||
|
const pth = path.join(PROJECT_DIR, "BetterDiscordApp", "dist", e);
|
||||||
|
if (!fs.existsSync(pth))
|
||||||
|
return console.error(
|
||||||
|
`\x1b[31mFile ${pth} from betterdiscord does not exist.\x1b[0m`
|
||||||
|
);
|
||||||
|
if (e.endsWith(".map")) {
|
||||||
|
const data = JSON.parse(fs.readFileSync(pth, "utf8"));
|
||||||
|
data.sourcesContent = [];
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(PROJECT_DIR, "distApp", "BetterDiscordApp", "dist", e),
|
||||||
|
JSON.stringify(data)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
fs.copyFileSync(
|
||||||
|
pth,
|
||||||
|
path.join(PROJECT_DIR, "distApp", "BetterDiscordApp", "dist", e)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await processDJS("dist")
|
await fs.promises.mkdir(
|
||||||
await copyFileDJS("package.json")
|
path.join(PROJECT_DIR, "distApp", "splash", "videos"),
|
||||||
|
{ recursive: true }
|
||||||
child_process.execSync("yarn --production", {
|
);
|
||||||
encoding: "binary",
|
await processNextDir(
|
||||||
cwd: path.join(PROJECT_DIR, "distApp", "DiscordJS"),
|
path.join(PROJECT_DIR, "splash"),
|
||||||
stdio: "inherit"
|
{
|
||||||
})
|
startDir: path.join(PROJECT_DIR, "splash"),
|
||||||
|
newDir: path.join(PROJECT_DIR, "distApp", "splash"),
|
||||||
fs.mkdirSync(path.join(PROJECT_DIR, "distApp", "BetterDiscordApp", "dist"), {recursive: true})
|
exclude: /node_modules/g,
|
||||||
const BDPackageJSON = require("../BetterDiscordApp/package.json")
|
},
|
||||||
fs.writeFileSync(path.join(PROJECT_DIR, "distApp", "BetterDiscordApp", "package.json"), JSON.stringify(BDPackageJSON), "utf8")
|
(filepath) => {
|
||||||
const files = [
|
if (filepath.endsWith(".js")) return true;
|
||||||
"index.min.js",
|
return false;
|
||||||
"style.min.css"
|
},
|
||||||
]
|
async (filepath, newpath) => {
|
||||||
files.forEach(e => {
|
console.info(`Minifying ${filepath} to ${newpath}`);
|
||||||
files.push(e + ".map")
|
await fs.promises.writeFile(
|
||||||
})
|
newpath,
|
||||||
files.forEach(e => {
|
(await terser.minify(await fs.promises.readFile(filepath, "utf8"))).code,
|
||||||
const pth = path.join(PROJECT_DIR, "BetterDiscordApp", "dist", e)
|
"utf8"
|
||||||
if(!fs.existsSync(pth))return console.error(`\x1b[31mFile ${pth} from betterdiscord does not exist.\x1b[0m`)
|
);
|
||||||
if(e.endsWith(".map")){
|
}
|
||||||
const data = JSON.parse(fs.readFileSync(pth, "utf8"))
|
).then(() => {
|
||||||
data.sourcesContent = []
|
console.info(
|
||||||
fs.writeFileSync(path.join(PROJECT_DIR, "distApp", "BetterDiscordApp", "dist", e), JSON.stringify(data))
|
`Copied files and minified them from ${path.join(PROJECT_DIR, "splash")}.`
|
||||||
}else{
|
);
|
||||||
fs.copyFileSync(pth, path.join(PROJECT_DIR, "distApp", "BetterDiscordApp", "dist", e))
|
});
|
||||||
}
|
fs.writeFileSync(
|
||||||
})
|
path.join(PROJECT_DIR, "distApp", "LICENSE"),
|
||||||
|
fs.readFileSync(path.join(PROJECT_DIR, "LICENSE"))
|
||||||
await fs.promises.mkdir(path.join(PROJECT_DIR, "distApp", "splash", "videos"), {recursive: true})
|
);
|
||||||
await processNextDir(path.join(PROJECT_DIR, "splash"), {
|
|
||||||
startDir: path.join(PROJECT_DIR, "splash"),
|
let packageJSON = require("../package.json");
|
||||||
newDir: path.join(PROJECT_DIR, "distApp", "splash"),
|
packageJSON.scripts["build:electron_linux"] = packageJSON.scripts[
|
||||||
exclude: /node_modules/g
|
"build:electron_linux"
|
||||||
}, (filepath) => {
|
].replace("./distApp", ".");
|
||||||
if(filepath.endsWith(".js"))return true
|
packageJSON.scripts["build:electron_win"] = packageJSON.scripts[
|
||||||
return false
|
"build:electron_win"
|
||||||
}, async (filepath, newpath) => {
|
].replace("./distApp", ".");
|
||||||
console.info(`Minifying ${filepath} to ${newpath}`)
|
packageJSON.scripts["build:electron_mac"] = packageJSON.scripts[
|
||||||
await fs.promises.writeFile(newpath, terser.minify(await fs.promises.readFile(filepath, "utf8")).code, "utf8")
|
"build:electron_mac"
|
||||||
}).then(() => {
|
].replace("./distApp", ".");
|
||||||
console.info(`Copied files and minified them from ${path.join(PROJECT_DIR, "splash")}.`)
|
|
||||||
})
|
fs.writeFileSync(
|
||||||
fs.writeFileSync(path.join(PROJECT_DIR, "distApp", "LICENSE"), fs.readFileSync(path.join(PROJECT_DIR, "LICENSE")))
|
path.join(PROJECT_DIR, "distApp", "package.json"),
|
||||||
|
JSON.stringify(packageJSON),
|
||||||
let packageJSON = require("../package.json")
|
"utf8"
|
||||||
packageJSON.scripts["build:electron_linux"] = packageJSON.scripts["build:electron_linux"].replace("./distApp", ".")
|
);
|
||||||
packageJSON.scripts["build:electron_win"] = packageJSON.scripts["build:electron_win"].replace("./distApp", ".")
|
|
||||||
packageJSON.scripts["build:electron_mac"] = packageJSON.scripts["build:electron_mac"].replace("./distApp", ".")
|
console.info(
|
||||||
|
`Installing ${Object.keys(packageJSON.dependencies).length} packages...`
|
||||||
fs.writeFileSync(path.join(PROJECT_DIR, "distApp", "package.json"), JSON.stringify(packageJSON), "utf8")
|
);
|
||||||
|
child_process.execSync("yarn --production", {
|
||||||
console.info(`Installing ${Object.keys(packageJSON.dependencies).length} packages...`)
|
encoding: "binary",
|
||||||
child_process.execSync("yarn --production", {
|
cwd: path.join(PROJECT_DIR, "distApp"),
|
||||||
encoding: "binary",
|
stdio: "inherit",
|
||||||
cwd: path.join(PROJECT_DIR, "distApp"),
|
});
|
||||||
stdio: "inherit"
|
console.info("Build took " + (Date.now() - startTimestamp) + "ms.");
|
||||||
})
|
|
||||||
console.info("Build took "+(Date.now() - startTimestamp) +"ms.")
|
|
||||||
}
|
}
|
||||||
main()
|
main().catch((err) => {
|
||||||
.catch(err => {
|
console.error(err);
|
||||||
console.error(err)
|
process.exit(1);
|
||||||
process.exit(1)
|
});
|
||||||
})
|
|
||||||
|
|
Loading…
Reference in New Issue