GPT - Player / Downloader

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

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==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 });
})();