Greasy Fork

Greasy Fork is available in English.

画中画

在任意网站开启画中画模式,使视频漂浮在所有窗口最上层

当前为 2021-04-22 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name 画中画
// @name:en Pic-in-pic
// @namespace       https://github.com/zaaack/tools/
// @description  在任意网站开启画中画模式,使视频漂浮在所有窗口最上层
// @description:en    Enable picture-in-picture mode in any video site, make video float on top window
// @version         1.0
// @author          zaaack
// @include         *
// @grant           unsafeWindow
// @run-at            context-menu
// @homepageURL       https://github.com/zaaack/tools/blob/master/tampermonkey/pic-in-pic/README.md
// @supportURL        https://github.com/zaaack/tools/issues
// ==/UserScript==

;(() => {
  // fork from https://github.com/GoogleChromeLabs/picture-in-picture-chrome-extension/blob/20288da7c56b525f2c21129df58d45b027d4f328/src/script.js#L2
  //
  //
  // Copyright 2018 Google LLC
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //      http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.

  function findLargestPlayingVideo() {
    const videos = Array.from(document.querySelectorAll('video'))
      .filter((video) => video.readyState != 0)
      .filter((video) => video.disablePictureInPicture == false)
      .sort((v1, v2) => {
        const v1Rect = v1.getClientRects()[0] || { width: 0, height: 0 }
        const v2Rect = v2.getClientRects()[0] || { width: 0, height: 0 }
        return v2Rect.width * v2Rect.height - v1Rect.width * v1Rect.height
      })

    if (videos.length === 0) {
      return
    }

    return videos[0]
  }

  async function requestPictureInPicture(video) {
    await video.requestPictureInPicture()
    video.setAttribute('__pip__', true)
    video.addEventListener(
      'leavepictureinpicture',
      (event) => {
        video.removeAttribute('__pip__')
      },
      { once: true }
    )
    new ResizeObserver(maybeUpdatePictureInPictureVideo).observe(video)
  }

  function maybeUpdatePictureInPictureVideo(entries, observer) {
    const observedVideo = entries[0].target
    if (!document.querySelector('[__pip__]')) {
      observer.unobserve(observedVideo)
      return
    }
    const video = findLargestPlayingVideo()
    if (video && !video.hasAttribute('__pip__')) {
      observer.unobserve(observedVideo)
      requestPictureInPicture(video)
    }
  }

  ;(async () => {
    const video = findLargestPlayingVideo()
    if (!video) {
      return
    }
    if (video.hasAttribute('__pip__')) {
      document.exitPictureInPicture()
      return
    }
    await requestPictureInPicture(video)
    // chrome.runtime.sendMessage({ message: 'enter' })
  })()
})()