YouTube Video Auto Pop-out

Pop-out video to bottom-right when scrolling down to comments

スクリプトをインストール?
作者が勧める他のスクリプト

YouTube Auto Likeも気に入るかもしれません。

スクリプトをインストール
このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name         YouTube Video Auto Pop-out
// @namespace    http://tampermonkey.net/
// @version      1.04
// @description  Pop-out video to bottom-right when scrolling down to comments
// @author       You
// @match        https://www.youtube.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function() {
  "use strict";

  let originalStyles;
  const fixedStyles = {
    position: "fixed",
    top: "auto",
    left: "auto",
    bottom: "20px",
    right: "50px",
    zIndex: "1000",
    transition: "none",
  };

  const playButton = `
    <svg height="100%" version="1.1" viewBox="0 0 36 36" width="100%">
      <path
        class="ytp-svg-fill"
        d="M 12,26 18.5,22 18.5,14 12,10 z M 18.5,22 25,18 25,18 18.5,14 z"
      />
    </svg>
  `;
  const pauseButton = `
    <svg height="100%" version="1.1" viewBox="0 0 36 36" width="100%">
      <path
        class="ytp-svg-fill"
        d="M 12,26 16,26 16,10 12,10 z M 21,26 25,26 25,10 21,10 z"
      />
    </svg>
  `;

  function printLog(message) {
    console.log(`[YouTube Video Pop-out]: ${message}`);
  }

  function getVideoPlayer() {
    return document.querySelector("video.html5-main-video");
  }

  function videoHasEnded() {
    const video = getVideoPlayer();
    const progress = video.currentTime / video.duration;
    return progress === 1;
  }

  function isValidVideo() {
    return getVideoPlayer().duration > 0;
  }

  function setStyles(elem, propertyObject) {
    for (let property in propertyObject) elem.style[property] = propertyObject[property];
  }

  function getVideoSize(fixed = false) {
    const rect = getVideoPlayer().getBoundingClientRect();
    const width = fixed ? rect.width / 2 : rect.width;
    const height = fixed ? rect.height / 2 : rect.height;
    return {
      width: `${width}px`,
      height: `${height}px`
    };
  }

  function setPlayIcon(button, paused) {
    const icon = paused ? playButton : pauseButton;
    button.innerHTML = icon;
  }

  function setButtonVisible(button, visible) {
    button.style.display = visible ? "block" : "none";
  }

  function removeFixedContainer() {
    const container = document.querySelector("#fixed-container");
    if (container) container.remove();
  }

  function moveVideoToCorner() {
    const videoPlayer = getVideoPlayer();
    if (videoPlayer.classList.contains("in-corner")) return false;

    originalStyles = {
      position: videoPlayer.style.position,
      // top: videoPlayer.style.top,
      // left: videoPlayer.style.left,
      width: getVideoSize().width,
      height: getVideoSize().height,
      zIndex: videoPlayer.style.zIndex,
      transition: videoPlayer.style.transition,
    };
    fixedStyles.width = getVideoSize(true).width;
    fixedStyles.height = getVideoSize(true).height;

    const videoContainer = document.createElement("div");
    videoContainer.setAttribute("id", "fixed-container");
    const containerStyle = {
      ...fixedStyles,
      cursor: "move"
    };
    setStyles(videoContainer, containerStyle);

    const pauseButton = document.createElement("div");
    setPlayIcon(pauseButton, videoPlayer.paused);
    pauseButton.addEventListener("click", () => {
      videoPlayer.paused ? videoPlayer.play() : videoPlayer.pause();
    });
    videoPlayer.addEventListener("play", () => setPlayIcon(pauseButton, false));
    videoPlayer.addEventListener("pause", () => setPlayIcon(pauseButton, true));
    videoPlayer.addEventListener("ended", () => restoreVideoPosition());

    const buttonStyles = {
      display: "none",
      position: "absolute",
      height: "36px",
      top: "calc(50% - 18px)",
      left: "calc(50% - 18px)",
      cursor: "pointer",
      zIndex: "1100",
    };
    setStyles(pauseButton, buttonStyles);
    videoContainer.appendChild(pauseButton);

    setStyles(videoPlayer, {
      width: "100%",
      height: "100%"
    });

    videoPlayer.style.pointerEvents = "none";
    videoPlayer.classList.add("in-corner");
    videoContainer.appendChild(videoPlayer);
    document.querySelector("body").appendChild(videoContainer);

    videoContainer.addEventListener("mouseenter", () => setButtonVisible(pauseButton, true));
    videoContainer.addEventListener("mouseleave", () => setButtonVisible(pauseButton, false));

    makeDraggable(videoContainer);
  }

  function restoreVideoPosition() {
    const videoPlayer = getVideoPlayer();
    if (!videoPlayer.classList.contains("in-corner")) return false;

    setStyles(originalStyles);
    videoPlayer.style.pointerEvents = "auto";
    videoPlayer.classList.remove("in-corner");
    document.querySelector(".html5-video-container").appendChild(videoPlayer);
    document.querySelector("#fixed-container").remove();
  }

  function makeDraggable(elem) {
    let pos1 = 0,
      pos2 = 0,
      pos3 = 0,
      pos4 = 0;

    elem.onmousedown = dragMouseDown;

    function dragMouseDown(e) {
      e = e || window.event;
      // get the mouse cursor position at startup:
      pos3 = e.clientX;
      pos4 = e.clientY;
      document.onmouseup = closeDragElement;
      // call a function whenever the cursor moves:
      document.onmousemove = elementDrag;
    }

    function elementDrag(e) {
      e = e || window.event;
      // calculate the new cursor position:
      pos1 = pos3 - e.clientX;
      pos2 = pos4 - e.clientY;
      pos3 = e.clientX;
      pos4 = e.clientY;
      // set the element's new position:
      const topPosition = elem.offsetTop - pos2 + "px";
      elem.style.top = topPosition;
      fixedStyles.top = topPosition;
      const leftPosition = elem.offsetLeft - pos1 + "px";
      elem.style.left = leftPosition;
      fixedStyles.left = leftPosition;
      fixedStyles.right = "auto";
    }

    function closeDragElement() {
      // stop moving when mouse button is released:
      document.onmouseup = null;
      document.onmousemove = null;
    }
  }

  function isElementInvisible(el) {
    const rect = el.getBoundingClientRect();

    return (
      rect.bottom <= 0 ||
      rect.right <= 0 ||
      rect.top >= (window.innerHeight || document.documentElement.clientHeight) ||
      rect.left >= (window.innerWidth || document.documentElement.clientWidth)
    );
  }

  function scrollListener() {
    const playerSection = document.querySelector("#player");
    const playerIsVisible = isElementInvisible(playerSection);

    if (playerIsVisible && isValidVideo() && !videoHasEnded()) {
      moveVideoToCorner();
    } else {
      restoreVideoPosition();
    }
  }

  document.addEventListener("yt-navigate-finish", () => {
    const isVideoPage = location.pathname === "/watch";
    if (isVideoPage) return document.addEventListener("scroll", scrollListener);
    document.removeEventListener("scroll", scrollListener);
    removeFixedContainer();
  });
})();