Implement transition commands

This commit is contained in:
Dominik Nakamura 2022-02-16 15:46:13 +09:00
parent a2e189b355
commit 7dc39a98ef
No known key found for this signature in database
GPG Key ID: E4C6A749B2491910
7 changed files with 260 additions and 2 deletions

View File

@ -31,7 +31,7 @@ use tracing::{debug, error, trace};
pub use self::{
config::Config, general::General, inputs::Inputs, media_inputs::MediaInputs, outputs::Outputs,
recording::Recording, scene_items::SceneItems, scenes::Scenes, sources::Sources,
streaming::Streaming,
streaming::Streaming, transitions::Transitions,
};
#[cfg(feature = "events")]
use crate::events::Event;
@ -51,6 +51,7 @@ mod scene_items;
mod scenes;
mod sources;
mod streaming;
mod transitions;
#[derive(Debug, thiserror::Error)]
enum InnerError {
@ -442,6 +443,11 @@ impl Client {
pub fn streaming(&self) -> Streaming<'_> {
Streaming { client: self }
}
/// Access API functions related to transitions.
pub fn transitions(&self) -> Transitions<'_> {
Transitions { client: self }
}
}
impl Drop for Client {

View File

@ -7,7 +7,7 @@ pub struct Streaming<'a> {
}
impl<'a> Streaming<'a> {
/// Gets the status of the stream output..
/// Gets the status of the stream output.
pub async fn get_stream_status(&self) -> Result<responses::StreamStatus> {
self.client.send_message(RequestType::GetStreamStatus).await
}

115
src/client/transitions.rs Normal file
View File

@ -0,0 +1,115 @@
use serde::Serialize;
use time::Duration;
use super::Client;
use crate::{requests::RequestType, responses, Error, Result};
/// API functions related to transitions.
pub struct Transitions<'a> {
pub(super) client: &'a Client,
}
impl<'a> Transitions<'a> {
/// Gets an array of all available transition kinds.
pub async fn get_transition_kind_list(&self) -> Result<Vec<String>> {
self.client
.send_message::<responses::TransitionKinds>(RequestType::GetTransitionKindList)
.await
.map(|tk| tk.transition_kinds)
}
/// Gets an array of all scene transitions in OBS.
pub async fn get_scene_transition_list(&self) -> Result<responses::SceneTransitionList> {
self.client
.send_message(RequestType::GetSceneTransitionList)
.await
}
/// Gets information about the current scene transition.
pub async fn get_current_scene_transition(&self) -> Result<responses::CurrentSceneTransition> {
self.client
.send_message(RequestType::GetCurrentSceneTransition)
.await
}
/// Sets the current scene transition.
///
/// **Small note:** While the namespace of scene transitions is generally unique, that
/// uniqueness is not a guarantee as it is with other resources like inputs.
///
/// - `transition_name`: Name of the transition to make active.
pub async fn set_current_scene_transition(&self, transition_name: &str) -> Result<()> {
self.client
.send_message(RequestType::SetCurrentSceneTransition { transition_name })
.await
}
/// Sets the duration of the current scene transition, if it is not fixed.
///
/// - `transition_duration`: Duration in milliseconds.
pub async fn set_current_scene_transition_duration(
&self,
transition_duration: Duration,
) -> Result<()> {
self.client
.send_message(RequestType::SetCurrentSceneTransitionDuration {
transition_duration,
})
.await
}
/// Sets the settings of the current scene transition.
///
/// - `transition_settings`: Settings object to apply to the transition.
/// - `overlay`: Whether to overlay over the current settings or replace them.
pub async fn set_current_scene_transition_settings<T>(
&self,
transition_settings: T,
overlay: Option<bool>,
) -> Result<()>
where
T: Serialize,
{
self.client
.send_message(RequestType::SetCurrentSceneTransitionSettings {
transition_settings: serde_json::to_value(&transition_settings)
.map_err(Error::SerializeCustomData)?,
overlay,
})
.await
}
/// Gets the cursor position of the current scene transition.
///
/// **Note:** `transitionCursor` will return `1.0` when the transition is inactive.
pub async fn get_current_scene_transition_cursor(&self) -> Result<f32> {
self.client
.send_message::<responses::TransitionCursor>(
RequestType::GetCurrentSceneTransitionCursor,
)
.await
.map(|tc| tc.transition_cursor)
}
/// Triggers the current scene transition. Same functionality as the `Transition` button in
/// studio mode.
pub async fn trigger_studio_mode_transition(&self) -> Result<()> {
self.client
.send_message(RequestType::TriggerStudioModeTransition)
.await
}
/// Sets the position of the TBar.
///
/// **Very important note:** This will be deprecated and replaced in a future version of
/// `obs-websocket`.
///
/// - `position`: New position.
/// - `release`: Whether to release the TBar. Only set `false` if you know that you will be
/// sending another position update.
pub async fn set_tbar_position(&self, position: f32, release: Option<bool>) -> Result<()> {
self.client
.send_message(RequestType::SetTbarPosition { position, release })
.await
}
}

View File

@ -288,6 +288,7 @@ pub(crate) enum RequestType<'a> {
#[serde(rename_all = "camelCase")]
GetInputList {
/// Restrict the array to only inputs of the specified kind.
#[serde(skip_serializing_if = "Option::is_none")]
input_kind: Option<&'a str>,
},
GetInputKindList {
@ -559,6 +560,42 @@ pub(crate) enum RequestType<'a> {
/// Caption text.
caption_text: &'a str,
},
// --------------------------------
// Transitions
// --------------------------------
GetTransitionKindList,
GetSceneTransitionList,
GetCurrentSceneTransition,
#[serde(rename_all = "camelCase")]
SetCurrentSceneTransition {
/// Name of the transition to make active.
transition_name: &'a str,
},
#[serde(rename_all = "camelCase")]
SetCurrentSceneTransitionDuration {
/// Duration in milliseconds.
#[serde(serialize_with = "ser::duration_millis")]
transition_duration: Duration,
},
#[serde(rename_all = "camelCase")]
SetCurrentSceneTransitionSettings {
/// Settings object to apply to the transition.
transition_settings: serde_json::Value,
/// Whether to overlay over the current settings or replace them.
#[serde(skip_serializing_if = "Option::is_none")]
overlay: Option<bool>,
},
GetCurrentSceneTransitionCursor,
TriggerStudioModeTransition,
#[serde(rename_all = "camelCase", rename = "SetTBarPosition")]
SetTbarPosition {
/// New position.
position: f32,
/// Whether to release the TBar. Only set `false` if you know that you will be sending
/// another position update.
#[serde(skip_serializing_if = "Option::is_none")]
release: Option<bool>,
},
}
#[derive(Clone, Copy, Serialize)]
@ -580,6 +617,7 @@ pub struct SetPersistentData<'a> {
pub slot_value: &'a serde_json::Value,
}
#[skip_serializing_none]
#[derive(Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SetProfileParameter<'a> {
@ -591,6 +629,7 @@ pub struct SetProfileParameter<'a> {
pub parameter_value: Option<&'a str>,
}
#[skip_serializing_none]
#[derive(Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SetVideoSettings {
@ -663,6 +702,7 @@ pub struct SetInputSettings<'a, T> {
pub overlay: Option<bool>,
}
#[skip_serializing_none]
#[derive(Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SetInputSettingsInternal<'a> {
@ -698,6 +738,7 @@ pub struct CreateInput<'a, T> {
pub scene_item_enabled: Option<bool>,
}
#[skip_serializing_none]
#[derive(Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct CreateInputInternal<'a> {
@ -708,6 +749,7 @@ pub(crate) struct CreateInputInternal<'a> {
pub scene_item_enabled: Option<bool>,
}
#[skip_serializing_none]
#[derive(Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateSceneItem<'a> {
@ -719,6 +761,7 @@ pub struct CreateSceneItem<'a> {
pub scene_item_enabled: Option<bool>,
}
#[skip_serializing_none]
#[derive(Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DuplicateSceneItem<'a> {
@ -741,6 +784,7 @@ pub struct SetSceneItemTransform<'a> {
pub scene_item_transform: SceneItemTransform,
}
#[skip_serializing_none]
#[derive(Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SceneItemTransform {
@ -814,6 +858,7 @@ pub struct SetSceneItemIndex<'a> {
pub scene_item_index: u32,
}
#[skip_serializing_none]
#[derive(Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SetSceneSceneTransitionOverride<'a> {
@ -826,6 +871,7 @@ pub struct SetSceneSceneTransitionOverride<'a> {
pub transition_duration: Option<Duration>,
}
#[skip_serializing_none]
#[derive(Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetSourceScreenshot<'a> {
@ -844,6 +890,7 @@ pub struct GetSourceScreenshot<'a> {
pub image_compression_quality: Option<i32>,
}
#[skip_serializing_none]
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SaveSourceScreenshot<'a> {

View File

@ -668,3 +668,59 @@ pub struct StreamStatus {
/// Total number of frames delivered by the output's process.
pub output_total_frames: u32,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct TransitionKinds {
/// Array of transition kinds.
pub transition_kinds: Vec<String>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SceneTransitionList {
/// Name of the current scene transition.
pub current_scene_transition_name: Option<String>,
/// Kind of the current scene transition.
pub current_scene_transition_kind: Option<String>,
/// Array of transitions.
pub transitions: Vec<Transition>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Transition {
/// Name of the transition.
pub transition_name: String,
/// Kind of the transition.
pub transition_kind: String,
/// Whether the transition uses a fixed (unconfigurable) duration.
pub transition_fixed: bool,
/// Whether the transition supports being configured.
pub transition_configurable: bool,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CurrentSceneTransition {
/// Name of the transition.
pub transition_name: String,
/// Kind of the transition.
pub transition_kind: String,
/// Whether the transition uses a fixed (unconfigurable) duration.
pub transition_fixed: bool,
/// Configured transition duration in milliseconds.
#[serde(deserialize_with = "crate::de::duration_millis_opt")]
pub transition_duration: Option<Duration>,
/// Whether the transition supports being configured.
pub transition_configurable: bool,
/// Object of settings for the transition.
pub transition_settings: Option<serde_json::Value>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct TransitionCursor {
/// Cursor position, between `0.0` and `1.0`.
pub transition_cursor: f32,
}

View File

@ -11,3 +11,4 @@ mod scene_items;
mod scenes;
mod sources;
mod streaming;
mod transitions;

View File

@ -0,0 +1,33 @@
use anyhow::Result;
use crate::common::{self, TEST_TRANSITION};
#[tokio::test]
async fn transitions() -> Result<()> {
let client = common::new_client().await?;
let general = client.general();
let client = client.transitions();
client.get_transition_kind_list().await?;
client.get_scene_transition_list().await?;
client.set_current_scene_transition(TEST_TRANSITION).await?;
let transition = client.get_current_scene_transition().await?;
client
.set_current_scene_transition_duration(transition.transition_duration.unwrap())
.await?;
client
.set_current_scene_transition_settings(transition.transition_settings.unwrap(), None)
.await?;
client.get_current_scene_transition_cursor().await?;
general.set_studio_mode_enabled(true).await?;
client.trigger_studio_mode_transition().await?;
client.set_tbar_position(0.5, None).await?;
general.set_studio_mode_enabled(false).await?;
Ok(())
}