Anti Rickroll (Universal)

A lightweight, robust userscript designed to protect users from known Rickrolls on YouTube. Optimized specifically for mobile environments (iOS Userscripts) and modern browser security policies.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name            Anti Rickroll (Universal)
// @version         1.7.5
// @description     A lightweight, robust userscript designed to protect users from known Rickrolls on YouTube. Optimized specifically for mobile environments (iOS Userscripts) and modern browser security policies.
// @author          JMcrafter26
// @match           https://*.youtube.com/watch?*
// @match           https://youtube.com/watch?*
// @match           https://*.youtube.com/embed/*
// @match           https://youtube.com/embed/*
// @match           https://*.youtu.be/*
// @match           https://youtu.be/*
// @run-at          document-start
// @grant           none
// @namespace http://github.com/sepehr
// ==/UserScript==

(function() {
    'use strict';

    const config = {
        storageKey: 'antiRickroll',
        bypassPrefix: 'rr_bypass_',
        icon: "https://files.catbox.moe/ylj5xb.png",
        apiUrl: "https://api.jm26.net/rickroll-db/?type=get&api=userscript",
        statsUrl: "https://api.jm26.net/extensions/anti-rickroll/stats.php",
        updateInterval: 86400000,
        fallbackDatabase: ["dQw4w9WgXcQ", "oHg5SJYRHA0", "cvh0nX08nRw", "xfr64zoBTAQ", "iik25wqIuFo"]
    };

    function generateTraceId() {
        const hex = () => Math.floor((1 + Math.random()) * 0x100000000).toString(16).substring(1);
        return `${hex()}${hex()}`;
    }

    // Using fetch with keepalive: true to survive window.stop()
    function reportStats(id, type, trace) {
        try {
            const formData = new URLSearchParams();
            formData.append('id', id);
            formData.append('type', type);
            formData.append('trace', trace);
            formData.append('version', '1.7.5');

            fetch(config.statsUrl, {
                method: 'POST',
                body: formData,
                keepalive: true, // CRITICAL: Ensures request survives page unloading
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                }
            }).catch(e => console.error("Stats Error:", e));
        } catch (e) {
            console.error("Stats Build Error:", e);
        }
    }

    function getFullDatabase() {
        let localIds = [];
        try {
            const data = localStorage.getItem(config.storageKey);
            if (data) {
                const parsed = JSON.parse(data);
                localIds = Array.isArray(parsed) ? parsed : (parsed.database || []);
            }
        } catch (e) {}
        return [...new Set([...config.fallbackDatabase, ...localIds])];
    }

    async function updateDatabase() {
        const now = Date.now();
        let store = {};
        try { store = JSON.parse(localStorage.getItem(config.storageKey) || "{}"); } catch(e) {}
        if (store.lastUpdate && (now - store.lastUpdate < config.updateInterval)) return;

        try {
            const response = await fetch(config.apiUrl);
            const json = await response.json();
            if (json && json.ids) {
                const existing = Array.isArray(store) ? store : (store.database || []);
                localStorage.setItem(config.storageKey, JSON.stringify({
                    database: [...new Set([...existing, ...json.ids])],
                    lastUpdate: now
                }));
            }
        } catch (e) {}
    }

    function checkVideo() {
        let videoId = null;
        let contentType = "video";
        const url = window.location.href;

        if (url.includes('v=')) {
            videoId = new URLSearchParams(window.location.search).get('v');
        } else if (url.includes('/embed/')) {
            videoId = url.split('/embed/')[1].split(/[?#]/)[0];
            contentType = "embed";
        } else if (url.includes('youtu.be/')) {
            videoId = url.split('youtu.be/')[1].split(/[?#]/)[0];
        }

        if (!videoId || sessionStorage.getItem(config.bypassPrefix + videoId) === 'true') {
            const blocker = document.getElementById('antirickroll-wrapper');
            if (blocker) {
                blocker.remove();
                document.documentElement.style.overflow = '';
            }
            return;
        }

        if (getFullDatabase().includes(videoId)) {
            const traceId = generateTraceId();
            
            // 1. Send stats FIRST (asynchronously)
            reportStats(videoId, contentType, traceId);

            // 2. Mute audio
            const video = document.querySelector('video');
            if (video) { video.muted = true; video.pause(); }

            // 3. Render block
            document.title = "⚠️ Rickroll Blocked!";
            renderBlocker(videoId, contentType, traceId);
        }
    }

    function renderBlocker(id, type, trace) {
        if (document.getElementById('antirickroll-wrapper')) return;
        
        // 4. Stop window execution AFTER firing the request
        window.stop();

        const blocker = document.createElement('div');
        blocker.id = 'antirickroll-wrapper';
        blocker.setAttribute('style', 'position:fixed !important; top:0 !important; left:0 !important; width:100vw !important; height:100vh !important; z-index:2147483647 !important; background:#fff; overflow:auto !important; display:block !important;');

        blocker.innerHTML = `
          <style>
              #antirickroll-wrapper { --google-gray-700: rgb(95, 99, 104); color: var(--google-gray-700); word-wrap: break-word; font-family: 'Segoe UI', Tahoma, sans-serif; font-size: 16px; line-height: 1.4; }
              .nav-wrapper .secondary-button{ background: #fff; border: 1px solid rgb(154, 160, 166); color: var(--google-gray-700); padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px;}
              .ssl button{ border: 0; border-radius: 4px; color: #fff; cursor: pointer; float: right; padding: 6px 12px; font-family: 'Segoe UI', Tahoma, sans-serif;}
              .ssl .primary-button { background: rgb(26, 115, 232); }
              .details-code{ color: var(--google-gray-700); font-size: .75em; margin-top: 15px; border-top: 1px solid #eee; padding-top: 10px; line-height: 1.8;}
              h1{ color: rgb(32, 33, 36); font-size: 1.4em; font-weight: normal; margin-bottom: 12px; margin-top: 0;}
              .interstitial-wrapper{ box-sizing: border-box; margin: 10vh auto 0; max-width: 90% !important; width: 500px; padding: 20px;}
              .nav-wrapper{ margin-top: 25px; display: flex; justify-content: flex-end; gap: 8px; }
              .icon{ height: 60px; margin: 0 0 20px; width: 60px; background-image: url("${config.icon}"); background-size: contain; background-repeat: no-repeat;}
              @media (prefers-color-scheme: dark){
                #antirickroll-wrapper { background: #202124 !important; color: rgb(155, 160, 165);}
                .nav-wrapper .secondary-button{ background: #202124; border: 1px solid rgb(154, 160, 166); color: rgb(138, 180, 248);}
                .ssl .primary-button { background: rgb(129, 162, 208); color: #202124; }
                .details-code { border-top-color: #3c4043; color: #9aa0a6; }
                h1 { color: rgb(155, 160, 165); }
              }
          </style>
          <div class="ssl">
              <div class="interstitial-wrapper">
                  <div class="icon"></div>
                  <h1>You are about to get Rickrolled!</h1>
                  <p>This ${type} is a known Rickroll and has been intercepted.</p>
                  <div class="nav-wrapper">
                      <button id="rr-continue" class="secondary-button">Continue</button>
                      <button class="primary-button" onclick="history.length>1?history.back():window.close();">Back to safety</button>
                  </div>
                  <div class="details-code">
                      Trace ID: <b>${trace}</b><br>
                      Video ID: <b>${id}</b><br>
                      Status: <b>BLOCKED_${type.toUpperCase()}</b>
                  </div>
              </div>
          </div>`;

        (document.documentElement || document.body).appendChild(blocker);
        document.documentElement.style.overflow = 'hidden';

        document.getElementById('rr-continue').onclick = function() {
            sessionStorage.setItem(config.bypassPrefix + id, 'true');
            location.reload();
        };
    }

    window.addEventListener('yt-navigate-finish', checkVideo);
    let lastUrl = location.href;
    new MutationObserver(() => {
        if (location.href !== lastUrl) {
            lastUrl = location.href;
            checkVideo();
        }
    }).observe(document, {subtree: true, childList: true});

    checkVideo();
    updateDatabase();
})();