你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式
你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式
(我已經安裝了使用者樣式管理器,讓我安裝!)
// ==UserScript==
// @name YouTube Wide Mode + Fix
// @version 1.4
// @match https://www.youtube.com/*
// @grant GM_addStyle
// @namespace https://gf.qytechs.cn/users/1029007
// @description Control video scaleX on /watch and tile count
// ==/UserScript==
(function () {
'use strict';
const SCALE_KEY = 'yt-wide-scaleX';
const TILE_KEY = 'yt-tile-count';
let scaleX = parseFloat(localStorage.getItem(SCALE_KEY)) || 1.3;
let tilesPerRow = parseInt(localStorage.getItem(TILE_KEY)) || 4;
let lastScaleBeforeFullscreen = scaleX;
GM_addStyle(`
html, body {
scrollbar-width: none;
-ms-overflow-style: none;
}
body::-webkit-scrollbar {
display: none;
}
#yt-wide-control, #yt-tile-control {
display: flex;
align-items: center;
gap: 6px;
margin-right: 10px;
background: transparent !important;
}
.yt-editable-label {
cursor: pointer;
color: var(--yt-spec-text-primary, #fff);
font-size: 14px;
}
.yt-editable-input {
width: 60px;
padding: 2px 4px;
font-size: 14px;
background: transparent !important;
border: none !important;
outline: none;
color: var(--yt-spec-text-primary, #fff);
font-family: inherit;
display: none;
}
.yt-control:hover .yt-editable-label {
text-decoration: underline;
}
`);
function calculateAutoScale(video) {
const flexy = document.querySelector('ytd-watch-flexy[theater]');
if (!flexy || !video) return scaleX;
const containerWidth = flexy.clientWidth;
const videoNaturalWidth = video.videoWidth;
if (!containerWidth || !videoNaturalWidth) return scaleX;
const currentDisplayedWidth = video.clientWidth;
if (!currentDisplayedWidth) return scaleX;
const requiredScale = containerWidth / currentDisplayedWidth;
return Math.min(Math.max(requiredScale, 1.0), 2.0);
}
function applyScale(value = scaleX, auto = false) {
const video = document.querySelector('ytd-watch-flexy[theater] #movie_player video');
if (video) {
const targetScale = auto ? calculateAutoScale(video) : value;
video.style.transition = 'none';
video.style.transform = `scaleX(${targetScale})`;
video.style.transformOrigin = 'center center';
if (auto) {
const label = document.querySelector('#yt-wide-control .yt-editable-label');
if (label) label.textContent = `ScaleX: ${targetScale.toFixed(2)}`;
}
}
}
const videoObserver = new MutationObserver(() => {
const video = document.querySelector('ytd-watch-flexy[theater] #movie_player video');
if (video) applyScale(undefined, true);
});
function observeVideo() {
const moviePlayer = document.querySelector('ytd-watch-flexy[theater] #movie_player');
if (moviePlayer) {
videoObserver.disconnect();
videoObserver.observe(moviePlayer, {
childList: true,
subtree: true,
});
const video = moviePlayer.querySelector('video');
if (video) {
applyScale();
} else {
const tmpObserver = new MutationObserver(() => {
const vid = moviePlayer.querySelector('video');
if (vid) {
applyScale(undefined, true);
tmpObserver.disconnect();
}
});
tmpObserver.observe(moviePlayer, { childList: true, subtree: true });
}
}
}
function updateTiles() {
let style = document.getElementById('tile-style');
if (!style) {
style = document.createElement('style');
style.id = 'tile-style';
document.head.appendChild(style);
}
style.textContent = `
ytd-rich-grid-renderer {
--ytd-rich-grid-items-per-row: ${tilesPerRow} !important;
}
`;
}
function preventScrollOnWheel(input) {
input.addEventListener('wheel', e => {
e.stopPropagation();
}, { passive: false });
}
function waitForTargetElement(selector, callback) {
const interval = setInterval(() => {
const target = document.querySelector(selector);
if (target) {
clearInterval(interval);
callback(target);
}
}, 50);
}
function createEditableControl(id, labelText, value, onChange) {
if (document.getElementById(id)) return;
const container = document.createElement('div');
container.id = id;
container.className = 'yt-control';
const label = document.createElement('span');
label.className = 'yt-editable-label';
label.textContent = `${labelText}: ${value}`;
const input = document.createElement('input');
input.className = 'yt-editable-input';
input.type = 'number';
input.step = '0.01';
input.value = value;
preventScrollOnWheel(input);
label.addEventListener('click', () => {
label.style.display = 'none';
input.style.display = 'inline';
input.focus();
input.select();
});
input.addEventListener('blur', () => {
const newValue = parseFloat(input.value);
if (!isNaN(newValue)) {
onChange(newValue);
label.textContent = `${labelText}: ${newValue.toFixed(2)}`;
if (!document.fullscreenElement && labelText === 'ScaleX') {
lastScaleBeforeFullscreen = newValue;
}
}
label.style.display = 'inline';
input.style.display = 'none';
});
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
input.blur();
}
});
container.appendChild(label);
container.appendChild(input);
return container;
}
function setup() {
const path = window.location.pathname;
const isWatch = path.startsWith('/watch');
const isMainOrSubs = path === '/' || path === '/feed/subscriptions';
waitForTargetElement('#end', (target) => {
if (isWatch) {
if (!document.getElementById('yt-wide-control')) {
const scaleControl = createEditableControl(
'yt-wide-control',
'ScaleX',
scaleX,
(newValue) => {
scaleX = newValue;
localStorage.setItem(SCALE_KEY, newValue.toFixed(2));
lastScaleBeforeFullscreen = newValue;
applyScale();
}
);
target.insertBefore(scaleControl, target.firstChild);
applyScale();
observeVideo();
}
} else {
const wideControl = document.getElementById('yt-wide-control');
if (wideControl) wideControl.remove();
videoObserver.disconnect();
}
if (isMainOrSubs) {
if (!document.getElementById('yt-tile-control')) {
const tileControl = createEditableControl(
'yt-tile-control',
'Tiles',
tilesPerRow,
(newValue) => {
tilesPerRow = parseInt(newValue) || 1;
localStorage.setItem(TILE_KEY, tilesPerRow);
updateTiles();
}
);
tileControl.querySelector('input').step = '1';
tileControl.querySelector('input').min = '1';
target.insertBefore(tileControl, target.firstChild);
updateTiles();
}
} else {
const tileControl = document.getElementById('yt-tile-control');
if (tileControl) tileControl.remove();
}
});
}
document.addEventListener('fullscreenchange', () => {
const isFullscreen = !!document.fullscreenElement;
if (isFullscreen) {
lastScaleBeforeFullscreen = scaleX;
applyScale(1.0);
} else {
applyScale(lastScaleBeforeFullscreen);
}
});
const pageObserver = new MutationObserver(() => {
setup();
});
pageObserver.observe(document.body, { childList: true, subtree: true });
window.addEventListener('yt-navigate-finish', setup);
setup();
const theaterObserver = new MutationObserver(mutations => {
for (const mutation of mutations) {
if (mutation.type === 'attributes' && mutation.attributeName === 'theater') {
const isTheater = mutation.target.hasAttribute('theater');
if (isTheater) {
applyScale(undefined, true);
observeVideo();
}
}
}
});
waitForTargetElement('ytd-watch-flexy', target => {
theaterObserver.observe(target, { attributes: true });
});
})();