+ }
+
+ );
+};
diff --git a/src/equicordplugins/betterMicrophone.desktop/components/index.tsx b/src/equicordplugins/betterMicrophone.desktop/components/index.tsx
new file mode 100644
index 00000000..fb0c72d5
--- /dev/null
+++ b/src/equicordplugins/betterMicrophone.desktop/components/index.tsx
@@ -0,0 +1,19 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./MicrophoneSettingsModal";
diff --git a/src/equicordplugins/betterMicrophone.desktop/constants/constants.ts b/src/equicordplugins/betterMicrophone.desktop/constants/constants.ts
new file mode 100644
index 00000000..961c3d20
--- /dev/null
+++ b/src/equicordplugins/betterMicrophone.desktop/constants/constants.ts
@@ -0,0 +1,31 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Devs } from "@utils/constants";
+
+import { types } from "../../philsPluginLibrary";
+
+export const PluginInfo = {
+ PLUGIN_NAME: "BetterMicrophone",
+ DESCRIPTION: "This plugin allows you to further customize your microphone.",
+ AUTHOR: {
+ ...Devs.philhk,
+ github: "https://github.com/philhk"
+ },
+ CONTRIBUTORS: {}
+} as const satisfies types.PluginInfo;
diff --git a/src/equicordplugins/betterMicrophone.desktop/constants/index.ts b/src/equicordplugins/betterMicrophone.desktop/constants/index.ts
new file mode 100644
index 00000000..c7b948ab
--- /dev/null
+++ b/src/equicordplugins/betterMicrophone.desktop/constants/index.ts
@@ -0,0 +1,19 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./constants";
diff --git a/src/equicordplugins/betterMicrophone.desktop/index.tsx b/src/equicordplugins/betterMicrophone.desktop/index.tsx
new file mode 100644
index 00000000..f4cd29a8
--- /dev/null
+++ b/src/equicordplugins/betterMicrophone.desktop/index.tsx
@@ -0,0 +1,63 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Devs } from "@utils/constants";
+import definePlugin from "@utils/types";
+
+import { addSettingsPanelButton, Emitter, MicrophoneSettingsIcon, removeSettingsPanelButton } from "../philsPluginLibrary";
+import { PluginInfo } from "./constants";
+import { openMicrophoneSettingsModal } from "./modals";
+import { MicrophonePatcher } from "./patchers";
+import { initMicrophoneStore } from "./stores";
+
+var microphonePatcher;
+
+// const settings = definePluginSettings({
+// openSettings: {
+// description: "",
+// type: OptionType.COMPONENT,
+// component: (() => {
+// return (
+//
+// );
+// })
+// }
+// });
+
+export default definePlugin({
+ name: "PhilsBetterMicrophone",
+ description: "This plugin allows you to further customize your microphone.",
+ authors: [Devs.philhk],
+ dependencies: ["PhilsPluginLibrary"],
+
+ start() {
+ initMicrophoneStore();
+ microphonePatcher = new MicrophonePatcher().patch();
+ addSettingsPanelButton({ name: PluginInfo.PLUGIN_NAME, icon: MicrophoneSettingsIcon, tooltipText: "Microphone Settings", onClick: openMicrophoneSettingsModal });
+ },
+
+ stop() {
+ microphonePatcher?.unpatch();
+ Emitter.removeAllListeners(PluginInfo.PLUGIN_NAME);
+ removeSettingsPanelButton(PluginInfo.PLUGIN_NAME);
+ },
+
+ // settings,
+});
+
+export { microphonePatcher };
diff --git a/src/equicordplugins/betterMicrophone.desktop/logger/index.ts b/src/equicordplugins/betterMicrophone.desktop/logger/index.ts
new file mode 100644
index 00000000..828c0f86
--- /dev/null
+++ b/src/equicordplugins/betterMicrophone.desktop/logger/index.ts
@@ -0,0 +1,23 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Logger } from "@utils/Logger";
+
+import { PluginInfo } from "../constants";
+
+export const logger = new Logger(PluginInfo.PLUGIN_NAME);
diff --git a/src/equicordplugins/betterMicrophone.desktop/modals/index.tsx b/src/equicordplugins/betterMicrophone.desktop/modals/index.tsx
new file mode 100644
index 00000000..781f5d3c
--- /dev/null
+++ b/src/equicordplugins/betterMicrophone.desktop/modals/index.tsx
@@ -0,0 +1,43 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { openModalLazy } from "@utils/modal";
+
+import { MicrophoneSettingsModal } from "../components";
+import { PluginInfo } from "../constants";
+import Plugin from "../index";
+import { microphoneStore } from "../stores";
+
+const onMicrophoneModalDone = () => {
+ const { microphonePatcher } = Plugin;
+
+ if (microphonePatcher)
+ microphonePatcher.forceUpdateTransportationOptions();
+};
+
+export const openMicrophoneSettingsModal =
+ () => openModalLazy(async () => {
+ return props =>
+ ;
+ });
diff --git a/src/equicordplugins/betterMicrophone.desktop/patchers/index.ts b/src/equicordplugins/betterMicrophone.desktop/patchers/index.ts
new file mode 100644
index 00000000..541fa744
--- /dev/null
+++ b/src/equicordplugins/betterMicrophone.desktop/patchers/index.ts
@@ -0,0 +1,19 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./microphone";
diff --git a/src/equicordplugins/betterMicrophone.desktop/patchers/microphone.ts b/src/equicordplugins/betterMicrophone.desktop/patchers/microphone.ts
new file mode 100644
index 00000000..69bbadf1
--- /dev/null
+++ b/src/equicordplugins/betterMicrophone.desktop/patchers/microphone.ts
@@ -0,0 +1,72 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Emitter, MediaEngineStore, Patcher, types } from "../../philsPluginLibrary";
+import { patchConnectionAudioTransportOptions } from "../../philsPluginLibrary/patches/audio";
+import { PluginInfo } from "../constants";
+import { logger } from "../logger";
+import { microphoneStore } from "../stores";
+
+export class MicrophonePatcher extends Patcher {
+ private mediaEngineStore: types.MediaEngineStore;
+ private mediaEngine: types.MediaEngine;
+ public connection?: types.Connection;
+ public oldSetTransportOptions: (...args: any[]) => void;
+ public forceUpdateTransportationOptions: () => void;
+
+ constructor() {
+ super();
+ this.mediaEngineStore = MediaEngineStore;
+ this.mediaEngine = this.mediaEngineStore.getMediaEngine();
+ this.oldSetTransportOptions = () => void 0;
+ this.forceUpdateTransportationOptions = () => void 0;
+ }
+
+ public patch(): this {
+ this.unpatch();
+
+ const { get } = microphoneStore;
+
+ const connectionEventFunction =
+ (connection: types.Connection) => {
+ if (connection.context !== "default") return;
+
+ this.connection = connection;
+
+ const { oldSetTransportOptions, forceUpdateTransportationOptions } = patchConnectionAudioTransportOptions(connection, get, logger);
+
+ this.oldSetTransportOptions = oldSetTransportOptions;
+ this.forceUpdateTransportationOptions = forceUpdateTransportationOptions;
+ };
+
+ Emitter.addListener(
+ this.mediaEngine.emitter,
+ "on",
+ // @ts-ignore
+ "connection",
+ connectionEventFunction,
+ PluginInfo.PLUGIN_NAME
+ );
+
+ return this;
+ }
+
+ public unpatch(): this {
+ return this._unpatch();
+ }
+}
diff --git a/src/equicordplugins/betterMicrophone.desktop/stores/index.ts b/src/equicordplugins/betterMicrophone.desktop/stores/index.ts
new file mode 100644
index 00000000..0a95f671
--- /dev/null
+++ b/src/equicordplugins/betterMicrophone.desktop/stores/index.ts
@@ -0,0 +1,19 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./microphoneStore";
diff --git a/src/equicordplugins/betterMicrophone.desktop/stores/microphoneStore.ts b/src/equicordplugins/betterMicrophone.desktop/stores/microphoneStore.ts
new file mode 100644
index 00000000..f0a16a26
--- /dev/null
+++ b/src/equicordplugins/betterMicrophone.desktop/stores/microphoneStore.ts
@@ -0,0 +1,94 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { createPluginStore, ProfilableInitializer, ProfilableStore, profileable, ProfileableProfile } from "../../philsPluginLibrary";
+import { PluginInfo } from "../constants";
+
+
+export interface MicrophoneProfile {
+ freq?: number,
+ pacsize?: number,
+ channels?: number,
+ rate?: number,
+ voiceBitrate?: number;
+ freqEnabled?: boolean,
+ pacsizeEnabled?: boolean;
+ channelsEnabled?: boolean;
+ rateEnabled?: boolean;
+ voiceBitrateEnabled?: boolean;
+}
+
+export interface MicrophoneStore {
+ simpleMode?: boolean;
+ setSimpleMode: (enabled?: boolean) => void;
+ setFreq: (freq?: number) => void;
+ setPacsize: (pacsize?: number) => void;
+ setChannels: (channels?: number) => void;
+ setRate: (rate?: number) => void;
+ setVoiceBitrate: (voiceBitrate?: number) => void;
+ setFreqEnabled: (enabled?: boolean) => void;
+ setPacsizeEnabled: (enabled?: boolean) => void;
+ setChannelsEnabled: (enabled?: boolean) => void;
+ setRateEnabled: (enabled?: boolean) => void;
+ setVoiceBitrateEnabled: (enabled?: boolean) => void;
+}
+
+export const defaultMicrophoneProfiles = {
+ normal: {
+ name: "Normal",
+ channels: 2,
+ channelsEnabled: true,
+ voiceBitrate: 96,
+ voiceBitrateEnabled: true
+ },
+ high: {
+ name: "High",
+ channels: 2,
+ channelsEnabled: true,
+ voiceBitrate: 320,
+ voiceBitrateEnabled: true
+ },
+} as const satisfies Record;
+
+export const microphoneStoreDefault: ProfilableInitializer = (set, get) => ({
+ simpleMode: true,
+ setSimpleMode: enabled => get().simpleMode = enabled,
+ setChannels: channels => get().currentProfile.channels = channels,
+ setRate: rate => get().currentProfile.rate = rate,
+ setVoiceBitrate: voiceBitrate => get().currentProfile.voiceBitrate = voiceBitrate,
+ setPacsize: pacsize => get().currentProfile.pacsize = pacsize,
+ setFreq: freq => get().currentProfile.freq = freq,
+ setChannelsEnabled: enabled => get().currentProfile.channelsEnabled = enabled,
+ setFreqEnabled: enabled => get().currentProfile.freqEnabled = enabled,
+ setPacsizeEnabled: enabled => get().currentProfile.pacsizeEnabled = enabled,
+ setRateEnabled: enabled => get().currentProfile.rateEnabled = enabled,
+ setVoiceBitrateEnabled: enabled => get().currentProfile.voiceBitrateEnabled = enabled,
+});
+
+export let microphoneStore: ProfilableStore;
+
+export const initMicrophoneStore = () =>
+ microphoneStore = createPluginStore(
+ PluginInfo.PLUGIN_NAME,
+ "MicrophoneStore",
+ profileable(
+ microphoneStoreDefault,
+ { name: "" },
+ Object.values(defaultMicrophoneProfiles)
+ )
+ );
diff --git a/src/equicordplugins/betterScreenshare.dev/components/AudioSourceSelect.tsx b/src/equicordplugins/betterScreenshare.dev/components/AudioSourceSelect.tsx
new file mode 100644
index 00000000..01bc9f92
--- /dev/null
+++ b/src/equicordplugins/betterScreenshare.dev/components/AudioSourceSelect.tsx
@@ -0,0 +1,62 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Select, useEffect, useState } from "@webpack/common";
+import React from "react";
+
+import { MediaEngineStore, types } from "../../philsPluginLibrary";
+import { screenshareStore } from "../stores";
+
+export const AudioSourceSelect = (props?: typeof Select["defaultProps"]) => {
+ const { use } = screenshareStore;
+
+ const { audioSource, setAudioSource } = use();
+
+ const [windowPreviews, setWindowPreviews] = useState([]);
+
+ useEffect(() => {
+ const intervalFn = async () => {
+ const newPreviews = await MediaEngineStore.getMediaEngine().getWindowPreviews(1, 1);
+ setWindowPreviews(oldPreviews => [...oldPreviews, ...newPreviews].filter((preview, index, array) => array.findIndex(t => t.id === preview.id) === index));
+ };
+ intervalFn();
+
+ const intervals = [
+ setInterval(async () => {
+ intervalFn();
+ }, 4000), setInterval(async () => {
+ setWindowPreviews(await MediaEngineStore.getMediaEngine().getWindowPreviews(1, 1));
+ }, 30000)
+ ];
+
+ return () => intervals.forEach(interval => clearInterval(interval));
+ }, []);
+
+ return (
+
+ );
+};
diff --git a/src/equicordplugins/betterScreenshare.dev/components/OpenScreenshareSettingsButton.tsx b/src/equicordplugins/betterScreenshare.dev/components/OpenScreenshareSettingsButton.tsx
new file mode 100644
index 00000000..d12493a9
--- /dev/null
+++ b/src/equicordplugins/betterScreenshare.dev/components/OpenScreenshareSettingsButton.tsx
@@ -0,0 +1,38 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Button } from "@webpack/common";
+import React from "react";
+
+import { openScreenshareModal } from "../modals";
+
+export interface OpenScreenshareSettingsButtonProps {
+ title?: string;
+}
+
+export const OpenScreenshareSettingsButton = (props: OpenScreenshareSettingsButtonProps) => {
+ return (
+
+ );
+};
diff --git a/src/equicordplugins/betterScreenshare.dev/components/ScreenshareSettingsModal.tsx b/src/equicordplugins/betterScreenshare.dev/components/ScreenshareSettingsModal.tsx
new file mode 100644
index 00000000..ac529445
--- /dev/null
+++ b/src/equicordplugins/betterScreenshare.dev/components/ScreenshareSettingsModal.tsx
@@ -0,0 +1,467 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Flex } from "@components/Flex";
+import { Switch } from "@components/Switch";
+import { ModalSize, openModalLazy } from "@utils/modal";
+import { Button, Card, Forms, React, Select, Slider, TextInput, useEffect, useState } from "@webpack/common";
+import { SelectOption } from "@webpack/types";
+
+import { MicrophoneSettingsModal } from "../../betterMicrophone.desktop/components";
+import {
+ MediaEngineStore,
+ openURL,
+ ProfilableStore,
+ SettingsModal,
+ SettingsModalCard,
+ SettingsModalCardItem,
+ SettingsModalCardRow,
+ SettingsModalProfilesCard,
+ types,
+ validateNumberInput,
+ validateTextInputNumber
+} from "../../philsPluginLibrary";
+import { Styles } from "../../philsPluginLibrary/styles";
+import { PluginInfo } from "../constants";
+import { ScreenshareAudioProfile, ScreenshareAudioStore, ScreenshareProfile, ScreenshareStore } from "../stores";
+
+const simpleResolutions: readonly (SelectOption & { value: types.Resolution; })[] = [
+ {
+ label: "480p",
+ value: {
+ height: 480,
+ width: 720
+ }
+ },
+ {
+ label: "720p",
+ value: {
+ height: 720,
+ width: 1280
+ }
+ },
+ {
+ label: "1080p",
+ value: {
+ height: 1080,
+ width: 1920
+ }
+ },
+ {
+ label: "1440p",
+ value: {
+ height: 1440,
+ width: 2560
+ }
+ },
+ {
+ label: "2160p",
+ value: {
+ height: 2160,
+ width: 3840
+ }
+ }
+] as const;
+
+const simpleVideoBitrates: readonly SelectOption[] = [
+ {
+ label: "Low",
+ value: 2500
+ },
+ {
+ label: "Medium",
+ value: 5000
+ },
+ {
+ label: "Medium-High",
+ value: 7500
+ },
+ {
+ label: "High",
+ value: 10000
+ }
+] as const;
+
+export interface ScreenshareSettingsModalProps extends React.ComponentProps {
+ screenshareStore: ProfilableStore;
+ screenshareAudioStore?: ProfilableStore;
+ onAudioDone?: () => void;
+}
+
+export const ScreenshareSettingsModal = (props: ScreenshareSettingsModalProps) => {
+ const { screenshareStore, screenshareAudioStore, onAudioDone } = props;
+
+ const {
+ currentProfile,
+ profiles,
+ simpleMode,
+ setVideoBitrateEnabled,
+ setVideoCodec,
+ setVideoCodecEnabled,
+ setFramerate,
+ setFramerateEnabled,
+ setHeight,
+ setKeyframeInterval,
+ setKeyframeIntervalEnabled,
+ setResolutionEnabled,
+ setVideoBitrate,
+ setWidth,
+ setCurrentProfile,
+ getProfile,
+ saveProfile,
+ setHdrEnabled,
+ setSimpleMode,
+ deleteProfile,
+ duplicateProfile,
+ getCurrentProfile,
+ getProfiles
+ } = screenshareStore.use();
+
+
+ const {
+ name,
+ framerate,
+ framerateEnabled,
+ height,
+ keyframeInterval,
+ keyframeIntervalEnabled,
+ resolutionEnabled,
+ videoBitrate,
+ videoBitrateEnabled,
+ videoCodec,
+ videoCodecEnabled,
+ width,
+ hdrEnabled
+ } = currentProfile;
+
+ const [videoCodecs, setVideoCodecs] = useState([]);
+
+ const [isSaving, setIsSaving] = useState(false);
+
+ const [textinputWidth, setTextinputWidth] = useState(width ? width.toString() : "");
+ const [textinputHeight, setTextinputHeight] = useState(height ? height.toString() : "");
+ const [textinputFramerate, setTextinputFramerate] = useState(framerate ? framerate.toString() : "");
+ const [textinputKeyframeInterval, setTextinputKeyframeInterval] = useState(keyframeInterval ? keyframeInterval.toString() : "");
+
+ useEffect(() => {
+ setTextinputWidth(width ? width.toString() : "");
+ setTextinputHeight(height ? height.toString() : "");
+ setTextinputFramerate(framerate ? framerate.toString() : "");
+ setTextinputKeyframeInterval(keyframeInterval ? keyframeInterval.toString() : "");
+ }, [width, height, framerate, keyframeInterval]);
+
+ useEffect(() => {
+ (async () => {
+ const mediaEngine = MediaEngineStore.getMediaEngine();
+
+ const stringifiedCodecs: types.CodecCapabilities[] = JSON.parse(
+ await new Promise(res => mediaEngine.getCodecCapabilities(res))
+ );
+
+ setVideoCodecs(stringifiedCodecs);
+ })();
+ }, []);
+
+ const settingsCardResolutionSimple =
+ setResolutionEnabled(status)
+ }}>
+
+
+ ;
+
+ const settingsCardVideoBitrateSimple =
+ setVideoBitrateEnabled(status)
+ }}>
+
+
+ ;
+
+ const settingsCardResolution =
+ setResolutionEnabled(status),
+ disabled: isSaving
+ }}>
+
+ validateTextInputNumber(value) && setTextinputWidth(value)}
+ onBlur={e => {
+ const result = validateNumberInput(e.target.value);
+ setWidth(result);
+ setTextinputWidth(result ? result.toString() : "");
+ }} />
+
+
+ validateTextInputNumber(value) && setTextinputHeight(value)}
+ onBlur={e => {
+ const result = validateNumberInput(e.target.value);
+ setHeight(result);
+ setTextinputHeight(result ? result.toString() : "");
+ }} />
+
+ ;
+
+ const settingsCardItemFramerate =
+
+ validateTextInputNumber(value) && setTextinputFramerate(value)}
+ onBlur={e => {
+ const result = validateNumberInput(e.target.value);
+ setFramerate(result);
+ setTextinputFramerate(result ? result.toString() : "");
+ }} />
+ ;
+
+ const settingsCardFramerateProps: React.ComponentProps = {
+ title: "Framerate",
+ switchEnabled: true,
+ switchProps: {
+ checked: framerateEnabled ?? false,
+ disabled: isSaving,
+ onChange: status => setFramerateEnabled(status)
+ }
+ };
+
+ const settingsCardFramerate =
+
+ {settingsCardItemFramerate}
+ ;
+
+ const settingsCardFramerateSimple =
+
+ {settingsCardItemFramerate}
+ ;
+
+ const settingsCardKeyframeInterval =
+ setKeyframeIntervalEnabled(status)
+ }}>
+
+ validateTextInputNumber(value) && setTextinputKeyframeInterval(value)}
+ onBlur={e => {
+ const result = validateNumberInput(e.target.value);
+ setKeyframeInterval(result);
+ setTextinputKeyframeInterval(result ? result.toString() : "");
+ }} />
+
+ ;
+
+ const settingsCardVideoBitrate =
+ setVideoBitrateEnabled(status)
+ }}>
+
+
+ : <>
+
+ {settingsCardResolution}
+ {settingsCardFramerate}
+ {settingsCardKeyframeInterval}
+
+
+ {settingsCardVideoBitrate}
+ {settingsCardVideoCodec}
+ {screenshareAudioStore && settingsCardAudio}
+
+
+ {guideCard}
+ {settingsCardHdr}
+ {settingsCardProfiles}
+
+ >
+ }
+
+ );
+};
diff --git a/src/equicordplugins/betterScreenshare.dev/components/index.tsx b/src/equicordplugins/betterScreenshare.dev/components/index.tsx
new file mode 100644
index 00000000..5177495f
--- /dev/null
+++ b/src/equicordplugins/betterScreenshare.dev/components/index.tsx
@@ -0,0 +1,21 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./AudioSourceSelect";
+export * from "./OpenScreenshareSettingsButton";
+export * from "./ScreenshareSettingsModal";
diff --git a/src/equicordplugins/betterScreenshare.dev/constants/constants.ts b/src/equicordplugins/betterScreenshare.dev/constants/constants.ts
new file mode 100644
index 00000000..32d9d44c
--- /dev/null
+++ b/src/equicordplugins/betterScreenshare.dev/constants/constants.ts
@@ -0,0 +1,38 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Devs } from "@utils/constants";
+
+import { types } from "../../philsPluginLibrary";
+
+export const PluginInfo = {
+ PLUGIN_NAME: "BetterScreenshare",
+ DESCRIPTION: "This plugin allows you to further customize your screen sharing.",
+ AUTHOR: {
+ ...Devs.philhk,
+ github: "https://github.com/philhk"
+ },
+ CONTRIBUTORS: {
+ walrus: {
+ github: "https://github.com/philhk",
+ id: 305317288775778306n,
+ name: "walrus"
+ },
+ },
+ README: "https://github.com/Vendicated/Vencord/tree/main/src/plugins/betterScreenshare"
+} as const satisfies types.PluginInfo;
diff --git a/src/equicordplugins/betterScreenshare.dev/constants/index.ts b/src/equicordplugins/betterScreenshare.dev/constants/index.ts
new file mode 100644
index 00000000..c7b948ab
--- /dev/null
+++ b/src/equicordplugins/betterScreenshare.dev/constants/index.ts
@@ -0,0 +1,19 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./constants";
diff --git a/src/equicordplugins/betterScreenshare.dev/index.tsx b/src/equicordplugins/betterScreenshare.dev/index.tsx
new file mode 100644
index 00000000..e0b74539
--- /dev/null
+++ b/src/equicordplugins/betterScreenshare.dev/index.tsx
@@ -0,0 +1,71 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Devs } from "@utils/constants";
+import definePlugin from "@utils/types";
+
+import { addSettingsPanelButton, Emitter, removeSettingsPanelButton, ScreenshareSettingsIcon } from "../philsPluginLibrary";
+import { PluginInfo } from "./constants";
+import { openScreenshareModal } from "./modals";
+import { ScreenshareAudioPatcher, ScreensharePatcher } from "./patchers";
+import { replacedScreenshareModalComponent } from "./patches";
+import { initScreenshareAudioStore, initScreenshareStore } from "./stores";
+
+var screensharePatcher;
+var screenshareAudioPatcher;
+
+export default definePlugin({
+ name: "PhilsBetterScreenshare",
+ description: "This plugin allows you to further customize your screen sharing.",
+ authors: [Devs.philhk, Devs.walrus],
+ dependencies: ["PhilsPluginLibrary"],
+ replacedScreenshareModalComponent: replacedScreenshareModalComponent,
+
+ patches: [
+ {
+ find: "Messages.SCREENSHARE_RELAUNCH",
+ replacement: {
+ match: /(function .{1,2}\(.{1,2}\){)(.{1,40}(?=selectGuild).+?(?:]}\)}\)))(})/,
+ replace: "$1return $self.replacedScreenshareModalComponent(function(){$2}, this, arguments)$3"
+ }
+ }
+ ],
+
+ start() {
+ initScreenshareStore();
+ initScreenshareAudioStore();
+ screensharePatcher = new ScreensharePatcher().patch();
+ screenshareAudioPatcher = new ScreenshareAudioPatcher().patch();
+
+ addSettingsPanelButton({
+ name: PluginInfo.PLUGIN_NAME,
+ icon: ScreenshareSettingsIcon,
+ tooltipText: "Screenshare Settings",
+ onClick: openScreenshareModal
+ });
+ },
+
+ stop() {
+ screensharePatcher?.unpatch();
+ screenshareAudioPatcher?.unpatch();
+ Emitter.removeAllListeners(PluginInfo.PLUGIN_NAME);
+ removeSettingsPanelButton(PluginInfo.PLUGIN_NAME);
+ },
+});
+
+export { screenshareAudioPatcher, screensharePatcher };
diff --git a/src/equicordplugins/betterScreenshare.dev/logger/index.ts b/src/equicordplugins/betterScreenshare.dev/logger/index.ts
new file mode 100644
index 00000000..828c0f86
--- /dev/null
+++ b/src/equicordplugins/betterScreenshare.dev/logger/index.ts
@@ -0,0 +1,23 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Logger } from "@utils/Logger";
+
+import { PluginInfo } from "../constants";
+
+export const logger = new Logger(PluginInfo.PLUGIN_NAME);
diff --git a/src/equicordplugins/betterScreenshare.dev/modals/index.tsx b/src/equicordplugins/betterScreenshare.dev/modals/index.tsx
new file mode 100644
index 00000000..6650ed11
--- /dev/null
+++ b/src/equicordplugins/betterScreenshare.dev/modals/index.tsx
@@ -0,0 +1,55 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { openModalLazy } from "@utils/modal";
+
+import Plugin from "..";
+import { ScreenshareSettingsModal } from "../components";
+import { PluginInfo } from "../constants";
+import { screenshareAudioStore, screenshareStore } from "../stores";
+
+const onScreenshareModalDone = () => {
+ const { screenshareAudioPatcher, screensharePatcher } = Plugin;
+
+ if (screensharePatcher) {
+ screensharePatcher.forceUpdateTransportationOptions();
+ screensharePatcher.forceUpdateDesktopSourceOptions();
+ }
+ if (screenshareAudioPatcher)
+ screenshareAudioPatcher.forceUpdateTransportationOptions();
+};
+
+const onScreenshareAudioModalDone = () => {
+ const { screenshareAudioPatcher } = Plugin;
+
+ if (screenshareAudioPatcher)
+ screenshareAudioPatcher.forceUpdateTransportationOptions();
+};
+
+export const openScreenshareModal =
+ () => openModalLazy(async () => {
+ return props =>
+ ;
+ });
diff --git a/src/equicordplugins/betterScreenshare.dev/patchers/index.ts b/src/equicordplugins/betterScreenshare.dev/patchers/index.ts
new file mode 100644
index 00000000..24d37b2c
--- /dev/null
+++ b/src/equicordplugins/betterScreenshare.dev/patchers/index.ts
@@ -0,0 +1,20 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./screenshare";
+export * from "./screenshareAudio";
diff --git a/src/equicordplugins/betterScreenshare.dev/patchers/screenshare.ts b/src/equicordplugins/betterScreenshare.dev/patchers/screenshare.ts
new file mode 100644
index 00000000..51aadbed
--- /dev/null
+++ b/src/equicordplugins/betterScreenshare.dev/patchers/screenshare.ts
@@ -0,0 +1,99 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { UserStore } from "@webpack/common";
+
+import { Emitter, MediaEngineStore, Patcher, types } from "../../philsPluginLibrary";
+import { patchConnectionVideoSetDesktopSourceWithOptions, patchConnectionVideoTransportOptions } from "../../philsPluginLibrary/patches/video";
+import { PluginInfo } from "../constants";
+import { logger } from "../logger";
+import { screenshareStore } from "../stores";
+
+export class ScreensharePatcher extends Patcher {
+ private mediaEngineStore: types.MediaEngineStore;
+ private mediaEngine: types.MediaEngine;
+ public connection?: types.Connection;
+ public oldSetDesktopSourceWithOptions: (...args: any[]) => void;
+ public oldSetTransportOptions: (...args: any[]) => void;
+ public forceUpdateTransportationOptions: () => void;
+ public forceUpdateDesktopSourceOptions: () => void;
+
+ constructor() {
+ super();
+ this.mediaEngineStore = MediaEngineStore;
+ this.mediaEngine = this.mediaEngineStore.getMediaEngine();
+ this.forceUpdateTransportationOptions = () => void 0;
+ this.forceUpdateDesktopSourceOptions = () => void 0;
+ this.oldSetDesktopSourceWithOptions = () => void 0;
+ this.oldSetTransportOptions = () => void 0;
+ }
+
+ public patch(): this {
+ this.unpatch();
+
+ const { get } = screenshareStore;
+
+ const connectionEventFunction =
+ (connection: types.Connection) => {
+ if (!(connection.context === "stream" && connection.streamUserId === UserStore.getCurrentUser().id)) return;
+
+ this.connection = connection;
+
+ const {
+ oldSetDesktopSourceWithOptions,
+ oldSetTransportOptions,
+ forceUpdateDesktopSourceOptions,
+ forceUpdateTransportationOptions
+ } = {
+ ...patchConnectionVideoTransportOptions(connection, get, logger),
+ ...patchConnectionVideoSetDesktopSourceWithOptions(connection, get, logger)
+ };
+
+ this.oldSetDesktopSourceWithOptions = oldSetDesktopSourceWithOptions;
+ this.oldSetTransportOptions = oldSetTransportOptions;
+ this.forceUpdateDesktopSourceOptions = forceUpdateDesktopSourceOptions;
+ this.forceUpdateTransportationOptions = forceUpdateTransportationOptions;
+
+ Emitter.addListener(connection.emitter, "on", "connected", () => {
+ this.forceUpdateTransportationOptions();
+ this.forceUpdateDesktopSourceOptions();
+ });
+
+ Emitter.addListener(connection.emitter, "on", "destroy", () => {
+ this.forceUpdateTransportationOptions = () => void 0;
+ this.forceUpdateDesktopSourceOptions = () => void 0;
+ this.oldSetTransportOptions = () => void 0;
+ this.oldSetDesktopSourceWithOptions = () => void 0;
+ });
+ };
+
+ Emitter.addListener(
+ this.mediaEngine.emitter,
+ "on",
+ "connection",
+ connectionEventFunction,
+ PluginInfo.PLUGIN_NAME
+ );
+
+ return this;
+ }
+
+ public unpatch(): this {
+ return this._unpatch();
+ }
+}
diff --git a/src/equicordplugins/betterScreenshare.dev/patchers/screenshareAudio.ts b/src/equicordplugins/betterScreenshare.dev/patchers/screenshareAudio.ts
new file mode 100644
index 00000000..64205057
--- /dev/null
+++ b/src/equicordplugins/betterScreenshare.dev/patchers/screenshareAudio.ts
@@ -0,0 +1,85 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { UserStore } from "@webpack/common";
+
+import { Emitter, MediaEngineStore, patchConnectionAudioTransportOptions, Patcher, types } from "../../philsPluginLibrary";
+import { PluginInfo } from "../constants";
+import { logger } from "../logger";
+import { screenshareAudioStore } from "../stores/screenshareAudioStore";
+
+export class ScreenshareAudioPatcher extends Patcher {
+ private mediaEngineStore: types.MediaEngineStore;
+ private mediaEngine: types.MediaEngine;
+ public connection?: types.Connection;
+
+ public oldSetTransportOptions: (...args: any[]) => void;
+ public forceUpdateTransportationOptions: () => void;
+
+ constructor() {
+ super();
+ this.mediaEngineStore = MediaEngineStore;
+ this.mediaEngine = this.mediaEngineStore.getMediaEngine();
+
+ this.forceUpdateTransportationOptions = () => void 0;
+ this.oldSetTransportOptions = () => void 0;
+ }
+
+ public patch(): this {
+ this.unpatch();
+
+ const { get } = screenshareAudioStore;
+
+ const connectionEventFunction =
+ (connection: types.Connection) => {
+ if (connection.context !== "stream" || connection.streamUserId !== UserStore.getCurrentUser().id) return;
+
+ this.connection = connection;
+
+ const {
+ forceUpdateTransportationOptions: forceUpdateTransportationOptionsAudio,
+ oldSetTransportOptions: oldSetTransportOptionsAudio
+ } = patchConnectionAudioTransportOptions(connection, get, logger);
+
+ this.forceUpdateTransportationOptions = forceUpdateTransportationOptionsAudio;
+ this.oldSetTransportOptions = oldSetTransportOptionsAudio;
+
+ Emitter.addListener(connection.emitter, "on", "connected", () => {
+ this.forceUpdateTransportationOptions();
+ });
+
+ Emitter.addListener(connection.emitter, "on", "destroy", () => {
+ this.forceUpdateTransportationOptions = () => void 0;
+ });
+ };
+
+ Emitter.addListener(
+ this.mediaEngine.emitter,
+ "on",
+ "connection",
+ connectionEventFunction,
+ PluginInfo.PLUGIN_NAME
+ );
+
+ return this;
+ }
+
+ public unpatch(): this {
+ return this._unpatch();
+ }
+}
diff --git a/src/equicordplugins/betterScreenshare.dev/patches/index.ts b/src/equicordplugins/betterScreenshare.dev/patches/index.ts
new file mode 100644
index 00000000..169a8b28
--- /dev/null
+++ b/src/equicordplugins/betterScreenshare.dev/patches/index.ts
@@ -0,0 +1,19 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./screenshareModal";
diff --git a/src/equicordplugins/betterScreenshare.dev/patches/screenshareModal.tsx b/src/equicordplugins/betterScreenshare.dev/patches/screenshareModal.tsx
new file mode 100644
index 00000000..4b069df0
--- /dev/null
+++ b/src/equicordplugins/betterScreenshare.dev/patches/screenshareModal.tsx
@@ -0,0 +1,98 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Flex } from "@components/Flex";
+import { React } from "@webpack/common";
+import { Settings } from "Vencord";
+
+import { SettingsModalCard, SettingsModalCardItem } from "../../philsPluginLibrary";
+import Plugin from "..";
+import { AudioSourceSelect, OpenScreenshareSettingsButton } from "../components";
+import { PluginInfo } from "../constants";
+import { screenshareStore } from "../stores";
+
+const ReplacedStreamSettings = () => {
+ const { use } = screenshareStore;
+
+ const { audioSourceEnabled, setAudioSourceEnabled } = use();
+
+ const cardProps = { style: { border: "1px solid var(--primary-800)" } };
+
+ return (
+
+
+ The MediaDownloader plugin requires a working version of yt-dlp to be installed on your system. For extra features such as higher quality videos and gifs, you can also optionally install ffmpeg.
+
+ If you don't know how to install yt-dlp or ffmpeg, check the installation guides below (if you do know how to install it, make sure it's in your PATH).
+
+ Note: You may need to completely restart Discord after installing yt-dlp or ffmpeg for the plugin to detect them.
+
+
+
+ );
+}
diff --git a/src/equicordplugins/mediaDownloader.desktop/index.tsx b/src/equicordplugins/mediaDownloader.desktop/index.tsx
new file mode 100644
index 00000000..faf7ec1b
--- /dev/null
+++ b/src/equicordplugins/mediaDownloader.desktop/index.tsx
@@ -0,0 +1,333 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import "./style.css";
+
+import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, sendBotMessage } from "@api/Commands";
+import * as DataStore from "@api/DataStore";
+import { definePluginSettings } from "@api/Settings";
+import ErrorBoundary from "@components/ErrorBoundary";
+import { Link } from "@components/Link";
+import { EquicordDevs } from "@utils/constants";
+import { Logger } from "@utils/Logger";
+import { openModal } from "@utils/modal";
+import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types";
+import { Button, DraftType, FluxDispatcher, Forms, UploadHandler, UploadManager, UserStore } from "@webpack/common";
+import { Channel } from "discord-types/general";
+
+import { DependencyModal } from "./DependencyModal";
+
+type ButtonComponent = {
+ customId?: string;
+ disabled?: boolean;
+ emoji?: {
+ animated?: boolean | string;
+ id?: string;
+ name?: string;
+ src?: string;
+ };
+ id: string;
+ label?: string;
+ style: number;
+ type: number;
+ url?: string;
+};
+
+const Native = VencordNative.pluginHelpers.MediaDownloader as PluginNative;
+const logger = new Logger("MediaDownloader", "#ff0b01");
+
+const maxFileSize = () => {
+ const premiumType = (UserStore.getCurrentUser().premiumType ?? 0);
+ if (premiumType > 1) return 500000000; // 500MB
+ if (premiumType > 0) return 50000000; // 50MB
+ return 25000000; // 25MB
+};
+/** Takes a string and splits it into an array of arguments. */
+const argParse = (args: string): string[] => args.match(
+ /(?:[^\s"']+|"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([^\s]+))/g
+) ?? [];
+
+function mimetype(extension: "mp4" | "webm" | "gif" | "mp3" | string) {
+ switch (extension) {
+ case "mp4":
+ return "video/mp4";
+ case "webm":
+ return "video/webm";
+ case "gif":
+ return "image/gif";
+ case "mp3":
+ return "audio/mp3";
+ default:
+ return "application/octet-stream";
+ }
+}
+
+const CancelButton = [{
+ components: [{
+ customId: "media-downloader-stop-download", // ! for some reason customId is always undefined, so I'm just saving the id in the emoji animated field :3
+ emoji: {
+ name: "⚪",
+ animated: "media-downloader-stop-download"
+ },
+ label: "Cancel download",
+ id: "0,0",
+ style: 4,
+ type: 2,
+ }], id: "0", type: 1
+}];
+async function sendProgress(channelId: string, promise: Promise<{
+ buffer: Buffer;
+ title: string;
+ logs: string;
+} | {
+ error: string;
+ logs: string;
+}>) {
+ if (!settings.store.showProgress) {
+ sendBotMessage(channelId, {
+ components: CancelButton
+ });
+ return await promise;
+ }
+ const clydeMessage = sendBotMessage(channelId, {
+ content: "Downloading video...",
+ components: CancelButton
+ });
+ const updateMessage = (stdout: string, append?: string) => {
+ const text = stdout.toString();
+ FluxDispatcher.dispatch({
+ type: "MESSAGE_UPDATE",
+ message: {
+ ...clydeMessage,
+ content: `Downloading video...\n\`\`\`\n${text}\n\`\`\`${append || ""}`,
+ components: append ? [] : clydeMessage.components
+ }
+ });
+ };
+ // Hacky way to send info from native to renderer for progress updates
+ const id = setInterval(async () => {
+ const stdout = await Native.getStdout();
+ updateMessage(stdout);
+ }, 500);
+
+ const data = await promise;
+ clearInterval(id);
+ const stdout = await Native.getStdout();
+ updateMessage(stdout, "error" in data ? "Error!" : "Done!");
+ return data;
+}
+
+function sendFfmpegWarning(channelId: string) {
+ sendBotMessage(channelId, {
+ content: "FFmpeg not detected. You may experience lower download quality and missing features."
+ });
+}
+
+// Mostly taken from viewRaw and betterSessions plugins.
+async function openDependencyModal() {
+ const key = openModal(props => (
+
+
+
+ ));
+}
+
+const settings = definePluginSettings({
+ supportedWebsites: {
+ description: "See the link for a list of supported websites.",
+ type: OptionType.COMPONENT,
+ default: "none",
+ component: () => (
+ <>
+
+
+
+
+
+
+ >
+ )
+ },
+ showProgress: {
+ type: OptionType.BOOLEAN,
+ description: "Send a Clyde message with the download progress.",
+ default: true,
+ },
+ showFfmpegWarning: {
+ type: OptionType.BOOLEAN,
+ description: "Show a warning message if ffmpeg is not installed.",
+ default: true,
+ },
+ defaultGifQuality: {
+ type: OptionType.NUMBER,
+ description: "The quality level to use if no value is specified when downloading gifs. A number between 1 and 5.",
+ default: 3,
+ },
+ ytdlpArgs: {
+ type: OptionType.STRING,
+ description: "Additional arguments to pass to yt-dlp. This may overwrite default plugin arguments such format selection. Note: if modifying the ouptup, ensure the filename starts with `download`.",
+ placeholder: "--format bestvideo+bestaudio",
+ },
+ ffmpegArgs: {
+ type: OptionType.STRING,
+ description: "Additional arguments to pass to ffmpeg. This may overwrite default plugin arguments such as auto-scaling. Note: if modifying the output, ensure the filename starts with `remux`.",
+ placeholder: "-vf scale=1280:720",
+ }
+}, {
+ defaultGifQuality: {
+ isValid(value) {
+ return value >= 1 && value <= 5;
+ }
+ }
+});
+
+export default definePlugin({
+ name: "MediaDownloader",
+ description: "Download and send videos with from YouTube, Twitter, Reddit and more.",
+ authors: [EquicordDevs.Colorman],
+ dependencies: ["CommandsAPI"],
+ reporterTestable: ReporterTestable.Patches,
+ settings,
+ commands: [{
+ inputType: ApplicationCommandInputType.BUILT_IN,
+ name: "download",
+ description: "Download and send videos, audio or gifs.",
+ options: [{
+ name: "url",
+ description: "The URL of any video supported by yt-dlp.",
+ required: true,
+ type: ApplicationCommandOptionType.STRING
+ }, {
+ name: "format",
+ description: "Whether to download a video or audio.",
+ type: ApplicationCommandOptionType.STRING,
+ choices: [
+ { name: "Video", value: "video", label: "Video" },
+ { name: "Audio", value: "audio", label: "Audio" },
+ { name: "GIF", value: "gif", label: "GIF" }
+ ],
+ required: false,
+ }, {
+ name: "gif_quality",
+ type: ApplicationCommandOptionType.INTEGER,
+ description: "The quality level when using GIF. Try lowering this number if the GIF is too large.",
+ required: false,
+ choices: [
+ { name: "5", value: "5", label: "5" },
+ { name: "4", value: "4", label: "4" },
+ { name: "3", value: "3", label: "3" },
+ { name: "2", value: "2", label: "2" },
+ { name: "1", value: "1", label: "1" }
+ ]
+ }, {
+ name: "yt-dlp_args",
+ description: "Additional arguments to pass to yt-dlp. These will take precedence over arguments set in the settings. This may overwrite default plugin arguments such format selection. Note: if modifying the output, ensure the filename starts with `download`.",
+ required: false,
+ type: ApplicationCommandOptionType.STRING
+ }, {
+ name: "ffmpeg_args",
+ description: "Additional arguments to pass to ffmpeg. These will take precedence over arguments set in the settings. This may overwrite default plugin arguments such as auto-scaling. Note: if modifying the output, ensure the filename starts with `remux`.",
+ required: false,
+ type: ApplicationCommandOptionType.STRING
+ }],
+
+ execute: async (args, ctx) => {
+ if (!await Native.isYtdlpAvailable()) return openDependencyModal();
+ if (!await Native.isFfmpegAvailable() && settings.store.showFfmpegWarning) sendFfmpegWarning(ctx.channel.id);
+
+ const url = findOption(args, "url", "");
+ const format = findOption<"video" | "audio" | "gif">(args, "format", "video");
+ const gifQuality = parseInt(findOption(args, "gif_quality", settings.store.defaultGifQuality.toString())) as 1 | 2 | 3 | 4 | 5;
+ const ytdlpArgs = findOption(args, "yt-dlp_args", "");
+ const ffmpegArgs = findOption(args, "ffmpeg_args", "");
+
+ return await download(ctx.channel, {
+ url,
+ format,
+ gifQuality,
+ ytdlpArgs,
+ ffmpegArgs
+ });
+ }
+ }],
+ patches: [
+ {
+ find: "missing validator for this component",
+ replacement: {
+ match: /(\i)(\.type\)\{case \i\.\i\.BUTTON):return null;/,
+ replace: "$1$2:return ($self.handleButtonClick($1),null);"
+ }
+ }
+ ],
+ handleButtonClick: (buttonComponent: ButtonComponent) => {
+ if (!(buttonComponent.emoji?.animated === "media-downloader-stop-download")) return;
+ Native.interrupt();
+ },
+ start: async () => {
+ await Native.checkytdlp();
+ await Native.checkffmpeg();
+
+ const videoDir = await DataStore.get("media-downloader-video-dir");
+ const newVideoDir = await Native.start(videoDir);
+ await DataStore.set("media-downloader-video-dir", newVideoDir);
+ },
+ stop: async () => {
+ // Clean up the temp files
+ await Native.stop();
+ await DataStore.del("media-downloader-video-dir");
+ }
+});
+
+async function download(channel: Channel, {
+ url, format, ytdlpArgs, ffmpegArgs, gifQuality
+}: {
+ url: string;
+ format: "video" | "audio" | "gif";
+ ytdlpArgs: string;
+ ffmpegArgs: string;
+ gifQuality: 1 | 2 | 3 | 4 | 5;
+}) {
+ const promise = Native.execute({
+ url,
+ format,
+ gifQuality,
+ ytdlpArgs: [
+ ...argParse(settings.store.ytdlpArgs || ""),
+ ...argParse(ytdlpArgs)
+ ],
+ ffmpegArgs: [
+ ...argParse(settings.store.ffmpegArgs || ""),
+ ...argParse(ffmpegArgs)
+ ],
+ maxFileSize: maxFileSize()
+ });
+
+ const data = await sendProgress(channel.id, promise);
+
+ for (const log of data.logs.trim().split("\n")) logger.info(log);
+
+ if ("error" in data) {
+ // Open the modal if the error is due to missing formats (could be fixed by downloading ffmpeg)
+ if (data.error.includes("--list-formats") && !(await Native.isFfmpegAvailable()))
+ return sendBotMessage(channel.id, { content: "No good streams found. Consider installing ffmpeg to increase the likelihood of a successful stream." }), openDependencyModal();
+
+ return sendBotMessage(channel.id, {
+ content: `Failed to download video: ${data.error.includes("\n") ? "\n```" + data.error + "\n```" : `\`${data.error}\``}`
+ });
+ }
+
+ const { buffer, title } = data;
+ UploadManager.clearAll(channel.id, DraftType.SlashCommand);
+ const file = new File([buffer], title, { type: mimetype(title.split(".")[1]) });
+ // See petpet
+ setTimeout(() => UploadHandler.promptToUpload([file], channel, DraftType.ChannelMessage), 10);
+}
diff --git a/src/equicordplugins/mediaDownloader.desktop/native.ts b/src/equicordplugins/mediaDownloader.desktop/native.ts
new file mode 100644
index 00000000..d35d3abd
--- /dev/null
+++ b/src/equicordplugins/mediaDownloader.desktop/native.ts
@@ -0,0 +1,300 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { ChildProcessWithoutNullStreams, execFileSync, spawn } from "child_process";
+import { IpcMainInvokeEvent } from "electron";
+import * as fs from "fs";
+import os from "os";
+import path from "path";
+
+type Format = "video" | "audio" | "gif";
+type DownloadOptions = {
+ url: string;
+ format?: Format;
+ gifQuality?: 1 | 2 | 3 | 4 | 5;
+ ytdlpArgs?: string[];
+ ffmpegArgs?: string[];
+ maxFileSize?: number;
+};
+
+let workdir: string | null = null;
+let stdout_global: string = "";
+let logs_global: string = "";
+
+let ytdlpAvailable = false;
+let ffmpegAvailable = false;
+
+let ytdlpProcess: ChildProcessWithoutNullStreams | null = null;
+let ffmpegProcess: ChildProcessWithoutNullStreams | null = null;
+
+const getdir = () => workdir ?? process.cwd();
+const p = (file: string) => path.join(getdir(), file);
+const cleanVideoFiles = () => {
+ if (!workdir) return;
+ fs.readdirSync(workdir)
+ .filter(f => f.startsWith("download.") || f.startsWith("remux."))
+ .forEach(f => fs.unlinkSync(p(f)));
+};
+const appendOut = (data: string) => ( // Makes carriage return (\r) work
+ (stdout_global += data), (stdout_global = stdout_global.replace(/^.*\r([^\n])/gm, "$1")));
+const log = (...data: string[]) => (console.log(`[Plugin:MediaDownloader] ${data.join(" ")}`), logs_global += `[Plugin:MediaDownloader] ${data.join(" ")}\n`);
+const error = (...data: string[]) => console.error(`[Plugin:MediaDownloader] [ERROR] ${data.join(" ")}`);
+
+function ytdlp(args: string[]): Promise {
+ log(`Executing yt-dlp with args: ["${args.map(a => a.replace('"', '\\"')).join('", "')}"]`);
+ let errorMsg = "";
+
+ return new Promise((resolve, reject) => {
+ ytdlpProcess = spawn("yt-dlp", args, {
+ cwd: getdir(),
+ });
+
+ ytdlpProcess.stdout.on("data", data => appendOut(data));
+ ytdlpProcess.stderr.on("data", data => {
+ appendOut(data);
+ error(`yt-dlp encountered an error: ${data}`);
+ errorMsg += data;
+ });
+ ytdlpProcess.on("exit", code => {
+ ytdlpProcess = null;
+ code === 0 ? resolve(stdout_global) : reject(new Error(errorMsg || `yt-dlp exited with code ${code}`));
+ });
+ });
+}
+function ffmpeg(args: string[]): Promise {
+ log(`Executing ffmpeg with args: ["${args.map(a => a.replace('"', '\\"')).join('", "')}"]`);
+ let errorMsg = "";
+
+ return new Promise((resolve, reject) => {
+ ffmpegProcess = spawn("ffmpeg", args, {
+ cwd: getdir(),
+ });
+
+ ffmpegProcess.stdout.on("data", data => appendOut(data));
+ ffmpegProcess.stderr.on("data", data => {
+ appendOut(data);
+ error(`ffmpeg encountered an error: ${data}`);
+ errorMsg += data;
+ });
+ ffmpegProcess.on("exit", code => {
+ ffmpegProcess = null;
+ code === 0 ? resolve(stdout_global) : reject(new Error(errorMsg || `ffmpeg exited with code ${code}`));
+ });
+ });
+
+}
+
+export async function start(_: IpcMainInvokeEvent, _workdir: string | undefined) {
+ _workdir ||= fs.mkdtempSync(path.join(os.tmpdir(), "vencord_mediaDownloader_"));
+ if (!fs.existsSync(_workdir)) fs.mkdirSync(_workdir, { recursive: true });
+ workdir = _workdir;
+ log("Using workdir: ", workdir);
+ return workdir;
+}
+export async function stop(_: IpcMainInvokeEvent) {
+ if (workdir) {
+ log("Cleaning up workdir");
+ fs.rmSync(workdir, { recursive: true });
+ workdir = null;
+ }
+}
+
+async function metadata(options: DownloadOptions) {
+ stdout_global = "";
+ const metadata = JSON.parse(await ytdlp(["-J", options.url, "--no-warnings"]));
+ if (metadata.is_live) throw "Live streams are not supported.";
+ stdout_global = "";
+ return { videoTitle: `${metadata.title || "video"} (${metadata.id})` };
+}
+function genFormat({ videoTitle }: { videoTitle: string; }, { maxFileSize, format }: DownloadOptions) {
+ const HAS_LIMIT = !!maxFileSize;
+ const MAX_VIDEO_SIZE = HAS_LIMIT ? maxFileSize * 0.8 : 0;
+ const MAX_AUDIO_SIZE = HAS_LIMIT ? maxFileSize * 0.2 : 0;
+
+ const audio = {
+ noFfmpeg: "ba[ext=mp3]{TOT_SIZE}/wa[ext=mp3]{TOT_SIZE}",
+ ffmpeg: "ba*{TOT_SIZE}/ba{TOT_SIZE}/wa*{TOT_SIZE}/ba*"
+ };
+ const video = {
+ noFfmpeg: "b{TOT_SIZE}{HEIGHT}[ext=webm]/b{TOT_SIZE}{HEIGHT}[ext=mp4]/w{HEIGHT}{TOT_SIZE}",
+ ffmpeg: "b*{VID_SIZE}{HEIGHT}+ba{AUD_SIZE}/b{TOT_SIZE}{HEIGHT}/b*{HEIGHT}+ba",
+ };
+ const gif = {
+ ffmpeg: "bv{TOT_SIZE}/wv{TOT_SIZE}"
+ };
+
+ let format_group: { noFfmpeg?: string; ffmpeg: string; };
+ switch (format) {
+ case "audio":
+ format_group = audio;
+ break;
+ case "gif":
+ format_group = gif;
+ break;
+ case "video":
+ default:
+ format_group = video;
+ break;
+ }
+
+ const format_string = (ffmpegAvailable ? format_group.ffmpeg : format_group.noFfmpeg)
+ ?.replaceAll("{TOT_SIZE}", HAS_LIMIT ? `[filesize<${maxFileSize}]` : "")
+ .replaceAll("{VID_SIZE}", HAS_LIMIT ? `[filesize<${MAX_VIDEO_SIZE}]` : "")
+ .replaceAll("{AUD_SIZE}", HAS_LIMIT ? `[filesize<${MAX_AUDIO_SIZE}]` : "")
+ .replaceAll("{HEIGHT}", "[height<=1080]");
+ if (!format_string) throw "Gif format is only supported with ffmpeg.";
+ log("Video formated calculated as ", format_string);
+ log(`Based on: format=${format}, maxFileSize=${maxFileSize}, ffmpegAvailable=${ffmpegAvailable}`);
+ return { format: format_string, videoTitle };
+}
+async function download({ format, videoTitle }: { format: string; videoTitle: string; }, { ytdlpArgs, url, format: usrFormat }: DownloadOptions) {
+ cleanVideoFiles();
+ const baseArgs = ["-f", format, "-o", "download.%(ext)s", "--force-overwrites", "-I", "1"];
+ const remuxArgs = ffmpegAvailable
+ ? usrFormat === "video"
+ ? ["--remux-video", "webm>webm/mp4"]
+ : usrFormat === "audio"
+ ? ["--extract-audio", "--audio-format", "mp3"]
+ : []
+ : [];
+ const customArgs = ytdlpArgs?.filter(Boolean) || [];
+
+ await ytdlp([url, ...baseArgs, ...remuxArgs, ...customArgs]);
+ const file = fs.readdirSync(getdir()).find(f => f.startsWith("download."));
+ if (!file) throw "No video file was found!";
+ return { file, videoTitle };
+}
+async function remux({ file, videoTitle }: { file: string; videoTitle: string; }, { ffmpegArgs, format, maxFileSize, gifQuality }: DownloadOptions) {
+ const sourceExtension = file.split(".").pop();
+ if (!ffmpegAvailable) return log("Skipping remux, ffmpeg is unavailable."), { file, videoTitle, extension: sourceExtension };
+
+ // We only really need to remux if
+ // 1. The file is too big
+ // 2. The file is in a format not supported by discord
+ // 3. The user provided custom ffmpeg arguments
+ // 4. The target format is gif
+ const acceptableFormats = ["mp3", "mp4", "webm"];
+ const fileSize = fs.statSync(p(file)).size;
+ const customArgs = ffmpegArgs?.filter(Boolean) || [];
+
+ const isFormatAcceptable = acceptableFormats.includes(sourceExtension ?? "");
+ const isFileSizeAcceptable = (!maxFileSize || fileSize <= maxFileSize);
+ const hasCustomArgs = customArgs.length > 0;
+ const isGif = format === "gif";
+ if (isFormatAcceptable && isFileSizeAcceptable && !hasCustomArgs && !isGif)
+ return log("Skipping remux, file type and size are good, and no ffmpeg arguments were specified."), { file, videoTitle, extension: sourceExtension };
+
+ const duration = parseFloat(execFileSync("ffprobe", ["-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", p(file)]).toString());
+ if (isNaN(duration)) throw "Failed to get video duration.";
+ // ffmpeg tends to go above the target size, so I'm setting it to 7/8
+ const targetBits = maxFileSize ? (maxFileSize * 7) / duration : 9999999;
+ const kilobits = ~~(targetBits / 1024);
+
+ let baseArgs: string[];
+ let ext: string;
+ switch (format) {
+ case "audio":
+ baseArgs = ["-i", p(file), "-b:a", `${kilobits}k`, "-maxrate", `${kilobits}k`, "-bufsize", "1M", "-y"];
+ ext = "mp3";
+ break;
+ case "video":
+ default:
+ // Dynamically resize based on target bitrate
+ const height = kilobits <= 100 ? 480 : kilobits <= 500 ? 720 : 1080;
+ baseArgs = ["-i", p(file), "-b:v", `${~~(kilobits * 0.8)}k`, "-b:a", `${~~(kilobits * 0.2)}k`, "-maxrate", `${kilobits}k`, "-bufsize", "1M", "-y", "-filter:v", `scale=-1:${height}`];
+ ext = "mp4";
+ break;
+ case "gif":
+ let fps: number, width: number, colors: number, bayer_scale: number;
+ // WARNING: these parameters have been arbitrarily chosen, optimization is welcome!
+ switch (gifQuality) {
+ case 1:
+ fps = 5, width = 360, colors = 24, bayer_scale = 5;
+ break;
+ case 2:
+ fps = 10, width = 420, colors = 32, bayer_scale = 5;
+ break;
+ default:
+ case 3:
+ fps = 15, width = 480, colors = 64, bayer_scale = 4;
+ break;
+ case 4:
+ fps = 20, width = 540, colors = 64, bayer_scale = 3;
+ break;
+ case 5:
+ fps = 30, width = 720, colors = 128, bayer_scale = 1;
+ break;
+ }
+
+ baseArgs = ["-i", p(file), "-vf", `fps=${fps},scale=w=${width}:h=-1:flags=lanczos,mpdecimate,split[s0][s1];[s0]palettegen=max_colors=${colors}[p];[s1][p]paletteuse=dither=bayer:bayer_scale=${bayer_scale}`, "-loop", "0", "-bufsize", "1M", "-y"];
+ ext = "gif";
+ break;
+ }
+
+ await ffmpeg([...baseArgs, ...customArgs, `remux.${ext}`]);
+ return { file: `remux.${ext}`, videoTitle, extension: ext };
+}
+function upload({ file, videoTitle, extension }: { file: string; videoTitle: string; extension: string | undefined; }) {
+ if (!extension) throw "Invalid extension.";
+ const buffer = fs.readFileSync(p(file));
+ return { buffer, title: `${videoTitle}.${extension}` };
+}
+export async function execute(
+ _: IpcMainInvokeEvent,
+ opt: DownloadOptions
+): Promise<{
+ buffer: Buffer;
+ title: string;
+ logs: string;
+} | {
+ error: string;
+ logs: string;
+}> {
+ logs_global = "";
+ try {
+ const videoMetadata = await metadata(opt);
+ const videoFormat = genFormat(videoMetadata, opt);
+ const videoDownload = await download(videoFormat, opt);
+ const videoRemux = await remux(videoDownload, opt);
+ const videoUpload = upload(videoRemux);
+ return { logs: logs_global, ...videoUpload };
+ } catch (e: any) {
+ return { error: e.toString(), logs: logs_global };
+ }
+}
+
+export function checkffmpeg(_?: IpcMainInvokeEvent) {
+ try {
+ execFileSync("ffmpeg", ["-version"]);
+ execFileSync("ffprobe", ["-version"]);
+ ffmpegAvailable = true;
+ return true;
+ } catch (e) {
+ ffmpegAvailable = false;
+ return false;
+ }
+}
+export async function checkytdlp(_?: IpcMainInvokeEvent) {
+ try {
+ execFileSync("yt-dlp", ["--version"]);
+ ytdlpAvailable = true;
+ return true;
+ } catch (e) {
+ ytdlpAvailable = false;
+ return false;
+ }
+}
+
+export async function interrupt(_: IpcMainInvokeEvent) {
+ log("Interrupting...");
+ ytdlpProcess?.kill();
+ ffmpegProcess?.kill();
+ cleanVideoFiles();
+}
+
+export const getStdout = () => stdout_global;
+export const isYtdlpAvailable = () => ytdlpAvailable;
+export const isFfmpegAvailable = () => ffmpegAvailable;
diff --git a/src/equicordplugins/mediaDownloader.desktop/style.css b/src/equicordplugins/mediaDownloader.desktop/style.css
new file mode 100644
index 00000000..dfe22d90
--- /dev/null
+++ b/src/equicordplugins/mediaDownloader.desktop/style.css
@@ -0,0 +1,9 @@
+/* Only way to make on:hover styles */
+
+.media-downloader-link {
+ text-decoration: none !important;
+}
+
+.media-downloader-link:hover {
+ text-decoration: none !important;
+}
diff --git a/src/equicordplugins/noNitroUpsell/index.ts b/src/equicordplugins/noNitroUpsell/index.ts
index a5c47768..5208ba11 100644
--- a/src/equicordplugins/noNitroUpsell/index.ts
+++ b/src/equicordplugins/noNitroUpsell/index.ts
@@ -11,47 +11,45 @@ import { User } from "discord-types/general";
let user: ModifiedUser | undefined;
let lastUserId: string | undefined;
-let uninject: (() => void) | undefined;
interface ModifiedUser extends User {
_realPremiumType?: number;
}
+const onChange = () => {
+ const newUser = UserStore.getCurrentUser();
+ if (newUser && newUser.id !== lastUserId) {
+ user = newUser;
+ ready(user);
+ }
+};
+
+function ready(user: ModifiedUser) {
+ if (!user) return;
+ if ("_realPremiumType" in user) return;
+
+ user._realPremiumType = user.premiumType ?? 0;
+ user.premiumType = 2;
+ lastUserId = user.id;
+}
+
export default definePlugin({
name: "NoNitroUpsell",
description: "Removes ALL of Discord's nitro upsells by tricking the client into thinking you have nitro.",
authors: [EquicordDevs.thororen],
- ready(user: ModifiedUser): void {
- if (!user) return;
- if ("_realPremiumType" in user) return;
-
- user._realPremiumType = user.premiumType ?? 0;
- user.premiumType = 2;
- lastUserId = user.id;
- },
- start(): void {
+ start() {
user = UserStore.getCurrentUser();
- if (user) this.ready(user);
-
- const onChange = (): void => {
- const newUser = UserStore.getCurrentUser();
- if (newUser && newUser.id !== lastUserId) {
- user = newUser;
- this.ready(user);
- }
- };
+ if (user) ready(user);
UserStore.addChangeListener(onChange);
- uninject = () => UserStore.removeChangeListener(onChange);
},
- stop(): void {
- uninject?.();
+ stop() {
const user = UserStore.getCurrentUser();
if (!user) return;
if (!("_realPremiumType" in user)) return;
- UserStore?.getCurrentUser()?.premiumType;
// @ts-ignore
user.premiumType = user._realPremiumType;
delete user._realPremiumType;
+ UserStore.removeChangeListener(onChange);
}
});
diff --git a/src/equicordplugins/philsPluginLibrary/components/AuthorSummaryItem.tsx b/src/equicordplugins/philsPluginLibrary/components/AuthorSummaryItem.tsx
new file mode 100644
index 00000000..2f1d83b7
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/AuthorSummaryItem.tsx
@@ -0,0 +1,57 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { PluginAuthor } from "@utils/types";
+import { useEffect, UserUtils, useState } from "@webpack/common";
+import { User } from "discord-types/general";
+import React from "react";
+
+import { createDummyUser, types, UserSummaryItem } from "../../philsPluginLibrary";
+
+export interface AuthorUserSummaryItemProps extends Partial> {
+ authors: PluginAuthor[];
+}
+
+export const AuthorUserSummaryItem = (props: AuthorUserSummaryItemProps) => {
+ const [users, setUsers] = useState[]>([]);
+
+ useEffect(() => {
+ (async () => {
+ props.authors.forEach(author =>
+ UserUtils.getUser(`${author.id}`)
+ .then(user => setUsers(users => [...users, user]))
+ .catch(() => setUsers(users => [...users, createDummyUser({
+ username: author.name,
+ id: `${author.id}`,
+ bot: true,
+ })]))
+ );
+ })();
+ }, []);
+
+ return (
+
+ );
+};
diff --git a/src/equicordplugins/philsPluginLibrary/components/ContributorAuthorSummary.tsx b/src/equicordplugins/philsPluginLibrary/components/ContributorAuthorSummary.tsx
new file mode 100644
index 00000000..56f827b4
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/ContributorAuthorSummary.tsx
@@ -0,0 +1,53 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Flex } from "@components/Flex";
+import { Text } from "@webpack/common";
+import React from "react";
+
+import { Author, Contributor } from "../types";
+import { openURL } from "../utils";
+import { AuthorUserSummaryItem } from "./AuthorSummaryItem";
+
+export interface ContributorAuthorSummaryProps {
+ author?: Author;
+ contributors?: Contributor[];
+}
+
+export const ContributorAuthorSummary = ({ author, contributors }: ContributorAuthorSummaryProps) => {
+ return (
+
+ {author &&
+
+
+ Author: author.github && openURL(author.github)}>{`${author.name}`}
+
+
+
+ }
+ {(contributors && contributors.length > 0) &&
+
+
+ Contributors:
+
+
+
+ }
+
+ );
+};
diff --git a/src/equicordplugins/philsPluginLibrary/components/buttons/CopyButton.tsx b/src/equicordplugins/philsPluginLibrary/components/buttons/CopyButton.tsx
new file mode 100644
index 00000000..e1b46904
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/buttons/CopyButton.tsx
@@ -0,0 +1,37 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Button } from "@webpack/common";
+import React from "react";
+
+import { IconTooltipButton } from ".";
+
+export const CopyButton = (props: typeof Button["defaultProps"]) => {
+ return (
+
+
+
+
+ }
+ {...props} />
+ );
+};
diff --git a/src/equicordplugins/philsPluginLibrary/components/buttons/DeleteButton.tsx b/src/equicordplugins/philsPluginLibrary/components/buttons/DeleteButton.tsx
new file mode 100644
index 00000000..fa3901e7
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/buttons/DeleteButton.tsx
@@ -0,0 +1,39 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Button } from "@webpack/common";
+import * as types from "@webpack/types";
+import React from "react";
+
+import { IconTooltipButton } from ".";
+
+export const DeleteButton = (props: types.Button["defaultProps"]) => {
+ return (
+
+
+
+
+ }
+ {...props} />
+ );
+};
+
diff --git a/src/equicordplugins/philsPluginLibrary/components/buttons/IconTooltipButton.tsx b/src/equicordplugins/philsPluginLibrary/components/buttons/IconTooltipButton.tsx
new file mode 100644
index 00000000..7acaac38
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/buttons/IconTooltipButton.tsx
@@ -0,0 +1,44 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Flex } from "@components/Flex";
+import { Button, Tooltip } from "@webpack/common";
+import React from "react";
+
+
+export interface IconTooltipButtonProps {
+ tooltipText?: string;
+ icon?: JSX.Element;
+}
+
+export const IconTooltipButton = (props: typeof Button["defaultProps"] & IconTooltipButtonProps) => {
+ return (
+
+ {tooltipProps => }
+
+ );
+};
diff --git a/src/equicordplugins/philsPluginLibrary/components/buttons/NewButton.tsx b/src/equicordplugins/philsPluginLibrary/components/buttons/NewButton.tsx
new file mode 100644
index 00000000..12307030
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/buttons/NewButton.tsx
@@ -0,0 +1,37 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Button } from "@webpack/common";
+import React from "react";
+
+import { IconTooltipButton } from ".";
+
+export const NewButton = (props: typeof Button["defaultProps"]) => {
+ return (
+
+
+
+
+ }
+ {...props} />
+ );
+};
diff --git a/src/equicordplugins/philsPluginLibrary/components/buttons/SaveButton.tsx b/src/equicordplugins/philsPluginLibrary/components/buttons/SaveButton.tsx
new file mode 100644
index 00000000..6e1a2a16
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/buttons/SaveButton.tsx
@@ -0,0 +1,38 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Button } from "@webpack/common";
+import React from "react";
+
+import { IconTooltipButton } from ".";
+
+export const SaveButton = (props: typeof Button["defaultProps"]) => {
+ return (
+
+
+
+
+
+ }
+ {...props} />
+ );
+};
diff --git a/src/equicordplugins/philsPluginLibrary/components/buttons/index.tsx b/src/equicordplugins/philsPluginLibrary/components/buttons/index.tsx
new file mode 100644
index 00000000..e22ccb66
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/buttons/index.tsx
@@ -0,0 +1,23 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./CopyButton";
+export * from "./DeleteButton";
+export * from "./IconTooltipButton";
+export * from "./NewButton";
+export * from "./SaveButton";
diff --git a/src/equicordplugins/philsPluginLibrary/components/index.tsx b/src/equicordplugins/philsPluginLibrary/components/index.tsx
new file mode 100644
index 00000000..614223a6
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/index.tsx
@@ -0,0 +1,23 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./AuthorSummaryItem";
+export * from "./buttons";
+export * from "./ContributorAuthorSummary";
+export * from "./settingsModal";
+export * from "./settingsPanel";
diff --git a/src/equicordplugins/philsPluginLibrary/components/settingsModal/SettingsModal.tsx b/src/equicordplugins/philsPluginLibrary/components/settingsModal/SettingsModal.tsx
new file mode 100644
index 00000000..b1de6fd5
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/settingsModal/SettingsModal.tsx
@@ -0,0 +1,77 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Flex } from "@components/Flex";
+import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot } from "@utils/modal";
+import { Button, Text } from "@webpack/common";
+import React from "react";
+
+import { Author, Contributor } from "../../types";
+import { ContributorAuthorSummary } from "../ContributorAuthorSummary";
+
+
+export interface SettingsModalProps extends React.ComponentProps {
+ title?: string;
+ onClose: () => void;
+ onDone?: () => void;
+ footerContent?: JSX.Element;
+ closeButtonName?: string;
+ author?: Author,
+ contributors?: Contributor[];
+}
+
+export const SettingsModal = (props: SettingsModalProps) => {
+ const doneButton =
+ ;
+
+ return (
+
+
+ {props.title && {props.title}}
+
+
+
+
+ );
+};
diff --git a/src/equicordplugins/philsPluginLibrary/components/settingsModal/SettingsModalCard.tsx b/src/equicordplugins/philsPluginLibrary/components/settingsModal/SettingsModalCard.tsx
new file mode 100644
index 00000000..a532b3b5
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/settingsModal/SettingsModalCard.tsx
@@ -0,0 +1,83 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Switch } from "@components/Switch";
+import { Card, Forms } from "@webpack/common";
+import React from "react";
+
+export interface SettingsModalItemProps extends Pick,
+ | "children"> {
+ title?: string;
+ switchEnabled?: boolean;
+ switchProps?: React.ComponentProps;
+ flex?: number;
+ cardProps?: React.ComponentProps;
+}
+
+export const SettingsModalCard = ({ children, title, switchProps, switchEnabled, flex, cardProps }: SettingsModalItemProps) => {
+ return (
+
+ {title && {title}}
+
+
+ );
+};
diff --git a/src/equicordplugins/philsPluginLibrary/components/settingsModal/SettingsModalCardItem.tsx b/src/equicordplugins/philsPluginLibrary/components/settingsModal/SettingsModalCardItem.tsx
new file mode 100644
index 00000000..067cc875
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/settingsModal/SettingsModalCardItem.tsx
@@ -0,0 +1,39 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Forms } from "@webpack/common";
+import React from "react";
+
+export interface SettingsModalCardItemProps extends Pick,
+ | "children"> {
+ title?: string;
+}
+
+export const SettingsModalCardItem = ({ children, title }: SettingsModalCardItemProps) => {
+ return (
+
+ {title && {title}}
+ {children}
+
+ );
+};
diff --git a/src/equicordplugins/philsPluginLibrary/components/settingsModal/SettingsModalCardRow.tsx b/src/equicordplugins/philsPluginLibrary/components/settingsModal/SettingsModalCardRow.tsx
new file mode 100644
index 00000000..443aa549
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/settingsModal/SettingsModalCardRow.tsx
@@ -0,0 +1,31 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import React from "react";
+
+export interface SettingsModalRowProps extends Pick,
+ | "children"
+ | "style"> {
+ gap?: string;
+}
+
+export const SettingsModalCardRow = ({ children, style, gap }: SettingsModalRowProps) => {
+ return (
+
{children}
+ );
+};
diff --git a/src/equicordplugins/philsPluginLibrary/components/settingsModal/SettingsModalProfilesCard.tsx b/src/equicordplugins/philsPluginLibrary/components/settingsModal/SettingsModalProfilesCard.tsx
new file mode 100644
index 00000000..9caecb83
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/settingsModal/SettingsModalProfilesCard.tsx
@@ -0,0 +1,113 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Flex } from "@components/Flex";
+import { Select, TextInput, useEffect, useState } from "@webpack/common";
+
+import { PluginSettings, ProfilableStore } from "../../../philsPluginLibrary";
+import { CopyButton, DeleteButton, NewButton, SaveButton } from "../buttons";
+import { SettingsModalCard } from "./SettingsModalCard";
+import { SettingsModalCardItem } from "./SettingsModalCardItem";
+
+export interface SettingsModalProfilesCardProps extends React.ComponentProps {
+ profileableStore: ProfilableStore;
+ onSaveStateChanged: (isSaving: boolean) => void;
+}
+
+export const SettingsModalProfilesCard = (props: SettingsModalProfilesCardProps) => {
+ const { profileableStore: { use } } = props;
+
+ const {
+ currentProfile,
+ setCurrentProfile,
+ deleteProfile,
+ getCurrentProfile,
+ getDefaultProfiles,
+ getProfile,
+ getProfiles,
+ saveProfile,
+ isCurrentProfileADefaultProfile
+ } = use();
+
+ const { name } = currentProfile;
+
+ const [isSaving, setIsSaving] = useState(false);
+ const [profileNameInput, setProfileNameInput] = useState("");
+
+ useEffect(() => {
+ props.onSaveStateChanged(isSaving);
+ }, [isSaving]);
+
+ const onSaveProfile = () => {
+ if (!isSaving) {
+ setIsSaving(true);
+
+ } else {
+ if (profileNameInput.length && !getDefaultProfiles().some(value => value.name === profileNameInput)) {
+ saveProfile({ ...getCurrentProfile(), name: profileNameInput });
+ setCurrentProfile(getProfile(profileNameInput) || { name: "" });
+ setIsSaving(false);
+ }
+ }
+ };
+
+ const onCopyProfile = () => {
+ setCurrentProfile({ ...getCurrentProfile(), name: "" });
+ };
+
+ const onNewProfile = () => {
+ setCurrentProfile({ name: "" });
+ };
+
+ const onDeleteProfile = () => {
+ deleteProfile(currentProfile);
+ };
+
+ return (
+
+
+
+
+ {isSaving
+ ?
+ :
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/equicordplugins/philsPluginLibrary/components/settingsModal/index.tsx b/src/equicordplugins/philsPluginLibrary/components/settingsModal/index.tsx
new file mode 100644
index 00000000..7522d9f4
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/settingsModal/index.tsx
@@ -0,0 +1,23 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./SettingsModal";
+export * from "./SettingsModalCard";
+export * from "./SettingsModalCardItem";
+export * from "./SettingsModalCardRow";
+export * from "./SettingsModalProfilesCard";
diff --git a/src/equicordplugins/philsPluginLibrary/components/settingsPanel/SettingsPanel.tsx b/src/equicordplugins/philsPluginLibrary/components/settingsPanel/SettingsPanel.tsx
new file mode 100644
index 00000000..654af6bf
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/settingsPanel/SettingsPanel.tsx
@@ -0,0 +1,35 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { React } from "@webpack/common";
+
+import { panelClasses } from "../../discordModules";
+
+
+export interface SettingsPanelProps {
+ children: React.ComponentProps<"div">["children"];
+}
+
+export const SettingsPanel = ({ children }: SettingsPanelProps) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/src/equicordplugins/philsPluginLibrary/components/settingsPanel/SettingsPanelButton.tsx b/src/equicordplugins/philsPluginLibrary/components/settingsPanel/SettingsPanelButton.tsx
new file mode 100644
index 00000000..5531d7ea
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/settingsPanel/SettingsPanelButton.tsx
@@ -0,0 +1,40 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { classes } from "@utils/misc";
+import { Button } from "@webpack/common";
+import React from "react";
+
+import { panelClasses } from "../../../philsPluginLibrary";
+
+export type IconComponent = (props: T) => JSX.Element;
+export interface SettingsPanelButtonProps extends Partial> {
+ icon?: IconComponent;
+}
+
+export const SettingsPanelButton = (props: SettingsPanelButtonProps) => {
+ return (
+ }
+ {...props} />
+ );
+};
diff --git a/src/equicordplugins/philsPluginLibrary/components/settingsPanel/SettingsPanelRow.tsx b/src/equicordplugins/philsPluginLibrary/components/settingsPanel/SettingsPanelRow.tsx
new file mode 100644
index 00000000..71300c92
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/settingsPanel/SettingsPanelRow.tsx
@@ -0,0 +1,37 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { classes } from "@utils/misc";
+import React from "react";
+
+import { panelClasses } from "../../discordModules";
+
+export interface SettingsPanelRowProps {
+ children: React.ComponentProps<"div">["children"];
+}
+
+export const SettingsPanelRow = ({ children }: SettingsPanelRowProps) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/src/equicordplugins/philsPluginLibrary/components/settingsPanel/SettingsPanelTooltipButton.tsx b/src/equicordplugins/philsPluginLibrary/components/settingsPanel/SettingsPanelTooltipButton.tsx
new file mode 100644
index 00000000..4bb2f7f0
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/settingsPanel/SettingsPanelTooltipButton.tsx
@@ -0,0 +1,34 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Tooltip } from "@webpack/common";
+import React from "react";
+
+import { SettingsPanelButton, SettingsPanelButtonProps } from "./SettingsPanelButton";
+
+export interface SettingsPanelTooltipButtonProps extends SettingsPanelButtonProps {
+ tooltipProps: Omit, "children">;
+}
+
+export const SettingsPanelTooltipButton = (props: SettingsPanelTooltipButtonProps) => {
+ return (
+
+ {tooltipProps => }
+
+ );
+};
diff --git a/src/equicordplugins/philsPluginLibrary/components/settingsPanel/index.tsx b/src/equicordplugins/philsPluginLibrary/components/settingsPanel/index.tsx
new file mode 100644
index 00000000..e8d96aab
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/components/settingsPanel/index.tsx
@@ -0,0 +1,22 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./SettingsPanel";
+export * from "./SettingsPanelButton";
+export * from "./SettingsPanelRow";
+export * from "./SettingsPanelTooltipButton";
diff --git a/src/equicordplugins/philsPluginLibrary/constants/constants.ts b/src/equicordplugins/philsPluginLibrary/constants/constants.ts
new file mode 100644
index 00000000..f92808c1
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/constants/constants.ts
@@ -0,0 +1,30 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Devs } from "@utils/constants";
+
+import * as types from "../types/constants";
+
+export const PluginInfo: types.PluginInfo = {
+ PLUGIN_NAME: "PhilsPluginLibrary",
+ DESCRIPTION: "A library for phil's plugins",
+ AUTHOR: {
+ ...Devs.philhk,
+ github: "https://github.com/philhk"
+ },
+} as const;
diff --git a/src/equicordplugins/philsPluginLibrary/constants/index.ts b/src/equicordplugins/philsPluginLibrary/constants/index.ts
new file mode 100644
index 00000000..c7b948ab
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/constants/index.ts
@@ -0,0 +1,19 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./constants";
diff --git a/src/equicordplugins/philsPluginLibrary/discordModules/classes.ts b/src/equicordplugins/philsPluginLibrary/discordModules/classes.ts
new file mode 100644
index 00000000..9496bf86
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/discordModules/classes.ts
@@ -0,0 +1,25 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { findByPropsLazy } from "@webpack";
+
+import * as types from "../types";
+
+export const panelClasses: types.PanelClasses = findByPropsLazy("button", "buttonContents", "buttonColor");
+
+// waitFor(filters.byProps("button", "buttonContents", "buttonColor"), result => panelClasses = result);
diff --git a/src/equicordplugins/philsPluginLibrary/discordModules/components.tsx b/src/equicordplugins/philsPluginLibrary/discordModules/components.tsx
new file mode 100644
index 00000000..fc38f1e5
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/discordModules/components.tsx
@@ -0,0 +1,24 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { LazyComponent } from "@utils/react";
+import { findByCode } from "@webpack";
+
+import { types } from "../";
+
+export const UserSummaryItem = LazyComponent>(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"));
diff --git a/src/equicordplugins/philsPluginLibrary/discordModules/index.ts b/src/equicordplugins/philsPluginLibrary/discordModules/index.ts
new file mode 100644
index 00000000..af5ab967
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/discordModules/index.ts
@@ -0,0 +1,22 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./classes";
+export * from "./components";
+export * from "./modules";
+export * from "./stores";
diff --git a/src/equicordplugins/philsPluginLibrary/discordModules/modules.ts b/src/equicordplugins/philsPluginLibrary/discordModules/modules.ts
new file mode 100644
index 00000000..13715411
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/discordModules/modules.ts
@@ -0,0 +1,25 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { filters, waitFor } from "@webpack";
+
+import * as types from "../types";
+
+export let utils: types.Utils;
+
+waitFor(filters.byProps("getPidFromDesktopSource"), result => utils = result);
diff --git a/src/equicordplugins/philsPluginLibrary/discordModules/stores.ts b/src/equicordplugins/philsPluginLibrary/discordModules/stores.ts
new file mode 100644
index 00000000..a620e9b1
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/discordModules/stores.ts
@@ -0,0 +1,25 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { waitForStore } from "webpack/common/internal";
+
+import * as types from "../types";
+
+export let MediaEngineStore: types.MediaEngineStore;
+
+waitForStore("MediaEngineStore", store => MediaEngineStore = store);
diff --git a/src/equicordplugins/philsPluginLibrary/emitter/emitter.ts b/src/equicordplugins/philsPluginLibrary/emitter/emitter.ts
new file mode 100644
index 00000000..c3643c8e
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/emitter/emitter.ts
@@ -0,0 +1,86 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import EventEmitter from "events";
+import TypedEmitter from "typed-emitter";
+
+export type TypedEmitterEvents> = J extends TypedEmitter<
+ infer N
+>
+ ? N
+ : never;
+
+export interface EmitterEvent {
+ emitter: TypedEmitter | EventEmitter;
+ event: any;
+ fn: (...args: any[]) => any;
+ plugin?: string;
+}
+
+export class Emitter {
+ private static events: EmitterEvent[] = [];
+
+ public static addListener<
+ T extends TypedEmitter,
+ U extends keyof TypedEmitterEvents,
+ V extends TypedEmitterEvents[U]
+ >(
+ emitter: T,
+ type: keyof Pick,
+ event: U,
+ fn: V,
+ plugin?: string
+ ): () => void;
+
+ public static addListener(
+ emitter: EventEmitter,
+ type: keyof Pick,
+ event: string,
+ fn: (...args: any[]) => void,
+ plugin?: string
+ ): () => void {
+ emitter[type](event, fn);
+ const emitterEvenet: EmitterEvent = {
+ emitter,
+ event,
+ fn,
+ plugin: plugin
+ };
+ this.events.push(emitterEvenet);
+
+ return () => this.removeListener(emitterEvenet);
+ }
+
+ public static removeListener(emitterEvent: EmitterEvent) {
+ emitterEvent.emitter.removeListener(emitterEvent.event, emitterEvent.fn);
+ this.events = this.events.filter(
+ emitterEvent_ => emitterEvent_ !== emitterEvent
+ );
+ }
+
+ public static removeAllListeners(plugin?: string) {
+ if (!plugin) {
+ this.events.forEach(emitterEvent =>
+ this.removeListener(emitterEvent)
+ );
+ } else
+ this.events.forEach(emitterEvent =>
+ plugin === emitterEvent.plugin && this.removeListener(emitterEvent)
+ );
+ }
+}
diff --git a/src/equicordplugins/philsPluginLibrary/emitter/index.ts b/src/equicordplugins/philsPluginLibrary/emitter/index.ts
new file mode 100644
index 00000000..5f6e9fdb
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/emitter/index.ts
@@ -0,0 +1,19 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./emitter";
diff --git a/src/equicordplugins/philsPluginLibrary/icons/index.tsx b/src/equicordplugins/philsPluginLibrary/icons/index.tsx
new file mode 100644
index 00000000..726a6782
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/icons/index.tsx
@@ -0,0 +1,142 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export const ScreenshareSettingsIcon =
+ (props: React.ComponentProps<"svg">) =>
+ ;
+
+export const CameraSettingsIcon =
+ (props: React.ComponentProps<"svg">) =>
+ ;
+
+export const MicrophoneSettingsIcon =
+ (props: React.ComponentProps<"svg">) =>
+ ;
diff --git a/src/equicordplugins/philsPluginLibrary/index.tsx b/src/equicordplugins/philsPluginLibrary/index.tsx
new file mode 100644
index 00000000..46b211a5
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/index.tsx
@@ -0,0 +1,50 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Devs } from "@utils/constants";
+import definePlugin from "@utils/types";
+
+import { replacedUserPanelComponent } from "./patches";
+
+export default definePlugin({
+ name: "PhilsPluginLibrary",
+ description: "A library for phil's plugins",
+ authors: [Devs.philhk],
+ replacedUserPanelComponent: replacedUserPanelComponent,
+
+ patches: [
+ {
+ find: "Messages.ACCOUNT_A11Y_LABEL",
+ replacement: {
+ match: /(?<=function)( .{0,8}(?={).)(.{0,1000}isFullscreenInContext\(\).+?\)]}\))(})/,
+ replace: "$1return $self.replacedUserPanelComponent(function(){$2}, this, arguments)$3"
+ }
+ }
+ ],
+});
+
+
+export * from "./components";
+export * from "./discordModules";
+export * from "./emitter";
+export * from "./icons";
+export * from "./patchers";
+export * from "./patches";
+export * from "./store";
+export * as types from "./types";
+export * from "./utils";
diff --git a/src/equicordplugins/philsPluginLibrary/patchers/index.ts b/src/equicordplugins/philsPluginLibrary/patchers/index.ts
new file mode 100644
index 00000000..a2c10f64
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/patchers/index.ts
@@ -0,0 +1,19 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./patcher";
diff --git a/src/equicordplugins/philsPluginLibrary/patchers/patcher.ts b/src/equicordplugins/philsPluginLibrary/patchers/patcher.ts
new file mode 100644
index 00000000..88b2b824
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/patchers/patcher.ts
@@ -0,0 +1,29 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export abstract class Patcher {
+ protected unpatchFunctions: (() => any)[] = [];
+ public abstract patch(): this;
+ public abstract unpatch(): this;
+ protected _unpatch(): this {
+ this.unpatchFunctions.forEach(fn => fn());
+ this.unpatchFunctions = [];
+
+ return this;
+ }
+}
diff --git a/src/equicordplugins/philsPluginLibrary/patches/audio.ts b/src/equicordplugins/philsPluginLibrary/patches/audio.ts
new file mode 100644
index 00000000..005a5330
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/patches/audio.ts
@@ -0,0 +1,88 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Logger } from "@utils/Logger";
+import { lodash } from "@webpack/common";
+
+import { MicrophoneProfile, MicrophoneStore } from "../../betterMicrophone.desktop/stores";
+import { ProfilableStore, replaceObjectValuesIfExist, types } from "../../philsPluginLibrary";
+
+export function getDefaultAudioTransportationOptions(connection: types.Connection) {
+ return {
+ audioEncoder: {
+ ...connection.getCodecOptions("opus").audioEncoder,
+ },
+ encodingVoiceBitRate: 64000
+ };
+}
+
+export function getReplaceableAudioTransportationOptions(connection: types.Connection, get: ProfilableStore["get"]) {
+ const { currentProfile } = get();
+ const {
+ channels,
+ channelsEnabled,
+ freq,
+ freqEnabled,
+ pacsize,
+ pacsizeEnabled,
+ rate,
+ rateEnabled,
+ voiceBitrate,
+ voiceBitrateEnabled
+ } = currentProfile;
+
+ return {
+ ...(voiceBitrateEnabled && voiceBitrate
+ ? {
+ encodingVoiceBitRate: voiceBitrate * 1000
+ }
+ : {}
+ ),
+ audioEncoder: {
+ ...connection.getCodecOptions("opus").audioEncoder,
+ ...(rateEnabled && rate ? { rate } : {}),
+ ...(pacsizeEnabled && pacsize ? { pacsize } : {}),
+ ...(freqEnabled && freq ? { freq } : {}),
+ ...(channelsEnabled && channels ? { channels } : {})
+ }
+ };
+}
+
+export function patchConnectionAudioTransportOptions(
+ connection: types.Connection,
+ get: ProfilableStore["get"],
+ logger?: Logger
+) {
+ const oldSetTransportOptions = connection.conn.setTransportOptions;
+
+ connection.conn.setTransportOptions = function (this: any, options: Record) {
+ replaceObjectValuesIfExist(options, getReplaceableAudioTransportationOptions(connection, get));
+
+ return Reflect.apply(oldSetTransportOptions, this, [options]);
+ };
+
+ const forceUpdateTransportationOptions = () => {
+ const transportOptions = lodash.merge({ ...getDefaultAudioTransportationOptions(connection) }, getReplaceableAudioTransportationOptions(connection, get));
+
+ logger?.info("Overridden Transport Options", transportOptions);
+
+ oldSetTransportOptions(transportOptions);
+ };
+
+ return { oldSetTransportOptions, forceUpdateTransportationOptions };
+}
diff --git a/src/equicordplugins/philsPluginLibrary/patches/index.ts b/src/equicordplugins/philsPluginLibrary/patches/index.ts
new file mode 100644
index 00000000..7341e509
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/patches/index.ts
@@ -0,0 +1,21 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./audio";
+export * from "./userPanel";
+export * from "./video";
diff --git a/src/equicordplugins/philsPluginLibrary/patches/userPanel.tsx b/src/equicordplugins/philsPluginLibrary/patches/userPanel.tsx
new file mode 100644
index 00000000..891b78e4
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/patches/userPanel.tsx
@@ -0,0 +1,107 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { React } from "@webpack/common";
+
+import { SettingsPanel } from "../components";
+import { IconComponent, SettingsPanelButton } from "../components/settingsPanel/SettingsPanelButton";
+import { SettingsPanelRow } from "../components/settingsPanel/SettingsPanelRow";
+import { SettingsPanelTooltipButton } from "../components/settingsPanel/SettingsPanelTooltipButton";
+
+export interface PanelButton {
+ name: string,
+ tooltipText?: string,
+ icon?: IconComponent;
+ onClick?: () => void;
+}
+
+const settingsPanelButtonsSubscriptions = new Set();
+export const settingsPanelButtons: PanelButton[] = new Proxy([], {
+ set: (target, p, newValue) => {
+ target[p] = newValue;
+ settingsPanelButtonsSubscriptions.forEach(fn => fn());
+ return true;
+ },
+});
+
+export const useButtons = () => {
+ const [, forceUpdate] = React.useReducer(() => ({}), {});
+
+ React.useEffect(() => {
+ settingsPanelButtonsSubscriptions.add(forceUpdate);
+ return () => void settingsPanelButtonsSubscriptions.delete(() => forceUpdate);
+ }, []);
+
+ return settingsPanelButtons;
+};
+
+export const ButtonsSettingsPanel = () => {
+ const rawPanelButtons = useButtons();
+
+ const convertRawPanelButtons = (buttons: PanelButton[]) => {
+ const settingsPanelButtonsClone = [...buttons].sort();
+ const groupedButtons: JSX.Element[][] = [];
+
+ while (settingsPanelButtonsClone.length) {
+ const splicedButtons =
+ settingsPanelButtonsClone
+ .splice(0, 3)
+ .map(({ icon, tooltipText, onClick }) =>
+ tooltipText
+ ?
+ :
+ );
+
+ groupedButtons.push(splicedButtons);
+ }
+
+ return groupedButtons;
+ };
+
+ return rawPanelButtons.length > 0
+ ?
+ {...convertRawPanelButtons(rawPanelButtons).map(value => )}
+
+ : <>
+ >;
+};
+
+export function replacedUserPanelComponent(oldComponent: (...args: any[]) => any, thisContext: any, functionArguments: any) {
+ const componentResult: JSX.Element = Reflect.apply(oldComponent, thisContext, functionArguments);
+
+ if (!componentResult?.props) return componentResult;
+
+ const { children } = componentResult.props;
+
+ const userPanel = children.at(-2);
+ const userPanelChildren = userPanel.props.children;
+
+ userPanelChildren.splice(userPanelChildren.length - 1, 0,
+
+ );
+
+ return componentResult;
+}
+
+export function addSettingsPanelButton(settings: PanelButton) {
+ settingsPanelButtons.push(settings);
+}
+
+export function removeSettingsPanelButton(name: string) {
+ settingsPanelButtons.splice(0, settingsPanelButtons.length, ...settingsPanelButtons.filter(value => value.name !== name));
+}
diff --git a/src/equicordplugins/philsPluginLibrary/patches/video.ts b/src/equicordplugins/philsPluginLibrary/patches/video.ts
new file mode 100644
index 00000000..956e7725
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/patches/video.ts
@@ -0,0 +1,253 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Logger } from "@utils/Logger";
+import { lodash } from "@webpack/common";
+
+import { ScreenshareProfile, ScreenshareStore } from "../../betterScreenshare.dev/stores";
+import { ProfilableStore, replaceObjectValuesIfExist, types, utils } from "../../philsPluginLibrary";
+
+
+export function getDefaultVideoTransportationOptions(connection: types.Connection) {
+ return {
+ ...connection.videoQualityManager.applyQualityConstraints({}).constraints,
+ videoEncoder: {
+ ...connection.getCodecOptions("", "H264", "stream").videoEncoder
+ },
+ streamParameters: connection.videoStreamParameters[0],
+ keyframeInterval: 0,
+ };
+}
+
+export function getDefaultVideoDesktopSourceOptions(connection: types.Connection) {
+ const [type, sourceId] = connection.goLiveSourceIdentifier?.split(":") ?? ["screen", 0];
+
+ return {
+ hdrCaptureMode: "never",
+ allowScreenCaptureKit: true,
+ useQuartzCapturer: true,
+ useGraphicsCapture: true,
+ useVideoHook: true,
+ sourceId: sourceId,
+ type: type
+ };
+}
+
+export function getStreamParameters(connection: types.Connection, get: ProfilableStore["get"]) {
+ const { currentProfile } = get();
+ const {
+ framerate,
+ framerateEnabled,
+ height,
+ resolutionEnabled,
+ videoBitrate,
+ videoBitrateEnabled,
+ width,
+ } = currentProfile;
+
+ const { bitrateMax, capture } = connection.applyQualityConstraints({}).quality;
+
+ return {
+ ...connection.videoStreamParameters[0],
+ ...(videoBitrateEnabled && videoBitrate
+ ? {
+ maxBitrate: videoBitrate * 1000,
+ }
+ : {
+ maxBitrate: bitrateMax
+ }
+ ),
+ ...((resolutionEnabled && width && height)
+ ? {
+ maxResolution: {
+ height: height,
+ width: width,
+ type: "fixed"
+ },
+ maxPixelCount: width * height
+ }
+ : {
+ maxResolution: !capture.height || !capture.width ? {
+ height: capture.height,
+ width: capture.width,
+ type: "source"
+ } : {
+ height: capture.height,
+ width: capture.width,
+ type: "fixed"
+ }
+ }
+ ),
+ ...(framerateEnabled && framerate
+ ? {
+ maxFrameRate: framerate,
+ }
+ : {
+ maxFrameRate: capture.framerate
+ }
+ ),
+ active: true,
+ };
+}
+
+export function getReplaceableVideoTransportationOptions(connection: types.Connection, get: ProfilableStore["get"]) {
+ const { currentProfile, audioSource, audioSourceEnabled } = get();
+ const {
+ framerate,
+ framerateEnabled,
+ height,
+ keyframeInterval,
+ keyframeIntervalEnabled,
+ resolutionEnabled,
+ videoBitrate,
+ videoBitrateEnabled,
+ videoCodec,
+ videoCodecEnabled,
+ width,
+ } = currentProfile;
+
+ return {
+ ...(videoBitrateEnabled && videoBitrate
+ ? {
+ encodingVideoBitRate: videoBitrate * 1000,
+ encodingVideoMinBitRate: videoBitrate * 1000,
+ encodingVideoMaxBitRate: videoBitrate * 1000,
+ callBitRate: videoBitrate * 1000,
+ callMinBitRate: videoBitrate * 1000,
+ callMaxBitRate: videoBitrate * 1000
+ }
+ : {}
+ ),
+ ...((resolutionEnabled && width && height)
+ ? {
+ encodingVideoHeight: height,
+ encodingVideoWidth: width,
+ remoteSinkWantsPixelCount: height * width
+ }
+ : {}
+ ),
+ ...(framerateEnabled && framerate
+ ? {
+ encodingVideoFrameRate: framerate,
+ remoteSinkWantsMaxFramerate: framerate
+ }
+ : {}
+ ),
+ ...(keyframeIntervalEnabled && keyframeInterval
+ ? {
+ keyframeInterval: keyframeInterval
+ }
+ : {}
+ ),
+ ...(videoCodecEnabled && videoCodec
+ ? {
+ videoEncoder: connection.getCodecOptions("", videoCodec, "stream").videoEncoder
+ }
+ : {}
+ ),
+ ...(audioSourceEnabled && audioSource && utils.getPidFromDesktopSource(audioSource)
+ ? {
+ soundsharePid: utils.getPidFromDesktopSource(audioSource),
+ soundshareEventDriven: true,
+ soundshareLoopback: true
+ }
+ : {}
+ ),
+ streamParameters: getStreamParameters(connection, get)
+ };
+}
+
+export function getReplaceableVideoDesktopSourceOptions(get: ProfilableStore["get"]) {
+ const { currentProfile } = get();
+ const {
+ hdrEnabled,
+ } = currentProfile;
+
+ return {
+ ...(hdrEnabled
+ ? {
+ hdrCaptureMode: "always"
+ }
+ : {}
+ ),
+ };
+}
+
+export function patchConnectionVideoSetDesktopSourceWithOptions(
+ connection: types.Connection,
+ get: ProfilableStore["get"],
+ logger?: Logger
+) {
+ const oldSetDesktopSourceWithOptions = connection.conn.setDesktopSourceWithOptions;
+
+ connection.conn.setDesktopSourceWithOptions = function (this: any, options: Record) {
+ const replaceableDesktopSourceOptions = getReplaceableVideoDesktopSourceOptions(get);
+ replaceObjectValuesIfExist(options, replaceableDesktopSourceOptions);
+
+ logger?.info("Overridden Desktop Source Options", options);
+
+ return Reflect.apply(oldSetDesktopSourceWithOptions, this, [options]);
+ };
+
+ const forceUpdateDesktopSourceOptions = () => {
+ const desktopSourceOptions = lodash.merge({ ...getDefaultVideoDesktopSourceOptions(connection) }, getReplaceableVideoDesktopSourceOptions(get));
+
+ logger?.info("Force Updated Desktop Source Options", desktopSourceOptions);
+
+ oldSetDesktopSourceWithOptions(desktopSourceOptions);
+ };
+
+ return {
+ oldSetDesktopSourceWithOptions,
+ forceUpdateDesktopSourceOptions
+ };
+}
+
+export function patchConnectionVideoTransportOptions(
+ connection: types.Connection,
+ get: ProfilableStore["get"],
+ logger?: Logger
+) {
+ const oldSetTransportOptions = connection.conn.setTransportOptions;
+
+ connection.conn.setTransportOptions = function (this: any, options: Record) {
+ const replaceableTransportOptions = getReplaceableVideoTransportationOptions(connection, get);
+
+ if (options.streamParameters)
+ connection.videoStreamParameters = Array.isArray(options.streamParameters) ? options.streamParameters : [options.streamParameters];
+
+ replaceObjectValuesIfExist(options, replaceableTransportOptions);
+
+ logger?.info("Overridden Transport Options", options);
+
+ return Reflect.apply(oldSetTransportOptions, this, [options]);
+ };
+
+ const forceUpdateTransportationOptions = () => {
+ const transportOptions = lodash.merge({ ...getDefaultVideoTransportationOptions(connection) }, getReplaceableVideoTransportationOptions(connection, get));
+
+ logger?.info("Force Updated Transport Options", transportOptions);
+
+ oldSetTransportOptions(transportOptions);
+ };
+
+ return {
+ oldSetTransportOptions,
+ forceUpdateTransportationOptions,
+ };
+}
diff --git a/src/equicordplugins/philsPluginLibrary/store/index.ts b/src/equicordplugins/philsPluginLibrary/store/index.ts
new file mode 100644
index 00000000..4e1e78a1
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/store/index.ts
@@ -0,0 +1,20 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./profileable";
+export * from "./store";
diff --git a/src/equicordplugins/philsPluginLibrary/store/profileable.ts b/src/equicordplugins/philsPluginLibrary/store/profileable.ts
new file mode 100644
index 00000000..5113a843
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/store/profileable.ts
@@ -0,0 +1,85 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { PluginInitializer, PluginSettings, PluginStore } from "./store";
+
+export interface ProfileableProfile {
+ name: string;
+}
+
+export interface ProfileableSettings {
+ currentProfile: B;
+ profiles: B[];
+ setCurrentProfile: (f: ((currentProfile: B) => B | undefined) | B | undefined) => void;
+ getCurrentProfile: () => B;
+ duplicateProfile: (profile: string | B, name: string) => void;
+ deleteProfile: (profile: string | B) => void;
+ saveProfile: (profile: B) => void;
+ getProfile: (profile: string) => B | undefined;
+ getProfiles: (defaultProfiles: boolean) => B[],
+ isCurrentProfileADefaultProfile: () => boolean;
+ getDefaultProfiles: () => B[];
+}
+
+export type ProfilableStore<
+ T extends PluginSettings = {},
+ S extends PluginSettings = {}
+> = PluginStore>;
+
+export type ProfilableMiddleware<
+ T extends PluginSettings = {},
+ S extends PluginSettings = {},
+ B = T & ProfileableSettings
+> = PluginInitializer, B>;
+
+export type ProfilableInitializer<
+ T extends PluginSettings = {},
+ S extends PluginSettings = {}
+> = ProfilableMiddleware>>;
+
+export function profileable<
+ T extends PluginSettings = {},
+ S extends PluginSettings = {}
+>(f: ProfilableInitializer, defaultProfile: ProfileableProfile & S, defaultProfiles: (ProfileableProfile & S)[] = []): ProfilableMiddleware {
+ return (set, get) => ({
+ currentProfile: defaultProfile,
+ profiles: [],
+ getCurrentProfile: () => get().currentProfile,
+ getProfile: profile => [...get().profiles, ...(defaultProfiles ?? [])].find(p => p.name === profile),
+ deleteProfile: profile => get().profiles = get().profiles.filter(p => typeof profile === "string" ? p.name !== profile : p.name !== profile.name),
+ duplicateProfile: (profile, name) => {
+ const foundProfile = get().profiles.find(p => typeof profile === "string" ? p.name === profile : p.name === profile.name);
+ if (foundProfile) {
+ foundProfile.name = name;
+ get().profiles.push(foundProfile);
+ }
+ },
+ setCurrentProfile: f => {
+ const currProfile = get().currentProfile;
+ get().currentProfile = (typeof f === "function" ? f(currProfile) ?? currProfile : f ?? currProfile);
+ },
+ saveProfile: profile => {
+ get().deleteProfile(profile.name);
+ get().profiles.push(profile);
+ },
+ isCurrentProfileADefaultProfile: () => defaultProfiles.some(profile => get().currentProfile.name === profile.name),
+ getDefaultProfiles: () => defaultProfiles,
+ getProfiles: defaultProfiles => [...get().profiles, ...(defaultProfiles ? get().getDefaultProfiles() : [])],
+ ...f(set as any, get as any)
+ });
+}
diff --git a/src/equicordplugins/philsPluginLibrary/store/store.ts b/src/equicordplugins/philsPluginLibrary/store/store.ts
new file mode 100644
index 00000000..1e5251e4
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/store/store.ts
@@ -0,0 +1,102 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Settings, useSettings } from "@api/Settings";
+
+export type PluginSettings = {
+ [key: string]: any;
+};
+
+export type PluginUse = () => Z;
+export type PluginGet = () => Z;
+export type PluginSet = (s: Z | ((settings: Z) => Z | undefined)) => Z;
+export type PluginInitializer = (set: PluginSet, get: PluginGet) => T;
+export interface PluginStore {
+ use: PluginUse,
+ get: PluginGet,
+ set: PluginSet;
+}
+
+function createObjectProxy(obj1: T, onUpdate: (updatedObject: T) => void): T {
+ const handler: ProxyHandler = {
+ set(target, property, value, receiver) {
+ const success = Reflect.set(target, property, value, receiver);
+ const nestedObj = target[property];
+
+ if (typeof nestedObj === "object") {
+ target[property] = createObjectProxy(nestedObj, () => { onUpdate(obj1); }); // On update will call itself until the top level object
+ }
+
+ onUpdate(obj1); // This will recursively call on nested objects
+ return success;
+ }
+ };
+
+ return new Proxy(obj1, handler);
+}
+
+
+const startupStates = {};
+const settingStorage = new Map();
+export function createPluginStore(pluginName: string, storeName: string, f: PluginInitializer): PluginStore {
+ if (!Settings.plugins[pluginName])
+ throw new Error("The specified plugin does not exist");
+
+ if (!Settings.plugins[pluginName].stores)
+ Settings.plugins[pluginName].stores = {};
+
+ if (!Settings.plugins[pluginName].stores[storeName]) // Just incase the store doesn't exist we create it here (otherwise we crash)
+ Settings.plugins[pluginName].stores[storeName] = {};
+
+ const get: PluginGet = () => {
+ const storeSettings = settingStorage.get(storeName);
+
+ if (!startupStates[storeName]) { // We do this so that we can load all the saved data without the proxy attempting to overwrite it
+ const startupInfo = Settings.plugins[pluginName].stores[storeName];
+ Object.keys(startupInfo).forEach(prop => storeSettings[prop] = startupInfo[prop]);
+
+ startupStates[storeName] = true;
+ }
+
+ return storeSettings;
+ };
+
+ const set: PluginSet = (s: ((settings: Z) => Z | undefined) | Z) =>
+ (typeof s === "function" ? s(get()) : s) || get();
+
+ const use: PluginUse = () => { useSettings().plugins[pluginName].stores[storeName]; return get(); }; // useSettings is called to update renderer (after settings change)
+
+ const initialSettings: Z = f(set, get);
+ const proxiedSettings = createObjectProxy(initialSettings as unknown, updateCallback); // Setup our proxy that allows us connections to the datastore
+
+ function updateCallback(updatedObject: any) {
+ if (!startupStates[storeName]) return; // Wait for the startup information to overwrite the blank proxy
+ Settings.plugins[pluginName].stores[storeName] = JSON.parse(JSON.stringify(updatedObject));
+ }
+
+ for (const key of Object.keys(initialSettings)) { proxiedSettings[key] = initialSettings[key]; } // Set them so the nested objects also become proxies
+ settingStorage.set(storeName, proxiedSettings);
+
+ updateCallback(initialSettings);
+
+ return {
+ use,
+ get,
+ set
+ };
+}
diff --git a/src/equicordplugins/philsPluginLibrary/styles/index.ts b/src/equicordplugins/philsPluginLibrary/styles/index.ts
new file mode 100644
index 00000000..c38f7535
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/styles/index.ts
@@ -0,0 +1,19 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./styles";
diff --git a/src/equicordplugins/philsPluginLibrary/styles/styles.ts b/src/equicordplugins/philsPluginLibrary/styles/styles.ts
new file mode 100644
index 00000000..9c99b5fc
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/styles/styles.ts
@@ -0,0 +1,25 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export const Styles = {
+ infoCard: {
+ padding: "1em",
+ width: "100%",
+ boxSizing: "border-box"
+ },
+} as const satisfies Record;
diff --git a/src/equicordplugins/philsPluginLibrary/types/common/index.ts b/src/equicordplugins/philsPluginLibrary/types/common/index.ts
new file mode 100644
index 00000000..2521026b
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/types/common/index.ts
@@ -0,0 +1,46 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export interface Resolution {
+ width: number;
+ height: number;
+}
+
+export interface Framerate {
+ framerate: number;
+}
+
+export interface Bitrate {
+ min: number;
+ target: number;
+ max: number;
+}
+
+export type DeepPartial = T extends Function
+ ? T
+ : T extends Array
+ ? DeepPartialArray
+ : T extends object
+ ? DeepPartialObject
+ : T | undefined;
+
+interface DeepPartialArray extends Array> { }
+
+type DeepPartialObject = {
+ [key in keyof T]?: DeepPartial;
+};
diff --git a/src/equicordplugins/philsPluginLibrary/types/constants/index.ts b/src/equicordplugins/philsPluginLibrary/types/constants/index.ts
new file mode 100644
index 00000000..df7ec417
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/types/constants/index.ts
@@ -0,0 +1,31 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { PluginAuthor } from "@utils/types";
+
+export type Author = PluginAuthor & { github?: string; };
+export type Contributor = Author;
+
+export interface PluginInfo {
+ [key: string]: any;
+ PLUGIN_NAME: string,
+ DESCRIPTION: string,
+ AUTHOR: PluginAuthor & { github?: string; },
+ CONTRIBUTORS?: Record,
+ README?: string;
+}
diff --git a/src/equicordplugins/philsPluginLibrary/types/discordModules/classes/index.ts b/src/equicordplugins/philsPluginLibrary/types/discordModules/classes/index.ts
new file mode 100644
index 00000000..584d422e
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/types/discordModules/classes/index.ts
@@ -0,0 +1,19 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./panelClasses";
diff --git a/src/equicordplugins/philsPluginLibrary/types/discordModules/classes/panelClasses.ts b/src/equicordplugins/philsPluginLibrary/types/discordModules/classes/panelClasses.ts
new file mode 100644
index 00000000..f78850cd
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/types/discordModules/classes/panelClasses.ts
@@ -0,0 +1,53 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export interface PanelClasses {
+ container: string;
+ inner: string;
+ channel: string;
+ statusWithPopout: string;
+ hotspot: string;
+ customStatusContainer: string;
+ noiseCancellationPopout: string;
+ noiseCancellationTooltip: string;
+ krispLogo: string;
+ krispLink: string;
+ micTestButton: string;
+ beta: string;
+ connection: string;
+ voiceUsers: string;
+ actionButtons: string;
+ button: string;
+ buttonColor: string;
+ buttonActive: string;
+ fauxDisabled: string;
+ buttonDeveloperActivityShelf: string;
+ active: string;
+ buttonContents: string;
+ buttonIcon: string;
+ withText: string;
+ voicePanelIntroductionHeader: string;
+ voicePanelIntroductionText: string;
+ voicePanelIntroductionButton: string;
+ voicePanelIntroductionWrapper: string;
+ wrapper: string;
+ viewAsRolesWarning: string;
+ viewAsRolesWarningText: string;
+ viewAsRolesWarningButton: string;
+ disabled: string;
+}
diff --git a/src/equicordplugins/philsPluginLibrary/types/discordModules/components/index.ts b/src/equicordplugins/philsPluginLibrary/types/discordModules/components/index.ts
new file mode 100644
index 00000000..57f4385e
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/types/discordModules/components/index.ts
@@ -0,0 +1,19 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./userSummaryItem";
diff --git a/src/equicordplugins/philsPluginLibrary/types/discordModules/components/userSummaryItem.ts b/src/equicordplugins/philsPluginLibrary/types/discordModules/components/userSummaryItem.ts
new file mode 100644
index 00000000..8e04ab96
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/types/discordModules/components/userSummaryItem.ts
@@ -0,0 +1,35 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { User } from "discord-types/general";
+import type { ComponentType } from "react";
+
+export interface UserSummaryItemProps {
+ guildId?: string;
+ className?: string;
+ users?: User[];
+ renderUser?: (...props: any[]) => any;
+ renderMoreUsers?: (...props: any[]) => any;
+ max?: number;
+ showUserPopout?: boolean;
+ renderIcon?: boolean;
+ showDefaultAvatarsForNullUsers?: boolean;
+ size?: number;
+}
+
+export type UserSummaryItem = ComponentType;
diff --git a/src/equicordplugins/philsPluginLibrary/types/discordModules/index.ts b/src/equicordplugins/philsPluginLibrary/types/discordModules/index.ts
new file mode 100644
index 00000000..af5ab967
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/types/discordModules/index.ts
@@ -0,0 +1,22 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./classes";
+export * from "./components";
+export * from "./modules";
+export * from "./stores";
diff --git a/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/conn.ts b/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/conn.ts
new file mode 100644
index 00000000..d9b969e8
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/conn.ts
@@ -0,0 +1,56 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export interface Conn {
+ destroy: (...args: any[]) => any;
+ setTransportOptions: (options: Record) => any;
+ setSelfMute: (...args: any[]) => any;
+ setSelfDeafen: (...args: any[]) => any;
+ mergeUsers: (...args: any[]) => any;
+ destroyUser: (...args: any[]) => any;
+ setLocalVolume: (...args: any[]) => any;
+ setLocalMute: (...args: any[]) => any;
+ setLocalPan: (...args: any[]) => any;
+ setDisableLocalVideo: (...args: any[]) => any;
+ setMinimumOutputDelay: (...args: any[]) => any;
+ getEncryptionModes: (...args: any[]) => any;
+ configureConnectionRetries: (...args: any[]) => any;
+ setOnSpeakingCallback: (...args: any[]) => any;
+ setOnSpeakingWhileMutedCallback: (...args: any[]) => any;
+ setPingInterval: (...args: any[]) => any;
+ setPingCallback: (...args: any[]) => any;
+ setPingTimeoutCallback: (...args: any[]) => any;
+ setRemoteUserSpeakingStatus: (...args: any[]) => any;
+ setRemoteUserCanHavePriority: (...args: any[]) => any;
+ setOnVideoCallback: (...args: any[]) => any;
+ setVideoBroadcast: (...args: any[]) => any;
+ setDesktopSource: (...args: any[]) => any;
+ setDesktopSourceWithOptions: (...args: any[]) => any;
+ clearDesktopSource: (...args: any[]) => any;
+ setDesktopSourceStatusCallback: (...args: any[]) => any;
+ setOnDesktopSourceEnded: (...args: any[]) => any;
+ setOnSoundshare: (...args: any[]) => any;
+ setOnSoundshareEnded: (...args: any[]) => any;
+ setOnSoundshareFailed: (...args: any[]) => any;
+ setPTTActive: (...args: any[]) => any;
+ getStats: (...args: any[]) => any;
+ getFilteredStats: (...args: any[]) => any;
+ startReplay: (...args: any[]) => any;
+ startSamplesPlayback: (...args: any[]) => any;
+ stopSamplesPlayback: (...args: any[]) => any;
+}
diff --git a/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/connection.ts b/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/connection.ts
new file mode 100644
index 00000000..4809c471
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/connection.ts
@@ -0,0 +1,387 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import TypedEmitter from "typed-emitter";
+
+import { Framerate, Resolution } from "../../../types";
+import { Conn, FramerateReducer, VideoQualityManager } from "./";
+
+export const ConnectionEvent = {
+ SPEAKING: "speaking",
+ MUTE: "mute",
+ NEW_LISTENER: "newListener",
+ DESTORY: "destroy",
+ CONNECTED: "connected",
+ SILENCE: "silence",
+ DESKTOP_SOURCE_END: "desktopsourceend",
+ SOUNDSHARE_ATTACHED: "soundshareattached",
+ SOUNDSHARE_FAILED: "soundsharefailed",
+ SOUNDSHARE_SPEAKING: "soundsharespeaking",
+ SOUNDSHARE_TRACE: "soundsharetrace",
+ INTERACTION_REQUIRED: "interactionrequired",
+ VIDEOHOOK_INITIALIZED: "videohook-initialize",
+ SCREENSHARE_FAILED: "screenshare-finish",
+ NOISE_CANCELLER_ERROR: "noisecancellererror",
+ VOICE_ACTIVITY_DETECTOR_ERROR: "voiceactivitydetectorerror",
+ VIDEO_STATE: "video-state",
+ VIDEO: "video",
+ FIRST_FRAME: "first-frame",
+ ERROR: "error",
+ CONNECTION_STATE_CHANGE: "connectionstatechange",
+ PING: "ping",
+ PING_TIMEOUT: "pingtimeout",
+ OUTBOUND_LOSSRATE: "outboundlossrate",
+ LOCAL_VIDEO_DISABLED: "local-video-disabled",
+ STATS: "stats",
+} as const;
+
+export type ConnectionEvent = typeof ConnectionEvent;
+
+export type ConnectionEvents = {
+ [ConnectionEvent.SPEAKING]: (...args: any[]) => any;
+ [ConnectionEvent.MUTE]: (...args: any[]) => any;
+ [ConnectionEvent.NEW_LISTENER]: (...args: any[]) => any;
+ [ConnectionEvent.DESTORY]: (...args: any[]) => any;
+ [ConnectionEvent.CONNECTED]: (...args: any[]) => any;
+ [ConnectionEvent.SILENCE]: (...args: any[]) => any;
+ [ConnectionEvent.DESKTOP_SOURCE_END]: (...args: any[]) => any;
+ [ConnectionEvent.SOUNDSHARE_ATTACHED]: (...args: any[]) => any;
+ [ConnectionEvent.SOUNDSHARE_FAILED]: (...args: any[]) => any;
+ [ConnectionEvent.SOUNDSHARE_SPEAKING]: (...args: any[]) => any;
+ [ConnectionEvent.SOUNDSHARE_TRACE]: (...args: any[]) => any;
+ [ConnectionEvent.INTERACTION_REQUIRED]: (...args: any[]) => any;
+ [ConnectionEvent.VIDEOHOOK_INITIALIZED]: (...args: any[]) => any;
+ [ConnectionEvent.SCREENSHARE_FAILED]: (...args: any[]) => any;
+ [ConnectionEvent.NOISE_CANCELLER_ERROR]: (...args: any[]) => any;
+ [ConnectionEvent.VOICE_ACTIVITY_DETECTOR_ERROR]: (...args: any[]) => any;
+ [ConnectionEvent.VIDEO_STATE]: (...args: any[]) => any;
+ [ConnectionEvent.VIDEO]: (...args: any[]) => any;
+ [ConnectionEvent.FIRST_FRAME]: (...args: any[]) => any;
+ [ConnectionEvent.ERROR]: (...args: any[]) => any;
+ [ConnectionEvent.CONNECTION_STATE_CHANGE]: (...args: any[]) => any;
+ [ConnectionEvent.PING]: (...args: any[]) => any;
+ [ConnectionEvent.PING_TIMEOUT]: (...args: any[]) => any;
+ [ConnectionEvent.OUTBOUND_LOSSRATE]: (...args: any[]) => any;
+ [ConnectionEvent.LOCAL_VIDEO_DISABLED]: (...args: any[]) => any;
+ [ConnectionEvent.STATS]: (...args: any[]) => any;
+};
+
+export type Connection = TypedEmitter &
+ Connection__ &
+ Connection_ & {
+ streamUserId: string,
+ goLiveSourceIdentifier?: string;
+ emitter: TypedEmitter;
+ mediaEngineConnectionId: string;
+ destroyed: boolean;
+ audioSSRC: number;
+ selfDeaf: boolean;
+ localMutes: LocalMutes;
+ disabledLocalVideos: LocalMutes;
+ localVolumes: LocalVolumes;
+ isActiveOutputSinksEnabled: boolean;
+ activeOutputSinks: LocalMutes;
+ videoSupported: boolean;
+ useElectronVideo: boolean;
+ voiceBitrate: number;
+ remoteSinkWantsMaxFramerate: number;
+ wantsPriority: Set;
+ localSpeakingFlags: LocalSpeakingFlags;
+ videoReady: boolean;
+ videoStreamParameters: VideoStreamParameter[];
+ remoteVideoSinkWants: VideoSinkWants;
+ localVideoSinkWants: VideoSinkWants;
+ connectionState: string;
+ experimentFlags: string[];
+ context: string;
+ ids: Ids;
+ selfMute: boolean;
+ selfVideo: boolean;
+ forceAudioNormal: boolean;
+ forceAudioPriority: boolean;
+ codecs: Codec[];
+ desktopDegradationPreference: number;
+ sourceDesktopDegradationPreference: number;
+ videoDegradationPreference: number;
+ localPans: LocalMutes;
+ remoteAudioSSRCs: LocalMutes;
+ remoteVideoSSRCs: LocalMutes;
+ inputMode: string;
+ vadThreshold: number;
+ vadAutoThreshold: boolean;
+ vadUseKrisp: boolean;
+ vadLeading: number;
+ vadTrailing: number;
+ pttReleaseDelay: number;
+ soundshareActive: boolean;
+ soundshareId?: any;
+ soundshareSentSpeakingEvent: boolean;
+ echoCancellation: boolean;
+ noiseSuppression: boolean;
+ automaticGainControl: boolean;
+ noiseCancellation: boolean;
+ experimentalEncoders: boolean;
+ hardwareH264: boolean;
+ attenuationFactor: number;
+ attenuateWhileSpeakingSelf: boolean;
+ attenuateWhileSpeakingOthers: boolean;
+ qos: boolean;
+ minimumJitterBufferLevel: number;
+ postponeDecodeLevel: number;
+ reconnectInterval: number;
+ keyframeInterval: number;
+ conn: Conn;
+ stats: Stats;
+ framerateReducer: FramerateReducer;
+ videoQualityManager: VideoQualityManager;
+ handleSpeakingNative: (...args: any[]) => any;
+ handleSpeakingFlags: (...args: any[]) => any;
+ handleSpeakingWhileMuted: (...args: any[]) => any;
+ handlePing: (...args: any[]) => any;
+ handlePingTimeout: (...args: any[]) => any;
+ handleVideo: (...args: any[]) => any;
+ handleFirstFrame: (...args: any[]) => any;
+ handleNoInput: (...args: any[]) => any;
+ handleDesktopSourceEnded: (...args: any[]) => any;
+ handleSoundshare: (...args: any[]) => any;
+ handleSoundshareFailed: (...args: any[]) => any;
+ handleSoundshareEnded: (...args: any[]) => any;
+ handleNewListenerNative: (...args: any[]) => any;
+ handleStats: (...args: any[]) => any;
+ __proto__: Connection_;
+ };
+
+interface Connection_ {
+ initialize: (...args: any[]) => any;
+ destroy: (...args: any[]) => any;
+ setCodecs: (audioCodec: string, videoCodec: string, context: string) => any;
+ getStats: (...args: any[]) => any;
+ createUser: (...args: any[]) => any;
+ destroyUser: (...args: any[]) => any;
+ setSelfMute: (...args: any[]) => any;
+ setSelfDeaf: (...args: any[]) => any;
+ setSoundshareSource: (id: number, loopback: boolean) => void;
+ setLocalMute: (...args: any[]) => any;
+ setLocalVideoDisabled: (...args: any[]) => any;
+ setMinimumJitterBufferLevel: (...args: any[]) => any;
+ setPostponeDecodeLevel: (...args: any[]) => any;
+ setClipRecordSsrc: (...args: any[]) => any;
+ getLocalVolume: (...args: any[]) => any;
+ setLocalVolume: (...args: any[]) => any;
+ setLocalPan: (...args: any[]) => any;
+ isAttenuating: (...args: any[]) => any;
+ setAttenuation: (...args: any[]) => any;
+ setCanHavePriority: (...args: any[]) => any;
+ setBitRate: (...args: any[]) => any;
+ setVoiceBitRate: (target: number) => void;
+ setCameraBitRate: (target: number, min: number, max: number) => void;
+ setEchoCancellation: (...args: any[]) => any;
+ setNoiseSuppression: (...args: any[]) => any;
+ setAutomaticGainControl: (...args: any[]) => any;
+ setNoiseCancellation: (...args: any[]) => any;
+ setExperimentalEncoders: (...args: any[]) => any;
+ setHardwareH264: (...args: any[]) => any;
+ setQoS: (...args: any[]) => any;
+ setInputMode: (...args: any[]) => any;
+ setSilenceThreshold: (...args: any[]) => any;
+ setForceAudioInput: (...args: any[]) => any;
+ setSpeakingFlags: (...args: any[]) => any;
+ clearAllSpeaking: (...args: any[]) => any;
+ setEncryption: (...args: any[]) => any;
+ setReconnectInterval: (...args: any[]) => any;
+ setKeyframeInterval: (keyframeInterval: number) => void;
+ setVideoBroadcast: (...args: any[]) => any;
+ setDesktopSource: (
+ source: string | null,
+ options?: DesktopSourceOptions
+ ) => void;
+ clearDesktopSource: (...args: any[]) => any;
+ setDesktopSourceStatusCallback: (...args: any[]) => any;
+ hasDesktopSource: (...args: any[]) => any;
+ setDesktopEncodingOptions: (
+ width: number,
+ height: number,
+ framerate: number
+ ) => void;
+ setSDP: (...args: any[]) => any;
+ setRemoteVideoSinkWants: (...args: any[]) => any;
+ setLocalVideoSinkWants: (...args: any[]) => any;
+ startSamplesPlayback: (...args: any[]) => any;
+ stopSamplesPlayback: (...args: any[]) => any;
+ startSamplesLocalPlayback: (...args: any[]) => any;
+ stopAllSamplesLocalPlayback: (...args: any[]) => any;
+ stopSamplesLocalPlayback: (...args: any[]) => any;
+ updateVideoQualityCore: (...args: any[]) => any;
+ setStreamParameters: (...args: any[]) => any;
+ applyVideoTransportOptions: (...args: any[]) => any;
+ chooseEncryptionMode: (...args: any[]) => any;
+ getUserOptions: (...args: any[]) => any;
+ createInputModeOptions: (...args: any[]) => any;
+ getAttenuationOptions: (...args: any[]) => any;
+ getCodecParams: (...args: any[]) => any;
+ getCodecOptions: (...args: any[]) => any;
+ getConnectionTransportOptions: (...args: any[]) => any;
+ setStream: (...args: any[]) => any;
+ getUserIdBySsrc: (...args: any[]) => any;
+ setRtcLogEphemeralKey: (...args: any[]) => any;
+ setRtcLogMarker: (...args: any[]) => any;
+ __proto__: Connection__;
+}
+
+interface Connection__ {
+ destroy: (...args: any[]) => any;
+ getLocalMute: (...args: any[]) => any;
+ getLocalVideoDisabled: (...args: any[]) => any;
+ setLocalVideoDisabled: (...args: any[]) => any;
+ getHasActiveVideoOutputSink: (...args: any[]) => any;
+ setHasActiveVideoOutputSink: (...args: any[]) => any;
+ getActiveOutputSinkTrackingEnabled: (...args: any[]) => any;
+ setUseElectronVideo: (...args: any[]) => any;
+ setClipRecordSsrc: (...args: any[]) => any;
+ getStreamParameters: (...args: any[]) => any;
+ setExperimentFlag: (...args: any[]) => any;
+ setConnectionState: (...args: any[]) => any;
+ updateVideoQuality: (...args: any[]) => any;
+ applyVideoQualityMode: (...args: any[]) => any;
+ overwriteQualityForTesting: (args: {
+ encode: Resolution & Framerate;
+ capture: Resolution & Framerate;
+ bitrateMin: number;
+ bitrateMax: number;
+ bitrateTarget: number;
+ }) => any;
+ applyQualityConstraints: (...args: any[]) => any;
+ pickProperties: (...args: any[]) => any;
+ initializeStreamParameters: (...args: any[]) => any;
+ getLocalWant: (...args: any[]) => any;
+ emitStats: (...args: any[]) => any;
+ __proto__: TypedEmitter;
+}
+
+export interface Stats {
+ mediaEngineConnectionId: string;
+ transport: Transport;
+ camera?: any;
+ rtp: Rtp;
+}
+
+export interface Rtp {
+ inbound: LocalMutes;
+ outbound: Outbound[];
+}
+
+export interface Outbound {
+ type: string;
+ ssrc: number;
+ sinkWant: string;
+ codec: OutboundCodec;
+ bytesSent: number;
+ packetsSent: number;
+ packetsLost: number;
+ fractionLost: number;
+ audioLevel: number;
+ audioDetected: number;
+ framesCaptured: number;
+ framesRendered: number;
+ noiseCancellerProcessTime: number;
+}
+
+export interface OutboundCodec {
+ id: number;
+ name: string;
+}
+
+export interface Transport {
+ availableOutgoingBitrate: number;
+ ping: number;
+ decryptionFailures: number;
+ routingFailures: number;
+ localAddress: string;
+ pacerDelay: number;
+ receiverReports: any[];
+ receiverBitrateEstimate: number;
+ outboundBitrateEstimate: number;
+ inboundBitrateEstimate: number;
+ bytesSent: number;
+}
+
+export interface Codec {
+ type: string;
+ name: string;
+ priority: number;
+ payloadType: number;
+ rtxPayloadType?: number;
+ encode?: boolean;
+ decode?: boolean;
+}
+
+export interface Ids {
+ userId: string;
+ channelId: string;
+ guildId: string;
+}
+
+export interface VideoSinkWants {
+ any: number;
+}
+
+export interface VideoStreamParameter {
+ type: string;
+ active: boolean;
+ rid: string;
+ ssrc: number;
+ rtxSsrc: number;
+ quality: number;
+ maxBitrate: number;
+ maxFrameRate: number;
+ maxResolution: MaxResolution;
+ maxPixelCount: number;
+}
+
+export type MaxResolution = Partial & {
+ type: "fixed" | "source";
+};
+
+export interface LocalSpeakingFlags {
+ [key: string]: number;
+}
+
+export interface LocalVolumes {
+ [key: string]: number;
+}
+
+export interface LocalMutes {
+ [key: string]: any;
+}
+
+export const HdrCaptureMode = {
+ NEVER: "never",
+ ALWAYS: "always",
+ PERMITTED_DEVICES_ONLY: "permittedDevicesOnly",
+} as const;
+
+export type HdrCaptureMode = typeof HdrCaptureMode;
+
+export interface DesktopSourceOptions extends Partial {
+ fps?: number;
+ useVideoHook?: boolean;
+ useGraphicsCapture?: boolean;
+ useQuartzCapturer?: boolean;
+ allowScreenCaptureKit?: boolean;
+ hdrCaptureMode?: HdrCaptureMode[keyof HdrCaptureMode];
+}
diff --git a/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/framerateReducer.ts b/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/framerateReducer.ts
new file mode 100644
index 00000000..6d659cf7
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/framerateReducer.ts
@@ -0,0 +1,36 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Connection, VideoQualityManager } from "./";
+
+export type FramerateReducer = FramerateReducer_ & {
+ connection: Connection;
+ sinkWants: VideoQualityManager;
+ framerateReductionTimeout?: number;
+ handleSelfMute: (...args: any[]) => any;
+ handleSpeaking: (...args: any[]) => any;
+ __proto__: FramerateReducer_;
+};
+
+export interface FramerateReducer_ {
+ destroy: (...args: any[]) => any;
+ destroyFramerateScaleFactorTimers: (...args: any[]) => any;
+ initialize: (...args: any[]) => any;
+ updateRemoteWantsFramerate: (...args: any[]) => any;
+ userSpeakingChange: (...args: any[]) => any;
+}
diff --git a/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/index.ts b/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/index.ts
new file mode 100644
index 00000000..7f2f06cb
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/index.ts
@@ -0,0 +1,24 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./conn";
+export * from "./connection";
+export * from "./framerateReducer";
+export * from "./mediaEngine";
+export * from "./utils";
+export * from "./videoQualityManager";
diff --git a/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/mediaEngine.ts b/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/mediaEngine.ts
new file mode 100644
index 00000000..2a51eb6b
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/mediaEngine.ts
@@ -0,0 +1,148 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import TypedEmitter from "typed-emitter";
+
+import { Connection } from "./";
+
+export const MediaEngineEvent = {
+ REMOVE_LISTENER: "removeListener",
+ NEW_LISTENER: "newListener",
+ DESTORY: "destroy",
+ CONNECTION: "connection",
+ DEVICE_CHANGE: "devicechange",
+ VOLUME_CHANGE: "volumechange",
+ DESKTOP_SOURCE_END: "desktopsourceend",
+ AUDIO_PERMISSION: "audio-permission",
+ VIDEO_PERMISSION: "video-permission",
+ WATCHDOG_TIMEOUT: "watchdogtimeout",
+ VIDEO_INPUT_INITIALIZED: "video-input-initialized",
+ CONNECTION_STATS: "connection-stats",
+} as const;
+
+export type MediaEngineEvents = {
+ [MediaEngineEvent.REMOVE_LISTENER]: (...args: any[]) => any;
+ [MediaEngineEvent.NEW_LISTENER]: (...args: any[]) => any;
+ [MediaEngineEvent.DESTORY]: (...args: any[]) => any;
+ [MediaEngineEvent.CONNECTION]: (connection: Connection) => void;
+ [MediaEngineEvent.DEVICE_CHANGE]: (...args: any[]) => any;
+ [MediaEngineEvent.VOLUME_CHANGE]: (...args: any[]) => any;
+ [MediaEngineEvent.DESKTOP_SOURCE_END]: (...args: any[]) => any;
+ [MediaEngineEvent.AUDIO_PERMISSION]: (...args: any[]) => any;
+ [MediaEngineEvent.VIDEO_PERMISSION]: (...args: any[]) => any;
+ [MediaEngineEvent.WATCHDOG_TIMEOUT]: (...args: any[]) => any;
+ [MediaEngineEvent.VIDEO_INPUT_INITIALIZED]: (...args: any[]) => any;
+ [MediaEngineEvent.CONNECTION_STATS]: (...args: any[]) => any;
+ [MediaEngineEvent.REMOVE_LISTENER]: (...args: any[]) => any;
+};
+
+export type MediaEngine = TypedEmitter &
+ MediaEngine_ & {
+ Video: (...args: any[]) => any;
+ Camera: (...args: any[]) => any;
+ handleDeviceChange: (...args: any[]) => any;
+ handleVolumeChange: (...args: any[]) => any;
+ handleVoiceActivity: (...args: any[]) => any;
+ handleActiveSinksChange: (...args: any[]) => any;
+ handleNewListener: (...args: any[]) => any;
+ handleRemoveListener: (...args: any[]) => any;
+ handleVideoInputInitialization: (...args: any[]) => any;
+ emitter: TypedEmitter;
+ videoInputDeviceId: string;
+ lastVoiceActivity: number;
+ audioSubsystem: string;
+ audioLayer: string;
+ loopback: boolean;
+ deviceChangeGeneration: number;
+ consecutiveWatchdogFailures: number;
+ codecSurvey?: any;
+ connections: Set;
+ __proto__: MediaEngine_;
+ };
+
+export interface MediaEngine_ {
+ destroy: (...args: any[]) => any;
+ interact: (...args: any[]) => any;
+ supported: (...args: any[]) => any;
+ supports: (...args: any[]) => any;
+ connect: (...args: any[]) => any;
+ shouldConnectionBroadcastVideo: (...args: any[]) => any;
+ eachConnection: (callback: (connection: Connection) => void) => void;
+ enable: (...args: any[]) => any;
+ setInputVolume: (...args: any[]) => any;
+ setOutputVolume: (...args: any[]) => any;
+ getAudioInputDevices: (...args: any[]) => any;
+ setAudioInputDevice: (...args: any[]) => any;
+ getAudioOutputDevices: (...args: any[]) => any;
+ setAudioOutputDevice: (...args: any[]) => any;
+ getVideoInputDevices: (...args: any[]) => any;
+ setVideoInputDevice: (...args: any[]) => any;
+ getSupportedVideoCodecs: (...args: any[]) => any;
+ getCodecCapabilities: (callback: (codecs: string) => void) => void;
+ setDesktopSource: (...args: any[]) => any;
+ setSoundshareSource: (...args: any[]) => any;
+ getDesktopSource: (...args: any[]) => any;
+ getDesktopSources: (...args: any[]) => any;
+ getScreenPreviews: (...args: any[]) => any;
+ setClipBufferLength: (...args: any[]) => any;
+ saveClip: (...args: any[]) => any;
+ updateClipMetadata: (...args: any[]) => any;
+ exportClip: (...args: any[]) => any;
+ getWindowPreviews: (
+ width: number,
+ height: number
+ ) => Promise;
+ setAudioSubsystem: (...args: any[]) => any;
+ getAudioSubsystem: (...args: any[]) => any;
+ getAudioLayer: (...args: any[]) => any;
+ getDebugLogging: (...args: any[]) => any;
+ setDebugLogging: (...args: any[]) => any;
+ setExperimentalAdm: (...args: any[]) => any;
+ setLoopback: (...args: any[]) => any;
+ getLoopback: (...args: any[]) => any;
+ setH264Enabled: (...args: any[]) => any;
+ setAv1Enabled: (...args: any[]) => any;
+ getCodecSurvey: () => Promise;
+ writeAudioDebugState: (...args: any[]) => any;
+ startAecDump: (...args: any[]) => any;
+ stopAecDump: (...args: any[]) => any;
+ setAecDump: (...args: any[]) => any;
+ rankRtcRegions: (...args: any[]) => any;
+ getSoundshareStatus: (...args: any[]) => any;
+ enableSoundshare: (...args: any[]) => any;
+ createReplayConnection: (...args: any[]) => any;
+ setUseDirectVideo: (...args: any[]) => any;
+ setMaxSyncDelayOverride: (...args: any[]) => any;
+ applyMediaFilterSettings: (...args: any[]) => any;
+ startLocalAudioRecording: (...args: any[]) => any;
+ stopLocalAudioRecording: (...args: any[]) => any;
+ watchdogTick: (...args: any[]) => any;
+ __proto__: TypedEmitter;
+}
+
+export interface CodecCapabilities {
+ codec: string;
+ decode: boolean;
+ encode: boolean;
+}
+
+export interface WindowPreview {
+ id: string;
+ url: string;
+ name: string;
+}
diff --git a/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/utils.ts b/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/utils.ts
new file mode 100644
index 00000000..af25211d
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/utils.ts
@@ -0,0 +1,103 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export interface Utils {
+ requireModule: (...args: any[]) => any;
+ ensureModule: (...args: any[]) => any;
+ getCrashReporterMetadata: (...args: any[]) => any;
+ getSetting: (...args: any[]) => any;
+ beforeUnload: (...args: any[]) => any;
+ inputEventRegister: (...args: any[]) => any;
+ inputEventUnregister: (...args: any[]) => any;
+ setOnInputEventCallback: (...args: any[]) => any;
+ setFocused: (...args: any[]) => any;
+ getIdleMilliseconds: (...args: any[]) => any;
+ setObservedGamesCallback: (...args: any[]) => any;
+ setCandidateGamesCallback: (...args: any[]) => any;
+ clearCandidateGamesCallback: (...args: any[]) => any;
+ setGameCandidateOverrides: (...args: any[]) => any;
+ detectPid: (...args: any[]) => any;
+ undetectPid: (...args: any[]) => any;
+ shouldDisplayNotifications: (...args: any[]) => any;
+ getVoiceEngine: (...args: any[]) => any;
+ getDiscordUtils: (...args: any[]) => any;
+ isSystemDarkMode: (...args: any[]) => any;
+ getGameUtils: (...args: any[]) => any;
+ getCloudSync: (...args: any[]) => any;
+ getDispatch: (...args: any[]) => any;
+ setBadge: (...args: any[]) => any;
+ setSystemTrayIcon: (...args: any[]) => any;
+ setThumbarButtons: (...args: any[]) => any;
+ bounceDock: (...args: any[]) => any;
+ setSystemTrayApplications: (...args: any[]) => any;
+ architecture: (...args: any[]) => any;
+ moduleVersions: (...args: any[]) => any;
+ copy: (...args: any[]) => any;
+ copyImage: (...args: any[]) => any;
+ saveImage: (...args: any[]) => any;
+ saveFile: (...args: any[]) => any;
+ canCopyImage: (...args: any[]) => any;
+ cut: (...args: any[]) => any;
+ paste: (...args: any[]) => any;
+ readClipboard: (...args: any[]) => any;
+ on: (...args: any[]) => any;
+ invoke: (...args: any[]) => any;
+ send: (...args: any[]) => any;
+ flashFrame: (...args: any[]) => any;
+ minimize: (...args: any[]) => any;
+ restore: (...args: any[]) => any;
+ maximize: (...args: any[]) => any;
+ focus: (...args: any[]) => any;
+ blur: (...args: any[]) => any;
+ fullscreen: (...args: any[]) => any;
+ close: (...args: any[]) => any;
+ setAlwaysOnTop: (...args: any[]) => any;
+ isAlwaysOnTop: (...args: any[]) => any;
+ purgeMemory: (...args: any[]) => any;
+ updateCrashReporter: (...args: any[]) => any;
+ flushDNSCache: (...args: any[]) => any;
+ supportsFeature: (...args: any[]) => any;
+ getEnableHardwareAcceleration: (...args: any[]) => any;
+ setEnableHardwareAcceleration: (...args: any[]) => any;
+ getGPUDriverVersions: (...args: any[]) => any;
+ setZoomFactor: (...args: any[]) => any;
+ setBackgroundThrottling: (...args: any[]) => any;
+ getPidFromDesktopSource: (...args: any[]) => any;
+ getDesktopSourceFromPid: (...args: any[]) => any;
+ generateSessionFromPid: (...args: any[]) => any;
+ getAudioPid: (...args: any[]) => any;
+ setForegroundProcess: (...args: any[]) => any;
+ getDiscordMemoryUsage: (...args: any[]) => any;
+ showOpenDialog: (...args: any[]) => any;
+ flushStorageData: (...args: any[]) => any;
+ flushCookies: (...args: any[]) => any;
+ setCrashInformation: (...args: any[]) => any;
+ blockDisplaySleep: (...args: any[]) => any;
+ unblockDisplaySleep: (...args: any[]) => any;
+ cleanupDisplaySleep: (...args: any[]) => any;
+ relaunch: (...args: any[]) => any;
+ makeChunkedRequest: (...args: any[]) => any;
+ submitLiveCrashReport: (...args: any[]) => any;
+ crash: (...args: any[]) => any;
+ setApplicationBackgroundColor: (...args: any[]) => any;
+ asyncify: (...args: any[]) => any;
+ releaseChannel: string,
+ canBootstrapNewUpdater: boolean;
+ buildNumber: number;
+ version: number[];
+}
diff --git a/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/videoQualityManager.ts b/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/videoQualityManager.ts
new file mode 100644
index 00000000..fee62934
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/types/discordModules/modules/videoQualityManager.ts
@@ -0,0 +1,81 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { Bitrate, Framerate, Resolution } from "../../";
+import { Connection } from "./";
+
+export type VideoQualityManager = VideoQualityManager_ & {
+ connection: Connection;
+ contextType: string;
+ isMuted: boolean;
+ isStreamContext: boolean;
+ ladder: Ladder;
+ options: Options;
+ qualityOverwrite: QualityOverwrite;
+ __proto__: VideoQualityManager_;
+};
+
+export interface VideoQualityManager_ {
+ applyQualityConstraints: (...args: any[]) => any;
+ getDesktopQuality: (...args: any[]) => any;
+ getQuality: (...args: any[]) => any;
+ getVideoQuality: (...args: any[]) => any;
+ setQuality: (...args: any[]) => any;
+}
+
+export interface QualityOverwrite {
+ bitrateMax?: number;
+ bitrateMin?: number;
+ bitrateTarget?: number;
+ capture?: Resolution & Framerate;
+ encode?: Resolution & Framerate;
+}
+
+export interface Options {
+ videoBudget: VideoBudget;
+ videoCapture: VideoBudget;
+ videoBitrate: VideoBitrate;
+ desktopBitrate: DesktopBitrate;
+ videoBitrateFloor: number;
+}
+
+export type DesktopBitrate = Bitrate;
+
+export type VideoBitrate = Omit;
+
+export type VideoBudget = Resolution & Framerate;
+
+export interface Ladder {
+ pixelBudget: number;
+ ladder: {
+ [key: number]: LadderValue;
+ };
+ orderedLadder: OrderedLadder[];
+}
+
+export interface OrderedLadder extends Resolution, Framerate {
+ pixelCount: number;
+ wantValue: number;
+ budgetPortion: number;
+ mutedFramerate: Framerate;
+}
+
+export interface LadderValue extends Resolution, Framerate {
+ budgetPortion: number;
+ mutedFramerate: Framerate;
+}
diff --git a/src/equicordplugins/philsPluginLibrary/types/discordModules/stores/index.ts b/src/equicordplugins/philsPluginLibrary/types/discordModules/stores/index.ts
new file mode 100644
index 00000000..ead1316a
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/types/discordModules/stores/index.ts
@@ -0,0 +1,19 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./mediaEngineStore";
diff --git a/src/equicordplugins/philsPluginLibrary/types/discordModules/stores/mediaEngineStore.ts b/src/equicordplugins/philsPluginLibrary/types/discordModules/stores/mediaEngineStore.ts
new file mode 100644
index 00000000..97c52fdd
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/types/discordModules/stores/mediaEngineStore.ts
@@ -0,0 +1,119 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { types } from "../../..";
+
+export type MediaEngineStore = MediaEngineStore__ &
+ MediaEngineStore_ & {
+ __proto__: MediaEngineStore_;
+ };
+
+export interface MediaEngineStore_ {
+ initialize: (...args: any[]) => any;
+ supports: (...args: any[]) => any;
+ supportsInApp: (...args: any[]) => any;
+ isSupported: (...args: any[]) => any;
+ isExperimentalEncodersSupported: (...args: any[]) => any;
+ isNoiseSuppressionSupported: (...args: any[]) => any;
+ isNoiseCancellationSupported: (...args: any[]) => any;
+ isNoiseCancellationError: (...args: any[]) => any;
+ isAutomaticGainControlSupported: (...args: any[]) => any;
+ isAdvancedVoiceActivitySupported: (...args: any[]) => any;
+ isAecDumpSupported: (...args: any[]) => any;
+ isSimulcastSupported: (...args: any[]) => any;
+ getAecDump: (...args: any[]) => any;
+ getMediaEngine: () => types.MediaEngine;
+ getVideoComponent: (...args: any[]) => any;
+ getCameraComponent: (...args: any[]) => any;
+ isEnabled: (...args: any[]) => any;
+ isMute: (...args: any[]) => any;
+ isDeaf: (...args: any[]) => any;
+ hasContext: (...args: any[]) => any;
+ isSelfMutedTemporarily: (...args: any[]) => any;
+ isSelfMute: (...args: any[]) => any;
+ isHardwareMute: (...args: any[]) => any;
+ isSelfDeaf: (...args: any[]) => any;
+ isVideoEnabled: (...args: any[]) => any;
+ isVideoAvailable: (...args: any[]) => any;
+ isScreenSharing: (...args: any[]) => any;
+ isSoundSharing: (...args: any[]) => any;
+ isLocalMute: (...args: any[]) => any;
+ supportsDisableLocalVideo: (...args: any[]) => any;
+ isLocalVideoDisabled: (...args: any[]) => any;
+ isLocalVideoAutoDisabled: (...args: any[]) => any;
+ isMediaFilterSettingLoading: (...args: any[]) => any;
+ isNativeAudioPermissionReady: (...args: any[]) => any;
+ getDesktopSource: (...args: any[]) => any;
+ getDesktopSourceContext: (...args: any[]) => any;
+ getLocalPan: (...args: any[]) => any;
+ getLocalVolume: (...args: any[]) => any;
+ getInputVolume: (...args: any[]) => any;
+ getOutputVolume: (...args: any[]) => any;
+ getMode: (...args: any[]) => any;
+ getModeOptions: (...args: any[]) => any;
+ getShortcuts: (...args: any[]) => any;
+ getInputDeviceId: (...args: any[]) => any;
+ getOutputDeviceId: (...args: any[]) => any;
+ getVideoDeviceId: (...args: any[]) => any;
+ getInputDevices: (...args: any[]) => any;
+ getOutputDevices: (...args: any[]) => any;
+ getVideoDevices: (...args: any[]) => any;
+ getEchoCancellation: (...args: any[]) => any;
+ getLoopback: (...args: any[]) => any;
+ getNoiseSuppression: (...args: any[]) => any;
+ getAutomaticGainControl: (...args: any[]) => any;
+ getNoiseCancellation: (...args: any[]) => any;
+ getExperimentalEncoders: (...args: any[]) => any;
+ getHardwareH264: (...args: any[]) => any;
+ getEnableSilenceWarning: (...args: any[]) => any;
+ getDebugLogging: (...args: any[]) => any;
+ getQoS: (...args: any[]) => any;
+ getAttenuation: (...args: any[]) => any;
+ getAttenuateWhileSpeakingSelf: (...args: any[]) => any;
+ getAttenuateWhileSpeakingOthers: (...args: any[]) => any;
+ getAudioSubsystem: (...args: any[]) => any;
+ getSettings: (...args: any[]) => any;
+ getState: (...args: any[]) => any;
+ getInputDetected: (...args: any[]) => any;
+ getNoInputDetectedNotice: (...args: any[]) => any;
+ getPacketDelay: (...args: any[]) => any;
+ setCanHavePriority: (...args: any[]) => any;
+ isInteractionRequired: (...args: any[]) => any;
+ getVideoHook: (...args: any[]) => any;
+ getExperimentalSoundshare: (...args: any[]) => any;
+ supportsExperimentalSoundshare: (...args: any[]) => any;
+ getOpenH264: (...args: any[]) => any;
+ getAv1Enabled: (...args: any[]) => any;
+ getEverSpeakingWhileMuted: (...args: any[]) => any;
+ getSoundshareEnabled: (...args: any[]) => any;
+ supportsEnableSoundshare: (...args: any[]) => any;
+ getVideoStreamParameters: (...args: any[]) => any;
+ __proto__: MediaEngineStore__;
+}
+
+export interface MediaEngineStore__ {
+ registerActionHandlers: (...args: any[]) => any;
+ getName: (...args: any[]) => any;
+ initializeIfNeeded: (...args: any[]) => any;
+ initialize: (...args: any[]) => any;
+ syncWith: (...args: any[]) => any;
+ waitFor: (...args: any[]) => any;
+ emitChange: (...args: any[]) => any;
+ getDispatchToken: (...args: any[]) => any;
+ mustEmitChanges: (...args: any[]) => any;
+}
diff --git a/src/equicordplugins/philsPluginLibrary/types/index.ts b/src/equicordplugins/philsPluginLibrary/types/index.ts
new file mode 100644
index 00000000..d3268dfd
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/types/index.ts
@@ -0,0 +1,21 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./common";
+export * from "./constants";
+export * from "./discordModules";
diff --git a/src/equicordplugins/philsPluginLibrary/utils/index.ts b/src/equicordplugins/philsPluginLibrary/utils/index.ts
new file mode 100644
index 00000000..adc94f67
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/utils/index.ts
@@ -0,0 +1,19 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+export * from "./utils";
diff --git a/src/equicordplugins/philsPluginLibrary/utils/utils.ts b/src/equicordplugins/philsPluginLibrary/utils/utils.ts
new file mode 100644
index 00000000..3568f299
--- /dev/null
+++ b/src/equicordplugins/philsPluginLibrary/utils/utils.ts
@@ -0,0 +1,27 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+import { UserStore } from "@webpack/common";
+import { User } from "discord-types/general";
+
+export const createDummyUser = (props: Partial) => new (UserStore.getCurrentUser().constructor as any)(props);
+export const openURL = (url: string) => VencordNative.native.openExternal(url);
+export const validateNumberInput = (value: string) => parseInt(value) ? parseInt(value) : undefined;
+export const validateTextInputNumber = (value: string) => /^[0-9\b]+$/.test(value) || value === "";
+export const replaceObjectValuesIfExist =
+ (target: Object, replace: Object) => Object.entries(target).forEach(([key, value]) => replace[key] && (target[key] = replace[key]));
diff --git a/src/equicordplugins/showBadgesInChat/index.tsx b/src/equicordplugins/showBadgesInChat/index.tsx
index d74d44dc..58875ddd 100644
--- a/src/equicordplugins/showBadgesInChat/index.tsx
+++ b/src/equicordplugins/showBadgesInChat/index.tsx
@@ -126,13 +126,21 @@ function CheckBadge({ badge, author }: { badge: string; author: User; }): JSX.El
) : null;
case "DiscordNitro":
- return (author?.premiumType ?? 0) > 0 ? (
+ let premiumType: Number | undefined;
+ premiumType = author?.premiumType;
+ // @ts-ignore
+ if (author?._realPremiumType) {
+ // @ts-ignore
+ premiumType = author?._realPremiumType;
+ }
+ // @ts-ignore
+ return premiumType > 0 ? (
);
export const SuncordDevs = /* #__PURE__*/ Object.freeze({