画中画

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

// ==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' })
  })()
})()

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址