Asked  7 Months ago    Answers:  5   Viewed   38 times

I'm making a simple Chrome extension to add up the length of each video in a YouTube playlist and insert the total length in the page. I've succeeded at that, but my script only works after refreshing a page but not when the site is navigated. That's not very convenient though.

Is it possible to detect page navigation on YouTube and insert HTML into the page before it's rendered in the browser so that the added content is shown immediately without any delay and no page refresh is required?

Example link: https://www.youtube.com/playlist?list=PL_8APVyhfhpdgMJ3J80YQxWBMUhbwXw8B

P.S. My question is not the same as Modify elements immediately after they are displayed (not after page completely loads) in greasemonkey script? because I've tried MutationObserver and the problem is the same - a refresh is needed to show changes to the web page:

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

 Answers

29

YouTube site doesn't reload pages on navigation, it replaces the history state.

The extension's content scripts aren't reinjected when URL changes without a page being reloaded. Naturally when you reload a page manually the content script is executed.

There are several methods to detect page transitions on Youtube site:

  • using a background page script: webNavigation API, tabs API

  • using a content script: transitionend event for the progress meter on video pages

  • using a content script and a site-specific event triggered on video navigation:

    Run getEventListeners(document) in devtools console and inspect the output.

    enter image description here

    yt-navigate-start is what we need in this case.

    Notes:

    • for other tasks we might want yt-navigate-finish
    • the old youtube design was using spfdone event

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

Note: the matches key encompasses the entire youtube.com domain so that the content script runs when the user first opens the home youtube page then navigates to a watch page.

content.js:

document.addEventListener('yt-navigate-start', process);

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

The process function will alter the page.
Note, the specified element classes and structure will change in the future.

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>');
}
Tuesday, June 1, 2021
 
DiglettPotato
answered 7 Months ago
99

I've found a solution to override some dimen values in dimens.xml file it works for the text size but icon still remains tiny. Here is how i do that. Hope this code will help others too :-)

<!-- Overriding Default Bottom Navigation sizes-->
    <dimen name="design_bottom_navigation_text_size" tools:override="true">16sp</dimen>
    <dimen name="design_bottom_navigation_active_text_size" tools:override="true">20sp</dimen>
    <dimen name="design_bottom_navigation_height" tools:override="true">70dp</dimen>
Monday, August 2, 2021
 
shwabob
answered 4 Months ago
62

I have written a sample trivial demonstration of Browsing Data API, it can help you to pick from here. Deletion may take time so you have to wait for message "All data is Deleted..." in console of extension for confirmation.

Before:

enter image description here

After:

enter image description here

manifest.json

{
  "name" : "BrowsingData Demo",
  "version" : "1",
  "description" : "Trivial Demonstration of Browsing Data",
  "permissions": [
    "browsingData"
  ],
  "browser_action": {
     "default_icon": "icon.png",
     "default_popup": "popup.html"
  },
  "manifest_version": 2
}

popup.html

<html>
<head>
<script src="popup.js"></script>
</head>
<body>
</body>
</html>

popup.js

   function browsingdata(){
    chrome.browsingData.remove({
      "originTypes": {
        "protectedWeb": true, // Set to true or true as per your requirement
        "unprotectedWeb":true,// Set to true or true as per your requirement
        "extension":true    // Set to true or true as per your requirement
      }
    }, {
      "appcache": true, // Set to true or true as per your requirement
      "cache": true, // Set to true or true as per your requirement
      "cookies": true, // Set to true or true as per your requirement
      "downloads": true, // Set to true or true as per your requirement
      "fileSystems": true, // Set to true or true as per your requirement
      "formData": true, // Set to true or true as per your requirement
      "history": true, // Set to true or true as per your requirement
      "indexedDB": true, // Set to true or true as per your requirement
      "localStorage": true, // Set to true or true as per your requirement
      "pluginData": true, // Set to true or true as per your requirement
      "passwords": true, // Set to true or true as per your requirement
      "webSQL": true // Set to true or true as per your requirement
    }, function (){
        console.log("All data is Deleted...");
    });
}
window.onload=browsingdata;

For more information refer browsing data API to get idea of all methods etc.

Wednesday, August 4, 2021
 
tedders
answered 4 Months ago
83
if ('onpagehide' in window) {
   window.addEventListener('pagehide', exitFunction, false);
} else {
   window.addEventListener('unload', exitFunction, false);
}
Sunday, September 12, 2021
 
Hans-Peter Störr
answered 3 Months ago
19

My problem get to solved by removing the plugin completely from chrome store and re-upload and re-publish the plugin again.

The problem was : Initially i did not had 'externally_connectable' property, so wasn't able to get chrome.runtime defined. Later when i included, then i was updating the chrome plugin. And the main cause may be : 'Chrome store does not modify the 'manifest.json' (at least for certain properties like 'externally_connectable') just by updating the plugin by uploading. You may have to remove and re-upload to get manifest.json updated' (This is what i can conclude because of my experience, Please correct me if i am wrong with some valid reference source.)

and so 'chrome.runtime' remains undefined.

Later when i removed the plugin and re-uploaded, everything worked fine.

Friday, November 5, 2021
 
fadd
answered 1 Month ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share