Я пытаюсь разработать расширение для Chrome, которое будет работать на youtube.com, и поэтому мне нужен доступ к API-интерфейсу плеера iframe.
Сначала я думал, что это проблема перекрестного происхождения, но источником является youtube.com, поэтому я не уверен, что это проблема.
раздражает то, что другое расширение под названием TubeBuddy, кажется, делает именно то, что я хочу, поэтому я знаю, что это возможно!
Попытка 1
Я попытался внедрить API-интерфейс iframe YouTube, как показано в документации здесь: https://developers.google.com/youtube/player_parameters
В моем манифесте у меня есть следующее:
{
"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"
}
где bundle.js содержит код для создания тега сценария, который должен вызывать 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
}
});
};
Я могу заметить, что тег script создается в DOM на странице, но никогда не вызывается window.onYoutubeIframeAPIReady, что, как я полагаю, связано с тем, что расширение chrome не имеет доступа к объекту окна.
Я пытался следовать этим ответам, но безрезультатно:
API проигрывателя Youtube в расширении Chrome
YouTube видео в расширении Chrome сценарий содержания
Обнаружение видео-событий на YouTube с помощью скрипта с расширением Chrome
API iframe для YouTube: OnReady не запускает расширение Chrome
Попытка 2
Я пытался использовать API sendMessage для внедрения объекта YT в content_scripts, но это не работает, так как обратный вызов sendResponse лишает методы и функции из-за сериализации JSON.
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)
}
}
})
Попытка 3
Я также попытался внедрить скрипт в dom, как это предлагается в этом ответе Расширение Chrome - получение глобальной переменной со страницы
В inject.js у меня есть следующее:
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();
};
и в 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);
}
})();
и в моем bundle.js:
window.addEventListener('RW759_connectExtension', function(e) {
console.log('e.youtubeLoaded', e); // null
console.log('window.YT', window.YT); // null
});
Я ожидал, что прослушиватель событий вернет объект YT, но он будет возвращен как ноль.
Если у кого-то есть предложения или вопросы, пожалуйста, задавайте :) Буду очень признателен за помощь с этим