Skip to content

Instantly share code, notes, and snippets.

@snowyegret23
Last active December 29, 2025 11:33
Show Gist options
  • Select an option

  • Save snowyegret23/d7ee8b977c32c79eea36ae2a42c86a67 to your computer and use it in GitHub Desktop.

Select an option

Save snowyegret23/d7ee8b977c32c79eea36ae2a42c86a67 to your computer and use it in GitHub Desktop.
Userscript: 유튜브의 특정 UI 요소를 토글합니다.
// ==UserScript==
// @name YouTube UI Customizer
// @namespace https://github.com/snowyegret23
// @version 1.1
// @description 유튜브의 특정 UI 요소를 토글합니다.
// @author Snowyegret
// @match https://www.youtube.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant none
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
const ttPolicy = window.trustedTypes?.createPolicy('yt_ui_customizer_policy', {
createHTML: (string) => string
}) || { createHTML: (string) => string };
const STORAGE_KEY = 'yt_ui_customizer_settings_v3';
const DEFAULT_SETTINGS = {
hideShorts: false,
hideVoiceSearch: false,
hideCreateButton: false,
hideNotification: false,
hideDownload: false,
hideClip: false,
hideThanks: false,
hideCommentInput: false,
hideCommentActions: false,
hideRichMetadata: false,
hideMoreButton: false,
hideVideoLikeDislike: false,
hideChannelRecommendation: false,
extractSaveButton: false,
replaceShareButton: false
};
let settings = loadSettings();
const styleElement = document.createElement('style');
document.head.appendChild(styleElement);
function updateStyles() {
let css = '';
if (settings.hideShorts) {
css += `
ytd-rich-section-renderer:has(ytd-rich-shelf-renderer[is-shorts]),
ytd-reel-shelf-renderer,
ytd-guide-entry-renderer:has(a[title="Shorts"]),
ytd-mini-guide-entry-renderer:has(a[title="Shorts"])
{ display: none !important; }
`;
}
if (settings.hideVoiceSearch) css += `#voice-search-button, ytd-voice-search-renderer { display: none !important; }`;
if (settings.hideCreateButton) {
css += `
ytd-masthead #buttons ytd-button-renderer:has(button[aria-label*="만들기"]),
ytd-masthead #buttons ytd-button-renderer:has(button[aria-label*="Create"]),
ytd-masthead #buttons ytd-topbar-menu-button-renderer:has(button[aria-label*="만들기"]),
ytd-masthead #buttons ytd-topbar-menu-button-renderer:has(button[aria-label*="Create"])
{ display: none !important; }
`;
}
if (settings.hideNotification) css += `ytd-notification-topbar-button-renderer { display: none !important; }`;
if (settings.hideDownload) css += `ytd-download-button-renderer { display: none !important; }`;
if (settings.hideClip) {
css += `
ytd-watch-metadata #actions button-view-model:has(button[aria-label*="클립"]),
ytd-watch-metadata #actions button-view-model:has(path[d*="M8 7c0 .55-.45 1-1 1"]),
ytd-menu-service-item-renderer:has(path[d*="M8 7c0 .55-.45 1-1 1"])
{ display: none !important; }
`;
}
if (settings.hideThanks) {
css += `
ytd-watch-metadata #actions button-view-model:has(button[aria-label*="Thanks"]),
ytd-watch-metadata #actions button-view-model:has(path[d*="M16.5 3C19.538 3 22 5.5 22 9c0 7-7.5 11-10 12.5"])
{ display: none !important; }
`;
}
if (settings.hideCommentInput) css += `#simple-box.ytd-comments-header-renderer, #placeholder-area.ytd-comment-simplebox-renderer { display: none !important; }`;
if (settings.hideCommentActions) {
css += `
ytd-comment-renderer #action-buttons,
ytd-comment-view-model #action-buttons,
ytd-comment-action-buttons-renderer
{ display: none !important; }
`;
}
if (settings.hideRichMetadata) {
css += `
#contents.style-scope.ytd-rich-metadata-row-renderer,
ytd-rich-metadata-row-renderer,
ytd-metadata-row-container-renderer
{ display: none !important; }
`;
}
if (settings.hideMoreButton) {
css += `
ytd-watch-metadata #actions ytd-menu-renderer yt-button-shape,
ytd-watch-metadata #actions ytd-menu-renderer yt-icon-button,
ytd-watch-metadata #actions ytd-menu-renderer button[aria-label*="추가 작업"] {
opacity: 0 !important;
width: 0 !important;
padding: 0 !important;
margin: 0 !important;
pointer-events: none !important;
overflow: hidden !important;
}
`;
}
if (settings.replaceShareButton) {
css += `
ytd-watch-metadata #actions button-view-model:has(button[aria-label*="공유"]),
ytd-watch-metadata #actions button-view-model:has(path[d*="M15 5.63 20.66 12 15 18.37V14h-1c-3.96 0-7.14 1-9.75 3.09 1.84-4.07 5.11-6.4 9.89-7.1l.86-.13V5.63M14 3v6C6.22 10.13 3.11 15.33 2 21c3.13-6.53 9.71-9.08 12-9.42v6l10-10.57L14 3z"])
{ display: none !important; }
`;
}
if (settings.hideVideoLikeDislike) {
css += `
segmented-like-dislike-button-view-model,
ytd-menu-renderer ytd-toggle-button-renderer
{ display: none !important; }
`;
}
if (settings.hideChannelRecommendation) {
css += `
ytd-item-section-renderer ytd-channel-renderer,
ytd-rich-section-renderer:has(ytd-channel-renderer),
ytd-compact-channel-renderer,
ytd-shelf-renderer:has(ytd-channel-renderer)
{ display: none !important; }
`;
}
css += `
tp-yt-paper-dialog:has(ytd-add-to-playlist-renderer),
ytd-popup-container tp-yt-paper-dialog:has(ytd-add-to-playlist-renderer) {
position: fixed !important;
top: 50% !important;
left: 50% !important;
bottom: auto !important;
right: auto !important;
transform: translate(-50%, -50%) !important;
margin: 0 !important;
box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.4) !important;
z-index: 2202 !important;
}
`;
css += `
body.yt-ui-save-processing ytd-menu-popup-renderer,
body.yt-ui-save-processing tp-yt-iron-dropdown,
body.yt-ui-save-processing #menu-container {
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
`;
css += `
.yt-ui-custom-btn {
background-color: rgba(255, 255, 255, 0.1);
color: #f1f1f1;
border: none;
border-radius: 18px;
padding: 0 16px;
height: 36px;
font-size: 14px;
font-family: "Roboto", "Arial", sans-serif;
font-weight: 500;
line-height: 36px;
cursor: pointer;
margin: 0 !important;
display: inline-flex;
align-items: center;
justify-content: center;
white-space: nowrap;
vertical-align: middle;
transition: background-color 0.1s;
box-sizing: border-box;
}
#yt-ui-customizer-btn { margin-right: 4px !important; }
#yt-ui-action-group {
display: flex;
flex-direction: row;
align-items: center;
gap: 4px;
margin-left: 4px;
}
.yt-ui-custom-btn:hover { background-color: rgba(255, 255, 255, 0.2); }
html:not([dark]) .yt-ui-custom-btn { background-color: rgba(0, 0, 0, 0.05); color: #0f0f0f; }
html:not([dark]) .yt-ui-custom-btn:hover { background-color: rgba(0, 0, 0, 0.1); }
.yt-ui-btn-icon { width: 24px; height: 24px; margin-right: 6px; fill: currentColor; pointer-events: none; margin-top: -2px; }
#yt-ui-toast {
position: fixed; bottom: 80px; left: 50%; transform: translateX(-50%);
background-color: rgba(0,0,0,0.8); color: white;
padding: 10px 20px; border-radius: 4px;
font-size: 14px; z-index: 9999;
opacity: 0; transition: opacity 0.3s; pointer-events: none;
}
html:not([dark]) #yt-ui-toast { background-color: rgba(0,0,0,0.7); }
#yt-ui-settings-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 9998; }
#yt-ui-settings-modal {
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: #212121; color: white; padding: 24px; border-radius: 12px;
z-index: 9999; width: 360px; max-height: 85vh; overflow-y: auto;
box-shadow: 0 12px 24px rgba(0,0,0,0.5);
}
html:not([dark]) #yt-ui-settings-modal { background: #ffffff; color: #0f0f0f; box-shadow: 0 12px 24px rgba(0,0,0,0.2); }
.yt-ui-modal-header { font-size: 18px; font-weight: bold; margin-bottom: 16px; display: flex; justify-content: space-between; }
.yt-ui-modal-close { cursor: pointer; font-size: 20px; }
.yt-ui-setting-item { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,0.1); }
html:not([dark]) .yt-ui-setting-item { border-bottom: 1px solid rgba(0,0,0,0.1); }
.yt-ui-btn-group { margin-top: 20px; display: flex; gap: 10px; }
.yt-ui-action-btn { flex: 1; padding: 10px; border: 1px solid #555; border-radius: 18px; background: transparent; color: inherit; cursor: pointer; }
`;
styleElement.textContent = css;
}
function loadSettings() {
const saved = localStorage.getItem(STORAGE_KEY);
return saved ? { ...DEFAULT_SETTINGS, ...JSON.parse(saved) } : DEFAULT_SETTINGS;
}
function saveSettings() {
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
updateStyles();
runDynamicModules();
}
function showToast(message) {
let toast = document.getElementById('yt-ui-toast');
if (!toast) {
toast = document.createElement('div');
toast.id = 'yt-ui-toast';
document.body.appendChild(toast);
}
toast.textContent = message;
toast.style.opacity = '1';
setTimeout(() => { toast.style.opacity = '0'; }, 2000);
}
function formatTime(seconds) {
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = Math.floor(seconds % 60);
if (h > 0) return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
return `${m}:${s.toString().padStart(2, '0')}`;
}
function getActionGroupContainer() {
const actionsContainer = document.querySelector('ytd-watch-metadata #actions #top-level-buttons-computed');
if (!actionsContainer) return null;
let group = document.getElementById('yt-ui-action-group');
if (!group) {
group = document.createElement('div');
group.id = 'yt-ui-action-group';
const downloadBtn = actionsContainer.querySelector('ytd-download-button-renderer');
if (downloadBtn) {
actionsContainer.insertBefore(group, downloadBtn);
} else {
actionsContainer.appendChild(group);
}
}
return group;
}
function injectSaveButton() {
const btnId = 'yt-ui-custom-save-btn';
if (!settings.extractSaveButton) {
const existing = document.getElementById(btnId);
if (existing) existing.remove();
return;
}
if (document.getElementById(btnId)) return;
const group = getActionGroupContainer();
if (!group) return;
const btn = document.createElement('button');
btn.id = btnId;
btn.className = 'yt-ui-custom-btn';
btn.innerHTML = ttPolicy.createHTML(`
<svg class="yt-ui-btn-icon" viewBox="0 0 24 24"><path d="M14 10H2v2h12v-2zm0-4H2v2h12V6zm4 8v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zM2 16h8v-2H2v2z"></path></svg>
저장
`);
btn.onclick = (e) => {
e.stopPropagation();
e.preventDefault();
const moreBtn = document.querySelector('ytd-watch-metadata #actions ytd-menu-renderer yt-icon-button#button');
if (!moreBtn) {
alert('원본 더보기 버튼을 찾을 수 없습니다.');
return;
}
document.body.classList.add('yt-ui-save-processing');
const popupContainer = document.querySelector('ytd-popup-container');
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.addedNodes.length > 0) {
const items = document.querySelectorAll('ytd-menu-service-item-renderer');
for (const item of items) {
const path = item.querySelector('path')?.getAttribute('d');
const text = item.innerText;
if ((path && path.includes('M14 10H2v2h12v-2zm0-4H2v2h12V6zm4 8v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4z')) || text.includes('저장') || text.includes('Save')) {
const clickable = item.querySelector('tp-yt-paper-item') || item;
clickable.click();
observer.disconnect();
document.body.classList.remove('yt-ui-save-processing');
return;
}
}
}
}
});
if (popupContainer) {
observer.observe(popupContainer, { childList: true, subtree: true });
}
moreBtn.click();
setTimeout(() => {
observer.disconnect();
document.body.classList.remove('yt-ui-save-processing');
}, 1000);
};
group.appendChild(btn);
}
function injectShareButtons() {
const shareId = 'yt-ui-custom-share-btn';
const shareTimeId = 'yt-ui-custom-share-time-btn';
if (!settings.replaceShareButton) {
const b1 = document.getElementById(shareId);
const b2 = document.getElementById(shareTimeId);
if (b1) b1.remove();
if (b2) b2.remove();
return;
}
if (document.getElementById(shareId) && document.getElementById(shareTimeId)) return;
const group = getActionGroupContainer();
if (!group) return;
const shareBtn = document.createElement('button');
shareBtn.id = shareId;
shareBtn.className = 'yt-ui-custom-btn';
shareBtn.innerHTML = ttPolicy.createHTML(`
<svg class="yt-ui-btn-icon" viewBox="0 0 24 24"><path d="M15 5.63 20.66 12 15 18.37V14h-1c-3.96 0-7.14 1-9.75 3.09 1.84-4.07 5.11-6.4 9.89-7.1l.86-.13V5.63M14 3v6C6.22 10.13 3.11 15.33 2 21c3.13-6.53 9.71-9.08 12-9.42v6l10-10.57L14 3z"></path></svg>
공유
`);
shareBtn.onclick = (e) => {
e.stopPropagation();
const videoId = new URLSearchParams(window.location.search).get('v');
if (videoId) {
const url = `https://youtu.be/${videoId}`;
navigator.clipboard.writeText(url).then(() => showToast('링크가 복사되었습니다'));
}
};
const shareTimeBtn = document.createElement('button');
shareTimeBtn.id = shareTimeId;
shareTimeBtn.className = 'yt-ui-custom-btn';
shareTimeBtn.innerHTML = ttPolicy.createHTML(`
<svg class="yt-ui-btn-icon" viewBox="0 0 24 24"><path d="M15 5.63 20.66 12 15 18.37V14h-1c-3.96 0-7.14 1-9.75 3.09 1.84-4.07 5.11-6.4 9.89-7.1l.86-.13V5.63M14 3v6C6.22 10.13 3.11 15.33 2 21c3.13-6.53 9.71-9.08 12-9.42v6l10-10.57L14 3z"></path></svg>
공유(시간)
`);
shareTimeBtn.onclick = (e) => {
e.stopPropagation();
const videoId = new URLSearchParams(window.location.search).get('v');
const videoElem = document.querySelector('video');
if (videoId && videoElem) {
const time = Math.floor(videoElem.currentTime);
const url = `https://youtu.be/${videoId}?t=${time}`;
navigator.clipboard.writeText(url).then(() => showToast(`${formatTime(time)} 지점 링크 복사됨`));
}
};
group.insertBefore(shareBtn, group.firstChild);
group.insertBefore(shareTimeBtn, shareBtn.nextSibling);
}
function injectConfigButton() {
if (document.getElementById('yt-ui-customizer-btn')) return;
const actionsContainer = document.querySelector('ytd-watch-metadata #actions #top-level-buttons-computed');
if (!actionsContainer) return;
const btn = document.createElement('button');
btn.id = 'yt-ui-customizer-btn';
btn.className = 'yt-ui-custom-btn';
btn.textContent = 'UI 설정';
btn.onclick = openSettingsModal;
actionsContainer.insertBefore(btn, actionsContainer.firstChild);
}
function openSettingsModal() {
if (document.getElementById('yt-ui-settings-overlay')) return;
const overlay = document.createElement('div');
overlay.id = 'yt-ui-settings-overlay';
overlay.onclick = () => { overlay.remove(); document.getElementById('yt-ui-settings-modal').remove(); };
const modal = document.createElement('div');
modal.id = 'yt-ui-settings-modal';
let html = `
<div class="yt-ui-modal-header">
<span>UI 제거 설정</span>
<span class="yt-ui-modal-close" onclick="document.getElementById('yt-ui-settings-overlay').click()">×</span>
</div>
`;
const items = [
{ k: 'hideCreateButton', t: "'만들기' 버튼 숨기기" },
{ k: 'hideNotification', t: '알림 벨 숨기기' },
{ k: 'hideVoiceSearch', t: '음성 검색 숨기기' },
{ k: 'hideDownload', t: '오프라인 저장 숨기기' },
{ k: 'hideClip', t: '클립 버튼 숨기기' },
{ k: 'hideThanks', t: 'Thanks 버튼 숨기기' },
{ k: 'hideVideoLikeDislike', t: '영상 좋아요/싫어요 숨기기' },
{ k: 'hideRichMetadata', t: '게임/카테고리 정보 숨기기' },
{ k: 'hideShorts', t: 'Shorts 섹션 숨기기' },
{ k: 'hideChannelRecommendation', t: '채널 추천(구독) 카드 숨기기' },
{ k: 'replaceShareButton', t: "'공유' 버튼 교체 (일반/시간)" },
{ k: 'hideMoreButton', t: '더보기(...) 버튼 숨기기' },
{ k: 'extractSaveButton', t: "'저장' 버튼 꺼내기" },
{ k: 'hideCommentInput', t: '댓글 입력란 숨기기' },
{ k: 'hideCommentActions', t: '댓글 좋아요/싫어요 숨기기' }
];
items.forEach(item => {
html += `
<div class="yt-ui-setting-item">
<label for="yt-opt-${item.k}">${item.t}</label>
<input type="checkbox" id="yt-opt-${item.k}" ${settings[item.k] ? 'checked' : ''}>
</div>
`;
});
html += `
<div class="yt-ui-btn-group">
<button class="yt-ui-action-btn" id="yt-btn-export">설정 내보내기</button>
<button class="yt-ui-action-btn" id="yt-btn-import">설정 불러오기</button>
</div>
`;
modal.innerHTML = ttPolicy.createHTML(html);
document.body.appendChild(overlay);
document.body.appendChild(modal);
items.forEach(item => {
document.getElementById(`yt-opt-${item.k}`).onchange = (e) => {
settings[item.k] = e.target.checked;
saveSettings();
};
});
document.getElementById('yt-btn-export').onclick = () => {
const a = document.createElement('a');
a.href = URL.createObjectURL(new Blob([JSON.stringify(settings, null, 2)], { type: 'application/json' }));
a.download = 'yt_ui_settings.json';
a.click();
};
document.getElementById('yt-btn-import').onclick = () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.onchange = e => {
const r = new FileReader();
r.onload = ev => {
try {
settings = { ...DEFAULT_SETTINGS, ...JSON.parse(ev.target.result) };
saveSettings();
document.getElementById('yt-ui-settings-overlay').click();
showToast('설정 불러오기 완료');
} catch (e) { alert('오류 발생'); }
};
r.readAsText(e.target.files[0]);
};
input.click();
};
}
function runDynamicModules() {
injectConfigButton();
injectSaveButton();
injectShareButtons();
}
updateStyles();
setInterval(runDynamicModules, 1000);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment