UNPKG

@khabzox/ytb-shorts-blocker

Version:

Block YouTube Shorts and keep your feed clean

40 lines (39 loc) 10.9 kB
var a={SHORTS_CONTAINER:["ytd-shorts","ytd-reel-video-renderer","[is-shorts]","ytd-shorts-player"].join(", "),HOME_SHORTS:["ytd-rich-shelf-renderer[is-shorts]","ytd-reel-shelf-renderer","ytd-video-renderer[is-shorts]","ytd-grid-video-renderer[is-shorts]","ytd-rich-grid-renderer ytd-video-renderer[is-shorts]",'div[data-content-type="shorts"]',"ytd-reel-video-renderer","ytd-reel-item-renderer",'a[href*="/shorts/"]','[aria-label*="Shorts"]','[title*="Shorts"]'].join(", "),SEARCH_SHORTS:["ytd-video-renderer[is-shorts]","ytd-reel-video-renderer",'a[href*="/shorts/"]','[data-content-type="shorts"]',".ytd-search ytd-video-renderer[is-shorts]"].join(", "),SUBSCRIPTION_SHORTS:["ytd-video-renderer[is-shorts]","ytd-grid-video-renderer[is-shorts]","ytd-reel-video-renderer",'a[href*="/shorts/"]','.ytd-browse[page-subtype="subscriptions"] ytd-video-renderer[is-shorts]'].join(", "),TRENDING_SHORTS:["ytd-video-renderer[is-shorts]","ytd-expanded-shelf-contents-renderer ytd-video-renderer[is-shorts]",'a[href*="/shorts/"]'].join(", "),CHANNEL_SHORTS:["ytd-grid-video-renderer[is-shorts]","ytd-reel-video-renderer",'a[href*="/shorts/"]','[data-content-type="shorts"]'].join(", "),RELATED_SHORTS:["ytd-compact-video-renderer[is-shorts]","ytd-video-renderer[is-shorts]",'#related a[href*="/shorts/"]'].join(", "),MOBILE_SHORTS:[".shorts-video-cell",".reel-video-in-sequence",'a[href*="/shorts/"]'].join(", "),FALLBACK_SHORTS:['[class*="shorts"]','[class*="reel"]',"[data-shorts]","[data-reel]",'[aria-label*="short"]','[aria-label*="Short"]','a[href*="/shorts/"]'].join(", ")};var H={HIDE:"hide",REMOVE:"remove",REDIRECT:"redirect"},Z={enabled:!0,blockingMode:H.HIDE,showNotifications:!0,blockInSearch:!0,blockInHome:!0,blockInSubscriptions:!0,blockInTrending:!1,allowWhitelistedChannels:!0,whitelistedChannels:[],customCss:"",debugMode:!1,version:"1.0.4"},z={SETTINGS:"ytb_settings",STATS:"ytb_stats",CHANNEL_INFO:"ytb_channel_info",TEMP_DATA:"ytb_temp_data"},$={NAME:"YouTube Shorts Blocker",VERSION:"1.0.4",AUTHOR:"khabzox",GITHUB_URL:"https://github.com/khabzox/ytb-shorts-blocker",CHROME_STORE_URL:"https://chrome.google.com/webstore",DEBOUNCE_DELAY:100,NAVIGATION_CHECK_INTERVAL:1000,STATS_UPDATE_INTERVAL:5000,NOTIFICATION_DURATION:3000,ANIMATION_DURATION:300,MAX_DAILY_STATS_ENTRIES:90,MAX_WEEKLY_STATS_ENTRIES:12,MAX_MONTHLY_STATS_ENTRIES:12,MAX_WHITELIST_ENTRIES:1000,DEBUG_LOG_PREFIX:"[YTB Shorts Blocker]",DEBUG_STYLES:{BLOCKED_BORDER:"3px solid red",BLOCKED_OPACITY:"0.5",DEBUG_BACKGROUND:"rgba(255, 0, 0, 0.1)"}};var X={BLOCKED:"ytb-blocked",HIDDEN:"ytb-hidden",REMOVED:"ytb-removed",DEBUG_HIGHLIGHT:"ytb-debug-highlight",DEBUG_OVERLAY:"ytb-debug-overlay",NOTIFICATION:"ytb-notification",POPUP_CONTAINER:"ytb-popup",STATS_DISPLAY:"ytb-stats",DISABLED:"ytb-disabled",LOADING:"ytb-loading",ERROR:"ytb-error"},e={HIDE_WITH_FADE:` .${X.BLOCKED} { opacity: 0 !important; transition: opacity 0.3s ease-out !important; pointer-events: none !important; } `,BLUR_EFFECT:` .${X.BLOCKED} { filter: blur(10px) !important; opacity: 0.3 !important; transition: all 0.3s ease-out !important; } `,GRAYSCALE_EFFECT:` .${X.BLOCKED} { filter: grayscale(100%) !important; opacity: 0.5 !important; position: relative !important; } .${X.BLOCKED}::after { content: "\uD83D\uDEAB BLOCKED SHORTS" !important; position: absolute !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; background: rgba(0, 0, 0, 0.8) !important; color: white !important; padding: 8px 12px !important; border-radius: 4px !important; font-size: 12px !important; font-weight: bold !important; z-index: 1000 !important; } `,SLIDE_OUT_EFFECT:` .${X.BLOCKED} { transform: translateX(-100%) !important; transition: transform 0.5s ease-in-out !important; opacity: 0 !important; } `};async function P(){try{let p=await chrome.storage.sync.get(z.SETTINGS);return{...Z,...p[z.SETTINGS]}}catch(p){return console.error("Failed to get settings:",p),Z}}async function q(){try{let p=await chrome.storage.local.get(z.STATS);return{...{totalBlocked:0,sessionBlocked:0,lastResetDate:new Date().toISOString(),dailyStats:{},weeklyStats:{},monthlyStats:{}},...p[z.STATS]}}catch(p){return console.error("Failed to get stats:",p),{totalBlocked:0,sessionBlocked:0,lastResetDate:new Date().toISOString(),dailyStats:{},weeklyStats:{},monthlyStats:{}}}}async function u(p=1){try{let o=await q(),r=new Date,i=r.toISOString().split("T")[0],b=t(r),d=`${r.getFullYear()}-${(r.getMonth()+1).toString().padStart(2,"0")}`,f={...o,totalBlocked:o.totalBlocked+p,sessionBlocked:o.sessionBlocked+p,dailyStats:{...o.dailyStats,[i]:(o.dailyStats[i]||0)+p},weeklyStats:{...o.weeklyStats,[b]:(o.weeklyStats[b]||0)+p},monthlyStats:{...o.monthlyStats,[d]:(o.monthlyStats[d]||0)+p}};await chrome.storage.local.set({[z.STATS]:f}),await B()}catch(o){console.error("Failed to update stats:",o)}}async function B(){try{let p=await q(),o=new Date,i=new Date(o.getTime()-7776000000).toISOString().split("T")[0],b={};Object.entries(p.dailyStats).forEach(([y,w])=>{if(y>=i)b[y]=w});let d=t(new Date(o.getTime()-7257600000)),f={};Object.entries(p.weeklyStats).forEach(([y,w])=>{if(y>=d)f[y]=w});let J=new Date(o.getFullYear(),o.getMonth()-12),N=`${J.getFullYear()}-${(J.getMonth()+1).toString().padStart(2,"0")}`,k={};Object.entries(p.monthlyStats).forEach(([y,w])=>{if(y>=N)k[y]=w});let Y={...p,dailyStats:b,weeklyStats:f,monthlyStats:k};await chrome.storage.local.set({[z.STATS]:Y})}catch(p){console.error("Failed to cleanup old stats:",p)}}function t(p){let o=p.getFullYear(),r=new Date(o,0,1),i=Math.ceil(((p.getTime()-r.getTime())/86400000+r.getDay()+1)/7);return`${o}-${i.toString().padStart(2,"0")}`}function W(p){return p.enabled===!0}function F(p){return p.debugMode||localStorage.getItem("ytb-debug")==="true"}function m(p,o,...r){if(o)console.log(`[YTB Shorts Blocker] ${p}`,...r)}function U(){let p=location.pathname;if(p==="/"||p==="/feed/subscriptions")return"home";if(p==="/results")return"search";if(p==="/feed/subscriptions")return"subscriptions";if(p==="/feed/trending")return"trending";return"other"}function v(p){switch(U()){case"home":return p.blockInHome;case"search":return p.blockInSearch;case"subscriptions":return p.blockInSubscriptions;case"trending":return p.blockInTrending;default:return!0}}function K(p){let o=[],r=U(),i;switch(r){case"home":i=a.HOME_SHORTS;break;case"search":i=a.SEARCH_SHORTS;break;case"subscriptions":i=a.SUBSCRIPTION_SHORTS;break;case"trending":i=a.TRENDING_SHORTS;break;default:i=a.SHORTS_CONTAINER}let b=`${i}, ${a.SHORTS_CONTAINER}`;return o.push(...Array.from(document.querySelectorAll(b))),M(o)}function M(p){let o=new Set;return p.filter((r)=>{let i=r.id||r.outerHTML;if(o.has(i))return!1;return o.add(i),!0})}function G(p){let o=p.querySelector('a[href*="/shorts/"]');if(o){let r=o.href.match(/\/shorts\/([a-zA-Z0-9_-]+)/);return r?r[1]:null}return null}function D(p){let o=p.querySelector('a[href*="/channel/"], a[href*="/@"]');if(o){let r=o.href,i=o.textContent?.trim(),b=r.match(/\/channel\/([a-zA-Z0-9_-]+)/),d=b?b[1]:null,f=r.match(/\/@([a-zA-Z0-9_.-]+)/),J=f?f[1]:null;return{name:i||void 0,id:d||void 0,handle:J||void 0}}return null}function L(p,o){if(!o.allowWhitelistedChannels)return!1;let r=D(p);if(!r?.id)return!1;return o.whitelistedChannels?.includes(r.id)||!1}function j(p,o){let r=p;if(o)r.classList.add("ytb-debug-mode"),r.title="YTB: Blocked Short (Debug Mode)";else r.classList.add("ytb-animate-fade")}function A(p,o){if(o)j(p,!0);else p.classList.add("ytb-animate-slide"),setTimeout(()=>p.remove(),500)}function R(p){p.classList.add("ytb-mode-redirect");let r=p.querySelector('a[href*="/shorts/"]');if(r){let i=G(p);if(i)r.href=`https://www.youtube.com/watch?v=${i}`}}function C(p,o,r){if(p.hasAttribute("data-ytb-blocked"))return!1;if(L(p,o))return m("Skipping whitelisted channel",r),!1;switch(o.blockingMode){case H.HIDE:j(p,r);break;case H.REMOVE:A(p,r);break;case H.REDIRECT:R(p);break;default:j(p,r)}return p.setAttribute("data-ytb-blocked","true"),!0}async function I(p=1){try{await u(p)}catch(o){console.error("Failed to update statistics:",o)}}function _(p){if(!p.showNotifications)return;let o=document.createElement("div");o.className="ytb-notification success",o.textContent="\uD83D\uDEAB Short blocked successfully!",document.body.appendChild(o),setTimeout(()=>{if(o.parentNode)o.parentNode.removeChild(o)},3000)}function T(p){return new MutationObserver((o)=>{let r=!1;if(o.forEach((i)=>{if(i.type==="childList"&&i.addedNodes.length>0)r=!0}),r)setTimeout(p,$.DEBOUNCE_DELAY)})}function O(p,o){return p!==o}async function Q(p){let{settings:o,isDebugMode:r}=p;if(!W(o))return m("Extension is disabled",r),0;if(!v(o))return m("Blocking disabled for current page",r),0;let i=K(o);m(`Found ${i.length} shorts on page`,r);let b=0;for(let d of i)if(C(d,o,r))b++,p.blockedCount++,await I(1),_(o),m(`Blocked short #${p.blockedCount}`,r,d);return b}async function c(){let p=await P(),o=F(p);return m("Initializing YouTube Shorts Blocker...",o),{settings:p,blockedCount:0,isDebugMode:o,observer:null,lastUrl:location.href}}function g(p,o){return{...p,settings:{...p.settings,...o},isDebugMode:F({...p.settings,...o})}}function n(p){if(p.observer)p.observer.disconnect();if(p.observer=T(()=>{Q(p)}),document.body)p.observer.observe(document.body,{childList:!0,subtree:!0}),m("Mutation observer set up",p.isDebugMode);else{let o=()=>{if(document.body)p.observer.observe(document.body,{childList:!0,subtree:!0}),m("Mutation observer set up (delayed)",p.isDebugMode);else setTimeout(o,100)};o()}}function E(p){let o=()=>{let r=location.href;if(O(r,p.lastUrl))p.lastUrl=r,m("Navigation detected, re-scanning for shorts",p.isDebugMode),setTimeout(()=>Q(p),500)};setInterval(o,$.NAVIGATION_CHECK_INTERVAL),window.addEventListener("popstate",o)}function h(){document.querySelectorAll("[data-ytb-blocked]").forEach((o)=>{o.removeAttribute("data-ytb-blocked");let r=o;r.style.display="",r.style.border="",r.style.opacity="",r.title=""})}function S(p){if(p.observer)p.observer.disconnect(),p.observer=null;h(),m("Extension destroyed and elements restored",p.isDebugMode)}async function V(){let p=await c();if(!W(p.settings))return m("Extension is disabled",p.isDebugMode),p;return await Q(p),n(p),E(p),m("Initialization complete",p.isDebugMode),p}var x=null;function l(){if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",()=>{V().then((p)=>{x=p})});else V().then((p)=>{x=p})}l();chrome.runtime.onMessage.addListener((p,o,r)=>{if(!x){r({success:!1,error:"Blocker not initialized"});return}if(p.action==="updateSettings")x=g(x,p.settings),Q(x),r({success:!0});else if(p.action==="getStatus")r({blocked:x.blockedCount,enabled:x.settings.enabled})});window.addEventListener("beforeunload",()=>{if(x)S(x)});