/* * Vencord, a Discord client mod * Copyright (c) 2023 Vendicated and contributors * SPDX-License-Identifier: GPL-3.0-or-later */ import "./styles.css"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import definePlugin from "@utils/types"; import { Tooltip } from "@webpack/common"; import type { Component } from "react"; interface Props { embed: { rawTitle: string; provider?: { name: string; }; thumbnail: { proxyURL: string; }; video: { url: string; }; dearrow: { enabled: boolean; oldTitle?: string; oldThumb?: string; }; }; } const embedUrlRe = /https:\/\/www\.youtube\.com\/embed\/([a-zA-Z0-9_-]{11})/; async function embedDidMount(this: Component) { try { const { embed } = this.props; if (!embed || embed.dearrow || embed.provider?.name !== "YouTube" || !embed.video?.url) return; const videoId = embedUrlRe.exec(embed.video.url)?.[1]; if (!videoId) return; const res = await fetch(`https://sponsor.ajay.app/api/branding?videoID=${videoId}`); if (!res.ok) return; const { titles, thumbnails } = await res.json(); const hasTitle = titles[0]?.votes >= 0; const hasThumb = thumbnails[0]?.votes >= 0; if (!hasTitle && !hasThumb) return; embed.dearrow = { enabled: true }; if (titles[0]?.votes >= 0) { embed.dearrow.oldTitle = embed.rawTitle; embed.rawTitle = titles[0].title; } if (thumbnails[0]?.votes >= 0) { embed.dearrow.oldThumb = embed.thumbnail.proxyURL; embed.thumbnail.proxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`; } this.forceUpdate(); } catch (err) { new Logger("Dearrow").error("Failed to dearrow embed", err); } } function renderButton(this: Component) { const { embed } = this.props; if (!embed?.dearrow) return null; return ( {({ onMouseEnter, onMouseLeave }) => ( )} ); } export default definePlugin({ name: "Dearrow", description: "Makes YouTube embed titles and thumbnails less sensationalist, powered by Dearrow", authors: [Devs.Ven], embedDidMount, renderButton: ErrorBoundary.wrap(renderButton, { noop: true }), patches: [{ find: "this.renderInlineMediaEmbed", replacement: [ // patch componentDidMount to replace embed thumbnail and title { match: /(\i).render=function.{0,50}\i\.embed/, replace: "$1.componentDidMount=$self.embedDidMount,$&" }, // add dearrow button { match: /children:\[(?=null!=\i\?\i\.renderSuppressButton)/, replace: "children:[$self.renderButton.call(this)," } ] }], });