diff --git a/packages/discord-types/src/stores/MediaEngineStore.d.ts b/packages/discord-types/src/stores/MediaEngineStore.d.ts
index d44f5aa58..4ef81639d 100644
--- a/packages/discord-types/src/stores/MediaEngineStore.d.ts
+++ b/packages/discord-types/src/stores/MediaEngineStore.d.ts
@@ -1,10 +1,37 @@
import { FluxStore } from "..";
export class MediaEngineStore extends FluxStore {
- isSelfMute(): boolean;
- isSelfDeaf(): boolean;
+ isAdvancedVoiceActivitySupported(): boolean;
+ isAecDumpSupported(): boolean;
+ isAnyLocalVideoAutoDisabled(): boolean;
+ isAutomaticGainControlSupported(): boolean;
+ isDeaf(): boolean;
+ isEnableHardwareMuteNotice(): boolean;
+ isEnabled(): boolean;
+ isExperimentalEncodersSupported(): boolean;
+ isHardwareMute(): boolean;
+ isInputProfileCustom(): boolean;
+ isInteractionRequired(): boolean;
+ isLocalMute(): boolean;
isLocalMute(userId): boolean;
+ isLocalVideoAutoDisabled(userId): boolean;
isLocalVideoDisabled(userId): boolean;
+ isLocalVideoDisabled(): boolean;
+ isMediaFilterSettingLoading(): boolean;
+ isMute(): boolean;
+ isNativeAudioPermissionReady(): boolean;
+ isNoiseCancellationError(): boolean;
+ isNoiseCancellationSupported(): boolean;
+ isNoiseSuppressionSupported(): boolean;
+ isScreenSharing(): boolean;
+ isSelfDeaf(): boolean;
+ isSelfMute(): boolean;
+ isSelfMutedTemporarily(): boolean;
+ isSimulcastSupported(): boolean;
+ isSoundSharing(): boolean;
+ isSupported(): boolean;
+ isVideoAvailable(): boolean;
+ isVideoEnabled(): boolean;
setAv1Enabled(AV1: boolean): void;
setH265Enabled(H265: boolean): void;
diff --git a/src/equicordplugins/instantScreenshare/index.tsx b/src/equicordplugins/instantScreenshare/index.tsx
index 980973b9c..8a148c4ac 100644
--- a/src/equicordplugins/instantScreenshare/index.tsx
+++ b/src/equicordplugins/instantScreenshare/index.tsx
@@ -4,45 +4,60 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
+import { getUserSettingLazy } from "@api/UserSettings";
import { HeadingSecondary } from "@components/Heading";
import { Paragraph } from "@components/Paragraph";
-import { Devs } from "@utils/constants";
+import { Devs, EquicordDevs } from "@utils/constants";
import definePlugin from "@utils/types";
import { VoiceState } from "@vencord/discord-types";
-import { findByCodeLazy } from "@webpack";
-import { ChannelStore, PermissionsBits, PermissionStore, SelectedChannelStore, UserStore } from "@webpack/common";
+import { findByCodeLazy, findStoreLazy } from "@webpack";
+import { ChannelStore, MediaEngineStore, PermissionsBits, PermissionStore, SelectedChannelStore, UserStore, VoiceActions } from "@webpack/common";
import { getCurrentMedia, settings } from "./utils";
-const startStream = findByCodeLazy('type:"STREAM_START"');
-
let hasStreamed;
+const startStream = findByCodeLazy('type:"STREAM_START"');
+const StreamPreviewSettings = getUserSettingLazy("voiceAndVideo", "disableStreamPreviews")!;
+const ApplicationStreamingSettingsStore = findStoreLazy("ApplicationStreamingSettingsStore");
async function autoStartStream() {
const selected = SelectedChannelStore.getVoiceChannelId();
if (!selected) return;
- const channel = ChannelStore.getChannel(selected);
- if (channel.type === 13 || !PermissionStore.can(PermissionsBits.STREAM, channel)) return;
+ const channel = ChannelStore.getChannel(selected);
+ const isGuildChannel = !channel.isDM() && !channel.isGroupDM();
+
+ if (channel.type === 13 || isGuildChannel && !PermissionStore.can(PermissionsBits.STREAM, channel)) return;
+
+ if (settings.store.autoDeafen && !MediaEngineStore.isSelfDeaf()) {
+ VoiceActions.toggleSelfDeaf();
+ } else if (settings.store.autoMute && !MediaEngineStore.isSelfMute()) {
+ VoiceActions.toggleSelfMute();
+ }
const streamMedia = await getCurrentMedia();
+ const preview = StreamPreviewSettings.getSetting();
+ const { soundshareEnabled } = ApplicationStreamingSettingsStore.getState();
+ let sourceId = streamMedia.id;
+ if (streamMedia.type === "video_device") sourceId = `camera:${streamMedia.id}`;
- startStream(channel.guild_id, selected, {
+ startStream(channel.guild_id ?? null, selected, {
"pid": null,
- "sourceId": streamMedia.id,
+ "sourceId": sourceId,
"sourceName": streamMedia.name,
- "audioSourceId": null,
- "sound": true,
- "previewDisabled": false
+ "audioSourceId": streamMedia.name,
+ "sound": soundshareEnabled,
+ "previewDisabled": preview
});
}
export default definePlugin({
name: "InstantScreenshare",
- description: "Instantly screenshare when joining a voice channel",
- authors: [Devs.HAHALOSAH, Devs.thororen],
+ description: "Instantly screenshare when joining a voice channel with support for desktop sources, windows, and video input devices (cameras, capture cards)",
+ authors: [Devs.HAHALOSAH, Devs.thororen, EquicordDevs.mart],
getCurrentMedia,
settings,
+
settingsAboutComponent: () => (
<>
For Linux
@@ -51,8 +66,19 @@ export default definePlugin({
For X11 it may or may not work :shrug:
+
+ Video Devices
+
+ Supports cameras and capture cards (like Elgato HD60X) when enabled in settings
+
+
+ Regarding Sound & Preview Settings
+
+ We use the settings set and used by discord to decide if stream preview and sound should be enabled or not
+
>
),
+
flux: {
async VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) {
const myId = UserStore.getCurrentUser().id;
diff --git a/src/equicordplugins/instantScreenshare/utils.tsx b/src/equicordplugins/instantScreenshare/utils.tsx
index 853b1c14d..b73bc5ce7 100644
--- a/src/equicordplugins/instantScreenshare/utils.tsx
+++ b/src/equicordplugins/instantScreenshare/utils.tsx
@@ -9,7 +9,7 @@ import { Heading } from "@components/Heading";
import { Paragraph } from "@components/Paragraph";
import { Logger } from "@utils/Logger";
import { OptionType } from "@utils/types";
-import { findByCodeLazy } from "@webpack";
+import { findByCodeLazy, findByPropsLazy } from "@webpack";
import { MediaEngineStore, SearchableSelect, useEffect, useState } from "@webpack/common";
interface PickerProps {
@@ -18,26 +18,52 @@ interface PickerProps {
}
const getDesktopSources = findByCodeLazy("desktop sources");
-
+const configModule = findByPropsLazy("getOutputVolume");
+const log = new Logger("InstantScreenShare");
export const settings = definePluginSettings({
streamMedia: {
type: OptionType.COMPONENT,
component: SettingSection,
},
+ includeVideoDevices: {
+ type: OptionType.BOOLEAN,
+ description: "Include video input devices (cameras, capture cards) in the source list",
+ default: false,
+ },
+ autoMute: {
+ type: OptionType.BOOLEAN,
+ description: "Automatically mute your microphone when joining a voice channel",
+ default: false,
+ },
+ autoDeafen: {
+ type: OptionType.BOOLEAN,
+ description: "Automatically deafen when joining a voice channel (also mutes you)",
+ default: false,
+ },
});
export async function getCurrentMedia() {
const media = MediaEngineStore.getMediaEngine();
- const sources = [
- ...(await getDesktopSources(media, ["screen"], null) ?? []),
- ...(await getDesktopSources(media, ["window", "application"], null) ?? [])
- ];
- const streamMedia = sources.find(screen => screen.id === settings.store.streamMedia);
- console.log(sources);
+ const sources = await getDesktopSources(media, ["screen", "window"], null) ?? [];
+ if (settings.store.includeVideoDevices) {
+ try {
+ const videoDevices = Object.values(configModule.getVideoDevices() || {});
+ const videoSources = videoDevices.map((device: any) => ({
+ id: device.id,
+ name: device.name,
+ type: "video_device"
+ }));
+ sources.push(...videoSources);
+ } catch (e) {
+ new log.warn("Failed to get video devices:", e);
+ }
+ }
+
+ const streamMedia = sources.find(screen => screen.id === settings.store.streamMedia);
if (streamMedia) return streamMedia;
- new Logger("InstantScreenShare").error(`Stream Media "${settings.store.streamMedia}" not found. Resetting to default.`);
+ log.error(`Stream Media "${settings.store.streamMedia}" not found. Resetting to default.`);
settings.store.streamMedia = sources[0];
return sources[0];
@@ -63,7 +89,7 @@ function StreamSimplePicker({ streamMediaSelection, streamMedia }: PickerProps)
}
function ScreenSetting() {
- const { streamMedia } = settings.use(["streamMedia"]);
+ const { streamMedia, includeVideoDevices } = settings.use(["streamMedia", "includeVideoDevices"]);
const media = MediaEngineStore.getMediaEngine();
const [streamMediaSelection, setStreamMediaSelection] = useState([]);
const [loading, setLoading] = useState(true);
@@ -72,10 +98,21 @@ function ScreenSetting() {
let active = true;
async function fetchMedia() {
setLoading(true);
- const sources = [
- ...(await getDesktopSources(media, ["screen"], null) ?? []),
- ...(await getDesktopSources(media, ["window", "application"], null) ?? [])
- ];
+ const sources = await getDesktopSources(media, ["screen", "window"], null) ?? [];
+
+ if (includeVideoDevices) {
+ try {
+ const videoDevices = Object.values(configModule.getVideoDevices() || {});
+ const videoSources = videoDevices.map((device: any) => ({
+ id: device.id,
+ name: device.name,
+ type: "video_device"
+ }));
+ sources.push(...videoSources);
+ } catch (e) {
+ log.warn("Failed to get video devices:", e);
+ }
+ }
if (active) {
setStreamMediaSelection(sources);
@@ -84,7 +121,7 @@ function ScreenSetting() {
}
fetchMedia();
return () => { active = false; };
- }, []);
+ }, [includeVideoDevices]);
if (loading) return Loading media sources...;
if (!streamMediaSelection.length) return No Media found.;
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index 29995ce27..d52c83bc3 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -1174,6 +1174,10 @@ export const EquicordDevs = Object.freeze({
name: "ZcraftElite",
id: 926788037785047050n
},
+ mart: {
+ name: "mja00",
+ id: 108698087769260032n
+ },
} satisfies Record);
// iife so #__PURE__ works correctly