Как обнаружить навигацию по страницам на YouTube и изменить HTML перед отображением страницы?

Я делаю простое расширение для Chrome, чтобы добавить длину каждого видео в плейлист YouTube и указать общую длину на странице. Мне это удалось, но мой сценарий работает только после обновления страницы, но не при переходе по сайту. Это не очень удобно, хотя.

Можно ли обнаружить навигацию по страницам на YouTube и вставить HTML-код на страницу до того, как она будет отображена в браузере, чтобы добавленное содержимое отображалось немедленно без каких-либо задержек и не требовалось обновление страницы?

Пример ссылки: https://www.youtube.com/playlist?list=PL_8APVyhfhpdgMJ3J80YQxWBMUhbwXw8B

PS Мой вопрос не совпадает с изменением элементов сразу после их отображения (не после полной загрузки страницы) в скрипте greasemonkey? потому что я пробовал MutationObserver и проблема та же - требуется обновление, чтобы показать изменения на веб-странице:

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});

Ответ 1

Сайт Youtube не перезагружает страницы при навигации, он заменяет состояние истории.

Сценарии расширения содержимого не повторно вводятся при изменении URL-адреса без перезагрузки страницы. Естественно, когда вы перезагружаете страницу вручную, выполняется скрипт содержимого.

Существует несколько способов обнаружения переходов страниц на сайте Youtube:

  • использование фонового скрипта страницы: API webNavigation, API вкладок
  • использование сценария содержимого: событие transitionend для индикатора выполнения на страницах с видео
  • используя скрипт контента и специфичное для сайта событие, запускаемое при видеонавигации:

    Запустите getEventListeners(window) в консоли devtools и проверьте вывод.

    enter image description here

    yt-navigate-start - это то, что нам нужно.
    Обратите внимание, что старый дизайн YouTube все еще показан в некоторых случаях с использованием события spfdone


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"
  }]
}

Обратите внимание, что когда мы загружаем скрипт контента в document_start он заставляет нашего слушателя DOMContentLoaded запускаться немного раньше по сравнению с поведением по умолчанию, когда скрипты контента вводятся незначительно после DOMContentLoaded. С document_start скрипт контента запускается, когда в body документа ничего нет и даже нет элемента head. Присоединить слушателя к document можно, однако, точно так же.

content.js:

window.addEventListener("spfdone", process); // old youtube design
window.addEventListener("yt-navigate-start", process); // new youtube design

document.addEventListener("DOMContentLoaded", process); // one-time early processing
window.addEventListener("load", postProcess); // one-time late postprocessing 

Функция process изменит страницу.
Обратите внимание, что указанные классы элементов и структура изменится в будущем.

function process() {
    if (location.pathname != "/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>");
}

Функция postProcess которую мы прикрепили при load будет запускаться только один раз при открытии сайта. Используйте его для однократной обработки страницы после загрузки всех ее скриптов.

Ответ 2

Ответ 2017 года:

Я использую это для новой версии Material Design Youtube

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

и это для старого Youtube

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

код пришел из 2 сценария я написал вызов
"Youtube загрузчик субтитров" и "Youtube автоматический загрузчик субтитров".

оба они работают, я проверял.

если вы заинтересованы в моем сценарии и хотите узнать более подробно:
https://github.com/1c7/Youtube-Auto-Subtitle-Download