Merge branch 'master' into use-ToastXML

This commit is contained in:
Araxeus
2023-01-14 16:08:34 +02:00
committed by GitHub
24 changed files with 631 additions and 99 deletions

View File

@ -96,9 +96,79 @@ const defaultConfig = {
"saveSize": false, "saveSize": false,
"hotkey": "P" "hotkey": "P"
}, },
"captions-selector": {
enabled: false,
disableCaptions: false
},
"skip-silences": { "skip-silences": {
onlySkipBeginning: false, onlySkipBeginning: false,
}, },
visualizer: {
enabled: false,
type: "butterchurn",
// Config per visualizer
butterchurn: {
preset: "martin [shadow harlequins shape code] - fata morgana",
renderingFrequencyInMs: 500,
blendTimeInSeconds: 2.7,
},
vudio: {
effect: "lighting",
accuracy: 128,
lighting: {
maxHeight: 160,
maxSize: 12,
lineWidth: 1,
color: "#49f3f7",
shadowBlur: 2,
shadowColor: "rgba(244,244,244,.5)",
fadeSide: true,
prettify: false,
horizontalAlign: "center",
verticalAlign: "middle",
dottify: true,
},
},
wave: {
animations: [
{
type: "Cubes",
config: {
bottom: true,
count: 30,
cubeHeight: 5,
fillColor: { gradient: ["#FAD961", "#F76B1C"] },
lineColor: "rgba(0,0,0,0)",
radius: 20,
},
},
{
type: "Cubes",
config: {
top: true,
count: 12,
cubeHeight: 5,
fillColor: { gradient: ["#FAD961", "#F76B1C"] },
lineColor: "rgba(0,0,0,0)",
radius: 10,
},
},
{
type: "Circles",
config: {
lineColor: {
gradient: ["#FAD961", "#FAD961", "#F76B1C"],
rotate: 90,
},
lineWidth: 4,
diameter: 20,
count: 10,
frequencyBand: "base",
},
},
],
},
},
}, },
}; };

View File

@ -9,14 +9,15 @@ const setDefaultPluginOptions = (store, plugin) => {
} }
const migrations = { const migrations = {
">1.19.0": (store) => { ">=1.20.0": (store) => {
if (store.get("plugins.notifications.toastStyle") === undefined) { setDefaultPluginOptions(store, "visualizer");
if (store.get("plugins.notifications.toastStyle") === undefined) {
const pluginOptions = store.get("plugins.notifications") || {}; const pluginOptions = store.get("plugins.notifications") || {};
store.set("plugins.notifications", { store.set("plugins.notifications", {
...defaults.plugins.notifications, ...defaults.plugins.notifications,
...pluginOptions, ...pluginOptions,
}); });
}
}, },
">=1.17.0": (store) => { ">=1.17.0": (store) => {
setDefaultPluginOptions(store, "picture-in-picture"); setDefaultPluginOptions(store, "picture-in-picture");

View File

@ -96,12 +96,15 @@
"@cliqz/adblocker-electron": "^1.25.1", "@cliqz/adblocker-electron": "^1.25.1",
"@ffmpeg/core": "^0.11.0", "@ffmpeg/core": "^0.11.0",
"@ffmpeg/ffmpeg": "^0.11.6", "@ffmpeg/ffmpeg": "^0.11.6",
"@foobar404/wave": "^2.0.4",
"Simple-YouTube-Age-Restriction-Bypass": "https://gitpkg.now.sh/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/dist?v2.5.4", "Simple-YouTube-Age-Restriction-Bypass": "https://gitpkg.now.sh/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/dist?v2.5.4",
"async-mutex": "^0.4.0", "async-mutex": "^0.4.0",
"browser-id3-writer": "^4.4.0", "browser-id3-writer": "^4.4.0",
"butterchurn": "^2.6.7",
"butterchurn-presets": "^2.4.7",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
"custom-electron-prompt": "^1.5.0", "custom-electron-prompt": "^1.5.0",
"custom-electron-titlebar": "^4.1.2", "custom-electron-titlebar": "^4.1.5",
"discord-rpc": "^4.0.1", "discord-rpc": "^4.0.1",
"electron-better-web-request": "^1.0.1", "electron-better-web-request": "^1.0.1",
"electron-debug": "^3.2.0", "electron-debug": "^3.2.0",
@ -111,11 +114,11 @@
"electron-unhandled": "^4.0.1", "electron-unhandled": "^4.0.1",
"electron-updater": "^4.6.3", "electron-updater": "^4.6.3",
"filenamify": "^4.3.0", "filenamify": "^4.3.0",
"hark": "^1.2.3",
"html-to-text": "^8.2.1", "html-to-text": "^8.2.1",
"md5": "^2.3.0", "md5": "^2.3.0",
"mpris-service": "^2.1.2", "mpris-service": "^2.1.2",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.7",
"vudio": "^2.1.1",
"ytdl-core": "^4.11.1", "ytdl-core": "^4.11.1",
"ytpl": "^2.3.0" "ytpl": "^2.3.0"
}, },

View File

@ -1,5 +1,5 @@
const applyCompressor = () => { const applyCompressor = (e) => {
const audioContext = new AudioContext(); const audioContext = e.detail.audioContext;
const compressor = audioContext.createDynamicsCompressor(); const compressor = audioContext.createDynamicsCompressor();
compressor.threshold.value = -50; compressor.threshold.value = -50;
@ -8,10 +8,11 @@ const applyCompressor = () => {
compressor.attack.value = 0; compressor.attack.value = 0;
compressor.release.value = 0.25; compressor.release.value = 0.25;
const source = audioContext.createMediaElementSource(document.querySelector("video")); e.detail.audioSource.connect(compressor);
source.connect(compressor);
compressor.connect(audioContext.destination); compressor.connect(audioContext.destination);
}; };
module.exports = () => document.addEventListener('apiLoaded', applyCompressor, { once: true, passive: true }); module.exports = () =>
document.addEventListener("audioCanPlay", applyCompressor, {
passive: true,
});

View File

@ -0,0 +1,15 @@
const { ipcMain, dialog } = require("electron");
module.exports = () => {
ipcMain.handle('captionsSelector', async (_, captionLabels, currentIndex) => {
return await dialog.showMessageBox({
type: "question",
buttons: captionLabels,
defaultId: currentIndex,
title: "Choose Caption",
message: "Choose Caption:",
detail: `Current Caption: ${captionLabels[currentIndex]}`,
cancelId: -1
})
})
};

View File

@ -0,0 +1,60 @@
const { ElementFromFile, templatePath } = require("../utils");
const { ipcRenderer } = require("electron");
function $(selector) { return document.querySelector(selector); }
const captionsSettingsButton = ElementFromFile(
templatePath(__dirname, "captionsSettingsTemplate.html")
);
module.exports = (options) => {
document.addEventListener('apiLoaded', (event) => setup(event, options), { once: true, passive: true });
}
/**
* If captions are disabled by default,
* unload "captions" module when video changes.
*/
const videoChanged = (api, options) => {
if (options.disableCaptions) {
setTimeout(() => api.unloadModule("captions"), 100);
}
}
function setup(event, options) {
const api = event.detail;
$("video").addEventListener("srcChanged", () => videoChanged(api, options));
$(".right-controls-buttons").append(captionsSettingsButton);
captionsSettingsButton.onclick = function chooseQuality() {
api.loadModule("captions");
const captionTrackList = api.getOption("captions", "tracklist");
if (captionTrackList?.length) {
const currentCaptionTrack = api.getOption("captions", "track");
const currentIndex = captionTrackList.indexOf(captionTrackList.find(track => track.languageCode === currentCaptionTrack.languageCode));
const captionLabels = [
...captionTrackList.map(track => track.displayName),
'None'
];
ipcRenderer.invoke('captionsSelector', captionLabels, currentIndex).then(promise => {
if (promise.response === -1) return;
const newCaptions = captionTrackList[promise.response];
if (newCaptions) {
api.loadModule("captions");
api.setOption("captions", "track", { languageCode: newCaptions.languageCode });
} else {
api.unloadModule("captions");
}
setTimeout(() => api.playVideo());
});
}
}
}

View File

@ -0,0 +1,12 @@
const { setOptions } = require('../../config/plugins');
module.exports = (_win, options) => [
{
label: "No captions by default",
type: "checkbox",
checked: options.disabledCaptions,
click: (item) => {
setOptions("captions-selector", { disableCaptions: item.checked });
},
}
];

View File

@ -0,0 +1,13 @@
<tp-yt-paper-icon-button class="player-captions-button style-scope ytmusic-player" icon="yt-icons:subtitles"
title="Open captions selector" aria-label="Open captions selector" role="button" tabindex="0" aria-disabled="false">
<tp-yt-iron-icon id="icon" class="style-scope tp-yt-paper-icon-button"><svg viewBox="0 0 24 24"
preserveAspectRatio="xMidYMid meet" focusable="false" class="style-scope yt-icon"
style="pointer-events: none; display: block; width: 100%; height: 100%;">
<g class="style-scope yt-icon">
<path
d="M20 4H4c-1.103 0-2 .897-2 2v12c0 1.103.897 2 2 2h16c1.103 0 2-.897 2-2V6c0-1.103-.897-2-2-2zm-9 6H8v4h3v2H8c-1.103 0-2-.897-2-2v-4c0-1.103.897-2 2-2h3v2zm7 0h-3v4h3v2h-3c-1.103 0-2-.897-2-2v-4c0-1.103.897-2 2-2h3v2z"
class="style-scope tp-yt-iron-icon"></path>
</g>
</svg>
</tp-yt-iron-icon>
</tp-yt-paper-icon-button>

View File

@ -47,6 +47,7 @@ const togglePiP = async () => {
win.webContents.on("before-input-event", blockShortcutsInPiP); win.webContents.on("before-input-event", blockShortcutsInPiP);
win.setMaximizable(false);
win.setFullScreenable(false); win.setFullScreenable(false);
runAdaptors(); runAdaptors();
@ -62,6 +63,7 @@ const togglePiP = async () => {
} }
} else { } else {
win.webContents.removeListener("before-input-event", blockShortcutsInPiP); win.webContents.removeListener("before-input-event", blockShortcutsInPiP);
win.setMaximizable(true);
win.setFullScreenable(true); win.setFullScreenable(true);
runAdaptors(); runAdaptors();

View File

@ -10,6 +10,21 @@ const pipButton = ElementFromFile(
templatePath(__dirname, "picture-in-picture.html") templatePath(__dirname, "picture-in-picture.html")
); );
// will also clone
function replaceButton(query, button) {
const svg = button.querySelector("#icon svg").cloneNode(true);
button.replaceWith(button.cloneNode(true));
button.remove();
const newButton = $(query);
newButton.querySelector("#icon").appendChild(svg);
return newButton;
}
function cloneButton(query) {
replaceButton(query, $(query));
return $(query);
}
const observer = new MutationObserver(() => { const observer = new MutationObserver(() => {
if (!menu) { if (!menu) {
menu = getSongMenu(); menu = getSongMenu();
@ -30,9 +45,6 @@ global.togglePictureInPicture = () => {
const listenForToggle = () => { const listenForToggle = () => {
const originalExitButton = $(".exit-fullscreen-button"); const originalExitButton = $(".exit-fullscreen-button");
const clonedExitButton = originalExitButton.cloneNode(true);
clonedExitButton.onclick = () => togglePictureInPicture();
const appLayout = $("ytmusic-app-layout"); const appLayout = $("ytmusic-app-layout");
const expandMenu = $('#expanding-menu'); const expandMenu = $('#expanding-menu');
const middleControls = $('.middle-controls'); const middleControls = $('.middle-controls');
@ -44,7 +56,7 @@ const listenForToggle = () => {
ipcRenderer.on('pip-toggle', (_, isPip) => { ipcRenderer.on('pip-toggle', (_, isPip) => {
if (isPip) { if (isPip) {
$(".exit-fullscreen-button").replaceWith(clonedExitButton); replaceButton(".exit-fullscreen-button", originalExitButton).onclick = () => togglePictureInPicture();
player.onDoubleClick_ = () => {}; player.onDoubleClick_ = () => {};
expandMenu.onmouseleave = () => middleControls.click(); expandMenu.onmouseleave = () => middleControls.click();
if (!playerPage.playerPageOpen_) { if (!playerPage.playerPageOpen_) {
@ -67,10 +79,8 @@ function observeMenu(options) {
"apiLoaded", "apiLoaded",
() => { () => {
listenForToggle(); listenForToggle();
const minButton = $(".player-minimize-button");
// remove native listeners // remove native listeners
minButton.replaceWith(minButton.cloneNode(true)); cloneButton(".player-minimize-button").onclick = () => {
$(".player-minimize-button").onclick = () => {
global.togglePictureInPicture(); global.togglePictureInPicture();
setTimeout(() => $('#player').click()); setTimeout(() => $('#player').click());
}; };

View File

@ -1,53 +1,112 @@
const hark = require("hark/hark.bundle.js");
module.exports = (options) => { module.exports = (options) => {
let isSilent = false; let isSilent = false;
let hasAudioStarted = false; let hasAudioStarted = false;
document.addEventListener("apiLoaded", () => { const smoothing = 0.1;
const video = document.querySelector("video"); const threshold = -100; // dB (-100 = absolute silence, 0 = loudest)
const speechEvents = hark(video, { const interval = 2; // ms
threshold: -100, // dB (-100 = absolute silence, 0 = loudest) const history = 10;
interval: 2, // ms const speakingHistory = Array(history).fill(0);
});
const skipSilence = () => {
if (options.onlySkipBeginning && hasAudioStarted) {
return;
}
if (isSilent && !video.paused) { document.addEventListener(
video.currentTime += 0.2; // in s "audioCanPlay",
} (e) => {
}; const video = document.querySelector("video");
const audioContext = e.detail.audioContext;
const sourceNode = e.detail.audioSource;
speechEvents.on("speaking", function () { // Use an audio analyser similar to Hark
isSilent = false; // https://github.com/otalk/hark/blob/master/hark.bundle.js
hasAudioStarted = true; const analyser = audioContext.createAnalyser();
}); analyser.fftSize = 512;
analyser.smoothingTimeConstant = smoothing;
const fftBins = new Float32Array(analyser.frequencyBinCount);
speechEvents.on("stopped_speaking", function () { sourceNode.connect(analyser);
if ( analyser.connect(audioContext.destination);
!(
video.paused || const looper = () => {
video.seeking || setTimeout(() => {
video.ended || const currentVolume = getMaxVolume(analyser, fftBins);
video.muted ||
video.volume === 0 let history = 0;
) if (currentVolume > threshold && isSilent) {
) { // trigger quickly, short history
isSilent = true; for (
let i = speakingHistory.length - 3;
i < speakingHistory.length;
i++
) {
history += speakingHistory[i];
}
if (history >= 2) {
// Not silent
isSilent = false;
hasAudioStarted = true;
}
} else if (currentVolume < threshold && !isSilent) {
for (let i = 0; i < speakingHistory.length; i++) {
history += speakingHistory[i];
}
if (history == 0) {
// Silent
if (
!(
video.paused ||
video.seeking ||
video.ended ||
video.muted ||
video.volume === 0
)
) {
isSilent = true;
skipSilence();
}
}
}
speakingHistory.shift();
speakingHistory.push(0 + (currentVolume > threshold));
looper();
}, interval);
};
looper();
const skipSilence = () => {
if (options.onlySkipBeginning && hasAudioStarted) {
return;
}
if (isSilent && !video.paused) {
video.currentTime += 0.2; // in s
}
};
video.addEventListener("play", function () {
hasAudioStarted = false;
skipSilence(); skipSilence();
} });
});
video.addEventListener("play", function () { video.addEventListener("seeked", function () {
hasAudioStarted = false; hasAudioStarted = false;
skipSilence(); skipSilence();
}); });
},
video.addEventListener("seeked", function () { {
hasAudioStarted = false; passive: true,
skipSilence(); }
}); );
});
}; };
function getMaxVolume(analyser, fftBins) {
var maxVolume = -Infinity;
analyser.getFloatFrequencyData(fftBins);
for (var i = 4, ii = fftBins.length; i < ii; i++) {
if (fftBins[i] > maxVolume && fftBins[i] < 0) {
maxVolume = fftBins[i];
}
}
return maxVolume;
}

View File

@ -63,7 +63,7 @@ module.exports = (win) => {
// If the page is ready, register the callback // If the page is ready, register the callback
win.once("ready-to-show", () => { win.once("ready-to-show", () => {
controls = [previous, playPause, next, like, dislike]; controls = [previous, playPause, next, dislike, like];
// Register the callback // Register the callback
registerCallback((songInfo) => { registerCallback((songInfo) => {

View File

@ -22,7 +22,6 @@
color: #fff; color: #fff;
padding-right: 120px; padding-right: 120px;
position: absolute; position: absolute;
left: var(--align);
} }
.video-switch-button:before { .video-switch-button:before {

View File

@ -31,21 +31,6 @@ module.exports = (_options) => {
document.addEventListener("apiLoaded", setup, { once: true, passive: true }); document.addEventListener("apiLoaded", setup, { once: true, passive: true });
} }
} }
const mainpanel = document.querySelector("#main-panel");
switch (_options.align) {
case "right": {
mainpanel.style.setProperty("--align", "calc(100% - 240px)");
return;
}
case "middle": {
mainpanel.style.setProperty("--align", "calc(50% - 120px)");
return;
}
default:
case "left": {
mainpanel.style.setProperty("--align", "0px");
}
}
}; };
function setup(e) { function setup(e) {
@ -73,6 +58,21 @@ function setup(e) {
video.addEventListener('srcChanged', videoStarted); video.addEventListener('srcChanged', videoStarted);
observeThumbnail(); observeThumbnail();
switch (options.align) {
case "right": {
switchButtonDiv.style.left = "calc(100% - 240px)";
return;
}
case "middle": {
switchButtonDiv.style.left = "calc(50% - 120px)";
return;
}
default:
case "left": {
switchButtonDiv.style.left = "0px";
}
}
} }
function changeDisplay(showVideo) { function changeDisplay(showVideo) {

View File

@ -0,0 +1,6 @@
const { injectCSS } = require("../utils");
const path = require("path");
module.exports = (win, options) => {
injectCSS(win.webContents, path.join(__dirname, "empty-player.css"));
};

View File

@ -0,0 +1,9 @@
#player {
margin: 0 !important;
background: black;
}
#song-image,
#song-video {
display: none !important;
}

View File

@ -0,0 +1,61 @@
const defaultConfig = require("../../config/defaults");
module.exports = (options) => {
const optionsWithDefaults = {
...defaultConfig.plugins.visualizer,
...options,
};
const VisualizerType = require(`./visualizers/${optionsWithDefaults.type}`);
document.addEventListener(
"audioCanPlay",
(e) => {
const video = document.querySelector("video");
const visualizerContainer = document.querySelector("#player");
let canvas = document.getElementById("visualizer");
if (!canvas) {
canvas = document.createElement("canvas");
canvas.id = "visualizer";
canvas.style.position = "absolute";
canvas.style.background = "black";
visualizerContainer.append(canvas);
}
const resizeCanvas = () => {
canvas.width = visualizerContainer.clientWidth;
canvas.height = visualizerContainer.clientHeight;
};
resizeCanvas();
const gainNode = e.detail.audioContext.createGain();
gainNode.gain.value = 1.25;
e.detail.audioSource.connect(gainNode);
const visualizer = new VisualizerType(
e.detail.audioContext,
e.detail.audioSource,
visualizerContainer,
canvas,
gainNode,
video.captureStream(),
optionsWithDefaults[optionsWithDefaults.type]
);
const resizeVisualizer = (width, height) => {
resizeCanvas();
visualizer.resize(width, height);
};
resizeVisualizer(canvas.width, canvas.height);
const visualizerContainerObserver = new ResizeObserver((entries) => {
entries.forEach((entry) => {
resizeVisualizer(entry.contentRect.width, entry.contentRect.height);
});
});
visualizerContainerObserver.observe(visualizerContainer);
visualizer.render();
},
{ passive: true }
);
};

View File

@ -0,0 +1,23 @@
const { readdirSync } = require("fs");
const path = require("path");
const { setMenuOptions } = require("../../config/plugins");
const visualizerTypes = readdirSync(path.join(__dirname, "visualizers")).map(
(filename) => path.parse(filename).name
);
module.exports = (win, options) => [
{
label: "Type",
submenu: visualizerTypes.map((visualizerType) => ({
label: visualizerType,
type: "radio",
checked: options.type === visualizerType,
click: () => {
options.type = visualizerType;
setMenuOptions("visualizer", options);
},
})),
},
];

View File

@ -0,0 +1,46 @@
const butterchurn = require("butterchurn");
const butterchurnPresets = require("butterchurn-presets");
const presets = butterchurnPresets.getPresets();
class ButterchurnVisualizer {
constructor(
audioContext,
audioSource,
visualizerContainer,
canvas,
audioNode,
stream,
options
) {
this.visualizer = butterchurn.default.createVisualizer(
audioContext,
canvas,
{
width: canvas.width,
height: canvas.height,
}
);
const preset = presets[options.preset];
this.visualizer.loadPreset(preset, options.blendTimeInSeconds);
this.visualizer.connectAudio(audioNode);
this.renderingFrequencyInMs = options.renderingFrequencyInMs;
}
resize(width, height) {
this.visualizer.setRendererSize(width, height);
}
render() {
const renderVisualizer = () => {
requestAnimationFrame(() => renderVisualizer());
this.visualizer.render();
};
setTimeout(renderVisualizer(), this.renderingFrequencyInMs);
}
}
module.exports = ButterchurnVisualizer;

View File

@ -0,0 +1,33 @@
const Vudio = require("vudio/umd/vudio");
class VudioVisualizer {
constructor(
audioContext,
audioSource,
visualizerContainer,
canvas,
audioNode,
stream,
options
) {
this.visualizer = new Vudio(stream, canvas, {
width: canvas.width,
height: canvas.height,
// Visualizer config
...options,
});
}
resize(width, height) {
this.visualizer.setOption({
width: width,
height: height,
});
}
render() {
this.visualizer.dance();
}
}
module.exports = VudioVisualizer;

View File

@ -0,0 +1,31 @@
const { Wave } = require("@foobar404/wave");
class WaveVisualizer {
constructor(
audioContext,
audioSource,
visualizerContainer,
canvas,
audioNode,
stream,
options
) {
this.visualizer = new Wave(
{ context: audioContext, source: audioSource },
canvas
);
options.animations.forEach((animation) => {
this.visualizer.addAnimation(
eval(`new this.visualizer.animations.${animation.type}(
${JSON.stringify(animation.config)}
)`)
);
});
}
resize(width, height) {}
render() {}
}
module.exports = WaveVisualizer;

View File

@ -97,6 +97,32 @@ function listenForApiLoad() {
} }
function onApiLoaded() { function onApiLoaded() {
const video = document.querySelector("video");
const audioContext = new AudioContext();
const audioSource = audioContext.createMediaElementSource(video);
video.addEventListener(
"loadstart",
() => {
// Emit "audioCanPlay" for each video
video.addEventListener(
"canplaythrough",
() => {
document.dispatchEvent(
new CustomEvent("audioCanPlay", {
detail: {
audioContext: audioContext,
audioSource: audioSource,
},
})
);
},
{ once: true }
);
},
{ passive: true }
);
document.dispatchEvent(new CustomEvent('apiLoaded', { detail: api })); document.dispatchEvent(new CustomEvent('apiLoaded', { detail: api }));
ipcRenderer.send('apiLoaded'); ipcRenderer.send('apiLoaded');

View File

@ -108,6 +108,8 @@ winget install th-ch.YouTubeMusic
- **Video Toggle**: Adds a [button](https://user-images.githubusercontent.com/28893833/173663950-63e6610e-a532-49b7-9afa-54cb57ddfc15.png) to switch between Video/Song mode. can also optionally remove the whole video tab - **Video Toggle**: Adds a [button](https://user-images.githubusercontent.com/28893833/173663950-63e6610e-a532-49b7-9afa-54cb57ddfc15.png) to switch between Video/Song mode. can also optionally remove the whole video tab
- **Visualizer**: Different music visualizers
--- ---
- **Auto confirm when paused** (Always Enabled): disable the ["Continue Watching?"](https://user-images.githubusercontent.com/61631665/129977894-01c60740-7ec6-4bf0-9a2c-25da24491b0e.png) popup that pause music after a certain time - **Auto confirm when paused** (Always Enabled): disable the ["Continue Watching?"](https://user-images.githubusercontent.com/61631665/129977894-01c60740-7ec6-4bf0-9a2c-25da24491b0e.png) popup that pause music after a certain time

View File

@ -202,6 +202,13 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.15.tgz#8e66775fb523599acb6a289e12929fa5ab0954d8" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.15.tgz#8e66775fb523599acb6a289e12929fa5ab0954d8"
integrity sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ== integrity sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==
"@babel/runtime@^7.0.0":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd"
integrity sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==
dependencies:
regenerator-runtime "^0.13.11"
"@babel/runtime@^7.7.2": "@babel/runtime@^7.7.2":
version "7.12.13" version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.13.tgz#0a21452352b02542db0ffb928ac2d3ca7cb6d66d" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.13.tgz#0a21452352b02542db0ffb928ac2d3ca7cb6d66d"
@ -374,6 +381,11 @@
regenerator-runtime "^0.13.7" regenerator-runtime "^0.13.7"
resolve-url "^0.2.1" resolve-url "^0.2.1"
"@foobar404/wave@^2.0.4":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@foobar404/wave/-/wave-2.0.4.tgz#c9bc54c41b18642c6a4587851e28b8f858af98b0"
integrity sha512-FEyg37hDvQtrQVlFxbit7ov5e487BjsR32bZfJ4oAb5i+NnlbGaNyy6iYBZ8ocVHo8fgug+SL+mFdDTzqjvPww==
"@humanwhocodes/config-array@^0.5.0": "@humanwhocodes/config-array@^0.5.0":
version "0.5.0" version "0.5.0"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9"
@ -1364,6 +1376,14 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
babel-runtime@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
integrity sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==
dependencies:
core-js "^2.4.0"
regenerator-runtime "^0.11.0"
balanced-match@^1.0.0: balanced-match@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
@ -1557,6 +1577,23 @@ builtin-modules@^3.0.0:
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887"
integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA== integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==
butterchurn-presets@^2.4.7:
version "2.4.7"
resolved "https://registry.yarnpkg.com/butterchurn-presets/-/butterchurn-presets-2.4.7.tgz#41e4e37cd3af2aec124fa8062352816100956c29"
integrity sha512-4MdM8ripz/VfH1BCldrIKdAc/1ryJFBDvqlyow6Ivo1frwj0H3duzvSMFC7/wIjAjxb1QpwVHVqGqS9uAFKhpg==
dependencies:
babel-runtime "^6.26.0"
ecma-proposal-math-extensions "0.0.2"
lodash "^4.17.4"
butterchurn@^2.6.7:
version "2.6.7"
resolved "https://registry.yarnpkg.com/butterchurn/-/butterchurn-2.6.7.tgz#1ff0c1365731a4fb7ada7bb16ae1c6f09a110c12"
integrity sha512-BJiRA8L0L2+84uoG2SSfkp0kclBuN+vQKf217pK7pMlwEO2ZEg3MtO2/o+l8Qpr8Nbejg8tmL1ZHD1jmhiaaqg==
dependencies:
"@babel/runtime" "^7.0.0"
ecma-proposal-math-extensions "0.0.2"
cacheable-request@^6.0.0: cacheable-request@^6.0.0:
version "6.1.0" version "6.1.0"
resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912"
@ -1892,6 +1929,11 @@ convert-source-map@^1.7.0:
dependencies: dependencies:
safe-buffer "~5.1.1" safe-buffer "~5.1.1"
core-js@^2.4.0:
version "2.6.12"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
core-util-is@1.0.2, core-util-is@~1.0.0: core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@ -1939,10 +1981,10 @@ custom-electron-prompt@^1.5.0:
resolved "https://registry.yarnpkg.com/custom-electron-prompt/-/custom-electron-prompt-1.5.0.tgz#b514267f28e9f0d61011e03f76b1e59473af33d4" resolved "https://registry.yarnpkg.com/custom-electron-prompt/-/custom-electron-prompt-1.5.0.tgz#b514267f28e9f0d61011e03f76b1e59473af33d4"
integrity sha512-DO+CIfO8c5lG+yzAkXD8PbFunPQ+WCJ4QeGN8bCvos7Fxt3xFDW0Qdnm1v9DKkAMj7iG0SujhdfNzsrtA4fl5g== integrity sha512-DO+CIfO8c5lG+yzAkXD8PbFunPQ+WCJ4QeGN8bCvos7Fxt3xFDW0Qdnm1v9DKkAMj7iG0SujhdfNzsrtA4fl5g==
custom-electron-titlebar@^4.1.2: custom-electron-titlebar@^4.1.5:
version "4.1.3" version "4.1.5"
resolved "https://registry.yarnpkg.com/custom-electron-titlebar/-/custom-electron-titlebar-4.1.3.tgz#6b5d0527858bae89314f3d0b4e6a0b9be4efad81" resolved "https://registry.yarnpkg.com/custom-electron-titlebar/-/custom-electron-titlebar-4.1.5.tgz#5032759ee5594fd618a62cff0de46150fd8e2b24"
integrity sha512-9tqiRxp7KG3qgS5Qh0ejSTwzqJ/pkB8RXQrvZHmilIzaWFmvjHiaSnHgCj+1iJcnhvzACYXFiFo2fxD64kFi4A== integrity sha512-KpVmO+Fz34zNw/jYbOD6YwxPr6h2hgZmNbjy0AvhlXiH4dxeZJXDlMcBX08NqsESymMQvIWwsGaA//YGW1bXjA==
dashdash@^1.12.0: dashdash@^1.12.0:
version "1.14.1" version "1.14.1"
@ -2266,6 +2308,11 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0" jsbn "~0.1.0"
safer-buffer "^2.1.0" safer-buffer "^2.1.0"
ecma-proposal-math-extensions@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/ecma-proposal-math-extensions/-/ecma-proposal-math-extensions-0.0.2.tgz#a6a3d64819db70cee0d2e1976b6315d00e4714a0"
integrity sha512-80BnDp2Fn7RxXlEr5HHZblniY4aQ97MOAicdWWpSo0vkQiISSE9wLR4SqxKsu4gCtXFBIPPzy8JMhay4NWRg/Q==
ejs@^3.1.6: ejs@^3.1.6:
version "3.1.7" version "3.1.7"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.7.tgz#c544d9c7f715783dd92f0bddcf73a59e6962d006" resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.7.tgz#c544d9c7f715783dd92f0bddcf73a59e6962d006"
@ -3522,13 +3569,6 @@ hard-rejection@^2.1.0:
resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883"
integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==
hark@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/hark/-/hark-1.2.3.tgz#959981400f561be5580ecd4321a9f55b16bacbd0"
integrity sha512-u68vz9SCa38ESiFJSDjqK8XbXqWzyot7Cj6Y2b6jk2NJ+II3MY2dIrLMg/kjtIAun4Y1DHF/20hfx4rq1G5GMg==
dependencies:
wildemitter "^1.2.0"
has-ansi@^2.0.0: has-ansi@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
@ -4289,9 +4329,9 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
json5@^1.0.1: json5@^1.0.1:
version "1.0.1" version "1.0.2"
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==
dependencies: dependencies:
minimist "^1.2.0" minimist "^1.2.0"
@ -4527,7 +4567,7 @@ lodash.truncate@^4.4.2:
resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=
lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.3.0: lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.3.0:
version "4.17.21" version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@ -5595,6 +5635,16 @@ redent@^4.0.0:
indent-string "^5.0.0" indent-string "^5.0.0"
strip-indent "^4.0.0" strip-indent "^4.0.0"
regenerator-runtime@^0.11.0:
version "0.11.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
regenerator-runtime@^0.13.11:
version "0.13.11"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7: regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7:
version "0.13.7" version "0.13.7"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
@ -6562,6 +6612,11 @@ verror@1.10.0, verror@^1.10.0:
core-util-is "1.0.2" core-util-is "1.0.2"
extsprintf "^1.2.0" extsprintf "^1.2.0"
vudio@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/vudio/-/vudio-2.1.1.tgz#af256c4e1c8ae8bdbbba0e718f8106a30a44e96e"
integrity sha512-VkFQcFt/b/kpF5Eg5Sq+oXUo1Zp5aRFF4BSmIrOzau5o+5WMWwX9ae/EGJZstCyZFiCTU5iw1Y+u2BCGW6Y6Jw==
webidl-conversions@^3.0.0: webidl-conversions@^3.0.0:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
@ -6612,11 +6667,6 @@ widest-line@^3.1.0:
dependencies: dependencies:
string-width "^4.0.0" string-width "^4.0.0"
wildemitter@^1.2.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/wildemitter/-/wildemitter-1.2.1.tgz#9da3b5ca498e4378628d1783145493c70a10b774"
integrity sha512-UMmSUoIQSir+XbBpTxOTS53uJ8s/lVhADCkEbhfRjUGFDPme/XGOb0sBWLx5sTz7Wx/2+TlAw1eK9O5lw5PiEw==
word-wrap@^1.2.3: word-wrap@^1.2.3:
version "1.2.3" version "1.2.3"
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"