Skip to main content

pulse_pixelstream_types/
shot.rs

1use myko::prelude::*;
2use myko::TS;
3use serde::{Deserialize, Serialize};
4
5use crate::ShotEntryMode;
6
7/// The shared shot library used when no production-specific library is chosen.
8pub const DEFAULT_SHOT_LIBRARY_ID: &str = "shared";
9pub const DEFAULT_SHOT_TRANSLATION_SPEED_CM_S: f32 = 10.0;
10pub const DEFAULT_SHOT_ROTATION_SPEED_DEG_S: f32 = 2.0;
11pub const DEFAULT_SHOT_HOLD_DURATION_MS: u64 = 5_000;
12/// Initial estimate for a moving shot. Unreal currently reports normalized
13/// travel position but not authored rail length, so speed alone cannot produce
14/// an honest duration. Operators can tune this persisted value per shot and the
15/// recorder refines RecordingJob overhead estimates from observed phases.
16pub const DEFAULT_SHOT_TRAVEL_DURATION_MS: u64 = 30_000;
17
18pub fn default_shot_travel_duration_ms() -> u64 {
19    DEFAULT_SHOT_TRAVEL_DURATION_MS
20}
21
22#[derive(
23    Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, TS, PartialOrd, Ord, Hash,
24)]
25#[serde(rename_all = "snake_case")]
26pub enum ShotKind {
27    #[serde(alias = "rig", alias = "rig_move", alias = "moving")]
28    Moving,
29    #[default]
30    #[serde(alias = "preset", alias = "preset_hold", alias = "stationary")]
31    Static,
32}
33
34/// One camera target reported by the live StreamCamera pawn. Discovery carries
35/// identity only; the server owns default parameters and creates a persistent
36/// Shot only when that target is not already represented in the library.
37#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, TS)]
38#[serde(rename_all = "camelCase")]
39pub struct ShotDiscovery {
40    pub name: String,
41    pub kind: ShotKind,
42    pub target_name: String,
43}
44
45/// A persistent, shared camera shot. A shot is the unit the operator
46/// names, indexes, tunes, places in timelines, and eventually records. Its kind
47/// describes whether the camera remains stationary or moves during capture.
48#[myko_macros::myko_item]
49pub struct Shot {
50    /// Shared definition scope. Empty on legacy rows and interpreted as the
51    /// default shared library.
52    #[serde(default)]
53    pub library_id: String,
54    /// Legacy provenance retained for wire and persisted-row compatibility.
55    /// Capture execution chooses a stream at job start; definitions do not.
56    #[serde(default)]
57    pub streamer_id: String,
58    pub name: String,
59    #[serde(alias = "target_kind")]
60    pub kind: ShotKind,
61    pub target_name: String,
62    pub translation_speed_cm_s: f32,
63    pub rotation_speed_deg_s: f32,
64    pub hold_duration_ms: u64,
65    #[serde(default)]
66    pub travel_duration_ms: u64,
67    /// Default used when creating a new ShotEntry. Existing entries own their
68    /// direction independently.
69    #[serde(
70        default,
71        alias = "defaultClipMode",
72        alias = "defaultListMode",
73        alias = "batchMode"
74    )]
75    pub default_entry_mode: ShotEntryMode,
76    /// Stable operator-facing label for the shot. This is persisted with the
77    /// Shot but does not determine timeline capture or resume order.
78    #[serde(rename = "sortOrder", alias = "shotIndex")]
79    pub shot_index: u32,
80}
81
82impl Shot {
83    pub fn stable_id(library_id: &str, kind: &ShotKind, target_name: &str) -> String {
84        let kind = match kind {
85            ShotKind::Moving => "moving",
86            ShotKind::Static => "static",
87        };
88        format!("{library_id}:shot:{kind}:{target_name}")
89    }
90
91    pub fn effective_library_id(&self) -> &str {
92        effective_library_id(&self.library_id)
93    }
94
95    pub fn effective_travel_duration_ms(&self) -> u64 {
96        if self.travel_duration_ms == 0 {
97            DEFAULT_SHOT_TRAVEL_DURATION_MS
98        } else {
99            self.travel_duration_ms
100        }
101    }
102}
103
104pub fn effective_library_id(library_id: &str) -> &str {
105    if library_id.trim().is_empty() {
106        DEFAULT_SHOT_LIBRARY_ID
107    } else {
108        library_id
109    }
110}