// ==UserScript==
// @name YouTube Tweaker Pro (Full Suite) [v3.1 Enhanced]
// @namespace https://gf.qytechs.cn/users/eliminater74
// @version 3.1.1
// @description Same as 3.1 but with optional gear auto-hide in fullscreen & delay settings. Nothing else changed.
// @author Eliminater74
// @match *://www.youtube.com/*
// @grant GM_addStyle
// @run-at document-end
// @license MIT
// ==/UserScript==
(function () {
'use strict';
const SETTINGS_KEY = 'ytTweakerSettingsV3';
const defaultSettings = {
hideRightSidebar: false,
hideLeftNav: false,
muteAutoplay: false,
hideShorts: false,
forceTheater: false,
widenLayout: false,
hideComments: false,
expandDescription: false,
hideEndscreen: false,
autoHD: false,
hideHomepageAds: false,
hideTopBannerPromo: false,
hideKeywordChips: false,
autoSkipAds: true,
muteDuringAds: true,
darkMode: true,
hideGearFullscreen: false,
gearAutoHideDelay: 0 // in seconds
};
let settings = { ...defaultSettings, ...JSON.parse(localStorage.getItem(SETTINGS_KEY) || '{}') };
const saveSettings = () => localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
const q = s => document.querySelector(s);
const qAll = s => document.querySelectorAll(s);
function resetSettings() {
settings = { ...defaultSettings };
saveSettings();
location.reload();
}
function applySettings() {
if (q('#related')) q('#related').style.display = settings.hideRightSidebar ? 'none' : '';
['#guide', 'ytd-mini-guide-renderer', 'ytd-guide-section-renderer'].forEach(sel =>
qAll(sel).forEach(e => (e.style.display = settings.hideLeftNav ? 'none' : ''))
);
['ytd-rich-section-renderer', 'ytd-reel-shelf-renderer'].forEach(sel =>
qAll(sel).forEach(e => (e.style.display = settings.hideShorts ? 'none' : ''))
);
qAll('ytd-comments').forEach(e => (e.style.display = settings.hideComments ? 'none' : ''));
qAll('ytd-promoted-video-renderer, ytd-display-ad-renderer, ytd-rich-item-renderer[is-promoted], ytd-ad-slot-renderer')
.forEach(e => (e.style.display = settings.hideHomepageAds ? 'none' : ''));
qAll('ytd-banner-promo-renderer, ytd-rich-banner-renderer')
.forEach(e => (e.style.display = settings.hideTopBannerPromo ? 'none' : ''));
qAll('ytd-feed-filter-chip-bar-renderer, .chip-bar')
.forEach(e => (e.style.display = settings.hideKeywordChips ? 'none' : ''));
const vid = q('video');
if (vid && settings.muteAutoplay) vid.muted = true;
if (settings.forceTheater) {
const flexy = q('ytd-watch-flexy');
const btn = q('.ytp-size-button');
if (btn && flexy && !flexy.hasAttribute('theater')) btn.click();
}
if (settings.widenLayout && location.pathname.startsWith('/watch')) {
const player = document.getElementById('player-container');
if (player) player.style.maxWidth = '100%';
}
if (settings.expandDescription && location.pathname.startsWith('/watch')) {
const exp = q('#expand');
if (exp && exp.getAttribute('aria-expanded') !== 'true') exp.click();
}
const styleId = 'yt-endscreen-style';
let style = document.getElementById(styleId);
if (settings.hideEndscreen && !style) {
style = document.createElement('style');
style.id = styleId;
style.textContent = `.ytp-ce-element, .ytp-ce-video { display: none !important; }`;
document.head.appendChild(style);
} else if (!settings.hideEndscreen && style) {
style.remove();
}
if (settings.autoHD) {
setTimeout(() => {
const btn = q('.ytp-settings-button');
if (btn) btn.click();
}, 3000);
}
}
function autoAdWatcher() {
setInterval(() => {
const skip = q('.ytp-ad-skip-button');
if (settings.autoSkipAds && skip) skip.click();
const adShowing = document.querySelector('.ad-showing');
const vid = q('video');
if (settings.muteDuringAds && adShowing && vid) vid.muted = true;
else if (vid && settings.muteAutoplay === false) vid.muted = false;
const adIndicator = q('.ytp-ad-preview-container,.ytp-ad-player-overlay');
if (settings.autoSkipAds && adIndicator && vid && vid.duration < 60) {
try {
vid.currentTime = vid.duration;
} catch (e) {}
}
}, 500);
}
GM_addStyle(`
#yt-tweaker-gear {
position: fixed;
bottom: 20px;
right: 20px;
background: #000;
color: #0ff;
font-size: 20px;
padding: 8px 12px;
border-radius: 8px;
cursor: grab;
z-index: 2147483647;
box-shadow: 0 0 8px #0ff;
user-select: none;
}
#yt-tweaker-panel {
position: fixed;
bottom: 60px;
right: 20px;
padding: 12px;
border-radius: 10px;
font-family: monospace;
font-size: 13px;
z-index: 2147483647;
display: none;
box-shadow: 0 0 12px rgba(0,0,0,0.5);
}
.yt-dark-theme {
background: #111;
color: #0f0;
}
.yt-light-theme {
background: #eee;
color: #111;
}
#yt-tweaker-panel label {
display: block;
margin: 5px 0;
}
#yt-tweaker-panel button {
margin: 4px 2px;
font-size: 12px;
}
#yt-tweaker-panel input[type="checkbox"],
#yt-tweaker-panel select {
margin-right: 5px;
}
`);
function createCheckbox(labelText, key) {
const label = document.createElement('label');
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = settings[key];
checkbox.addEventListener('change', () => {
settings[key] = checkbox.checked;
saveSettings();
});
label.appendChild(checkbox);
label.appendChild(document.createTextNode(labelText));
return label;
}
function createUI() {
if (document.getElementById('yt-tweaker-gear')) return;
const gear = document.createElement('div');
gear.id = 'yt-tweaker-gear';
gear.textContent = '⚙️';
document.body.appendChild(gear);
const panel = document.createElement('div');
panel.id = 'yt-tweaker-panel';
panel.className = settings.darkMode ? 'yt-dark-theme' : 'yt-light-theme';
const title = document.createElement('strong');
title.textContent = 'YouTube Tweaker';
panel.appendChild(title);
panel.appendChild(document.createElement('br'));
panel.appendChild(document.createElement('br'));
[
['Hide Right Sidebar (Suggestions)', 'hideRightSidebar'],
['Hide Left Nav Menu', 'hideLeftNav'],
['Mute Autoplay', 'muteAutoplay'],
['Hide Shorts', 'hideShorts'],
['Force Theater Mode', 'forceTheater'],
['Widen Layout', 'widenLayout'],
['Hide Comments', 'hideComments'],
['Expand Description', 'expandDescription'],
['Hide Endscreen', 'hideEndscreen'],
['Auto HD Quality', 'autoHD'],
['Hide Homepage Ads', 'hideHomepageAds'],
['Hide Top Banner Promo', 'hideTopBannerPromo'],
['Hide Keyword Chips', 'hideKeywordChips'],
['Auto-Skip Ads', 'autoSkipAds'],
['Mute During Ads', 'muteDuringAds'],
['Auto-Hide Gear in Fullscreen', 'hideGearFullscreen']
].forEach(([label, key]) => {
panel.appendChild(createCheckbox(label, key));
});
// Delay Dropdown
const delayLabel = document.createElement('label');
delayLabel.textContent = 'Gear Auto-Hide Delay (sec): ';
const select = document.createElement('select');
[0, 3, 5, 10].forEach(val => {
const option = document.createElement('option');
option.value = val;
option.textContent = val;
if (val == settings.gearAutoHideDelay) option.selected = true;
select.appendChild(option);
});
select.addEventListener('change', () => {
settings.gearAutoHideDelay = parseInt(select.value);
saveSettings();
});
delayLabel.appendChild(select);
panel.appendChild(delayLabel);
// Theme toggle
const themeToggle = createCheckbox('Dark Theme UI', 'darkMode');
themeToggle.querySelector('input').addEventListener('change', () => {
panel.className = settings.darkMode ? 'yt-dark-theme' : 'yt-light-theme';
});
panel.appendChild(themeToggle);
// Export / Import
const tools = document.createElement('div');
const btnReset = document.createElement('button');
btnReset.textContent = 'Reset Settings';
btnReset.onclick = () => confirm('Reset all settings?') && resetSettings();
const btnExport = document.createElement('button');
btnExport.textContent = 'Export';
btnExport.onclick = () => {
navigator.clipboard.writeText(JSON.stringify(settings, null, 2));
alert('Settings copied to clipboard.');
};
const btnImport = document.createElement('button');
btnImport.textContent = 'Import';
btnImport.onclick = () => {
const input = prompt('Paste settings JSON:');
try {
const obj = JSON.parse(input);
settings = { ...defaultSettings, ...obj };
saveSettings();
location.reload();
} catch {
alert('Invalid JSON.');
}
};
tools.appendChild(btnReset);
tools.appendChild(btnExport);
tools.appendChild(btnImport);
panel.appendChild(tools);
document.body.appendChild(panel);
// Gear behavior
gear.addEventListener('click', () => {
panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
});
let dragging = false, offsetX = 0, offsetY = 0;
gear.addEventListener('mousedown', (e) => {
dragging = true;
offsetX = e.clientX - gear.offsetLeft;
offsetY = e.clientY - gear.offsetTop;
gear.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', (e) => {
if (dragging) {
gear.style.left = `${e.clientX - offsetX}px`;
gear.style.top = `${e.clientY - offsetY}px`;
gear.style.bottom = 'auto';
gear.style.right = 'auto';
}
});
document.addEventListener('mouseup', () => {
dragging = false;
gear.style.cursor = 'grab';
});
// Auto-hide logic
const handleVisibility = () => {
const isFullscreen = !!document.fullscreenElement;
const gearEl = document.getElementById('yt-tweaker-gear');
const panelEl = document.getElementById('yt-tweaker-panel');
if (!gearEl || dragging || panelEl.style.display === 'block') return;
// fullscreen hide
if (settings.hideGearFullscreen && isFullscreen) {
gearEl.style.display = 'none';
} else {
gearEl.style.display = '';
}
// delayed hide
if (settings.gearAutoHideDelay > 0) {
clearTimeout(gearEl._hideTimer);
gearEl._hideTimer = setTimeout(() => {
if (!dragging && panelEl.style.display === 'none') {
gearEl.style.display = 'none';
}
}, settings.gearAutoHideDelay * 1000);
}
};
document.addEventListener('fullscreenchange', handleVisibility);
document.addEventListener('mousemove', handleVisibility);
document.addEventListener('keydown', handleVisibility);
gear.addEventListener('mousemove', handleVisibility);
gear.addEventListener('mouseenter', handleVisibility);
}
function watchNavigation() {
let lastUrl = location.href;
new MutationObserver(() => {
if (location.href !== lastUrl) {
lastUrl = location.href;
setTimeout(applySettings, 1000);
}
}).observe(document.body, { childList: true, subtree: true });
}
function init() {
const wait = setInterval(() => {
if (document.querySelector('ytd-app')) {
clearInterval(wait);
createUI();
applySettings();
autoAdWatcher();
watchNavigation();
}
}, 300);
}
init();
})();