为什么在 youtube 视频之间导航时 youtube ytplayer 对象不更新?

Why does the youtube ytplayer object not update when navigating between youtube videos?

我正在开发一个 chrome 扩展,它使用我的 contentscript.js 文件中 youtube 页面中的 ytplayer 对象。过去,每次您导航到新的 YouTube 视频时,ytplayer 对象都会更新。截至目前,我没有看到它再更新。

如何复制

  1. 转到 chrome 中的 YouTube 视频页面(例如,https://www.youtube.com/watch?v=KUh2O8HylUM
  2. 打开开发工具控制台
  3. 在控制台输入'ytplayer.config.args'
  4. 捕获 ytplayer.config.args 对象
  5. 点击推荐列表中的另一个视频,导航到另一个 YouTube 视频。
  6. 再次输入ytplayer.config.args
  7. 与之前捕获的 ytplayer.config.args 相比

您会看到 ytplayer 在导航到第二个视频后没有更新。我的扩展使用这个对象。问题是导航到另一个视频后该对象已过时。当我在每次视频导航后拉出 ytplayer 对象时,我会显示错误的数据,因为 ytplayer 始终来自第一个视频。

问题。

  1. 有没有办法获取更新后的 ytplayer?

  2. 有没有办法让 ytplayer 重新加载? (我可以在我的 js 代码中添加一个 location.reload() 来解决这个问题,但它很笨拙)

  3. 还有其他方法可以从 chrome 中的 YouTube 视频页面获取 ytplayer.conf.args 数据吗?

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) 您的扩展程序是 updated/reloaded 或 disabled/re-enabled,并且 (2) youtube 页面不是第一个导航,则初始配置对象将是错误的。要解决此问题,您可以将视频的 ID(从 URL 中提取)与配置中的 ID(例如 "loaderUrl" 属性)进行比较,如果没有match 只需通过 get_video_info 端点获取 args,这很容易解析(按 &,然后按 =,然后使用 decodeURIComponent): 'https://www.youtube.com/get_video_info?video_id=' + id + '&hl=en_US&html5=1&el=embedded'