Greasy Fork

来自缓存

Zoom and crop YouTube videos to fill screen height

Removes letterboxing by cropping the left and right edges off of full-screen YouTube videos and zooming to fill the screen height.

当前为 2019-01-08 提交的版本,查看 最新版本

// ==UserScript==
// @name Zoom and crop YouTube videos to fill screen height
// @version      0.4
// @description  Removes letterboxing by cropping the left and right edges off of full-screen YouTube videos and zooming to fill the screen height.
// @author       uxamend
// @namespace    Violentmonkey Scripts
// @match        https://www.youtube.com/*
// @grant        none
// @run-at       document-idle
// @license      CC0-1.0
// @compatible   firefox version >=64 (older versions untested)
// @compatible   chrome version >=71 (older versions untested)
// ==/UserScript==

"use strict";

var debug_logging_on = false;
var debug_script_name = "Userscript: Zoom YouTube videos to fill screen height"

// For tall screens: Sets the narrowest aspect ratio that videos will ever be cropped to.
var min_cropped_aspect = 16/10; // Express as a fraction, e.g. 16/10, not 16:10.

// For very wide videos: The maximum proportion of video width to crop off
var max_crop_proportion = 1;

function debug_log(message) {
  if(debug_logging_on){
    console.log("[" + debug_script_name + "] " + message);
  }
}

// Define a mutation observer and callback to re-apply zoom when ads start and end.
function mo_callback(mutation_list, observer) {
  mutation_list.forEach((mutation) => {
    if(mutation.type == "childList"){
      debug_log("Ad started or ended.");
      set_zoom_with_timeout();
    }
  });
}

var mo = new MutationObserver(mo_callback);


function set_zoom() {
  if(document.fullscreenElement) {
    debug_log("Called to set zoom.");
    
    var video = document.getElementsByClassName("html5-main-video")[0];
    var vs = video.style;
    var w = window.innerWidth;
    var h = window.innerHeight;
    
    var video_aspect = video.videoWidth/video.videoHeight;
    var screen_aspect = w/h;
    
    // Only zoom and crop videos that are wide enough to crop
    if(video_aspect > screen_aspect && video_aspect > min_cropped_aspect) {
      debug_log("Video is wider than screen. Setting zoom.");
      
      var vh = h; // height of video
      
      // Apply min_cropped_aspect constraint to video height
      if (min_cropped_aspect > screen_aspect) vh = w/min_cropped_aspect;
      
      var vw = video_aspect * vh; // width of video, including cropped portion
      
      // Apply max_crop_proportion constraint to video width
      if (w/vw < 1-max_crop_proportion) vw = w + vw * max_crop_proportion;
      
      var vt = (h-vh)/2; // top edge position of video
      var vl = (w-vw)/2; // left edge position of video
      
      debug_log("Calculated new video dimensions:");
      debug_log("vh:" + vh + ", vw:" + vw + ", vt:" + vt + ", vl:" + vl + ".");
      debug_log("h:" + h + ", w:" + w + ".");
      debug_log("screen_aspect:" + screen_aspect + ", min_cropped_aspect:" + min_cropped_aspect + ".");
      debug_log("video_aspect:" + video_aspect + ", max_crop_proportion:" + max_crop_proportion + ".");
      
      vs.height = vh+"px";
      vs.width = vw+"px";
      vs.top = vt+"px";
      vs.left = vl+"px";
    } else {
      debug_log("Video is not wider than screen. No need to set zoom.");
    }
    
    // Since we're now in full-screen, we should observe when ads start or stop and
    // re-apply the zoom when they do.
    var ad_module = document.getElementsByClassName("ytp-ad-module")[0];
    mo.observe(ad_module, {"childList" : true});
    
  } else {
      debug_log("Not full-screen. Not setting zoom.");
      
      // Don't waste time detecting ads when we're not in fullscreen
      mo.disconnect();
  }
}


function set_zoom_with_timeout() {
  // Timeout to minimise chance of a race condition against standard YouTube behaviour.
  // (Our function needs to run after the video has changed to standard full-screen size.)
  setTimeout(set_zoom, 200);
}


function add_event_listener_for_zoom() {
  debug_log("Adding fullscreenchange event listener.")
  document.addEventListener(
    'fullscreenchange',
    function() {
      debug_log("Full-screen state changed.");
      set_zoom_with_timeout();
    }
  );
}

add_event_listener_for_zoom();