Tagged: JavaScript

YouTube Annoyances Remover (GreaseMonkey JavaScript) 2020.01 update

Format labels are now included in the download links

Recently, I found that the thumbnails were not loading and the alternative download links were not getting listed. Both problems have been fixed in this update.

Youtube video download links and thumbnail fixed

About this User JS

Disables ads, turns off autoload/autoplay, adds RSS link, deletes “recommended for you” videos, unhides description, displays all comments, changes profile link to videos page; changes region to US, displays video thumbnail image, and adds a link to the lighter version on Hooktube if video is not playable for codec reasons. Supports Firefox-based browser up to Firefox 36. Newer versions should use a UserAgent (UA) spoofer add-on. YouTube loads a lighter version of the YouTube page for older browsers. This script will require the GreaseMonkey add-on to be executed by the Firefox browser. For embedded YouTube videos, use the script com.vsubhash.js.embedded-video-catcher.user.js.

The download links can then be sent to the DownThemAll add-on with the file renaming mask set to “*text*.mp4”.

Bloomberg and New Yorker also protect their paywall with Javascript

Joins New York Times and BloombergQuint

Thanks to Modi’s slow Internet, I found yet another site taking forever to download. When the page finished downloading, the content of the article suddenly disappeared and paywall appeared. Now, Bloomberg is also confirmed to be using Javascript to protect its paywall.

A few months after I informed BloombergQuint, they implemented a real paywall with truncated content, instead of one protected by CSS. New York Times continues to be on Javascript security.

When you truncate content, search engines cannot find much value in the pages. These pages will not appear in the search results. CSS and Javascript paywalls continue to provide the content to search engines.

There is a simple alternative to all this. Like my website, use Javascript to lazy-load images, CSS, other Javascript files and content for sidebar, header and footer. Someone who disables Javascript will not see the images and miss out on formatting. Search engines will continue to index those pages.

Articles should be truncated and moved behind the paywall as they grow old. Their URLs should not be changed to retain whatever search engine mojo they have acquired. And, the meta tags for robots (in the HTML) should say “index,nofollow,nocache,noarchive”.

Update: I found that the New Yorker is also on Javascript security.

New York Times protects its paywall with JavaScript – BloombergQuint used CSS

Instead of promoting social media sites and writing click-baity articles using millennials, how about hiring some experienced journalists and programmers.

One of the advantages of having the Modi government’s slow Internet is that web pages take forever to download. I clicked on a link and this NYT page started downloading quite lethargically. For some time, I was able to read the article but when the Javascript gunk finished downloading the article suddenly got truncated and a floating panel asked me to register to read the article. So, I turned off Javascript and the article loaded in full. No registration necessary.

They say that the site is still free if you sign up but I was not going to investigate. With BloombergQuint, I had to write a GreaseMonkey script.

NYT paywall defeated by switching off Javascript

NYT paywall defeated by switching off Javascript


age

YouTube Annoyances Remover 2019.06 Update

Disables ads, turns off autoload/autoplay, adds RSS link, deletes “recommended for you” videos, unhides description, displays all comments, changes profile link to videos page; changes region to US, displays video thumbnail image, lists offline download links, and adds a link to the lighter version on Hooktube if video is not playable for codec reasons.

Youtube video download links

The current video link, the Hooktube video link, and other format/bitrates are linked. These links when used with DownThemAll add-on (file renaming option set to “*text*.mp4” will ensure the download is completed offline.

This script supports Firefox-based browser up to version 36. Newer versions should use a UserAgent (UA) spoofer add-on. YouTube loads a lighter version of the YouTube page for older browsers. This script will require the GreaseMonkey add-on to be executed by the Firefox browser. The download links can then be sent to the DownThemAll add-on with the file renaming mask “*text*.mp4”.

It seems that Mozilla has effectively destroyed its add-on ecosystem by switching over to Google Chrome’s WebExtensions format. I recently installed the latest version of Firefox and it has no add-ons worth naming in its download portal. DTA is not there and neither is Greasemonkey. If had Google had infiltrated Mozilla and decapitated its top management, Firefox couldn’t be worse. Mozilla’s own management is diluting its brand appeal, frustrating add-on developers, disappointing its user community and effectively throwing Internet users to the jaws of the Google spyware called Chrome. (https://www.thewindowsclub.com/microsoft-disabled-these-chromium-services-features-in-new-edge)

For embedded YouTube videos, use the new script com.vsubhash.js.embedded-video-catcher.user.js.

Unable to stream YouTube and other videos

I have lived in several houses in three South Indian states. In all of them, there are unusual electric disturbances. The net connection is also very unstable. (I wrote the NetCheck desktop and mobile application for this reason – http://vsubhash.com/netcheck-app-for-android.html) When the net connection seems to be stable, the electrical disturbances trips the Internet modem.

For this reason, I am unable to play videos as an online stream. I have to download them first and play them offline on the TV. The downloads have to be constantly restarted because the net connection stops intermittently.

The electrical disturbances destroys electrical appliances. I run the entertainment systems and computers on a UPS and this protects them. But, I can hear the UPS relay clicking incessantly. Earlier, the modem was on the mains directly. Now, it runs of the UPS. Even then, the modem stops net connection for some reason.

Apart from the electrical disturbances, the power supply is cut several dozen times a day – in all three states. In a month, they are off for an entire day or an entire night for at least three or four days. This always happens when I am visiting someone and trying to work on a computer. The UPS supports the modem and the network switch for about 20 minutes if my laptop is powered from the battery. If I am on the desktop, the backup lasts for just five minutes.

I rarely use the mobile now. Here too, the net connection is not stable.

In this video, you can hear the racket created by the relay. In the background, you can hear the fan speed going up and down with the voltage fluctuations.

GreaseMonkey Javascript Source Code


// ==UserScript==
// @name YouTubeAnnoyancesRemover
// @namespace com.vsubhash.js.youtube-annoyances-remover
// @description Disables ads, turns off autoload/autoplay, adds RSS link, deletes "recommended for you" videos, unhides description, displays all comments, changes profile link to videos page; changes region to US, displays video thumbnail image, and adds a link to the lighter version on Hooktube if video is not playable for codec reasons. Supports Firefox-based browser up to version 36. Newer versions should use a UserAgent (UA) spoofer add-on. YouTube loads a lighter version of the YouTube page for older browsers. This script will require the GreaseMonkey add-on to be executed by the Firefox browser. For embedded YouTube videos, use the script com.vsubhash.js.embedded-video-catcher.user.js.
// @include https://www.youtube.com/watch*
// @include https://www.youtube.com/channel*
// @include https://www.youtube.com/user*
// @version 2020.01
// @grant none
// ==/UserScript==
var sAdStyle = " { visibility: none!important; display: none!important; }";
var oDlDiv;
document.addEventListener("readystatechange", fixYouTubeAnnoyances, false);
try {
console.log("YAD: Page start");
window.setTimeout(pauseVideos, 2*1000);
window.setTimeout(pauseVideos, 4*1000);
window.setTimeout(pauseVideos, 6*1000);
} catch (e) { }
function fixYouTubeAnnoyances() {
if ((document.readyState == "interactive") || (document.readyState == "complete")) {
console.log("YAD: loaded");
try {
pauseVideos();
window.setTimeout(showDescription, 1*200);
window.setTimeout(closeVideoAds, 2*200);
window.setTimeout(changeRegion, 3*200);
window.setTimeout(addRssButton, 1*1000);
window.setTimeout(changeProfileLink, 2*1000);
window.setTimeout(pauseVideos, 3*1000);
window.setTimeout(removeRecommendedForYouAds, 4*1000);
window.setTimeout(disableAndHideAdContainers, 5*1000);
window.setTimeout(disableAutoPlay, 6*1000);
window.setTimeout(addVideoDownloadList, 7*1000);
window.setTimeout(loadAllComments, 15*1000);
window.setTimeout(removeRecommendedForYouAds, 16*1000);
} catch (e) {
console.error("YAD Error: " + e);
}
}
}
function addRssButton() {
console.log("YAD: addRssButton")
var oLink, oLinkEl, oChannelTag, oRssImg, oRssLink, sChannel, sChannelName, sUserName, sFeedUrl;
// Android 1.6
oLink = document.querySelector("div.tv span.gmb a");
oChannelTag = document.querySelector("div.tv span.gmb a span");
if (oLink == null) {
// Android 3+
oLink = document.querySelector("a.slim-owner-icon-and-title");
oChannelTag = document.querySelector("a.slim-owner-icon-and-title div h3");
}
if (oLink == null) {
// Android 46
oLink = document.querySelector("div.yt-user-info a");
oChannelTag = document.querySelector("div.yt-user-info a");
}
if (oLink == null) {
// Android 60
oLink = document.querySelector("div#owner-container *#owner-name a");
oChannelTag = document.querySelector("div#owner-container *#owner-name a");
}
if ((oLink ==null) || (oChannelTag == null)) { return; } else {
console.log("YAD: Channel name found.");
sChannelName = oChannelTag.textContent;
sChannelName = sChannelName.replace(/^\s+|\s+$/g, '');
console.log("YAD: Channel link found.");
if (oLink.getAttribute("href").indexOf("/channel/") == 0) {
sChannel = oLink.getAttribute("href").substr("/channel/".length);
sFeedUrl = "https://www.youtube.com/feeds/videos.xml?channel_id=" + sChannel;
console.log("YAD: RSS ~ " + sChannelName + " = " + sFeedUrl);
} else if (oLink.getAttribute("href").indexOf("/user/") == 0) {
sUserName = oLink.getAttribute("href").substr("/user/".length);
sFeedUrl = "https://www.youtube.com/feeds/videos.xml?user=" + sUserName;
console.log("YAD: RSS ~ " + sChannelName + " = " + sFeedUrl);
} else { return; }
oLinkEl = document.createElement("link");
oLinkEl.setAttribute("rel", "alternate");
oLinkEl.setAttribute("title", sChannelName);
oLinkEl.setAttribute("type", "application/rss+xml");
oLinkEl.setAttribute("href", sFeedUrl);
document.getElementsByTagName("head")[0].appendChild(oLinkEl);
oRssImg = document.createElement("img");
oRssImg.setAttribute("style", "margin: auto 1em; ");
oRssImg.setAttribute("alt", "RSS");
oRssImg.setAttribute("src", "https://www.google.com/images/rss.png");
oRssLink = document.createElement("a");
oRssLink.setAttribute("id", "mvytRssFeedLink");
oRssLink.setAttribute("href", sFeedUrl);
oRssLink.setAttribute("title", "RSS feed link for this channel");
oRssLink.setAttribute("style", "text-decoration: none; border-style: none; ");
oRssLink.appendChild(oRssImg);
if (document.getElementById("mvytRssFeedLink") == null) {
oChannelTag.insertAdjacentHTML('afterend', oRssLink.outerHTML);
}
}
}
function pauseVideos() {
console.log("YAD: Pausing videos");
try {
var oVideoEls = document.getElementsByTagName("video");
for (var i = 0; i < oVideoEls.length; i++) {
oVideoEls[i].pause();
oVideoEls[i].muted = true;
if (oVideoEls[i].src.indexOf("pltype=adhost") > -1) {
console.log("YAD: Video ad found… closing tab");
window.open(location.href, '_blank');
window.close();
}
console.log("YAD: Pausing video " + (i+1));
if (!oVideoEls[i].paused) {
oVideoEls[i].pause();
}
//oVideoEls[i].volume = 0.6; // custom controls do not update
oVideoEls[i].muted = true;
oVideoEls[i].removeAttribute("autoplay");
oVideoEls[i].removeAttribute("loop");
oVideoEls[i].removeAttribute("controls");
oVideoEls[i].setAttribute("preload", "none");
oVideoEls[i].pause();
}
var oButtons = document.getElementsByTagName("button");
for (var i = 0; i < oButtons.length; i++) {
if (oButtons[i].className) {
if (oButtons[i].className.indexOf("ytp-mute-button") > -1) {
console.log("YAD: Mute button " + oButtons[i].className);
oButtons[i].click();
oButtons[i].click();
}
}
}
} catch (e) {
console.error("YAD: Error – " + e);
}
}
function showDescription() {
console.log("YAD: Finding description…");
if (document.getElementById("action-panel-details") != null) {
document.getElementById("action-panel-details").className = "action-panel-content yt-uix-expander yt-card yt-card-has-padding";
}
console.log("YAD: Description unhidden.");
}
function closeVideoAds() {
console.log("YAD: Detecting video ads…");
if ((document.getElementsByClassName("videoAdUiTopButtons").length > 0) || (document.getElementsByClassName("videoAdUi").length > 0)) {
console.log("YAD: Video ad found");
window.open(location.href, '_blank');
window.close();
} else {
console.log("YAD: No video ad");
}
}
function disableAutoPlay() {
var oEl = document.getElementById("autoplay-checkbox");
if (oEl == null) {
console.log("YAD: Did not find autoplay button.");
} else if (oEl.hasAttribute("checked")) {
console.log("YAD: Disabling autoplay…");
oEl.click();
} else {
console.log("YAD: Autoplay already disabled.");
}
}
function removeRecommendedForYouAds() {
console.log("YAD: Removing recommended videos");
var oRelatedColumn = document.getElementById("watch-related");
if (oRelatedColumn != null) {
var arRelatedVids = oRelatedColumn.getElementsByTagName("li");
var j = 0;
if (arRelatedVids.length > 0) {
for (var i = arRelatedVids.length-1; i > -1; i–) {
if (arRelatedVids[i].textContent.indexOf("Recommended for you") != -1) {
//console.log("YAD: Removing " + arRelatedVids[i].textContent);
arRelatedVids[i].parentNode.removeChild(arRelatedVids[i]);
++j;
}
}
}
console.log("YAD: Removed " + j + " recommended videos");
}
oRelatedColumn = document.getElementById("watch7-sidebar-modules");
if (oRelatedColumn != null) {
var arRelatedVids = oRelatedColumn.getElementsByClassName("watch-sidebar-section");
var j = 0;
if (arRelatedVids.length > 0) {
for (var i = arRelatedVids.length-1; i > -1; i–) {
if (arRelatedVids[i].textContent.indexOf("Recommended for you") != -1) {
//console.log("YAD: Removing " + arRelatedVids[i].textContent);
arRelatedVids[i].parentNode.removeChild(arRelatedVids[i]);
++j;
}
}
}
console.log("YADN: Removed " + j + " new recommended videos");
}
}
function disableAndHideAdContainers() {
console.log("YAD: Disabling/deleting ad containers…");
var arDivIds = ["AdSense", "watch7-sidebar-ads", "promotion-shelf", "live-chat-iframe", "invideo-overlay:7", ];
var arDivClasses = [ "adDisplay", "annotation", "html5-endscreen", "iv-promo", "video-ads", "videoAdUiBottomBar", "ytp-ad-module", "ytp-endscreen-content", "ytp-cards-button", "ytp-cards-teaser", "ytp-ad-overlay-container", "ytp-ad-overlay-slot", "ytp-ad-text-overlay", "ytp-ad-overlay-ad-info-button-container", "ytp-ad-hover-text-button", "ytp-ad-info-hover-text-button", "ytp-ad-overlay-text-image", "ytp-ad-overlay-text-image", "ytp-ad-image-overlay", "ytp-ad-overlay-close-container", "ytp-ad-overlay-close-button" ];
for (var i = 0; i < arDivIds.length; i++) {
var oDiv = document.getElementById(arDivIds[i]);
if (oDiv != null) {
oDiv.style.visibility = "hidden!important";
oDiv.style.display = "none!important";
oDiv.parentNode.removeChild(oDiv);
console.log("YAD: Removed " + arDivIds[i] + " by ID");
} else {
console.log("YAD: Not found: " + arDivIds[i] + " by ID");
}
sAdStyle = "#" + arDivIds[i] + ((i==0)?" ":" , ") + sAdStyle;
}
for (var i = 0; i < arDivClasses.length; i++) {
var oDivs = document.getElementsByClassName(arDivClasses[i]);
if (oDivs != null) {
for (var j = 0; j < oDivs.length; j++) {
oDivs[j].style.visibility = "hidden!important";
oDivs[j].style.display = "none!important";
oDivs[j].parentNode.removeChild(oDivs[j]);
}
} else {
console.log("YAD: Not found: " + oDivs[j] + " by ID");
}
sAdStyle = "*." + arDivClasses[i] + " , " + sAdStyle;
}
document.getElementsByTagName("head")[0].innerHTML = document.getElementsByTagName("head")[0].innerHTML + "\n<style>" + sAdStyle + "\n</style>";
}
function changeRegion() {
var oLangButton = document.getElementById("yt-picker-country-button");
if (oLangButton != null) {
if (oLangButton.textContent.indexOf("United States") == -1) {
oLangButton.click();
window.setTimeout(
function() {
var arRegions = document.getElementsByClassName("yt-picker-item");
for (var i = 0; i < arRegions.length; i++) {
if (arRegions[i].textContent.indexOf("United States") > -1) {
arRegions[i].click();
break;
}
}
}, 3*1000);
}
}
}
function changeProfileLink() {
console.log("YAD: Changing profile link")
var oDivs = document.getElementsByTagName("div");
if ((oDivs != null) && (oDivs.length > 0)) {
for (var i = 0; i < oDivs.length; i++) { if (oDivs[i].className == "yt-user-info") {
var oAnchors = oDivs[i].getElementsByTagName("a");
if ((oAnchors != null) && (oDivs.length>1)) {
var bFound = false;
for (var j = 0; j < oAnchors.length; j++) {
if (oAnchors[j].href.substring(0, "https://www.youtube.com/channel/&quot;.length) == "https://www.youtube.com/channel/&quot;) {
oAnchors[j].href = oAnchors[j].href + "/videos";
oLatestVideosLink = document.createElement("a");
oLatestVideosLink.setAttribute("style", "background-image: url(https://s.ytimg.com/yts/imgbin/www-hitchhiker-vflYQU35a.png); width: 17px; height: 17px; background-position: -94px -472px; border-style: none; margin: 0; padding: 0; ");
oLatestVideosLink.setAttribute("id", "MvPopularLink");
oLatestVideosLink.setAttribute("href", oAnchors[j].href + "?view=0&sort=p&flow=grid");
oAnchors[j].insertAdjacentHTML("afterend", oLatestVideosLink.outerHTML);
bFound = true;
break;
}
}
if (bFound) { break; }
}
}
}
}
}
var iLoadAllCommentsTimeout = 0;
function loadAllComments() {
if (iLoadAllCommentsTimeout > 0) {
window.clearTimeout(iLoadAllCommentsTimeout);
}
var oDiv = document.getElementById("watch-discussion");
if (oDiv == null) { return; }
var oButtons = oDiv.getElementsByClassName("comment-section-renderer-paginator");
if (oButtons != null) {
console.log("YAD: Comments");
if (oButtons[0] != null) {
oButtons[0].click();
iLoadAllCommentsTimeout = window.setTimeout(loadAllComments, 20*1000);
}
}
}
function parseYTPlayer() {
console.log("YAD: Inside parser" );
console.log("YAD: Title" + ytplayer.config.args.title);
try {
if (document.getElementsByTagName("video")[0] && (document.getElementsByTagName("video")[0].pause)) {
document.getElementsByTagName("video")[0].pause();
}
} catch (e) {
window.alert(e);
}
var arFormatParams, arFormats, i, j, sURL, sQuality, sMimeType, sExtension;
var oDlList, oDlListItem;
oDlList = document.getElementById("mvyJsList");
if (oDlList == null) {
return;
}
oDlList.innerHTML += "<li><a href=\"" + location.href.replace("www.you", "www.hook") + "\" style=\"color: navy; font-weight: bold; \" target=\"_blank\">Load in HookTube</a></li>";
var oFormats = JSON.parse(ytplayer.config.args.player_response);
var arFormats = oFormats.streamingData.formats;
for (i = 0; i < arFormats.length; i++) {
oDlListItem = document.createElement("li");
var sFormat = arFormats[i].mimeType.split(';')[0];
if (arFormats[i].mimeType.split(';')[0] == "video/mp4") {
sFormat = "M";
} else if (arFormats[i].mimeType.split(';')[0] == "video/webm") {
sFormat = "W";
}
console.log("YAD: Format " + arFormats[i].quality + " – " + arFormats[i].qualityLabel + " – " + arFormats[i].mimeType.split(';')[0]);
oDlListItem.innerHTML = "<a title=\"" + arFormats[i].mimeType.replace(/\"/g,"") + "\" style=\"color: navy; font-weight: bold; \" target=\"_blank\" href=\"" + arFormats[i].url + "\">" + document.title.replace(" – YouTube", "") + " – " + arFormats[i].qualityLabel + " " + sFormat + "</a>";
oDlList.appendChild(oDlListItem);
}
}
function addCurrentPlayURL() {
var oList = document.getElementById("mvyJsList");
if (oList != null) {
if (document.getElementById("movie_player") != null) {
if (document.getElementById("movie_player").getElementsByTagName("video") != null) {
var oVideo = document.getElementById("movie_player").getElementsByTagName("video")[0];
if (oVideo.src) {
oList.innerHTML += "<li><a id=\"VidLinkUrl\" title=\"" + document.title + "\" download=\"" + document.title.replace(/\s+/ig, "-").replace(/-{2,}/ig, "-") + ".mp4\" style=\"color: navy; font-weight: bold; \" target=\"_blank\" href=\"" + oVideo.src + "\">" + document.title + "</a></li>";
console.log("YAD: Video URL = " + oVideo.src);
oVideo.addEventListener(
"loadeddata",
function() {
console.log("YAD: New video loaded");
document.getElementById("VidLinkUrl").setAttribute("href", document.getElementsByTagName("video")[0].src);
}, false);
} else {
console.log("YAD: Error in player");
}
}
} else {
console.log("YouTube detector: No player");
oList.innerHTML += "<li><a style=\"color: navy; font-weight: bold; \"href=\"" + location.href.replace("www.you", "www.hook") + "\" style=\"color: navy; font-weight: bold; \" target=\"_blank\">Load in HookTube</a></li>";
}
}
}
function addVideoDownloadList() {
console.log("Executing YouTube detector");
var i, n, oDlButtonEl, oVideosList;
oDlDiv = document.createElement("div");
oDlDiv.setAttribute("id", "mvyJsDiv");
oDlDiv.setAttribute("style", "background-color: orange!important; border: 2px dashed firebrick; font-size: 0.34cm!important; font-family: sans-serif!important; line-height: 0.4cm!important; margin: 1em auto; padding: 1em; min-height: 120px; ");
oDlDiv.innerHTML = "Download video from:";
var oTwitterImage = document.querySelector("html head meta[name='twitter:image']");
console.log("YAD: Twitter image" + oTwitterImage.getAttribute("content"));
if (oTwitterImage) {
oDlDiv.innerHTML += "<img src=\"" + oTwitterImage.getAttribute("content") + "\" style=\"max-width: 200px; float: right; margin: 1em auto 1em 1em; \" />\n";
} else if (window.URL) {
var oURL = new URL(location.href);
var sID = oURL.searchParams.get("v");
oDlDiv.innerHTML += "<img src=\"https://i.ytimg.com/vi/&quot; + sID + "/hqdefault.jpg\" style=\"max-width: 200px; float: right; margin: 1em auto 1em 1em; \" />";
}
oDlList = document.createElement("ul");
oDlList.setAttribute("style", "display: block; list-style: disc inside none; margin-left: 1em!important; ");
oDlList.setAttribute("id", "mvyJsList");
oDlDiv.appendChild(oDlList);
if (location.href.indexOf("youtube.com/watch") > -1) {
document.getElementById("watch-headline-title").appendChild(oDlDiv);
addCurrentPlayURL();
parseYTPlayer();
} else if (location.href.indexOf("youtube.com/embed/") > -1) {
// Use the script com.vsubhash.js.embedded-video-catcher
}
}


// ==UserScript==
// @name EmbeddedVideoCatcher
// @namespace com.vsubhash.js.embedded-video-catcher
// @description Adds a link to the video file. Supports Firefox-based browser up to version 36. Newer versions should use a UserAgent (UA) spoofer add-on. YouTube loads a lighter version of the YouTube page for older browsers. This script will require the GreaseMonkey add-on to be executed by the Firefox browser. If the video link is inaccessible by a transparent image or other HTML element, the URL can be obtained from the browser's web console output (Tools -> Web Developers -> Console). The script also automatically pauses the video several times.
// @include http://*
// @include https://*
// @exclude https://www.youtube.com/watch*
// @exclude https://www.dailymotion.com/video*
// @version 2019.06
// @grant none
// ==/UserScript==
var sAdStyle = " { visibility: none!important; display: none!important; }";
document.addEventListener("readystatechange", loadHandler, false);
try {
window.setTimeout(pauseVideos, 2*1000);
window.setTimeout(pauseVideos, 4*1000);
window.setTimeout(pauseVideos, 6*1000);
} catch (e) { console.error("EVC Error: " + e); }
function loadHandler() {
try {
addVideoList();
} catch (e) {
console.error("EVC Error: " + e);
}
}
function updateYouTubeVideoLink() {
console.log("EVC: Loaded video event for YouTube video");
if (document.getElementById("mvyJsList") &&
document.getElementById("player") &&
(document.getElementById("player").getElementsByTagName("video").length > 0)) {
document.getElementById("player").getElementsByTagName("video")[0].pause();
document.getElementById("mvyJsList").innerHTML = "<li style=\"margin-left: 0.2em; padding-left: 0.2em; \"><a id=\"VidLinkUrl\" title=\"" + document.title + "\" download=\"" + document.title.replace(/\s+/ig, "-").replace(/-{2,}/ig, "-") + ".mp4\" style=\"color: navy; font-weight: bold; \" target=\"_blank\" href=\"" + document.getElementById("player").getElementsByTagName("video")[0].src + "\">" + document.title + "</a></li>";
console.log("Video : " + document.title + "\n" + document.getElementById("player").getElementsByTagName("video")[0].src);
} else {
//console.log("EVC: Loaded video event for YouTube video noooh");
}
}
function updateNumberedVideoLink(aoEvent) {
console.log("EVC: Loaded video event for " + aoEvent.target.src);
var oLink = document.getElementById("VidLinkUrl" + aoEvent.target.getAttribute("mvy_id"));
if (oLink) {
oLink.setAttribute("href", aoEvent.target.getAttribute("src"));
oLink.innerHTML = aoEvent.target.getAttribute("src").substr(aoEvent.target.getAttribute("src").lastIndexOf("/")+1);
console.log("EVC: URL : " + document.title + "\n" + aoEvent.target.src);
console.log("EVC: File: " + aoEvent.target.getAttribute("src").substr(aoEvent.target.getAttribute("src").lastIndexOf("/")+1));
}
}
function addVideoList() {
if (document.readyState == "complete") {
console.log("EVC: Executing EmbeddedVideoCatcher");
var i, n, oDlButtonEl, oDlDiv, oVideosList;
if (location.href.indexOf("youtube.com/embed") > -1) {
// Embedded Youtube video page loads twice. On the second time, it will have a video element.
// However, the video element will not have a 'src' attribute. The attribute will be assigned
// a video URL after the play button is clicked. A handler for the "loadeddata" event is added
// obtain this video URL.
console.log("EVC: Found YouTube embedded video player");
if (document.getElementById("mvyJsDiv") == null) {
oDlDiv = document.createElement("div");
oDlDiv.setAttribute("id", "mvyJsDiv");
oDlDiv.setAttribute("style", "position: absolute; top: 0; display: block; background-color: orange!important; border: 2px dashed firebrick; font-size: 0.34cm!important; font-family: sans-serif!important; line-height: 0.4cm!important; margin: 10px 10px 10px 50%; padding: 1em; width: 40%; z-index: 203; ");
}
if (document.getElementById("mvyJsList") == null) {
oDlDiv.innerHTML = "Download video from:";
oDlList = document.createElement("ul");
oDlList.setAttribute("style", "display: block; list-style: disc outside none; margin-left: 0.2em; padding-left: 0.2em; ");
oDlList.setAttribute("id", "mvyJsList");
oDlDiv.appendChild(oDlList);
}
var oVideoDiv = document.getElementById("player");
if (oVideoDiv == null) {
console.log("EVC: Player DIV not found");
} else if (oVideoDiv.getElementsByTagName("video").length < 1) {
console.log("EVC: Video tag not found in player DIV");
} else {
oVideoDiv.appendChild(oDlDiv);
var oVideo = oVideoDiv.getElementsByTagName("video")[0];
oVideo.addEventListener("loadeddata", updateYouTubeVideoLink, false);
if (oVideo.src) {
updateYouTubeVideoLink();
} else {
document.querySelector("button.ytp-large-play-button").click();
window.setTimeout(updateYouTubeVideoLink, 3*1000);
document.getElementById("mvyJsList").innerHTML = "Video not loaded yet";
}
console.log("EVC: Added Youtube video to list");
}
} else {
console.log("EVC: Not youtube");
if (document.getElementsByTagName("video").length > 0) {
console.log("EVC: Number of videos = " + document.getElementsByTagName("video").length);
for (var i = 0; i < document.getElementsByTagName("video").length; i++) {
console.log("EVC: Parsing video " + i);
oDlDiv = document.createElement("div");
oDlDiv.setAttribute("id", ("mvyJsDiv" + i));
oDlDiv.setAttribute("style", "position: absolute; top: 0; display: block; background-color: orange!important; border: 2px dashed firebrick; font-size: 0.34cm!important; font-family: sans-serif!important; line-height: 0.4cm!important; margin: 10px 10px 10px 50%; padding: 0.5em; width: 40%; z-index: " + (203 + (i*10)) + "; ");
oDlDiv.style.top = (i*100) + "px";
oDlDiv.innerHTML = "Download video from:";
oDlList = document.createElement("ul");
oDlList.setAttribute("style", "display: block; list-style: disc outside none; margin-left: 0.2em!important; padding-left: 0.2em!important;");
oDlList.setAttribute("id", ("mvyJsList" + i));
oDlDiv.appendChild(oDlList);
var oVideo = document.getElementsByTagName("video")[i];
oVideo.pause();
oVideo.parentElement.setAttribute("style", "background-color: brown; overflow: scroll!important; ");
oVideo.parentElement.insertBefore(oDlDiv, oVideo);
oVideo.setAttribute("mvy_id", i);
oVideo.addEventListener("loadeddata", updateNumberedVideoLink, false);
//oVideo.onloadeddata = updateNumberedVideoLink;
if (oVideo.getElementsByTagName("source").length > 0) {
oDlList.innerHTML = "<li style=\"margin-left: 0.2em; padding-left: 0.2em; \"><a id=\"VidLinkUrl" + i + "\" title=\"" + document.title + "\" style=\"color: navy; font-weight: bold; \" target=\"_blank\" href=\"" + oVideo.src + "\">Not Available</a></li>";
console.log("EVC: Video " + (i) + " has source tag(s)");
for (var j = 0; j < oVideo.getElementsByTagName("source").length; j++) {
var oSource = oVideo.getElementsByTagName("source")[j];
oDlList.innerHTML = oDlList.innerHTML + "<li style=\"margin-left: 0.2em; padding-left: 0.2em; \"><a id=\"VidLinkUrl" + i + "-" + j + "\" title=\"" + (document.title?document.title:"") + " – Source " + (j+1) + " (" + oSource.type + ")\" download=\"" + document.title.replace(/\s+/ig, "-").replace(/-{2,}/ig, "-") + ".mp4\" style=\"color: navy; font-weight: bold; \" target=\"_blank\" href=\"" + oSource.src + "\">" + (document.title?document.title:"") + " – Source " + (j+1) + (oSource.getAttribute("res")?(" [" + oSource.getAttribute("res") + "]"):"") + (oSource.getAttribute("label")?(" [" + oSource.getAttribute("label") + "]"):"") + "</a></li>";
console.log("EVC: Found source " + (j+1) + " " + oSource.getAttribute("res") + " ~ " + oSource.src);
}
console.log("EVC: Added " + oVideo.getElementsByTagName("source").length + " videos to list");
} else {
console.log("EVC: Video " + (i+1) + " has no source tag");
if (oVideo.src) {
console.log("EVC: Video " + (i+1) + " is at ~ " + oVideo.src);
if (document.title) {
oDlList.innerHTML = "<li style=\"margin-left: 0.2em; padding-left: 0.2em; \"><a id=\"VidLinkUrl" + i + "\" title=\"" + document.title + "\" download=\"" + document.title.replace(/\s+/ig, "-").replace(/-{2,}/ig, "-") + ".mp4\" style=\"color: navy; font-weight: bold; \" target=\"_blank\" href=\"" + oVideo.src + "\">" + document.title + "</a></li>";
} else {
oDlList.innerHTML = "<li style=\"margin-left: 0.2em; padding-left: 0.2em; \"><a id=\"VidLinkUrl" + i + "\" title=\"Video\" style=\"color: navy; font-weight: bold; \" target=\"_blank\" href=\"" + oVideo.src + "\">Video Link</a></li>";
}
} else {
console.log("EVC: Video " + (i+1) + " has no URL");
oDlList.innerHTML = "<li style=\"margin-left: 0.2em; padding-left: 0.2em; \"><a id=\"VidLinkUrl" + i + "\" title=\"Video\" style=\"color: navy; font-weight: bold; \" href=\"javascript:return(false);\">Video Link Not Available</a></li>";
}
}
}
} else {
console.log("EVC: Found no videos");
}
}
} else {
// page not ready
}
}
function pauseVideos() {
console.log("EVC: Pausing videos");
try {
var oVideoEls = document.getElementsByTagName("video");
console.log("EVC: Number of videos " + oVideoEls.length);
for (var i = 0; i < oVideoEls.length; i++) {
oVideoEls[i].pause();
oVideoEls[i].muted = true;
if (oVideoEls[i].src.indexOf("pltype=adhost") > -1) {
console.log("EVC: Video ad found… closing tab");
oVideoEls[i].src = "";
}
console.log("EVC: Pausing video " + (i+1));
if (!oVideoEls[i].paused) {
oVideoEls[i].pause();
}
//oVideoEls[i].volume = 0.6; // custom controls do not update
oVideoEls[i].muted = true;
oVideoEls[i].removeAttribute("autoplay");
oVideoEls[i].removeAttribute("loop");
oVideoEls[i].removeAttribute("controls");
oVideoEls[i].setAttribute("preload", "none");
oVideoEls[i].pause();
}
var oButtons = document.getElementsByTagName("button");
for (var i = 0; i < oButtons.length; i++) {
if (oButtons[i].className) {
if (oButtons[i].className.indexOf("ytp-mute-button") > -1) {
console.log("EVC: Mute button " + oButtons[i].className);
oButtons[i].click();
oButtons[i].click();
}
}
}
} catch (e) {
console.error("EVC: Error – " + e);
}
}

How to make your website serve pages faster?

I made this list for CNN but it might be useful for any website owner.

CNN source code message

HTML source code at CNN.com has a hidden message. CNN Labs team asks for more ways to speed up their website.

I am always on the lookout for junk-serving domain names that I can add to my OS hosts file and CNN provided a number of them. Browsing through their source code, I found a message asking for ideas to improve site speeds. The CNN Labs team has already implemented several speed improvement techniques such as CDN, DNS prefetching, aysnc loads, and code minifying, and were interested in anything developers curious enough to look into their code could provide. Here are a few that I came up with:

  • Load HTML first. Everything else should be loaded afterwards. When CSS and JS are loaded along with HTML, they will block the display of the page content for at least one or two seconds. To do it properly, pre-press static HTML pages from your CMS and serve those static HTML pages to your visitors. Don’t put your HTML content in a database and serve them using a server-side script every time some one requests an article. Static HTML pages are served without any server-side processing latency. Like an image file, static pages are copied to the browser without any processing. They will not be parsed and interpreted like a PHP or ASPX script. (Pre-compiled scripts may be faster but not as fast as a simple file copy to a browser response stream.) Use server-side scripts for what they were originally meant for – really dynamic stuff such as Ajax requests, database searches and changes, handling live data and processing form submissions.
  • Lazy-load Javascript files, CSS stylsheets, images and videos using Javascript code in the static HTML pages. (My unreleased CMS does these two things already – check out www.vsubhash.in. While this CMS is targeted at people with personal websites, these two ideas can reduce waiting times for any kind of website.)
  • Don’t use custom fonts that get automatically downloaded from your website or even the Google fonts repository. (CNN uses its own “CNN” font.) Use a universal font stylesheet instead. Browsers can render web pages faster if they can use fonts already installed on the device. Does that make your webpages appear differently on different devices? Sweet mother of God! How will you survive? Really? No, sir, we don’t think pay attention to your corporate/branding font. It barely registers on our minds. If you are serving English text to an English audience, you don’t need a custom font. Custom fonts are meant for languages that don’t have universal device support or if the text is in a non-Unicode non-standard encoding tied to some legacy font. The need to display bar codes on a shopping site is a good use-case for a custom font. Emojis are not. Your EOT font download is likely to get stuck with all the other junk your page is downloading simultaneously. The visitor sees blanks everywhere in place of text, wondering what has gone wrong. Is that good for your corporate image? Don’t shock the user. Please read about the Principle of Least Surprise (PLS).
  • Don’t think people with enough bandwidth will have no problem. Whether you are on a PC or a mobile device (good Linux computers exempted), there are hundreds of processes that are forever talking to base and downloading/uploading files and data. Your webpage is competing with that and other tabs already opened in the browser. (Google’s policy states that Android will be talking to base, even if no app is running, even if it is in sleep mode, even if you have turned off wireless… So, imagine the situation when the device is being actively used.)
  • Don’t use images when the same effect can be achieved by CSS. But don’t use a CSS or Javascript framework when a simple image would do.
  • Don’t use a third-party CMS or big Javascript frameworks for your high-traffic website. This kind of software suffer from code bloat as CMS developers try to cram more features or try to make certain features work in all situations. Write your own CMS with custom Javascript and CSS optimized for your website. Maximum control & efficiency should be the goal. (CNN web pages, typical of off-the-shelf CMS-driven websites, contain a ton of JS and CSS and very little HTML.) Another problem with popular CMS programs are that site admins well-versed with them are not easy to find. Even if you do find them, it may be difficult for them to determine what a piece of code is doing. You will eventually end up hiring developers who can add extra or customized functionality over the CMS-generated Javascript and CSS. Good developers will write their own code. Lazy ones will resort to using more third-party CMS plugins. Over time, the code bloat will get beyond control. Very few developers in the Web team will be familiar with the site’s code and be able to troubleshoot effectively. Look at the dysfunctional MSDN (msdn.microsoft.com) site. There was never a proper plan. Over decades, different teams used different in-house CMS systems. They were numerous failed attempts to bring order to the whole mess. MSDN blogs used to be a popular thing. One day, someone, probably a suicidal maniac, decided to delete them. They tried to archive the blogs at their old location but botched it, resulting in millions of broken links. Despite all this, everytime you visited the site, there was an annoying survey popup asking about your impression of the site. Man, their increduluous audacity…
  • When you use ad networks, it is inevitable that you lose some control. However, you:
    • can load their script asynchronously (as CNN has already done).
    • ensure that their code is within limits. Many ad networks download a ton of (the same) Javascript libraries to do the simplest of things.
    • do not use more than one ad network in one page serving. You can rotate different providers on different browser requests. If you hit so many third-party sites for one request, as CNN does, what efficiency will you achieve?
  • Don’t use autoplay videos, even if you are a TV news channel. Are you misrepresenting video plays to advertisers? What? Top tech companies do it as well? Well, it is not a matter of just ethics but money too. Be assured that autoplay videos are a waste of bandwidth when the visitor just wants to read your article or is already listening to music. Even with the inflated statistics, you will still lose money… over time.
  • Why use a third-party service to serve related articles from your own site? Do it on your own. Ensure that your articles are tagged and categorized appropriately and serve their links on the sidebar, ordered by date and/or popularity. Add some AI and make it work. Ensure that this “related” stuff is not part of the static HTML churned out by your CMS. Lazy-load related content as well.
  • Limit the number of redirects in the links you post online. CNN links on Twitter pass through a maze of domains before settling down. These hops are time-consuming because they are on a https connection. Here is a list of redirects for one of their links (https://cnn.it/2DtZSme).
    • t.co (Twitter) to cnn.it
    • cnn.it to bit.ly
    • bit.ly to trib.al
    • trib.al to www․cnn.com
    • www․cnn.com to edition.cnn.com
  • Is your website accessibility-friendly? When you create an accessible website, you create a fast website by default. If you see more CSS+JS than HTML in a ‘View Source’, then your site is not accessible. It is not search engine-friendly either. Single-page websites are the creation of the Devil.
  • Don’t use pages that automatically reload. Are you a moron? (Not you, sir/madam, but I can list a dozen websites, a prominent news aggregation site among them, that do this without shame and this question is rhetorically addressed to them.) Use Ajax unobtrusively to change only the updated content.
  • Understand the principle behind Ajax. You display a page first and then use Ajax to inject data into it without reloading. Gmail does this in reverse. It will make you wait while it loads several megabytes of XML data, containing all your emails, before it displays your inbox – totally defeating the idea behind XML requests. Load what is needed, not everything from the beginning of time.
  • Don’t use social media plugins as-is-where-is. They usually block the loading of the page. Study their no-script versions and use your Javascript to generate valid links dynamically.
  • Analytics code increases bloat , reduce the responsiveness of the site and increases bandwidth usage. Do you really need them? There are many free server software that can process server log files and generate insightful visitor stats. Check your CMS or ask your hosting provider. If you need to study click-intensive regions for improving site effectiveness, use them on a temporary basis and get rid of it once you have generated enough data.
  • Don’t think that as Javascript engines are so fast, browsers will have no problem with all the gunk you have included in your webpages. Nah-hah! Javascript engines may claim to be fast but HTML and CSS rendering is still slow. Don’t believe me? Check out my Javascript benchmark test. After that, add network latencies (over which you have no control) to the mix and judge for yourself.
  • Over time, your pages will become overweight and be lethargic. Either give your site a redesign or start afresh. If not, do regular weight trimming. Measure the dry and wet weight of a web page. Is it really worth it? Regular code reviews help identify parts that are no longer required and provide ideas to optimize CSS and JS.

BloombergQuint protects its paywall using a CSS filter!!!

They are selling one-year subscription for Rs. 3000.

I mailed BloombergQuint a few days ago but they don’t care. They seem to think that even if someone figures this out, it is still a hassle to do this on all pages. I have a GreaseMonkey script that does this automatically on all their pages. Not that I read the site every day.

Open the browser’s console and disable the fuzzy container’s filter and the page’s content is visible.

BloombergQuint is one of the few remaining independent news organizations. The Quint was in the news recently when Modi govt raided their offices after some explosive Rafale deal-related stories and IT officials were found cloning the contents of journalists’ phones and computers.

It seems that their subscribers’ support is thin.

Facebook Posts Deleter updated – Greasemonkey script to delete FB posts one by one

Facebook archives all your posts, photos and videos to optical media, not just HDDs. So, when you tell them to delete your content, do they really delete it from its cold-storage system too? Unlikely. Delete Facebook, if you have one, now.

Facebook Posts Deleter 2016 was a User Script that automatically deleted Facebook posts one by one on the FB “Activity Log” page. You could use this script if you did not trust Facebook to delete the content when you choose that option on its page. One FB user, who sued Facebook, found that deleted content continues to reside on Facebook servers on its distributed content delivery network long after the account has been “deleted” by Facebook.

Like Google/YouTube, Facebook seems to be serving different set of web pages, CSS & JS for different versions of web browsers. This User JavaScript was developed and tested in Firefox 34. Spoof this version with a Firefox add-on. The code is now very simple. There is no start button. It will start deleting posts without warning.


// ==UserScript==
// @name Facebook Posts Deleter
// @namespace com.vsubhash.js.facebook-posts-deleter
// @description Deletes all facebook posts, one by one if browser is spoofed as Firefox 34
// @include https://www.facebook.com/*allactivity
// @exclude %exclude%
// @version 2018
// @grant none
// ==/UserScript==
/*
* "Facebook Posts Deleter 2018" script is a User Script to automatically delete Facebook posts one by one without any user intervention. Install this script in Firefox-based browsers (Firefox, Seamonkey, IceWeasel) using the Greasemonkey add-on. Then, go to your Facebook "Activity Log". This scripts was tested in Firefox 34. If you use a much different browser, it may not work as Facebook serves different web pages (CSS/JS) for different versions of browsers. To fix that, spoof your current browser as Firefox 34. For this, use a browser ID spoofer add-on (such as User Agent Overrider https://addons.mozilla.org/en-US/firefox/addon/user-agent-overrider/) and with the custom agent string "Mozilla/5.0 (X11; rv:34.0) Gecko/20100101 Firefox/34.0". This script was simplified and tested in 2018 and it works fine.
*/
document.addEventListener("DOMContentLoaded", startItDelayed, false);
function startItDelayed() {
window.setTimeout(clickTheMenu, 10*1000);
}
function clickTheMenu() {
console.log("FPD: Finding the menu");
arAnchors = document.getElementsByTagName("a");
for (var i = 0; i < arAnchors.length; i++) {
if (arAnchors[i].id.indexOf("u_jsonp") > -1) {
console.log("FPD: Found a menu");
arAnchors[i].click();
console.log("FPD: Clicked the menu");
window.setTimeout(clickDeleteOption, 3*1000);
break;
}
}
}
function clickDeleteOption() {
console.log("FPD: Finding the Delete option");
arAnchors = document.getElementsByTagName("a");
for (var i = 0; i < arAnchors.length; i++) {
if (arAnchors[i].hasAttribute("ajaxify")) {
if (arAnchors[i].getAttribute("ajaxify").indexOf("/ajax/timeline/delete/confirm") > -1) {
console.log("FPD: Found the Delete option");
arAnchors[i].click();
console.log("FPD: Clicked the delete option");
window.setTimeout(clickDeleteConfirmButton, 3*1000);
break;
}
}
}
}
function clickDeleteConfirmButton() {
console.log("FPD: Finding the Delete confirmation button");
arButtons = document.getElementsByTagName("button");
for (var i = 0; i < arButtons.length; i++) {
if ((arButtons[i].className.indexOf("layerConfirm") > -1) &&
(arButtons[i].textContent.indexOf("Delete") > -1)) {
console.log("FPD: Found the Delete confirm button");
arButtons[i].click();
console.log("FPD: Clicked the Delete confirm button");
window.setTimeout(function() { location.reload(true); }, 3*1000);
break;
}
}
}

Greasemonkey script to click the “Load More” button of YouTube video pages

YouTube forces its content creators to create lots of videos than is necessary. I have been watching the drawing videos of an artist. She is now posting videos every day, not about drawing but about her everyday activities. In these videos, she posts links to the real drawing videos or tells about them in advance. It’s awful. She is not alone. There are lots of people who seem to be doing this. There was this one guy whose electronics videos I watched a few times. I decided to check what else he had posted. Yikes! A whole lot of useless nothingness videos! I didn’t see them earlier because I always change the filter to “Most popular”.

If content creators stop making such videos, YouTube lets their earnings fall. This is because only the latest videos of subscribed channels are listed when people launch the YouTube app. If a content creator does not produce at least two or three videos per week, it is easy to get hidden by other channels when he/she does release a new video after a long break.

I use bookmarks and the hidden RSS feeds of YouTube. I don’t login and I don’t watch them live. Because of unexplained lag issues, I download the videos and watch them offline on my WDTV device.

When I check the videos pages, only some video thumbnails are listed there. There is a “Load More” button, which needs to be clicked several times to get the full listing. Sometimes the Net connection breaks during this interval and the expansion of the page stops. I need to then refresh the page and then click the button a few times to get to down to where I was. So, I wrote this Greasemonkey script to that for me. I load the video page and the script automatically clicks the button several times, waiting for sometime between each click to let the videos thumbnails.


// ==UserScript==
// @name YouTube LoadMore Till No More
// @namespace com.vsubhash.js.youtube-load-more-till-no-more
// @description Automatically expands the videoslist
// @include /https://www\.youtube\.com/(channel|user)/(\w+)/videos*/
// @exclude %exclude%
// @version 2018
// @grant none
// ==/UserScript==
var iYLM_Timeout = 0;
var iCounter = 0;
document.addEventListener("DOMContentLoaded", startItDelayed, false);
function startItDelayed() {
iYLM_Timeout = window.setTimeout(loadMore, 10*1000);
}
function loadMore() {
console.log("YLM: We are in");
if (document.getElementsByClassName("load-more-button").length > 0) {
if (document.getElementsByClassName("load-more-button")[0].getElementsByClassName("load-more-text").length > 0) {
document.getElementsByClassName("load-more-button")[0].getElementsByClassName("load-more-text")[0].click();
console.log("YLM: Loading more " + (++iCounter));
startItDelayed();
}
} else if (iYLM_Timeout != 0) {
console.log("YLM: No more found ");
window.clearTimeout(iYLM_Timeout);
}
}

With this script, I open the videos page to let it roll out. Meanwhile, I browse some other page. By the time I come back to the video page, all the thumbnails are listed.

Greasemonkey JavaScript-based YouTube adblocker and annoyances remover

Watching YouTube online is still impossible for me – the Net connection is still flaky and there are disturbances in the electricity supply. Downloading the videos and playing them offline is still the only option for me. Even then, the YouTube comes with a great deal of annoyances – ads (text and video), autoplay, “Recommended for you” video list, lack of RSS feeds, half-hidden description, YouTube-curated (or censored) “Top Comments”.

I have created several Greasemonkey scripts to deal with YouTube. Recently, I added a few more. Here they are:

The last one is new. It does several things – disables video ads, hides text ads, disables autoplay, deletes “recommended for you” ads.

Disabling video ads was tricky. The current implementation opens the same video URL in another tab and closes the current tab. This is repeated until YouTube relents and loads the original video. This can range anywhere from 2 to 10 reloads, probably. The code also finds the mute button and mutes the ad.


// ==UserScript==
// @name YouTube Ads Disabler
// @namespace com.vsubhash.js.youtube-ads-disabler
// @description Disables ads, swithces off autoplay, adds RSS link, deletes "recommended for you" videos, unhides description, displays latest comments
// @include https://www.youtube.com/watch*
// @version 1
// @grant none
// ==/UserScript==
try {
window.setTimeout(
function() {
document.getElementsByTagName('video')[0].pause();
document.getElementsByTagName('video')[1].pause();
},
5*1000);
} catch (e) {
console.error("YAD Error: " + e);
}
document.addEventListener("DOMContentLoaded", fixYouTubeAnnoyances, false);
function fixYouTubeAnnoyances() {
console.log("YAD: Here");
var sAdStyle = " { visibility: none!important; display: none!important; }";
try {
window.setTimeout(closeVideoAds, 1*1000);
window.setTimeout(showDescription, 2*1000);
window.setTimeout(removeRecommendedForYouAds, 3*1000);
window.setTimeout(disableAndHideAdContainers, 4*1000);
window.setTimeout(disableAutoPlay, 5*1000);
} catch (e) {
console.error("YAD Error: " + e);
}
}
function closeVideoAds() {
console.log("YAD: Detecting video ads…");
if ((document.getElementsByClassName("videoAdUiTopButtons").length > 0) || (document.getElementsByClassName("videoAdUi").length > 0)) {
console.log("YAD: Video ad found");
window.open(location.href, '_blank');
//window.open(location, '_self').close();
window.close();
if (document.getElementsByClassName("ytp-mute-button").length > 0) {
console.log("YAD: Video ad mute button found");
if (document.getElementsByClassName("ytp-volume-panel").length > 0) {
if (document.getElementsByClassName("ytp-volume-panel")[0].getAttribute("aria-volumetext") != "100% volume muted") {
document.getElementsByClassName("ytp-mute-button")[0].click();
console.log("YAD: Video ad muted");
}
}
}
} else {
console.log("YAD: No video ad");
}
}
function disableAutoPlay() {
console.log("YAD: Disabling autoplay…");
var arAutoPlayIds = [ "toggleButton", "autoplay-checkbox" ];
for (var i = 0; i < arAutoPlayIds.length; i++) {
console.log("YAD: Checking for autoplay ID " + arAutoPlayIds[i]);
if ((document.getElementById(arAutoPlayIds[i]) != null) && (document.getElementById(arAutoPlayIds[i]).checked)) {
console.log("YAD: Aautoplay ID found " + arAutoPlayIds[i]);
document.getElementById(arAutoPlayIds[i]).click();
console.log("YAD: Autoplay ID disabled – " + arAutoPlayIds[i]);
}
}
}
function showDescription() {
console.log("YAD: Finding description…");
if (document.getElementById("action-panel-details") != null) {
document.getElementById("action-panel-details").className = "action-panel-content yt-uix-expander yt-card yt-card-has-padding";
}
console.log("YAD: Description unhidden.");
}
function removeRecommendedForYouAds() {
console.log("YAD: Removing recommended videos");
var oRelatedColumn = document.getElementById("watch-related");
if (oRelatedColumn != null) {
var arRelatedVids = oRelatedColumn.getElementsByTagName("li");
var j = 0;
if (arRelatedVids.length > 0) {
for (var i = arRelatedVids.length-1; i > -1; i–) {
if (arRelatedVids[i].textContent.indexOf("Recommended for you") != -1) {
//console.log("YAD: Removing " + arRelatedVids[i].textContent);
arRelatedVids[i].parentNode.removeChild(arRelatedVids[i]);
++j;
}
}
}
console.log("YAD: Removed " + j + " recommended videos");
}
}
function disableAndHideAdContainers() {
console.log("YAD: Disabling/deleting ad containers…");
var arDivIds = ["AdSense", "watch7-sidebar-ads", "promotion-shelf", "live-chat-iframe"];
var arDivClasses = [ "adDisplay", "annotation", "html5-endscreen", "iv-promo", "videoAdUiBottomBar", "ytp-endscreen-content", "ytp-cards-button", "ytp-cards-teaser" ];
for (var i = 0; i < arDivIds.length; i++) {
var oDiv = document.getElementById(arDivIds[i]);
if (oDiv != null) {
oDiv.style.visibility = "hidden!important";
oDiv.style.display = "none!important";
oDiv.parentNode.removeChild(oDiv);
console.log("YAD: Removed " + arDivIds[i] + " by ID");
} else {
console.log("YAD: Not found: " + arDivIds[i] + " by ID");
}
sAdStyle = "#" + arDivIds[i] + ((i==0)?" ":" , ") + sAdStyle;
}
for (var i = 0; i < arDivClasses.length; i++) {
var oDivs = document.getElementsByClassName(arDivClasses[i]);
if (oDivs != null) {
for (var j = 0; j < oDivs.length; j++) {
oDivs[j].style.visibility = "hidden!important";
oDivs[j].style.display = "none!important";
oDivs[j].parentNode.removeChild(oDivs[j]);
}
} else {
console.log("YAD: Not found: " + oDivs[j] + " by ID");
}
sAdStyle = "*." + arDivClasses[i] + " , " + sAdStyle;
}
document.getElementsByTagName("head")[0].innerHTML = document.getElementsByTagName("head")[0].innerHTML + "\n<style>" + sAdStyle + "\n</style>";
}

NOTE: I use an old version of Firefox and I spoof a newer but still old version in it. YouTube and other Google sites serve different styles of pages for different browser versions. The above code works with Firefox 42. To make the code work, you can spoof version 42 using the UserAgent Switcher add-on with this UA string.

Mozilla/5.0 (Windows NT 6.2; rv:42) Gecko/20100101 Firefox/42.0

Of course, I will not be publishing the ad-blocking code. YouTube apocalypse is on now. Ad agencies have done to YouTube what they had been doing for decades to newspaper and TV journalism – interfering in content. After CNN decided to launch a crusade against Alex Jones’ YouTube channels, ad agencies are now telling YouTube that their ads should not be playing on “controversial” content. The bottomline now is that videos from big music labels and stars can have all kind of crude/vulgar/unsuitable content but ordinary folk stepping out of the line will have to be immediately “demonetized.” I found one retired nurse, who publishes makeup tutorials, complaining that she never said a bad word or anything controversial but all her videos were demonetized.

Until the said apocalypse, YouTube content creators seem to have been generously paid. When the times were good, YouTube went all over the world asking people to make videos and make tons of money. Now, YouTube’s revenues are down and the first victims are the small and independent content creators who took YouTube’s bait. A million page views earns the creators very little. It is only with off-YouTube marketing deals and paid-promotion that YouTube content creators can make money. I am surprised that that many seemingly intelligent people left their day jobs and became “Youtubers” full time. Even more sad are the legion of retired folks whose savings have been destroyed by Bush and Obama’s zero-interest monetary regime and free-trade policies, and Wall Street’s looting of pension funds and local government funds. There can be no economy without local manufacturing. Selfies and cat videos are not what an advanced economy runs on.

Anyway, I can tolerate text ads. The videos ads are horrible. The “recommended for you” videos are downright creepy. Why are they recommended for me?

Greasemonkey script to display newer comments on YouTube – bypass censorship

Changes comments filter to “Newest first” from “Top comments”

YouTube has been raining on the parade of its content creators with demonetization, censorship and unexplained notifications disablement. Youtube comments are also being censored. The default filter is not “Newest first” or “Oldest first” but “Top comments”. Trolls will eventually game the system. I would rather see unfiltered comments. So, here it is:


// ==UserScript==
// @name Show newer YouTube comments
// @namespace com.vsubhash.js.show-youtube-comments-newer
// @description Automatically changes YouTube comments filter off
// @include https://www.youtube.com/watch*
// @exclude %exclude%
// @version 2018
// @grant none
// ==/UserScript==
document.addEventListener("DOMContentLoaded", startItDelayed, false);
function startItDelayed() {
window.setTimeout(switchCommentsList, 10*1000);
}
function switchCommentsList() {
console.log("Showing newer comments");
var arButtons = document.getElementsByTagName("button");
console.log(arButtons.length);
var bFound = false;
for (var i = 0; i < arButtons.length; i++) {
//console.log(arButtons[i].textContent);
if (arButtons[i].textContent.trim() == "Top comments") {
arButtons[i].click();
bFound = true;
break;
}
}
if (bFound) {
//console.log("Menu found————–");
var arButtons = document.getElementsByTagName("button");
console.log(arButtons.length);
for (var i = 0; i < arButtons.length; i++) {
//console.log(arButtons[i].textContent);
if (arButtons[i].textContent.trim() == "Newest first") {
arButtons[i].click();
bFound = true;
break;
}
}
}
}