你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式
(我已經安裝了使用者樣式管理器,讓我安裝!)
// ==UserScript==
// @name Youtube Shorts Improved
// @name:zh-CN 更好的Youtube Shorts
// @name:zh-TW 更好的Youtube Shorts
// @name:ja より良いYoutube Shorts
// @namespace Violentmonkey Scripts
// @version 1.0.5
// @description Provides more control features for Youtube Shorts, including volume control, progress bar, auto-scroll, hotkeys, and more.
// @description:zh-CN 为Youtube Shorts提供更多的控制功能,包括音量控制,进度条,自动滚动,快捷键等等。
// @description:zh-TW 為Youtube Shorts提供更多的控制功能,包括音量控制,進度條,自動滾動,快捷鍵等等。
// @description:ja Youtube Shortsに音量コントロール、プログレスバー、自動スクロール、ホットキーなどの機能を提供します。
// @author Meriel
// @match *://www.youtube.com/shorts/*
// @require http://code.jquery.com/jquery-latest.min.js
// @run-at document-start
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @license GPLv3
// @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// ==/UserScript==
GM_addStyle(
`input[type="range"].volslider {
height: 14px;
-webkit-appearance: none;
margin: 10px 0;
}
input[type="range"].volslider:focus {
outline: none;
}
input[type="range"].volslider::-webkit-slider-runnable-track {
height: 8px;
cursor: pointer;
box-shadow: 0px 0px 0px #000000;
background: rgb(50 50 50);
border-radius: 25px;
border: 1px solid #000000;
}
input[type="range"].volslider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
margin-top: -7px;
border-radius: 0px;
background-image: url("https://i.imgur.com/vcQoCVS.png");
background-size: 20px;
background-repeat: no-repeat;
background-position: 50%;
}
input[type="range"]:focus::-webkit-slider-runnable-track {
background: rgb(50 50 50);
}
.switch {
position: relative;
display: inline-block;
width: 46px;
height: 20px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: 0.4s;
transition: 0.4s;
}
.slider:before {
position: absolute;
content: "";
height: 12px;
width: 12px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: 0.4s;
transition: 0.4s;
}
input:checked + .slider {
background-color: #ff0000;
}
input:focus + .slider {
box-shadow: 0 0 1px #ff0000;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 12px;
}
.slider.round:before {
border-radius: 50%;
}`
);
var $ = window.jQuery;
var vid = null;
var reel = null;
var progbar = null;
var seekMouseDown = false;
var bytsVol = null;
var bytsTimeInfo = null;
var lastCurSeconds = 0;
var progress = null;
var progressTime = 0;
var shortsAndPlayerReady = 0;
// Storage
var savedVolume = 1.0;
var autoScrollVal = true;
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
try {
if (
node.tagName === "YTD-SHORTS" ||
node.classList.contains("html5-video-player")
) {
shortsAndPlayerReady++;
}
} catch (_) {}
if (shortsAndPlayerReady === 2) {
observer.disconnect();
loadSettings();
updateVidElemWithRAF();
addEventListener("keydown", function (e) {
switch (e.key.toUpperCase()) {
case "ARROWLEFT":
$(vid).prop("currentTime", $(vid).prop("currentTime") - 2);
break;
case "ARROWRIGHT":
$(vid).prop("currentTime", $(vid).prop("currentTime") + 2);
break;
default:
break;
}
});
}
}
}
});
observer.observe(document.documentElement, {
childList: true,
subtree: true,
});
function loadSettings() {
savedVolume = GM_getValue("bytsVolume", 1.0);
autoScrollVal = GM_getValue("bytsAutoscroll", true);
}
function updateVidElemWithRAF() {
updateVidElem();
requestAnimationFrame(updateVidElemWithRAF);
}
function padTo2Digits(num) {
return num.toString().padStart(2, "0");
}
function navigationButtonDown() {
const button = document.querySelector("#navigation-button-down");
if (button) {
button.click();
}
}
function updateVidElem() {
// vid = $(".html5-video-player").first().find("video").first();
vid = document.querySelector(".html5-video-player video");
// 用原生的方法获取video元素
// reel = $(vid).closest("ytd-reel-video-renderer");
reel = vid.closest("ytd-reel-video-renderer");
// if ($(vid).length === 0 || $(reel).length === 0) {
if (vid === null || reel === null) {
return;
}
if (autoScrollVal == true) {
// $(vid).removeAttr("loop");
// $(vid).unbind("ended");
// $(vid).on("ended", function () {
// $("#navigation-button-down").find("button").first().click();
// });
vid.removeAttribute("loop");
vid.removeEventListener("ended", navigationButtonDown);
vid.addEventListener("ended", navigationButtonDown);
} else {
// $(vid).attr("loop", true);
// $(vid).unbind("ended");
vid.setAttribute("loop", true);
vid.removeEventListener("ended", navigationButtonDown);
}
// Progress Bar
// if ($(reel).find("#byts-progbar").length === 0) {
if (document.querySelector("#byts-progbar") === null) {
// $(reel).find("#progress-bar").remove();
reel.querySelector("#progress-bar").remove();
// if ($("#byts-progbar").length === 0) {
if (document.querySelector("#byts-progbar") === null) {
// $(reel).append(
// '<div id="byts-progbar" style="user-select: none; cursor: pointer; width: 98%; height: 6px; background-color: #343434; position: absolute; margin-top: 846px; border-radius: 10px"></div>'
// );
const bytsProgbar = document.createElement("div");
bytsProgbar.id = "byts-progbar";
bytsProgbar.style.userSelect = "none";
bytsProgbar.style.cursor = "pointer";
bytsProgbar.style.width = "98%";
bytsProgbar.style.height = "6px";
bytsProgbar.style.backgroundColor = "#343434";
bytsProgbar.style.position = "absolute";
bytsProgbar.style.marginTop = "846px";
bytsProgbar.style.borderRadius = "10px";
reel.appendChild(bytsProgbar);
} else {
$(reel).append($("#byts-progbar"));
}
progbar = $("#byts-progbar").first();
$(progbar).mousemove((e) => {
if (seekMouseDown) {
$(vid).prop(
"currentTime",
((e.offsetX * 1) / $(reel).outerWidth()) * $(vid).prop("duration")
);
}
});
$(progbar).mousedown(() => {
seekMouseDown = true;
});
$(progbar).mouseleave(() => {
seekMouseDown = false;
});
$(progbar).mouseup((e) => {
seekMouseDown = false;
$(vid).prop(
"currentTime",
((e.offsetX * 1) / $(reel).outerWidth()) * $(vid).prop("duration")
);
});
}
// Progress Bar (Inner Red Bar)
progressTime = ($(vid).prop("currentTime") / $(vid).prop("duration")) * 100;
if ((progress = $(progbar).find("#byts-progress")).length === 0) {
progress = $("<div></div>");
progress.attr("id", "byts-progress");
progress.css({
userSelect: "none",
backgroundColor: "#FF0000",
height: "100%",
borderRadius: "10px",
width: progressTime + "%",
});
progress.on("mouseup", function (e) {
var selected_val = (e.offsetX * 1) / $(reel).outerWidth();
$(vid).prop("currentTime", selected_val * $(vid).prop("duration"));
});
progress.appendTo(progbar);
} else {
progress.css("width", progressTime + "%");
}
// Time Info
let durSecs = Math.floor($(vid).prop("duration"));
let durMinutes = Math.floor(durSecs / 60);
let durSeconds = durSecs % 60;
let curSecs = Math.floor($(vid).prop("currentTime"));
if (
curSecs != lastCurSeconds ||
$(reel).find("#byts-timeinfo").length === 0
) {
lastCurSeconds = curSecs;
let curMinutes = Math.floor(curSecs / 60);
let curSeconds = curSecs % 60;
// TimeInfo Element
if ($(reel).find("#byts-timeinfo").length === 0) {
if ($("#byts-timeinfo").length === 0) {
$(reel).append(
'<div id="byts-timeinfo" style="user-select: none; display: flex; right: auto; left: auto; position: absolute; margin-top: ' +
($(reel).height() + 2) +
'px;"><div id="byts-timeinfo-textdiv" style="display: flex; margin-right: 5px; margin-top: 4px; color: white; font-size: 1.2rem;">' +
`${curMinutes}:${padTo2Digits(
curSeconds
)} / ${durMinutes}:${padTo2Digits(durSeconds)}` +
"</div></div>"
);
} else {
$(reel).append($("#byts-timeinfo"));
}
bytsTimeInfo = $("#byts-timeinfo");
}
$("#byts-timeinfo-textdiv").text(
`${curMinutes}:${padTo2Digits(curSeconds)} / ${durMinutes}:${padTo2Digits(
durSeconds
)}`
);
}
$("#byts-timeinfo").css("margin-top", $(reel).height() + 2);
// Volume Slide
if ($(reel).find("#byts-vol").length === 0) {
if ($("#byts-vol").length === 0) {
$(reel).append(
'<input style="user-select: none; width: 100px; left: 0px; background-color: transparent; position: absolute; margin-top: ' +
($(reel).height() + 5) +
'px;" type="range" id="byts-vol" class="volslider" name="vol" min="0.0" max="1.0" step="0.05" value="' +
savedVolume +
'"></input>'
);
} else {
$(reel).append($("#byts-vol"));
}
bytsVol = $("#byts-vol");
$("#byts-vol").on("input change", function () {
$(vid).prop("volume", $(this).val());
GM_setValue("bytsVolume", $(this).val());
});
} else {
$("#byts-vol").val($(vid).prop("volume"));
}
$("#byts-vol").css("margin-top", $(reel).height() + 5);
// AutoScroll
if ($(reel).find("#byts-autoscroll-div").length === 0) {
if ($("#byts-autoscroll-div").length === 0) {
let astc = "";
if (autoScrollVal) {
astc = " checked";
}
$(reel).append(
'<div id="byts-autoscroll-div" style="user-select: none; display: flex; right: 0px; position: absolute; margin-top: ' +
($(reel).height() + 2) +
'px;"><div style="display: flex; margin-right: 5px; margin-top: 4px; color: white; font-size: 1.2rem;">Auto-Scroll: </div><label class="switch"><input id="byts-autoscroll-input" type="checkbox"' +
astc +
'><span class="slider round"></span></label></div>'
);
} else {
$(reel).append($("#byts-autoscroll-div"));
}
bytsVol = $("#byts-autoscroll-div");
$("#byts-autoscroll-input").on("input change", function () {
GM_setValue("bytsAutoscroll", $(this).is(":checked"));
if ($(this).is(":checked")) {
autoScrollVal = true;
} else {
autoScrollVal = false;
}
if (autoScrollVal == true) {
$(vid).removeAttr("loop");
$(vid).unbind("ended");
$(vid).on("ended", function () {
$("#navigation-button-down").find("button").first().click();
});
} else {
$(vid).attr("loop", true);
$(vid).unbind("ended");
}
});
}
$("#byts-autoscroll-div").css("margin-top", $(reel).height() + 2);
}