From c085555bce0f6346b987a2b0f28c72e22882b238 Mon Sep 17 00:00:00 2001 From: Arnei Date: Tue, 30 Sep 2025 16:52:36 +0200 Subject: [PATCH 1/4] Extended CA API - Add field stream Adds a new field "stream" to the possible capture agent settings as an example. Shows you all the files you need to touch to get this done. --- .../EventDetailsSchedulingTab.tsx | 61 +++++++++++++- .../ModalTabsAndPages/NewSourcePage.tsx | 54 +++++++++++- .../wizards/scheduling/SchedulingInputs.tsx | 4 +- .../wizards/scheduling/SchedulingRadio.tsx | 35 ++++++++ .../adminui/languages/lang-en_US.json | 18 ++-- src/slices/eventDetailsSlice.ts | 84 ++++++++++++------- src/slices/eventSlice.ts | 3 + src/slices/recordingDetailsSlice.ts | 14 +++- src/slices/recordingSlice.ts | 18 +++- 9 files changed, 239 insertions(+), 52 deletions(-) create mode 100644 src/components/events/partials/wizards/scheduling/SchedulingRadio.tsx diff --git a/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx b/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx index 005301990a..ddcad6de55 100644 --- a/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx +++ b/src/components/events/partials/ModalTabsAndPages/EventDetailsSchedulingTab.tsx @@ -52,6 +52,7 @@ import SchedulingInputs from "../wizards/scheduling/SchedulingInputs"; import SchedulingConflicts from "../wizards/scheduling/SchedulingConflicts"; import { ParseKeys } from "i18next"; import ModalContentTable from "../../../shared/modals/ModalContentTable"; +import SchedulingRadio from "../wizards/scheduling/SchedulingRadio"; export type InitialValues = { scheduleStartDate: string; @@ -64,6 +65,7 @@ export type InitialValues = { scheduleEndMinute: string; captureAgent: string; inputs: string[]; + stream: string; } /** @@ -124,11 +126,24 @@ const EventDetailsSchedulingTab = ({ // finds the inputs to be displayed in the formik const getInputs = (deviceId: Recording["id"]) => { if (deviceId === source.device.id) { - return source.device.inputs ? source.device.inputs : []; + return source.device.parsedCapabilities.inputs ? source.device.parsedCapabilities.inputs : []; } else { for (const agent of filterDevicesForAccess(user, captureAgents)) { if (agent.id === deviceId) { - return agent.inputs ? agent.inputs : []; + return agent.parsedCapabilities.inputs ? agent.parsedCapabilities.inputs : []; + } + } + return []; + } + }; + + const getStream = (deviceId: Recording["id"]) => { + if (deviceId === source.device.id) { + return source.device.parsedCapabilities.stream ? source.device.parsedCapabilities.stream : []; + } else { + for (const agent of filterDevicesForAccess(user, captureAgents)) { + if (agent.id === deviceId) { + return agent.parsedCapabilities.stream ? agent.parsedCapabilities.stream : []; } } return []; @@ -141,6 +156,12 @@ const EventDetailsSchedulingTab = ({ return value ? t(value as ParseKeys) : ""; }; + const getStreamForAgent = (deviceId: Recording["id"], s: string) => { + const stream = getStream(deviceId); + const value = stream.find(agent => agent.id === s)?.value; + return value ? t(value as ParseKeys) : ""; + }; + // changes the inputs in the formik const changeInputs = (deviceId: Recording["id"], setFieldValue: (field: string, value: any) => Promise>) => { setFieldValue("captureAgent", deviceId); @@ -211,9 +232,12 @@ const EventDetailsSchedulingTab = ({ const startDate = new Date(source.start.date); const endDate = new Date(source.end.date); - const inputs = source.device.inputMethods - ? Array.from(source.device.inputMethods) + const inputs = source.device.capabilitiesMethods.inputs + ? Array.from(source.device.capabilitiesMethods.inputs) : []; + const stream = source.device.capabilitiesMethods.stream && source.device.capabilitiesMethods.stream.length > 0 + ? source.device.capabilitiesMethods.stream[0] + : ""; startDate.setHours(0, 0, 0); endDate.setHours(0, 0, 0); @@ -229,6 +253,7 @@ const EventDetailsSchedulingTab = ({ scheduleEndMinute: source.end.minute != null ? makeTwoDigits(source.end.minute) : "", captureAgent: source.device.name, inputs: inputs.filter(input => input !== ""), + stream: stream, }; }; @@ -504,6 +529,7 @@ const EventDetailsSchedulingTab = ({ (hasAccessRole && accessAllowed(formik.values.captureAgent) ? : formik.values.inputs.map((input, key) => ( @@ -514,6 +540,33 @@ const EventDetailsSchedulingTab = ({ )))} + + {/* stream */} + + + {t( + "EVENTS.EVENTS.DETAILS.SOURCE.PLACEHOLDER.STREAM", + )} + + + {!!formik.values.captureAgent && + !!getStream(formik.values.captureAgent) && + getStream(formik.values.captureAgent).length > + 0 && + (hasAccessRole && + accessAllowed(formik.values.captureAgent) + ? + : + + {getStreamForAgent(formik.values.captureAgent, formik.values.stream)} +
+
+ )} + + diff --git a/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx b/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx index 2bfedc4ae3..c04369da90 100644 --- a/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx +++ b/src/components/events/partials/ModalTabsAndPages/NewSourcePage.tsx @@ -49,6 +49,7 @@ import SchedulingInputs from "../wizards/scheduling/SchedulingInputs"; import SchedulingConflicts from "../wizards/scheduling/SchedulingConflicts"; import { ParseKeys } from "i18next"; import { LuCircleX } from "react-icons/lu"; +import SchedulingRadio from "../wizards/scheduling/SchedulingRadio"; /** * This component renders the source page for new events in the new event wizard. @@ -434,9 +435,32 @@ const Schedule = ; } return ( - + <> + + + ); + } + }; + + const renderStreamDeviceOptions = () => { + if (formik.values.location) { + const inputDevice = inputDevices.find( + ({ name }) => name === formik.values.location, + ); + if (!inputDevice) { + return <>; + } + return ( + <> + + ); } }; @@ -671,6 +695,24 @@ const Schedule = { formik.setFieldValue("location", value); + // Reset location specific fields + const inputDevice = inputDevices.find( + ({ name }) => name === value, + ); + if (inputDevice) { + if (inputDevice.parsedCapabilities.stream) { + formik.setFieldValue("inputs", []); + } + if (inputDevice.parsedCapabilities.stream) { + if (inputDevice.parsedCapabilities.stream.find(item => item.id === "0")) { + formik.setFieldValue("stream", 0); + } else if (inputDevice.parsedCapabilities.stream.length === 1) { + formik.setFieldValue("stream", inputDevice.parsedCapabilities.stream[0].id); + } else { + formik.setFieldValue("stream", ""); + } + } + } }} /> @@ -680,6 +722,12 @@ const Schedule = + + {t("EVENTS.EVENTS.NEW.SOURCE.PLACEHOLDER.STREAM")} + + {renderStreamDeviceOptions()} + + diff --git a/src/components/events/partials/wizards/scheduling/SchedulingInputs.tsx b/src/components/events/partials/wizards/scheduling/SchedulingInputs.tsx index 17bf33c7dc..14cebd858d 100644 --- a/src/components/events/partials/wizards/scheduling/SchedulingInputs.tsx +++ b/src/components/events/partials/wizards/scheduling/SchedulingInputs.tsx @@ -3,8 +3,10 @@ import { Field } from "formik"; import { ParseKeys } from "i18next"; const SchedulingInputs = ({ + name, inputs, }: { + name: string, inputs: { id: string, value: string @@ -18,7 +20,7 @@ const SchedulingInputs = ({ (input, key) => (