mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-11 10:31:47 +00:00
Add plugin to control playback speed like in YouTube (from 0.25 to 2)
This commit is contained in:
81
plugins/playback-speed/front.js
Normal file
81
plugins/playback-speed/front.js
Normal file
@ -0,0 +1,81 @@
|
||||
const { watchDOMElement } = require("../../providers/dom-elements");
|
||||
const { ElementFromFile, templatePath } = require("../utils");
|
||||
|
||||
const slider = ElementFromFile(templatePath(__dirname, "slider.html"));
|
||||
|
||||
const MIN_PLAYBACK_SPEED = 0.25;
|
||||
const MAX_PLAYBACK_SPEED = 2;
|
||||
|
||||
let videoElement;
|
||||
let playbackSpeedPercentage = 50; // = Playback speed of 1
|
||||
|
||||
const computePlayBackSpeed = () => {
|
||||
if (playbackSpeedPercentage <= 50) {
|
||||
// Slow down video by setting a playback speed between MIN_PLAYBACK_SPEED and 1
|
||||
return (
|
||||
MIN_PLAYBACK_SPEED +
|
||||
((1 - MIN_PLAYBACK_SPEED) / 50) * playbackSpeedPercentage
|
||||
);
|
||||
}
|
||||
|
||||
// Accelerate video by setting a playback speed between 1 and MAX_PLAYBACK_SPEED
|
||||
return 1 + ((MAX_PLAYBACK_SPEED - 1) / 50) * (playbackSpeedPercentage - 50);
|
||||
};
|
||||
|
||||
const updatePlayBackSpeed = () => {
|
||||
const playbackSpeed = Math.round(computePlayBackSpeed() * 100) / 100;
|
||||
|
||||
if (!videoElement || videoElement.playbackRate === playbackSpeed) {
|
||||
return;
|
||||
}
|
||||
|
||||
videoElement.playbackRate = playbackSpeed;
|
||||
|
||||
const playbackSpeedElement = document.querySelector("#playback-speed-value");
|
||||
if (playbackSpeedElement) {
|
||||
playbackSpeedElement.innerHTML = playbackSpeed;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = () => {
|
||||
watchDOMElement(
|
||||
"video",
|
||||
(document) => document.querySelector("video"),
|
||||
(element) => {
|
||||
videoElement = element;
|
||||
updatePlayBackSpeed();
|
||||
}
|
||||
);
|
||||
|
||||
watchDOMElement(
|
||||
"menu",
|
||||
(document) =>
|
||||
document.querySelector("ytmusic-menu-popup-renderer paper-listbox"),
|
||||
(menuElement) => {
|
||||
if (!menuElement.contains(slider)) {
|
||||
menuElement.prepend(slider);
|
||||
}
|
||||
|
||||
const playbackSpeedElement = document.querySelector(
|
||||
"#playback-speed-slider #sliderKnob .slider-knob-inner"
|
||||
);
|
||||
|
||||
const playbackSpeedObserver = new MutationObserver((mutations) => {
|
||||
mutations.forEach(function (mutation) {
|
||||
if (mutation.type == "attributes") {
|
||||
const value = playbackSpeedElement.getAttribute("value");
|
||||
playbackSpeedPercentage = parseInt(value, 10);
|
||||
if (isNaN(playbackSpeedPercentage)) {
|
||||
playbackSpeedPercentage = 50;
|
||||
}
|
||||
updatePlayBackSpeed();
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
playbackSpeedObserver.observe(playbackSpeedElement, {
|
||||
attributes: true,
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
94
plugins/playback-speed/templates/slider.html
Normal file
94
plugins/playback-speed/templates/slider.html
Normal file
@ -0,0 +1,94 @@
|
||||
<div
|
||||
class="menu-item ytmusic-menu-popup-renderer"
|
||||
role="option"
|
||||
tabindex="-1"
|
||||
aria-disabled="false"
|
||||
aria-selected="false"
|
||||
>
|
||||
<paper-slider
|
||||
id="playback-speed-slider"
|
||||
class="volume-slider style-scope ytmusic-player-bar on-hover"
|
||||
max="100"
|
||||
min="0"
|
||||
step="5"
|
||||
dir="ltr"
|
||||
title="Playback speed"
|
||||
aria-label="Playback speed"
|
||||
role="slider"
|
||||
tabindex="0"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
aria-valuenow="50"
|
||||
aria-disabled="false"
|
||||
value="50"
|
||||
><!--css-build:shady-->
|
||||
<div id="sliderContainer" class="style-scope paper-slider">
|
||||
<div class="bar-container style-scope paper-slider">
|
||||
<paper-progress
|
||||
id="sliderBar"
|
||||
aria-hidden="true"
|
||||
class="style-scope paper-slider"
|
||||
role="progressbar"
|
||||
value="50"
|
||||
aria-valuenow="50"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
aria-disabled="false"
|
||||
style="touch-action: none;"
|
||||
><!--css-build:shady-->
|
||||
|
||||
<div id="progressContainer" class="style-scope paper-progress">
|
||||
<div
|
||||
id="secondaryProgress"
|
||||
class="style-scope paper-progress"
|
||||
hidden="true"
|
||||
style="transform: scaleX(0);"
|
||||
></div>
|
||||
<div
|
||||
id="primaryProgress"
|
||||
class="style-scope paper-progress"
|
||||
style="transform: scaleX(0.5);"
|
||||
></div>
|
||||
</div>
|
||||
</paper-progress>
|
||||
</div>
|
||||
<dom-if class="style-scope paper-slider"
|
||||
><template is="dom-if"></template
|
||||
></dom-if>
|
||||
<div
|
||||
id="sliderKnob"
|
||||
class="slider-knob style-scope paper-slider"
|
||||
style="left: 50%; touch-action: none;"
|
||||
>
|
||||
<div
|
||||
class="slider-knob-inner style-scope paper-slider"
|
||||
value="50"
|
||||
></div>
|
||||
<paper-ripple
|
||||
id="ink"
|
||||
center=""
|
||||
class="circle style-scope paper-slider"
|
||||
style="display: none;"
|
||||
><!--css-build:shady-->
|
||||
|
||||
<div
|
||||
id="background"
|
||||
class="style-scope paper-ripple"
|
||||
style="opacity: 0.006008;"
|
||||
></div>
|
||||
<div id="waves" class="style-scope paper-ripple"></div>
|
||||
</paper-ripple>
|
||||
</div>
|
||||
</div>
|
||||
<dom-if class="style-scope paper-slider"
|
||||
><template is="dom-if"></template
|
||||
></dom-if>
|
||||
</paper-slider>
|
||||
|
||||
<div
|
||||
class="text style-scope ytmusic-toggle-menu-service-item-renderer"
|
||||
id="ytmcustom-playback-speed"
|
||||
>
|
||||
Speed (<span id="playback-speed-value">1</span>)
|
||||
</div>
|
||||
</div>
|
||||
20
providers/dom-elements.js
Normal file
20
providers/dom-elements.js
Normal file
@ -0,0 +1,20 @@
|
||||
let domElements = {};
|
||||
|
||||
const watchDOMElement = (name, selectorFn, cb) => {
|
||||
const observer = new MutationObserver((mutations, observer) => {
|
||||
if (!domElements[name]) {
|
||||
domElements[name] = selectorFn(document);
|
||||
}
|
||||
|
||||
if (domElements[name]) {
|
||||
cb(domElements[name]);
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = { watchDOMElement };
|
||||
Reference in New Issue
Block a user