您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Block annoying user comments.
当前为
// ==UserScript== // @name YouTube-Comment-Snub // @description Block annoying user comments. // @version 1.3.4 // @author wormboy // @license MIT // @match https://www.youtube.com/* // @run-at document-idle // @grant GM.setValue // @grant GM.getValue // @noframes // @namespace patchmonkey // ==/UserScript== async function loadBlacklist() { updateSnubIds(await getBlacklist()) } function getBlacklist() { try { return GM.getValue('blacklist', []) } catch(e) { return JSON.parse(localStorage.blacklist || '[]') } } function setValue(val) { try { return GM.setValue('blacklist', val) } catch(e) { localStorage.blacklist = JSON.stringify(val) } } function observeComments(thread) { for (const child of thread.children) { tagComment(child) } commentObserver.observe(thread, { childList: true }) } function tagComment(node) { if (node.nodeName == 'YTD-COMMENT-THREAD-RENDERER' || node.nodeName == 'YTD-COMMENT-RENDERER') { addButton(node) const target = node.querySelector('#author-thumbnail a') linkMap.set(target, node) linkObserver.observe(target, { attributeFilter: ['href'] }) updateCommentTag(node, target) } } function updateCommentTag(node, target) { node.dataset.snub = target.getAttribute('href') } async function quarantineUser(event) { const { snub } = buttonMap.get(event.currentTarget).dataset let list = await getBlacklist() list = new Set(list) list.add(snub) list = Array.from(list) updateSnubIds(list) await setValue(list) } function updateSnubIds(list) { let style = document.head.querySelector('#snub-id-list') if (!style) { style = document.head.appendChild(document.createElement('style')) style.id = 'snub-id-list' } const rules = list.length ? list.map(i => `[data-snub="${i}"]`).join(',\n') : '' style.textContent = rules && snubSelector(rules) } function waitForToolbar(node) { return new Promise(resolve => { const toolbar = node.querySelector('#toolbar') if (toolbar) resolve(toolbar) else { const observer = new MutationObserver(() => { const el = node.querySelector('#toolbar') if (el) { observer.disconnect() resolve(el) } }) observer.observe(node, { childList: true, subtree: true }) } }) } function addButton(node) { if (node.querySelector('#snub-button')) return waitForToolbar(node).then(toolbar => { const el = document.createElement('div') el.id = 'snub-button' el.style.cssText = buttonCss el.innerHTML = buttonTemplate('snub this user', 'Snub') el.querySelector('#button').style.cssText = iconButtonCss el.addEventListener('click', quarantineUser) const icon = el.querySelector('#icon') icon.innerHTML = snubIcon if (toolbar.firstElementChild) { toolbar.insertBefore(el, toolbar.firstElementChild) } else { toolbar.appendChild(el) } buttonMap.set(el, node) }) } function reusable(strs) { return function(...vals) { return strs.map((s, i) => `${s}${vals[i] || ''}`).join('') } } const linkMap = new WeakMap() const buttonMap = new WeakMap() const linkObserver = new MutationObserver(records => { for (const { target } of records) { updateCommentTag(linkMap.get(target), target) } }) const commentObserver = new MutationObserver(records => { for (const { addedNodes } of records) { for (const node of addedNodes) { tagComment(node) } } }) const buttonTemplate = reusable` <button id="button" class="style-scope yt-icon-button" aria-label="${'aria'}"> <div id="icon" class="style-scope ytd-toggle-button-renderer"></div> </button> <paper-tooltip>${'tooltip'}</paper-tooltip> ` const buttonCss = ` --yt-button-icon-size: var(--ytd-comment-thumb-dimension); margin: 0 8px 0 -8px; ` const iconButtonCss = ` padding: var(--yt-button-icon-padding, 8px); width: var(--yt-button-icon-size, var(--yt-icon-width, 40px)); height: var(--yt-button-icon-size, var(--yt-icon-height, 40px)); ` const snubIcon = ` <div style="pointer-events: none;">🤐</div> ` const snubSelector = reusable` ${'list'} { display: none !important; } ` const SECTION_SELECTOR = 'ytd-item-section-renderer' const COMMENT_SELECTOR = 'ytd-comment-replies-renderer,ytd-backstage-comments-renderer' window.addEventListener('yt-next-continuation-data-updated', ({ target: e }) => { if (e.matches(SECTION_SELECTOR)) { observeComments(e.querySelector('#contents')) } else if (e.matches(COMMENT_SELECTOR)) { observeComments(e.querySelector('#loaded-comments,#loaded-replies')) } }) window.addEventListener('yt-navigate-start', loadBlacklist) loadBlacklist()