From 4c9996d620322e4a4f09538e829f2fe52e5f18a2 Mon Sep 17 00:00:00 2001 From: Hugo C Date: Fri, 8 Sep 2023 03:57:44 +0200 Subject: [PATCH] feat(plugin): PictureInPicture (#1697) Co-authored-by: V --- src/plugins/pictureInPicture.tsx | 83 ++++++++++++++++++++++++++++++++ src/utils/constants.ts | 6 ++- 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 src/plugins/pictureInPicture.tsx diff --git a/src/plugins/pictureInPicture.tsx b/src/plugins/pictureInPicture.tsx new file mode 100644 index 00000000..7a9e7903 --- /dev/null +++ b/src/plugins/pictureInPicture.tsx @@ -0,0 +1,83 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { definePluginSettings } from "@api/Settings"; +import ErrorBoundary from "@components/ErrorBoundary"; +import { Devs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { React, Tooltip } from "@webpack/common"; + +const settings = definePluginSettings({ + loop: { + description: "Whether to make the PiP video loop or not", + type: OptionType.BOOLEAN, + default: true, + restartNeeded: false + } +}); + +export default definePlugin({ + name: "PictureInPicture", + description: "Adds picture in picture to videos (next to the Download button)", + authors: [Devs.Lumap], + settings, + + patches: [ + { + find: ".onRemoveAttachment,", + replacement: { + match: /\.nonMediaAttachment.{0,10}children:\[(\i),/, + replace: "$&$1&&$self.renderPiPButton()," + }, + }, + ], + + renderPiPButton: ErrorBoundary.wrap(() => { + return ( + + {tooltipProps => ( +
{ + const video = e.currentTarget.parentNode!.parentNode!.querySelector("video")!; + const videoClone = document.body.appendChild(video.cloneNode(true)) as HTMLVideoElement; + + videoClone.loop = settings.store.loop; + videoClone.style.display = "none"; + videoClone.onleavepictureinpicture = () => videoClone.remove(); + + function launchPiP() { + videoClone.currentTime = video.currentTime; + videoClone.requestPictureInPicture(); + video.pause(); + videoClone.play(); + } + + if (videoClone.readyState === 4 /* HAVE_ENOUGH_DATA */) + launchPiP(); + else + videoClone.onloadedmetadata = launchPiP; + }} + > + + + +
+ )} +
+ ); + }, { noop: true }) +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 245c8bbb..400fcaa4 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -355,6 +355,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "bb010g", id: 72791153467990016n, }, + Lumap: { + name: "lumap", + id: 635383782576357407n + }, Dolfies: { name: "Dolfies", id: 852892297661906993n, @@ -362,7 +366,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({ RuukuLada: { name: "RuukuLada", id: 119705748346241027n, - }, + } } satisfies Record); // iife so #__PURE__ works correctly