XVideos Auto Downloader Max Quality + Progress Bar

Автоматически скачивает видео в максимальном качестве (1080p если есть). Кнопка над плеером + прогресс-бар.

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

You will need to install an extension such as Tampermonkey 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         XVideos Auto Downloader Max Quality + Progress Bar
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  Автоматически скачивает видео в максимальном качестве (1080p если есть). Кнопка над плеером + прогресс-бар.
// @author       DiAi
// @match        https://www.xvideos.com/video*
// @match        https://*.xv-ru.com/video*
// @grant        none
// @run-at       document-end
// @license MIT
// ==/UserScript==

(async function() {
    'use strict';

    let progressContainer = null;
    let progressBar = null;
    let progressText = null;

    function createProgressBar() {
        progressContainer = document.createElement('div');
        progressContainer.style.cssText = 'margin: 10px 0; padding: 12px; background: #2c3e50; color: white; border-radius: 6px; font-weight: bold; text-align: center;';

        progressBar = document.createElement('div');
        progressBar.style.cssText = 'height: 24px; background: #34495e; border-radius: 4px; overflow: hidden; margin: 10px 0;';

        const fill = document.createElement('div');
        fill.id = 'progress-fill';
        fill.style.cssText = 'height: 100%; width: 0%; background: #27ae60; transition: width 0.3s;';
        progressBar.appendChild(fill);

        progressText = document.createElement('div');
        progressText.textContent = 'Подготовка... 0% (0/0 сегментов)';

        progressContainer.appendChild(progressBar);
        progressContainer.appendChild(progressText);
    }

    function updateProgress(current, total, percent) {
        if (!progressBar) return;
        document.getElementById('progress-fill').style.width = percent + '%';
        progressText.textContent = `Загрузка... ${percent}% (${current}/${total} сегментов)`;
    }

    function finishProgress() {
        progressText.textContent = 'Готово! Скачивание файла...';
        progressBar.style.background = '#27ae60';
        document.getElementById('progress-fill').style.width = '100%';
        setTimeout(() => {
            if (progressContainer) progressContainer.remove();
        }, 3000);
    }

    function addDownloadButton() {
        // Цель — вставить кнопку НАД видео (выше плеера)
        let target = document.querySelector('#html5video') ||
                     document.querySelector('#video-player-bg') ||
                     document.querySelector('#video-tabs') ||
                     document.body;

        const wrapper = document.createElement('div');
        wrapper.style.cssText = 'margin: 10px 0; text-align: center;';

        const button = document.createElement('button');
        button.textContent = 'Скачать MAX качество (1080p+)';
        button.style.cssText = 'padding: 14px 30px; background: #c0392b; color: white; border: none; border-radius: 8px; font-weight: bold; cursor: pointer; font-size: 18px; box-shadow: 0 4px 8px rgba(0,0,0,0.3);';

        button.onclick = async () => {
            button.disabled = true;
            button.textContent = 'Скачивание...';
            wrapper.appendChild(document.createElement('br'));
            createProgressBar();
            wrapper.appendChild(progressContainer);

            try {
                let hlsUrl = null;
                document.querySelectorAll('script').forEach(script => {
                    const text = script.textContent || '';
                    const match = text.match(/setVideoHLS\(['"]([^'"]+\.m3u8)['"]\)/i);
                    if (match) hlsUrl = match[1];
                });

                if (!hlsUrl) throw new Error('HLS URL не найден');

                const masterContent = await fetch(hlsUrl).then(r => r.text());
                const baseUrl = hlsUrl.substring(0, hlsUrl.lastIndexOf('/') + 1);

                const lines = masterContent.split('\n');
                let highestSubUrl = null;
                let maxBw = 0;

                for (let i = 0; i < lines.length; i++) {
                    if (lines[i].includes('BANDWIDTH=')) {
                        const bw = parseInt(lines[i].match(/BANDWIDTH=(\d+)/)?.[1] || 0);
                        if (bw > maxBw && lines[i+1] && !lines[i+1].startsWith('#')) {
                            maxBw = bw;
                            highestSubUrl = new URL(lines[i+1].trim(), baseUrl).href;
                        }
                    }
                }

                if (!highestSubUrl) throw new Error('Максимальное качество не найдено');

                const subContent = await fetch(highestSubUrl).then(r => r.text());
                const subBase = highestSubUrl.substring(0, highestSubUrl.lastIndexOf('/') + 1);

                const segments = [];
                subContent.split('\n').forEach(line => {
                    if (line.trim() && !line.startsWith('#') && line.endsWith('.ts')) {
                        segments.push(new URL(line.trim(), subBase).href);
                    }
                });

                if (segments.length === 0) throw new Error('Сегменты не найдены');

                updateProgress(0, segments.length, 0);

                const blobs = [];
                for (let i = 0; i < segments.length; i++) {
                    const blob = await fetch(segments[i]).then(r => r.blob());
                    blobs.push(blob);
                    const percent = Math.round(((i + 1) / segments.length) * 100);
                    updateProgress(i + 1, segments.length, percent);
                }

                const fullBlob = new Blob(blobs, { type: 'video/mp4' });
                const url = URL.createObjectURL(fullBlob);

                const a = document.createElement('a');
                a.href = url;
                a.download = document.title.replace(/ - XVIDEOS.COM$/, '') + '_max.mp4';
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
                URL.revokeObjectURL(url);

                finishProgress();

            } catch (err) {
                console.error('[XV Downloader] Ошибка:', err);
                if (progressText) progressText.textContent = 'Ошибка: ' + err.message;
                button.disabled = false;
                button.textContent = 'Повторить';
            }
        };

        wrapper.appendChild(button);
        if (target && target.parentNode) {
            target.parentNode.insertBefore(wrapper, target);
        } else {
            document.body.prepend(wrapper);
        }
    }

    // Запуск после загрузки страницы
    window.addEventListener('load', () => {
        setTimeout(addDownloadButton, 2000);
    });

})();