GPT - Player / Downloader

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

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

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

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.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

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