Skip to main content

obws/requests/
mod.rs

1//! All requests that can be send to the API.
2
3#![expect(clippy::ref_option_ref)]
4
5use bitflags::bitflags;
6use derive_more::Debug;
7use serde::{Serialize, ser::SerializeStruct};
8use serde_with::skip_serializing_none;
9
10use crate::common::FlagsDebug;
11
12pub mod canvases;
13pub mod config;
14pub mod custom;
15pub mod filters;
16pub mod general;
17pub mod hotkeys;
18pub(crate) mod ids;
19pub mod inputs;
20pub(crate) mod media_inputs;
21pub(crate) mod outputs;
22pub mod profiles;
23pub(crate) mod recording;
24pub(crate) mod replay_buffer;
25pub(crate) mod scene_collections;
26pub mod scene_items;
27pub mod scenes;
28pub mod sources;
29pub(crate) mod streaming;
30pub(crate) mod transitions;
31pub mod ui;
32pub(crate) mod virtual_cam;
33
34pub(crate) enum ClientRequest<'a> {
35    /// Response to [`crate::responses::ServerMessage::Hello`] message, should contain
36    /// authentication string if authentication is required, along with Pub-sub subscriptions and
37    /// other session parameters.
38    Identify(Identify),
39    /// Sent at any time after initial identification to update the provided session parameters.
40    Reidentify(Reidentify),
41    /// Client is making a request to obs-websocket. For example get current scene, create source.
42    Request(Request<'a>),
43    /// Client is making a batch of requests for obs-websocket. Requests are processed serially
44    /// (in order) by the server.
45    #[allow(dead_code)]
46    RequestBatch(RequestBatch<'a>),
47}
48
49impl Serialize for ClientRequest<'_> {
50    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
51    where
52        S: serde::Serializer,
53    {
54        #[derive(Clone, Copy, Serialize)]
55        #[serde(into = "u8")]
56        #[repr(u8)]
57        enum OpCode {
58            /// The message sent by a newly connected client to obs-websocket in response to a
59            /// `Hello`.
60            Identify = 1,
61            /// The message sent by an already-identified client to update identification
62            /// parameters.
63            Reidentify = 3,
64            /// The message sent by a client to obs-websocket to perform a request.
65            Request = 6,
66            /// The message sent by a client to obs-websocket to perform a batch of requests.
67            RequestBatch = 8,
68        }
69
70        impl From<OpCode> for u8 {
71            fn from(value: OpCode) -> Self {
72                value as Self
73            }
74        }
75
76        fn write_state<S>(serializer: S, op: OpCode, d: &impl Serialize) -> Result<S::Ok, S::Error>
77        where
78            S: serde::Serializer,
79        {
80            let mut state = serializer.serialize_struct("ClientRequest", 2)?;
81            state.serialize_field("op", &op)?;
82            state.serialize_field("d", d)?;
83            state.end()
84        }
85
86        match self {
87            Self::Identify(value) => write_state(serializer, OpCode::Identify, value),
88            Self::Reidentify(value) => write_state(serializer, OpCode::Reidentify, value),
89            Self::Request(value) => write_state(serializer, OpCode::Request, value),
90            Self::RequestBatch(value) => write_state(serializer, OpCode::RequestBatch, value),
91        }
92    }
93}
94
95/// Response to [`crate::responses::ServerMessage::Hello`] message, should contain
96/// authentication string if authentication is required, along with Pub-sub subscriptions and other
97/// session parameters.
98#[skip_serializing_none]
99#[derive(Serialize)]
100pub(crate) struct Identify {
101    /// Version number that the client would like the obs-websocket server to use.
102    #[serde(rename = "rpcVersion")]
103    pub rpc_version: u32,
104    #[serde(rename = "authentication")]
105    pub authentication: Option<String>,
106    /// Bit mask of event subscription items to subscribe to events and event categories at will.
107    /// By default, all event categories are subscribed, except for events marked as high
108    /// volume. High volume events must be explicitly subscribed to.
109    #[serde(rename = "eventSubscriptions")]
110    pub event_subscriptions: Option<EventSubscription>,
111}
112
113/// Sent at any time after initial identification to update the provided session parameters.
114#[skip_serializing_none]
115#[derive(Serialize)]
116pub(crate) struct Reidentify {
117    #[serde(rename = "eventSubscriptions")]
118    pub event_subscriptions: Option<EventSubscription>,
119}
120
121/// Client is making a request to obs-websocket. For example get current scene, create source.
122#[derive(Serialize)]
123pub(crate) struct Request<'a> {
124    #[serde(rename = "requestId")]
125    pub request_id: &'a str,
126    #[serde(flatten)]
127    pub ty: RequestType<'a>,
128}
129
130/// Client is making a batch of requests for obs-websocket. Requests are processed serially
131/// (in order) by the server.
132#[skip_serializing_none]
133#[derive(Serialize)]
134pub(crate) struct RequestBatch<'a> {
135    #[serde(rename = "requestId")]
136    pub request_id: &'a str,
137    /// When true, the processing of requests will be halted on first failure. Returns only the
138    /// processed requests in
139    /// [`crate::responses::ServerMessage::RequestBatchResponse`].
140    #[serde(rename = "haltOnFailure")]
141    pub halt_on_failure: Option<bool>,
142    #[serde(rename = "requests")]
143    pub requests: &'a [RequestType<'a>],
144    #[serde(rename = "executionType")]
145    pub execution_type: Option<ExecutionType>,
146}
147
148/// Bit flags for possible event subscriptions, that can be enabled when connecting to the OBS
149/// instance.
150#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Debug, Serialize)]
151#[cfg_attr(
152    feature = "test-integration",
153    derive(serde::Deserialize),
154    serde(from = "u32")
155)]
156#[serde(into = "u32")]
157pub struct EventSubscription(#[debug("{:?}", FlagsDebug(self))] u32);
158
159bitflags! {
160    /// Bit flags for possible event subscriptions, that can be enabled when connecting to the OBS
161    /// instance.
162    impl EventSubscription: u32 {
163        /// Subscription value used to disable all events.
164        const NONE = 0;
165        /// Subscription value to receive events in the `General` category.
166        const GENERAL = 1 << 0;
167        /// Subscription value to receive events in the `Config` category.
168        const CONFIG = 1 << 1;
169        /// Subscription value to receive events in the `Scenes` category.
170        const SCENES = 1 << 2;
171        /// Subscription value to receive events in the `Inputs` category.
172        const INPUTS = 1 << 3;
173        /// Subscription value to receive events in the `Transitions` category.
174        const TRANSITIONS = 1 << 4;
175        /// Subscription value to receive events in the `Filters` category.
176        const FILTERS = 1 << 5;
177        /// Subscription value to receive events in the `Outputs` category.
178        const OUTPUTS = 1 << 6;
179        /// Subscription value to receive events in the `SceneItems` category.
180        const SCENE_ITEMS = 1 << 7;
181        /// Subscription value to receive events in the `MediaInputs` category.
182        const MEDIA_INPUTS = 1 << 8;
183        /// Subscription value to receive the [`VendorEvent`] event.
184        ///
185        /// [`VendorEvent`]: crate::events::Event::VendorEvent
186        const VENDORS = 1 << 9;
187        /// Subscription value to receive events in the `Ui` category.
188        const UI = 1 << 10;
189        /// Subscription value to receive events in the `Canvases` category.
190        const CANVASES = 1 << 11;
191
192        /// Helper to receive all non-high-volume events.
193        const ALL = Self::GENERAL.bits()
194            | Self::CONFIG.bits()
195            | Self::SCENES.bits()
196            | Self::INPUTS.bits()
197            | Self::TRANSITIONS.bits()
198            | Self::FILTERS.bits()
199            | Self::OUTPUTS.bits()
200            | Self::SCENE_ITEMS.bits()
201            | Self::MEDIA_INPUTS.bits()
202            | Self::VENDORS.bits()
203            | Self::UI.bits()
204            | Self::CANVASES.bits();
205
206        /// Subscription value to receive the [`InputVolumeMeters`] high-volume event.
207        ///
208        /// [`InputVolumeMeters`]: crate::events::Event::InputVolumeMeters
209        const INPUT_VOLUME_METERS = 1 << 16;
210        /// Subscription value to receive the [`InputActiveStateChanged`] high-volume event.
211        ///
212        /// [`InputActiveStateChanged`]: crate::events::Event::InputActiveStateChanged
213        const INPUT_ACTIVE_STATE_CHANGED = 1 << 17;
214        /// Subscription value to receive the [`InputShowStateChanged`] high-volume event.
215        ///
216        /// [`InputShowStateChanged`]: crate::events::Event::InputShowStateChanged
217        const INPUT_SHOW_STATE_CHANGED = 1 << 18;
218        /// Subscription value to receive the [`SceneItemTransformChanged`] high-volume event.
219        ///
220        /// [`SceneItemTransformChanged`]: crate::events::Event::SceneItemTransformChanged
221        const SCENE_ITEM_TRANSFORM_CHANGED = 1 << 19;
222
223    }
224}
225
226impl From<EventSubscription> for u32 {
227    fn from(value: EventSubscription) -> Self {
228        value.bits()
229    }
230}
231
232#[cfg(feature = "test-integration")]
233impl From<u32> for EventSubscription {
234    fn from(value: u32) -> Self {
235        Self::from_bits_truncate(value)
236    }
237}
238
239#[allow(dead_code)]
240#[derive(Clone, Copy, Serialize)]
241#[serde(into = "i8")]
242#[repr(i8)]
243pub(crate) enum ExecutionType {
244    /// Not a request batch.
245    None = -1,
246    /// A request batch which processes all requests serially, as fast as possible.
247    SerialRealtime = 0,
248    /// A request batch type which processes all requests serially, in sync with the graphics
249    /// thread. Designed to provide high accuracy for animations.
250    SerialFrame = 1,
251    /// A request batch type which processes all requests using all available threads in the thread
252    /// pool.
253    Parallel = 2,
254}
255
256impl From<ExecutionType> for i8 {
257    fn from(value: ExecutionType) -> Self {
258        value as Self
259    }
260}
261
262pub(crate) enum RequestType<'a> {
263    Canvases(self::canvases::Request),
264    Config(self::config::Request<'a>),
265    Filters(self::filters::Request<'a>),
266    General(self::general::Request<'a>),
267    Hotkeys(self::hotkeys::Request<'a>),
268    Inputs(self::inputs::Request<'a>),
269    MediaInputs(self::media_inputs::Request<'a>),
270    Outputs(self::outputs::Request<'a>),
271    Profiles(self::profiles::Request<'a>),
272    Recording(self::recording::Request<'a>),
273    ReplayBuffer(self::replay_buffer::Request),
274    SceneCollections(self::scene_collections::Request<'a>),
275    SceneItems(self::scene_items::Request<'a>),
276    Scenes(self::scenes::Request<'a>),
277    Sources(self::sources::Request<'a>),
278    Streaming(self::streaming::Request<'a>),
279    Transitions(self::transitions::Request<'a>),
280    Ui(self::ui::Request<'a>),
281    VirtualCam(self::virtual_cam::Request),
282}
283
284impl Serialize for RequestType<'_> {
285    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
286    where
287        S: serde::Serializer,
288    {
289        match self {
290            Self::Canvases(req) => req.serialize(serializer),
291            Self::Config(req) => req.serialize(serializer),
292            Self::Filters(req) => req.serialize(serializer),
293            Self::General(req) => req.serialize(serializer),
294            Self::Hotkeys(req) => req.serialize(serializer),
295            Self::Inputs(req) => req.serialize(serializer),
296            Self::MediaInputs(req) => req.serialize(serializer),
297            Self::Outputs(req) => req.serialize(serializer),
298            Self::Profiles(req) => req.serialize(serializer),
299            Self::Recording(req) => req.serialize(serializer),
300            Self::ReplayBuffer(req) => req.serialize(serializer),
301            Self::SceneCollections(req) => req.serialize(serializer),
302            Self::SceneItems(req) => req.serialize(serializer),
303            Self::Scenes(req) => req.serialize(serializer),
304            Self::Sources(req) => req.serialize(serializer),
305            Self::Streaming(req) => req.serialize(serializer),
306            Self::Transitions(req) => req.serialize(serializer),
307            Self::Ui(req) => req.serialize(serializer),
308            Self::VirtualCam(req) => req.serialize(serializer),
309        }
310    }
311}