GPT - Player / Downloader

Enables downloads & replaces site player with native HTML5 player + controls

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

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

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         GPT - Player / Downloader
// @namespace    https://github.com/BD9Max/userscripts
// @version      1.2
// @description  Enables downloads & replaces site player with native HTML5 player + controls
// @author       krbd9max
// @license      MIT
// @match        https://*.gayporntube.com/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function () {
    'use strict';

    function restoreNativePlayer() {
        const video = document.getElementById('play-video') || document.querySelector('video');
        if (!video || video.dataset.nativeRestored) return;
        video.dataset.nativeRestored = '1';

        // 1. Try to destroy Fluid Player instance if it exists
        try {
            if (typeof fluidPlayer !== 'undefined' && window.fluidPlayerInstances) {
                // some versions store instances
                Object.values(window.fluidPlayerInstances || {}).forEach(p => {
                    try { p.destroy && p.destroy(); } catch (e) {}
                });
            }
            // also try common global
            if (video.fluidPlayer) {
                try { video.fluidPlayer.destroy(); } catch (e) {}
            }
        } catch (e) {}

        // 2. Clean source URL (remove ?br=xxxx)
        const source = video.querySelector('source');
        if (source && source.src) {
            const clean = source.src.replace(/\/\?br=\d*$/, '').replace(/\?br=\d*$/, '');
            if (clean !== source.src) {
                source.src = clean;
            }
        }

        // 3. Unwrap video from Fluid Player wrapper if present
        const wrapper = video.closest('.fluid_video_wrapper') || video.parentElement;
        if (wrapper && wrapper !== video && wrapper.classList.contains('fluid_video_wrapper')) {
            // Move video out of the wrapper
            wrapper.parentNode.insertBefore(video, wrapper);
            // Remove the entire Fluid Player DOM
            wrapper.remove();
        }

        // 4. Force native controls + download
        video.controls = true;
        video.setAttribute('controls', '');
        video.removeAttribute('controlsList');
        video.controlsList = '';
        video.setAttribute('preload', 'auto');
        video.removeAttribute('width');
        video.removeAttribute('height');

        // 5. Force visibility and proper size
        Object.assign(video.style, {
            display: 'block',
            visibility: 'visible',
            opacity: '1',
            width: '100%',
            height: 'auto',
            maxHeight: '80vh',
            position: 'relative',
            zIndex: '9999',
            background: '#000',
            margin: '0 auto'
        });

        // 6. Make sure parent containers don’t collapse
        let parent = video.parentElement;
        for (let i = 0; i < 5 && parent; i++) {
            parent.style.display = 'block';
            parent.style.visibility = 'visible';
            parent.style.height = 'auto';
            parent.style.minHeight = '300px';
            parent.style.overflow = 'visible';
            parent = parent.parentElement;
        }

        // 7. Hide any leftover Fluid Player elements that might still be around
        document.querySelectorAll(
            '.fluid_controls_container, .fluid_control_bar, .fluid_initial_play_button, ' +
            '.fluid_big_play_button, .fluid_context_menu, [class*="fluid_"]'
        ).forEach(el => {
            if (!el.contains(video)) {
                el.style.display = 'none';
                el.remove();
            }
        });

        // 8. Reload so the clean source takes effect
        video.load();

        console.log('[Native Player] Successfully restored native HTML5 player');
    }

    // Run multiple times because the player is created asynchronously
    function start() {
        restoreNativePlayer();
        setTimeout(restoreNativePlayer, 800);
        setTimeout(restoreNativePlayer, 2000);
        setTimeout(restoreNativePlayer, 4000);
    }

    start();

    // Keep watching for late injection
    const obs = new MutationObserver(() => {
        if (document.getElementById('play-video') && !document.getElementById('play-video').dataset.nativeRestored) {
            restoreNativePlayer();
        }
    });
    obs.observe(document.documentElement, { childList: true, subtree: true });
})();