您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Hide from YouTube's mobile browser homepage any videos that are recommended more than twice. You can also hide by channel or by partial title.
当前为
- // ==UserScript==
- // @name YouTube Mobile Repeated Recommendations Hider
- // @description Hide from YouTube's mobile browser homepage any videos that are recommended more than twice. You can also hide by channel or by partial title.
- // @version 1.8
- // @author BLBC (github.com/hjk789, greasyfork.org/users/679182-hjk789)
- // @copyright 2020+, BLBC (github.com/hjk789, greasyfork.org/users/679182-hjk789)
- // @homepage https://github.com/hjk789/Creations/tree/master/JavaScript/Userscripts/YouTube-Mobile-Repeated-Recommendations-Hider
- // @license https://github.com/hjk789/Creations/tree/master/JavaScript/Userscripts/YouTube-Mobile-Repeated-Recommendations-Hider#license
- // @match https://m.youtube.com
- // @grant GM.setValue
- // @grant GM.getValue
- // @grant GM.listValues
- // @grant GM.deleteValue
- // @namespace https://greasyfork.org/users/679182
- // ==/UserScript==
- //******* SETTINGS ********
- const maxRepetitions = 2 // The maximum number of times that the same recommended video is allowed to appear on your
- // homepage before starting to get hidden. Set this to 1 if you want one-time recommendations.
- //*************************
- let channelsToHide, partialTitlesToHide
- let processedVideosList
- GM.getValue("channels").then(function(value)
- {
- if (!value)
- {
- value = JSON.stringify([])
- GM.setValue("channels", value)
- }
- channelsToHide = JSON.parse(value)
- GM.getValue("partialTitles").then(function(value)
- {
- if (!value)
- {
- value = JSON.stringify([])
- GM.setValue("partialTitles", value)
- }
- partialTitlesToHide = JSON.parse(value)
- GM.listValues().then(function(GmList) // Get in an array all the items currently in the script's storage. Searching for a value in
- { // an array is much faster and lighter than calling GM.getValue for every recommendation.
- processedVideosList = GmList
- const recommendationsContainer = document.querySelector(".rich-grid-renderer-contents")
- const firstVideos = recommendationsContainer.querySelectorAll("ytm-rich-item-renderer") // Because a mutation observer is being used and the script is run after the page is fully
- // loaded, the observer isn't triggered with the recommendations that appear first.
- for (let i=0; i < firstVideos.length; i++) // This does the processing manually to these first ones.
- processRecommendation(firstVideos[i])
- const loadedRecommendedVideosObserver = new MutationObserver(function(mutations) // A mutation observer is being used so that all processings happen only
- { // when actually needed, which is when more recommendations are loaded.
- for (let i=0; i < mutations.length; i++)
- processRecommendation(mutations[i].addedNodes[0])
- })
- loadedRecommendedVideosObserver.observe(recommendationsContainer, {childList: true})
- })
- })
- })
- async function processRecommendation(node)
- {
- if (!node) return
- const videoTitleEll = node.querySelector("h3")
- const videoTitleText = videoTitleEll.textContent.toLowerCase() // Convert the title's text to lowercase so that there's no distinction with uppercase letters.
- const videoChannel = videoTitleEll.nextSibling.firstChild.firstChild.textContent
- const videoUrl = videoTitleEll.parentElement.href
- const videoMenuBtn = node.querySelector("ytm-menu")
- if (videoMenuBtn)
- {
- videoMenuBtn.onclick = function()
- {
- waitForMenu = setInterval(function(node, videoChannel)
- {
- const menu = document.getElementById("menu")
- if (menu)
- {
- clearInterval(waitForMenu)
- const hideChannelButton = document.createElement("button")
- hideChannelButton.className = "menu-item-button"
- hideChannelButton.innerText = "Hide videos from this channel"
- hideChannelButton.onclick = function()
- {
- if (confirm("Are you sure you want to hide all videos from the channel ''" + videoChannel + "''?"))
- {
- channelsToHide.push(videoChannel)
- GM.setValue("channels", JSON.stringify(channelsToHide))
- }
- }
- const hidePartialTitleButton = document.createElement("button")
- hidePartialTitleButton.className = "menu-item-button"
- hidePartialTitleButton.innerText = "Hide videos that include a text"
- hidePartialTitleButton.onclick = function()
- {
- const partialText = prompt("Specify the partial title of the videos to hide. All videos that contain this text in the title will get hidden.")
- if (partialText)
- {
- partialTitlesToHide.push(partialText.toLowerCase())
- GM.setValue("partialTitles", JSON.stringify(partialTitlesToHide))
- }
- }
- menu.firstChild.appendChild(hideChannelButton)
- menu.firstChild.appendChild(hidePartialTitleButton)
- }
- }, 100, node, videoChannel)
- }
- }
- if (processedVideosList.includes("hide::"+videoUrl) || channelsToHide.includes(videoChannel) || partialTitlesToHide.some(p => videoTitleText.includes(p)))
- node.style.display = "none"
- else
- {
- if (maxRepetitions == 1) // If the script is set to show only one-time recommendations, to avoid unnecessary processings,
- { // rightaway mark to hide in the next time the page is loaded every video not found in the storage.
- GM.setValue("hide::"+videoUrl,"")
- return
- }
- else
- var value = await GM.getValue(videoUrl)
- if (typeof value == "undefined")
- value = 1
- else
- {
- if (value >= maxRepetitions)
- {
- node.style.display = "none"
- GM.deleteValue(videoUrl)
- GM.setValue("hide::"+videoUrl,"")
- return
- }
- value++
- }
- GM.setValue(videoUrl, value)
- }
- }