VSTACK: Global Video UI Overhaul

Upgrade all HTML5 <video> elements to the elegant Vidstack UI on every website you visit.

// ==UserScript==
// @name         VSTACK: Global Video UI Overhaul
// @version      1.0
// @description  Upgrade all HTML5 <video> elements to the elegant Vidstack UI on every website you visit.
// @namespace    https://github.com/poihoii/VSplyr-UI-Overhaul
// @author       poihoii
// @match        *://*/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    const loadVidstackAssets = () => {
        if (document.getElementById('vidstack-js')) return;

        const mediaCSS = document.createElement('link');
        mediaCSS.rel = 'stylesheet';
        mediaCSS.href = 'https://cdn.vidstack.io/player/theme/classic/index.css';
        document.head.appendChild(mediaCSS);

        const vidstackJS = document.createElement('script');
        vidstackJS.id = 'vidstack-js';
        vidstackJS.type = 'module';
        vidstackJS.innerHTML = `
            import 'https://cdn.vidstack.io/player/core/index.js';
        `;
        document.head.appendChild(vidstackJS);
    };

    const enhanceVideos = () => {
        document.querySelectorAll('video').forEach((video) => {
            if (video.classList.contains('vs-enhanced')) return;
            const src = video.currentSrc || video.src;
            if (!src) return;

            const wrapper = document.createElement('div');
            wrapper.innerHTML = `
                <media-player title="Injected Video" src="${src}" crossorigin="anonymous" playsinline>
                    <media-provider></media-provider>
                    <media-video-layout></media-video-layout>
                </media-player>
            `;
            wrapper.querySelector('media-player').classList.add('vs-enhanced');

            video.replaceWith(wrapper);
        });
    };

    const observeVideos = () => {
        const observer = new MutationObserver(() => enhanceVideos());
        observer.observe(document.body, { childList: true, subtree: true });
    };

    loadVidstackAssets();
    window.addEventListener('load', () => {
        enhanceVideos();
        observeVideos();
    });
})();