Thotflix Native Cast Player

Enables native casting, blocks ads, and prevents outdated cache files via automatic cache-buster parameters.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Thotflix Native Cast Player
// @name:de      Thotflix Native Cast Player
// @name:fr      Thotflix Native Cast Player
// @name:es      Thotflix Native Cast Player
// @name:it      Thotflix Native Cast Player
// @name:pt      Thotflix Native Cast Player
// @name:ru      Thotflix Native Cast Player
// @name:zh      Thotflix Native Cast Player
// @name:ja      Thotflix Native Cast Player
// @description      Enables native casting, blocks ads, and prevents outdated cache files via automatic cache-buster parameters.
// @description:de   Ermöglicht natives Casting, blockiert Werbung und verhindert veraltete Cache-Dateien durch automatische Cache-Buster-Parameter.
// @description:fr   Active la diffusion native, bloque les publicités et évite les fichiers de cache obsolètes grâce à des paramètres de suppression du cache automatiques.
// @description:es   Habilita la transmisión nativa, bloquea anuncios y evita archivos de caché obsoletos mediante parámetros automáticos de eliminación de caché.
// @description:it   Abilita il casting nativo, blocca gli annunci e impedisce file di cache obsoleti tramite parametri automatici di pulizia della cache.
// @description:pt   Ativa a transmissão nativa, bloqueia anúncios e evita arquivos de cache desatualizados por meio de parâmetros automáticos de limpeza de cache.
// @description:ru   Включает встроенную трансляцию, блокирует рекламу и предотвращает использование устаревших файлов кэша с помощью автоматических параметров очистки.
// @description:zh   启用原生投屏、拦截广告,并通过自动VLC/Cast缓存清除参数防止缓存过期的视频。
// @description:ja   ネイティブキャストを有効にし、広告をブロックし、自動キャッシュバスターパラメータによって古いキャッシュファイルを防ぎます。
// @version      0.0.6-stable
// @author       Wack.3gp (https://sleazyfork.org/users/4792)
// @copyright    2026+, Wack.3gp
// @namespace    https://sleazyfork.org/users/4792/
// @license      CC BY-NC-SA-4.0; https://creativecommons.org/licenses/by-nc-sa/4.0/
// @icon         https://thotflix.com/wp-content/uploads/2023/03/cropped-Copy-of-Copy-of-vpnanon-1-32x32.png
//
// @match        https://thotflix.com/*
//
// @run-at       document-end
// @grant        GM_info
// @grant        GM_notification
//
// @supportURL   https://sleazyfork.org/scripts/589475/feedback
// @compatible   Chrome tested with Tampermonkey
// @contributionURL https://www.paypal.com/donate/?hosted_button_id=BYW9D395KJWZ2
// @contributionAmount €1.00
// ==/UserScript==

/* jshint esversion: 11 */

(function() {
    'use strict';

    window.open = function() { return null; };

    document.addEventListener('click', function(e) {
        const isVideo = e.target.closest('video');
        const isButton = e.target.closest('button');
        const isLink = e.target.closest('a');
        const isControl = e.target.closest('.fluid_controls');

        if (!isVideo && !isButton && !isLink && !isControl) {
            e.stopPropagation();
            e.preventDefault();
        }
    }, true);

    document.addEventListener('auxclick', function(e) {
        e.stopPropagation();
        e.preventDefault();
    }, true);

    const removeAdContainer = () => {
        const adContainer = document.querySelector('.ts-im-container');
        if (adContainer) {
            adContainer.remove();
        }
    };

    function applyFix() {
        if (document.getElementById('native-cast-player') || document.getElementById('native-error-box')) return;

        const originalVideo = document.querySelector('video.js-fluid-player, video[id^="cfp_"], video');
        if (!originalVideo) return;

        const fluidWrapper = originalVideo.closest('.fluid_video_wrapper') || originalVideo.parentElement;

        const sourceElement = originalVideo.querySelector('source');
        let videoSrc = originalVideo.currentSrc || originalVideo.src || originalVideo.getAttribute('data-cfp-src') || (sourceElement ? sourceElement.src : null);
        
        if (!videoSrc || videoSrc.includes('fig.to')) {
            const allSources = document.querySelectorAll('source, video');
            for (let s of allSources) {
                let src = s.src || s.getAttribute('data-cfp-src') || (s.querySelector && s.querySelector('source') ? s.querySelector('source').src : null);
                if (src && src.includes('.mp4') && !src.includes('fig.to')) {
                    videoSrc = src;
                    break;
                }
            }
        }

        if (!videoSrc || videoSrc.includes('fig.to')) return;

        if ('caches' in window) {
            caches.keys().then(names => {
                names.forEach(name => { caches.delete(name); });
            });
        }

        if (!videoSrc.includes('nocache=')) {
            const separator = videoSrc.includes('?') ? '&' : '?';
            videoSrc = videoSrc + separator + 'nocache=' + Date.now();
        }

        const poster = originalVideo.getAttribute('poster') || originalVideo.getAttribute('data-cfp-poster');

        const newVideo = document.createElement('video');
        newVideo.id = 'native-cast-player';
        newVideo.src = videoSrc;
        if (poster) newVideo.poster = poster;
        newVideo.controls = true;
        
        newVideo.style.cssText = "width:100% !important; height:auto !important; max-height:80vh !important; display:block !important; background:black !important; border:3px solid #4C8BF5 !important; position:relative !important; z-index:999999 !important;";

        newVideo.setAttribute('x-webkit-airplay', 'allow');
        newVideo.setAttribute('webkit-playsinline', 'true');
        newVideo.setAttribute('playsinline', 'true');
        newVideo.disableRemotePlayback = false;

        newVideo.addEventListener('loadedmetadata', function() {
            if (newVideo.duration > 0 && newVideo.duration < 15) {
                const titleElement = document.querySelector('h1.g1-mega.g1-mega-1st.entry-title[itemprop="headline"]');
                let searchTitle = titleElement ? titleElement.innerText.trim() : document.title;
                
                const query = encodeURIComponent(searchTitle + " -site:thotflix.com");
                const googleUrl = `https://www.google.com/search?q=${query}&tbm=vid&safe=off`;

                const errorBox = document.createElement('div');
                errorBox.id = 'native-error-box';
                errorBox.style.cssText = "width:100% !important; height:300px !important; display:flex !important; flex-direction:column !important; align-items:center !important; justify-content:center !important; background:#111 !important; color:#fff !important; border:3px solid #ff4c4c !important; font-family:sans-serif !important; text-align:center !important; position:relative !important; z-index:999999 !important; padding: 20px !important;";
                errorBox.innerHTML = `
                    <div style="font-size:36px; margin-bottom:10px;">⚠️</div>
                    <div style="font-size:18px; font-weight:bold; margin-bottom:5px;">No Full Video Found, Only Ad</div>
                    <div style="font-size:13px; color:#aaa; margin-bottom:15px;">No playable full-length video stream could be detected; the source is shorter than 15 seconds.</div>
                    <a href="${googleUrl}" target="_blank" style="background:#4C8BF5; color:#fff; padding:10px 20px; border-radius:5px; text-decoration:none; font-weight:bold; font-size:14px;">🔍 Search Video on Google</a>
                `;
                if (newVideo.parentNode) {
                    newVideo.parentNode.replaceChild(errorBox, newVideo);
                }
            }
        });

        if (fluidWrapper && fluidWrapper.parentNode) {
            fluidWrapper.parentNode.replaceChild(newVideo, fluidWrapper);
        } else if (originalVideo.parentNode) {
            originalVideo.parentNode.replaceChild(newVideo, originalVideo);
        }

        try {
            newVideo.load();
        } catch (err) {}
    }

    const checkUpdate = () => {
        const currentVersion = GM_info.script.version;
        const lastSeenVersion = localStorage.getItem('thotflix_last_version');
        const donationUrl = GM_info.script.header.match(/@contributionURL\s+(.+)/)?.[1] || "https://www.paypal.com/donate/?hosted_button_id=BYW9D395KJWZ2";

        if (lastSeenVersion !== currentVersion) {
            GM_notification({
                title: `🚀 ${GM_info.script.name} v${currentVersion}`,
                text: `Version ${currentVersion} is live.`,
                timeout: 10000,
                onclick: () => {
                    window.open(donationUrl, "_blank");
                }
            });
            localStorage.setItem('thotflix_last_version', currentVersion);
        }
    };

    const observer = new MutationObserver(() => {
        removeAdContainer();
        const originalVideo = document.querySelector('video.js-fluid-player, video[id^="cfp_"], video');
        if (originalVideo && !document.getElementById('native-cast-player') && !document.getElementById('native-error-box')) {
            applyFix();
        }
    });

    observer.observe(document.documentElement, {
        childList: true,
        subtree: true
    });

    removeAdContainer();
    setTimeout(applyFix, 500);
    setTimeout(checkUpdate, 2000);

    console.log(`${GM_info.script.name}: Fully Loaded`);
})();