diff --git a/plugins/precise-volume/back.js b/plugins/precise-volume/back.js
index 93ebea9f..f891ce32 100644
--- a/plugins/precise-volume/back.js
+++ b/plugins/precise-volume/back.js
@@ -1,23 +1,9 @@
-const { isEnabled } = require("../../config/plugins");
-
/*
This is used to determine if plugin is actually active
(not if its only enabled in options)
*/
let enabled = false;
-module.exports = (win) => {
- enabled = true;
+module.exports = () => enabled = true;
- // youtube-music register some of the target listeners after DOMContentLoaded
- // did-finish-load is called after all elements finished loading, including said listeners
- // Thats the reason the timing is controlled from main
- win.webContents.once("did-finish-load", () => {
- win.webContents.send("restoreAddEventListener");
- win.webContents.send("setupVideoPlayerVolumeMousewheel", !isEnabled("hide-video-player"));
- });
-};
-
-module.exports.enabled = () => {
- return enabled;
-};
+module.exports.enabled = () => enabled;
diff --git a/plugins/precise-volume/front.js b/plugins/precise-volume/front.js
index f44aa643..84241fb8 100644
--- a/plugins/precise-volume/front.js
+++ b/plugins/precise-volume/front.js
@@ -3,25 +3,73 @@ const { ipcRenderer, remote } = require("electron");
const { setOptions } = require("../../config/plugins");
function $(selector) { return document.querySelector(selector); }
+let api;
module.exports = (options) => {
+ document.addEventListener('apiLoaded', e => {
+ api = e.detail;
+ firstRun(options);
+ })
+};
+
+/** Restore saved volume and setup tooltip */
+function firstRun(options) {
+ if (typeof options.savedVolume === "number") {
+ // Set saved volume as tooltip
+ setTooltip(options.savedVolume);
+
+ if (api.getVolume() !== options.savedVolume) {
+ api.setVolume(options.savedVolume);
+ }
+ }
setupPlaybar(options);
- setupSliderObserver(options);
-
setupLocalArrowShortcuts(options);
setupGlobalShortcuts(options);
- firstRun(options);
+ const noVid = $("#main-panel")?.computedStyleMap().get("display").value === "none";
+ injectVolumeHud(noVid);
+ if (!noVid) {
+ setupVideoPlayerOnwheel(options);
+ }
+}
- // This way the ipc listener gets cleared either way
- ipcRenderer.once("setupVideoPlayerVolumeMousewheel", (_event, toEnable) => {
- if (toEnable)
- setupVideoPlayerOnwheel(options);
- });
-};
+function injectVolumeHud(noVid) {
+ if (noVid) {
+ const position = "top: 18px; right: 60px; z-index: 999; position: absolute;";
+ const mainStyle = "font-size: xx-large; padding: 10px; transition: opacity 1s";
+
+ $(".center-content.ytmusic-nav-bar").insertAdjacentHTML("beforeend",
+ ``)
+ } else {
+ const position = `top: 10px; left: 10px; z-index: 999; position: absolute;`;
+ const mainStyle = "font-size: xxx-large; padding: 10px; transition: opacity 0.6s; webkit-text-stroke: 1px black; font-weight: 600;";
+
+ $("#song-video").insertAdjacentHTML('afterend',
+ ``)
+ }
+}
+
+let hudFadeTimeout;
+
+function showVolumeHud(volume) {
+ let volumeHud = $("#volumeHud");
+ if (!volumeHud) return;
+
+ volumeHud.textContent = volume + '%';
+ volumeHud.style.opacity = 1;
+
+ if (hudFadeTimeout) {
+ clearTimeout(hudFadeTimeout);
+ }
+
+ hudFadeTimeout = setTimeout(() => {
+ volumeHud.style.opacity = 0;
+ hudFadeTimeout = null;
+ }, 2000);
+}
/** Add onwheel event to video player */
function setupVideoPlayerOnwheel(options) {
@@ -32,35 +80,20 @@ function setupVideoPlayerOnwheel(options) {
});
}
-function toPercent(volume) {
- return Math.round(Number.parseFloat(volume) * 100);
-}
-
function saveVolume(volume, options) {
options.savedVolume = volume;
- setOptions("precise-volume", options);
+ writeOptions(options);
}
-/** Restore saved volume and setup tooltip */
-function firstRun(options) {
- const videoStream = $(".video-stream");
- const slider = $("#volume-slider");
- // Those elements load abit after DOMContentLoaded
- if (videoStream && slider) {
- // Set saved volume IF it pass checks
- if (options.savedVolume
- && options.savedVolume >= 0 && options.savedVolume <= 100
- && Math.abs(slider.value - options.savedVolume) < 5
- // If plugin was disabled and volume changed then diff>4
- ) {
- videoStream.volume = options.savedVolume / 100;
- slider.value = options.savedVolume;
- }
- // Set current volume as tooltip
- setTooltip(toPercent(videoStream.volume));
- } else {
- setTimeout(firstRun, 500, options); // Try again in 500 milliseconds
- }
+//without this function it would rewrite config 20 time when volume change by 20
+let writeTimeout;
+function writeOptions(options) {
+ if (writeTimeout) clearTimeout(writeTimeout);
+
+ writeTimeout = setTimeout(() => {
+ setOptions("precise-volume", options);
+ writeTimeout = null;
+ }, 1500)
}
/** Add onwheel event to play bar and also track if play bar is hovered*/
@@ -81,32 +114,63 @@ function setupPlaybar(options) {
playerbar.addEventListener("mouseleave", () => {
playerbar.classList.remove("on-hover");
});
+
+ setupSliderObserver(options);
+}
+
+/** Save volume + Update the volume tooltip when volume-slider is manually changed */
+function setupSliderObserver(options) {
+ const sliderObserver = new MutationObserver(mutations => {
+ for (const mutation of mutations) {
+ // This checks that volume-slider was manually set
+ if (mutation.oldValue !== mutation.target.value &&
+ (typeof options.savedVolume !== "number" || Math.abs(options.savedVolume - mutation.target.value) > 4)) {
+ // Diff>4 means it was manually set
+ setTooltip(mutation.target.value);
+ saveVolume(mutation.target.value, options);
+ }
+ }
+ });
+
+ // Observing only changes in 'value' of volume-slider
+ sliderObserver.observe($("#volume-slider"), {
+ attributeFilter: ["value"],
+ attributeOldValue: true
+ });
}
/** if (toIncrease = false) then volume decrease */
function changeVolume(toIncrease, options) {
- // Need to change both the actual volume and the slider
- const videoStream = $(".video-stream");
- const slider = $("#volume-slider");
// Apply volume change if valid
- const steps = (options.steps || 1) / 100;
- videoStream.volume = toIncrease ?
- Math.min(videoStream.volume + steps, 1) :
- Math.max(videoStream.volume - steps, 0);
+ const steps = (options.steps || 1);
+ api.setVolume(toIncrease ?
+ Math.min(api.getVolume() + steps, 100) :
+ Math.max(api.getVolume() - steps, 0));
// Save the new volume
- saveVolume(toPercent(videoStream.volume), options);
- // Slider value automatically rounds to multiples of 5
- slider.value = options.savedVolume;
+ saveVolume(api.getVolume(), options);
+
+ // change slider position (important)
+ updateVolumeSlider(options);
+
// Change tooltips to new value
setTooltip(options.savedVolume);
- // Show volume slider on volume change
- showVolumeSlider(slider);
+ // Show volume slider
+ showVolumeSlider();
+ // Show volume HUD
+ showVolumeHud(options.savedVolume);
+}
+
+function updateVolumeSlider(options) {
+ // Slider value automatically rounds to multiples of 5
+ $("#volume-slider").value = options.savedVolume > 0 && options.savedVolume < 5 ?
+ 5 : options.savedVolume;
}
let volumeHoverTimeoutID;
-function showVolumeSlider(slider) {
+function showVolumeSlider() {
+ const slider = $("#volume-slider");
// This class display the volume slider if not in minimized mode
slider.classList.add("on-hover");
// Reset timeout if previous one hasn't completed
@@ -122,27 +186,6 @@ function showVolumeSlider(slider) {
}, 3000);
}
-/** Save volume + Update the volume tooltip when volume-slider is manually changed */
-function setupSliderObserver(options) {
- const sliderObserver = new MutationObserver(mutations => {
- for (const mutation of mutations) {
- // This checks that volume-slider was manually set
- if (mutation.oldValue !== mutation.target.value &&
- (!options.savedVolume || Math.abs(options.savedVolume - mutation.target.value) > 4)) {
- // Diff>4 means it was manually set
- setTooltip(mutation.target.value);
- saveVolume(mutation.target.value, options);
- }
- }
- });
-
- // Observing only changes in 'value' of volume-slider
- sliderObserver.observe($("#volume-slider"), {
- attributeFilter: ["value"],
- attributeOldValue: true
- });
-}
-
// Set new volume as tooltip for volume slider and icon + expanding slider (appears when window size is small)
const tooltipTargets = [
"#volume-slider",
diff --git a/plugins/precise-volume/preload.js b/plugins/precise-volume/preload.js
index edc5f20c..6a0fd482 100644
--- a/plugins/precise-volume/preload.js
+++ b/plugins/precise-volume/preload.js
@@ -24,10 +24,10 @@ function overrideAddEventListener() {
module.exports = () => {
overrideAddEventListener();
- // Restore original function after did-finish-load to avoid keeping Element.prototype altered
- ipcRenderer.once("restoreAddEventListener", () => { // Called from main to make sure page is completly loaded
+ // Restore original function after finished loading to avoid keeping Element.prototype altered
+ window.addEventListener('load', () => {
Element.prototype.addEventListener = Element.prototype._addEventListener;
Element.prototype._addEventListener = undefined;
ignored = undefined;
- });
+ }, { once: true });
};
diff --git a/preload.js b/preload.js
index 5a09c845..67ae8f7c 100644
--- a/preload.js
+++ b/preload.js
@@ -10,6 +10,8 @@ const setupSongInfo = require("./providers/song-info-front");
const plugins = config.plugins.getEnabled();
+let api;
+
plugins.forEach(([plugin, options]) => {
const preloadPath = path.join(__dirname, "plugins", plugin, "preload.js");
fileExists(preloadPath, () => {
@@ -38,6 +40,9 @@ document.addEventListener("DOMContentLoaded", () => {
});
});
+ // wait for complete load of youtube api
+ listenForApiLoad();
+
// inject song-info provider
setupSongInfo();
@@ -51,3 +56,25 @@ document.addEventListener("DOMContentLoaded", () => {
global.reload = () =>
remote.getCurrentWindow().webContents.loadURL(config.get("url"));
});
+
+function listenForApiLoad() {
+ api = document.querySelector('#movie_player');
+ if (api) {
+ onApiLoaded();
+ return;
+ }
+
+ const observer = new MutationObserver(() => {
+ api = document.querySelector('#movie_player');
+ if (api) {
+ observer.disconnect();
+ onApiLoaded();
+ }
+ })
+
+ observer.observe(document.documentElement, { childList: true, subtree: true });
+}
+
+function onApiLoaded() {
+ document.dispatchEvent(new CustomEvent('apiLoaded', { detail: api }));
+}