{
  "manifest_version": 3,
  "name": "Media Loader",
  "version": "1.0",
  "description": "Load and play music and videos from YouTube and Vimeo",
  "permissions": ["storage", "tabs"],
  "host_permissions": [
    "https://www.youtube.com/*",
    "https://player.vimeo.com/*"
  ],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_title": "Open Media Loader",
    "default_icon": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  },
  "icons": {
    "16": "icons/icon16.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  },
  "content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self'"
  }
}

// background.js

chrome.action.onClicked.addListener(() => {
    const dashboardURL = chrome.runtime.getURL("dashboard.html");
    
    // Query for any existing tabs with the dashboard URL
    chrome.tabs.query({ url: dashboardURL }, (tabs) => {
        if (chrome.runtime.lastError) {
            console.error("Tab Query Error:", chrome.runtime.lastError);
            return;
        }

        if (tabs.length > 0) {
            // Activate the first existing dashboard tab
            chrome.tabs.update(tabs[0].id, { active: true }, (updatedTab) => {
                if (chrome.runtime.lastError) {
                    console.error("Tab Update Error:", chrome.runtime.lastError);
                }
            });
        } else {
            // Create a new dashboard tab
            chrome.tabs.create({ url: dashboardURL, active: true }, (newTab) => {
                if (chrome.runtime.lastError) {
                    console.error("Tab Creation Error:", chrome.runtime.lastError);
                }
            });
        }
    });
});

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Media Loader Dashboard</title>
    <link rel="stylesheet" href="styles.css">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
    <main class="container">
        <!-- Header with Settings Button and SyncWave Title -->
        <header class="header">
            <button id="settings-button" class="settings-button" title="Settings" aria-label="Open Settings">
                <img src="icons/settings.svg" alt="Settings" class="settings-icon" aria-hidden="true">
            </button>
            <h1 class="main-title">SyncWave</h1>
        </header>
        
        <!-- Load Audio Section -->
        <section class="section" id="load-music-section">
            <div class="section-header">
                <legend>Load Audio</legend>
            </div>
            
            <fieldset>
                <label for="music-url">Audio URL</label>
                <input type="text" id="music-url" name="music-url" placeholder="Enter YouTube or Vimeo URL">

                <label for="music-file">Upload Audio File</label>
                <input type="file" id="music-file" name="music-file" accept="audio/*">

                <label for="music-start">Start Time (seconds)</label>
                <input type="number" id="music-start" name="music-start" placeholder="Start Time (seconds)" min="0">

                <button type="button" id="load-music" class="load-button">Load Audio</button>
            </fieldset>
            <div id="music-player-container" class="player-container"></div>
        </section>

        <!-- Load Video Section -->
        <section class="section" id="load-video-section">
            <fieldset>
                <legend>Load Video</legend>
                
                <label for="video-url">Video URL</label>
                <input type="text" id="video-url" name="video-url" placeholder="Enter YouTube or Vimeo URL">

                <label for="video-file">Upload Video File</label>
                <input type="file" id="video-file" name="video-file" accept="video/*">

                <label for="video-start">Start Time (seconds)</label>
                <input type="number" id="video-start" name="video-start" placeholder="Start Time (seconds)" min="0">

                <button type="button" id="load-video" class="load-button">Load Video</button>
            </fieldset>
            <div id="video-player-container" class="player-container"></div>
        </section>

        <!-- Playback Controls -->
        <section class="controls">
            <button type="button" id="play-button" disabled aria-label="Play">Play</button>
            <button type="button" id="pause-resume-button" disabled aria-label="Pause">Pause</button>
            <button type="button" id="play-start-button" disabled aria-label="Play from Start">Play from Start</button>
        </section>

        <!-- Message Container -->
        <section id="message-container" class="message-container" role="alert" aria-live="assertive"></section>

        <!-- Settings Modal (Remains Unchanged) -->
        <div id="settings-modal" class="modal" role="dialog" aria-labelledby="settings-modal-title" aria-modal="true" hidden>
            <div class="modal-content">
                <button class="close-button" aria-label="Close Settings Modal">&times;</button>
                <h2 id="settings-modal-title">Settings</h2>
                <form>
                    <label for="fullscreen-toggle">
                        <input type="checkbox" id="fullscreen-toggle" name="fullscreen-toggle">
                        Full Screen on Play
                    </label>
                    <!-- Additional settings can go here -->
                </form>
            </div>
        </div>
    </main>

    <script src="dashboard.js"></script>
</body>
</html>

"use strict";

document.addEventListener('DOMContentLoaded', () => {
    // === Element References ===
    const elements = {
        music: {
            urlInput: document.getElementById('music-url'),
            fileInput: document.getElementById('music-file'),
            startInput: document.getElementById('music-start'),
            loadButton: document.getElementById('load-music'),
            playerContainer: document.getElementById('music-player-container')
        },
        video: {
            urlInput: document.getElementById('video-url'),
            fileInput: document.getElementById('video-file'),
            startInput: document.getElementById('video-start'),
            loadButton: document.getElementById('load-video'),
            playerContainer: document.getElementById('video-player-container')
        },
        controls: {
            playButton: document.getElementById('play-button'),
            pauseResumeButton: document.getElementById('pause-resume-button'),
            playStartButton: document.getElementById('play-start-button')
        },
        messageContainer: document.getElementById('message-container'),
        settings: {
            button: document.getElementById('settings-button'),
            modal: document.getElementById('settings-modal'),
            closeButton: document.querySelector('.close-button'),
            fullscreenToggle: document.getElementById('fullscreen-toggle')
        }
    };

    // === State Management ===
    const state = {
        music: null, // { type: 'file' | 'youtube' | 'vimeo', element: HTMLAudioElement | iframe Element, start: Number }
        video: null, // { type: 'file' | 'youtube' | 'vimeo', element: HTMLVideoElement | iframe Element, start: Number }
        isPaused: false,
        fullscreenOnPlay: false
    };

    // === Utility Functions ===

    /**
     * Formats seconds into 'XmYs' (e.g., 125 seconds -> '2m5s')
     * @param {number} seconds - The time in seconds.
     * @returns {string} Formatted time string.
     */
    function formatTime(seconds) {
        if (isNaN(seconds) || seconds < 0) return '';
        const minutes = Math.floor(seconds / 60);
        const remainingSeconds = seconds % 60;
        return `${minutes}m${remainingSeconds}s`;
    }

    /**
     * Capitalize the first letter of a string.
     * @param {string} text - The string to capitalize.
     * @returns {string} Capitalized string.
     */
    function capitalize(text) {
        if (!text) return '';
        return text.charAt(0).toUpperCase() + text.slice(1);
    }

    /**
     * Displays a message to the user.
     * @param {string} message - The message to display.
     * @param {string} type - The type of message ('success', 'error', 'info').
     */
    function showMessage(message, type) {
        elements.messageContainer.innerHTML = '';
        const msgDiv = document.createElement('div');
        msgDiv.textContent = message;
        msgDiv.classList.add('message', type); // 'success', 'error', 'info'
        elements.messageContainer.appendChild(msgDiv);

        // Automatically hide message after 5 seconds
        setTimeout(() => {
            if (elements.messageContainer.contains(msgDiv)) {
                elements.messageContainer.removeChild(msgDiv);
            }
        }, 5000);
    }

    /**
     * Shows a loading spinner in the specified container.
     * @param {HTMLElement} container - The container to show the loader in.
     */
    function showLoading(container) {
        container.innerHTML = '';
        const loader = document.createElement('div');
        loader.classList.add('loader'); // Define loader styles in CSS
        container.appendChild(loader);
    }

    /**
     * Hides the loading spinner from the specified container.
     * @param {HTMLElement} container - The container to hide the loader from.
     */
    function hideLoading(container) {
        const loader = container.querySelector('.loader');
        if (loader) {
            container.removeChild(loader);
        }
    }

    /**
     * Traps keyboard focus within a specified element.
     * @param {HTMLElement} element - The element to trap focus within.
     */
    function trapFocus(element) {
        const focusableElementsString = 'a[href], area[href], input:not([disabled]), select:not([disabled]),' +
            ' textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"],' +
            ' [contenteditable]';
        const focusableElements = element.querySelectorAll(focusableElementsString);
        const firstTabStop = focusableElements[0];
        const lastTabStop = focusableElements[focusableElements.length - 1];

        // Listen for and trap the keyboard
        element.addEventListener('keydown', trapTabKey);

        function trapTabKey(e) {
            // Check for TAB key press
            if (e.key === 'Tab') {
                // SHIFT + TAB
                if (e.shiftKey) {
                    if (document.activeElement === firstTabStop) {
                        e.preventDefault();
                        lastTabStop.focus();
                    }
                } else { // TAB
                    if (document.activeElement === lastTabStop) {
                        e.preventDefault();
                        firstTabStop.focus();
                    }
                }
            }
        }

        // Remove trap when modal is closed
        function removeTrapKey(e) {
            element.removeEventListener('keydown', trapTabKey);
        }

        // Store the function reference to remove it later
        element.removeTrapKey = removeTrapKey;
    }

    function removeTrapFocus() {
        elements.settings.modal.removeTrapKey();
    }

    // === Initialization ===
    loadSettings();

    // === Event Listeners ===
    elements.music.loadButton.addEventListener('click', handleLoadMusic);
    elements.video.loadButton.addEventListener('click', handleLoadVideo);
    elements.controls.playButton.addEventListener('click', handlePlay);
    elements.controls.pauseResumeButton.addEventListener('click', handlePauseResume);
    elements.controls.playStartButton.addEventListener('click', handlePlayFromStart);

    // Settings Modal Event Listeners
    elements.settings.button.addEventListener('click', openSettingsModal);
    elements.settings.closeButton.addEventListener('click', closeSettingsModal);
    window.addEventListener('click', (event) => {
        if (event.target === elements.settings.modal) {
            closeSettingsModal();
        }
    });
    document.addEventListener('keydown', (event) => {
        if (event.key === 'Escape' && !elements.settings.modal.hasAttribute('hidden')) {
            closeSettingsModal();
        }
    });
    elements.settings.fullscreenToggle.addEventListener('change', (event) => {
        state.fullscreenOnPlay = event.target.checked;
        saveSettings();
    });

    // === Handler Functions ===

    // Load Music Handler
    function handleLoadMusic() {
        const url = elements.music.urlInput.value.trim();
        const file = elements.music.fileInput.files[0];
        const start = parseInt(elements.music.startInput.value) || 0;

        // Show loading indicator
        showLoading(elements.music.playerContainer);

        // Unload existing music if any
        if (state.music) {
            unloadMedia('music');
        }

        if (file) {
            if (file.size > 10 * 1024 * 1024) { // 10MB limit
                showMessage('Audio file size exceeds 10MB limit.', 'error');
                hideLoading(elements.music.playerContainer);
                return;
            }

            // Handle local audio file
            const fileUrl = URL.createObjectURL(file);
            const audioElement = document.createElement('audio');
            audioElement.controls = true;
            audioElement.src = fileUrl;
            audioElement.id = 'loaded-music';
            audioElement.setAttribute('aria-label', 'Loaded Audio Player');
            audioElement.style.maxWidth = '100%';
            audioElement.style.maxHeight = '360px';
            elements.music.playerContainer.innerHTML = '';
            elements.music.playerContainer.appendChild(audioElement);

            // Set start time if applicable
            if (start > 0) {
                audioElement.currentTime = start;
            }

            state.music = {
                type: 'file',
                element: audioElement,
                start: start
            };
            showMessage('Local audio file loaded.', 'success');
            hideLoading(elements.music.playerContainer);
        } else if (url && isValidMediaURL(url)) {
            const mediaInfo = extractMediaInfo(url);
            if (mediaInfo) {
                if (mediaInfo.platform === 'youtube') {
                    embedYouTubeVideo('music-player-container', mediaInfo.id, start, 'music');
                } else if (mediaInfo.platform === 'vimeo') {
                    embedVimeoVideo('music-player-container', mediaInfo.id, start, 'music');
                }
                showMessage(`Loaded ${capitalize(mediaInfo.platform)} audio.`, 'success');
                hideLoading(elements.music.playerContainer);
            } else {
                showMessage('Invalid URL. Please enter a valid YouTube or Vimeo URL.', 'error');
                hideLoading(elements.music.playerContainer);
            }
        } else {
            showMessage('Please enter a URL or select an audio file.', 'error');
            hideLoading(elements.music.playerContainer);
        }

        checkReadyToPlay();
        saveSettings();
    }

    // Load Video Handler
    function handleLoadVideo() {
        const url = elements.video.urlInput.value.trim();
        const file = elements.video.fileInput.files[0];
        const start = parseInt(elements.video.startInput.value) || 0;

        // Show loading indicator
        showLoading(elements.video.playerContainer);

        // Unload existing video if any
        if (state.video) {
            unloadMedia('video');
        }

        if (file) {
            if (file.size > 50 * 1024 * 1024) { // 50MB limit
                showMessage('Video file size exceeds 50MB limit.', 'error');
                hideLoading(elements.video.playerContainer);
                return;
            }

            // Handle local video file
            const fileUrl = URL.createObjectURL(file);
            const videoElement = document.createElement('video');
            videoElement.controls = true;
            videoElement.src = fileUrl;
            videoElement.id = 'loaded-video';
            videoElement.setAttribute('aria-label', 'Loaded Video Player');
            videoElement.style.maxWidth = '100%';
            videoElement.style.maxHeight = '360px';
            elements.video.playerContainer.innerHTML = '';
            elements.video.playerContainer.appendChild(videoElement);

            // Set start time if applicable
            if (start > 0) {
                videoElement.currentTime = start;
            }

            state.video = {
                type: 'file',
                element: videoElement,
                start: start
            };
            showMessage('Local video file loaded.', 'success');
            hideLoading(elements.video.playerContainer);
        } else if (url && isValidMediaURL(url)) {
            const mediaInfo = extractMediaInfo(url);
            if (mediaInfo) {
                if (mediaInfo.platform === 'youtube') {
                    embedYouTubeVideo('video-player-container', mediaInfo.id, start, 'video');
                } else if (mediaInfo.platform === 'vimeo') {
                    embedVimeoVideo('video-player-container', mediaInfo.id, start, 'video');
                }
                showMessage(`Loaded ${capitalize(mediaInfo.platform)} video.`, 'success');
                hideLoading(elements.video.playerContainer);
            } else {
                showMessage('Invalid URL. Please enter a valid YouTube or Vimeo URL.', 'error');
                hideLoading(elements.video.playerContainer);
            }
        } else {
            showMessage('Please enter a URL or select a video file.', 'error');
            hideLoading(elements.video.playerContainer);
        }

        checkReadyToPlay();
        saveSettings();
    }

    // Play Handler
    function handlePlay() {
        if (state.music && state.music.type === 'file') {
            state.music.element.play();
            if (state.fullscreenOnPlay) {
                enterFullscreen(state.music.element);
            }
        }

        if (state.video && state.video.type === 'file') {
            state.video.element.play();
            if (state.fullscreenOnPlay) {
                enterFullscreen(state.video.element);
            }
        }

        // Note: Cannot control embedded iframes due to CSP and cross-origin restrictions

        state.isPaused = false;
        elements.controls.pauseResumeButton.textContent = 'Pause';
        showMessage('Playback started!', 'success');
    }

    // Pause/Resume Handler
    function handlePauseResume() {
        if (state.isPaused) {
            resumeMedia();
        } else {
            pauseMedia();
        }
    }

    // Pause Media
    function pauseMedia() {
        if (state.music && state.music.type === 'file') {
            state.music.element.pause();
        }

        if (state.video && state.video.type === 'file') {
            state.video.element.pause();
        }

        // Cannot control embedded iframes

        state.isPaused = true;
        elements.controls.pauseResumeButton.textContent = 'Resume';
        showMessage('Playback paused.', 'info');
    }

    // Resume Media
    function resumeMedia() {
        if (state.music && state.music.type === 'file') {
            state.music.element.play();
        }

        if (state.video && state.video.type === 'file') {
            state.video.element.play();
        }

        // Cannot control embedded iframes

        state.isPaused = false;
        elements.controls.pauseResumeButton.textContent = 'Pause';
        showMessage('Playback resumed.', 'success');
    }

    // Play From Start Handler
    function handlePlayFromStart() {
        if (state.music && state.music.type === 'file') {
            state.music.element.currentTime = state.music.start;
            state.music.element.play();
            if (state.fullscreenOnPlay) {
                enterFullscreen(state.music.element);
            }
        }

        if (state.video && state.video.type === 'file') {
            state.video.element.currentTime = state.video.start;
            state.video.element.play();
            if (state.fullscreenOnPlay) {
                enterFullscreen(state.video.element);
            }
        }

        // Cannot control embedded iframes

        state.isPaused = false;
        elements.controls.pauseResumeButton.textContent = 'Pause';
        showMessage('Playback restarted!', 'success');
    }

    // === Settings Modal Functions ===
    function openSettingsModal() {
        elements.settings.modal.removeAttribute('hidden');
        elements.settings.modal.setAttribute('aria-hidden', 'false');
        elements.settings.modal.querySelector('.close-button').focus();
        trapFocus(elements.settings.modal);
    }

    function closeSettingsModal() {
        elements.settings.modal.setAttribute('hidden', '');
        elements.settings.modal.setAttribute('aria-hidden', 'true');
        elements.settings.button.focus();
        removeTrapFocus();
    }

    // === Media Embedding Functions ===

    /**
     * Embeds a YouTube video into the specified container.
     * @param {string} containerId - The ID of the container.
     * @param {string} videoId - The YouTube video ID.
     * @param {number} start - The start time in seconds.
     * @param {string} mediaType - 'music' or 'video'.
     */
    function embedYouTubeVideo(containerId, videoId, start, mediaType) {
        const iframe = document.createElement('iframe');
        iframe.src = `https://www.youtube.com/embed/${videoId}?start=${start}&autoplay=0&controls=1&rel=0`;
        iframe.width = '100%';
        iframe.height = '360';
        iframe.frameBorder = '0';
        // Include 'fullscreen' in the allow attribute
        iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; fullscreen';
        // Do not set allowFullscreen in JavaScript; it's redundant with allow attribute

        iframe.id = `iframe-${mediaType}-${videoId}`;
        iframe.setAttribute('title', 'YouTube video player');

        elements[mediaType].playerContainer.innerHTML = '';
        elements[mediaType].playerContainer.appendChild(iframe);

        state[mediaType] = {
            type: 'youtube',
            element: iframe,
            start: start
        };
    }

    /**
     * Embeds a Vimeo video into the specified container.
     * @param {string} containerId - The ID of the container.
     * @param {string} videoId - The Vimeo video ID.
     * @param {number} start - The start time in seconds.
     * @param {string} mediaType - 'music' or 'video'.
     */
    function embedVimeoVideo(containerId, videoId, start, mediaType) {
        const formattedTime = formatTime(start);
        const iframe = document.createElement('iframe');
        iframe.src = `https://player.vimeo.com/video/${videoId}?autoplay=0#t=${formattedTime}`;
        iframe.width = '100%';
        iframe.height = '360';
        iframe.frameBorder = '0';
        iframe.allow = 'autoplay; fullscreen; picture-in-picture';
        iframe.setAttribute('allowfullscreen', '');
        iframe.setAttribute('title', 'Vimeo video player');

        iframe.id = `iframe-${mediaType}-${videoId}`;

        elements[mediaType].playerContainer.innerHTML = '';
        elements[mediaType].playerContainer.appendChild(iframe);

        state[mediaType] = {
            type: 'vimeo',
            element: iframe,
            start: start
        };

        console.log(`Embedded Vimeo Video: ${iframe.src}`);
    }

    // === Settings Management ===

    /**
     * Loads settings from Chrome storage.
     */
    function loadSettings() {
        try {
            chrome.storage.local.get(['mediaSettings'], (result) => {
                if (chrome.runtime.lastError) {
                    throw new Error(chrome.runtime.lastError);
                }

                if (result.mediaSettings) {
                    const { music, video, fullscreenOnPlay: fsPlay } = result.mediaSettings;

                    if (music) {
                        if (music.type === 'youtube' || music.type === 'vimeo') {
                            const url = music.type === 'youtube' 
                                ? `https://www.youtube.com/watch?v=${music.id}` 
                                : `https://vimeo.com/${music.id}`;
                            elements.music.urlInput.value = url;
                            elements.music.startInput.value = music.start || 0;
                            // Optionally, auto-load the media by triggering the load button
                            // handleLoadMusic();
                        }
                        // Handle file-based music loading if necessary
                    }

                    if (video) {
                        if (video.type === 'youtube' || video.type === 'vimeo') {
                            const url = video.type === 'youtube' 
                                ? `https://www.youtube.com/watch?v=${video.id}` 
                                : `https://vimeo.com/${video.id}`;
                            elements.video.urlInput.value = url;
                            elements.video.startInput.value = video.start || 0;
                            // Optionally, auto-load the media by triggering the load button
                            // handleLoadVideo();
                        }
                        // Handle file-based video loading if necessary
                    }

                    if (typeof fsPlay === 'boolean') {
                        state.fullscreenOnPlay = fsPlay;
                        elements.settings.fullscreenToggle.checked = fsPlay;
                    }
                }
            });
        } catch (error) {
            console.error('Error loading settings:', error);
            showMessage('Failed to load settings.', 'error');
        }
    }

    /**
     * Saves settings to Chrome storage.
     */
    function saveSettings() {
        const settings = {
            music: state.music,
            video: state.video,
            fullscreenOnPlay: state.fullscreenOnPlay
        };
        try {
            chrome.storage.local.set({ mediaSettings: settings }, () => {
                if (chrome.runtime.lastError) {
                    throw new Error(chrome.runtime.lastError);
                }
                console.log('Settings saved.');
            });
        } catch (error) {
            console.error('Error saving settings:', error);
            showMessage('Error saving settings.', 'error');
        }
    }

    /**
     * Unloads media from the specified type.
     * @param {string} mediaType - 'music' or 'video'.
     */
    function unloadMedia(mediaType) {
        if (state[mediaType]) {
            if (state[mediaType].type === 'file') {
                if (state[mediaType].element.src) {
                    URL.revokeObjectURL(state[mediaType].element.src);
                    state[mediaType].element.src = '';
                    state[mediaType].element.remove();
                }
            } else if (state[mediaType].type === 'youtube' || state[mediaType].type === 'vimeo') {
                // Reset iframe src to stop the media
                const iframe = state[mediaType].element;
                iframe.src = '';
            }
            state[mediaType] = null;
        }
    }

    // === Playback Controls ===

    /**
     * Enables or disables playback controls based on media loading state.
     */
    function checkReadyToPlay() {
        // Enable Play button only when at least one of audio or video is loaded
        const readyAudio = state.music && (state.music.type === 'file' || state.music.type === 'youtube' || state.music.type === 'vimeo');
        const readyVideo = state.video && (state.video.type === 'file' || state.video.type === 'youtube' || state.video.type === 'vimeo');
        const ready = readyAudio || readyVideo;

        elements.controls.playButton.disabled = !ready;
        elements.controls.pauseResumeButton.disabled = !ready;
        elements.controls.playStartButton.disabled = !ready;
    }

    /**
     * Enters fullscreen mode for the specified element.
     * @param {HTMLElement} element - The element to enter fullscreen.
     */
    function enterFullscreen(element) {
        if (element && element.requestFullscreen) {
            element.requestFullscreen().then(() => {
                showMessage('Entered fullscreen mode.', 'success');
            }).catch(err => {
                console.error(`Error attempting to enable full-screen mode: ${err.message} (${err.name})`);
                showMessage('Failed to enter fullscreen mode.', 'error');
            });
        } else if (element && element.parentElement.requestFullscreen) {
            // For iframe embeds, request fullscreen on the container
            element.parentElement.requestFullscreen().then(() => {
                showMessage('Entered fullscreen mode.', 'success');
            }).catch(err => {
                console.error(`Error attempting to enable full-screen mode: ${err.message} (${err.name})`);
                showMessage('Failed to enter fullscreen mode.', 'error');
            });
        } else {
            showMessage('Fullscreen not supported on this element.', 'error');
        }
    }

    // === New Utility Functions ===

    /**
     * Validates whether the URL is a valid YouTube or Vimeo URL.
     * @param {string} url - The URL to validate.
     * @returns {boolean} True if valid, else false.
     */
    function isValidMediaURL(url) {
        try {
            const parsedUrl = new URL(url);
            const hostname = parsedUrl.hostname.toLowerCase();
            const pathname = parsedUrl.pathname;

            // YouTube RegEx Patterns
            const youtubeRegex = /^(?:\/watch\/?|\/embed\/|\/v\/)?([a-zA-Z0-9_-]{11})$/;
            // Vimeo RegEx Patterns
            const vimeoRegex = /^\/(?:video\/)?(\d+)$/;

            if (hostname.includes('youtube.com') || hostname === 'youtu.be') {
                // Extract YouTube video ID
                if (hostname === 'youtu.be') {
                    const videoId = pathname.slice(1);
                    return youtubeRegex.test(videoId);
                } else {
                    const videoId = parsedUrl.searchParams.get('v');
                    return youtubeRegex.test(videoId);
                }
            } else if (hostname.includes('vimeo.com')) {
                // Extract Vimeo video ID
                const match = pathname.match(vimeoRegex);
                return match && match[1].length > 0;
            }

            return false;
        } catch (e) {
            return false;
        }
    }

    /**
     * Extracts media information from the URL.
     * @param {string} url - The media URL.
     * @returns {Object|null} An object with platform and id, or null if invalid.
     */
    function extractMediaInfo(url) {
        try {
            const parsedUrl = new URL(url);
            const hostname = parsedUrl.hostname.toLowerCase();
            const pathname = parsedUrl.pathname;

            // YouTube Patterns
            if (hostname.includes('youtube.com') || hostname === 'youtu.be') {
                let videoId = '';
                if (hostname === 'youtu.be') {
                    videoId = pathname.slice(1);
                } else {
                    videoId = parsedUrl.searchParams.get('v') || '';
                }

                // Validate videoId length
                if (videoId.length === 11) {
                    return { platform: 'youtube', id: videoId };
                }
            }

            // Vimeo Patterns
            if (hostname.includes('vimeo.com')) {
                const vimeoMatch = pathname.match(/^\/(?:video\/)?(\d+)$/);
                if (vimeoMatch && vimeoMatch[1]) {
                    return { platform: 'vimeo', id: vimeoMatch[1] };
                }
            }

            return null;
        } catch (e) {
            return null;
        }
    }
});

/* General Styles */
body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f4;
    margin: 0;
    padding: 0;
}

.container {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 20px;
    max-width: 800px;
    margin: 0 auto;
}

.section {
    width: 100%;
    margin-bottom: 20px;
    background-color: #fff;
    padding: 15px;
    border-radius: 8px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

/* Header with Settings Button and Main Title */
.header {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center; /* Center the content */
    margin-bottom: 20px;
    position: relative;
}

.settings-button {
    position: absolute;
    left: 0;
    background: #fff;
    border: 1px solid #ccc;
    border-radius: 5px;
    padding: 5px;
    cursor: pointer;
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background-color 0.3s ease, border-color 0.3s ease;
}

.settings-button:hover {
    background-color: #f0f0f0;
    border-color: #999;
}

.settings-icon {
    width: 20px;
    height: 20px;
}

.main-title {
    font-size: 2em;
    font-weight: bold;
    margin: 0;
    opacity: 0;
    animation: fadeInShake 5s forwards;
}

@keyframes fadeInShake {
    0% {
        opacity: 0;
        transform: translateX(0);
        color: #FF0000; /* Red */
    }
    10% {
        opacity: 0.2;
        transform: translateX(-10px);
        color: #FF7F00; /* Orange */
    }
    20% {
        opacity: 0.4;
        transform: translateX(10px);
        color: #FFFF00; /* Yellow */
    }
    30% {
        opacity: 0.6;
        transform: translateX(-10px);
        color: #00FF00; /* Green */
    }
    40% {
        opacity: 0.7;
        transform: translateX(10px);
        color: #0000FF; /* Blue */
    }
    50% {
        opacity: 0.8;
        transform: translateX(-10px);
        color: #4B0082; /* Indigo */
    }
    60% {
        opacity: 0.85;
        transform: translateX(10px);
        color: #8B00FF; /* Violet */
    }
    70% {
        opacity: 0.9;
        transform: translateX(-10px);
        color: #FF00FF; /* Magenta */
    }
    80% {
        opacity: 0.95;
        transform: translateX(10px);
        color: #000000; /* Black */
    }
    100% {
        opacity: 1;
        transform: translateX(0);
        color: #000000; /* Black */
    }
}

/* Section Header */
.section-header {
    display: flex;
    align-items: center;
    margin-bottom: 10px;
}

.section-header legend {
    font-size: 1.2em;
    font-weight: bold;
    margin: 0;
}

/* Controls */
.controls {
    display: flex;
    gap: 10px;
    margin-bottom: 20px;
}

.controls button {
    padding: 10px 20px;
    cursor: pointer;
    border: none;
    border-radius: 5px;
    background-color: #4285f4;
    color: #fff;
    transition: background-color 0.3s ease;
}

.controls button:disabled {
    background-color: #a0c3ff;
    cursor: not-allowed;
}

.controls button:hover:not(:disabled) {
    background-color: #357ae8;
}

/* Modal Styles */
.modal[hidden] {
    display: none;
}

.modal {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(0, 0, 0, 0.5);
    display: flex; /* Visible when not hidden */
    align-items: center;
    justify-content: center;
    z-index: 1000;
}

.modal-content {
    background-color: #fff;
    padding: 20px 30px;
    border-radius: 8px;
    width: 90%;
    max-width: 400px;
    position: relative;
}

.close-button {
    position: absolute;
    top: 10px;
    right: 15px;
    background: none;
    border: none;
    font-size: 24px;
    cursor: pointer;
}

.modal-content h2 {
    margin-top: 0;
}

/* Form Elements */
fieldset {
    border: none;
    padding: 0;
}

fieldset label {
    display: block;
    margin-bottom: 5px;
    font-weight: bold;
}

fieldset input[type="text"],
fieldset input[type="number"],
fieldset input[type="file"] {
    width: 100%;
    padding: 8px;
    margin-bottom: 15px;
    box-sizing: border-box;
    border: 1px solid #ccc;
    border-radius: 4px;
}

fieldset .load-button {
    width: 100%;
    padding: 10px;
    background-color: #34a853;
    color: #fff;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    transition: background-color 0.3s ease;
}

fieldset .load-button:hover {
    background-color: #2c8e46;
}

/* Player Containers */
.player-container {
    margin-top: 10px;
    width: 100%;
    max-width: 100%;
    min-height: 50px;
    box-sizing: border-box;
    overflow: hidden; /* Prevent overflow */
}

/* Responsive Video Elements */
.player-container video,
.player-container iframe {
    width: 100%;
    height: auto;
    max-height: 360px; /* Adjust as needed */
    border-radius: 8px;
}

/* Messages */
.message-container {
    margin-top: 20px;
    width: 100%;
}

.message {
    padding: 10px;
    border-radius: 5px;
    margin-bottom: 10px;
    color: #fff;
}

.message.success {
    background-color: #4caf50;
}

.message.error {
    background-color: #f44336;
}

.message.info {
    background-color: #2196f3;
}

/* Loader */
.loader {
    border: 8px solid #f3f3f3; /* Light grey */
    border-top: 8px solid #4285f4; /* Blue */
    border-radius: 50%;
    width: 40px;
    height: 40px;
    animation: spin 1s linear infinite;
    margin: 0 auto;
}

@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

/* Responsive Design */
@media (max-width: 600px) {
    .container {
        padding: 10px;
    }

    .modal-content {
        padding: 15px 20px;
    }

    .controls {
        flex-direction: column;
        width: 100%;
    }

    .controls button {
        width: 100%;
    }

    .settings-container {
        /* Removed absolute positioning */
    }

    .settings-button {
        width: 35px;
        height: 35px;
    }

    .settings-icon {
        width: 18px;
        height: 18px;
    }

    .main-title {
        font-size: 1.5em;
    }
}

lets make the videos as wide as the load buttons. the play button didnt play the videos when i tried it with vimeo urls. i got the following error but i dont know when it happened: Errors
Allow attribute will take precedence over 'allowfullscreen'.
Context
dashboard.html    i only need the whole files that are changed