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.

目前為 2014-06-10 提交的版本,檢視 最新版本

// ==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://gf.qytechs.cn/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;
}

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址