diff --git a/ui/src/components/WebRTCVideo.tsx b/ui/src/components/WebRTCVideo.tsx index 833c0b5..e28f571 100644 --- a/ui/src/components/WebRTCVideo.tsx +++ b/ui/src/components/WebRTCVideo.tsx @@ -46,6 +46,11 @@ export default function WebRTCVideo() { clientHeight: videoClientHeight, } = useVideoStore(); + // Video enhancement settings + const videoSaturation = useSettingsStore(state => state.videoSaturation); + const videoBrightness = useSettingsStore(state => state.videoBrightness); + const videoContrast = useSettingsStore(state => state.videoContrast); + // HID related states const keyboardLedStateSyncAvailable = useHidStore(state => state.keyboardLedStateSyncAvailable); const keyboardLedSync = useSettingsStore(state => state.keyboardLedSync); @@ -674,6 +679,9 @@ export default function WebRTCVideo() { playsInline disablePictureInPicture controlsList="nofullscreen" + style={{ + filter: `saturate(${videoSaturation}) brightness(${videoBrightness}) contrast(${videoContrast})`, + }} className={cx( "max-h-full min-h-[384px] max-w-full min-w-[512px] bg-black/50 object-contain transition-all duration-1000", { diff --git a/ui/src/hooks/stores.ts b/ui/src/hooks/stores.ts index 9475316..6bc7e17 100644 --- a/ui/src/hooks/stores.ts +++ b/ui/src/hooks/stores.ts @@ -319,6 +319,14 @@ interface SettingsState { showPressedKeys: boolean; setShowPressedKeys: (show: boolean) => void; + + // Video enhancement settings + videoSaturation: number; + setVideoSaturation: (value: number) => void; + videoBrightness: number; + setVideoBrightness: (value: number) => void; + videoContrast: number; + setVideoContrast: (value: number) => void; } export const useSettingsStore = create( @@ -362,6 +370,14 @@ export const useSettingsStore = create( showPressedKeys: true, setShowPressedKeys: show => set({ showPressedKeys: show }), + + // Video enhancement settings with default values (1.0 = normal) + videoSaturation: 1.0, + setVideoSaturation: value => set({ videoSaturation: value }), + videoBrightness: 1.0, + setVideoBrightness: value => set({ videoBrightness: value }), + videoContrast: 1.0, + setVideoContrast: value => set({ videoContrast: value }), }), { name: "settings", diff --git a/ui/src/routes/devices.$id.settings.video.tsx b/ui/src/routes/devices.$id.settings.video.tsx index 07472a3..9e888ab 100644 --- a/ui/src/routes/devices.$id.settings.video.tsx +++ b/ui/src/routes/devices.$id.settings.video.tsx @@ -4,6 +4,7 @@ import { Button } from "@/components/Button"; import { TextAreaWithLabel } from "@/components/TextArea"; import { useJsonRpc } from "@/hooks/useJsonRpc"; import { SettingsPageHeader } from "@components/SettingsPageheader"; +import { useSettingsStore } from "@/hooks/stores"; import notifications from "../notifications"; import { SelectMenuBasic } from "../components/SelectMenuBasic"; @@ -45,6 +46,14 @@ export default function SettingsVideoRoute() { const [customEdidValue, setCustomEdidValue] = useState(null); const [edid, setEdid] = useState(null); + // Video enhancement settings from store + const videoSaturation = useSettingsStore(state => state.videoSaturation); + const setVideoSaturation = useSettingsStore(state => state.setVideoSaturation); + const videoBrightness = useSettingsStore(state => state.videoBrightness); + const setVideoBrightness = useSettingsStore(state => state.setVideoBrightness); + const videoContrast = useSettingsStore(state => state.videoContrast); + const setVideoContrast = useSettingsStore(state => state.setVideoContrast); + useEffect(() => { send("getStreamQualityFactor", {}, resp => { if ("error" in resp) return; @@ -126,6 +135,73 @@ export default function SettingsVideoRoute() { onChange={e => handleStreamQualityChange(e.target.value)} /> + + {/* Video Enhancement Settings */} + + +
+ + setVideoSaturation(parseFloat(e.target.value))} + className="w-32 h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700" + /> + + + + setVideoBrightness(parseFloat(e.target.value))} + className="w-32 h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700" + /> + + + + setVideoContrast(parseFloat(e.target.value))} + className="w-32 h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700" + /> + + +
+
+
+