Skip to content

Instantly share code, notes, and snippets.

@kphrx
Last active August 16, 2025 13:37
Show Gist options
  • Select an option

  • Save kphrx/eb4290150bc7556af95eac2c89965cbe to your computer and use it in GitHub Desktop.

Select an option

Save kphrx/eb4290150bc7556af95eac2c89965cbe to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name YouTube block user comment
// @namespace https://kpherox.dev
// @version 1.2.1
// @downloadURL https://gist.github.com/kphrx/eb4290150bc7556af95eac2c89965cbe/raw/youtube-comment-block.user.js
// @updateURL https://gist.github.com/kphrx/eb4290150bc7556af95eac2c89965cbe/raw/youtube-comment-block.meta.js
// @author kPherox
// @description block user comment
// @noframes
// ==/UserScript==
// ==UserScript==
// @name YouTube block user comment
// @namespace https://kpherox.dev
// @match https://www.youtube.com/*
// @grant GM.addStyle
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_getValues
// @grant GM_setValue
// @grant GM_listValues
// @grant GM_deleteValue
// @version 1.2.1
// @downloadURL https://gist.github.com/kphrx/eb4290150bc7556af95eac2c89965cbe/raw/youtube-comment-block.user.js
// @updateURL https://gist.github.com/kphrx/eb4290150bc7556af95eac2c89965cbe/raw/youtube-comment-block.meta.js
// @author kPherox
// @description block user comment
// @noframes
// ==/UserScript==
const config = new class {
#values;
blockedList;
constructor() {
this.#values = GM_listValues().filter((key) => {
return key.startsWith('/channel/') || key === 'blocked';
});
this.blockedList = GM_getValues(this.#values);
this.#migrate();
Promise.all(Object.entries(this.blockedList).map(([channelUrl, handle]) => {
return this.#addStyle({channelUrl, handle})
})).then(console.debug);
}
get #requireMigrate() {
return this.#values.includes("blocked");
}
#migrate() {
if (this.#requireMigrate) {
for (const blocked of (this.blockedList['blocked'] ?? [])) {
this.blockedList[blocked.channelUrl] = blocked.handle;
GM_setValue(blocked.channelUrl, blocked.handle);
}
delete this.blockedList['blocked'];
GM_deleteValue('blocked');
}
}
#addStyle({channelUrl, handle}) {
return GM.addStyle(`ytd-comment-thread-renderer:has(> #comment-container > ytd-comment-view-model > #body > div#author-thumbnail > button#author-thumbnail-button[aria-label="${handle}"]),
ytd-comment-replies-renderer ytd-comment-view-model:has(
> #body > #author-thumbnail > #author-thumbnail-button[aria-label="${handle}"],
> #body > #main > #expander > #content > #content-text a.yt-core-attributed-string__link[href="${channelUrl}"]
) {
display: none;
}`);
}
block({channelUrl, handle}) {
this.blockedList[channelUrl] = handle;
GM_setValue(channelUrl, handle);
return this.#addStyle({channelUrl, handle});
}
};
const blockUserFn = (handle, channelUrl) => ((ev) => {
config.block({handle, channelUrl});
});
const addBlockButton = (profileCardView) => {
if (profileCardView == null) {
return;
}
const profileIdentityInfo = profileCardView.rawProps.data().profileIdentityInfo.profileIdentityInfoViewModel;
const channelBannerContainer = profileCardView.querySelector('.yt-profile-identity-info-view-model-wiz__channel-banner-container');
const wrapButton = channelBannerContainer.appendChild(document.createElement('div'));
wrapButton.classList.add('yt-profile-identity-info-view-model-wiz__wrap-button');
wrapButton.style.paddingLeft = '16px';
const button = wrapButton.appendChild(document.createElement('div'));
button.classList.add('yt-profile-identity-info-view-model-wiz__button');
const buttonViewModel = button.appendChild(document.createElement('div'));
buttonViewModel.classList.add('yt-spec-button-view-model');
const buttonShape = buttonViewModel.appendChild(document.createElement('a'));
buttonShape.classList.add('yt-spec-button-shape-next', 'yt-spec-button-shape-next--tonal', 'yt-spec-button-shape-next--mono', 'yt-spec-button-shape-next--size-m');
const buttonText = buttonShape.appendChild(document.createElement('div'));
buttonText.classList.add('yt-spec-button-shape-next__button-text-content');
buttonText.textContent = 'Block';
const listener = blockUserFn(profileIdentityInfo.channelHandle, profileIdentityInfo.channelAccess.buttonViewModel.onTap.innertubeCommand.commandMetadata.webCommandMetadata.url);
buttonShape.addEventListener('click', listener);
};
const watchMultiPageMenu = new MutationObserver((records, observer) => {
for (const record of records) {
if (record.type !== "childList" || record.addedNodes.length === 0) {
continue;
}
addBlockButton(Array.from(record.addedNodes).find(node => node.nodeName.toLowerCase() === 'yt-profile-card-view-model'));
}
});
new MutationObserver((records, observer) => {
const ytdMultiPageMenu = document.querySelector('#sections.ytd-multi-page-menu-renderer');
if (ytdMultiPageMenu == null) {
return;
}
observer.disconnect();
addBlockButton(document.querySelector('yt-profile-card-view-model'));
watchMultiPageMenu.observe(ytdMultiPageMenu, {childList: true, subtree: true});
}).observe(document.body, {childList: true});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment