Автоматически скачивает видео в максимальном качестве (1080p если есть). Кнопка над плеером + прогресс-бар.
// ==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);
});
})();