Last active
December 29, 2025 11:33
-
-
Save snowyegret23/d7ee8b977c32c79eea36ae2a42c86a67 to your computer and use it in GitHub Desktop.
Userscript: 유튜브의 특정 UI 요소를 토글합니다.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==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