如何检测YouTube页面导航并无缝修改其外观?

29

我正在制作一个简单的Chrome扩展程序,用于计算YouTube播放列表中每个视频的长度,并在页面中插入总长度。我已经成功完成了这个功能,但是我的脚本只能在刷新页面后才能起作用,而不能在网站导航时起作用。虽然这不太方便。

是否可能在YouTube上检测页面导航并在浏览器呈现页面之前插入HTML,以便添加的内容立即显示,无需延迟,并且不需要刷新页面?

示例链接:https://www.youtube.com/playlist?list=PL_8APVyhfhpdgMJ3J80YQxWBMUhbwXw8B

P.S. 我的问题与修改元素显示后立即显示(而不是在页面完全加载后)的油猴脚本? 不同,因为我尝试过MutationObserver,但问题仍然存在- 需要刷新才能显示Web页面的更改:

var observer = new MutationObserver(function(mutations) {
    for (var i=0; i<mutations.length; i++) {
        var mutationAddedNodes = mutations[i].addedNodes;
        for (var j=0; j<mutationAddedNodes.length; j++) {
            var node = mutationAddedNodes[j];
            if (node.classList && node.classList.contains("timestamp")) {
                var videoLength = node.firstElementChild.innerText;
                observer.disconnect();    

                var lengthNode = document.createElement("li");
                var lengthNodeText = document.createTextNode(videoLength);
                lengthNode.appendChild(lengthNodeText);
                document.getElementsByClassName("pl-header-details")[0].appendChild(lengthNode);

                return;
            }
        }
    }
});
observer.observe(document, {childList: true, subtree: true});

嗯,我尝试过了,但它对我的目的没有用。我想要将组合视频长度插入页面的部分已经在每个视频的时间戳加载时加载,所以我有同样的问题,需要刷新页面才能显示注入的HTML。由于被标记为重复,我更新了我的问题。 - Lauren
刚刚尝试了一下,不行,那并没有解决问题。还需要刷新。 - Lauren
感谢您迄今为止的帮助。以下是脚本,底部还附有清单:http://pastie.org/private/yyc7piosnsna4yq7oymia - Lauren
2个回答

58

YouTube网站在导航时不重新加载页面,而是替换历史状态

当URL更改但未重新加载页面时,扩展的内容脚本不会被再次注入。当您手动重新加载页面时,内容脚本会被执行。

在YouTube网站上,有几种方法可以检测页面转换:

  • 使用背景页(或MV3服务工作者)脚本:webNavigation APItabs API

  • 使用内容脚本和现代Chrome中的navigatesuccess事件

  • 使用内容脚本和站点自身的视频导航事件

    因此,要找到它,让我们在开发者工具控制台上运行getEventListeners(document)

    enter image description here

    • yt-navigate-start是我们在这种情况下需要的。
    • yt-navigate-finish对其他情况可能更好。

manifest.json:

{
  "name": "YouTube Playlist Length",
  "version": "0.0.1",
  "manifest_version": 2,
  "description": ".............",
  "content_scripts": [{
      "matches": [ "*://*.youtube.com/*" ],
      "js": [ "content.js" ],
      "run_at": "document_start"
  }]
}

注意:`matches` 键包含整个 youtube.com 域,以便内容脚本在用户首次打开 YouTube 主页后导航到观看页面时运行。

content.js:

document.addEventListener('yt-navigate-start', process);
// Choose a different event depending on when you want to apply the change
// document.addEventListener('yt-navigate-finish', process);

if (document.body) process();
else document.addEventListener('DOMContentLoaded', process);

process函数将改变页面。
请注意,指定的元素类和结构将来可能会发生变化。

function process() {
  if (!location.pathname.startsWith('/playlist')) {
    return;
  }
  var seconds = [].reduce.call(
    document.getElementsByClassName('timestamp'),
    function (sum, ts) {
      var minsec = ts.textContent.split(':');
      return sum + minsec[0] * 60 + minsec[1] * 1;
    },
    0,
  );
  if (!seconds) {
    console.warn('Got no timestamps. Empty playlist?');
    return;
  }
  var timeHMS = new Date(seconds * 1000).toUTCString().split(' ')[4]
    .replace(/^[0:]+/, ''); // trim leading zeroes
  document.querySelector('.pl-header-details')
    .insertAdjacentHTML('beforeend', '<li>Length: ' + timeHMS + '</li>');
}

1
这个解决方案很好,但不幸的是只适用于YouTube旧版设计。 - Oleg Popov
网站上有一个新的 yt-navigate-start 事件,向一些用户展示。此外,我稍微概括了答案,以解释如何找到实际的事件。 - wOxxOm
1
你确定他们使用了 pushState 吗?几天前我因为一个问题(我会找回来作为这个问题的重复),快速查看了一下,但是覆盖 history.pushStatehistory.replaceState,以便在这些方法上包含事件发射器根本没有起作用,即使在页面加载之前我已经覆盖了。 - Kaiido
1
@Kaiido,是的,他们只是使用了黑客技巧,通过iframe来恢复原始API绑定:https://puu.sh/wRIZg/6a3ad30828.png - wOxxOm
我无法感谢你足够,这让我能够编写一个Firefox插件,将YouTube Shorts重定向到正确的YouTube布局中的实际格式。谢谢! - undefined
显示剩余4条评论

19

2017年的答案:

我将其用于新版Material Design的Youtube。

body.addEventListener("yt-navigate-finish", function(event) {
    // code here
});

并且这是针对旧版Youtube的

window.addEventListener("spfdone", function(e) {
    // code here
});

这段代码来自我编写的两个脚本:
"Youtube字幕下载器"和"Youtube自动字幕下载器"。

它们都能正常工作,我已经测试过了。

如果你对我的脚本感兴趣,并想了解更多详情:
https://github.com/1c7/Youtube-Auto-Subtitle-Download


值得注意的是,这个信息已经被现有的答案所涵盖(尽管它使用了 yt-navigate-start)。 - Xan
1
是的,这个回答没有讲原则,但直接给出了代码,我认为这个回答仍然有一定的价值,所以我还是会保留这个回答。谢谢提到它,@Xan。 :D - NamNamNam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接