Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I prevent the browser from preloading the <video> tag?

I have written a user script extension for Chrome with JavaScript in order to prevent video and audio tags from downloading automatically on pageload

This is the code:

var videoTags = document.getElementsByTagName("Video");
var i;
for(i=0; i<videoTags.length; i++)
{
    videoTags[i].setAttribute("preload", "none");
    videoTags[i].removeAttribute("autoplay");
}

var audioTags = document.getElementsByTagName("audio");
var i;
for(i=0; i<audioTags.length; i++)
{
    audioTags[i].setAttribute("preload", "none");
    audioTags[i].removeAttribute("autoplay");
}

And this is the manifest.json file:

   {
      "content_scripts": [ {
      "exclude_globs": [  ],
      "exclude_matches": [  ],
      "include_globs": [ "*" ],
      "js": [ "script.js" ],
      "matches": [ "http://*/*", "https://*/*" ],
      "run_at": "document_start"
   } ],
      "converted_from_user_script": true,
      "description": "",
      "key": "an2xaeZJluPfnpmcsHPXI4aajQPL1cBm5C2kKjaQwXA=",
      "name": "test.user.js",
      "version": "1.0"
  }

The problem is that my script runs after a moment and until then the browser (Chrome) downloads a part of video/audio file.

like image 874
Ali7091 Avatar asked Jun 19 '16 07:06

Ali7091


4 Answers

One dirty solution that seems to work is to use a MutationObserver from your userscript, once you're sure it does run at document-start.

This TamperMonkey script does work for me :

// ==UserScript==
// @name         Block videos preloading
// @include      *
// @run-at document-start
// ==/UserScript==

(function() {
  'use strict';
  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      var nodes = mutation.addedNodes;
      for (var i = 0; i < nodes.length; i++) {
        if (nodes[i].nodeName == "VIDEO") {
          nodes[i].setAttribute('preload', 'none');
          nodes[i].removeAttribute('autoplay');
        }
      }
    })
  });
  observer.observe(document.documentElement, {
    childList: true,
    subtree: true
  });

})();

You may want to call observer.disconnect() on DOMContentLoaded, if you're not expecting other video elements for being inserted afterwards by scripts.

like image 147
Kaiido Avatar answered Nov 02 '22 19:11

Kaiido


Check if what you think really happens. It should be impossible, because both "run_at": "document_start" and "run_at": "document_end" should make your code run before anything is loaded.

From the documentation in developer.chrome.com:

In the case of "document_start", the files are injected after any files from css, but before any other DOM is constructed or any other script is run.

In the case of "document_end", the files are injected immediately after the DOM is complete, but before subresources like images and frames have loaded.

In addition, because your code runs in document_start, the document.getElementsByTagName("Video") should fail because no DOM is even constructed yet.

Try debugging your code (start with checking for errors in the console). Also, read more about the "run_at" attribute here: https://developer.chrome.com/extensions/content_scripts#run_at

like image 28
kmaork Avatar answered Nov 02 '22 18:11

kmaork


Try storing <audio>, <video> src value, then remove src attribute from <audio>, <video> elements for setting preload, autoplay attributes; using DOMContentLoaded event

The DOMContentLoaded event is fired when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <script>
    var sources = [];
    document.addEventListener("DOMContentLoaded", function(event) {
      var media = document.querySelectorAll("audio, video");
      [].forEach.call(media, function(el) {
        if (el.src) {
          sources.push(el.src);
          el.removeAttribute("src");
        }
        var src = el.querySelectorAll("source");
        if (src.length) {
          [].forEach.call(src, function(source) {
            sources.push(source.src);
            source.removeAttribute("src");
          });
        };
      });
      console.log(sources);
    });
  </script>
</head>
<body style="height:270px">
  <video src="http://mirrors.creativecommons.org/movingimages/webm/ScienceCommonsJesseDylan_240p.webm" controls></video>
 <audio controls>
    <source src="https://upload.wikimedia.org/wikipedia/commons/6/6e/Micronesia_National_Anthem.ogg" type="video/ogg" />
  </audio>
</body>
</html>

Edit, Updated

can you test it in user script ?

Utilizing content_scripts, "run_at": "document_start" in manifest.json returned expected results as a chromium, chrome extension; that is, src attribute of <audio>, <video>, <source> elements should be removed from document.

manifest.json

{
  "manifest_version": 2,
  "name": "blockmedia",
  "description": "remove src from audio, video elements",
  "version": "1.0",
  "permissions": ["<all_urls>"],
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["script.js"],
      "run_at": "document_start"
    }
  ]
}

script.js

var sources = [];
document.addEventListener("DOMContentLoaded", function(event) {
  var media = document.querySelectorAll("audio, video");
  [].forEach.call(media, function(el) {
    if (el.src) {
      sources.push(el.src);
      el.removeAttribute("src");
    }
    var src = el.querySelectorAll("source");
    if (src.length) {
      [].forEach.call(src, function(source) {
        sources.push(source.src);
        source.removeAttribute("src");
      });
    };
  });
 console.log(sources);
});

like image 3
guest271314 Avatar answered Nov 02 '22 18:11

guest271314


You're battling against the Chrome parser preloader.

Even if you inject a content script at document start, using Mutation Observer or another technique suggested above, you still cannot action the HTML before the browser preloader.

Read this article, which helped me.

https://andydavies.me/blog/2013/10/22/how-the-browser-pre-loader-makes-pages-load-faster/

To achieve what you want to do, you're going to need to use the Chrome webRequest API to block the request.

https://developer.chrome.com/extensions/webRequest

You will need to filter requests according to the media type and then search the headers for the file types you want to block.

like image 1
Tom Avatar answered Nov 02 '22 18:11

Tom