i only need the complete updated files.

{
"manifest_version": 3,
"name": "Media Loader",
"version": "1.4",
"description": "Load and play music and videos from YouTube and Vimeo",
"permissions": ["storage", "tabs"],
"host_permissions": [
"https://www.youtube.com/",
"https://youtu.be/",
"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'; frame-src https://www.youtube.com https://youtu.be https://player.vimeo.com"
},
"web_accessible_resources": [
{
"resources": ["lib/player.js"],
"matches": [""]
},
{
"resources": ["youtube-sandbox.html"],
"matches": [""]
}
]
}

// 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);
        }
    });
}
});
});

Settings
SyncWave


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

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

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

        <button type="button" id="load-music" class="load-button">Load Audio</button>
    </fieldset>
    <div id="music-player-container" class="player-container" aria-live="polite"></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" aria-label="Enter YouTube or Vimeo Video URL">

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

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

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

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

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

<!-- Settings Modal -->
<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>
// dashboard.js

"use strict";

let youtubePlayers = {
music: null,
video: null
};

// Function to send messages to the YouTube sandbox iframe
function sendMessageToYouTubeSandbox(message) {
const iframe = document.getElementById('youtube-sandbox-iframe');
iframe.contentWindow.postMessage(message, '*');
}

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'),
pauseButton: document.getElementById('pause-button'),
resumeButton: document.getElementById('resume-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')
},
youtubeSandbox: document.getElementById('youtube-sandbox-iframe')
};

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

// === Utility Functions ===

/**

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() {
if (elements.settings.modal.removeTrapKey) {
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.pauseButton.addEventListener('click', handlePause);
elements.controls.resumeButton.addEventListener('click', handleResume);

// 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();
});

// Listen for messages from YouTube Sandbox
window.addEventListener('message', (event) => {
// For security, you can check event.origin here if necessary
const data = event.data;
if (data.type === 'playerReady') {
// YouTube Player is ready
showMessage(YouTube ${capitalize(data.mediaType)} loaded and ready., 'success');
hideLoading(elements[data.mediaType].playerContainer);
} else if (data.type === 'playerError') {
showMessage(Error loading YouTube ${capitalize(data.mediaType)}., 'error');
hideLoading(elements[data.mediaType].playerContainer);
}
});

// === 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) {
    // 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.width = '100%';
    audioElement.style.height = 'auto';

    // Listen for metadata to set start time
    audioElement.addEventListener('loadedmetadata', () => {
        if (start > 0 && audioElement.duration > start) {
            audioElement.currentTime = start;
        }
    });

    elements.music.playerContainer.innerHTML = '';
    elements.music.playerContainer.appendChild(audioElement);

    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') {
            embedYouTubePlayer(mediaInfo.id, start, 'music');
        } else if (mediaInfo.platform === 'vimeo') {
            embedVimeoPlayer(mediaInfo.id, start, 'music');
        }
        // Loading will be handled inside embed functions
    } 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 a local 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) {
    // 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.width = '100%';
    videoElement.style.height = 'auto';

    // Listen for metadata to set start time
    videoElement.addEventListener('loadedmetadata', () => {
        if (start > 0 && videoElement.duration > start) {
            videoElement.currentTime = start;
        }
    });

    elements.video.playerContainer.innerHTML = '';
    elements.video.playerContainer.appendChild(videoElement);

    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') {
            embedYouTubePlayer(mediaInfo.id, start, 'video');
        } else if (mediaInfo.platform === 'vimeo') {
            embedVimeoPlayer(mediaInfo.id, start, 'video');
        }
        // Loading will be handled inside embed functions
    } 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 local video file.', 'error');
    hideLoading(elements.video.playerContainer);
}

checkReadyToPlay();
saveSettings();
}

// Play Handler
function handlePlay() {
// Play Music
if (state.music) {
if (state.music.type === 'file') {
state.music.element.play();
if (state.fullscreenOnPlay) {
enterFullscreen(state.music.element);
}
} else if (state.music.type === 'vimeo') {
state.music.element.play().catch(error => {
console.error('Error playing Vimeo audio:', error);
showMessage('Failed to play Vimeo audio.', 'error');
});
} else if (state.music.type === 'youtube') {
sendMessageToYouTubeSandbox({ type: 'play' });
}
}


// Play Video
if (state.video) {
    if (state.video.type === 'file') {
        state.video.element.play();
        if (state.fullscreenOnPlay) {
            enterFullscreen(state.video.element);
        }
    } else if (state.video.type === 'vimeo') {
        state.video.element.play().catch(error => {
            console.error('Error playing Vimeo video:', error);
            showMessage('Failed to play Vimeo video.', 'error');
        });
    } else if (state.video.type === 'youtube') {
        sendMessageToYouTubeSandbox({ type: 'play' });
    }
}

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

// Pause Handler
function handlePause() {
if (state.isPaused) return;


// Pause Music
if (state.music) {
    if (state.music.type === 'file') {
        state.music.element.pause();
    } else if (state.music.type === 'vimeo') {
        state.music.element.pause().catch(error => {
            console.error('Error pausing Vimeo audio:', error);
            showMessage('Failed to pause Vimeo audio.', 'error');
        });
    } else if (state.music.type === 'youtube') {
        sendMessageToYouTubeSandbox({ type: 'pause' });
    }
}

// Pause Video
if (state.video) {
    if (state.video.type === 'file') {
        state.video.element.pause();
    } else if (state.video.type === 'vimeo') {
        state.video.element.pause().catch(error => {
            console.error('Error pausing Vimeo video:', error);
            showMessage('Failed to pause Vimeo video.', 'error');
        });
    } else if (state.video.type === 'youtube') {
        sendMessageToYouTubeSandbox({ type: 'pause' });
    }
}

state.isPaused = true;
elements.controls.resumeButton.disabled = false;
elements.controls.pauseButton.disabled = true;
elements.controls.pauseButton.textContent = 'Paused';
showMessage('Playback paused.', 'info');
}

// Resume Handler
function handleResume() {
if (!state.isPaused) return;


// Resume Music
if (state.music) {
    if (state.music.type === 'file') {
        state.music.element.play();
        if (state.fullscreenOnPlay) {
            enterFullscreen(state.music.element);
        }
    } else if (state.music.type === 'vimeo') {
        state.music.element.play().catch(error => {
            console.error('Error resuming Vimeo audio:', error);
            showMessage('Failed to resume Vimeo audio.', 'error');
        });
    } else if (state.music.type === 'youtube') {
        sendMessageToYouTubeSandbox({ type: 'play' });
    }
}

// Resume Video
if (state.video) {
    if (state.video.type === 'file') {
        state.video.element.play();
        if (state.fullscreenOnPlay) {
            enterFullscreen(state.video.element);
        }
    } else if (state.video.type === 'vimeo') {
        state.video.element.play().catch(error => {
            console.error('Error resuming Vimeo video:', error);
            showMessage('Failed to resume Vimeo video.', 'error');
        });
    } else if (state.video.type === 'youtube') {
        sendMessageToYouTubeSandbox({ type: 'play' });
    }
}

state.isPaused = false;
elements.controls.resumeButton.disabled = true;
elements.controls.pauseButton.disabled = false;
elements.controls.pauseButton.textContent = 'Pause';
showMessage('Playback resumed.', '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 by sending an initialization message to the sandboxed iframe.
@param {string} videoId - The YouTube video ID.
@param {number} start - The start time in seconds.
@param {string} mediaType - 'music' or 'video'.
*/
function embedYouTubePlayer(videoId, start, mediaType) {
const container = elements[mediaType].playerContainer;
container.innerHTML = ''; // Clear previous content showLoading(container); // Send initialization message to sandbox
sendMessageToYouTubeSandbox({
type: 'init',
videoId: videoId,
startTime: start,
mediaType: mediaType
}); state[mediaType] = {
type: 'youtube',
element: null, // Will be set when sandbox responds
start: start
};
}

/**

Embeds a Vimeo video using the Player API.
@param {string} videoId - The Vimeo video ID.
@param {number} start - The start time in seconds.
@param {string} mediaType - 'music' or 'video'.
*/
function embedVimeoPlayer(videoId, start, mediaType) {
const container = elements[mediaType].playerContainer;
container.innerHTML = ''; // Clear previous content const iframe = document.createElement('iframe');
const src = https://player.vimeo.com/video/${videoId}?muted=1&autoplay=0&start=${start};
iframe.src = src;
iframe.width = '640';
iframe.height = '360';
iframe.frameBorder = '0';
iframe.allow = 'autoplay; fullscreen; picture-in-picture';
iframe.setAttribute('title', 'Vimeo video player'); container.appendChild(iframe); const player = new Vimeo.Player(iframe, {
id: videoId,
width: 640,
muted: true,
autoplay: false,
start: start
}); state[mediaType] = {
type: 'vimeo',
element: player,
start: start
}; // Buffering: play briefly, then pause and set start time
bufferAndPause('vimeo', mediaType, start); hideLoading(container);
}

/**

Implements the buffering process: plays briefly, then pauses and sets to start time.
@param {string} platform - 'youtube' or 'vimeo'.
@param {string} mediaType - 'music' or 'video'.
@param {number} startTime - The start time in seconds.
*/
function bufferAndPause(platform, mediaType, startTime) {
if (platform === 'vimeo') {
const player = state[mediaType].element;
player.play().then(() => {
// Wait for at least 125ms
setTimeout(() => {
player.pause().then(() => {
player.setCurrentTime(startTime).then(() => {
showMessage(Vimeo ${capitalize(mediaType)} buffered and set to ${startTime} seconds., 'success');
}).catch(error => {
console.error(Error setting start time for Vimeo ${mediaType}:, error);
showMessage(Failed to set start time for Vimeo ${mediaType}., 'error');
});
}).catch(error => {
console.error(Error pausing Vimeo ${mediaType}:, error);
showMessage(Failed to pause Vimeo ${mediaType}., 'error');
});
}, 125);
}).catch(error => {
console.error(Error playing Vimeo ${mediaType}:, error);
showMessage(Failed to play Vimeo ${mediaType}., 'error');
});
}
}
// === Settings Persistence ===

/**

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') {
// No direct access to destroy YouTube player from main
// Send a message to sandbox to destroy player if needed
// For simplicity, reload the sandbox iframe
const iframe = document.getElementById('youtube-sandbox-iframe');
iframe.src = iframe.src;
} else if (state[mediaType].type === 'vimeo') {
const player = state[mediaType].element;
player.unload().catch(error => {
console.error(Error unloading Vimeo ${mediaType}:, error);
});
}
state[mediaType] = null;
}
}
// === Playback Controls ===

/**

Enables or disables playback controls based on media loading state.
*/
function checkReadyToPlay() {
// Enable buttons 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.pauseButton.disabled = !ready;
elements.controls.resumeButton.disabled = true; // Start with Resume disabled
}

/**

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(?:?v=|v/)|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
let videoId = '';
if (hostname === 'youtu.be') {
videoId = pathname.slice(1);
} else {
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;
}
}

/**

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);
}
/**

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 && element.parentElement.requestFullscreen) {
// Fallback to parent element
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');
}
}
/**

Sends a message to the YouTube sandbox iframe.
@param {Object} message - The message object to send.
*/
function sendMessageToYouTubeSandbox(message) {
sendMessageToYouTubeSandboxFrame(message);
}
/**

Directly sends a message to the YouTube sandbox iframe.
@param {Object} message - The message object to send.
/
function sendMessageToYouTubeSandboxFrame(message) {
const iframe = document.getElementById('youtube-sandbox-iframe');
iframe.contentWindow.postMessage(message, '');
}
// === YouTube Sandbox Communication Handlers ===

// No additional code here as messaging is handled via event listeners above

// === Initialize ready state ===
checkReadyToPlay();

// Ensure buttons are correctly enabled/disabled on load
if (state.music || state.video) {
elements.controls.playButton.disabled = false;
}
});

/* styles.css */

/* 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: 1200px; /* Increased max-width for larger displays */
margin: 0 auto;
}

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

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

.settings-button {
position: absolute;
left: 0;
background: #fff;
border: 1px solid #ccc;
border-radius: 5px;
padding: 8px;
cursor: pointer;
width: 50px; /* Increased size for better touch targets /
height: 50px; / Increased size for better touch targets */
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: 24px;
height: 24px;
}

/* Main Title with Animation /
.main-title {
font-size: 3em; / Increased font size for better visibility */
font-weight: bold;
margin: 0;
animation: titleAnimation 3s infinite;
}

/* Title Animation Keyframes */
@keyframes titleAnimation {
0% { color: #4285f4; transform: scale(1); }
50% { color: #ea4335; transform: scale(1.05); }
100% { color: #4285f4; transform: scale(1); }
}

/* Section Headers */
.section > fieldset > legend {
font-size: 1.5em;
font-weight: bold;
margin-bottom: 15px;
}

/* Controls /
.controls {
display: flex;
gap: 15px; / Increased gap for better spacing /
margin-bottom: 30px; / Increased margin for better spacing */
}

.controls button {
padding: 15px 30px; /* Increased padding for better accessibility /
cursor: pointer;
border: none;
border-radius: 8px;
background-color: #4285f4;
color: #fff;
transition: background-color 0.3s ease;
font-size: 1.2em; / Increased font size for better readability */
}

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

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

/* Play Button Specific Styles /
.play-button {
background-color: #34a853; / Green */
}

.play-button:hover:not(:disabled) {
background-color: #2c8e46;
}

/* Pause Button Specific Styles /
.pause-button {
background-color: #fbbc05; / Yellow */
}

.pause-button:hover:not(:disabled) {
background-color: #e0a800;
}

/* Resume Button Specific Styles /
.resume-button {
background-color: #ea4335; / Red */
}

.resume-button:hover:not(:disabled) {
background-color: #c33c2c;
}

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

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

.modal-content {
background-color: #fff;
padding: 25px 35px; /* Increased padding for better spacing /
border-radius: 10px; / More pronounced rounding /
width: 90%;
max-width: 600px; / Increased max-width for better layout */
position: relative;
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
}

.close-button {
position: absolute;
top: 15px;
right: 20px;
background: none;
border: none;
font-size: 28px;
cursor: pointer;
color: #333;
}

.modal-content h2 {
margin-top: 0;
font-size: 2em; /* Increased font size for better visibility */
}

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

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

fieldset input[type="text"],
fieldset input[type="number"],
fieldset input[type="file"] {
width: 100%;
padding: 12px; /* Increased padding for better usability /
margin-bottom: 20px; / Increased margin for better spacing /
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 6px;
font-size: 1em; / Increased font size for better readability */
}

fieldset .load-button {
width: 100%;
padding: 15px;
background-color: #4285f4; /* Blue /
color: #fff;
border: none;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.3s ease;
font-size: 1.2em; / Increased font size for better readability */
}

fieldset .load-button:hover {
background-color: #357ae8;
}

/* Player Containers /
.player-container {
margin-top: 10px;
width: 100%;
max-width: 100%;
min-height: 120px; / Increased minimum height for better visibility /
box-sizing: border-box;
overflow: hidden; / Prevent overflow */
position: relative;
}

/* Responsive Video and Audio Elements /
.player-container video,
.player-container iframe {
width: 100%;
height: 360px; / Fixed height for larger displays */
border-radius: 8px;
display: block;
}

.player-container audio {
width: 100%;
height: auto;
border-radius: 5px;
margin-top: 10px; /* Increased margin for better spacing */
}

/* Messages /
.message-container {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 1001; / Above other content */
width: 90%;
max-width: 700px;
}

.message {
padding: 15px 25px;
border-radius: 8px;
margin-bottom: 15px;
color: #fff;
font-size: 1.1em;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
opacity: 0.95;
animation: fadeInOut 6s forwards;
}

.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: 80px; / Increased size for better visibility /
height: 80px; / Increased size for better visibility */
animation: spin 1s linear infinite;
margin: 0 auto;
}

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

/* Fade In and Out for Messages */
@keyframes fadeInOut {
0% { opacity: 0; transform: translateY(-20px); }
10% { opacity: 0.95; transform: translateY(0); }
90% { opacity: 0.95; transform: translateY(0); }
100% { opacity: 0; transform: translateY(-20px); }
}

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

.controls {
flex-direction: column;
gap: 10px;
}

.controls button {
width: 100%;
}

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

.player-container video,
.player-container iframe {
height: 280px; /* Adjusted height for smaller screens */
}

.loader {
width: 60px; /* Adjusted size for smaller screens /
height: 60px; / Adjusted size for smaller screens */
}
}

@media (max-width: 700px) {
.section {
padding: 15px;
}

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

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

.player-container video,
.player-container iframe {
height: 220px; /* Further adjusted height for even smaller screens */
}

.loader {
width: 50px; /* Further adjusted size for even smaller screens /
height: 50px; / Further adjusted size for even smaller screens */
}
}

@media (max-width: 500px) {
.container {
padding: 15px;
}

.controls button {
padding: 12px;
font-size: 1em;
}

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

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

.player-container video,
.player-container iframe {
height: 180px; /* Further adjusted height for very small screens */
}

.loader {
width: 40px; /* Further adjusted size for very small screens /
height: 40px; / Further adjusted size for very small screens */
}
}

Errors
Refused to load the script 'https://www.youtube.com/iframe_api' because it violates the following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
Context
dashboard.html
Stack Trace
youtube-sandbox.html:0 (anonymous function)

To address the Content Security Policy (CSP) error you're encountering, you'll need to adjust your Chrome extension's manifest.json to permit loading the YouTube IFrame API. Additionally, since Chrome extensions enforce strict CSPs that disallow inline scripts, you'll need to externalize any inline scripts present in your youtube-sandbox.html.

Below are the complete updated files with the necessary modifications:

manifest.json
Changes Made:
Updated content_security_policy to allow scripts from https://www.youtube.com/iframe_api.
Externalized scripts by removing any inline script allowances.

{
"manifest_version": 3,
"name": "Media Loader",
"version": "1.4",
"description": "Load and play music and videos from YouTube and Vimeo",
"permissions": ["storage", "tabs"],
"host_permissions": [
"https://www.youtube.com/",
"https://youtu.be/",
"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' https://www.youtube.com/iframe_api; object-src 'self'; frame-src https://www.youtube.com https://youtu.be https://player.vimeo.com"
},
"web_accessible_resources": [
{
"resources": ["lib/player.js"],
"matches": [""]
},
{
"resources": ["youtube-sandbox.html", "sandbox.js"],
"matches": [""]
}
]
}
2. youtube-sandbox.html
Changes Made:

Removed inline scripts and moved them to an external sandbox.js file.
Included the external sandbox.js script to handle all YouTube player functionalities.


<!-- External Sandbox Script -->
<script src="sandbox.js"></script>
3. sandbox.js New File Created:
Externalized the previously inline JavaScript code from youtube-sandbox.html to comply with CSP restrictions.

"use strict";

let player;
let mediaType = ''; // 'music' or 'video'

// Function called by YouTube API once it's ready
function onYouTubeIframeAPIReady() {
// Player will be created upon receiving initialization message
}

// Listen for messages from the parent
window.addEventListener('message', (event) => {
// For security, you can check event.origin here if necessary
const data = event.data;
if (data.type === 'init') {
mediaType = data.mediaType; // 'music' or 'video'
player = new YT.Player('youtube-player', {
height: '360',
width: '640',
videoId: data.videoId,
playerVars: {
'autoplay': 0,
'controls': 1,
'start': data.startTime,
'mute': 1,
'rel': 0,
'disablekb': 1,
'modestbranding': 1,
'iv_load_policy': 3,
'fs': 1
},
events: {
'onReady': () => {
// Notify the parent that the player is ready
window.parent.postMessage({
type: 'playerReady',
mediaType: mediaType
}, '');
},
'onError': (error) => {
window.parent.postMessage({
type: 'playerError',
mediaType: mediaType,
error: error
}, '');
}
}
});
} else if (data.type === 'play') {
if (player && typeof player.playVideo === 'function') {
player.playVideo();
}
} else if (data.type === 'pause') {
if (player && typeof player.pauseVideo === 'function') {
player.pauseVideo();
}
} else if (data.type === 'seek') {
if (player && typeof player.seekTo === 'function') {
player.seekTo(data.time, true);
}
}
});

// Load YouTube IFrame API
const script = document.createElement('script');
script.src = 'https://www.youtube.com/iframe_api';
document.head.appendChild(script);
4. Additional Notes
Permissions: Ensure that all required permissions are correctly specified in the manifest.json. The updated CSP allows the extension to load the YouTube IFrame API without violating security policies.
Security Considerations: Allowing external scripts can introduce security vulnerabilities. Make sure to validate and sanitize all inputs and communications between your extension and external sources. Always verify the origin of messages in postMessage handlers to prevent potential cross-site scripting (XSS) attacks.
Testing: After making these changes, reload your extension in Chrome to apply the updates. Monitor the Chrome Developer Console for any further CSP-related issues or other errors that may arise.
Alternative Approaches: If you continue to face CSP issues or want to enhance security, consider proxying YouTube API requests through your extension or utilize Chrome's native APIs to manage media playback without relying on external scripts.
By implementing these updates, your extension should now successfully load and utilize the YouTube IFrame API without violating Chrome's Content Security Policy. Ensure you thoroughly test all functionalities to confirm that media loading and playback operate as expected.

Failed to load extension
File
~\Desktop\myext
Error
'content_security_policy.extension_pages': Insecure CSP value "https://www.youtube.com/iframe_api" in directive 'script-src'.
Could not load manifest.


You're encountering a Content Security Policy (CSP) issue because Manifest Version 3 (MV3) for Chrome extensions enforces stricter CSP rules, disallowing the inclusion of external scripts such as the YouTube IFrame API directly in your extension's pages. Specifically, MV3 does not permit adding external sources like https://www.youtube.com/iframe_api to the script-src directive in manifest.json.

To resolve the CSP error and ensure your extension complies with MV3, you'll need to remove external script sources from your manifest.json and externalize all scripts within your extension. However, this means you cannot use external scripts like the YouTube IFrame API directly. Instead, you'll need to adapt your approach to embed YouTube videos without relying on external APIs.

Below are the complete updated files with the necessary modifications to address the CSP issues:

1. Updated manifest.json
Changes Made:
Removed External Script Sources: Removed the https://www.youtube.com/iframe_api from the script-src directive because MV3 disallows adding external scripts.
Adjusted host_permissions: Ensured that host permissions include necessary YouTube and Vimeo URLs.
Updated web_accessible_resources: Included sandbox.js as a web-accessible resource to facilitate communication between the extension and the sandboxed iframe.
Updated manifest.json:

{
  "manifest_version": 3,
  "name": "Media Loader",
  "version": "1.4",
  "description": "Load and play music and videos from YouTube and Vimeo",
  "permissions": ["storage", "tabs"],
  "host_permissions": [
    "https://www.youtube.com/*",
    "https://youtu.be/*",
    "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'; frame-src https://www.youtube.com https://youtu.be https://player.vimeo.com"
  },
  "web_accessible_resources": [
    {
      "resources": ["lib/player.js"],
      "matches": ["<all_urls>"]
    },
    {
      "resources": ["youtube-sandbox.html", "sandbox.js"],
      "matches": ["<all_urls>"]
    }
  ]
}
2. Updated youtube-sandbox.html
Changes Made:
Removed External Script: Eliminated the external YouTube IFrame API script (https://www.youtube.com/iframe_api) to comply with the CSP.
Included Local sandbox.js: Added a local sandbox.js script to handle YouTube player interactions.
Updated youtube-sandbox.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>YouTube Sandbox</title>
</head>
<body>
    <!-- YouTube Player Container -->
    <div id="youtube-player"></div>

    <!-- External Sandbox Script -->
    <script src="sandbox.js"></script>
</body>
</html>
3. New sandbox.js
Purpose:
Handles the creation and control of the YouTube player within the sandboxed iframe without relying on external scripts. It uses postMessage to communicate with the parent extension.

sandbox.js:

"use strict";

let mediaType = ''; // 'music' or 'video'
let player = null;

// Listen for messages from the parent
window.addEventListener('message', (event) => {
    // Optionally, verify event.origin for security
    const data = event.data;

    if (data.type === 'init') {
        mediaType = data.mediaType; // 'music' or 'video'
        createYouTubePlayer(data.videoId, data.startTime);
    } else if (data.type === 'play') {
        playVideo();
    } else if (data.type === 'pause') {
        pauseVideo();
    } else if (data.type === 'seek') {
        seekVideo(data.time);
    }
});

/**
 * Creates a YouTube player iframe without using the IFrame API.
 * Limited control is possible through postMessage.
 * @param {string} videoId - The YouTube video ID.
 * @param {number} startTime - The start time in seconds.
 */
function createYouTubePlayer(videoId, startTime) {
    // Clear any existing content
    const container = document.getElementById('youtube-player');
    container.innerHTML = '';

    // Create the iframe
    const iframe = document.createElement('iframe');
    iframe.id = 'youtubeIframe';
    iframe.width = '640';
    iframe.height = '360';
    iframe.src = `https://www.youtube.com/embed/${videoId}?start=${startTime}&enablejsapi=1`;
    iframe.frameBorder = '0';
    iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture';
    iframe.allowFullscreen = true;

    container.appendChild(iframe);

    // Notify the parent that the player is ready
    iframe.onload = () => {
        window.parent.postMessage({
            type: 'playerReady',
            mediaType: mediaType
        }, '*');
    };
}

/**
 * Sends a play command to the YouTube iframe.
 */
function playVideo() {
    const iframe = document.getElementById('youtubeIframe');
    if (iframe && iframe.contentWindow) {
        iframe.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}', '*');
    }
}

/**
 * Sends a pause command to the YouTube iframe.
 */
function pauseVideo() {
    const iframe = document.getElementById('youtubeIframe');
    if (iframe && iframe.contentWindow) {
        iframe.contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}', '*');
    }
}

/**
 * Sends a seek command to the YouTube iframe.
 * @param {number} time - The time in seconds to seek to.
 */
function seekVideo(time) {
    const iframe = document.getElementById('youtubeIframe');
    if (iframe && iframe.contentWindow && typeof time === 'number') {
        iframe.contentWindow.postMessage(`{"event":"command","func":"seekTo","args":[${time}, true]}`, '*');
    }
}
Notes:
Limited Control: Without the YouTube IFrame API, control over the player is limited to basic functionalities like play, pause, and seek through postMessage. Advanced controls and event handling are not possible.
No API Features: Features provided by the YouTube IFrame API, such as tracking playback status, handling events (like video end), and other advanced controls, are unavailable.
4. Updated dashboard.js
Since the YouTube IFrame API is no longer being used, ensure that all interactions with the sandbox are compatible with the new sandbox.js implementation.

Updated dashboard.js (Only Relevant Sections):

"use strict";

let youtubePlayers = {
    music: null,
    video: null
};

// Function to send messages to the YouTube sandbox iframe
function sendMessageToYouTubeSandbox(message) {
    const iframe = document.getElementById('youtube-sandbox-iframe');
    iframe.contentWindow.postMessage(message, '*');
}

document.addEventListener('DOMContentLoaded', () => {
    // ... [Existing Code] ...

    // Load YouTube Player
    function embedYouTubePlayer(videoId, start, mediaType) {
        const container = elements[mediaType].playerContainer;
        container.innerHTML = ''; // Clear previous content

        // Create iframe pointing to youtube-sandbox.html
        const iframe = document.createElement('iframe');
        iframe.id = 'youtube-sandbox-iframe';
        iframe.src = chrome.runtime.getURL('youtube-sandbox.html');
        iframe.width = '640';
        iframe.height = '360';
        iframe.frameBorder = '0';
        iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture';
        iframe.allowFullscreen = true;

        container.appendChild(iframe);

        // Wait for the sandbox iframe to load and send initialization message
        iframe.onload = () => {
            sendMessageToYouTubeSandbox({
                type: 'init',
                videoId: videoId,
                startTime: start,
                mediaType: mediaType
            });
        };

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

    // ... [Existing Code] ...
});
Notes:
Iframe Embedding: The YouTube player is now embedded within a sandboxed iframe (youtube-sandbox.html) that does not rely on external scripts.
Communication via postMessage: Controls like play, pause, and seek are sent to the sandboxed iframe using postMessage.
5. Summary of Changes
Removed External Scripts: Eliminated references to external scripts, specifically the YouTube IFrame API, to comply with MV3 CSP.
Externalized Scripts: Moved all scripts into local files within the extension (e.g., sandbox.js) and ensured they are referenced properly.
Limited Player Control: Adjusted the YouTube player embedding to work without the IFrame API, allowing only basic controls via postMessage.
Updated Communication: Ensured that communication between the extension and the sandboxed iframe uses structured messages compatible with the new setup.
6. Important Considerations & Limitations
Reduced Functionality: Without the YouTube IFrame API, you lose access to advanced player controls and event handling. Only basic functionalities like play, pause, and seek are achievable.
Security: Ensure that messages are validated and origins are checked where possible to maintain security, especially when using postMessage.
User Experience: The limited control may affect the user experience. Consider whether the current level of functionality meets your extension's requirements.
Alternative Approaches: If advanced control over YouTube videos is essential, you might need to explore alternative methods, such as building a companion web service to handle YouTube interactions, though this adds complexity.
7. Final Thoughts
Chrome's Manifest Version 3 emphasizes security by enforcing strict CSP rules, which unfortunately restricts the inclusion of external scripts like the YouTube IFrame API. While the above modifications allow your extension to function without violating CSP, they also limit the capabilities related to YouTube video control.

If advanced interaction with YouTube videos is a core feature of your extension, you may need to consider redesigning certain aspects or exploring other platforms that offer more flexibility with CSP. Always ensure that any modifications adhere to Chrome's extension policies to maintain both functionality and security.

If you have any specific functionalities you wish to retain or encounter further issues, feel free to provide additional details, and I can assist you in finding alternative solutions.

the load video and load audio and settings buttons dont seem to do anything.