Twitch Player Plus

Various tweaks to the Twitch HTML5 player UI

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name       Twitch Player Plus
// @namespace  http://ehsankia.com
// @version    1.0
// @description  Various tweaks to the Twitch HTML5 player UI
// @match      http://www.twitch.tv/*
// @match      http://player.twitch.tv/*
// @grant      GM_addStyle
// @grant      GM_getValue
// @grant      GM_setValue
// @require    http://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js
// @copyright  2015+, Ehsan Kia
// ==/UserScript==

var html5Player;
var waitForPlayerReadyTimer = setInterval(function() {
    html5Player = $('div.player');
    if (html5Player.length > 0) {
      if (html5Player.attr('data-loading') === "false") {
        clearInterval(waitForPlayerReadyTimer);
        window.eval("var flashBackend = $('div.player-video object')[0];");
        setTimeout(applyFixes, 100);
      }
    }
}, 100);

function applyFixes() {

    // Move quality options to main bar
    $(".js-quality").insertAfter($('.js-quality-display-contain'));

    // Remove remaining label
    $("span:contains('Video Quality:')").remove();

    // Hide options if there are no transcoders
    checkForQualityOptions();
    setInterval(checkForQualityOptions, 5000);

    // Bind F key to toggle fullscreen
    $('body').keypress(function(e) {
      if (e.which === 102) {
        // fallback to event.target just in case
        var el = document.activeElement || e.target;
        var t  = (el && el.tagName.toLowerCase()) || '';

        // pass through to elements that take keyboard input
        if (/(input|textarea|select)/.test(t)) {
          return true;
        }

        $('.js-control-fullscreen').click();
        return false;
      }
    });

    // Add latency status under Live icon
    var liveIcon = $('.player-livestatus__online');
    liveIcon.append("<div class='lag-status'></div>");
    window.eval('flashBackend.startPlaybackStatistics();');
    setTimeout(updateLatency, 5000);

    // Remove old stats button and add new one
    $('.player-menu__item--stats').css('display', 'none');
    $('.js-control-fullscreen').before(" \
      <button type='button' class='player-button js-custom-stats-toggle'> \
        <span class='player-tip' data-tip='Video Stats'></span> \
        <svg id='icon-stats' viewBox='0 0 1024 1024' style='width: 16px; fill: white; margin: 1px 6px;'> \
          <path d='M960 0h-896c-35.328 0-64 28.672-64 64v640c0 35.328 28.672 64 64 64h256l-128 256h32l230.4-256h115.2l230.4 256h32l-128-256h256c35.328 0 64-28.672 64-64v-640c0-35.328-28.672-64-64-64zM960 672c0 17.696-14.304 32-32 32h-832c-17.696 0-32-14.304-32-32v-576c0-17.696 14.304-32 32-32h832c17.696 0 32 14.304 32 32v576zM668.096 500.192l-144.672-372.128-158.016 297.28-88.192-90.72-149.216 92.992 42.112 24.256 95.616-59.584 115.36 118.784 133.6-251.296 147.712 380.128 125.984-265.216 51.328 109.248 56.288-9.44-107.328-228.224-120.576 253.92z'></path> \
        </svg> \
      </button>");
    $('.js-custom-stats-toggle').click(function(){
      var prev = $('.js-playback-stats').attr('data-state');
      var state = prev === 'on' ? 'off' : 'on';
      $('.js-playback-stats').attr('data-state', state);
    });

    // Check if it's a VOD and there isn't a seek argument in the url
    var vodID = html5Player.attr('data-video');
    var hasSeekParam = document.location.search.search("t=") >= 0;
    if (vodID !== undefined && !hasSeekParam) {
      //seek to previous position and keep track of the position
      var oldTime = GM_getValue("seek_" + vodID);
      if (oldTime !== undefined) {
        oldTime = parseFloat(oldTime);
        window.eval('flashBackend.videoSeek(' + oldTime + ');');
      }
      setTimeout(function() {
        setInterval(trackSeekTime, 15000);
      }, 5 * 60 * 1000);
    }
}

function checkForQualityOptions() {
  var numQualityOptions = $(".js-quality > option").length;
  if (numQualityOptions > 1) {
    $('.js-quality').css('display', 'block');
  } else {
    $('.js-quality').css('display', 'none');
  }
}

function updateLatency() {
  var lat = $('.js-stat-hls-latency-broadcaster').text();
  if (lat === "" || lat === "NaN") {
    window.eval('flashBackend.stopPlaybackStatistics();');
    window.eval('flashBackend.startPlaybackStatistics();');
    setTimeout(updateLatency, 5000);
  } else {
    $('.lag-status').text(lat + ' sec.');
    setTimeout(updateLatency, 1000);
  }
}

function trackSeekTime() {
  var vodID = html5Player.attr('data-video');
  var seekTime = window.eval('flashBackend.getVideoTime();');
  if (seekTime < 5 * 60) return;
  GM_setValue("seek_" + vodID, seekTime);
}

GM_addStyle(" \
.js-volume-container { width: 13em; } \
select.js-quality:hover { color: #a991d4 !important; } \
select.js-quality, select.js-quality:focus { \
  float: left; \
  height: 29px; \
  margin: 0 6px 0 4px; \
  padding: 0; \
  color: white; \
  font-weight: bold; \
  background: none; \
  border: none; \
  box-shadow: 0 0 black; \
  appearance: none; \
  -moz-appearance: none; \
  -webkit-appearance: none; \
  outline: none; \
  cursor: pointer; \
} \
select.js-quality > option { \
  color: white; \
  background: black; \
  padding: 0 5px; \
  margin-right: -15px; \
  font-weight: bold; \
} \
.lag-status { \
  width: 60px; \
  margin-left: -20px; \
  text-align: center; \
} \
.js-custom-stats-toggle:hover > svg { \
  fill: #a991d4 !important; \
}");