diff --git a/src/plugins/mediaPlaybackSpeed/README.md b/src/plugins/mediaPlaybackSpeed/README.md new file mode 100644 index 000000000..5e8387544 --- /dev/null +++ b/src/plugins/mediaPlaybackSpeed/README.md @@ -0,0 +1,5 @@ +# MediaPlaybackSpeed + +Allows changing the (default) playback speed of media embeds + +![New icon with menu to change the playback speed](https://github.com/Vendicated/Vencord/assets/24937357/21792b09-8d6a-45be-a6e8-916cdd67a477) diff --git a/src/plugins/mediaPlaybackSpeed/components/SpeedIcon.tsx b/src/plugins/mediaPlaybackSpeed/components/SpeedIcon.tsx new file mode 100644 index 000000000..df93f1332 --- /dev/null +++ b/src/plugins/mediaPlaybackSpeed/components/SpeedIcon.tsx @@ -0,0 +1,19 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +export default function SpeedIcon() { + return ( + + + + ); +} diff --git a/src/plugins/mediaPlaybackSpeed/index.tsx b/src/plugins/mediaPlaybackSpeed/index.tsx new file mode 100644 index 000000000..e1776a933 --- /dev/null +++ b/src/plugins/mediaPlaybackSpeed/index.tsx @@ -0,0 +1,151 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./styles.css"; + +import { definePluginSettings } from "@api/Settings"; +import { classNameFactory } from "@api/Styles"; +import ErrorBoundary from "@components/ErrorBoundary"; +import { makeRange } from "@components/PluginSettings/components"; +import { Devs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { ContextMenuApi, FluxDispatcher, Heading, Menu, React, Tooltip, useEffect } from "@webpack/common"; +import { RefObject } from "react"; + +import SpeedIcon from "./components/SpeedIcon"; + +const cl = classNameFactory("vc-media-playback-speed-"); + +const min = 0.25; +const max = 3.5; +const speeds = makeRange(min, max, 0.25); + +const settings = definePluginSettings({ + test: { + type: OptionType.COMPONENT, + description: "", + component() { + return + Default playback speeds + ; + } + }, + defaultVoiceMessageSpeed: { + type: OptionType.SLIDER, + default: 1, + description: "Voice messages", + markers: speeds, + }, + defaultVideoSpeed: { + type: OptionType.SLIDER, + default: 1, + description: "Videos", + markers: speeds, + }, + defaultAudioSpeed: { + type: OptionType.SLIDER, + default: 1, + description: "Audios", + markers: speeds, + }, +}); + +type MediaRef = RefObject | undefined; + +export default definePlugin({ + name: "MediaPlaybackSpeed", + description: "Allows changing the (default) playback speed of media embeds", + authors: [Devs.D3SOX], + + settings, + + PlaybackSpeedComponent({ mediaRef }: { mediaRef: MediaRef }) { + const changeSpeed = (speed: number) => { + const media = mediaRef?.current; + if (media) { + media.playbackRate = speed; + } + }; + + useEffect(() => { + if (!mediaRef?.current) return; + const media = mediaRef.current; + if (media.tagName === "AUDIO") { + const isVoiceMessage = media.className.includes("audioElement_"); + changeSpeed(isVoiceMessage ? settings.store.defaultVoiceMessageSpeed : settings.store.defaultAudioSpeed); + } else if (media.tagName === "VIDEO") { + changeSpeed(settings.store.defaultVideoSpeed); + } + }, [mediaRef]); + + return ( + + {tooltipProps => ( + + )} + + ); + }, + + renderComponent(mediaRef: MediaRef) { + return + + ; + }, + + patches: [ + // voice message embeds + { + find: "\"--:--\"", + replacement: { + match: /onVolumeShow:\i,onVolumeHide:\i\}\)(?<=useCallback\(\(\)=>\{let \i=(\i).current;.+?)/, + replace: "$&,$self.renderComponent($1)" + } + }, + // audio & video embeds + { + // need to pass media ref via props to make it easily accessible from inside controls + find: "renderControls(){", + replacement: { + match: /onToggleMuted:this.toggleMuted,/, + replace: "$&mediaRef:this.mediaRef," + } + }, + { + find: "AUDIO:\"AUDIO\"", + replacement: { + match: /onVolumeHide:\i,iconClassName:\i.controlIcon,iconColor:"currentColor",sliderWrapperClassName:\i.volumeSliderWrapper\}\)\}\),/, + replace: "$&$self.renderComponent(this.props.mediaRef)," + } + } + ] +}); diff --git a/src/plugins/mediaPlaybackSpeed/styles.css b/src/plugins/mediaPlaybackSpeed/styles.css new file mode 100644 index 000000000..816190a48 --- /dev/null +++ b/src/plugins/mediaPlaybackSpeed/styles.css @@ -0,0 +1,10 @@ +.vc-media-playback-speed-icon { + background-color: transparent; + height: 100%; + z-index: 2; + color: var(--interactive-normal); +} + +.vc-media-playback-speed-icon:hover { + color: var(--interactive-active); +}