Youtube Video Ratings Bar with Power Meter

Places a bar below YouTube thumbnail images which shows not only the likes / dislikes, but also the enthusiasm people have for each videos. This helps you find the best videos and avoid the worst ones.

Per 10-06-2014. Zie de nieuwste versie.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name        Youtube Video Ratings Bar with Power Meter
// @description Places a bar below YouTube thumbnail images which shows not only the likes / dislikes, but also the enthusiasm people have for each videos. This helps you find the best videos and avoid the worst ones.
// @version     2014.06.10
// @include     http://www.youtube.com/*
// @include     https://www.youtube.com/*
// @grant       GM_addStyle
// @grant       GM_xmlhttpRequest
// @namespace https://greatest.deepsurf.us/users/253
// ==/UserScript==

// moves the thumbnail clocks up to avoid overlapping
GM_addStyle(".video-actions, .video-time {bottom:6px !important;})");

// This section could probably be handled in a better way
// On some pages, YouTube adds thumbnails as you scroll down the page. So this does scans whenever you scroll
scanVideos();
document.onload = function() {
    scanVideos();
};
// this will run scanVideos when you scroll, but only if a second has passed since the last time is scanned
var lastScanTime = new Date().getTime();
window.onscroll = function() {
    var timeNow = new Date().getTime();
    var timeDiff = timeNow - lastScanTime
    if (timeDiff >= 1000) {
        scanVideos();
    }
};

function scanVideos() {
    // makes a List of video links which are not in the ".processed" class yet. Once they are processed, they will be added to it.
    var videoList = document.querySelectorAll('a.ux-thumb-wrap[href^="/watch"] > span.video-thumb:not(.processed), a.related-video[href^="/watch"] > span:first-child:not(.processed), a.playlist-video[href^="/watch"] > span.yt-thumb-64:first-child:not(.processed)');
    for ( var i = 0; i < videoList.length; i++ ) {
        // searches for the video id number which we'll use to poll YouTube for ratings information
        var videoId = videoList[i].parentNode.getAttribute("href").replace(/.*[v|s]=([^&%]*).*/, "$1");
        getGdata(videoList[i],videoId);
    }
    lastScanTime = new Date().getTime();
};


// Parts of this were copied from elsewhere because I don't understand GM_xmlhttpRequest as well as I should.
// I did modify it to get the view count and date, and that seems to work
function getGdata(node,videoId) {
    GM_xmlhttpRequest({
        method: 'GET',
        url: "http://gdata.youtube.com/feeds/api/videos/" + videoId + "?v=2&alt=json&fields=yt:rating,yt:statistics,published",
        onload: function(response) {
            if (response.status === 200) {
                var rsp = eval( '(' + response.responseText + ')' );
                if (rsp && rsp.entry && rsp.entry.published && rsp.entry.yt$statistics && rsp.entry.yt$rating) {
                    var daysAgo = (lastScanTime - new Date(rsp.entry.published.$t).getTime())/1000/60/60/24;
                    var views = parseInt(rsp.entry.yt$statistics.viewCount);
                    var likes = parseInt(rsp.entry.yt$rating.numLikes);
                    var dislikes = parseInt(rsp.entry.yt$rating.numDislikes);
                    makeBar(node, daysAgo, views, likes, dislikes);
                }
                else {
                    // if there is no data, mark the thumbnail as "processed" to avoid checking it over and over again
                    node.classList.add('processed');
                }
            }
        }
    });
}

// the ratings bar is made up of differently colored divs stocked on top of each other
function makeBar(node, daysAgo, views, likes, dislikes) {
    // .ratingsBarContainer is for the position (top/bottom) and size of the bar 
    var container = document.createElement('div');
    container.classList.add('ratingsBarContainer');
    container.setAttribute("style","position:absolute; bottom:0px; width:100%; height: 4px;");
    // barMsg is for the "Power: X%" or "View Count Incorrect" messages for the tooltip
    var barMsg = "";
    var totalVotes = likes + dislikes;
    if (dislikes > 0) {
        var redBar = document.createElement('div');
        redBar.classList.add('redBar');
        redBar.setAttribute("style","position:absolute; right:0px; width:100%; height:100%; background-color:#c00;");
        container.appendChild(redBar);
    }
    // Checks to see if the view count has been paused by YouTube. (301-319 views and less than half a day old, or more votes than views)
    // We do this because we need an accurate view count to make a Power Meter.
    // This lets the user know that we can't make one yet, but at least the green/red ratings bar is still available
    if (((views > 300) && (views < 320) && (daysAgo <= 0.5)) || (totalVotes > views)) {
        if (likes > 0) {
            var pauseBar = document.createElement('div');
            pauseBar.classList.add('pauseBar');
            pauseBar.setAttribute("style","position:absolute; height:0px; width:"+ (100 * likes / totalVotes) +"%;");
            pauseBar.style.backgroundColor = "rgb(180, 240, 50)";
            pauseBar.style.borderTop = "4px dotted rgb(31, 177, 90)";
            container.appendChild(pauseBar);
        }
        barMsg = "  View Count Incorrect";
    }
    else {
        powerMeterScore = powerMeter(views, likes);
        if (likes > 0) {
            // 
            var middleBar = document.createElement('div');
            middleBar.classList.add('middleBar');
            if ((100 * likes / totalVotes) >= powerMeterScore) {
            	middleBar.classList.add('green');
                middleBar.setAttribute("style","position:absolute; height:100%; width:"+(100 * likes / totalVotes)+"%;");
                middleBar.style.backgroundColor = "rgb(0, 187, 34)";
            }
            else {
            	middleBar.classList.add('purple');
                middleBar.setAttribute("style","position:absolute; height:0px; width:"+powerMeterScore+"%;");
                middleBar.style.backgroundColor = "rgb(185, 102, 165)";
                middleBar.style.borderTop = "4px dotted rgb(5, 5, 209)";
            } 
            container.appendChild(middleBar);
        }
        if (powerMeterScore > 0) {
            var blueBar = document.createElement('div');
            blueBar.classList.add('blueBar');
            blueBar.setAttribute("style","position:absolute; background-color:rgb(53, 165, 201); border-top: 4px dotted rgb(0, 41, 255); height:0px;");
            if ((100 * likes / totalVotes) > powerMeterScore) {
                blueBar.style.width = powerMeterScore+"%";
            }
            else {
                blueBar.style.width = ((100 * likes / totalVotes))+"%";
            }
            barMsg = "  Power: "+ Math.round(powerMeterScore*100)/100 +"%";
            container.appendChild(blueBar);
        }
    }
    node.appendChild(container);
    node.setAttribute("title","Likes: "+ likes +"  Dislikes: "+ dislikes + barMsg);
    node.classList.add('processed');
}

// trade secrets
function powerMeter(views, likes) {
    if (views < 2000) {
        var viewLikeRatio2k = Math.round( (views + views * ((3000-views)/2000)) / (likes) );
        if (views < 255) {
            var viewLikeRatio = Math.round( viewLikeRatio2k / (views/255) );
        } 
        else {
            var viewLikeRatio = viewLikeRatio2k;
        }
    }
    else {
        var viewLikeRatio = Math.round( (views+7000) / 3 / (likes) );
    }
    if ((viewLikeRatio < 1) || (viewLikeRatio > 255)) {
        return 0;
    }
    var powerMeterScore = Math.round(Math.pow(((255-viewLikeRatio)/2.55), 3)) / 10000;
    return powerMeterScore;
}