Skip to content

Commit

Permalink
Merge pull request #275 from Araxeus/precise-volume-HUD
Browse files Browse the repository at this point in the history
precise-volume plugin fixes & updates
  • Loading branch information
th-ch committed Oct 24, 2021
2 parents 362da8c + df75e48 commit 6a2e3ab
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 87 deletions.
18 changes: 2 additions & 16 deletions plugins/precise-volume/back.js
Original file line number Diff line number Diff line change
@@ -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;

// 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 = true;

module.exports.enabled = () => {
return enabled;
};
module.exports.enabled = () => enabled;
179 changes: 111 additions & 68 deletions plugins/precise-volume/front.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
})
};

setupPlaybar(options);
/** Restore saved volume and setup tooltip */
function firstRun(options) {
if (typeof options.savedVolume === "number") {
// Set saved volume as tooltip
setTooltip(options.savedVolume);

setupSliderObserver(options);
if (api.getVolume() !== options.savedVolume) {
api.setVolume(options.savedVolume);
}
}

setupPlaybar(options);

setupLocalArrowShortcuts(options);

setupGlobalShortcuts(options);

firstRun(options);
const noVid = $("#main-panel")?.computedStyleMap().get("display").value === "none";
injectVolumeHud(noVid);
if (!noVid) {
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";

// This way the ipc listener gets cleared either way
ipcRenderer.once("setupVideoPlayerVolumeMousewheel", (_event, toEnable) => {
if (toEnable)
setupVideoPlayerOnwheel(options);
});
};
$(".center-content.ytmusic-nav-bar").insertAdjacentHTML("beforeend",
`<span id="volumeHud" style="${position + mainStyle}"></span>`)
} 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',
`<span id="volumeHud" style="${position + mainStyle}"></span>`)
}
}

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) {
Expand All @@ -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*/
Expand All @@ -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
Expand All @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions plugins/precise-volume/preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
};
27 changes: 27 additions & 0 deletions preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -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, () => {
Expand Down Expand Up @@ -38,6 +40,9 @@ document.addEventListener("DOMContentLoaded", () => {
});
});

// wait for complete load of youtube api
listenForApiLoad();

// inject song-info provider
setupSongInfo();

Expand All @@ -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 }));
}

0 comments on commit 6a2e3ab

Please sign in to comment.