Greasy Fork is available in English.

Youtube Anti Shorts

shorts is a shit, fuck you youtube

بۇ قوليازمىنى قاچىلاش؟
ئاپتورنىڭ تەۋسىيەلىگەن قوليازمىسى

سىز بەلكىم Youtube 封面 نى ياقتۇرۇشىڭىز مۇمكىن.

بۇ قوليازمىنى قاچىلاش
// ==UserScript==
// @name                Youtube Anti Shorts
// @name:zh             Youtube Anti Shorts 反短片
// @namespace           Anong0u0
// @version             0.7.5
// @description         shorts is a shit, fuck you youtube
// @description:zh      短片就是坨屎,去你的youtube
// @author              Anong0u0
// @match               *://*.youtube.com/*
// @icon                https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant               GM_setValue
// @grant               GM_getValue
// @grant               GM_registerMenuCommand
// @grant               GM_unregisterMenuCommand
// @run-at              document-start
// @license             MIT
// ==/UserScript==

console.log("[Anti Shorts] load start");

let Hide_Shorts_Renderer = GM_getValue("Hide_Shorts_Renderer", true);
let Hide_Shorts_Video = GM_getValue("Hide_Shorts_Video", true);
let Redirect_Shorts_URL = GM_getValue("Redirect_Shorts_URL", true);

Node.prototype.getParentElement = function(times = 0){let e=this;for(let i=0;i<times;i++)e=e.parentElement;return e;}
NodeList.prototype.filter = Array.prototype.filter
NodeList.prototype.slice = Array.prototype.slice

const delay = (ms = 0) => {return new Promise((r)=>{setTimeout(r, ms)})}

const waitElementLoad = (elementSelector, isSelectAll, tryTimes = 1, interval = 0) =>
{
    return new Promise(async (resolve, reject)=>
    {
        let t = 1, result;
        while(true)
        {
            if(isSelectAll) {if((result = document.querySelectorAll(elementSelector)).length > 0) break;}
            else {if(result = document.querySelector(elementSelector)) break;}

            if(tryTimes>0 && ++t>tryTimes) {return reject(new Error("Wait Timeout"))}
            await delay(interval);
        }
        resolve(result);
    })
}

if (window.trustedTypes)
{
    const policy = trustedTypes.createPolicy("ytAntiShorts", {createHTML: (string) => string,});
    Node.prototype.setHTML = function (html) {this.innerHTML = policy.createHTML(html)}
}
else Node.prototype.setHTML = function (html) {this.innerHTML = html}

const fillRow = () =>
{
    if(window.location.pathname!="/feed/subscriptions") return;
    console.log("[Anti Shorts] fill row count")
    let row = document.querySelector("ytd-browse[page-subtype='subscriptions'] ytd-rich-grid-renderer > div#contents.ytd-rich-grid-renderer > ytd-rich-grid-row")
    const rowCount = getComputedStyle(row).getPropertyValue("--ytd-rich-grid-items-per-row")
    while(row.nextElementSibling?.tagName=="YTD-RICH-GRID-ROW")
    {
        const showedItem = row.querySelectorAll("ytd-rich-item-renderer").filter((e)=>getComputedStyle(e).display!="none")
        let need = rowCount-showedItem.length
        let nextRow = row
        while(need>0 && nextRow.nextElementSibling!=null)
        {
            nextRow = nextRow.nextElementSibling
            const rowContent = row.querySelector("div#contents.ytd-rich-grid-row")
            for (const e of nextRow.querySelectorAll("ytd-rich-item-renderer"))
            {
                if (need == 0) break;
                if (getComputedStyle(e).display != "none")
                {
                    rowContent.appendChild(e);
                    need--;
                }
            }
        }
        row = row.nextElementSibling
    }
}

const unfillRow = () =>
{
    if(window.location.pathname!="/feed/subscriptions") return;
    console.log("[Anti Shorts] unfill row count")
    let row = document.querySelector("ytd-browse[page-subtype='subscriptions'] ytd-rich-grid-renderer > div#contents.ytd-rich-grid-renderer > ytd-rich-grid-row")
    const rowCount = getComputedStyle(row).getPropertyValue("--ytd-rich-grid-items-per-row")
    while(row.nextElementSibling?.tagName=="YTD-RICH-GRID-ROW")
    {
        const rowContent = row.nextElementSibling.querySelector("div#contents.ytd-rich-grid-row")
        row.querySelectorAll("ytd-rich-item-renderer").slice(rowCount).forEach((e)=>
        {
            rowContent.appendChild(e)
        })
        row = row.nextElementSibling
    }
}

const debounce = ()=>
{
    clearTimeout(lockID)
    lockID = setTimeout(() => {fillRow()}, 50);
}

if((()=>{try{document.querySelector(":has(body)");return false;}catch{return true;}})())
{
    alert(`[Anti Shorts] Warning:
Your browser Does Not Support CSS4 selector (:has).
Please update or change your browser.
For Firefox users, please to go to "about:config" and enable "layout.css.has-selector.enabled" setting.`)
}

let menuID = [], oldHref = null, lockID = null;

const css =
{
    hideRenderer: document.createElement("style"),
    hideVideo: document.createElement("style"),
}
css.hideRenderer.setHTML(`
ytm-pivot-bar-item-renderer:has(.pivot-shorts),
ytm-rich-section-renderer:has(a[href^='/shorts']),
ytm-reel-shelf-renderer:has(a[href^='/shorts']),

ytd-reel-shelf-renderer.style-scope:is(.ytd-item-section-renderer,.ytd-structured-description-content-renderer),
ytd-mini-guide-entry-renderer[aria-label='Shorts'],
ytd-rich-shelf-renderer[is-shorts],
a.yt-simple-endpoint.style-scope.ytd-guide-entry-renderer[title='Shorts']
{display:none !important}`);
css.hideVideo.setHTML(`
.is-shorts,
ytm-reel-item-renderer:has(a[href^='/shorts']),
ytm-video-with-context-renderer:has(a[href^='/shorts']),
ytm-compact-video-renderer:has(a[href^='/shorts']),

[is-short],
[is-shorts-grid] ytd-continuation-item-renderer,
ytd-video-renderer:has(a[href^='/shorts']),
ytd-reel-item-renderer:has(a[href^='/shorts']),
ytd-rich-item-renderer:has(a[href^='/shorts']),
ytd-grid-video-renderer:has(a[href^='/shorts']),
ytd-compact-video-renderer:has(a[href^='/shorts']),
ytd-search ytd-shelf-renderer:has(a[href^='/shorts']),
ytd-browse ytd-item-section-renderer:has(yt-img-shadow#avatar):has(div#title-text):has(ytd-video-renderer):has(a[href^='/shorts'])
{display:none !important}`);
// ":has" selector is simple and "efficient", Use it instead of javascript DOM manipulation

const onPageUpdate = () =>
{
    //console.log("[Anti Shorts] page updated");
    if (oldHref != window.location.href)
    {
        oldHref = window.location.href;
        toggle.redirect();
    }
}

const toggle =
{
    renderer: ()=>
    {
        if(Hide_Shorts_Renderer) document.documentElement.append(css.hideRenderer);
        else css.hideRenderer.remove();
    },

    video: async ()=>
    {
        if(Hide_Shorts_Video)
        {
            document.addEventListener("yt-rendererstamper-finished", debounce)
            document.documentElement.append(css.hideVideo);
            fillRow();
        }
        else
        {
            document.removeEventListener("yt-rendererstamper-finished", debounce)
            css.hideVideo.remove();
            unfillRow()
        }
    },

    redirect: ()=>
    {
        if(Redirect_Shorts_URL)
        {
            if(window.location.pathname.indexOf("/shorts/")!=-1)
            {console.log("[Anti Shorts] redirected");window.location.replace(window.location.href.replace("/shorts/","/watch?v="));}
        }
    }
}

const setMenu = ()=>
{
    menuID.forEach((e)=>{GM_unregisterMenuCommand(e)})
    menuID = [];
    menuID.push(GM_registerMenuCommand(`${Hide_Shorts_Renderer?"Dis":"En"}able "Hide Shorts Renderer"`, ()=>
    {
        Hide_Shorts_Renderer = !Hide_Shorts_Renderer;
        GM_setValue("Hide_Shorts_Renderer", Hide_Shorts_Renderer);
        toggle.renderer();
        setMenu();
    }))
    menuID.push(GM_registerMenuCommand(`${Hide_Shorts_Video?"Dis":"En"}able "Hide Shorts Video"`, ()=>
    {
        Hide_Shorts_Video = !Hide_Shorts_Video;
        GM_setValue("Hide_Shorts_Video", Hide_Shorts_Video);
        toggle.video();
        setMenu();
    }))
    menuID.push(GM_registerMenuCommand(`${Redirect_Shorts_URL?"Dis":"En"}able "Redirect Shorts URL"`, ()=>
    {
        Redirect_Shorts_URL = !Redirect_Shorts_URL;
        GM_setValue("Redirect_Shorts_URL", Redirect_Shorts_URL);
        toggle.redirect();
        setMenu();
    }))
}

console.log("[Anti Shorts] try to call function");

toggle.redirect();
toggle.renderer();
toggle.video();
setMenu();


document.addEventListener("yt-page-data-fetched", onPageUpdate)
document.addEventListener("yt-navigate-finish", onPageUpdate);
waitElementLoad("yt-page-navigation-progress",false,40,250)
    .then((e)=>{new MutationObserver(onPageUpdate).observe(e, {attributes: true});})
window.addEventListener("state-change", onPageUpdate);

console.log("[Anti Shorts] loaded");