I am trying to develop a chrome extension that will be run on youtube.com and as such i need access to the iframe player api. I thought it was a cross origin problem at first but the origin is youtube.com so not sure that is the issue.
the annoying thing is that another extension called TubeBuddy seems to do exactly what I want so I know it's possible!
Attempt 1 I have tried to implement youtube iframe api as found in the docs here: https://developers.google.com/youtube/player_parameters
In my manifest I have the following:
{
"short_name": "Test app",
"name": "TestApp",
"manifest_version": 2,
"description": "description",
"background":
{
"page": "./background_scripts/background.html",
"persistent": true
},
"chrome_url_overrides":
{
"newtab": "./new_tab/newtab.html"
},
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*",
"file:///*"
],
"all_frames": true,
"js": [
"./inject_scripts/react_inject.js",
"./inject_scripts/bundle.js",
"./libraries/jquery.min.js",
"./libraries/jquery-ui.min.js",
"./inject_scripts/inject.js"
],
"css": [
"./inject_scripts/inject.css",
"./libraries/jquery-ui.min.css"
]
}],
"permissions": [
"https://*.youtube.com/*",
"chrome://favicon/",
"clipboardRead",
"clipboardWrite",
"cookies",
"idle",
"notifications",
"tabs",
"topSites",
"bookmarks",
"identity",
"identity.email",
"storage",
"unlimitedStorage",
"management",
"tts",
"tabs",
"power",
"activeTab"
],
"externally_connectable":
{
"matches": ["*://*.youtube.com/*"]
},
"content_security_policy": "script-src 'self' https://www.youtube.com/ https://s.ytimg.com; object-src 'self'; child-src https://www.youtube.com/ https://s.ytimg.com",
"options_ui":
{
"page": "./options/options.html",
"open_in_tab": false
},
"web_accessible_resources": [
"assets/img/*.svg",
"assets/img/*.png",
"https://www.youtube.com/"
],
"version": "2.2.3"
}
where bundle.js contains the code to create a script tag which is supposed to call the iframe_api
const tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
const firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
window.onYouTubeIframeAPIReady = function onYouTubeIframeAPIReady() {
player = new YT.Player('wb-player', {
height: '390',
width: '640',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
};
I can observe that the script tag gets created in the DOM on the page but the window.onYoutubeIframeAPIReady is never called, which i assume is because the chrome extension doesn't have access to the window object.
I have tried to follow these answers to no avail:
Youtube player API in Chrome extension
youtube video in chrome extension content script
Detect youtube video events with Chrome extension content script
Youtube iframe API: OnReady not firing for Chrome extension
Attempt 2 I have tried using the sendMessage API to inject the YT object into the content_scripts but this doesn't work either as the sendResponse callback strips methods and functions due to JSON serialization.
background.js:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.message === 'youtube_api') {
const tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
const firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// sendResponse('ready');
window.onYouTubeIframeAPIReady = function onYouTubeIframeAPIReady() {
setTimeout(() => {
sendResponse(YT); // strips methods and functions as sendResponse gets JSON serialized
}, 0)
}
}
})
Attempt 3
I have also attempted to inject a script into the dom as suggested by this answer Chrome extension - retrieving global variable from webpage
In inject.js I have the following:
var s = document.createElement('script');
s.src = chrome.extension.getURL('./inject_scripts/youtube_api.js');
(document.head || document.documentElement).appendChild(s);
s.onload = function() {
console.log(window.YT);
s.remove();
};
and in youtube_api.js:
(() => {
const tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
const firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// sendResponse('ready');
window.onYouTubeIframeAPIReady = function onYouTubeIframeAPIReady() {
console.log('player ready'); // called as it should be
console.log(YT); // defined as it should be
});
setTimeout(() => {
const event = new CustomEvent('RW759_connectExtension', {
detail: YT, // null
});
window.dispatchEvent(event);
}, 1000);
}
})();
and in my bundle.js:
window.addEventListener('RW759_connectExtension', function(e) {
console.log('e.youtubeLoaded', e); // null
console.log('window.YT', window.YT); // null
});
I expected the event listener to return the YT object but it is returned as null.
If anyone has any suggestions or questions please ask :) Would really appreciate some help with this