mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-16 20:52:06 +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