Friday, March 9, 2012

YouTube Likes


// ==UserScript==
// @name          YouTube Likes
// @description   Displays number of likes and dislikes of YouTube videos.
// @author        TurplePurtle
// @version       1.6.1
// @include       http://www.youtube.com/*
// @include       https://www.youtube.com/*
// ==/UserScript==


// Utils
function $(s, a, p) {
 return (p || document)[a ? "querySelectorAll" : "querySelector"](s);
}
function mkEl(kind, text) {
 return kind === "text" ? document.createTextNode(text) : document.createElement(kind);
}


// Global objects
var _ytl = {}; //will contain JSON callback
var _vids = {};


// Attach global to window for JSONP callback
if(window.navigator.vendor.match(/Google/)) {
 var _div = mkEl("div");
 _div.setAttribute("onclick", "return window;");
 _div.onclick()._ytl = _ytl;
} else {
 unsafeWindow._ytl = _ytl;
}


// handle JSON
_ytl.rate = function(obj) {
 var data = obj.entry;
  id = data.media$group.yt$videoid.$t;
  rating = data.yt$rating;

 if (!(data && id && rating)) return;

 var cont = mkEl("span"), //container
  ups = mkEl("span"), //likes
  downs = mkEl("span"); //dislikes

 var likes = +rating.numLikes,
  dislikes = +rating.numDislikes,
  total = likes + dislikes;

 cont.className = "stat yt-likes";
 ups.style.color = "#060";
 ups.innerHTML = rating.numLikes;
 downs.style.color = "#c00";
 downs.innerHTML = rating.numDislikes;

 if (total > 0) {
  var percent = mkEl("span"),
   r = Math.round(dislikes / total * 0xcc).toString(16),
   g = Math.round(likes / total * 0x66).toString(16);
  if (r.length === 1) r = "0" + r;
  if (g.length === 1) g = "0" + g;
  percent.style.color = "#" + r + g + "00";
  percent.innerHTML = Math.round(100 * likes / total) + "% ";
  cont.appendChild(percent);
 }
 cont.appendChild(mkEl("text", "("));
 cont.appendChild(ups);
 cont.appendChild(mkEl("text", " | "));
 cont.appendChild(downs);
 cont.appendChild(mkEl("text", ")"));
 
 var containers = _vids[id]; // where to place the rating on the page
 containers[0].appendChild(cont);
 if (containers.length > 1) {
  for (var i=1; i<containers.length; i++) {
   containers[i].appendChild(cont.cloneNode(true));
  }
 }

 var s = $("head #v_" + id);
 s.parentNode.removeChild(s);
};

// Find element where likes should be placed
function findContainer(el) {
 var elClass = el.className;

 if (elClass.indexOf("related-video") >= 0) { // video sidebar
  return el;
 } else if (elClass.indexOf("video-list-item-link") >= 0) { // front page sidebar
  return el;
 } else if (elClass.indexOf("title") >= 0) { // front page
  return $(".metadata", false, el.parentNode.parentNode);
 } else if (elClass.indexOf("yt-uix-tile-link") >= 0) { // search results
  return $(".facets", false, el.parentNode.parentNode);
 }
}

function listVids(links) {
 var idRegex = /^[^v]+v.(.{11}).*/, idReplace = "$1", vids = _vids;

 for (var i=0, n=links.length; i < n; i++) {
  var likesContainer = findContainer(links[i]);

  if (likesContainer) {
   var linkID = links[i].href.replace(idRegex, idReplace);

   if (vids[linkID])
    vids[linkID].push(likesContainer);
   else
    vids[linkID] = [likesContainer];
  }
 }
 
 return vids;
}

function getJSON(vids) {
 var jsonSrc = [
  "https://gdata.youtube.com/feeds/api/videos/", null,
  "?v=2&alt=json-in-script&callback=_ytl.rate&fields=yt:rating,media:group(yt:videoid)&_ytl=t",
  Date.now().toString() // prevent caching the script
 ];

 for (var id in vids) {
  var s = mkEl("script");
  jsonSrc[1] = id;
  s.src = jsonSrc.join("");
  s.async = true;
  s.id = "v_" + id;
  document.head.appendChild(s);
 }
}

getJSON(listVids($('a[href*="/watch?v="]', true)));

0 comments:

Post a Comment