use new dynamic config

This commit is contained in:
Araxeus
2023-03-19 23:47:55 +02:00
parent 648d102101
commit 2ad097c743
5 changed files with 127 additions and 112 deletions

View File

@ -108,7 +108,7 @@ const defaultConfig = {
enabled: false, enabled: false,
fadeInDuration: 1500, // ms fadeInDuration: 1500, // ms
fadeOutDuration: 5000, // ms fadeOutDuration: 5000, // ms
exitMusicBeforeEnd: 10, // s secondsBeforeEnd: 10, // s
fadeScaling: "linear", // 'linear', 'logarithmic' or a positive number in dB fadeScaling: "linear", // 'linear', 'logarithmic' or a positive number in dB
}, },
visualizer: { visualizer: {

View File

@ -1,7 +1,9 @@
const { ipcMain } = require("electron"); const { ipcMain } = require("electron");
const { Innertube } = require("youtubei.js"); const { Innertube } = require("youtubei.js");
module.exports = async (win, options) => { require("./config");
module.exports = async () => {
const yt = await Innertube.create(); const yt = await Innertube.create();
ipcMain.handle("audio-url", async (_, videoID) => { ipcMain.handle("audio-url", async (_, videoID) => {

View File

@ -0,0 +1,3 @@
const { PluginConfig } = require("../../config/dynamic");
const config = new PluginConfig("crossfade", { enableFront: true });
module.exports = { ...config };

View File

@ -1,146 +1,156 @@
const { ipcRenderer } = require("electron"); const { ipcRenderer } = require('electron');
const { Howl } = require("howler"); const { Howl } = require('howler');
// Extracted from https://github.com/bitfasching/VolumeFader // Extracted from https://github.com/bitfasching/VolumeFader
require("./fader"); require('./fader');
let transitionAudio; // Howler audio used to fade out the current music let transitionAudio; // Howler audio used to fade out the current music
let firstVideo = true; let firstVideo = true;
let waitForTransition; let waitForTransition;
const config = require('../../config/defaults').plugins.crossfade; const defaultConfig = require('../../config/defaults').plugins.crossfade;
const configProvider = require('./config');
let config = configProvider.getAll();
console.log({ config });
configProvider.subscribeAll((newConfig) => {
config = newConfig;
});
const configGetNum = (key) => Number(config[key]) || defaultConfig[key];
const getStreamURL = async (videoID) => { const getStreamURL = async (videoID) => {
const url = await ipcRenderer.invoke("audio-url", videoID); const url = await ipcRenderer.invoke('audio-url', videoID);
return url; return url;
}; };
const getVideoIDFromURL = (url) => { const getVideoIDFromURL = (url) => {
return new URLSearchParams(url.split("?")?.at(-1)).get("v"); return new URLSearchParams(url.split('?')?.at(-1)).get('v');
}; };
const isReadyToCrossfade = () => { const isReadyToCrossfade = () => {
return transitionAudio && transitionAudio.state() === "loaded"; return transitionAudio && transitionAudio.state() === 'loaded';
}; };
const watchVideoIDChanges = (cb) => { const watchVideoIDChanges = (cb) => {
navigation.addEventListener("navigate", (event) => { navigation.addEventListener('navigate', (event) => {
const currentVideoID = getVideoIDFromURL( const currentVideoID = getVideoIDFromURL(
event.currentTarget.currentEntry.url event.currentTarget.currentEntry.url,
); );
const nextVideoID = getVideoIDFromURL(event.destination.url); const nextVideoID = getVideoIDFromURL(event.destination.url);
if ( if (
nextVideoID && nextVideoID &&
currentVideoID && currentVideoID &&
(firstVideo || nextVideoID !== currentVideoID) (firstVideo || nextVideoID !== currentVideoID)
) { ) {
if (isReadyToCrossfade()) { if (isReadyToCrossfade()) {
crossfade(() => { crossfade(() => {
cb(nextVideoID); cb(nextVideoID);
}); });
} else { } else {
cb(nextVideoID); cb(nextVideoID);
firstVideo = false; firstVideo = false;
} }
} }
}); });
}; };
const createAudioForCrossfade = async (url) => { const createAudioForCrossfade = async (url) => {
if (transitionAudio) { if (transitionAudio) {
transitionAudio.unload(); transitionAudio.unload();
} }
transitionAudio = new Howl({ transitionAudio = new Howl({
src: url, src: url,
html5: true, html5: true,
volume: 0, volume: 0,
}); });
await syncVideoWithTransitionAudio(); await syncVideoWithTransitionAudio();
}; };
const syncVideoWithTransitionAudio = async () => { const syncVideoWithTransitionAudio = async () => {
const video = document.querySelector("video"); const video = document.querySelector('video');
const videoFader = new VolumeFader(video, {
fadeScaling: config.fadeScaling,
fadeDuration: config.fadeInDuration,
});
await transitionAudio.play(); const videoFader = new VolumeFader(video, {
await transitionAudio.seek(video.currentTime); fadeScaling: configGetNum('fadeScaling'),
fadeDuration: configGetNum('fadeInDuration'),
});
video.onseeking = () => { await transitionAudio.play();
transitionAudio.seek(video.currentTime); await transitionAudio.seek(video.currentTime);
};
video.onpause = () => {
transitionAudio.pause();
};
video.onplay = async () => {
await transitionAudio.play();
await transitionAudio.seek(video.currentTime);
// Fade in video.onseeking = () => {
const videoVolume = video.volume; transitionAudio.seek(video.currentTime);
video.volume = 0; };
videoFader.fadeTo(videoVolume); video.onpause = () => {
}; transitionAudio.pause();
};
video.onplay = async () => {
await transitionAudio.play();
await transitionAudio.seek(video.currentTime);
// Exit just before the end for the transition // Fade in
const transitionBeforeEnd = () => { const videoVolume = video.volume;
if ( video.volume = 0;
video.currentTime >= videoFader.fadeTo(videoVolume);
video.duration - config.exitMusicBeforeEnd && };
isReadyToCrossfade()
) {
video.removeEventListener("timeupdate", transitionBeforeEnd);
// Go to next video - XXX: does not support "repeat 1" mode // Exit just before the end for the transition
document.querySelector(".next-button").click(); const transitionBeforeEnd = () => {
} if (
}; video.currentTime >=
video.ontimeupdate = transitionBeforeEnd; video.duration - configGetNum('secondsBeforeEnd') &&
isReadyToCrossfade()
) {
video.removeEventListener('timeupdate', transitionBeforeEnd);
// Go to next video - XXX: does not support "repeat 1" mode
document.querySelector('.next-button').click();
}
};
video.ontimeupdate = transitionBeforeEnd;
}; };
const onApiLoaded = () => { const onApiLoaded = () => {
watchVideoIDChanges(async (videoID) => { watchVideoIDChanges(async (videoID) => {
await waitForTransition; await waitForTransition;
const url = await getStreamURL(videoID); const url = await getStreamURL(videoID);
await createAudioForCrossfade(url); await createAudioForCrossfade(url);
}); });
}; };
const crossfade = (cb) => { const crossfade = async (cb) => {
if (!isReadyToCrossfade()) { if (!isReadyToCrossfade()) {
cb(); cb();
return; return;
} }
let resolveTransition; let resolveTransition;
waitForTransition = new Promise(function (resolve, reject) { waitForTransition = new Promise(function (resolve, reject) {
resolveTransition = resolve; resolveTransition = resolve;
}); });
const video = document.querySelector("video"); const video = document.querySelector('video');
const fader = new VolumeFader(transitionAudio._sounds[0]._node, { const fader = new VolumeFader(transitionAudio._sounds[0]._node, {
initialVolume: video.volume, initialVolume: video.volume,
fadeScaling: config.fadeScaling, fadeScaling: configGetNum('fadeScaling'),
fadeDuration: config.fadeOutDuration, fadeDuration: configGetNum('fadeOutDuration'),
}); });
// Fade out the music // Fade out the music
video.volume = 0; video.volume = 0;
fader.fadeOut(() => { fader.fadeOut(() => {
resolveTransition(); resolveTransition();
cb(); cb();
}); });
}; };
module.exports = (options) => { module.exports = () => {
Object.assign(config, options); document.addEventListener('apiLoaded', onApiLoaded, {
once: true,
document.addEventListener("apiLoaded", onApiLoaded, { passive: true,
once: true, });
passive: true,
});
}; };

View File

@ -1,15 +1,15 @@
const { setOptions } = require("../../config/plugins"); const config = require("./config");
const defaultOptions = require("../../config/defaults").plugins.crossfade; const defaultOptions = require("../../config/defaults").plugins.crossfade;
const prompt = require("custom-electron-prompt"); const prompt = require("custom-electron-prompt");
const promptOptions = require("../../providers/prompt-options"); const promptOptions = require("../../providers/prompt-options");
module.exports = (win, options) => [ module.exports = (win) => [
{ {
label: "Advanced", label: "Advanced",
click: async () => { click: async () => {
const newOptions = await promptCrossfadeValues(win, options); const newOptions = await promptCrossfadeValues(win, config.getAll());
setOptions("crossfade", { ...options, ...newOptions }); if (newOptions) config.setAll(newOptions);
}, },
}, },
]; ];
@ -43,7 +43,7 @@ async function promptCrossfadeValues(win, options) {
{ {
label: "Crossfade x seconds before end", label: "Crossfade x seconds before end",
value: value:
options.exitMusicBeforeEnd || defaultOptions.exitMusicBeforeEnd, options.secondsBeforeEnd || defaultOptions.secondsBeforeEnd,
inputAttrs: { inputAttrs: {
type: "number", type: "number",
required: true, required: true,
@ -66,7 +66,7 @@ async function promptCrossfadeValues(win, options) {
return { return {
fadeInDuration: Number(res[0]), fadeInDuration: Number(res[0]),
fadeOutDuration: Number(res[1]), fadeOutDuration: Number(res[1]),
exitMusicBeforeEnd: Number(res[2]), secondsBeforeEnd: Number(res[2]),
fadeScaling: res[3], fadeScaling: res[3],
}; };
} }