Tiktok revanced

Auto AD Skip tiktok + 2x speed button

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Tiktok revanced
// @name:pt-br   Pulador de anúncios do tiktok
// @namespace    http://tampermonkey.net/
// @version      28-12-2025
// @description:pt-br   Scrolla os anúncios do tiktok automaticamente e adiciona um botão que coloca velocidade 2x
// @description         Auto AD Skip tiktok + 2x speed button
// @author       Potly
// @match        https://www.tiktok.com/*
// @icon         https://www.tiktok.com/favicon.ico
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    const TOGGLE_KEY = "autoAdSkip_enabled";
    let enabled = localStorage.getItem(TOGGLE_KEY);
    if (enabled === null) enabled = "true";
    enabled = enabled === "true";

    function saveState() {
        localStorage.setItem(TOGGLE_KEY, enabled ? "true" : "false");
    }

    // ---------------------------
    // BOTÃO TOGGLE
    // ---------------------------
    function createToggle() {
        const wrapper = document.createElement("div");
        wrapper.style.position = "fixed";
        wrapper.style.top = "30px";
        wrapper.style.left = "170px";
        wrapper.style.zIndex = "999999";
        wrapper.style.display = "flex";
        wrapper.style.alignItems = "center";
        wrapper.style.gap = "10px";
        wrapper.style.userSelect = "none";

        // texto
        const label = document.createElement("span");
        label.textContent = "Auto AD Skip";
        label.style.fontSize = "18px";
        label.style.fontFamily = "Arial, sans-serif";
        label.style.fontWeight = "600";
        label.style.color = "#ffffff";
        wrapper.appendChild(label);

        // container do toggle
        const toggle = document.createElement("div");
        toggle.style.width = "50px";
        toggle.style.height = "26px";
        toggle.style.borderRadius = "50px";
        toggle.style.background = enabled ? "#4caf50" : "#ccc";
        toggle.style.position = "relative";
        toggle.style.cursor = "pointer";
        toggle.style.transition = "0.25s";
        toggle.style.boxShadow = "0 0 5px rgba(0,0,0,0.2)";

        // bolinha
        const knob = document.createElement("div");
        knob.style.width = "22px";
        knob.style.height = "22px";
        knob.style.background = "#fff";
        knob.style.borderRadius = "50%";
        knob.style.position = "absolute";
        knob.style.top = "2px";
        knob.style.left = enabled ? "26px" : "2px";
        knob.style.transition = "0.25s";
        knob.style.boxShadow = "0 2px 5px rgba(0,0,0,0.3)";

        toggle.appendChild(knob);
        wrapper.appendChild(toggle);
        document.body.appendChild(wrapper);

        toggle.onclick = () => {
            enabled = !enabled;
            saveState();

            // animação on e off
            toggle.style.background = enabled ? "#4caf50" : "#ccc";
            knob.style.left = enabled ? "26px" : "2px";
        };
    }

    if (document.readyState !== "loading") createToggle();
    else document.addEventListener("DOMContentLoaded", createToggle);

    // ---------------------------
    // Detecta se é ad
    // ---------------------------

    const selector = "div.css-3fgk4e-7937d88b--DivItemTagsContainer.e1d0egbk0 > div";
    const clickButtonSelector = "#main-content-homepage_hot > aside > div > div:nth-child(2) > button";

    const adKeywords = [
        "anúncio", "anuncio", "patrocinado", "patrocinada",
        "publicidade", "publi", "sponsored", "ad", "promo"
    ];

    const processed = new WeakSet();

    function isAd(el) {
        const text = (el.innerText || "").toLowerCase();
        return adKeywords.some(k => text.includes(k));
    }
 // acho q ter um delayzinho entre cada clique é bom, pq um pc ruim pode bugar

    let lastClick = 0;
function clickButton() {
    if (!enabled) return;
    const now = Date.now();
    if (now - lastClick < 500) return; // evita spam
    lastClick = now;
    const btn = document.querySelector(clickButtonSelector);
    if (btn) btn.click();

    }

    function handleNewAds() {
        document.querySelectorAll(selector).forEach(el => {
            if (processed.has(el)) return;
            if (!isAd(el)) return;

            processed.add(el);

            const io = new IntersectionObserver(entries => {
    entries.forEach(entry => {
        if (entry.isIntersecting && enabled) {
            clickButton();
            io.disconnect(); // para evitar vazamento de memoria
        }
    });
}, { threshold: 0.01 });


            io.observe(el);
        });
    }


//-----------------------------------------------------------

const SPEED_WHILE_HOLD = 2.0;
    const DEFAULT_SPEED = 1.0;

        const targetContainerSelector =
"section[class*='SectionActionBarContainer']";


//pega o vídeo que está assistindo no momento
function getCurrentVideo() {
        const videos = document.querySelectorAll("video");
        for (const v of videos) {
            const r = v.getBoundingClientRect();
            if (r.width > 0 && r.height > 0) return v;
        }
return null;
    }

function setSpeed(speed) {
        const video = getCurrentVideo();
        if (video) video.playbackRate = speed;
    }

        function createHoldButton(container) {
    if (container.querySelector("#hold-speed-wrapper")) return;

    const wrapper = document.createElement("div");
    wrapper.id = "hold-speed-wrapper";
    wrapper.style.position = "absolute";
    wrapper.style.left = "50%";
    wrapper.style.transform = "translateX(-50%)";
    wrapper.style.bottom = "100%";
    wrapper.style.marginBottom = "12px";
    wrapper.style.display = "flex";
    wrapper.style.flexDirection = "column";
    wrapper.style.alignItems = "center";
    wrapper.style.zIndex = "999999";


// ícone fast-forward (CSS)
const style = document.createElement("style");
style.textContent = `
.ff-icon {
    display: flex;
    gap: -3px;
    margin-left: 3px;
}

.ff-icon span {
    width: 0;
    height: 0;
    border-top: 8px solid transparent;
    border-bottom: 8px solid transparent;
    border-left: 12px solid white;
}
`;
document.head.appendChild(style);


    const btn = document.createElement("div");

    btn.innerHTML = `
  <div class="ff-icon">
    <span></span>
    <span></span>
  </div>
`;

    btn.style.display = "flex";
btn.style.justifyContent = "center";
btn.style.alignItems = "center";

btn.style.width = "48px";
btn.style.height = "48px";
btn.style.borderRadius = "50%";

btn.style.backgroundColor = "rgba(255, 255, 255, 0.12)";
btn.style.cursor = "pointer";

btn.style.transition = "200ms ease-in-out";
btn.style.marginTop = "8px";
btn.style.marginBottom = "6px";

btn.style.fontSize = "0px"; // igual TikTok
btn.style.userSelect = "none";


    const label = document.createElement("div");
    label.innerHTML = "<strong>2.0X</strong>";
    label.style.fontSize = "12px";
    label.style.marginTop = "4px";
    label.style.color = "rgba(255, 255, 255, 0.75)";
    label.style.opacity = "0.9";

// --- HOLD EVENTS ---
btn.addEventListener("mousedown", () => setSpeed(SPEED_WHILE_HOLD));
btn.addEventListener("mouseup", () => setSpeed(DEFAULT_SPEED));
btn.addEventListener("mouseleave", () => setSpeed(DEFAULT_SPEED));
btn.addEventListener("touchstart", () => setSpeed(SPEED_WHILE_HOLD));
btn.addEventListener("touchend", () => setSpeed(DEFAULT_SPEED));
btn.addEventListener("touchcancel", () => setSpeed(DEFAULT_SPEED));

// monta corretamente
wrapper.appendChild(btn);
wrapper.appendChild(label);

container.style.position = "relative";
container.prepend(wrapper);}


    function tryInjectButton() {
    document
        .querySelectorAll("section[class*='SectionActionBarContainer']")
        .forEach(container => {
            createHoldButton(container);
        });
}

//espera aparecer os botão do lado, pq o vídeo carrega antes doos botão e isso pode bugar a extensão se n esperar blablabla
function waitForFirstActionBar() {
    return new Promise(resolve => {
        const check = () => {
            const bar = document.querySelector(
                "section[class*='SectionActionBarContainer']"
            );
            if (bar) {
                resolve();
            } else {
                requestAnimationFrame(check);
            }
        };
        check();
    });
}

    // Observer pra feed infinito
   const mo = new MutationObserver(() => {
    tryInjectButton();
    handleNewAds();
});

mo.observe(document.body, {
    childList: true,
    subtree: true
});

// inicial
waitForFirstActionBar().then(() => {
    tryInjectButton();
    handleNewAds();
});

//evita vazamento de memoria
const io = new IntersectionObserver(entries => {
    entries.forEach(entry => {
        if (entry.isIntersecting && enabled) {
            clickButton();
            io.disconnect(); // <- IMPORTANTE
        }
    });
}, { threshold: 0.01 });


})();