Greasy Fork

Greasy Fork is available in English.

YouTube RatingBars (Like/Dislike Rating)

It shows RatingBars whitch represents Like/Dislike Rating ratio.

目前为 2017-09-23 提交的版本。查看 最新版本

// ==UserScript==
// @name        YouTube RatingBars (Like/Dislike Rating)
// @namespace   knoa.jp
// @description It shows RatingBars whitch represents Like/Dislike Rating ratio.
// @description 動画へのリンクに「高く評価」された比率を示すバーを表示します。
// @include     https://www.youtube.com/*
// @version     2.1.0
// @grant       none
// @noframes
// en:
// API limits 1M queries/day. (approximately 100 views by 10,000 users.)
// You can use your own APIKEY to support this script.
// https://console.developers.google.com/apis/
// It doesn't support Ajax additional videos yet.
// ja:
// APIの制限は1日あたり100万クエリ(1万ユーザーなら1人あたり100ビュー)です。
// 各自でAPIKEYを書き換えてくれるとスクリプトの寿命が延びます。
// https://console.developers.google.com/apis/
// Ajax追加要素への対応は保留。
// ==/UserScript==

(function () {
  const SCRIPTNAME = 'YouTubeRatingBars';
  const DEBUG = false;/**/
  console.time(SCRIPTNAME);
  const HEIGHT = '2px';/*border height*/
  const DISLIKECOLOR = 'rgba(136, 136, 136, 0.4)';
  const LIKECOLOR = 'rgb(39, 147, 230)';
  const MAXRESULTS = 48;/* API limits 50 videos per request */
  const APIKEY = 'AIzaSyAyOgssM7s_vvOUDV0ZTRvk6LrTwr_1f5k';
  const API = 'https://www.googleapis.com/youtube/v3/videos?id={VIDEOIDS}&part=statistics&fields=items(id,statistics)&maxResults=' + MAXRESULTS + '&key=' + APIKEY;
  const NEW = (!document.querySelector('body#body'));
  const VIEWS = (NEW) ? {/* querySelectors on each views */
    example: ['items', 'anchor[href]', 'insertParent', 'insertAfter'],
    home: ['ytd-grid-video-renderer', 'a', '#metadata', '#metadata-line'],
    results: ['ytd-video-renderer', 'a', 'ytd-video-meta-block', '#metadata'],
    watch: ['ytd-compact-video-renderer', 'a', '#metadata', '#metadata-line'],
  } : {
    example: ['items', 'anchor[href]', 'insertParent', 'insertAfter'],
    home: ['#feed ul > li.yt-shelf-grid-item', 'a', 'div.yt-lockup-content', 'div.yt-lockup-meta'],
    results: ['ol.item-section > li', 'div.yt-lockup-video a.yt-uix-tile-link[href]', 'div.yt-lockup-meta', 'ul.yt-lockup-meta-info'],
    watch: ['li.video-list-item', 'a.content-link[href]', 'a.content-link', 'span.view-count'],
  };
  const BARS = (NEW)/* Bar in video pages */
  ? `<div id="container" class="style-scope ytd-sentiment-bar-renderer" style="background-color: ${DISLIKECOLOR}"><div id="like-bar" class="style-scope ytd-sentiment-bar-renderer" style="width: {LIKES}%;height: ${HEIGHT};background-color: ${LIKECOLOR}"></div></div>`
  : `<div class="video-extras-sparkbars"><div class="video-extras-sparkbar-likes" style="width: {LIKES}%;height: ${HEIGHT};"></div><div class="video-extras-sparkbar-dislikes" style="width: {DISLIKES}%;height: ${HEIGHT};"></div></div>`;
  let view, items = [], previousContent = '';
  let core = {
    initialize: function(){
      let previousUrl = null;
      setInterval(function(){
        if(location.href === previousUrl) return;
        previousUrl = location.href;
        switch(true){
          case(location.href === 'https://www.youtube.com/'):
            view = VIEWS.home;
            break;
          case(location.href.startsWith('https://www.youtube.com/results?')):
            view = VIEWS.results;
            break;
          case(location.href.startsWith('https://www.youtube.com/watch?')):
            view = VIEWS.watch;
            break;
          default:
            return;
        }
        items.length = 0;
        core.getItems();
      }, 1000);
    },
    getItems: function(){
      let previousLength = items.length;
      items = document.querySelectorAll(view[0]);
      if(items.length === 0) return setTimeout(core.getItems, 1000);
      if(items.length !== previousLength) return setTimeout(core.getItems, 1000);/*on loading*/
      if(items[0].textContent === previousContent) return setTimeout(core.getItems, 1000);/*not yet replaced content*/
      previousContent = items[0].textContent;
      setTimeout(core.getBars, 100);
    },
    getBars: function(){
      let videoids = [];
      for(let i = 0; items[i]; i++){
        try{
          let id = items[i].querySelector(view[1]).href.match(/\?v=([^&]+)/)[1];
          videoids.push(id);
          items[i].dataset.videoid = id;
        }catch(e){
          continue;
        }
      }
      videoids.length = Math.min(videoids.length, MAXRESULTS);
      let xhr = new XMLHttpRequest();
      xhr.responseType = 'json';
      xhr.open('GET', API.replace('{VIDEOIDS}', videoids.join()));
      xhr.onreadystatechange = function () {
        if(xhr.readyState !== 4 || xhr.status !== 200) return;
        if(!xhr.response.items) return;
        let bars = {};
        for(let i = 0; xhr.response.items[i]; i++){
          let v = xhr.response.items[i], s = v.statistics;
          if(!s.likeCount && !s.dislikeCount) continue;
          bars[v.id] = BARS;
          bars[v.id] = bars[v.id].replace('{LIKES}', (100 * parseInt(s.likeCount)) / (parseInt(s.likeCount) + parseInt(s.dislikeCount)));
          bars[v.id] = bars[v.id].replace('{DISLIKES}', (100 * parseInt(s.dislikeCount)) / (parseInt(s.likeCount) + parseInt(s.dislikeCount)));
        }
        for(let i = 0; items[i]; i++){
          if(!bars[items[i].dataset.videoid]) continue;
          let bar = document.createElement('div');
          bar.innerHTML = bars[items[i].dataset.videoid];
          bar.id = SCRIPTNAME;
          let oldBar = items[i].querySelector('#' + SCRIPTNAME);
          if(oldBar){
            oldBar.parentNode.replaceChild(bar, oldBar);
          }else{
            items[i].querySelector(view[2]).insertBefore(bar, items[i].querySelector(view[3]).nextElementSibling);
          }
        }
      };
      xhr.send();
    },
  };
  let log = function(){
    let l = log.last = log.now || new Date(), n = log.now = new Date();
    console.log(
      'SCRIPTNAME' + ':',
      /* 00:00:00.000 */ n.toLocaleTimeString() + '.' + n.getTime().toString().slice(-3),
      /* +0.000s      */ '+' + ((n-l)/1000).toFixed(3) + 's',
      /* :00          */ ':' + new Error().stack.match(/:[0-9]+:[0-9]+/g)[1].split(':')[1],/*LINE*/
      /* caller.caller */ (log.caller.caller && log.caller.caller.name ? `${log.caller.caller.name}() => ` : '') +
      /* caller        */ `${log.caller.name}()`,
      ...arguments
    );
    if(arguments.length === 1) return arguments[0];
  };
  core.initialize();
  console.timeEnd(SCRIPTNAME);
})();