mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-16 20:52:06 +00:00
Merge pull request #275 from Araxeus/precise-volume-HUD
precise-volume plugin fixes & updates
This commit is contained in:
@ -1,23 +1,9 @@
|
|||||||
const { isEnabled } = require("../../config/plugins");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This is used to determine if plugin is actually active
|
This is used to determine if plugin is actually active
|
||||||
(not if its only enabled in options)
|
(not if its only enabled in options)
|
||||||
*/
|
*/
|
||||||
let enabled = false;
|
let enabled = false;
|
||||||
|
|
||||||
module.exports = (win) => {
|
module.exports = () => enabled = true;
|
||||||
enabled = true;
|
|
||||||
|
|
||||||
// youtube-music register some of the target listeners after DOMContentLoaded
|
module.exports.enabled = () => enabled;
|
||||||
// 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;
|
|
||||||
};
|
|
||||||
|
|||||||
@ -3,25 +3,73 @@ const { ipcRenderer, remote } = require("electron");
|
|||||||
const { setOptions } = require("../../config/plugins");
|
const { setOptions } = require("../../config/plugins");
|
||||||
|
|
||||||
function $(selector) { return document.querySelector(selector); }
|
function $(selector) { return document.querySelector(selector); }
|
||||||
|
let api;
|
||||||
|
|
||||||
module.exports = (options) => {
|
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);
|
setupPlaybar(options);
|
||||||
|
|
||||||
setupSliderObserver(options);
|
|
||||||
|
|
||||||
setupLocalArrowShortcuts(options);
|
setupLocalArrowShortcuts(options);
|
||||||
|
|
||||||
setupGlobalShortcuts(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
|
function injectVolumeHud(noVid) {
|
||||||
ipcRenderer.once("setupVideoPlayerVolumeMousewheel", (_event, toEnable) => {
|
if (noVid) {
|
||||||
if (toEnable)
|
const position = "top: 18px; right: 60px; z-index: 999; position: absolute;";
|
||||||
setupVideoPlayerOnwheel(options);
|
const mainStyle = "font-size: xx-large; padding: 10px; transition: opacity 1s";
|
||||||
});
|
|
||||||
};
|
$(".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 */
|
/** Add onwheel event to video player */
|
||||||
function setupVideoPlayerOnwheel(options) {
|
function setupVideoPlayerOnwheel(options) {
|
||||||
@ -32,35 +80,20 @@ function setupVideoPlayerOnwheel(options) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function toPercent(volume) {
|
|
||||||
return Math.round(Number.parseFloat(volume) * 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveVolume(volume, options) {
|
function saveVolume(volume, options) {
|
||||||
options.savedVolume = volume;
|
options.savedVolume = volume;
|
||||||
setOptions("precise-volume", options);
|
writeOptions(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Restore saved volume and setup tooltip */
|
//without this function it would rewrite config 20 time when volume change by 20
|
||||||
function firstRun(options) {
|
let writeTimeout;
|
||||||
const videoStream = $(".video-stream");
|
function writeOptions(options) {
|
||||||
const slider = $("#volume-slider");
|
if (writeTimeout) clearTimeout(writeTimeout);
|
||||||
// Those elements load abit after DOMContentLoaded
|
|
||||||
if (videoStream && slider) {
|
writeTimeout = setTimeout(() => {
|
||||||
// Set saved volume IF it pass checks
|
setOptions("precise-volume", options);
|
||||||
if (options.savedVolume
|
writeTimeout = null;
|
||||||
&& options.savedVolume >= 0 && options.savedVolume <= 100
|
}, 1500)
|
||||||
&& 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add onwheel event to play bar and also track if play bar is hovered*/
|
/** 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.addEventListener("mouseleave", () => {
|
||||||
playerbar.classList.remove("on-hover");
|
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 */
|
/** if (toIncrease = false) then volume decrease */
|
||||||
function changeVolume(toIncrease, options) {
|
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
|
// Apply volume change if valid
|
||||||
const steps = (options.steps || 1) / 100;
|
const steps = (options.steps || 1);
|
||||||
videoStream.volume = toIncrease ?
|
api.setVolume(toIncrease ?
|
||||||
Math.min(videoStream.volume + steps, 1) :
|
Math.min(api.getVolume() + steps, 100) :
|
||||||
Math.max(videoStream.volume - steps, 0);
|
Math.max(api.getVolume() - steps, 0));
|
||||||
|
|
||||||
// Save the new volume
|
// Save the new volume
|
||||||
saveVolume(toPercent(videoStream.volume), options);
|
saveVolume(api.getVolume(), options);
|
||||||
// Slider value automatically rounds to multiples of 5
|
|
||||||
slider.value = options.savedVolume;
|
// change slider position (important)
|
||||||
|
updateVolumeSlider(options);
|
||||||
|
|
||||||
// Change tooltips to new value
|
// Change tooltips to new value
|
||||||
setTooltip(options.savedVolume);
|
setTooltip(options.savedVolume);
|
||||||
// Show volume slider on volume change
|
// Show volume slider
|
||||||
showVolumeSlider(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;
|
let volumeHoverTimeoutID;
|
||||||
|
|
||||||
function showVolumeSlider(slider) {
|
function showVolumeSlider() {
|
||||||
|
const slider = $("#volume-slider");
|
||||||
// This class display the volume slider if not in minimized mode
|
// This class display the volume slider if not in minimized mode
|
||||||
slider.classList.add("on-hover");
|
slider.classList.add("on-hover");
|
||||||
// Reset timeout if previous one hasn't completed
|
// Reset timeout if previous one hasn't completed
|
||||||
@ -122,27 +186,6 @@ function showVolumeSlider(slider) {
|
|||||||
}, 3000);
|
}, 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)
|
// Set new volume as tooltip for volume slider and icon + expanding slider (appears when window size is small)
|
||||||
const tooltipTargets = [
|
const tooltipTargets = [
|
||||||
"#volume-slider",
|
"#volume-slider",
|
||||||
|
|||||||
@ -24,10 +24,10 @@ function overrideAddEventListener() {
|
|||||||
|
|
||||||
module.exports = () => {
|
module.exports = () => {
|
||||||
overrideAddEventListener();
|
overrideAddEventListener();
|
||||||
// Restore original function after did-finish-load to avoid keeping Element.prototype altered
|
// Restore original function after finished loading to avoid keeping Element.prototype altered
|
||||||
ipcRenderer.once("restoreAddEventListener", () => { // Called from main to make sure page is completly loaded
|
window.addEventListener('load', () => {
|
||||||
Element.prototype.addEventListener = Element.prototype._addEventListener;
|
Element.prototype.addEventListener = Element.prototype._addEventListener;
|
||||||
Element.prototype._addEventListener = undefined;
|
Element.prototype._addEventListener = undefined;
|
||||||
ignored = undefined;
|
ignored = undefined;
|
||||||
});
|
}, { once: true });
|
||||||
};
|
};
|
||||||
|
|||||||
27
preload.js
27
preload.js
@ -10,6 +10,8 @@ const setupSongInfo = require("./providers/song-info-front");
|
|||||||
|
|
||||||
const plugins = config.plugins.getEnabled();
|
const plugins = config.plugins.getEnabled();
|
||||||
|
|
||||||
|
let api;
|
||||||
|
|
||||||
plugins.forEach(([plugin, options]) => {
|
plugins.forEach(([plugin, options]) => {
|
||||||
const preloadPath = path.join(__dirname, "plugins", plugin, "preload.js");
|
const preloadPath = path.join(__dirname, "plugins", plugin, "preload.js");
|
||||||
fileExists(preloadPath, () => {
|
fileExists(preloadPath, () => {
|
||||||
@ -38,6 +40,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// wait for complete load of youtube api
|
||||||
|
listenForApiLoad();
|
||||||
|
|
||||||
// inject song-info provider
|
// inject song-info provider
|
||||||
setupSongInfo();
|
setupSongInfo();
|
||||||
|
|
||||||
@ -51,3 +56,25 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
global.reload = () =>
|
global.reload = () =>
|
||||||
remote.getCurrentWindow().webContents.loadURL(config.get("url"));
|
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 }));
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user