您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Hide comments by specific users in the comments section.
// ==UserScript== // @name YouTube Comment Snub // @description Hide comments by specific users in the comments section. // @version 1.4.3 // @author stinkrock // @namespace patchmonkey // @license MIT // @match https://www.youtube.com/* // @run-at document-idle // @noframes // ==/UserScript== const conf = { id: 'snub-button', innerText: '🤐', ariaLabel: 'Snub this user' } const style = `cursor:pointer;font-size:14px;margin:0 8px 0 -8px;padding:var(--yt-button-icon-padding,8px);` const YTNEXT = 'yt-next-continuation-data-updated' const YTNAVIGATE = 'yt-navigate-start' const YTDSECTION = 'ytd-comments ytd-item-section-renderer' const YTDREPLIES = 'ytd-comments ytd-comment-replies-renderer' const YTDTHREAD = 'ytd-comment-thread-renderer' const YTDCOMMENT = 'ytd-comment-renderer' const CONTENTS = '#contents' const TOOLBAR = '#toolbar' const AUTHOR = '#author-thumbnail a' const SNUB = '#' + conf.id const key = 'youtube-comment-snub' const users = new Set() const containers = new WeakSet() const buttons = new WeakMap() const thumbs = new WeakMap() const comp = (f, g) => x => g(f(x)) const compose = (...f) => f.reduce(comp) const or = (f, g) => x => f(x) || g(x) const either = (...f) => f.reduce(or) const cond = (f, g) => x => Promise.resolve(x).then(f).catch(g) const condition = (f, g) => x => f(x) ? g(x) : null const each = x => f => f(x) const list = (...f) => x => f.map(each(x)) const foreach = (...f) => x => f.forEach(each(x)) const flatmap = (...f) => x => f.flatMap(each(x)) const filter = f => x => x.filter(f) const map = f => x => x.map(f) const flat = x => x.flat() const join = x => y => y.join(x) const id = x => x const opt = x => y => y != null ? y : x const getitem = (x, y) => () => x.getItem(y) const setitem = (x, y) => z => x.setItem(y, z) const stringify = x => JSON.stringify(x) const parse = x => JSON.parse(x) const set = x => ([...y]) => x.set(...y) const get = x => y => x.get(y) const add = x => y => x.add(y) const has = x => y => x.has(y) const clear = x => x.clear() const observer = f => new MutationObserver(f) const childlist = x => y => x.observe(y, { childList: true }) const attribute = x => y => x.observe(y, { attributeFilter: ['href'] }) const subtree = x => y => x.observe(y, { childList: true, subtree: true }) const elem = x => document.createElement(x) const text = x => document.createTextNode(x) const append = (x, y) => x.appendChild(y) const insert = ([x, y, z]) => x.insertBefore(y, z) const match = x => y => y.matches(x) ? y : null const query = x => y => y.querySelector(x) const clone = x => y => x.cloneNode(y) const attr = x => y => y.getAttribute(x) const div = () => elem('div') const children = x => [...x.children] const added = x => [...x.addedNodes] const first = x => x.firstElementChild const current = x => x.currentTarget const target = x => x.target const type = x => x.nodeType const number = x => isNaN(x) ? 0 : x const lower = x => x.toLowerCase() const array = x => [...x] const value = x => () => x const equal = x => y => x == y const assign = x => y => Object.assign(y, x) const data = x => y => x.data = y const rule = x => `[data-snub="${x}"]` const body = x => x.length ? `${x} { display: none !important; }` : '' const css = x => y => (y.style.cssText = x, y) const snub = ([x, y]) => x.dataset.snub = y const sheet = append(document.documentElement, elem('style')) const rules = append(sheet, text('')) const compile = compose(map(rule), join(',\n'), body) const read = compose(getitem(localStorage, key), opt('[]'), parse) const write = compose(stringify, setitem(localStorage, key)) const update = compose(compile, data(rules)) const load = compose(read, foreach(map(add(users)), update)) const reset = compose(value(users), clear, load) const save = compose(value(users), array, foreach(write, update)) const tag = compose(list(id, compose(query(AUTHOR), attr('href'))), snub) const link = compose(list(get(thumbs), attr('href')), snub) const change = compose(map(target), flat, map(link)) const observe = compose(query(AUTHOR), attribute(observer(change))) const quarantine = compose(get(buttons), attr('href'), add(users)) const onclick = compose(current, foreach(quarantine, save)) const button = compose(div, assign(conf), assign({ onclick }), css(style)) const action = compose(query(TOOLBAR), list(id, button, first), insert) const auth = compose(list(query(SNUB), query(AUTHOR)), set(buttons)) const tool = compose(list(query(AUTHOR), id), set(thumbs)) const configure = foreach(action, auth, tool) const matches = flatmap(filter(match(YTDTHREAD)), filter(match(YTDCOMMENT))) const elements = filter(compose(type, equal(Node.ELEMENT_NODE))) const content = condition(match(YTDSECTION), query(CONTENTS)) const replies = condition(match(YTDREPLIES), query(CONTENTS)) const process = foreach(configure, tag, observe) const track = condition(compose(query(SNUB), equal(null)), process) const all = compose(matches, map(track)) const init = foreach(add(containers), compose(children, all)) const contain = condition(compose(has(containers), equal(false)), init) const mutations = compose(map(added), flat, elements, all) const follow = foreach(contain, childlist(observer(mutations))) const capture = compose(either(content, replies), follow) const run = compose(query(YTDSECTION), query(CONTENTS), follow) window.addEventListener(YTNEXT, compose(target, cond(capture, e => e))) window.addEventListener(YTNAVIGATE, reset) reset() Promise.resolve(document.body).then(run).catch(e => e)