// ==UserScript==
// @name SPL (SimplePatreonLoader)
// @namespace https://github.com/5f32797a
// @version 1.4
// @description Simple Vimeo video loader
// @match https://vimeo.com/*
// @grant GM_xmlhttpRequest
// @source https://github.com/5f32797a/VimeoSPL
// @supportURL https://github.com/5f32797a/VimeoSPL/issues
// ==/UserScript==
(function() {
'use strict';
/**
* Extracts Vimeo video ID from URL using optimized regex pattern
* Format: Matches /123456789 or 123456789 from any Vimeo URL variant
* @param {string} url - The Vimeo URL to parse
* @returns {string} The extracted video ID
* @throws {Error} If URL format is invalid
*/
const VIMEO_ID_REGEX = /(?:\/|^)(\d+)(?:[/?#]|$)/;
function extractVimeoVideoId(url) {
const match = url.match(VIMEO_ID_REGEX);
if (!match) {
throw new Error('Invalid Vimeo URL format');
}
return match[1];
}
/**
* Main function to handle video loading process
* - Checks if current page is a restricted video
* - Creates loading overlay with spinner
* - Initiates video request with Patreon referrer
* @throws {Error} If video loading fails
*/
function loadVimeoVideo() {
try {
const videoPlayerElement = document.querySelector('.exception_title.iris_header');
if (!videoPlayerElement) {
console.debug('Not a restricted video page, script not needed');
return;
}
const videoId = extractVimeoVideoId(window.location.href);
// Add black overlay and loading indicator with spinner animation
const overlayElement = document.createElement('div');
overlayElement.style.cssText = 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: black; z-index: 9998;';
document.body.appendChild(overlayElement);
const loadingElement = document.createElement('div');
loadingElement.innerHTML = `
`;
loadingElement.style.cssText = 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); color: white; z-index: 9999; font-family: Arial, sans-serif;';
// Add the spinner animation
const styleSheet = document.createElement('style');
styleSheet.textContent = `
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`;
document.head.appendChild(styleSheet);
document.body.appendChild(loadingElement);
GM_xmlhttpRequest({
method: 'GET',
url: `https://player.vimeo.com/video/${videoId}`,
headers: {
'Referer': 'https://www.patreon.com',
'Cache-Control': 'no-cache'
},
responseType: 'blob',
onload: function(response) {
const videoUrl = URL.createObjectURL(response.response);
setupVideoPlayer(videoUrl);
},
onerror: handleError
});
} catch (error) {
handleError(error);
}
}
/**
* Sets up the video player interface
* - Clears existing page content
* - Creates full-screen iframe player
* - Applies dark theme styling
* @param {string} videoUrl - Blob URL of the video content
*/
function setupVideoPlayer(videoUrl) {
document.body.innerHTML = '';
document.documentElement.style.cssText = 'background-color: #212121; color: #ffffff;';
const videoElement = document.createElement('iframe');
videoElement.src = videoUrl;
videoElement.style.cssText = 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; border: none;';
document.body.appendChild(videoElement);
}
/**
* Centralized error handler for all video loading failures
* @param {Error} error - The error object or message
*/
function handleError(error) {
console.error('Error loading video:', error);
alert('Failed to load video. Please check the URL or try again later.');
}
/**
* Script initialization point
*/
function init() {
loadVimeoVideo();
}
// Bootstrap the script
init();
})();