YouTube Quick Not Interested | Cododel [RU]
CODODELDEV
EN / RU
Back to Deck
[userscript]

YouTube Quick Not Interested

УСТАНОВИТЬ
ИСХОДНИК YouTube
ВЕРСИЯ 2.4
АВТОР Cododel

Target Match:

  • https://www.youtube.com/
  • https://www.youtube.com/?*

Убирает трение при обучении алгоритмов YouTube. Этот скрипт добавляет кнопку в один клик для моментального удаления нежелательного контента из ленты.

Возможности

  • Только главная: Работает исключительно на главной странице YouTube
  • Действие в один клик: Мгновенно помечает контент как “Не интересует”
  • Два языка: Находит пункт меню по его тексту (русский и английский)
  • Умное обнаружение: Автоматически находит кнопки меню через MutationObserver
  • Нативный вид: Компактная кнопка под меню из трёх точек, стилизована под YouTube

Как это работает

При нажатии кнопка автоматически:

  1. Открывает меню видео (три точки)
  2. Находит опцию “Не интересует” по её тексту
  3. Кликает по ней для удаления видео

Пункт меню определяется по тексту, поэтому поддерживаются русский и английский интерфейсы YouTube.

Установка

  1. Установите расширение для UserScripts (Tampermonkey/Violentmonkey)
  2. Установить скрипт
  3. Посетите главную YouTube и наслаждайтесь чистой лентой!

Исходный код

// ==UserScript==
// @name YouTube Quick Not Interested
// @namespace https://cododel.dev/
// @version 2.4
// @description Adds a small, centered "Not interested" button under the 3-dot menu (Only Homepage)
// @author Cododel
// @match https://www.youtube.com/
// @match https://www.youtube.com/?*
// @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @updateURL https://cododel.dev/mods/youtube-quick-not-interested/install.user.js
// @downloadURL https://cododel.dev/mods/youtube-quick-not-interested/install.user.js
// @grant none
// ==/UserScript==
;(function () {
'use strict'
const CONFIG = {
sel: {
container: 'yt-lockup-metadata-view-model',
menuWrapper: 'button-view-model',
menuItem: 'yt-list-item-view-model, ytd-menu-service-item-renderer, tp-yt-paper-item',
popupContainer: 'ytd-popup-container',
},
keywords: ['Не интересует', 'Not interested'],
customSvgPath:
'M 12 1C5.925 1 1 5.925 1 12s4.925 11 11 11 11-4.925 11-11S18.075 1 12 1Zm0 2a9 9 0 018.246 12.605L4.755 6.661A8.99 8.99 0 0112 3ZM3.754 8.393l15.491 8.944A9 9 0 013.754 8.393Z',
cls: 'custom-ni-btn',
boxWidth: 40, // match the 3-dot button box so it centers identically
boxHeight: 20, // half height
iconSize: 12, // half-size icon
}
const isHomePage = () => window.location.pathname === '/'
const h = (tag, props = {}, ...children) => {
const el = document.createElementNS(props.xmlns || 'http://www.w3.org/1999/xhtml', tag)
Object.entries(props).forEach(([k, v]) =>
k === 'style'
? Object.assign(el.style, v)
: k.startsWith('on')
? el.addEventListener(k.slice(2).toLowerCase(), v)
: el.setAttribute(k, v),
)
children.forEach((c) => el.appendChild(c))
return el
}
const performAction = async (container) => {
const wrapper = container.querySelector(CONFIG.sel.menuWrapper)
const menuBtn = wrapper && wrapper.querySelector('button')
if (!menuBtn) {
console.warn('YouTube Quick Not Interested: Menu button not found')
return
}
menuBtn.click()
await new Promise((r) => setTimeout(r, 200))
const popup = document.querySelector(CONFIG.sel.popupContainer)
if (!popup) return
const items = Array.from(popup.querySelectorAll(CONFIG.sel.menuItem))
const target = items.find((item) =>
CONFIG.keywords.some((txt) => (item.textContent || '').includes(txt)),
)
if (target) {
target.click()
} else {
document.body.click()
console.warn('YouTube Quick Not Interested: "Not interested" item not found')
}
}
const createButton = (container) =>
h(
'button',
{
class: CONFIG.cls,
title: 'Не интересует / Not interested',
onClick: (e) => {
e.preventDefault()
e.stopPropagation()
performAction(container)
},
style: {
background: 'transparent',
border: 'none',
cursor: 'pointer',
padding: '0',
borderRadius: '50%',
width: CONFIG.boxWidth + 'px',
height: CONFIG.boxHeight + 'px',
color: '#fff',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
boxSizing: 'border-box',
transition: 'background 0.2s',
},
onMouseEnter: (e) => (e.currentTarget.style.background = 'rgba(255,255,255,0.1)'),
onMouseLeave: (e) => (e.currentTarget.style.background = 'transparent'),
},
h(
'svg',
{
xmlns: 'http://www.w3.org/2000/svg',
viewBox: '0 0 24 24',
width: String(CONFIG.iconSize),
height: String(CONFIG.iconSize),
style: { pointerEvents: 'none', fill: 'currentColor', display: 'block' },
},
h('path', { xmlns: 'http://www.w3.org/2000/svg', d: CONFIG.customSvgPath }),
),
)
const inject = (node) => {
if (!isHomePage()) return
if (!node || node.nodeType !== 1) return
const containers = node.matches?.(CONFIG.sel.container)
? [node]
: node.querySelectorAll(CONFIG.sel.container)
containers.forEach((c) => {
if (c.querySelector('.' + CONFIG.cls)) return
const wrapper = c.querySelector(CONFIG.sel.menuWrapper)
if (!wrapper || !wrapper.parentElement) return
wrapper.parentElement.appendChild(createButton(c))
})
}
const start = () => {
const observer = new MutationObserver((mutations) =>
mutations.forEach((m) => m.addedNodes.forEach(inject)),
)
observer.observe(document.body, { childList: true, subtree: true })
inject(document.body)
}
if (document.body) {
start()
} else {
document.addEventListener('DOMContentLoaded', start)
}
})()
[ ▲ 0 ]