add equibopstreamfixes (#925)

* add equibopstreamfixes

* .equibop

* Update index.tsx

* fakenitro conflict

* add from fakenitro

---------

Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com>
This commit is contained in:
creations 2026-03-24 20:33:29 -04:00 • committed by GitHub
parent 2221bfade6
commit 55c7a1b233
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -0,0 +1,298 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { isPluginEnabled } from "@api/PluginManager";
import { definePluginSettings } from "@api/Settings";
import fakeNitro from "@plugins/fakeNitro";
import { EquicordDevs } from "@utils/constants";
import { localStorage } from "@utils/localStorage";
import definePlugin, { OptionType } from "@utils/types";
interface StreamQualityOpts {
bitrateMin?: number;
bitrateMax?: number;
bitrateTarget?: number;
capture?: {
width?: number;
height?: number;
framerate?: number;
pixelCount?: number;
};
encode?: {
width?: number;
height?: number;
framerate?: number;
pixelCount?: number;
};
}
const settings = definePluginSettings({
unlockQualityOptions: {
type: OptionType.BOOLEAN,
description: "Unlock stream quality options regardless of Nitro status",
default: true,
restartNeeded: true,
},
removeResolutionCap: {
type: OptionType.BOOLEAN,
description: "Allow resolutions above 720p at 60fps",
default: true,
restartNeeded: true,
},
forceEncoderSettings: {
type: OptionType.BOOLEAN,
description: "Force encoder to use configured resolution/fps",
default: true,
restartNeeded: true,
},
preventDownscale: {
type: OptionType.BOOLEAN,
description: "Prevent Discord from downscaling stream resolution",
default: true,
restartNeeded: true,
},
preventLqDowngrade: {
type: OptionType.BOOLEAN,
description: "Prevent LQ simulcast downgrade during bandwidth dips",
default: true,
restartNeeded: true,
},
keyframeInterval: {
type: OptionType.NUMBER,
description: "Keyframe interval in ms (0 = encoder default, 5000 = every 5s)",
default: 5000,
restartNeeded: true,
},
minBitrate: {
type: OptionType.NUMBER,
description: "Minimum encoder bitrate in kbps",
default: 500,
restartNeeded: true,
},
raiseBitrateCaps: {
type: OptionType.BOOLEAN,
description: "Raise default desktop bitrate caps (600kbps target → 10Mbps, 3.5Mbps max → 40Mbps)",
default: true,
restartNeeded: true,
},
preventFramerateReduction: {
type: OptionType.BOOLEAN,
description: "Prevent Discord from reducing stream framerate when not speaking",
default: true,
restartNeeded: true,
},
bitsPerPixelPct: {
type: OptionType.NUMBER,
description: "Bits per pixel percentage for target bitrate (8 = 0.08 bpp, 12 = 0.12bpp)",
default: 8,
restartNeeded: false,
},
});
export default definePlugin({
name: "EquibopStreamFixes",
description: "Tries to fix stream quality on Equibop by patching Discord's encoder and quality restrictions.",
authors: [EquicordDevs.creations],
settings,
patches: [
{
find: "this.getDefaultGoliveQuality()",
replacement: [
// override default stream quality with configured bitrate/resolution/fps
{
match: /(this\.goliveMaxQuality)=(this\.getDefaultGoliveQuality\(\))/,
replace: "$1=$self.patchStreamQuality($2)",
},
// inject configured quality when stream settings change mid-stream
{
match: /setGoliveQuality\((\i)\)\{/,
replace: "setGoliveQuality($1){$1=$self.patchGoliveArgs($1);",
},
// override encoder min/max bitrate limits
{
match:
/(\i)\.encodingVideoMinBitRate=\i\.bitrateMin,\i\.encodingVideoMaxBitRate=\i\.bitrateMax/,
replace: "$1.encodingVideoMinBitRate=$self.getMinBitrate(),$1.encodingVideoMaxBitRate=$self.getBitrateMax()",
},
],
},
// unlock stream quality options for all users regardless of nitro
{
find: "canUseCustomStickersEverywhere:",
replacement: [
{
match: /canStreamQuality:\i,/,
replace: "canStreamQuality:()=>true,",
},
{
match: /canUseHighVideoUploadQuality:\i,/,
replace: "canUseHighVideoUploadQuality:()=>true,",
},
],
predicate: () => settings.store.unlockQualityOptions && !isPluginEnabled(fakeNitro.name),
},
// remove guild premium tier restriction from stream fps options
{
find: "#{intl::STREAM_FPS_OPTION}",
replacement: {
match: /guildPremiumTier:\i\.\i\.TIER_\d,?/g,
replace: "",
},
predicate: () => settings.store.unlockQualityOptions && !isPluginEnabled(fakeNitro.name),
},
// allow resolutions above 720p at 60fps
{
find: ".RESOLUTION_720&&",
replacement: {
match: /\i===\i\.\i\.RESOLUTION_720&&\i!==\i\.\i\.FPS_60/,
replace: "false",
},
predicate: () => settings.store.removeResolutionCap,
},
// force encoder width/height/fps to configured values
{
find: "}setDesktopEncodingOptions(",
replacement: [
{
match: /setDesktopEncodingOptions\((\i),(\i),(\i)\)\{/,
replace: "$&$1=$self.getWidth();$2=$self.getHeight();$3=$self.getFps();",
predicate: () => settings.store.forceEncoderSettings,
},
// set keyframe interval for periodic quality refresh
{
match: /keyframeInterval=0/,
replace: "keyframeInterval=$self.getKeyframeInterval()",
},
],
},
// prevent LQ simulcast downgrade during bandwidth dips
{
find: "Attempting to downgrade to LQ simulcast",
replacement: {
match: /"LQ"===\i&&!\i&&\i&&\(/,
replace: "false&&(",
},
predicate: () => settings.store.preventLqDowngrade,
},
// raise default desktop bitrate caps at the source
{
find: "desktopBitrate:{",
replacement: {
match: /desktopBitrate:\{min:5e5,max:35e5,target:6e5\}/,
replace: "desktopBitrate:{min:5e5,max:4e7,target:1e7}",
},
predicate: () => settings.store.raiseBitrateCaps,
},
// prevent framerate reduction when not speaking
{
find: "Reduced framerate after",
replacement: {
match: /this\.framerateReductionTimeout=setTimeout/,
replace: "this.framerateReductionTimeout=void 0&&setTimeout",
},
predicate: () => settings.store.preventFramerateReduction,
},
],
getStreamConfig() {
let quality: { frameRate?: string; resolution?: string; } | undefined;
try {
const state = JSON.parse(localStorage.getItem("EquibopState") ?? "{}");
quality = state.screenshareQuality;
} catch { }
const framerate = Number(quality?.frameRate ?? 30);
const height = Number(quality?.resolution ?? 720);
const width = Math.round(height * (16 / 9));
const pixelCount = width * height;
const pixelRate = pixelCount * framerate;
const bpp = (settings.store.bitsPerPixelPct ?? 8) / 100;
const bitrateTarget = Math.min(20000000, Math.max(1500000, Math.round(pixelRate * bpp)),);
const bitrateMax = Math.min(40000000, Math.max(bitrateTarget * 2, 8000000));
return { framerate, height, width, pixelCount, bitrateTarget, bitrateMax };
},
getWidth() {
return this.getStreamConfig().width;
},
getHeight() {
return this.getStreamConfig().height;
},
getFps() {
return this.getStreamConfig().framerate;
},
getBitrateMax() {
return this.getStreamConfig().bitrateMax;
},
coerceResolution(res: { height?: number; width?: number; }) {
if (!res) return res;
const config = this.getStreamConfig();
return {
...res,
height: Math.max(res.height ?? 0, config.height),
width: Math.max(res.width ?? 0, config.width),
};
},
coerceFps(fps: number | undefined) {
const config = this.getStreamConfig();
return typeof fps === "number"
? Math.max(fps, config.framerate)
: config.framerate;
},
getMinBitrate() {
return (settings.store.minBitrate ?? 500) * 1000;
},
getKeyframeInterval() {
return settings.store.keyframeInterval ?? 5000;
},
patchGoliveArgs(opts: StreamQualityOpts) {
const config = this.getStreamConfig();
return {
...opts,
bitrateTarget: config.bitrateTarget,
capture: {
...opts.capture,
width: config.width,
height: config.height,
framerate: config.framerate,
},
encode: {
...opts.encode,
width: config.width,
height: config.height,
framerate: config.framerate,
pixelCount: config.pixelCount,
},
};
},
patchStreamQuality(opts: StreamQualityOpts) {
const config = this.getStreamConfig();
Object.assign(opts, {
bitrateMin: this.getMinBitrate(),
bitrateMax: config.bitrateMax,
bitrateTarget: config.bitrateTarget,
});
if (opts?.encode) {
Object.assign(opts.encode, {
framerate: config.framerate,
width: config.width,
height: config.height,
pixelCount: config.pixelCount,
});
}
Object.assign((opts.capture ??= {}), {
framerate: config.framerate,
width: config.width,
height: config.height,
pixelCount: config.pixelCount,
});
return opts;
},
});