diff --git a/CHANGELOG.md b/CHANGELOG.md index fa337ab..aaf1948 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,9 +12,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- New features from obs-websocket v5.3.0 +- New features from obs-websocket [v5.3.0](https://github.com/obsproject/obs-websocket/releases/tag/5.3.0) - New `set_record_directory` command, that allows to modify the output directory for recordings. - New `NotReady` status code, that signals the obs-websocket server is not ready yet to accept any commands. +- New features from obs-websocket [v5.4.0](https://github.com/obsproject/obs-websocket/releases/tag/5.4.0) + - **BREAKING CHANGE:** Support for UUIDs to identify sources, inputs, scenes and transitions. This includes various breaking changes around those fields to make it impossible to misuse them in requests (basically using an enum to only use the name _or_ UUID in a request but not both). + - New `list_kinds` command for filters. + - New `source` command for scene items. + - New `InputSettingsChanged` and `SourceFilterSettingsChanged` event. ## [0.11.5] - 2023-09-04 diff --git a/Cargo.toml b/Cargo.toml index 214b3af..4b35b88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ time = "0.3.31" tokio = { version = "1.35.1", features = ["net", "rt", "sync", "time"] } tokio-tungstenite = "0.21.0" tracing = "0.1.40" +uuid = { version = "1.7.0", features = ["serde"] } [dev-dependencies] anyhow = "1.0.79" diff --git a/examples/iter_scenes.rs b/examples/iter_scenes.rs index 4c02eb3..586e596 100644 --- a/examples/iter_scenes.rs +++ b/examples/iter_scenes.rs @@ -17,7 +17,7 @@ async fn main() -> Result<()> { for scene in scene_list.scenes.iter().cycle() { client .scenes() - .set_current_program_scene(&scene.name) + .set_current_program_scene(&*scene.name) .await?; tokio::time::sleep(Duration::from_secs(1)).await; } diff --git a/examples/screenshot.rs b/examples/screenshot.rs index d5a7a4d..fadc09b 100644 --- a/examples/screenshot.rs +++ b/examples/screenshot.rs @@ -17,7 +17,7 @@ async fn main() -> Result<()> { let screenshot = client .sources() .take_screenshot(TakeScreenshot { - source: "OBWS-TEST-Scene", + source: "OBWS-TEST-Scene".into(), width: None, height: None, compression_quality: None, diff --git a/src/client/filters.rs b/src/client/filters.rs index 0a3a3fb..6abded9 100644 --- a/src/client/filters.rs +++ b/src/client/filters.rs @@ -2,9 +2,12 @@ use serde::{de::DeserializeOwned, Serialize}; use super::Client; use crate::{ - requests::filters::{ - Create, CreateInternal, Request, SetEnabled, SetIndex, SetName, SetSettings, - SetSettingsInternal, + requests::{ + filters::{ + Create, CreateInternal, Request, SetEnabled, SetIndex, SetName, SetSettings, + SetSettingsInternal, + }, + sources::SourceId, }, responses::filters as responses, Error, Result, @@ -16,9 +19,18 @@ pub struct Filters<'a> { } impl<'a> Filters<'a> { + /// Gets an array of all available source filter kinds. + #[doc(alias = "GetSourceFilterKindList")] + pub async fn list_kinds(&self) -> Result> { + self.client + .send_message::<_, responses::FilterKinds>(Request::KindList) + .await + .map(|fk| fk.kinds) + } + /// Gets an array of all of a source's filters. #[doc(alias = "GetSourceFilterList")] - pub async fn list(&self, source: &str) -> Result> { + pub async fn list(&self, source: SourceId<'_>) -> Result> { self.client .send_message::<_, responses::Filters>(Request::List { source }) .await @@ -61,7 +73,7 @@ impl<'a> Filters<'a> { /// Removes a filter from a source. #[doc(alias = "RemoveSourceFilter")] - pub async fn remove(&self, source: &str, filter: &str) -> Result<()> { + pub async fn remove(&self, source: SourceId<'_>, filter: &str) -> Result<()> { self.client .send_message(Request::Remove { source, filter }) .await @@ -75,7 +87,7 @@ impl<'a> Filters<'a> { /// Gets the info for a specific source filter. #[doc(alias = "GetSourceFilter")] - pub async fn get(&self, source: &str, filter: &str) -> Result { + pub async fn get(&self, source: SourceId<'_>, filter: &str) -> Result { self.client .send_message(Request::Get { source, filter }) .await diff --git a/src/client/hotkeys.rs b/src/client/hotkeys.rs index 328c32d..c6f8947 100644 --- a/src/client/hotkeys.rs +++ b/src/client/hotkeys.rs @@ -22,9 +22,9 @@ impl<'a> Hotkeys<'a> { /// Triggers a hotkey using its name. See [`Self::list`]. #[doc(alias = "TriggerHotkeyByName")] - pub async fn trigger_by_name(&self, name: &str) -> Result<()> { + pub async fn trigger_by_name(&self, name: &str, context: Option<&str>) -> Result<()> { self.client - .send_message(Request::TriggerByName { name }) + .send_message(Request::TriggerByName { name, context }) .await } diff --git a/src/client/inputs.rs b/src/client/inputs.rs index 370c823..4799a82 100644 --- a/src/client/inputs.rs +++ b/src/client/inputs.rs @@ -5,7 +5,7 @@ use super::Client; use crate::{ common::MonitorType, requests::inputs::{ - Create, CreateInputInternal, Request, SetSettings, SetSettingsInternal, Volume, + Create, CreateInputInternal, InputId, Request, SetSettings, SetSettingsInternal, Volume, }, responses::inputs as responses, Error, Result, @@ -60,11 +60,11 @@ impl<'a> Inputs<'a> { /// **Note:** Does not include defaults. To create the entire settings object, overlay input /// settings over the default input settings provided by [`Inputs::default_settings`]. #[doc(alias = "GetInputSettings")] - pub async fn settings(&self, name: &str) -> Result> + pub async fn settings(&self, input: InputId<'_>) -> Result> where T: DeserializeOwned, { - self.client.send_message(Request::Settings { name }).await + self.client.send_message(Request::Settings { input }).await } /// Sets the settings of an input. @@ -85,60 +85,60 @@ impl<'a> Inputs<'a> { /// Gets the audio mute state of an input. #[doc(alias = "GetInputMute")] - pub async fn muted(&self, name: &str) -> Result { + pub async fn muted(&self, input: InputId<'_>) -> Result { self.client - .send_message::<_, responses::InputMuted>(Request::Muted { name }) + .send_message::<_, responses::InputMuted>(Request::Muted { input }) .await .map(|im| im.muted) } /// Sets the audio mute state of an input. #[doc(alias = "SetInputMute")] - pub async fn set_muted(&self, name: &str, muted: bool) -> Result<()> { + pub async fn set_muted(&self, input: InputId<'_>, muted: bool) -> Result<()> { self.client - .send_message(Request::SetMuted { name, muted }) + .send_message(Request::SetMuted { input, muted }) .await } /// Toggles the audio mute state of an input. #[doc(alias = "ToggleInputMute")] - pub async fn toggle_mute(&self, name: &str) -> Result { + pub async fn toggle_mute(&self, input: InputId<'_>) -> Result { self.client - .send_message::<_, responses::InputMuted>(Request::ToggleMute { name }) + .send_message::<_, responses::InputMuted>(Request::ToggleMute { input }) .await .map(|im| im.muted) } /// Gets the current volume setting of an input. #[doc(alias = "GetInputVolume")] - pub async fn volume(&self, name: &str) -> Result { - self.client.send_message(Request::Volume { name }).await + pub async fn volume(&self, input: InputId<'_>) -> Result { + self.client.send_message(Request::Volume { input }).await } /// Sets the volume setting of an input. #[doc(alias = "SetInputVolume")] - pub async fn set_volume(&self, name: &str, volume: Volume) -> Result<()> { + pub async fn set_volume(&self, input: InputId<'_>, volume: Volume) -> Result<()> { self.client - .send_message(Request::SetVolume { name, volume }) + .send_message(Request::SetVolume { input, volume }) .await } /// Sets the name of an input (rename). #[doc(alias = "SetInputName")] - pub async fn set_name(&self, name: &str, new: &str) -> Result<()> { + pub async fn set_name(&self, input: InputId<'_>, new: &str) -> Result<()> { self.client - .send_message(Request::SetName { name, new }) + .send_message(Request::SetName { input, new }) .await } /// Creates a new input, adding it as a scene item to the specified scene. #[doc(alias = "CreateInput")] - pub async fn create(&self, input: Create<'_, T>) -> Result + pub async fn create(&self, input: Create<'_, T>) -> Result where T: Serialize, { self.client - .send_message::<_, responses::SceneItemId>(Request::Create(CreateInputInternal { + .send_message(Request::Create(CreateInputInternal { scene: input.scene, input: input.input, kind: input.kind, @@ -151,31 +151,30 @@ impl<'a> Inputs<'a> { enabled: input.enabled, })) .await - .map(|sii| sii.scene_item_id) } /// Removes an existing input. /// /// **Note:** Will immediately remove all associated scene items. #[doc(alias = "RemoveInput")] - pub async fn remove(&self, name: &str) -> Result<()> { - self.client.send_message(Request::Remove { name }).await + pub async fn remove(&self, input: InputId<'_>) -> Result<()> { + self.client.send_message(Request::Remove { input }).await } /// Gets the audio balance of an input. #[doc(alias = "GetInputAudioBalance")] - pub async fn audio_balance(&self, name: &str) -> Result { + pub async fn audio_balance(&self, input: InputId<'_>) -> Result { self.client - .send_message::<_, responses::AudioBalance>(Request::AudioBalance { name }) + .send_message::<_, responses::AudioBalance>(Request::AudioBalance { input }) .await .map(|ab| ab.audio_balance) } /// Sets the audio balance of an input. #[doc(alias = "SetInputAudioBalance")] - pub async fn set_audio_balance(&self, name: &str, balance: f32) -> Result<()> { + pub async fn set_audio_balance(&self, input: InputId<'_>, balance: f32) -> Result<()> { self.client - .send_message(Request::SetAudioBalance { name, balance }) + .send_message(Request::SetAudioBalance { input, balance }) .await } @@ -183,26 +182,26 @@ impl<'a> Inputs<'a> { /// /// **Note:** The audio sync offset can be negative too! #[doc(alias = "GetInputAudioSyncOffset")] - pub async fn audio_sync_offset(&self, name: &str) -> Result { + pub async fn audio_sync_offset(&self, input: InputId<'_>) -> Result { self.client - .send_message::<_, responses::AudioSyncOffset>(Request::AudioSyncOffset { name }) + .send_message::<_, responses::AudioSyncOffset>(Request::AudioSyncOffset { input }) .await .map(|aso| aso.input_audio_sync_offset) } /// Sets the audio sync offset of an input. #[doc(alias = "SetInputAudioSyncOffset")] - pub async fn set_audio_sync_offset(&self, name: &str, offset: Duration) -> Result<()> { + pub async fn set_audio_sync_offset(&self, input: InputId<'_>, offset: Duration) -> Result<()> { self.client - .send_message(Request::SetAudioSyncOffset { name, offset }) + .send_message(Request::SetAudioSyncOffset { input, offset }) .await } /// Gets the audio monitor type of input. #[doc(alias = "GetInputAudioMonitorType")] - pub async fn audio_monitor_type(&self, name: &str) -> Result { + pub async fn audio_monitor_type(&self, input: InputId<'_>) -> Result { self.client - .send_message::<_, responses::AudioMonitorType>(Request::AudioMonitorType { name }) + .send_message::<_, responses::AudioMonitorType>(Request::AudioMonitorType { input }) .await .map(|amt| amt.monitor_type) } @@ -211,28 +210,35 @@ impl<'a> Inputs<'a> { #[doc(alias = "SetInputAudioMonitorType")] pub async fn set_audio_monitor_type( &self, - name: &str, + input: InputId<'_>, monitor_type: MonitorType, ) -> Result<()> { self.client - .send_message(Request::SetAudioMonitorType { name, monitor_type }) + .send_message(Request::SetAudioMonitorType { + input, + monitor_type, + }) .await } /// Gets the enable state of all audio tracks of an input. #[doc(alias = "GetInputAudioTracks")] - pub async fn audio_tracks(&self, name: &str) -> Result<[bool; 6]> { + pub async fn audio_tracks(&self, input: InputId<'_>) -> Result<[bool; 6]> { self.client - .send_message::<_, responses::AudioTracks>(Request::AudioTracks { name }) + .send_message::<_, responses::AudioTracks>(Request::AudioTracks { input }) .await .map(|at| at.audio_tracks) } /// Sets the enable state of audio tracks of an input. #[doc(alias = "SetInputAudioTracks")] - pub async fn set_audio_tracks(&self, name: &str, tracks: [Option; 6]) -> Result<()> { + pub async fn set_audio_tracks( + &self, + input: InputId<'_>, + tracks: [Option; 6], + ) -> Result<()> { self.client - .send_message(Request::SetAudioTracks { name, tracks }) + .send_message(Request::SetAudioTracks { input, tracks }) .await } @@ -243,7 +249,7 @@ impl<'a> Inputs<'a> { #[doc(alias = "GetInputPropertiesListPropertyItems")] pub async fn properties_list_property_items( &self, - input: &str, + input: InputId<'_>, property: &str, ) -> Result> { self.client @@ -261,7 +267,7 @@ impl<'a> Inputs<'a> { /// cannot be accessed in any other way. For example, browser sources, where there is a refresh /// button. #[doc(alias = "PressInputPropertiesButton")] - pub async fn press_properties_button(&self, input: &str, property: &str) -> Result<()> { + pub async fn press_properties_button(&self, input: InputId<'_>, property: &str) -> Result<()> { self.client .send_message(Request::PressPropertiesButton { input, property }) .await diff --git a/src/client/media_inputs.rs b/src/client/media_inputs.rs index 4ae1261..644f453 100644 --- a/src/client/media_inputs.rs +++ b/src/client/media_inputs.rs @@ -2,7 +2,9 @@ use time::Duration; use super::Client; use crate::{ - common::MediaAction, requests::media_inputs::Request, responses::media_inputs as responses, + common::MediaAction, + requests::{inputs::InputId, media_inputs::Request}, + responses::media_inputs as responses, Result, }; @@ -14,7 +16,7 @@ pub struct MediaInputs<'a> { impl<'a> MediaInputs<'a> { /// Gets the status of a media input. #[doc(alias = "GetMediaInputStatus")] - pub async fn status(&self, input: &str) -> Result { + pub async fn status(&self, input: InputId<'_>) -> Result { self.client.send_message(Request::Status { input }).await } @@ -22,7 +24,7 @@ impl<'a> MediaInputs<'a> { /// /// This request does not perform bounds checking of the cursor position. #[doc(alias = "SetMediaInputCursor")] - pub async fn set_cursor(&self, input: &str, cursor: Duration) -> Result<()> { + pub async fn set_cursor(&self, input: InputId<'_>, cursor: Duration) -> Result<()> { self.client .send_message(Request::SetCursor { input, cursor }) .await @@ -32,7 +34,7 @@ impl<'a> MediaInputs<'a> { /// /// This request does not perform bounds checking of the cursor position. #[doc(alias = "OffsetMediaInputCursor")] - pub async fn offset_cursor(&self, input: &str, offset: Duration) -> Result<()> { + pub async fn offset_cursor(&self, input: InputId<'_>, offset: Duration) -> Result<()> { self.client .send_message(Request::OffsetCursor { input, offset }) .await @@ -40,7 +42,7 @@ impl<'a> MediaInputs<'a> { /// Triggers an action on a media input. #[doc(alias = "TriggerMediaInputAction")] - pub async fn trigger_action(&self, input: &str, action: MediaAction) -> Result<()> { + pub async fn trigger_action(&self, input: InputId<'_>, action: MediaAction) -> Result<()> { self.client .send_message(Request::TriggerAction { input, action }) .await diff --git a/src/client/mod.rs b/src/client/mod.rs index 633e3a1..67a1b89 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -137,7 +137,7 @@ where const OBS_STUDIO_VERSION: Comparator = Comparator { op: Op::GreaterEq, - major: 28, + major: 30, minor: None, patch: None, pre: Prerelease::EMPTY, diff --git a/src/client/scene_items.rs b/src/client/scene_items.rs index 1cc2947..893602e 100644 --- a/src/client/scene_items.rs +++ b/src/client/scene_items.rs @@ -3,11 +3,14 @@ use serde::{de::DeserializeOwned, Serialize}; use super::Client; use crate::{ common::BlendMode, - requests::scene_items::{ - CreateSceneItem, Duplicate, Id, Request, SetBlendMode, SetEnabled, SetIndex, SetLocked, - SetPrivateSettings, SetPrivateSettingsInternal, SetTransform, + requests::{ + scene_items::{ + CreateSceneItem, Duplicate, Id, Request, SetBlendMode, SetEnabled, SetIndex, SetLocked, + SetPrivateSettings, SetPrivateSettingsInternal, SetTransform, Source, + }, + scenes::SceneId, }, - responses::scene_items as responses, + responses::{scene_items as responses, sources as source_responses}, Error, Result, }; @@ -19,7 +22,7 @@ pub struct SceneItems<'a> { impl<'a> SceneItems<'a> { /// Gets a list of all scene items in a scene. #[doc(alias = "GetSceneItemList")] - pub async fn list(&self, scene: &str) -> Result> { + pub async fn list(&self, scene: SceneId<'_>) -> Result> { self.client .send_message::<_, responses::SceneItemList>(Request::List { scene }) .await @@ -30,7 +33,7 @@ impl<'a> SceneItems<'a> { /// /// Using groups at all in OBS is discouraged, as they are very broken under the hood. #[doc(alias = "GetGroupSceneItemList")] - pub async fn list_group(&self, scene: &str) -> Result> { + pub async fn list_group(&self, scene: SceneId<'_>) -> Result> { self.client .send_message::<_, responses::SceneItemList>(Request::ListGroup { scene }) .await @@ -46,6 +49,12 @@ impl<'a> SceneItems<'a> { .map(|sii| sii.id) } + /// Gets the source associated with a scene item. + #[doc(alias = "GetSceneItemSource")] + pub async fn source(&self, get: Source<'_>) -> Result { + self.client.send_message(Request::Source(get)).await + } + /// Creates a new scene item using a source. #[doc(alias = "CreateSceneItem")] pub async fn create(&self, create: CreateSceneItem<'_>) -> Result { @@ -57,7 +66,7 @@ impl<'a> SceneItems<'a> { /// Removes a scene item from a scene. #[doc(alias = "RemoveSceneItem")] - pub async fn remove(&self, scene: &str, item_id: i64) -> Result<()> { + pub async fn remove(&self, scene: SceneId<'_>, item_id: i64) -> Result<()> { self.client .send_message(Request::Remove { scene, item_id }) .await @@ -76,7 +85,7 @@ impl<'a> SceneItems<'a> { #[doc(alias = "GetSceneItemTransform")] pub async fn transform( &self, - scene: &str, + scene: SceneId<'_>, item_id: i64, ) -> Result { self.client @@ -98,7 +107,7 @@ impl<'a> SceneItems<'a> { /// Gets the enable state of a scene item. #[doc(alias = "GetSceneItemEnabled")] - pub async fn enabled(&self, scene: &str, item_id: i64) -> Result { + pub async fn enabled(&self, scene: SceneId<'_>, item_id: i64) -> Result { self.client .send_message::<_, responses::SceneItemEnabled>(Request::Enabled { scene, item_id }) .await @@ -113,7 +122,7 @@ impl<'a> SceneItems<'a> { /// Gets the lock state of a scene item. #[doc(alias = "GetSceneItemLocked")] - pub async fn locked(&self, scene: &str, item_id: i64) -> Result { + pub async fn locked(&self, scene: SceneId<'_>, item_id: i64) -> Result { self.client .send_message::<_, responses::SceneItemLocked>(Request::Locked { scene, item_id }) .await @@ -130,7 +139,7 @@ impl<'a> SceneItems<'a> { /// /// An index of 0 is at the bottom of the source list in the UI. #[doc(alias = "GetSceneItemIndex")] - pub async fn index(&self, scene: &str, item_id: i64) -> Result { + pub async fn index(&self, scene: SceneId<'_>, item_id: i64) -> Result { self.client .send_message::<_, responses::SceneItemIndex>(Request::Index { scene, item_id }) .await @@ -145,7 +154,7 @@ impl<'a> SceneItems<'a> { /// Gets the blend mode of a scene item. #[doc(alias = "GetSceneItemBlendMode")] - pub async fn blend_mode(&self, scene: &str, item_id: i64) -> Result { + pub async fn blend_mode(&self, scene: SceneId<'_>, item_id: i64) -> Result { self.client .send_message::<_, responses::SceneItemBlendMode>(Request::BlendMode { scene, item_id }) .await @@ -160,7 +169,7 @@ impl<'a> SceneItems<'a> { /// Gets private scene item settings. #[doc(alias = "GetSceneItemPrivateSettings")] - pub async fn private_settings(&self, scene: &str, item_id: i64) -> Result + pub async fn private_settings(&self, scene: SceneId<'_>, item_id: i64) -> Result where T: DeserializeOwned, { diff --git a/src/client/scenes.rs b/src/client/scenes.rs index 31699ff..96f3c26 100644 --- a/src/client/scenes.rs +++ b/src/client/scenes.rs @@ -1,6 +1,8 @@ +use uuid::Uuid; + use super::Client; use crate::{ - requests::scenes::{Request, SetTransitionOverride}, + requests::scenes::{Request, SceneId, SetTransitionOverride}, responses::scenes as responses, Result, }; @@ -31,18 +33,17 @@ impl<'a> Scenes<'a> { /// Gets the current program scene. #[doc(alias = "GetCurrentProgramScene")] - pub async fn current_program_scene(&self) -> Result { - self.client - .send_message::<_, responses::CurrentProgramScene>(Request::CurrentProgramScene) - .await - .map(|cps| cps.current_program_scene_name) + pub async fn current_program_scene(&self) -> Result { + self.client.send_message(Request::CurrentProgramScene).await } /// Sets the current program scene. #[doc(alias = "SetCurrentProgramScene")] - pub async fn set_current_program_scene(&self, scene: &str) -> Result<()> { + pub async fn set_current_program_scene(&self, scene: impl Into>) -> Result<()> { self.client - .send_message(Request::SetCurrentProgramScene { scene }) + .send_message(Request::SetCurrentProgramScene { + scene: scene.into(), + }) .await } @@ -50,26 +51,25 @@ impl<'a> Scenes<'a> { /// /// Only available when studio mode is enabled. #[doc(alias = "GetCurrentPreviewScene")] - pub async fn current_preview_scene(&self) -> Result { - self.client - .send_message::<_, responses::CurrentPreviewScene>(Request::CurrentPreviewScene) - .await - .map(|cps| cps.current_preview_scene_name) + pub async fn current_preview_scene(&self) -> Result { + self.client.send_message(Request::CurrentPreviewScene).await } /// Sets the current preview scene. /// /// Only available when studio mode is enabled. #[doc(alias = "SetCurrentPreviewScene")] - pub async fn set_current_preview_scene(&self, scene: &str) -> Result<()> { + pub async fn set_current_preview_scene(&self, scene: impl Into>) -> Result<()> { self.client - .send_message(Request::SetCurrentPreviewScene { scene }) + .send_message(Request::SetCurrentPreviewScene { + scene: scene.into(), + }) .await } /// Sets the name of a scene (rename). #[doc(alias = "SetSceneName")] - pub async fn set_name(&self, scene: &str, new_name: &str) -> Result<()> { + pub async fn set_name(&self, scene: SceneId<'_>, new_name: &str) -> Result<()> { self.client .send_message(Request::SetName { scene, new_name }) .await @@ -77,13 +77,16 @@ impl<'a> Scenes<'a> { /// Creates a new scene in OBS. #[doc(alias = "CreateScene")] - pub async fn create(&self, name: &str) -> Result<()> { - self.client.send_message(Request::Create { name }).await + pub async fn create(&self, name: &str) -> Result { + self.client + .send_message::<_, responses::CreateScene>(Request::Create { name }) + .await + .map(|cs| cs.uuid) } /// Removes a scene from OBS. #[doc(alias = "RemoveScene")] - pub async fn remove(&self, scene: &str) -> Result<()> { + pub async fn remove(&self, scene: SceneId<'_>) -> Result<()> { self.client.send_message(Request::Remove { scene }).await } @@ -91,7 +94,7 @@ impl<'a> Scenes<'a> { #[doc(alias = "GetSceneSceneTransitionOverride")] pub async fn transition_override( &self, - scene: &str, + scene: SceneId<'_>, ) -> Result { self.client .send_message(Request::TransitionOverride { scene }) diff --git a/src/client/sources.rs b/src/client/sources.rs index ac47c01..1a25ff3 100644 --- a/src/client/sources.rs +++ b/src/client/sources.rs @@ -1,6 +1,6 @@ use super::Client; use crate::{ - requests::sources::{Request, SaveScreenshot, TakeScreenshot}, + requests::sources::{Request, SaveScreenshot, SourceId, TakeScreenshot}, responses::sources as responses, Result, }; @@ -13,8 +13,8 @@ pub struct Sources<'a> { impl<'a> Sources<'a> { /// Gets the active and show state of a source. #[doc(alias = "GetSourceActive")] - pub async fn active(&self, name: &str) -> Result { - self.client.send_message(Request::Active { name }).await + pub async fn active(&self, source: SourceId<'_>) -> Result { + self.client.send_message(Request::Active { source }).await } /// Gets a Base64-encoded screenshot of a source. diff --git a/src/client/ui.rs b/src/client/ui.rs index 5cb8b4f..a41c21a 100644 --- a/src/client/ui.rs +++ b/src/client/ui.rs @@ -1,8 +1,11 @@ use super::Client; use crate::{ - requests::ui::{ - OpenSourceProjector, OpenSourceProjectorInternal, OpenVideoMixProjector, - OpenVideoMixProjectorInternal, Request, + requests::{ + inputs::InputId, + ui::{ + OpenSourceProjector, OpenSourceProjectorInternal, OpenVideoMixProjector, + OpenVideoMixProjectorInternal, Request, + }, }, responses::ui as responses, Result, @@ -35,7 +38,7 @@ impl<'a> Ui<'a> { /// Opens the properties dialog of an input. #[doc(alias = "OpenInputPropertiesDialog")] - pub async fn open_properties_dialog(&self, input: &str) -> Result<()> { + pub async fn open_properties_dialog(&self, input: InputId<'_>) -> Result<()> { self.client .send_message(Request::OpenInputPropertiesDialog { input }) .await @@ -43,7 +46,7 @@ impl<'a> Ui<'a> { /// Opens the filters dialog of an input. #[doc(alias = "OpenInputFiltersDialog")] - pub async fn open_filters_dialog(&self, input: &str) -> Result<()> { + pub async fn open_filters_dialog(&self, input: InputId<'_>) -> Result<()> { self.client .send_message(Request::OpenInputFiltersDialog { input }) .await @@ -51,7 +54,7 @@ impl<'a> Ui<'a> { /// Opens the interact dialog of an input. #[doc(alias = "OpenInputInteractDialog")] - pub async fn open_interact_dialog(&self, input: &str) -> Result<()> { + pub async fn open_interact_dialog(&self, input: InputId<'_>) -> Result<()> { self.client .send_message(Request::OpenInputInteractDialog { input }) .await diff --git a/src/events.rs b/src/events.rs index b38d482..999ce25 100644 --- a/src/events.rs +++ b/src/events.rs @@ -4,10 +4,17 @@ use std::{collections::BTreeMap, path::PathBuf}; use serde::{Deserialize, Serialize}; use time::Duration; +use uuid::Uuid; use crate::{ common::{MediaAction, MonitorType}, - responses::{filters::SourceFilter, scene_items::SceneItemTransform}, + responses::{ + filters::SourceFilter, + ids::{SceneId, TransitionId}, + inputs::InputId, + scene_items::SceneItemTransform, + sources::SourceId, + }, }; /// All possible event types that can occur while the user interacts with OBS. @@ -128,6 +135,18 @@ pub enum Event { #[serde(rename = "filterName")] new_name: String, }, + /// A source filter's settings have changed (been updated). + SourceFilterSettingsChanged { + /// Name of the source the filter is on. + #[serde(rename = "sourceName")] + source: String, + /// Name of the filter. + #[serde(rename = "filterName")] + filter: String, + /// New settings object of the filter. + #[serde(rename = "filterSettings")] + settings: serde_json::Value, + }, // -------------------------------- // General // -------------------------------- @@ -159,9 +178,9 @@ pub enum Event { // -------------------------------- /// An input has been created. InputCreated { - /// Name of the input. - #[serde(rename = "inputName")] - name: String, + /// Identifier of the input. + #[serde(flatten)] + id: InputId, /// The kind of the input. #[serde(rename = "inputKind")] kind: String, @@ -177,12 +196,15 @@ pub enum Event { }, /// An input has been removed. InputRemoved { - /// Name of the input. - #[serde(rename = "inputName")] - name: String, + /// Identifier of the input. + #[serde(flatten)] + id: InputId, }, /// The name of an input has changed. InputNameChanged { + /// UUID of the input. + #[serde(rename = "inputUuid")] + uuid: Uuid, /// Old name of the input. #[serde(rename = "oldInputName")] old_name: String, @@ -190,13 +212,26 @@ pub enum Event { #[serde(rename = "inputName")] new_name: String, }, + /// An input's settings have changed (been updated). + /// + /// Note: On some inputs, changing values in the properties dialog will cause an immediate + /// update. Pressing the _Cancel_ button will revert the settings, resulting in another event + /// being fired. + InputSettingsChanged { + /// Identifier of the input. + #[serde(flatten)] + id: InputId, + /// New settings object of the input. + #[serde(rename = "inputSettings")] + settings: serde_json::Value, + }, /// An input's active state has changed. /// /// When an input is active, it means it's being shown by the program feed. InputActiveStateChanged { - /// Name of the input. - #[serde(rename = "inputName")] - name: String, + /// Identifier of the input. + #[serde(flatten)] + id: InputId, /// Whether the input is active. #[serde(rename = "videoActive")] active: bool, @@ -205,27 +240,27 @@ pub enum Event { /// /// When an input is showing, it means it's being shown by the preview or a dialog. InputShowStateChanged { - /// Name of the input. - #[serde(rename = "inputName")] - name: String, + /// Identifier of the input. + #[serde(flatten)] + id: InputId, /// Whether the input is showing. #[serde(rename = "videoShowing")] showing: bool, }, /// An input's mute state has changed. InputMuteStateChanged { - /// Name of the input. - #[serde(rename = "inputName")] - name: String, + /// Identifier of the input. + #[serde(flatten)] + id: InputId, /// Whether the input is muted. #[serde(rename = "inputMuted")] muted: bool, }, /// An input's volume level has changed. InputVolumeChanged { - /// Name of the input. - #[serde(rename = "inputName")] - name: String, + /// Identifier of the input. + #[serde(flatten)] + id: InputId, /// New volume level multiplier. #[serde(rename = "inputVolumeMul")] mul: f64, @@ -235,18 +270,18 @@ pub enum Event { }, /// The audio balance value of an input has changed. InputAudioBalanceChanged { - /// Name of the affected input. - #[serde(rename = "inputName")] - name: String, + /// Identifier of the input. + #[serde(flatten)] + id: InputId, /// New audio balance value of the input. #[serde(rename = "inputAudioBalance")] audio_balance: f64, }, /// The sync offset of an input has changed. InputAudioSyncOffsetChanged { - /// Name of the input. - #[serde(rename = "inputName")] - name: String, + /// Identifier of the input. + #[serde(flatten)] + id: InputId, /// New sync offset in milliseconds. #[serde( rename = "inputAudioSyncOffset", @@ -256,18 +291,18 @@ pub enum Event { }, /// The audio tracks of an input have changed. InputAudioTracksChanged { - /// Name of the input. - #[serde(rename = "inputName")] - name: String, + /// Identifier of the input. + #[serde(flatten)] + id: InputId, /// Object of audio tracks along with their associated enable states. #[serde(rename = "inputAudioTracks")] tracks: BTreeMap, }, /// The monitor type of an input has changed. InputAudioMonitorTypeChanged { - /// Name of the input. - #[serde(rename = "inputName")] - name: String, + /// Identifier of the input. + #[serde(flatten)] + id: InputId, /// New monitor type of the input. #[serde(rename = "monitorType")] monitor_type: MonitorType, @@ -283,21 +318,21 @@ pub enum Event { // -------------------------------- /// A media input has started playing. MediaInputPlaybackStarted { - /// Name of the input. - #[serde(rename = "inputName")] - name: String, + /// Identifier of the input. + #[serde(flatten)] + id: InputId, }, /// A media input has finished playing. MediaInputPlaybackEnded { - /// Name of the input. - #[serde(rename = "inputName")] - name: String, + /// Identifier of the input. + #[serde(flatten)] + id: InputId, }, /// An action has been performed on an input. MediaInputActionTriggered { - /// Name of the input. - #[serde(rename = "inputName")] - name: String, + /// Identifier of the input. + #[serde(flatten)] + id: InputId, /// Action performed on the input. #[serde(rename = "mediaAction")] media_action: MediaAction, @@ -355,12 +390,12 @@ pub enum Event { // -------------------------------- /// A scene item has been created. SceneItemCreated { - /// Name of the scene the item was added to. - #[serde(rename = "sceneName")] - scene: String, - /// Name of the underlying source (input/scene). - #[serde(rename = "sourceName")] - source: String, + /// Identifier of the scene the item was added to. + #[serde(flatten)] + scene: SceneId, + /// Identifier of the underlying source (input/scene). + #[serde(flatten)] + source: SourceId, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] item_id: u64, @@ -372,30 +407,30 @@ pub enum Event { /// /// This event is not emitted when the scene the item is in is removed. SceneItemRemoved { - /// Name of the scene the item was removed from. - #[serde(rename = "sceneName")] - scene: String, - /// Name of the underlying source (input/scene). - #[serde(rename = "sourceName")] - source: String, + /// Identifier of the scene the item was removed from. + #[serde(flatten)] + scene: SceneId, + /// Identifier of the underlying source (input/scene). + #[serde(flatten)] + source: SourceId, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] item_id: u64, }, /// A scene's item list has been re-indexed. SceneItemListReindexed { - /// Name of the scene. - #[serde(rename = "sceneName")] - scene: String, + /// Identifier of the scene. + #[serde(flatten)] + scene: SceneId, /// Array of scene item objects. #[serde(rename = "sceneItems")] items: Vec, }, /// A scene item's enable state has changed. SceneItemEnableStateChanged { - /// Name of the scene the item is in. - #[serde(rename = "sceneName")] - scene: String, + /// Identifier of the scene the item is in. + #[serde(flatten)] + scene: SceneId, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] item_id: u64, @@ -405,9 +440,9 @@ pub enum Event { }, /// A scene item's lock state has changed. SceneItemLockStateChanged { - /// Name of the scene the item is in. - #[serde(rename = "sceneName")] - scene: String, + /// Identifier of the scene the item is in. + #[serde(flatten)] + scene: SceneId, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] item_id: u64, @@ -417,18 +452,18 @@ pub enum Event { }, /// A scene item has been selected in the UI. SceneItemSelected { - /// Name of the scene the item is in. - #[serde(rename = "sceneName")] - scene: String, + /// Identifier of the scene the item is in. + #[serde(flatten)] + scene: SceneId, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] item_id: u64, }, /// The transform/crop of a scene item has changed. SceneItemTransformChanged { - /// The name of the scene the item is in. - #[serde(rename = "sceneName")] - scene: String, + /// Identifier of the scene the item is in. + #[serde(flatten)] + scene: SceneId, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] item_id: u64, @@ -441,24 +476,27 @@ pub enum Event { // -------------------------------- /// A new scene has been created. SceneCreated { - /// Name of the new scene. - #[serde(rename = "sceneName")] - name: String, + /// Identifier of the new scene. + #[serde(flatten)] + id: SceneId, /// Whether the new scene is a group. #[serde(rename = "isGroup")] is_group: bool, }, /// A scene has been removed. SceneRemoved { - /// Name of the removed scene. - #[serde(rename = "sceneName")] - name: String, + /// Identifier of the removed scene. + #[serde(flatten)] + id: SceneId, /// Whether the scene was a group. #[serde(rename = "isGroup")] is_group: bool, }, /// The name of a scene has changed. SceneNameChanged { + /// UUID of the scene. + #[serde(rename = "sceneUuid")] + uuid: Uuid, /// Old name of the scene. #[serde(rename = "oldSceneName")] old_name: String, @@ -468,15 +506,15 @@ pub enum Event { }, /// The current program scene has changed. CurrentProgramSceneChanged { - /// Name of the scene that was switched to. - #[serde(rename = "sceneName")] - name: String, + /// Identifier of the scene that was switched to. + #[serde(flatten)] + id: SceneId, }, /// The current preview scene has changed. CurrentPreviewSceneChanged { - /// Name of the scene that was switched to. - #[serde(rename = "sceneName")] - name: String, + /// Identifier of the scene that was switched to. + #[serde(flatten)] + id: SceneId, }, /// The list of scenes has changed. SceneListChanged { @@ -488,9 +526,9 @@ pub enum Event { // -------------------------------- /// The current scene transition has changed. CurrentSceneTransitionChanged { - /// Name of the new transition. - #[serde(rename = "transitionName")] - name: String, + /// Identifier of the new transition. + #[serde(flatten)] + id: TransitionId, }, /// The current scene transition duration has changed. CurrentSceneTransitionDurationChanged { @@ -500,17 +538,17 @@ pub enum Event { }, /// A scene transition has started. SceneTransitionStarted { - /// Scene transition name. - #[serde(rename = "transitionName")] - name: String, + /// Scene transition identifier. + #[serde(flatten)] + id: TransitionId, }, /// A scene transition has completed fully. /// /// **Note:** Does not appear to trigger when the transition is interrupted by the user. SceneTransitionEnded { - /// Scene transition name. - #[serde(rename = "transitionName")] - name: String, + /// Scene transition identifier. + #[serde(flatten)] + id: TransitionId, }, /// A scene transition's video has completed fully. /// @@ -520,9 +558,9 @@ pub enum Event { /// /// **Note:** Appears to be called by every transition, regardless of relevance. SceneTransitionVideoEnded { - /// Scene transition name. - #[serde(rename = "transitionName")] - name: String, + /// Scene transition identifier. + #[serde(flatten)] + id: TransitionId, }, // -------------------------------- // UI diff --git a/src/requests/filters.rs b/src/requests/filters.rs index 0e16593..0778122 100644 --- a/src/requests/filters.rs +++ b/src/requests/filters.rs @@ -3,14 +3,18 @@ use serde::Serialize; use serde_with::skip_serializing_none; +use super::sources::SourceId; + #[derive(Serialize)] #[serde(tag = "requestType", content = "requestData")] pub(crate) enum Request<'a> { + #[serde(rename = "GetSourceFilterKindList")] + KindList, #[serde(rename = "GetSourceFilterList")] List { - /// Name of the source. - #[serde(rename = "sourceName")] - source: &'a str, + /// Identifier of the source. + #[serde(flatten)] + source: SourceId<'a>, }, #[serde(rename = "GetSourceFilterDefaultSettings")] DefaultSettings { @@ -22,9 +26,9 @@ pub(crate) enum Request<'a> { Create(CreateInternal<'a>), #[serde(rename = "RemoveSourceFilter")] Remove { - /// Name of the source the filter is on. - #[serde(rename = "sourceName")] - source: &'a str, + /// Identifier of the source the filter is on. + #[serde(flatten)] + source: SourceId<'a>, /// Name of the filter to remove. #[serde(rename = "filterName")] filter: &'a str, @@ -33,9 +37,9 @@ pub(crate) enum Request<'a> { SetName(SetName<'a>), #[serde(rename = "GetSourceFilter")] Get { - /// Name of the source. - #[serde(rename = "sourceName")] - source: &'a str, + /// Identifier of the source. + #[serde(flatten)] + source: SourceId<'a>, /// Name of the filter. #[serde(rename = "filterName")] filter: &'a str, @@ -56,8 +60,8 @@ impl<'a> From> for super::RequestType<'a> { /// Request information for [`crate::client::Filters::create`]. pub struct Create<'a, T> { - /// Name of the source to add the filter to. - pub source: &'a str, + /// Identifier of the source to add the filter to. + pub source: SourceId<'a>, /// Name of the new filter to be created. pub filter: &'a str, /// The kind of filter to be created. @@ -70,9 +74,9 @@ pub struct Create<'a, T> { #[skip_serializing_none] #[derive(Default, Serialize)] pub(crate) struct CreateInternal<'a> { - /// Name of the source to add the filter to. - #[serde(rename = "sourceName")] - pub source: &'a str, + /// Identifier of the source to add the filter to. + #[serde(flatten)] + pub source: SourceId<'a>, /// Name of the new filter to be created. #[serde(rename = "filterName")] pub filter: &'a str, @@ -87,9 +91,9 @@ pub(crate) struct CreateInternal<'a> { /// Request information for [`crate::client::Filters::set_name`]. #[derive(Default, Serialize)] pub struct SetName<'a> { - /// Name of the source the filter is on. - #[serde(rename = "sourceName")] - pub source: &'a str, + /// Identifier of the source the filter is on. + #[serde(flatten)] + pub source: SourceId<'a>, /// Current name of the filter. #[serde(rename = "filterName")] pub filter: &'a str, @@ -101,9 +105,9 @@ pub struct SetName<'a> { /// Request information for [`crate::client::Filters::set_index`]. #[derive(Default, Serialize)] pub struct SetIndex<'a> { - /// Name of the source the filter is on. - #[serde(rename = "sourceName")] - pub source: &'a str, + /// Identifier of the source the filter is on. + #[serde(flatten)] + pub source: SourceId<'a>, /// Name of the filter. #[serde(rename = "filterName")] pub filter: &'a str, @@ -114,8 +118,8 @@ pub struct SetIndex<'a> { /// Request information for [`crate::client::Filters::set_settings`]. pub struct SetSettings<'a, T> { - /// Name of the source the filter is on. - pub source: &'a str, + /// Identifier of the source the filter is on. + pub source: SourceId<'a>, /// Name of the filter to set the settings of. pub filter: &'a str, /// Object of settings to apply. @@ -127,9 +131,9 @@ pub struct SetSettings<'a, T> { /// Request information for [`crate::client::Filters::set_settings`]. #[derive(Default, Serialize)] pub(crate) struct SetSettingsInternal<'a> { - /// Name of the source the filter is on. - #[serde(rename = "sourceName")] - pub source: &'a str, + /// Identifier of the source the filter is on. + #[serde(flatten)] + pub source: SourceId<'a>, /// Name of the filter to set the settings of. #[serde(rename = "filterName")] pub filter: &'a str, @@ -144,9 +148,9 @@ pub(crate) struct SetSettingsInternal<'a> { /// Request information for [`crate::client::Filters::set_enabled`]. #[derive(Default, Serialize)] pub struct SetEnabled<'a> { - /// Name of the source the filter is on. - #[serde(rename = "sourceName")] - pub source: &'a str, + /// Identifier of the source the filter is on. + #[serde(flatten)] + pub source: SourceId<'a>, /// Name of the filter. #[serde(rename = "filterName")] pub filter: &'a str, diff --git a/src/requests/hotkeys.rs b/src/requests/hotkeys.rs index ee6b098..be25481 100644 --- a/src/requests/hotkeys.rs +++ b/src/requests/hotkeys.rs @@ -12,6 +12,9 @@ pub(crate) enum Request<'a> { /// Name of the hotkey to trigger. #[serde(rename = "hotkeyName")] name: &'a str, + /// Name of context of the hotkey to trigger. + #[serde(rename = "contextName")] + context: Option<&'a str>, }, #[serde(rename = "TriggerHotkeyByKeySequence")] TriggerBySequence { diff --git a/src/requests/ids.rs b/src/requests/ids.rs new file mode 100644 index 0000000..dac7a65 --- /dev/null +++ b/src/requests/ids.rs @@ -0,0 +1,180 @@ +use std::fmt::{self, Display}; + +use serde::{ser::SerializeStruct, Serialize}; +use uuid::Uuid; + +macro_rules! item_id { + ($ident:ident, $name:literal, $name_field:literal, $uuid_field:literal) => { + #[doc = concat!("Identifier of the", $name, ".")] + #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] + pub enum $ident<'a> { + #[doc = concat!("Name of the ", $name, ".")] + Name(&'a str), + #[doc = concat!("UUID of the ", $name, ".")] + Uuid(Uuid), + } + + impl $ident<'_> { + /// If the identifier is a name, returns the associated value. + /// + /// Will return [`None`] if this identifier is not a name. + pub fn as_name(&self) -> Option<&str> { + match *self { + Self::Name(name) => Some(name), + Self::Uuid(_) => None, + } + } + + /// If the identifier is a UUID, returns the associated value. + /// + /// Will return [`None`] if this identifier is not a UUID. + pub fn as_uuid(&self) -> Option { + match *self { + Self::Name(_) => None, + Self::Uuid(uuid) => Some(uuid), + } + } + } + + impl Default for $ident<'_> { + fn default() -> Self { + Self::Name("") + } + } + + impl Display for $ident<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Name(name) => name.fmt(f), + Self::Uuid(uuid) => uuid.fmt(f), + } + } + } + + impl PartialEq for $ident<'_> { + fn eq(&self, other: &str) -> bool { + match *self { + Self::Name(name) => name == other, + Self::Uuid(_) => false, + } + } + } + + impl PartialEq for $ident<'_> { + fn eq(&self, other: &Uuid) -> bool { + match *self { + Self::Name(_) => false, + Self::Uuid(uuid) => uuid == *other, + } + } + } + + impl PartialEq<$ident<'_>> for String { + fn eq(&self, other: &$ident<'_>) -> bool { + other == self.as_str() + } + } + + impl PartialEq<$ident<'_>> for &str { + fn eq(&self, other: &$ident<'_>) -> bool { + other == *self + } + } + + impl PartialEq<$ident<'_>> for Uuid { + fn eq(&self, other: &$ident<'_>) -> bool { + other == self + } + } + + impl<'a> From<&'a str> for $ident<'a> { + fn from(value: &'a str) -> Self { + Self::Name(value) + } + } + + impl From for $ident<'_> { + fn from(value: Uuid) -> Self { + Self::Uuid(value) + } + } + + impl Serialize for $ident<'_> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct(stringify!($ident), 1)?; + match *self { + Self::Name(name) => { + state.serialize_field($name_field, name)?; + } + Self::Uuid(uuid) => { + state.serialize_field($uuid_field, &uuid)?; + } + } + state.end() + } + } + }; +} + +item_id!(InputId, "input", "inputName", "inputUuid"); +item_id!(SceneId, "scene", "sceneName", "sceneUuid"); +item_id!(SourceId, "source", "sourceName", "sourceUuid"); +item_id!( + TransitionId, + "transition", + "transitionName", + "transitionUuid" +); + +item_id!( + DestinationSceneId, + "destination scene", + "destinationSceneName", + "destinationSceneUuid" +); + +macro_rules! convert { + ($source:ident, $target:ident) => { + impl<'a> From<$source<'a>> for $target<'a> { + fn from(value: $source<'a>) -> Self { + match value { + $source::Name(name) => Self::Name(name), + $source::Uuid(uuid) => Self::Uuid(uuid), + } + } + } + + impl<'a> From<$target<'a>> for $source<'a> { + fn from(value: $target<'a>) -> Self { + match value { + $target::Name(name) => Self::Name(name), + $target::Uuid(uuid) => Self::Uuid(uuid), + } + } + } + }; +} + +convert!(SceneId, DestinationSceneId); + +impl<'a> InputId<'a> { + /// Convert the input identifier into a source identifier. + /// + /// This is a one-way operation, as there is no way of telling whether a source ID is an actual + /// input. + pub fn as_source(self) -> SourceId<'a> { + match self { + Self::Name(name) => SourceId::Name(name), + Self::Uuid(uuid) => SourceId::Uuid(uuid), + } + } +} + +impl<'a> From> for SourceId<'a> { + fn from(value: InputId<'a>) -> Self { + value.as_source() + } +} diff --git a/src/requests/inputs.rs b/src/requests/inputs.rs index 9795bb2..387d23c 100644 --- a/src/requests/inputs.rs +++ b/src/requests/inputs.rs @@ -4,6 +4,8 @@ use serde::Serialize; use serde_with::skip_serializing_none; use time::Duration; +pub use super::ids::InputId; +use super::scenes::SceneId; use crate::common::MonitorType; #[derive(Serialize)] @@ -31,53 +33,53 @@ pub(crate) enum Request<'a> { }, #[serde(rename = "GetInputSettings")] Settings { - /// Name of the input to get the settings of. - #[serde(rename = "inputName")] - name: &'a str, + /// The input to get the settings of. + #[serde(flatten)] + input: InputId<'a>, }, #[serde(rename = "SetInputSettings")] SetSettings(SetSettingsInternal<'a>), #[serde(rename = "GetInputMute")] Muted { - /// Name of input to get the mute state of. - #[serde(rename = "inputName")] - name: &'a str, + /// The input to get the mute state of. + #[serde(flatten)] + input: InputId<'a>, }, #[serde(rename = "SetInputMute")] SetMuted { - /// Name of the input to set the mute state of. - #[serde(rename = "inputName")] - name: &'a str, + /// The input to set the mute state of. + #[serde(flatten)] + input: InputId<'a>, /// Whether to mute the input. #[serde(rename = "inputMuted")] muted: bool, }, #[serde(rename = "ToggleInputMute")] ToggleMute { - /// Name of the input to toggle the mute state of. - #[serde(rename = "inputName")] - name: &'a str, + /// The input to toggle the mute state of. + #[serde(flatten)] + input: InputId<'a>, }, #[serde(rename = "GetInputVolume")] Volume { - /// Name of the input to get the volume of. - #[serde(rename = "inputName")] - name: &'a str, + /// The input to get the volume of. + #[serde(flatten)] + input: InputId<'a>, }, #[serde(rename = "SetInputVolume")] SetVolume { - /// Name of the input to set the volume of. - #[serde(rename = "inputName")] - name: &'a str, + /// The input to set the volume of. + #[serde(flatten)] + input: InputId<'a>, /// Volume settings in either mul or dB. #[serde(rename = "volume", flatten)] volume: Volume, }, #[serde(rename = "SetInputName")] SetName { - /// Current input name. - #[serde(rename = "inputName")] - name: &'a str, + /// Current input. + #[serde(flatten)] + input: InputId<'a>, /// New name for the input. #[serde(rename = "newInputName")] new: &'a str, @@ -86,36 +88,36 @@ pub(crate) enum Request<'a> { Create(CreateInputInternal<'a>), #[serde(rename = "RemoveInput")] Remove { - /// Name of the input to remove. - #[serde(rename = "inputName")] - name: &'a str, + /// The input to remove. + #[serde(flatten)] + input: InputId<'a>, }, #[serde(rename = "GetInputAudioBalance")] AudioBalance { - /// Name of the input to get the audio balance of. - #[serde(rename = "inputName")] - name: &'a str, + /// The input to get the audio balance of. + #[serde(flatten)] + input: InputId<'a>, }, #[serde(rename = "SetInputAudioBalance")] SetAudioBalance { - /// Name of the input to set the audio balance of. - #[serde(rename = "inputName")] - name: &'a str, + /// The input to set the audio balance of. + #[serde(flatten)] + input: InputId<'a>, /// New audio balance value. Must be in range of `0.0..=1.0`. #[serde(rename = "inputAudioBalance")] balance: f32, }, #[serde(rename = "GetInputAudioSyncOffset")] AudioSyncOffset { - /// Name of the input to get the audio sync offset of. - #[serde(rename = "inputName")] - name: &'a str, + /// The input to get the audio sync offset of. + #[serde(flatten)] + input: InputId<'a>, }, #[serde(rename = "SetInputAudioSyncOffset")] SetAudioSyncOffset { - /// Name of the input to set the audio sync offset of. - #[serde(rename = "inputName")] - name: &'a str, + /// The input to set the audio sync offset of. + #[serde(flatten)] + input: InputId<'a>, /// New audio sync offset in milliseconds. #[serde( rename = "inputAudioSyncOffset", @@ -125,30 +127,30 @@ pub(crate) enum Request<'a> { }, #[serde(rename = "GetInputAudioMonitorType")] AudioMonitorType { - /// Name of the input to get the audio monitor type of. - #[serde(rename = "inputName")] - name: &'a str, + /// The input to get the audio monitor type of. + #[serde(flatten)] + input: InputId<'a>, }, #[serde(rename = "SetInputAudioMonitorType")] SetAudioMonitorType { - /// Name of the input to set the audio monitor type of. - #[serde(rename = "inputName")] - name: &'a str, + /// The input to set the audio monitor type of. + #[serde(flatten)] + input: InputId<'a>, /// Audio monitor type. #[serde(rename = "monitorType")] monitor_type: MonitorType, }, #[serde(rename = "GetInputAudioTracks")] AudioTracks { - /// Name of the input. - #[serde(rename = "inputName")] - name: &'a str, + /// Identifier of the input. + #[serde(flatten)] + input: InputId<'a>, }, #[serde(rename = "SetInputAudioTracks")] SetAudioTracks { - /// Name of the input. - #[serde(rename = "inputName")] - name: &'a str, + /// Identifier of the input. + #[serde(flatten)] + input: InputId<'a>, /// Track settings to apply. #[serde( rename = "inputAudioTracks", @@ -158,18 +160,18 @@ pub(crate) enum Request<'a> { }, #[serde(rename = "GetInputPropertiesListPropertyItems")] PropertiesListPropertyItems { - /// Name of the input. - #[serde(rename = "inputName")] - input: &'a str, + /// Identifier of the input. + #[serde(flatten)] + input: InputId<'a>, /// Name of the list property to get the items of. #[serde(rename = "propertyName")] property: &'a str, }, #[serde(rename = "PressInputPropertiesButton")] PressPropertiesButton { - /// Name of the input. - #[serde(rename = "inputName")] - input: &'a str, + /// Identifier of the input. + #[serde(flatten)] + input: InputId<'a>, /// Name of the button property to press. #[serde(rename = "propertyName")] property: &'a str, @@ -184,8 +186,8 @@ impl<'a> From> for super::RequestType<'a> { /// Request information for [`crate::client::Inputs::set_settings`]. pub struct SetSettings<'a, T> { - /// Name of the input to set the settings of. - pub input: &'a str, + /// The input to set the settings of. + pub input: InputId<'a>, /// Object of settings to apply. pub settings: &'a T, /// Apply settings on top of existing ones or reset the input to its defaults, then apply @@ -197,9 +199,9 @@ pub struct SetSettings<'a, T> { #[skip_serializing_none] #[derive(Default, Serialize)] pub(crate) struct SetSettingsInternal<'a> { - /// Name of the input to set the settings of. - #[serde(rename = "inputName")] - pub input: &'a str, + /// The input to set the settings of. + #[serde(flatten)] + pub input: InputId<'a>, /// Object of settings to apply. #[serde(rename = "inputSettings")] pub settings: serde_json::Value, @@ -224,7 +226,7 @@ pub enum Volume { /// Request information for [`crate::client::Inputs::create`]. pub struct Create<'a, T> { /// Name of the scene to add the input to as a scene item. - pub scene: &'a str, + pub scene: SceneId<'a>, /// Name of the new input to created. pub input: &'a str, /// The kind of input to be created. @@ -239,8 +241,8 @@ pub struct Create<'a, T> { #[skip_serializing_none] #[derive(Default, Serialize)] pub(crate) struct CreateInputInternal<'a> { - #[serde(rename = "sceneName")] - pub scene: &'a str, + #[serde(flatten)] + pub scene: SceneId<'a>, #[serde(rename = "inputName")] pub input: &'a str, #[serde(rename = "inputKind")] diff --git a/src/requests/media_inputs.rs b/src/requests/media_inputs.rs index 7dae1e7..c4acb15 100644 --- a/src/requests/media_inputs.rs +++ b/src/requests/media_inputs.rs @@ -3,6 +3,7 @@ use serde::Serialize; use time::Duration; +use super::inputs::InputId; use crate::common::MediaAction; #[derive(Serialize)] @@ -10,33 +11,33 @@ use crate::common::MediaAction; pub(crate) enum Request<'a> { #[serde(rename = "GetMediaInputStatus")] Status { - /// Name of the media input. - #[serde(rename = "inputName")] - input: &'a str, + /// Identifier of the media input. + #[serde(flatten)] + input: InputId<'a>, }, #[serde(rename = "SetMediaInputCursor")] SetCursor { - /// Name of the media input. - #[serde(rename = "inputName")] - input: &'a str, + /// Identifier of the media input. + #[serde(flatten)] + input: InputId<'a>, /// New cursor position to set. #[serde(rename = "mediaCursor", with = "crate::serde::duration_millis")] cursor: Duration, }, #[serde(rename = "OffsetMediaInputCursor")] OffsetCursor { - /// Name of the media input. - #[serde(rename = "inputName")] - input: &'a str, + /// Identifier of the media input. + #[serde(flatten)] + input: InputId<'a>, /// Value to offset the current cursor position by. #[serde(rename = "mediaCursorOffset", with = "crate::serde::duration_millis")] offset: Duration, }, #[serde(rename = "TriggerMediaInputAction")] TriggerAction { - /// Name of the media input. - #[serde(rename = "inputName")] - input: &'a str, + /// Identifier of the media input. + #[serde(flatten)] + input: InputId<'a>, /// Identifier of the media action. #[serde(rename = "mediaAction")] action: MediaAction, diff --git a/src/requests/mod.rs b/src/requests/mod.rs index e55bf60..8a975e3 100644 --- a/src/requests/mod.rs +++ b/src/requests/mod.rs @@ -10,6 +10,7 @@ pub mod custom; pub mod filters; pub mod general; pub mod hotkeys; +pub(crate) mod ids; pub mod inputs; pub(crate) mod media_inputs; pub(crate) mod outputs; diff --git a/src/requests/scene_items.rs b/src/requests/scene_items.rs index 0f4ba7a..3f641f3 100644 --- a/src/requests/scene_items.rs +++ b/src/requests/scene_items.rs @@ -3,6 +3,7 @@ use serde::Serialize; use serde_with::skip_serializing_none; +use super::{ids::DestinationSceneId, scenes::SceneId, sources::SourceId}; use crate::common::{Alignment, BlendMode, BoundsType}; #[derive(Serialize)] @@ -10,25 +11,27 @@ use crate::common::{Alignment, BlendMode, BoundsType}; pub(crate) enum Request<'a> { #[serde(rename = "GetSceneItemList")] List { - /// Name of the scene to get the items of. - #[serde(rename = "sceneName")] - scene: &'a str, + /// Identifier of the scene to get the items of. + #[serde(flatten)] + scene: SceneId<'a>, }, #[serde(rename = "GetGroupSceneItemList")] ListGroup { - /// Name of the group to get the items of. - #[serde(rename = "sceneName")] - scene: &'a str, + /// Identifier of the group to get the items of. + #[serde(flatten)] + scene: SceneId<'a>, }, #[serde(rename = "GetSceneItemId")] Id(Id<'a>), + #[serde(rename = "GetSceneItemSource")] + Source(Source<'a>), #[serde(rename = "CreateSceneItem")] Create(CreateSceneItem<'a>), #[serde(rename = "RemoveSceneItem")] Remove { - /// Name of the scene the item is in. - #[serde(rename = "sceneName")] - scene: &'a str, + /// Identifier of the scene the item is in. + #[serde(flatten)] + scene: SceneId<'a>, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] item_id: i64, @@ -37,9 +40,9 @@ pub(crate) enum Request<'a> { Duplicate(Duplicate<'a>), #[serde(rename = "GetSceneItemTransform")] Transform { - /// Name of the scene the item is in. - #[serde(rename = "sceneName")] - scene: &'a str, + /// Identifier of the scene the item is in. + #[serde(flatten)] + scene: SceneId<'a>, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] item_id: i64, @@ -48,9 +51,9 @@ pub(crate) enum Request<'a> { SetTransform(SetTransform<'a>), #[serde(rename = "GetSceneItemEnabled")] Enabled { - /// Name of the scene the item is in. - #[serde(rename = "sceneName")] - scene: &'a str, + /// Identifier of the scene the item is in. + #[serde(flatten)] + scene: SceneId<'a>, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] item_id: i64, @@ -59,9 +62,9 @@ pub(crate) enum Request<'a> { SetEnabled(SetEnabled<'a>), #[serde(rename = "GetSceneItemLocked")] Locked { - /// Name of the scene the item is in. - #[serde(rename = "sceneName")] - scene: &'a str, + /// Identifier of the scene the item is in. + #[serde(flatten)] + scene: SceneId<'a>, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] item_id: i64, @@ -70,9 +73,9 @@ pub(crate) enum Request<'a> { SetLocked(SetLocked<'a>), #[serde(rename = "GetSceneItemIndex")] Index { - /// Name of the scene the item is in. - #[serde(rename = "sceneName")] - scene: &'a str, + /// Identifier of the scene the item is in. + #[serde(flatten)] + scene: SceneId<'a>, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] item_id: i64, @@ -81,9 +84,9 @@ pub(crate) enum Request<'a> { SetIndex(SetIndex<'a>), #[serde(rename = "GetSceneItemBlendMode")] BlendMode { - /// Name of the scene the item is in. - #[serde(rename = "sceneName")] - scene: &'a str, + /// Identifier of the scene the item is in. + #[serde(flatten)] + scene: SceneId<'a>, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] item_id: i64, @@ -92,9 +95,9 @@ pub(crate) enum Request<'a> { SetBlendMode(SetBlendMode<'a>), #[serde(rename = "GetSceneItemPrivateSettings")] PrivateSettings { - /// Name of the scene the item is in. - #[serde(rename = "sceneName")] - scene: &'a str, + /// Identifier of the scene the item is in. + #[serde(flatten)] + scene: SceneId<'a>, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] item_id: i64, @@ -113,9 +116,9 @@ impl<'a> From> for super::RequestType<'a> { #[skip_serializing_none] #[derive(Default, Serialize)] pub struct Id<'a> { - /// Name of the scene or group to search in. - #[serde(rename = "sceneName")] - pub scene: &'a str, + /// Identifier of the scene or group to search in. + #[serde(flatten)] + pub scene: SceneId<'a>, /// Name of the source to find. #[serde(rename = "sourceName")] pub source: &'a str, @@ -126,16 +129,28 @@ pub struct Id<'a> { pub search_offset: Option, } +/// Request information for [`crate::client::SceneItems::source`]. +#[skip_serializing_none] +#[derive(Default, Serialize)] +pub struct Source<'a> { + /// Identifier of the scene the item is in. + #[serde(flatten)] + pub scene: SceneId<'a>, + /// Numeric ID of the scene item. + #[serde(rename = "sceneItemId")] + pub item_id: i64, +} + /// Request information for [`crate::client::SceneItems::create`]. #[skip_serializing_none] #[derive(Default, Serialize)] pub struct CreateSceneItem<'a> { - /// Name of the scene to create the new item in. - #[serde(rename = "sceneName")] - pub scene: &'a str, - /// Name of the source to add to the scene. - #[serde(rename = "sourceName")] - pub source: &'a str, + /// Identifier of the scene to create the new item in. + #[serde(flatten)] + pub scene: SceneId<'a>, + /// Identifier of the source to add to the scene. + #[serde(flatten)] + pub source: SourceId<'a>, /// Enable state to apply to the scene item on creation. #[serde(rename = "sceneItemEnabled")] pub enabled: Option, @@ -145,23 +160,23 @@ pub struct CreateSceneItem<'a> { #[skip_serializing_none] #[derive(Default, Serialize)] pub struct Duplicate<'a> { - /// Name of the scene the item is in. - #[serde(rename = "sceneName")] - pub scene: &'a str, + /// Identifier of the scene the item is in. + #[serde(flatten)] + pub scene: SceneId<'a>, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] pub item_id: i64, - /// Name of the scene to create the duplicated item in. - #[serde(rename = "destinationSceneName")] - pub destination: Option<&'a str>, + /// Identifier of the scene to create the duplicated item in. + #[serde(flatten)] + pub destination: Option>, } /// Request information for [`crate::client::SceneItems::set_transform`]. #[derive(Default, Serialize)] pub struct SetTransform<'a> { - /// Name of the scene the item is in. - #[serde(rename = "sceneName")] - pub scene: &'a str, + /// Identifier of the scene the item is in. + #[serde(flatten)] + pub scene: SceneId<'a>, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] pub item_id: i64, @@ -291,9 +306,9 @@ pub struct Crop { /// Request information for [`crate::client::SceneItems::set_enabled`]. #[derive(Default, Serialize)] pub struct SetEnabled<'a> { - /// Name of the scene the item is in. - #[serde(rename = "sceneName")] - pub scene: &'a str, + /// Identifier of the scene the item is in. + #[serde(flatten)] + pub scene: SceneId<'a>, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] pub item_id: i64, @@ -305,9 +320,9 @@ pub struct SetEnabled<'a> { /// Request information for [`crate::client::SceneItems::set_locked`]. #[derive(Default, Serialize)] pub struct SetLocked<'a> { - /// Name of the scene the item is in. - #[serde(rename = "sceneName")] - pub scene: &'a str, + /// Identifier of the scene the item is in. + #[serde(flatten)] + pub scene: SceneId<'a>, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] pub item_id: i64, @@ -319,9 +334,9 @@ pub struct SetLocked<'a> { /// Request information for [`crate::client::SceneItems::set_index`]. #[derive(Default, Serialize)] pub struct SetIndex<'a> { - /// Name of the scene the item is in. - #[serde(rename = "sceneName")] - pub scene: &'a str, + /// Identifier of the scene the item is in. + #[serde(flatten)] + pub scene: SceneId<'a>, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] pub item_id: i64, @@ -333,9 +348,9 @@ pub struct SetIndex<'a> { /// Request information for [`crate::client::SceneItems::set_blend_mode`]. #[derive(Serialize)] pub struct SetBlendMode<'a> { - /// Name of the scene the item is in. - #[serde(rename = "sceneName")] - pub scene: &'a str, + /// Identifier of the scene the item is in. + #[serde(flatten)] + pub scene: SceneId<'a>, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] pub item_id: i64, @@ -346,8 +361,8 @@ pub struct SetBlendMode<'a> { /// Request information for [`crate::client::SceneItems::set_private_settings`]. pub struct SetPrivateSettings<'a, T> { - /// Name of the scene the item is in. - pub scene: &'a str, + /// Identifier of the scene the item is in. + pub scene: SceneId<'a>, /// Numeric ID of the scene item. pub item_id: i64, /// Object of settings to apply. @@ -359,9 +374,9 @@ pub struct SetPrivateSettings<'a, T> { #[skip_serializing_none] #[derive(Default, Serialize)] pub(crate) struct SetPrivateSettingsInternal<'a> { - /// Name of the scene the item is in. - #[serde(rename = "sceneName")] - pub scene: &'a str, + /// Identifier of the scene the item is in. + #[serde(flatten)] + pub scene: SceneId<'a>, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] pub item_id: i64, diff --git a/src/requests/scenes.rs b/src/requests/scenes.rs index ca12c27..4bd8a2c 100644 --- a/src/requests/scenes.rs +++ b/src/requests/scenes.rs @@ -4,6 +4,9 @@ use serde::Serialize; use serde_with::skip_serializing_none; use time::Duration; +pub use super::ids::SceneId; + +#[skip_serializing_none] #[derive(Serialize)] #[serde(tag = "requestType", content = "requestData")] pub(crate) enum Request<'a> { @@ -16,22 +19,22 @@ pub(crate) enum Request<'a> { #[serde(rename = "SetCurrentProgramScene")] SetCurrentProgramScene { /// Scene to set as the current program scene. - #[serde(rename = "sceneName")] - scene: &'a str, + #[serde(flatten)] + scene: SceneId<'a>, }, #[serde(rename = "GetCurrentPreviewScene")] CurrentPreviewScene, #[serde(rename = "SetCurrentPreviewScene")] SetCurrentPreviewScene { /// Scene to set as the current preview scene. - #[serde(rename = "sceneName")] - scene: &'a str, + #[serde(flatten)] + scene: SceneId<'a>, }, #[serde(rename = "SetSceneName")] SetName { - /// Name of the scene to be renamed. - #[serde(rename = "sceneName")] - scene: &'a str, + /// The scene to be renamed. + #[serde(flatten)] + scene: SceneId<'a>, /// New name for the scene. #[serde(rename = "newSceneName")] new_name: &'a str, @@ -44,15 +47,15 @@ pub(crate) enum Request<'a> { }, #[serde(rename = "RemoveScene")] Remove { - /// Name of the scene to remove. - #[serde(rename = "sceneName")] - scene: &'a str, + /// The scene to remove. + #[serde(flatten)] + scene: SceneId<'a>, }, #[serde(rename = "GetSceneSceneTransitionOverride")] TransitionOverride { - /// Name of the scene. - #[serde(rename = "sceneName")] - scene: &'a str, + /// Identifier of the scene. + #[serde(flatten)] + scene: SceneId<'a>, }, #[serde(rename = "SetSceneSceneTransitionOverride")] SetTransitionOverride(SetTransitionOverride<'a>), @@ -68,9 +71,9 @@ impl<'a> From> for super::RequestType<'a> { #[skip_serializing_none] #[derive(Default, Serialize)] pub struct SetTransitionOverride<'a> { - /// Name of the scene. - #[serde(rename = "sceneName")] - pub scene: &'a str, + /// The target scene. + #[serde(flatten)] + pub scene: SceneId<'a>, /// Name of the scene transition to use as override. #[serde(rename = "transitionName")] pub transition: Option<&'a str>, diff --git a/src/requests/sources.rs b/src/requests/sources.rs index 8f7ef88..73a15b0 100644 --- a/src/requests/sources.rs +++ b/src/requests/sources.rs @@ -5,14 +5,16 @@ use std::path::Path; use serde::Serialize; use serde_with::skip_serializing_none; +pub use super::ids::SourceId; + #[derive(Serialize)] #[serde(tag = "requestType", content = "requestData")] pub(crate) enum Request<'a> { #[serde(rename = "GetSourceActive")] Active { - /// Name of the source to get the active state of. - #[serde(rename = "sourceName")] - name: &'a str, + /// Identifier of the source to get the active state of. + #[serde(flatten)] + source: SourceId<'a>, }, #[serde(rename = "GetSourceScreenshot")] TakeScreenshot(TakeScreenshot<'a>), @@ -28,11 +30,11 @@ impl<'a> From> for super::RequestType<'a> { /// Request information for [`crate::client::Sources::take_screenshot`]. #[skip_serializing_none] -#[derive(Default, Serialize)] +#[derive(Serialize)] pub struct TakeScreenshot<'a> { - /// Name of the source to take a screenshot of. - #[serde(rename = "sourceName")] - pub source: &'a str, + /// Identifier of the source to take a screenshot of. + #[serde(flatten)] + pub source: SourceId<'a>, /// Image compression format to use. Use [`crate::client::General::version`] to get compatible /// image formats. #[serde(rename = "imageFormat")] @@ -53,9 +55,9 @@ pub struct TakeScreenshot<'a> { #[skip_serializing_none] #[derive(Serialize)] pub struct SaveScreenshot<'a> { - /// Name of the source to take a screenshot of. - #[serde(rename = "sourceName")] - pub source: &'a str, + /// Identifier of the source to take a screenshot of. + #[serde(flatten)] + pub source: SourceId<'a>, /// Image compression format to use. Use [`crate::client::General::version`] to get compatible /// image formats. #[serde(rename = "imageFormat")] diff --git a/src/requests/ui.rs b/src/requests/ui.rs index 4955097..a263f91 100644 --- a/src/requests/ui.rs +++ b/src/requests/ui.rs @@ -3,6 +3,8 @@ use bitflags::bitflags; use serde::Serialize; +use super::{inputs::InputId, sources::SourceId}; + #[derive(Serialize)] #[serde(tag = "requestType", content = "requestData")] pub(crate) enum Request<'a> { @@ -16,21 +18,21 @@ pub(crate) enum Request<'a> { }, #[serde(rename = "OpenInputPropertiesDialog")] OpenInputPropertiesDialog { - /// Name of the input to open the dialog of. - #[serde(rename = "inputName")] - input: &'a str, + /// Identifier of the input to open the dialog of. + #[serde(flatten)] + input: InputId<'a>, }, #[serde(rename = "OpenInputFiltersDialog")] OpenInputFiltersDialog { - /// Name of the input to open the dialog of. - #[serde(rename = "inputName")] - input: &'a str, + /// Identifier of the input to open the dialog of. + #[serde(flatten)] + input: InputId<'a>, }, #[serde(rename = "OpenInputInteractDialog")] OpenInputInteractDialog { - /// Name of the input to open the dialog of. - #[serde(rename = "inputName")] - input: &'a str, + /// Identifier of the input to open the dialog of. + #[serde(flatten)] + input: InputId<'a>, }, #[serde(rename = "GetMonitorList")] GetMonitorList, @@ -67,8 +69,8 @@ pub(crate) struct OpenVideoMixProjectorInternal { /// Request information for [`crate::client::Ui::open_source_projector`]. pub struct OpenSourceProjector<'a> { - /// Name of the source to open a projector for. - pub source: &'a str, + /// Identifier of the source to open a projector for. + pub source: SourceId<'a>, /// Optional location for the new projector window. pub location: Option, } @@ -76,9 +78,9 @@ pub struct OpenSourceProjector<'a> { /// Request information for [`crate::client::Ui::open_source_projector`]. #[derive(Serialize)] pub(crate) struct OpenSourceProjectorInternal<'a> { - /// Name of the source to open a projector for. - #[serde(rename = "sourceName")] - pub source: &'a str, + /// Identifier of the source to open a projector for. + #[serde(flatten)] + pub source: SourceId<'a>, /// Optional location for the new projector window. #[serde(flatten)] pub location: Option, diff --git a/src/responses/filters.rs b/src/responses/filters.rs index ad653e9..db9963b 100644 --- a/src/responses/filters.rs +++ b/src/responses/filters.rs @@ -2,6 +2,14 @@ use serde::{Deserialize, Serialize}; +/// Response value for [`crate::client::Filters::get_source_filter_kind_list`]. +#[derive(Debug, Deserialize)] +pub(crate) struct FilterKinds { + /// Array of source filter kinds. + #[serde(rename = "sourceFilterKinds")] + pub kinds: Vec, +} + /// Response value for [`crate::client::Filters::get_source_filter_list`]. #[derive(Debug, Deserialize)] pub(crate) struct Filters { diff --git a/src/responses/ids.rs b/src/responses/ids.rs new file mode 100644 index 0000000..1f51bba --- /dev/null +++ b/src/responses/ids.rs @@ -0,0 +1,141 @@ +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +macro_rules! item_id { + ($ident:ident, $name:literal, $name_field:literal, $uuid_field:literal) => { + #[doc = concat!("Identifier of the", $name, ".")] + #[derive( + Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, + )] + pub struct $ident { + #[doc = concat!("Name of the", $name, ".")] + #[serde(rename = $name_field)] + pub name: String, + #[doc = concat!("UUID of the", $name, ".")] + #[serde(rename = $uuid_field)] + pub uuid: Uuid, + } + + impl PartialEq<$ident> for String { + fn eq(&self, other: &$ident) -> bool { + other == self.as_str() + } + } + + impl PartialEq<$ident> for &str { + fn eq(&self, other: &$ident) -> bool { + other == *self + } + } + + impl PartialEq<$ident> for Uuid { + fn eq(&self, other: &$ident) -> bool { + other == self + } + } + + impl PartialEq for $ident { + fn eq(&self, other: &str) -> bool { + self.name == other + } + } + + impl PartialEq for $ident { + fn eq(&self, other: &Uuid) -> bool { + self.uuid == *other + } + } + }; +} + +item_id!(InputId, "input", "inputName", "inputUuid"); +item_id!(SceneId, "scene", "sceneName", "sceneUuid"); +item_id!(SourceId, "source", "sourceName", "sourceUuid"); +item_id!( + TransitionId, + "transition", + "transitionName", + "transitionUuid" +); + +item_id!( + CurrentPreviewSceneId, + "current preview scene", + "currentPreviewSceneName", + "currentPreviewSceneUuid" +); +item_id!( + CurrentProgramSceneId, + "current program scene", + "currentProgramSceneName", + "currentProgramSceneUuid" +); +item_id!( + CurrentSceneTransitionId, + "current scene transition", + "currentSceneTransitionName", + "currentSceneTransitionUuid" +); + +macro_rules! convert { + ($source:ident, $target:ident) => { + impl From<$source> for $target { + fn from(value: $source) -> Self { + Self { + name: value.name, + uuid: value.uuid, + } + } + } + + impl From<$target> for $source { + fn from(value: $target) -> Self { + Self { + name: value.name, + uuid: value.uuid, + } + } + } + }; +} + +convert!(SceneId, CurrentPreviewSceneId); +convert!(SceneId, CurrentProgramSceneId); +convert!(CurrentPreviewSceneId, CurrentProgramSceneId); +convert!(TransitionId, CurrentSceneTransitionId); + +macro_rules! request { + ($ident:ident) => { + impl From<$ident> for crate::requests::ids::$ident<'_> { + fn from(value: $ident) -> Self { + Self::Uuid(value.uuid) + } + } + + impl From<&$ident> for crate::requests::ids::$ident<'_> { + fn from(value: &$ident) -> Self { + Self::Uuid(value.uuid) + } + } + + impl PartialEq<$ident> for crate::requests::ids::$ident<'_> { + fn eq(&self, other: &$ident) -> bool { + match *self { + Self::Name(name) => name == other.name, + Self::Uuid(uuid) => uuid == other.uuid, + } + } + } + + impl PartialEq> for $ident { + fn eq(&self, other: &crate::requests::ids::$ident<'_>) -> bool { + other == self + } + } + }; +} + +request!(InputId); +request!(SceneId); +request!(SourceId); +request!(TransitionId); diff --git a/src/responses/inputs.rs b/src/responses/inputs.rs index 3106a0f..8b13948 100644 --- a/src/responses/inputs.rs +++ b/src/responses/inputs.rs @@ -2,7 +2,9 @@ use serde::{Deserialize, Serialize}; use time::Duration; +use uuid::Uuid; +pub use super::ids::InputId; use crate::common::MonitorType; /// Response value for [`crate::client::Inputs::get_input_list`]. @@ -16,9 +18,9 @@ pub(crate) struct Inputs { /// Response value for [`crate::client::Inputs::list`]. #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct Input { - /// Name of the input source. - #[serde(rename = "inputName")] - pub name: String, + /// Identifier of the input source. + #[serde(flatten)] + pub id: InputId, /// Version input kind. #[serde(rename = "inputKind")] pub kind: String, @@ -154,8 +156,12 @@ pub struct ListPropertyItem { pub value: serde_json::Value, } -#[derive(Debug, Deserialize)] -pub(crate) struct SceneItemId { +/// Response value for [`crate::client::Inputs::create`]. +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct SceneItemId { + /// UUID of the newly created input. + #[serde(rename = "inputUuid")] + pub input_uuid: Uuid, /// Numeric ID of the scene item. #[serde(rename = "sceneItemId")] pub scene_item_id: i64, diff --git a/src/responses/mod.rs b/src/responses/mod.rs index 65549f9..e304cc7 100644 --- a/src/responses/mod.rs +++ b/src/responses/mod.rs @@ -4,6 +4,7 @@ pub mod config; pub mod filters; pub mod general; pub(crate) mod hotkeys; +pub(crate) mod ids; pub mod inputs; pub mod media_inputs; pub mod outputs; @@ -39,6 +40,7 @@ pub(crate) enum ServerMessage { /// `obs-websocket` is responding to a request coming from a client. RequestResponse(RequestResponse), /// `obs-websocket` is responding to a request batch coming from the client. + #[allow(dead_code)] RequestBatchResponse(RequestBatchResponse), } diff --git a/src/responses/scenes.rs b/src/responses/scenes.rs index f18afd3..35fd413 100644 --- a/src/responses/scenes.rs +++ b/src/responses/scenes.rs @@ -2,16 +2,19 @@ use serde::{Deserialize, Serialize}; use time::Duration; +use uuid::Uuid; + +pub use super::ids::{CurrentPreviewSceneId, CurrentProgramSceneId, SceneId}; /// Response value for [`crate::client::Scenes::list`]. #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct Scenes { - /// Current program scene. - #[serde(rename = "currentProgramSceneName")] - pub current_program_scene_name: Option, - /// Current preview scene. [`None`] if not in studio mode. - #[serde(rename = "currentPreviewSceneName")] - pub current_preview_scene_name: Option, + /// Current program scene identifier. Can be [`None`] if internal state desync. + #[serde(flatten)] + pub current_program_scene: Option, + /// Current preview scene identifier. [`None`] if not in studio mode. + #[serde(flatten)] + pub current_preview_scene: Option, /// Array of scenes in OBS. #[serde(rename = "scenes")] pub scenes: Vec, @@ -38,20 +41,27 @@ pub(crate) struct Groups { /// Response value for /// [`crate::client::Scenes::get_current_program_scene`]. -#[derive(Debug, Deserialize)] -pub(crate) struct CurrentProgramScene { - /// Current program scene. - #[serde(rename = "currentProgramSceneName")] - pub current_program_scene_name: String, +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct CurrentProgramScene { + /// Current program scene identifier. + #[serde(flatten)] + pub id: SceneId, } /// Response value for /// [`crate::client::Scenes::get_current_preview_scene`]. +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct CurrentPreviewScene { + /// Current preview scene identifier. + #[serde(flatten)] + pub id: SceneId, +} + #[derive(Debug, Deserialize)] -pub(crate) struct CurrentPreviewScene { - /// Current preview scene. - #[serde(rename = "currentPreviewSceneName")] - pub current_preview_scene_name: String, +pub(crate) struct CreateScene { + /// UUID of the created scene. + #[serde(rename = "sceneUuid")] + pub uuid: Uuid, } /// Response value for [`crate::client::Scenes::transition_override`]. diff --git a/src/responses/sources.rs b/src/responses/sources.rs index 54fc2bc..ccaa058 100644 --- a/src/responses/sources.rs +++ b/src/responses/sources.rs @@ -2,6 +2,8 @@ use serde::{Deserialize, Serialize}; +pub use super::ids::SourceId; + /// Response value for [`crate::client::Sources::active`]. #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct SourceActive { diff --git a/src/responses/transitions.rs b/src/responses/transitions.rs index b81f446..a08e897 100644 --- a/src/responses/transitions.rs +++ b/src/responses/transitions.rs @@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize}; use time::Duration; +pub use super::ids::{CurrentSceneTransitionId, TransitionId}; + /// Response value for /// [`crate::client::Transitions::get_transition_kind_list`]. #[derive(Debug, Deserialize)] @@ -15,9 +17,9 @@ pub(crate) struct TransitionKinds { /// Response value for [`crate::client::Transitions::list`]. #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct SceneTransitionList { - /// Name of the current scene transition. - #[serde(rename = "currentSceneTransitionName")] - pub current_scene_transition_name: Option, + /// Identifier of the current scene transition. + #[serde(flatten)] + pub current_scene_transition: Option, /// Kind of the current scene transition. #[serde(rename = "currentSceneTransitionKind")] pub current_scene_transition_kind: Option, @@ -29,9 +31,9 @@ pub struct SceneTransitionList { /// Response value for [`crate::client::Transitions::list`] as part of [`SceneTransitionList`]. #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct Transition { - /// Name of the transition. - #[serde(rename = "transitionName")] - pub name: String, + /// Identifier of the transition. + #[serde(flatten)] + pub id: TransitionId, /// Kind of the transition. #[serde(rename = "transitionKind")] pub kind: String, @@ -46,9 +48,9 @@ pub struct Transition { /// Response value for [`crate::client::Transitions::current`]. #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct CurrentSceneTransition { - /// Name of the transition. - #[serde(rename = "transitionName")] - pub name: String, + /// Identifier of the transition. + #[serde(flatten)] + pub id: TransitionId, /// Kind of the transition. #[serde(rename = "transitionKind")] pub kind: String, diff --git a/tests/integration/common.rs b/tests/integration/common.rs index 40e418a..3ff00f2 100644 --- a/tests/integration/common.rs +++ b/tests/integration/common.rs @@ -2,21 +2,22 @@ use std::{env, sync::Once}; use anyhow::{ensure, Result}; use obws::{ + requests::{inputs::InputId, scenes::SceneId}, responses::{filters::SourceFilter, inputs::Input, scenes::Scene}, Client, }; pub const TEST_PROFILE: &str = "OBWS-TEST"; -pub const TEST_SCENE: &str = "OBWS-TEST-Scene"; -pub const TEST_SCENE_2: &str = "OBWS-TEST-Scene2"; -pub const TEST_SCENE_RENAME: &str = "OBWS-TEST-Scene-Renamed"; -pub const TEST_SCENE_CREATE: &str = "OBWS-TEST-Scene-Created"; -pub const TEST_TEXT: &str = "OBWS-TEST-Text"; -pub const TEST_TEXT_2: &str = "OBWS-TEST-Text2"; -pub const TEST_BROWSER: &str = "OBWS-TEST-Browser"; -pub const TEST_BROWSER_RENAME: &str = "OBWS-TEST-Browser-Renamed"; -pub const TEST_MEDIA: &str = "OBWS-TEST-Media"; -pub const TEST_GROUP: &str = "OBWS-TEST-Group"; +pub const TEST_SCENE: SceneId<'_> = SceneId::Name("OBWS-TEST-Scene"); +pub const TEST_SCENE_2: SceneId<'_> = SceneId::Name("OBWS-TEST-Scene2"); +pub const TEST_SCENE_RENAME: SceneId<'_> = SceneId::Name("OBWS-TEST-Scene-Renamed"); +pub const TEST_SCENE_CREATE: SceneId<'_> = SceneId::Name("OBWS-TEST-Scene-Created"); +pub const TEST_TEXT: InputId<'_> = InputId::Name("OBWS-TEST-Text"); +pub const TEST_TEXT_2: InputId<'_> = InputId::Name("OBWS-TEST-Text2"); +pub const TEST_BROWSER: InputId<'_> = InputId::Name("OBWS-TEST-Browser"); +pub const TEST_BROWSER_RENAME: InputId<'_> = InputId::Name("OBWS-TEST-Browser-Renamed"); +pub const TEST_MEDIA: InputId<'_> = InputId::Name("OBWS-TEST-Media"); +pub const TEST_GROUP: SceneId<'_> = SceneId::Name("OBWS-TEST-Group"); pub const TEST_TRANSITION: &str = "OBWS-TEST-Transition"; pub const TEST_FILTER: &str = "OBWS-TEST-Filter"; pub const TEST_FILTER_2: &str = "OBWS-TEST-Filter2"; @@ -84,7 +85,7 @@ async fn ensure_obs_setup(client: &Client) -> Result<()> { ensure!( inputs.iter().any(is_required_text_2_input), "text input `{}` not found, required for inputs tests", - TEST_TEXT + TEST_TEXT_2 ); ensure!( inputs.iter().any(is_required_browser_input), @@ -102,7 +103,7 @@ async fn ensure_obs_setup(client: &Client) -> Result<()> { TEST_BROWSER_RENAME ); - let filters = client.filters().list(TEST_TEXT).await?; + let filters = client.filters().list(TEST_TEXT.as_source()).await?; ensure!( filters.iter().any(is_required_filter), "filter `{}` not found, required for filters tests", @@ -179,23 +180,23 @@ fn is_required_group(group: &str) -> bool { } fn is_required_text_input(input: &Input) -> bool { - input.name == TEST_TEXT && is_text_input(input) + input.id == TEST_TEXT && is_text_input(input) } fn is_required_text_2_input(input: &Input) -> bool { - input.name == TEST_TEXT_2 && is_text_input(input) + input.id == TEST_TEXT_2 && is_text_input(input) } fn is_required_browser_input(input: &Input) -> bool { - input.name == TEST_BROWSER && is_browser_input(input) + input.id == TEST_BROWSER && is_browser_input(input) } fn is_required_media_input(input: &Input) -> bool { - input.name == TEST_MEDIA && is_media_input(input) + input.id == TEST_MEDIA && is_media_input(input) } fn is_renamed_input(input: &Input) -> bool { - input.name == TEST_BROWSER_RENAME + input.id == TEST_BROWSER_RENAME } fn is_text_input(input: &Input) -> bool { diff --git a/tests/integration/filters.rs b/tests/integration/filters.rs index 70ca841..3e75802 100644 --- a/tests/integration/filters.rs +++ b/tests/integration/filters.rs @@ -10,48 +10,48 @@ async fn filters() -> Result<()> { let client = common::new_client().await?; let client = client.filters(); - client.list(TEST_TEXT).await?; + client.list(TEST_TEXT.as_source()).await?; client .default_settings::(FILTER_COLOR) .await?; client .create(Create { - source: TEST_TEXT, + source: TEST_TEXT.as_source(), filter: TEST_FILTER_2, kind: FILTER_COLOR, settings: Some(serde_json::Map::new()), }) .await?; - client.remove(TEST_TEXT, TEST_FILTER_2).await?; + client.remove(TEST_TEXT.as_source(), TEST_FILTER_2).await?; client .set_name(SetName { - source: TEST_TEXT, + source: TEST_TEXT.as_source(), filter: TEST_FILTER, new_name: TEST_FILTER_RENAME, }) .await?; client .set_name(SetName { - source: TEST_TEXT, + source: TEST_TEXT.as_source(), filter: TEST_FILTER_RENAME, new_name: TEST_FILTER, }) .await?; - client.get(TEST_TEXT, TEST_FILTER).await?; + client.get(TEST_TEXT.as_source(), TEST_FILTER).await?; client .set_index(SetIndex { - source: TEST_TEXT, + source: TEST_TEXT.as_source(), filter: TEST_FILTER, index: 0, }) .await?; client .set_settings(SetSettings { - source: TEST_TEXT, + source: TEST_TEXT.as_source(), filter: TEST_FILTER, settings: serde_json::Map::new(), overlay: Some(true), @@ -59,14 +59,14 @@ async fn filters() -> Result<()> { .await?; client .set_enabled(SetEnabled { - source: TEST_TEXT, + source: TEST_TEXT.as_source(), filter: TEST_FILTER, enabled: false, }) .await?; client .set_enabled(SetEnabled { - source: TEST_TEXT, + source: TEST_TEXT.as_source(), filter: TEST_FILTER, enabled: true, }) diff --git a/tests/integration/hotkeys.rs b/tests/integration/hotkeys.rs index 51ca251..1a3428b 100644 --- a/tests/integration/hotkeys.rs +++ b/tests/integration/hotkeys.rs @@ -9,7 +9,7 @@ async fn hotkeys() -> Result<()> { let client = client.hotkeys(); client.list().await?; - client.trigger_by_name("ReplayBuffer.Save").await?; + client.trigger_by_name("ReplayBuffer.Save", None).await?; client .trigger_by_sequence("OBS_KEY_P", KeyModifiers::default()) .await?; diff --git a/tests/integration/inputs.rs b/tests/integration/inputs.rs index c540381..cb80bec 100644 --- a/tests/integration/inputs.rs +++ b/tests/integration/inputs.rs @@ -42,8 +42,12 @@ async fn inputs() -> Result<()> { .set_volume(TEST_MEDIA, Volume::Mul(volume.mul)) .await?; - client.set_name(TEST_BROWSER, TEST_BROWSER_RENAME).await?; - client.set_name(TEST_BROWSER_RENAME, TEST_BROWSER).await?; + client + .set_name(TEST_BROWSER, TEST_BROWSER_RENAME.as_name().unwrap()) + .await?; + client + .set_name(TEST_BROWSER_RENAME, TEST_BROWSER.as_name().unwrap()) + .await?; let balance = client.audio_balance(TEST_MEDIA).await?; client.set_audio_balance(TEST_MEDIA, balance / 2.0).await?; diff --git a/tests/integration/outputs.rs b/tests/integration/outputs.rs index 4b03db8..133ffec 100644 --- a/tests/integration/outputs.rs +++ b/tests/integration/outputs.rs @@ -3,60 +3,29 @@ use std::time::Duration; use anyhow::Result; -use obws::events::{Event, OutputState}; use tokio::time; -use crate::{common, wait_for}; +use crate::common; const OUTPUT_VIRTUALCAM: &str = "virtualcam_output"; #[tokio::test] async fn outputs() -> Result<()> { let client = common::new_client().await?; - let events = client.events()?; let client = client.outputs(); - tokio::pin!(events); - + time::sleep(Duration::from_secs(1)).await; client.list().await?; client.status(OUTPUT_VIRTUALCAM).await?; client.toggle(OUTPUT_VIRTUALCAM).await?; - wait_for!( - events, - Event::VirtualcamStateChanged { - state: OutputState::Started, - .. - } - ); time::sleep(Duration::from_secs(1)).await; client.toggle(OUTPUT_VIRTUALCAM).await?; - wait_for!( - events, - Event::VirtualcamStateChanged { - state: OutputState::Stopped, - .. - } - ); time::sleep(Duration::from_secs(1)).await; + client.start(OUTPUT_VIRTUALCAM).await?; - wait_for!( - events, - Event::VirtualcamStateChanged { - state: OutputState::Started, - .. - } - ); time::sleep(Duration::from_secs(1)).await; client.stop(OUTPUT_VIRTUALCAM).await?; - wait_for!( - events, - Event::VirtualcamStateChanged { - state: OutputState::Stopped, - .. - } - ); - time::sleep(Duration::from_secs(1)).await; let settings = client .settings::(OUTPUT_VIRTUALCAM) diff --git a/tests/integration/scene_items.rs b/tests/integration/scene_items.rs index 5331c60..2223faa 100644 --- a/tests/integration/scene_items.rs +++ b/tests/integration/scene_items.rs @@ -20,7 +20,7 @@ async fn scene_items() -> Result<()> { let test_text_id = client .id(Id { scene: TEST_SCENE, - source: TEST_TEXT, + source: TEST_TEXT.as_name().unwrap(), search_offset: None, }) .await?; @@ -29,7 +29,7 @@ async fn scene_items() -> Result<()> { .duplicate(Duplicate { scene: TEST_SCENE, item_id: test_text_id, - destination: Some(TEST_SCENE_2), + destination: Some(TEST_SCENE_2.into()), }) .await?; client.remove(TEST_SCENE_2, id).await?; @@ -37,7 +37,7 @@ async fn scene_items() -> Result<()> { let id = client .create(CreateSceneItem { scene: TEST_SCENE_2, - source: TEST_TEXT, + source: TEST_TEXT.as_source(), enabled: Some(true), }) .await?; diff --git a/tests/integration/scenes.rs b/tests/integration/scenes.rs index 4a45a42..9ec10e1 100644 --- a/tests/integration/scenes.rs +++ b/tests/integration/scenes.rs @@ -16,19 +16,27 @@ async fn scenes() -> Result<()> { client.list_groups().await?; let current = client.current_program_scene().await?; - let other = &scenes.iter().find(|s| s.name != current).unwrap().name; - client.set_current_program_scene(other).await?; - client.set_current_program_scene(¤t).await?; + let other = &scenes.iter().find(|s| s.name != current.id).unwrap().name; + client.set_current_program_scene(other.as_str()).await?; + client + .set_current_program_scene(current.id.name.as_str()) + .await?; let current = client.current_preview_scene().await?; - let other = &scenes.iter().find(|s| s.name != current).unwrap().name; - client.set_current_preview_scene(other).await?; - client.set_current_preview_scene(¤t).await?; + let other = &scenes.iter().find(|s| s.name != current.id).unwrap().name; + client.set_current_preview_scene(other.as_str()).await?; + client + .set_current_preview_scene(current.id.name.as_str()) + .await?; - client.set_name(TEST_SCENE, TEST_SCENE_RENAME).await?; - client.set_name(TEST_SCENE_RENAME, TEST_SCENE).await?; + client + .set_name(TEST_SCENE, TEST_SCENE_RENAME.as_name().unwrap()) + .await?; + client + .set_name(TEST_SCENE_RENAME, TEST_SCENE.as_name().unwrap()) + .await?; - client.create(TEST_SCENE_CREATE).await?; + client.create(TEST_SCENE_CREATE.as_name().unwrap()).await?; client.remove(TEST_SCENE_CREATE).await?; let to = client.transition_override(TEST_SCENE).await?; diff --git a/tests/integration/sources.rs b/tests/integration/sources.rs index b478e61..e776f90 100644 --- a/tests/integration/sources.rs +++ b/tests/integration/sources.rs @@ -10,10 +10,10 @@ async fn sources() -> Result<()> { let client = common::new_client().await?; let client = client.sources(); - client.active(TEST_TEXT).await?; + client.active(TEST_TEXT.as_source()).await?; client .take_screenshot(TakeScreenshot { - source: TEST_TEXT, + source: TEST_TEXT.as_source(), width: Some(100), height: Some(100), compression_quality: Some(50), @@ -24,7 +24,7 @@ async fn sources() -> Result<()> { let file = env::temp_dir().join("obws-test-image.png"); client .save_screenshot(SaveScreenshot { - source: TEST_TEXT, + source: TEST_TEXT.as_source(), file_path: &file, width: None, height: None, diff --git a/tests/integration/ui.rs b/tests/integration/ui.rs index 3f07367..9cec5d9 100644 --- a/tests/integration/ui.rs +++ b/tests/integration/ui.rs @@ -31,7 +31,7 @@ async fn ui() -> Result<()> { .await?; client .open_source_projector(OpenSourceProjector { - source: TEST_TEXT, + source: TEST_TEXT.as_source(), location: Some(Location::MonitorIndex(-1)), }) .await?;