GPT - Player / Downloader

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

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

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