@norskvideo/norsk-studio-built-ins 1.12.0-2025-01-27-b21c8c91 → 1.12.0-2025-01-28-ffc8b619
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/info.js +155 -37
- package/client/output.whep/styles.css +2 -2
- package/client/style.css +19 -0
- package/lib/input.rtmp/summary-view.js +1 -1
- package/lib/input.rtmp/summary-view.js.map +1 -1
- package/lib/{input.srt-listener/openApi.js → input.rtmp/types.js} +1 -1
- package/lib/input.rtmp/types.js.map +1 -0
- package/lib/input.srt-listener/info.js +7 -1
- package/lib/input.srt-listener/info.js.map +1 -1
- package/lib/input.srt-listener/runtime.d.ts +18 -2
- package/lib/input.srt-listener/runtime.js +74 -5
- package/lib/input.srt-listener/runtime.js.map +1 -1
- package/lib/input.srt-listener/summary-view.js +59 -7
- package/lib/input.srt-listener/summary-view.js.map +1 -1
- package/lib/input.srt-listener/{openApi.d.ts → types.d.ts} +61 -1
- package/lib/{input.rtmp/openApi.js → input.srt-listener/types.js} +1 -1
- package/lib/input.srt-listener/types.js.map +1 -0
- package/lib/input.srt-listener/types.yaml +54 -8
- package/lib/output.preview/info.js +1 -0
- package/lib/output.preview/info.js.map +1 -1
- package/lib/output.preview/inline-view.js +36 -6
- package/lib/output.preview/inline-view.js.map +1 -1
- package/lib/output.preview/runtime.d.ts +1 -0
- package/lib/output.preview/runtime.js.map +1 -1
- package/lib/output.whep/info.js +2 -1
- package/lib/output.whep/info.js.map +1 -1
- package/lib/output.whep/inline-view.js +40 -7
- package/lib/output.whep/inline-view.js.map +1 -1
- package/lib/output.whep/runtime.d.ts +1 -0
- package/lib/output.whep/runtime.js.map +1 -1
- package/lib/processor.browserOverlay/runtime.js.map +1 -1
- package/lib/processor.browserOverlay/{openApi.js → types.js} +1 -1
- package/lib/processor.browserOverlay/types.js.map +1 -0
- package/lib/processor.onscreenGraphic/runtime.js.map +1 -1
- package/lib/processor.onscreenGraphic/summary-view.js +5 -5
- package/lib/processor.onscreenGraphic/summary-view.js.map +1 -1
- package/package.json +4 -4
- package/lib/input.rtmp/openApi.js.map +0 -1
- package/lib/input.srt-listener/openApi.js.map +0 -1
- package/lib/processor.browserOverlay/openApi.js.map +0 -1
- /package/lib/input.rtmp/{openApi.d.ts → types.d.ts} +0 -0
- /package/lib/processor.browserOverlay/{openApi.d.ts → types.d.ts} +0 -0
package/client/info.js
CHANGED
@@ -90,7 +90,7 @@ function SummaryView({ state, config, urls, sendCommand }) {
|
|
90
90
|
};
|
91
91
|
const disconnectStream = async (streamName) => {
|
92
92
|
try {
|
93
|
-
const response = await fetch(`${urls.
|
93
|
+
const response = await fetch(`${urls.staticUrl}/disconnect`, {
|
94
94
|
method: "POST",
|
95
95
|
headers: {
|
96
96
|
"Content-Type": "application/json"
|
@@ -169,9 +169,9 @@ __export(summary_view_exports2, {
|
|
169
169
|
function SummaryView3({ state, config, urls, sendCommand }) {
|
170
170
|
const connectedSources = [];
|
171
171
|
const disconnectedSources = [];
|
172
|
-
const
|
172
|
+
const resetStream = async (streamId) => {
|
173
173
|
try {
|
174
|
-
const response = await fetch(`${urls.
|
174
|
+
const response = await fetch(`${urls.instanceUrl}/disconnect`, {
|
175
175
|
method: "POST",
|
176
176
|
headers: {
|
177
177
|
"Content-Type": "application/json"
|
@@ -182,17 +182,64 @@ function SummaryView3({ state, config, urls, sendCommand }) {
|
|
182
182
|
console.error("Stream failed to disconnect");
|
183
183
|
}
|
184
184
|
sendCommand({
|
185
|
-
type: "
|
185
|
+
type: "reset-source",
|
186
186
|
streamId
|
187
187
|
});
|
188
188
|
} catch (error) {
|
189
189
|
console.error("Failed to disconnect stream:", error);
|
190
190
|
}
|
191
191
|
};
|
192
|
-
const
|
193
|
-
|
192
|
+
const disableStream = async (streamId) => {
|
193
|
+
try {
|
194
|
+
const response = await fetch(`${urls.instanceUrl}/disable`, {
|
195
|
+
method: "POST",
|
196
|
+
headers: {
|
197
|
+
"Content-Type": "application/json"
|
198
|
+
},
|
199
|
+
body: JSON.stringify({ streamId })
|
200
|
+
});
|
201
|
+
if (!response.ok) {
|
202
|
+
console.error("Failed to disable stream");
|
203
|
+
}
|
204
|
+
sendCommand({
|
205
|
+
type: "reset-source",
|
206
|
+
streamId
|
207
|
+
});
|
208
|
+
} catch (error) {
|
209
|
+
console.error("Failed to disable stream:", error);
|
210
|
+
}
|
211
|
+
};
|
212
|
+
const enableStream = async (streamId) => {
|
213
|
+
try {
|
214
|
+
const response = await fetch(`${urls.instanceUrl}/enable`, {
|
215
|
+
method: "POST",
|
216
|
+
headers: {
|
217
|
+
"Content-Type": "application/json"
|
218
|
+
},
|
219
|
+
body: JSON.stringify({ streamId })
|
220
|
+
});
|
221
|
+
if (!response.ok) {
|
222
|
+
console.error("Failed to enable stream");
|
223
|
+
}
|
224
|
+
sendCommand({
|
225
|
+
type: "reset-source",
|
226
|
+
streamId
|
227
|
+
});
|
228
|
+
} catch (error) {
|
229
|
+
console.error("Failed to enable stream:", error);
|
230
|
+
}
|
231
|
+
};
|
232
|
+
const handleResetStream = (streamId) => {
|
233
|
+
void resetStream(streamId);
|
234
|
+
};
|
235
|
+
const handleDisableStream = (streamId) => {
|
236
|
+
void disableStream(streamId);
|
237
|
+
};
|
238
|
+
const handleEnableStream = (streamId) => {
|
239
|
+
void enableStream(streamId);
|
194
240
|
};
|
195
241
|
config.streamIds.forEach((streamId) => {
|
242
|
+
console.log(config.streamIds, state);
|
196
243
|
if (state.connectedStreams.includes(streamId)) {
|
197
244
|
connectedSources.push(streamId);
|
198
245
|
} else {
|
@@ -200,9 +247,9 @@ function SummaryView3({ state, config, urls, sendCommand }) {
|
|
200
247
|
}
|
201
248
|
});
|
202
249
|
return (0, import_jsx_runtime5.jsxs)("div", { className: "dark:text-white text-black mb-3 w-60", children: [(0, import_jsx_runtime5.jsxs)("div", { id: "srt-sources-connected", children: [(0, import_jsx_runtime5.jsx)("span", { children: "Connected Sources" }), (0, import_jsx_runtime5.jsx)("ul", { children: connectedSources.map((streamId) => {
|
203
|
-
return (0, import_jsx_runtime5.jsxs)("li", { className: "text-green-300", children: [streamId, (0, import_jsx_runtime5.jsx)("button", { onClick: () =>
|
250
|
+
return (0, import_jsx_runtime5.jsxs)("li", { className: "text-green-300", children: [streamId, (0, import_jsx_runtime5.jsx)("button", { onClick: () => handleResetStream(streamId), className: "ml-2 px-2 py-1 text-xs bg-red-600 hover:bg-red-700 text-white rounded", children: "Reset" }), (0, import_jsx_runtime5.jsx)("button", { onClick: () => handleDisableStream(streamId), className: "ml-2 px-2 py-1 text-xs bg-red-600 hover:bg-red-700 text-white rounded", children: "Disable" })] }, streamId);
|
204
251
|
}) })] }), (0, import_jsx_runtime5.jsxs)("div", { id: "srt-sources-disconnected", className: "mt-3", children: [(0, import_jsx_runtime5.jsx)("span", { children: "Disconnected Sources" }), (0, import_jsx_runtime5.jsx)("ul", { children: disconnectedSources.map((streamId) => {
|
205
|
-
return (0, import_jsx_runtime5.
|
252
|
+
return (0, import_jsx_runtime5.jsxs)("li", { className: "text-orange-300", children: [streamId, state.disabledStreams.includes(streamId) ? (0, import_jsx_runtime5.jsx)("button", { onClick: () => handleEnableStream(streamId), className: "ml-2 px-2 py-1 text-xs bg-blue-600 hover:bg-blue-700 text-white rounded", children: "Enable" }) : (0, import_jsx_runtime5.jsx)("button", { onClick: () => handleDisableStream(streamId), className: "ml-2 px-2 py-1 text-xs bg-red-600 hover:bg-red-700 text-white rounded", children: "Disable" })] }, streamId);
|
206
253
|
}) })] })] });
|
207
254
|
}
|
208
255
|
var import_jsx_runtime5, summary_view_default2;
|
@@ -539,15 +586,43 @@ __export(inline_view_exports3, {
|
|
539
586
|
function InlineView5({ state, config, raise }) {
|
540
587
|
const url = state.url;
|
541
588
|
const id = config.id;
|
542
|
-
(0, import_react6.
|
543
|
-
|
544
|
-
|
545
|
-
const
|
546
|
-
url,
|
589
|
+
const [showPreview, setShowPreview] = (0, import_react6.useState)(config.showPreview ?? true);
|
590
|
+
const [client, setClient] = (0, import_react6.useState)(null);
|
591
|
+
const createClient = (url2) => {
|
592
|
+
const client2 = new import_webrtc_client.WhepClient({
|
593
|
+
url: url2,
|
547
594
|
container: document.getElementById(`preview-${id}`) ?? void 0
|
548
595
|
});
|
549
|
-
|
550
|
-
|
596
|
+
client2.start().catch(console.error);
|
597
|
+
return client2;
|
598
|
+
};
|
599
|
+
const cleanupClient = (client2) => {
|
600
|
+
client2.outputVideoTracks.forEach((track) => track.stop());
|
601
|
+
if (client2.outputAudioTrack) {
|
602
|
+
client2.outputAudioTrack.stop();
|
603
|
+
}
|
604
|
+
client2.videoElements.forEach((video) => {
|
605
|
+
video.srcObject = null;
|
606
|
+
video.remove();
|
607
|
+
});
|
608
|
+
};
|
609
|
+
(0, import_react6.useEffect)(() => {
|
610
|
+
if (!url || !showPreview) {
|
611
|
+
if (client) {
|
612
|
+
cleanupClient(client);
|
613
|
+
setClient(null);
|
614
|
+
}
|
615
|
+
return;
|
616
|
+
}
|
617
|
+
const newClient = createClient(url);
|
618
|
+
setClient(newClient);
|
619
|
+
return () => {
|
620
|
+
cleanupClient(newClient);
|
621
|
+
};
|
622
|
+
}, [state.url, showPreview]);
|
623
|
+
(0, import_react6.useEffect)(() => {
|
624
|
+
setShowPreview(config.showPreview ?? true);
|
625
|
+
}, [config.showPreview]);
|
551
626
|
raise && (0, import_react6.useEffect)(raise, []);
|
552
627
|
if (!url)
|
553
628
|
return (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: "..." });
|
@@ -560,7 +635,9 @@ function InlineView5({ state, config, raise }) {
|
|
560
635
|
const snapped = Math.floor(capped * 10) * 10;
|
561
636
|
return Math.max(0, 100 - snapped);
|
562
637
|
}
|
563
|
-
return (0, import_jsx_runtime8.jsxs)("div", { className: "preview-outer-container", children: [(0, import_jsx_runtime8.
|
638
|
+
return (0, import_jsx_runtime8.jsxs)("div", { className: "preview-outer-container", children: [(0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, import_jsx_runtime8.jsx)("input", { type: "checkbox", id: `video-toggle-${id}`, checked: showPreview, onChange: (e) => setShowPreview(e.target.checked), className: "h-4 w-4" }), (0, import_jsx_runtime8.jsx)("label", { htmlFor: `video-toggle-${id}`, className: "text-sm", children: "Show Preview" })] }), showPreview ? (0, import_jsx_runtime8.jsx)("div", { className: "preview-video", id: `preview-${id}`, children: (0, import_jsx_runtime8.jsx)("style", { children: `
|
639
|
+
#preview-${id} video::-webkit-media-controls-play-button { display: none; },
|
640
|
+
` }) }) : (0, import_jsx_runtime8.jsx)("div", { className: "preview-video bg-black flex items-center justify-center text-white h-full", children: "Preview turned off" }), (0, import_jsx_runtime8.jsx)("div", { className: "preview-levels", children: (0, import_jsx_runtime8.jsx)("div", { className: `preview-level clip-${percentage(state.levels)}-preview` }) })] });
|
564
641
|
}
|
565
642
|
var import_jsx_runtime8, import_react6, import_webrtc_client, inline_view_default3;
|
566
643
|
var init_inline_view3 = __esm({
|
@@ -597,19 +674,19 @@ var require_util = __commonJS({
|
|
597
674
|
"../../node_modules/@norskvideo/norsk-studio/lib/shared/util.js"(exports) {
|
598
675
|
"use strict";
|
599
676
|
Object.defineProperty(exports, "__esModule", { value: true });
|
600
|
-
exports.
|
601
|
-
exports.
|
677
|
+
exports.instanceApiUrl = instanceApiUrl;
|
678
|
+
exports.staticApiUrl = staticApiUrl;
|
602
679
|
exports.findObjectByType = findObjectByType;
|
603
680
|
exports.getObjectByType = getObjectByType;
|
604
681
|
exports.getObjectByPath = getObjectByPath;
|
605
682
|
exports.displayNodeId = displayNodeId;
|
606
683
|
exports.assertUnreachable = assertUnreachable15;
|
607
684
|
exports.waitForCondition = waitForCondition;
|
608
|
-
function
|
685
|
+
function instanceApiUrl(nodeId) {
|
609
686
|
return new URL(`${document.location.protocol}//${document.location.host}/live/api/${encodeURIComponent(nodeId)}`);
|
610
687
|
}
|
611
|
-
function
|
612
|
-
return new URL(`${document.location.protocol}//${document.location.host}/components/${encodeURIComponent(
|
688
|
+
function staticApiUrl(componentType) {
|
689
|
+
return new URL(`${document.location.protocol}//${document.location.host}/components/${encodeURIComponent(componentType)}`);
|
613
690
|
}
|
614
691
|
function findObjectByType(potentials, t) {
|
615
692
|
for (const v of potentials) {
|
@@ -766,19 +843,52 @@ __export(inline_view_exports6, {
|
|
766
843
|
function InlineView8({ state, config, raise }) {
|
767
844
|
const url = state.url;
|
768
845
|
const id = config.id;
|
769
|
-
(0, import_react11.
|
770
|
-
|
771
|
-
|
772
|
-
const
|
773
|
-
url,
|
846
|
+
const [showPreview, setShowPreview] = (0, import_react11.useState)(config.showPreview ?? true);
|
847
|
+
const [client, setClient] = (0, import_react11.useState)(null);
|
848
|
+
const createClient = (url2) => {
|
849
|
+
const newClient = new import_webrtc_client2.WhepClient({
|
850
|
+
url: url2,
|
774
851
|
container: document.getElementById(`whep-${id}`) ?? void 0
|
775
852
|
});
|
776
|
-
|
777
|
-
|
853
|
+
newClient.start().catch(console.error);
|
854
|
+
return newClient;
|
855
|
+
};
|
856
|
+
const cleanupClient = (client2) => {
|
857
|
+
client2.outputVideoTracks.forEach((track) => track.stop());
|
858
|
+
if (client2.outputAudioTrack) {
|
859
|
+
client2.outputAudioTrack.stop();
|
860
|
+
}
|
861
|
+
client2.videoElements.forEach((video) => {
|
862
|
+
video.srcObject = null;
|
863
|
+
video.remove();
|
864
|
+
});
|
865
|
+
};
|
866
|
+
(0, import_react11.useEffect)(() => {
|
867
|
+
if (!url || !showPreview) {
|
868
|
+
if (client) {
|
869
|
+
cleanupClient(client);
|
870
|
+
setClient(null);
|
871
|
+
}
|
872
|
+
return;
|
873
|
+
}
|
874
|
+
const newClient = createClient(url);
|
875
|
+
setClient(newClient);
|
876
|
+
return () => {
|
877
|
+
cleanupClient(newClient);
|
878
|
+
};
|
879
|
+
}, [state.url, showPreview]);
|
880
|
+
(0, import_react11.useEffect)(() => {
|
881
|
+
setShowPreview(config.showPreview ?? true);
|
882
|
+
}, [config.showPreview]);
|
778
883
|
raise && (0, import_react11.useEffect)(raise, []);
|
779
884
|
if (!url)
|
780
885
|
return (0, import_jsx_runtime11.jsx)(import_jsx_runtime11.Fragment, { children: "..." });
|
781
|
-
|
886
|
+
const videoStyles = `
|
887
|
+
#whep-${id} video::-webkit-media-controls-play-button {
|
888
|
+
display: none;
|
889
|
+
}
|
890
|
+
`;
|
891
|
+
return (0, import_jsx_runtime11.jsxs)("div", { className: "whep-container", children: [(0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, import_jsx_runtime11.jsx)("input", { type: "checkbox", id: `video-toggle-${id}`, checked: showPreview, onChange: (e) => setShowPreview(e.target.checked), className: "h-4 w-4" }), (0, import_jsx_runtime11.jsx)("label", { htmlFor: `video-toggle-${id}`, className: "text-sm", children: "Show Preview" })] }), showPreview ? (0, import_jsx_runtime11.jsx)("div", { className: "whep-video", id: `whep-${id}`, children: (0, import_jsx_runtime11.jsx)("style", { children: videoStyles }) }) : (0, import_jsx_runtime11.jsx)("div", { className: "whep-video bg-black flex items-center justify-center text-white h-full", children: "Preview turned off" })] });
|
782
892
|
}
|
783
893
|
var import_jsx_runtime11, import_react11, import_webrtc_client2, inline_view_default6;
|
784
894
|
var init_inline_view6 = __esm({
|
@@ -1043,7 +1153,7 @@ function SummaryView7({ state, sendCommand, urls }) {
|
|
1043
1153
|
const [graphicToDelete, setGraphicToDelete] = (0, import_react19.useState)("");
|
1044
1154
|
const updateGraphics = (0, import_react19.useCallback)(async () => {
|
1045
1155
|
try {
|
1046
|
-
const result2 = await fetch(`${urls.
|
1156
|
+
const result2 = await fetch(`${urls.staticUrl}/graphics`);
|
1047
1157
|
if (result2.ok) {
|
1048
1158
|
const newGraphics = await result2.json();
|
1049
1159
|
setGraphics(newGraphics);
|
@@ -1057,7 +1167,7 @@ function SummaryView7({ state, sendCommand, urls }) {
|
|
1057
1167
|
message: "Failed to update graphic list."
|
1058
1168
|
});
|
1059
1169
|
}
|
1060
|
-
}, [urls.
|
1170
|
+
}, [urls.staticUrl]);
|
1061
1171
|
(0, import_react19.useEffect)(() => {
|
1062
1172
|
updateGraphics().catch(console.error);
|
1063
1173
|
}, [updateGraphics]);
|
@@ -1073,7 +1183,7 @@ function SummaryView7({ state, sendCommand, urls }) {
|
|
1073
1183
|
try {
|
1074
1184
|
const form = new FormData();
|
1075
1185
|
form.append("file", fileToUpload);
|
1076
|
-
const response = await fetch(`${urls.
|
1186
|
+
const response = await fetch(`${urls.staticUrl}/graphics`, {
|
1077
1187
|
method: "POST",
|
1078
1188
|
body: form
|
1079
1189
|
});
|
@@ -1107,8 +1217,8 @@ function SummaryView7({ state, sendCommand, urls }) {
|
|
1107
1217
|
if (!graphicToDelete)
|
1108
1218
|
return;
|
1109
1219
|
try {
|
1110
|
-
console.log(`${urls.
|
1111
|
-
const response = await fetch(`${urls.
|
1220
|
+
console.log(`${urls.staticUrl}`);
|
1221
|
+
const response = await fetch(`${urls.staticUrl}/graphic`, {
|
1112
1222
|
method: "DELETE",
|
1113
1223
|
headers: {
|
1114
1224
|
"Content-Type": "application/json"
|
@@ -16033,7 +16143,7 @@ function info_default4({ defineComponent, Av, validation }) {
|
|
16033
16143
|
};
|
16034
16144
|
},
|
16035
16145
|
runtime: {
|
16036
|
-
initialState: () => ({ connectedStreams: [] }),
|
16146
|
+
initialState: () => ({ connectedStreams: [], disabledStreams: [] }),
|
16037
16147
|
handleEvent(ev, state) {
|
16038
16148
|
const evType = ev.type;
|
16039
16149
|
switch (evType) {
|
@@ -16043,6 +16153,12 @@ function info_default4({ defineComponent, Av, validation }) {
|
|
16043
16153
|
case "source-disconnected":
|
16044
16154
|
state.connectedStreams = state.connectedStreams.filter((streamId) => streamId !== ev.streamId);
|
16045
16155
|
break;
|
16156
|
+
case "source-enabled":
|
16157
|
+
state.disabledStreams = state.disabledStreams.filter((streamId) => streamId !== ev.streamId);
|
16158
|
+
break;
|
16159
|
+
case "source-disabled":
|
16160
|
+
state.disabledStreams.push(ev.streamId);
|
16161
|
+
break;
|
16046
16162
|
default:
|
16047
16163
|
assertUnreachable2(evType);
|
16048
16164
|
}
|
@@ -16500,7 +16616,8 @@ function info_default8(R) {
|
|
16500
16616
|
},
|
16501
16617
|
form: {
|
16502
16618
|
bufferDelayMs: { help: "How many milliseconds in the jitter buffer", hint: { type: "numeric", validation: JitterBuffer, defaultValue: 500 } },
|
16503
|
-
skipTranscode: { help: "Skip transcoding for WebRTC-ready streams", hint: { type: "boolean", defaultValue: false } }
|
16619
|
+
skipTranscode: { help: "Skip transcoding for WebRTC-ready streams", hint: { type: "boolean", defaultValue: false } },
|
16620
|
+
showPreview: { help: "Show video preview", hint: { type: "boolean", defaultValue: true } }
|
16504
16621
|
}
|
16505
16622
|
}
|
16506
16623
|
});
|
@@ -16750,7 +16867,8 @@ function info_default13(R) {
|
|
16750
16867
|
iceServers: (0, import_config3.GlobalIceServers)(R)
|
16751
16868
|
},
|
16752
16869
|
form: {
|
16753
|
-
bufferDelayMs: { help: "How many milliseconds in the jitter buffer", hint: { type: "numeric", validation: JitterBuffer, defaultValue: 500 } }
|
16870
|
+
bufferDelayMs: { help: "How many milliseconds in the jitter buffer", hint: { type: "numeric", validation: JitterBuffer, defaultValue: 500 } },
|
16871
|
+
showPreview: { help: "Show video preview", hint: { type: "boolean", defaultValue: true } }
|
16754
16872
|
}
|
16755
16873
|
}
|
16756
16874
|
});
|
package/client/style.css
CHANGED
@@ -209,6 +209,10 @@
|
|
209
209
|
display: none;
|
210
210
|
}
|
211
211
|
|
212
|
+
.h-4 {
|
213
|
+
height: 1rem;
|
214
|
+
}
|
215
|
+
|
212
216
|
.h-6 {
|
213
217
|
height: 1.5rem;
|
214
218
|
}
|
@@ -388,6 +392,16 @@
|
|
388
392
|
background-color: rgb(249 250 251 / var(--tw-bg-opacity)) !important;
|
389
393
|
}
|
390
394
|
|
395
|
+
.bg-black {
|
396
|
+
--tw-bg-opacity: 1;
|
397
|
+
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
398
|
+
}
|
399
|
+
|
400
|
+
.bg-blue-600 {
|
401
|
+
--tw-bg-opacity: 1;
|
402
|
+
background-color: rgb(28 100 242 / var(--tw-bg-opacity));
|
403
|
+
}
|
404
|
+
|
391
405
|
.bg-gray-100 {
|
392
406
|
--tw-bg-opacity: 1;
|
393
407
|
background-color: rgb(243 244 246 / var(--tw-bg-opacity));
|
@@ -838,6 +852,11 @@
|
|
838
852
|
content: var(--tw-content);
|
839
853
|
}
|
840
854
|
|
855
|
+
.hover\:bg-blue-700:hover {
|
856
|
+
--tw-bg-opacity: 1;
|
857
|
+
background-color: rgb(26 86 219 / var(--tw-bg-opacity));
|
858
|
+
}
|
859
|
+
|
841
860
|
.hover\:bg-primary-800:hover {
|
842
861
|
--tw-bg-opacity: 1;
|
843
862
|
background-color: rgb(30 64 175 / var(--tw-bg-opacity));
|
@@ -17,7 +17,7 @@ function SummaryView({ state, config, urls, sendCommand }) {
|
|
17
17
|
};
|
18
18
|
const disconnectStream = async (streamName) => {
|
19
19
|
try {
|
20
|
-
const response = await fetch(`${urls.
|
20
|
+
const response = await fetch(`${urls.staticUrl}/disconnect`, {
|
21
21
|
method: "POST",
|
22
22
|
headers: {
|
23
23
|
"Content-Type": "application/json",
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"summary-view.js","sourceRoot":"","sources":["../../src/input.rtmp/summary-view.tsx"],"names":[],"mappings":";;;AAGA,SAAS,WAAW,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAmE;IACxH,MAAM,gBAAgB,GAAa,EAAE,CAAC;IACtC,MAAM,mBAAmB,GAAa,EAAE,CAAC;IAEzC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE;QACxC,IAAI,KAAK,CAAC,gBAAgB,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YAChD,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,mBAAmB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,sBAAsB,GAAG,CAAC,UAAkB,EAAQ,EAAE;QAC1D,KAAK,gBAAgB,CAAC,UAAU,CAAC,CAAC;IACpC,CAAC,CAAC;IAEF,MAAM,gBAAgB,GAAG,KAAK,EAAE,UAAkB,EAAE,EAAE;QACpD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,
|
1
|
+
{"version":3,"file":"summary-view.js","sourceRoot":"","sources":["../../src/input.rtmp/summary-view.tsx"],"names":[],"mappings":";;;AAGA,SAAS,WAAW,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAmE;IACxH,MAAM,gBAAgB,GAAa,EAAE,CAAC;IACtC,MAAM,mBAAmB,GAAa,EAAE,CAAC;IAEzC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE;QACxC,IAAI,KAAK,CAAC,gBAAgB,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YAChD,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,mBAAmB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,sBAAsB,GAAG,CAAC,UAAkB,EAAQ,EAAE;QAC1D,KAAK,gBAAgB,CAAC,UAAU,CAAC,CAAC;IACpC,CAAC,CAAC;IAEF,MAAM,gBAAgB,GAAG,KAAK,EAAE,UAAkB,EAAE,EAAE;QACpD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,aAAa,EAAE;gBAC3D,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;iBACnC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,CAAC;aACrC,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;YAC/C,CAAC;YAED,WAAW,CAAC;gBACV,IAAI,EAAE,mBAAmB;gBACzB,UAAU;aACX,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;QACvD,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CACL,iCAAK,SAAS,EAAC,sCAAsC,aACnD,iCAAK,EAAE,EAAC,wBAAwB,aAC9B,iEAA8B,EAC9B,yCACG,gBAAgB,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CACpC,gCAAqB,SAAS,EAAC,mCAAmC,aAChE,iCAAM,SAAS,EAAC,gBAAgB,YAAE,UAAU,GAAQ,EACpD,mCACE,OAAO,EAAG,GAAG,EAAE,CAAC,sBAAsB,CAAC,UAAU,CAAC,EAClD,SAAS,EAAC,uEAAuE,2BAG1E,KAPF,UAAU,CAQd,CACN,CAAC,GACC,IACD,EAEN,iCAAK,EAAE,EAAC,2BAA2B,EAAC,SAAS,EAAC,MAAM,aAClD,oEAAiC,EACjC,yCACG,mBAAmB,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CACvC,+BAAqB,SAAS,EAAC,mCAAmC,YAChE,iCAAM,SAAS,EAAC,iBAAiB,YAAE,UAAU,GAAQ,IAD9C,UAAU,CAEd,CACN,CAAC,GACC,IACD,IACF,CACP,CAAC;AACJ,CAAC;AAED,kBAAe,WAAW,CAAC"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/input.rtmp/types.ts"],"names":[],"mappings":""}
|
@@ -66,7 +66,7 @@ function default_1({ defineComponent, Av, validation }) {
|
|
66
66
|
};
|
67
67
|
},
|
68
68
|
runtime: {
|
69
|
-
initialState: () => ({ connectedStreams: [] }),
|
69
|
+
initialState: () => ({ connectedStreams: [], disabledStreams: [] }),
|
70
70
|
handleEvent(ev, state) {
|
71
71
|
const evType = ev.type;
|
72
72
|
switch (evType) {
|
@@ -76,6 +76,12 @@ function default_1({ defineComponent, Av, validation }) {
|
|
76
76
|
case "source-disconnected":
|
77
77
|
state.connectedStreams = state.connectedStreams.filter((streamId) => streamId !== ev.streamId);
|
78
78
|
break;
|
79
|
+
case "source-enabled":
|
80
|
+
state.disabledStreams = state.disabledStreams.filter((streamId) => streamId !== ev.streamId);
|
81
|
+
break;
|
82
|
+
case "source-disabled":
|
83
|
+
state.disabledStreams.push(ev.streamId);
|
84
|
+
break;
|
79
85
|
default:
|
80
86
|
assertUnreachable(evType);
|
81
87
|
}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"info.js","sourceRoot":"","sources":["../../src/input.srt-listener/info.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOA,
|
1
|
+
{"version":3,"file":"info.js","sourceRoot":"","sources":["../../src/input.srt-listener/info.ts"],"names":[],"mappings":"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"}
|
@@ -10,6 +10,7 @@ export type SrtInputSettings = Pick<SdkSettings, 'port' | 'host' | 'passphrase'>
|
|
10
10
|
};
|
11
11
|
export type SrtInputState = {
|
12
12
|
connectedStreams: string[];
|
13
|
+
disabledStreams: string[];
|
13
14
|
};
|
14
15
|
export type SrtInputEvent = {
|
15
16
|
type: "source-connected";
|
@@ -17,9 +18,21 @@ export type SrtInputEvent = {
|
|
17
18
|
} | {
|
18
19
|
type: "source-disconnected";
|
19
20
|
streamId: string;
|
21
|
+
} | {
|
22
|
+
type: "source-enabled";
|
23
|
+
streamId: string;
|
24
|
+
} | {
|
25
|
+
type: "source-disabled";
|
26
|
+
streamId: string;
|
20
27
|
};
|
21
28
|
export type SrtInputCommand = {
|
22
|
-
type: "
|
29
|
+
type: "disable-source";
|
30
|
+
streamId: string;
|
31
|
+
} | {
|
32
|
+
type: "enable-source";
|
33
|
+
streamId: string;
|
34
|
+
} | {
|
35
|
+
type: "reset-source";
|
23
36
|
streamId: string;
|
24
37
|
};
|
25
38
|
export declare class SrtInput implements CreatedMediaNode {
|
@@ -28,6 +41,7 @@ export declare class SrtInput implements CreatedMediaNode {
|
|
28
41
|
norsk: Norsk;
|
29
42
|
cfg: SrtInputSettings;
|
30
43
|
activeStreams: Map<string, number>;
|
44
|
+
disabledStreams: Set<string>;
|
31
45
|
initialised: Promise<void>;
|
32
46
|
srtServer: SrtInputNode | null;
|
33
47
|
updates: RuntimeUpdates<SrtInputState, SrtInputCommand, SrtInputEvent>;
|
@@ -35,7 +49,9 @@ export declare class SrtInput implements CreatedMediaNode {
|
|
35
49
|
static create(norsk: Norsk, cfg: SrtInputSettings, updates: RuntimeUpdates<SrtInputState, SrtInputCommand, SrtInputEvent>): Promise<SrtInput>;
|
36
50
|
constructor(norsk: Norsk, cfg: SrtInputSettings, updates: RuntimeUpdates<SrtInputState, SrtInputCommand, SrtInputEvent>);
|
37
51
|
initialise(): Promise<void>;
|
38
|
-
|
52
|
+
enableSource(streamId: string): Promise<void>;
|
53
|
+
disableSource(streamId: string): Promise<void>;
|
54
|
+
resetSource(streamId: string): Promise<void>;
|
39
55
|
}
|
40
56
|
export default class SrtInputDefinition implements ServerComponentDefinition<SrtInputSettings, SrtInput, SrtInputState, SrtInputCommand> {
|
41
57
|
create(norsk: Norsk, cfg: SrtInputSettings, cb: OnCreated<SrtInput>, runtime: StudioRuntime<SrtInputState, SrtInputCommand, SrtInputEvent>): Promise<void>;
|
@@ -17,6 +17,7 @@ class SrtInput {
|
|
17
17
|
norsk;
|
18
18
|
cfg;
|
19
19
|
activeStreams = new Map();
|
20
|
+
disabledStreams = new Set();
|
20
21
|
initialised;
|
21
22
|
srtServer = null;
|
22
23
|
updates;
|
@@ -55,6 +56,12 @@ class SrtInput {
|
|
55
56
|
}
|
56
57
|
},
|
57
58
|
onConnection: (streamId, index, remoteHost) => {
|
59
|
+
if (this.disabledStreams.has(streamId)) {
|
60
|
+
(0, logging_1.debuglog)("Rejecting connection as stream is presently disabled", { count: index, remoteHost });
|
61
|
+
return {
|
62
|
+
accept: false
|
63
|
+
};
|
64
|
+
}
|
58
65
|
if (this.cfg.sourceNames == 'permissive') {
|
59
66
|
if (this.cfg.streamIds.includes(streamId) && !this.activeStreams.has(streamId)) {
|
60
67
|
(0, logging_1.debuglog)("Accepting SRT connection", { streamId, remoteHost });
|
@@ -89,6 +96,7 @@ class SrtInput {
|
|
89
96
|
}
|
90
97
|
if (this.cfg.streamIds.includes(streamId)) {
|
91
98
|
(0, logging_1.debuglog)("Accepting SRT connection", { streamId, remoteHost });
|
99
|
+
this.updates.raiseEvent({ type: "source-connected", streamId });
|
92
100
|
this.activeStreams.set(streamId, index);
|
93
101
|
return {
|
94
102
|
accept: true,
|
@@ -108,7 +116,22 @@ class SrtInput {
|
|
108
116
|
}
|
109
117
|
});
|
110
118
|
}
|
111
|
-
async
|
119
|
+
async enableSource(streamId) {
|
120
|
+
this.disabledStreams.delete(streamId);
|
121
|
+
this.updates.raiseEvent({
|
122
|
+
type: 'source-enabled',
|
123
|
+
streamId
|
124
|
+
});
|
125
|
+
}
|
126
|
+
async disableSource(streamId) {
|
127
|
+
this.disabledStreams.add(streamId);
|
128
|
+
await this.resetSource(streamId);
|
129
|
+
this.updates.raiseEvent({
|
130
|
+
type: 'source-disabled',
|
131
|
+
streamId
|
132
|
+
});
|
133
|
+
}
|
134
|
+
async resetSource(streamId) {
|
112
135
|
if (this.srtServer) {
|
113
136
|
const index = this.activeStreams.get(streamId);
|
114
137
|
if (index !== undefined) {
|
@@ -141,8 +164,14 @@ class SrtInputDefinition {
|
|
141
164
|
async handleCommand(node, command) {
|
142
165
|
const commandType = command.type;
|
143
166
|
switch (commandType) {
|
144
|
-
case '
|
145
|
-
await node.
|
167
|
+
case 'disable-source':
|
168
|
+
await node.disableSource(command.streamId);
|
169
|
+
break;
|
170
|
+
case 'enable-source':
|
171
|
+
await node.enableSource(command.streamId);
|
172
|
+
break;
|
173
|
+
case 'reset-source':
|
174
|
+
await node.resetSource(command.streamId);
|
146
175
|
break;
|
147
176
|
default:
|
148
177
|
(0, util_1.assertUnreachable)(commandType);
|
@@ -163,12 +192,52 @@ class SrtInputDefinition {
|
|
163
192
|
return res.status(400).json({ error: 'Stream name is required' });
|
164
193
|
}
|
165
194
|
const state = runtime.updates.latest();
|
166
|
-
|
195
|
+
(0, logging_1.infolog)("Current state during disconnect:", state);
|
167
196
|
if (!state.connectedStreams.includes(streamId)) {
|
168
197
|
return res.status(404).json({ error: 'Stream not found or not connected' });
|
169
198
|
}
|
170
199
|
runtime.updates.sendCommand({
|
171
|
-
type: '
|
200
|
+
type: 'reset-source',
|
201
|
+
streamId
|
202
|
+
});
|
203
|
+
res.status(204).send();
|
204
|
+
}
|
205
|
+
catch (error) {
|
206
|
+
(0, logging_1.errorlog)('Error in disconnect handler:', error);
|
207
|
+
res.status(500).json({ error: 'Failed to disconnect stream' });
|
208
|
+
}
|
209
|
+
}
|
210
|
+
},
|
211
|
+
{
|
212
|
+
...post('/enable', paths),
|
213
|
+
handler: ({ runtime }) => async (req, res) => {
|
214
|
+
try {
|
215
|
+
const { streamId } = req.body;
|
216
|
+
if (!streamId) {
|
217
|
+
return res.status(400).json({ error: 'Stream name is required' });
|
218
|
+
}
|
219
|
+
runtime.updates.sendCommand({
|
220
|
+
type: 'enable-source',
|
221
|
+
streamId
|
222
|
+
});
|
223
|
+
res.status(204).send();
|
224
|
+
}
|
225
|
+
catch (error) {
|
226
|
+
console.error('Error in disconnect handler:', error);
|
227
|
+
res.status(500).json({ error: 'Failed to disconnect stream' });
|
228
|
+
}
|
229
|
+
}
|
230
|
+
},
|
231
|
+
{
|
232
|
+
...post('/disable', paths),
|
233
|
+
handler: ({ runtime }) => async (req, res) => {
|
234
|
+
try {
|
235
|
+
const { streamId } = req.body;
|
236
|
+
if (!streamId) {
|
237
|
+
return res.status(400).json({ error: 'Stream name is required' });
|
238
|
+
}
|
239
|
+
runtime.updates.sendCommand({
|
240
|
+
type: 'disable-source',
|
172
241
|
streamId
|
173
242
|
});
|
174
243
|
res.status(204).send();
|