Youtube 是一个现代网站,它仅使用来自服务器的数据来更新页面的一部分。这意味着只有一个完整的页面加载,在此期间ytplayer 是在内联<script> 标记中构建的。这也意味着您的内容脚本只运行一次。
建议的解决方案是通过覆盖page context中的XHR open方法来拦截站点的网络通信,以获取新的ytplayer对象。
manifest.json:
"content_scripts": [{
"run_at": "document_start",
"matches": ["https://www.youtube.com/*"],
"js": ["content.js"]
}]
content.js:
const token = chrome.runtime.id + ':' + performance.now() + ':' + Math.random();
window.addEventListener(token, e => {
console.log('gotPlayerArgs', e.detail);
chrome.runtime.sendMessage({
action: 'gotPlayerArgs',
data: e.detail,
});
});
const script = document.createElement('script');
script.textContent = '(' + (token => {
const origOpen = XMLHttpRequest.prototype.open;
const dispatch = data => window.dispatchEvent(new CustomEvent(token, {detail: data}));
const onLoad = e => {
const json = e.target.response;
const player = (Array.isArray(json) && json.find(_ => _.player) || {}).player || {};
dispatch(player.args);
};
// get the initial config
try {
dispatch(window.ytplayer.config.args);
} catch (e) {}
// intercept the subsequent config queries
XMLHttpRequest.prototype.open = function (method, url) {
if (url.startsWith('https://www.youtube.com/watch?')) {
this.addEventListener('load', onLoad);
}
return origOpen.apply(this, arguments);
};
}) + `)("${token}")`;
document.documentElement.appendChild(script);
script.remove();
仍然存在一个警告:如果 (1) 您的扩展程序已更新/重新加载或禁用/重新启用,并且 (2) youtube 页面不是第一个导航,则初始配置对象将是错误的。要解决此问题,您可以将视频的 id(从 URL 中提取)与配置中的 id(例如“loaderUrl”属性)进行比较,如果不匹配,只需通过 get_video_info 端点获取 args,即易于解析(由& 拆分,然后由= 拆分,然后使用decodeURIComponent):
'https://www.youtube.com/get_video_info?video_id=' + id + '&hl=en_US&html5=1&el=embedded'