@uniai-fe/uds-templates 0.6.25 → 0.6.27
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/README.md +0 -1
- package/package.json +1 -1
- package/src/cctv/apis/client.ts +56 -2
- package/src/cctv/apis/server.ts +1 -120
- package/src/cctv/components/Provider.tsx +66 -8
- package/src/cctv/components/cam-list/Item.tsx +6 -0
- package/src/cctv/components/pagination/list/Item.tsx +20 -0
- package/src/cctv/components/video/Template.tsx +3 -0
- package/src/cctv/components/viewer/desktop/Video.tsx +1 -0
- package/src/cctv/hooks/streamScheduler.ts +276 -0
- package/src/cctv/hooks/useRtcStream.ts +218 -18
- package/src/cctv/types/api.ts +0 -94
- package/src/cctv/types/context.ts +21 -0
- package/src/cctv/types/hook.ts +24 -0
- package/src/cctv/types/props.ts +12 -1
- package/src/cctv/types/video-state.ts +18 -3
- package/src/cctv/utils/video-state.ts +3 -0
package/src/cctv/types/api.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { API_Res_Base } from "@uniai-fe/util-api";
|
|
2
|
-
import type { NextRequest } from "next/server";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* CCTV; api getCompanyList params
|
|
@@ -298,7 +297,6 @@ export type API_Res_CctvCompany = API_Res_Base<API_Res_CctvCompanyData>;
|
|
|
298
297
|
* @property {string} company_id 업체 id코드
|
|
299
298
|
* @property {string} cam_id 카메라 id코드
|
|
300
299
|
* @property {string} username 사용자 계정 아이디
|
|
301
|
-
* @property {string} [password] 사용자 계정 비밀번호. 생략 시 서버 route에서 tokenPreset.password 또는 username fallback을 사용
|
|
302
300
|
*/
|
|
303
301
|
export interface API_Req_CctvRtcToken {
|
|
304
302
|
/**
|
|
@@ -313,54 +311,6 @@ export interface API_Req_CctvRtcToken {
|
|
|
313
311
|
* 사용자 계정 아이디
|
|
314
312
|
*/
|
|
315
313
|
username: string;
|
|
316
|
-
/**
|
|
317
|
-
* 사용자 계정 비밀번호
|
|
318
|
-
* - 서버 route에서 tokenPreset.password 또는 username fallback을 사용하는 경우 생략 가능
|
|
319
|
-
*/
|
|
320
|
-
password?: string;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* CCTV; 실시간 스트리밍 토큰 요청 (백엔드 요청)
|
|
325
|
-
* @property {string} site company_id 맵핑 (ex. farm102)
|
|
326
|
-
* @property {string} username 사용자 계정 아이디
|
|
327
|
-
* @property {string} password 사용자 계정 비밀번호
|
|
328
|
-
* @property {string} action "read"
|
|
329
|
-
* @property {string} path cam_id 맵핑 (ex. cam3)
|
|
330
|
-
* @property {number} ttl_seconds 시간? (default 300)
|
|
331
|
-
* @property {string} kid "streaming-auth-1"
|
|
332
|
-
*/
|
|
333
|
-
export interface API_Req_CctvRtcTokenOrigin {
|
|
334
|
-
/**
|
|
335
|
-
* company_id 맵핑
|
|
336
|
-
* ex) farm102
|
|
337
|
-
*/
|
|
338
|
-
site: string;
|
|
339
|
-
/**
|
|
340
|
-
* 사용자 계정 아이디
|
|
341
|
-
*/
|
|
342
|
-
username: string;
|
|
343
|
-
/**
|
|
344
|
-
* 사용자 계정 비밀번호
|
|
345
|
-
*/
|
|
346
|
-
password: string;
|
|
347
|
-
/**
|
|
348
|
-
* "read"
|
|
349
|
-
*/
|
|
350
|
-
action: string;
|
|
351
|
-
/**
|
|
352
|
-
* cam_id 맵핑
|
|
353
|
-
* ex) cam3
|
|
354
|
-
*/
|
|
355
|
-
path: string;
|
|
356
|
-
/**
|
|
357
|
-
* default) 300
|
|
358
|
-
*/
|
|
359
|
-
ttl_seconds: number;
|
|
360
|
-
/**
|
|
361
|
-
* "streaming-auth-1"
|
|
362
|
-
*/
|
|
363
|
-
kid: string;
|
|
364
314
|
}
|
|
365
315
|
|
|
366
316
|
/**
|
|
@@ -411,47 +361,3 @@ export interface API_Req_CctvStreamToken {
|
|
|
411
361
|
*/
|
|
412
362
|
cam_id: string;
|
|
413
363
|
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* CCTV; 서버 토큰 요청 파라미터
|
|
417
|
-
* @property {string} domain API 요청 도메인(서버)
|
|
418
|
-
* @property {string} routeUrl Next.js app/api 이하 경로 url
|
|
419
|
-
* @property {string} [queryUrl] 백엔드 요청 url
|
|
420
|
-
* @property {string} username 사용자 아이디
|
|
421
|
-
* @property {string} company_id 업체 id코드(site)
|
|
422
|
-
* @property {string} cam_id 카메라 id코드
|
|
423
|
-
* @property {string} [password] 서버 route에서 주입할 사용자 비밀번호
|
|
424
|
-
* @property {number} [ttl_seconds] 토큰 만료시간
|
|
425
|
-
* @property {string} [kid] 키 id
|
|
426
|
-
* @property {Record<string, string>} [headers] 추가 요청 헤더
|
|
427
|
-
*/
|
|
428
|
-
export type API_Req_CctvTokenPreset = Partial<
|
|
429
|
-
Pick<API_Req_CctvRtcTokenOrigin, "password" | "ttl_seconds" | "kid">
|
|
430
|
-
>;
|
|
431
|
-
|
|
432
|
-
export interface API_Req_GetCctvTokenParams {
|
|
433
|
-
/**
|
|
434
|
-
* API 도메인
|
|
435
|
-
*/
|
|
436
|
-
domain: string;
|
|
437
|
-
/**
|
|
438
|
-
* Next.js /app/api 라우트 주소
|
|
439
|
-
*/
|
|
440
|
-
routeUrl: string;
|
|
441
|
-
/**
|
|
442
|
-
* 백엔드 요청 url
|
|
443
|
-
*/
|
|
444
|
-
queryUrl?: string;
|
|
445
|
-
/**
|
|
446
|
-
* Next.js API 요청 body
|
|
447
|
-
*/
|
|
448
|
-
req: NextRequest;
|
|
449
|
-
/**
|
|
450
|
-
* 추가 요청 헤더
|
|
451
|
-
*/
|
|
452
|
-
headers?: Record<string, string>;
|
|
453
|
-
/**
|
|
454
|
-
* 서버 route에서 주입할 CCTV 토큰 요청 preset
|
|
455
|
-
*/
|
|
456
|
-
tokenPreset?: API_Req_CctvTokenPreset;
|
|
457
|
-
}
|
|
@@ -55,6 +55,22 @@ export interface CctvApiUrlContext {
|
|
|
55
55
|
tokenUrl?: string;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* CCTV; RTC stream lazy loading 옵션
|
|
60
|
+
* @property {number} [batchSize] 한 번에 token/WHEP 시작 권한을 받을 stream 개수 (default: 4)
|
|
61
|
+
* @property {number} [batchIntervalMs] 다음 batch 시작까지 대기할 시간(ms) (default: 1200)
|
|
62
|
+
*/
|
|
63
|
+
export interface CctvRtcStreamOptions {
|
|
64
|
+
/**
|
|
65
|
+
* 한 번에 token/WHEP 시작 권한을 받을 stream 개수
|
|
66
|
+
*/
|
|
67
|
+
batchSize?: number;
|
|
68
|
+
/**
|
|
69
|
+
* 다음 batch 시작까지 대기할 시간(ms)
|
|
70
|
+
*/
|
|
71
|
+
batchIntervalMs?: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
58
74
|
/**
|
|
59
75
|
* CCTV; 실시간 영상보기 컨텍스트
|
|
60
76
|
* @property {string} [company_id] 선택된 업체 id코드
|
|
@@ -125,6 +141,7 @@ export type CctvContext<ContextExtension extends object = object> =
|
|
|
125
141
|
* @property {string} [username] CCTV 조회시 권한 확인을 위한 계정명
|
|
126
142
|
* @property {string} [whepUsername] WHEP endpoint username 덮어쓰기 값
|
|
127
143
|
* @property {ContextExtension} [defaultValues] 서비스 확장 context 기본값
|
|
144
|
+
* @property {CctvRtcStreamOptions} [streamOptions] stream lazy loading 옵션
|
|
128
145
|
* @property {React.ReactNode} children
|
|
129
146
|
*/
|
|
130
147
|
export type CctvProviderProps<ContextExtension extends object = object> =
|
|
@@ -142,6 +159,10 @@ export type CctvProviderProps<ContextExtension extends object = object> =
|
|
|
142
159
|
* 서비스 확장 context 기본값
|
|
143
160
|
*/
|
|
144
161
|
defaultValues?: ContextExtension;
|
|
162
|
+
/**
|
|
163
|
+
* stream lazy loading 옵션
|
|
164
|
+
*/
|
|
165
|
+
streamOptions?: CctvRtcStreamOptions;
|
|
145
166
|
/**
|
|
146
167
|
* Provider 하위 콘텐츠
|
|
147
168
|
*/
|
package/src/cctv/types/hook.ts
CHANGED
|
@@ -20,6 +20,24 @@ export type CctvRtcReconnectTrigger = (
|
|
|
20
20
|
options?: RefetchOptions,
|
|
21
21
|
) => Promise<QueryObserverResult<API_Res_CctvRtcToken>>;
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* CCTV; RTC stream scheduler 상태
|
|
25
|
+
*/
|
|
26
|
+
export type CctvRtcStreamScheduleStatus = "idle" | "queued" | "granted";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* CCTV; RTC stream 표시 단계
|
|
30
|
+
*/
|
|
31
|
+
export type CctvRtcStreamStage =
|
|
32
|
+
| "idle"
|
|
33
|
+
| "queued"
|
|
34
|
+
| "token-loading"
|
|
35
|
+
| "connecting"
|
|
36
|
+
| "live"
|
|
37
|
+
| "recovering"
|
|
38
|
+
| "error"
|
|
39
|
+
| "skipped-offline";
|
|
40
|
+
|
|
23
41
|
/**
|
|
24
42
|
* CCTV; useCctvRtcStream params
|
|
25
43
|
* @property {CctvCompanyCameraData} [cam] 스트리밍 카메라 정보
|
|
@@ -64,6 +82,7 @@ export interface UseCctvRtcStreamError {
|
|
|
64
82
|
* @property {boolean} isTokenLoading UI 표시 기준 토큰 발급 요청 진행 여부
|
|
65
83
|
* @property {boolean} isTokenError 토큰 발급 실패 여부
|
|
66
84
|
* @property {string | null} streamError 스트림 오류 메시지
|
|
85
|
+
* @property {CctvRtcStreamStage} streamStage stream 표시 단계
|
|
67
86
|
*/
|
|
68
87
|
export interface UseCctvRtcStreamState extends UseCctvRtcStreamError {
|
|
69
88
|
/**
|
|
@@ -88,6 +107,10 @@ export interface UseCctvRtcStreamState extends UseCctvRtcStreamError {
|
|
|
88
107
|
* 재발급 중에는 기존 화면과 live count를 유지하기 위해 false로 smoothing될 수 있다.
|
|
89
108
|
*/
|
|
90
109
|
isTokenLoading: boolean;
|
|
110
|
+
/**
|
|
111
|
+
* stream 표시 단계
|
|
112
|
+
*/
|
|
113
|
+
streamStage: CctvRtcStreamStage;
|
|
91
114
|
}
|
|
92
115
|
|
|
93
116
|
/**
|
|
@@ -98,6 +121,7 @@ export interface UseCctvRtcStreamState extends UseCctvRtcStreamError {
|
|
|
98
121
|
* @property {boolean} isStreaming UI 표시 기준 startWhepStream 진행 여부
|
|
99
122
|
* @property {boolean} isTokenLoading UI 표시 기준 토큰 발급 요청 진행 여부
|
|
100
123
|
* @property {boolean} isTokenError 토큰 발급 실패 여부
|
|
124
|
+
* @property {CctvRtcStreamStage} streamStage stream 표시 단계
|
|
101
125
|
* @property {boolean} canReconnect UDS 내부 허용 사유와 grace/stagger를 통과해 재연결 호출이 가능한지 여부
|
|
102
126
|
* @property {() => Promise<void>} refetchToken 토큰 재발급 함수
|
|
103
127
|
* @property {() => Promise<QueryObserverResult<API_Res_CctvRtcToken>>} reconnectStream 재연결 trigger 함수
|
package/src/cctv/types/props.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CctvCompanyCameraData, CctvCompanyCameraList } from "./list";
|
|
2
|
-
import type { CctvRtcReconnectTrigger } from "./hook";
|
|
2
|
+
import type { CctvRtcReconnectTrigger, CctvRtcStreamStage } from "./hook";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* CCTV; custom overlay render context
|
|
@@ -9,6 +9,7 @@ import type { CctvRtcReconnectTrigger } from "./hook";
|
|
|
9
9
|
* @property {boolean | undefined} [isError] 영상 에러 상태 여부
|
|
10
10
|
* @property {boolean | undefined} [isLive] 영상 live 상태 여부
|
|
11
11
|
* @property {React.ReactNode} [overlayMessage] 기본 안내/에러 메시지 콘텐츠
|
|
12
|
+
* @property {CctvRtcStreamStage | undefined} [streamStage] stream 표시 단계
|
|
12
13
|
* @property {boolean | undefined} [canReconnect] UDS 내부 허용 사유와 grace/stagger를 통과해 재연결 호출이 가능한지 여부
|
|
13
14
|
* @property {CctvRtcReconnectTrigger | undefined} [reconnectStream] 재연결 trigger 함수
|
|
14
15
|
*/
|
|
@@ -37,6 +38,10 @@ export interface CctvVideoRenderOverlayContext {
|
|
|
37
38
|
* 기본 안내/에러 메시지 콘텐츠
|
|
38
39
|
*/
|
|
39
40
|
overlayMessage?: React.ReactNode;
|
|
41
|
+
/**
|
|
42
|
+
* stream 표시 단계
|
|
43
|
+
*/
|
|
44
|
+
streamStage?: CctvRtcStreamStage;
|
|
40
45
|
/**
|
|
41
46
|
* UDS 내부 허용 사유와 grace/stagger를 통과해 재연결 호출이 가능한지 여부
|
|
42
47
|
* @desc
|
|
@@ -130,6 +135,7 @@ export interface CctvVideoOverlayFooterProps {
|
|
|
130
135
|
* @property {boolean} [isError] 에러 상태 여부
|
|
131
136
|
* @property {boolean} [isLive] 영상이 정상적으로 재생 중인지 여부
|
|
132
137
|
* @property {React.ReactNode} [overlayMessage] 에러/안내 메시지 콘텐츠
|
|
138
|
+
* @property {CctvRtcStreamStage} [streamStage] stream 표시 단계
|
|
133
139
|
* @property {boolean} [canReconnect] UDS 내부 허용 사유와 grace/stagger를 통과해 재연결 호출이 가능한지 여부
|
|
134
140
|
* @property {CctvRtcReconnectTrigger} [reconnectStream] 재연결 trigger 함수
|
|
135
141
|
*/
|
|
@@ -146,6 +152,10 @@ export interface CctvVideoStateProps {
|
|
|
146
152
|
* 오버레이 메시지 콘텐츠
|
|
147
153
|
*/
|
|
148
154
|
overlayMessage?: React.ReactNode;
|
|
155
|
+
/**
|
|
156
|
+
* stream 표시 단계
|
|
157
|
+
*/
|
|
158
|
+
streamStage?: CctvRtcStreamStage;
|
|
149
159
|
/**
|
|
150
160
|
* UDS 내부 허용 사유와 grace/stagger를 통과해 재연결 호출이 가능한지 여부
|
|
151
161
|
*/
|
|
@@ -164,6 +174,7 @@ export interface CctvVideoStateProps {
|
|
|
164
174
|
* @property {boolean} [isError] 에러 상태 여부
|
|
165
175
|
* @property {boolean} [isLive] 영상이 정상적으로 재생 중인지 여부
|
|
166
176
|
* @property {React.ReactNode} [overlayMessage] 에러/안내 메시지 콘텐츠
|
|
177
|
+
* @property {CctvRtcStreamStage} [streamStage] stream 표시 단계
|
|
167
178
|
* @property {CctvVideoRenderOverlay} [renderOverlay] custom overlay 렌더 함수
|
|
168
179
|
*/
|
|
169
180
|
export interface CctvVideoTemplateProps extends CctvVideoStateProps {
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { CctvCompanyCameraData } from "./list";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
CctvRtcStreamStage,
|
|
4
|
+
UseCctvRtcStreamError,
|
|
5
|
+
UseCctvRtcStreamState,
|
|
6
|
+
} from "./hook";
|
|
3
7
|
|
|
4
8
|
/**
|
|
5
9
|
* CCTV; getOverlayMessage() params
|
|
@@ -11,10 +15,14 @@ import type { UseCctvRtcStreamError, UseCctvRtcStreamState } from "./hook";
|
|
|
11
15
|
* @property {boolean} isTokenLoading UI 표시 기준 토큰 발급 요청 진행 여부
|
|
12
16
|
* @property {boolean} isTokenError 토큰 발급 실패 여부
|
|
13
17
|
* @property {string | null} streamError 스트림 오류 메시지
|
|
18
|
+
* @property {CctvRtcStreamStage} [streamStage] stream 표시 단계
|
|
14
19
|
* @property {boolean} [canReconnect] UDS 내부 허용 사유와 grace/stagger를 통과해 재연결 호출이 가능한지 여부.
|
|
15
20
|
* 오류 스타일 판정에는 직접 사용하지 않는다.
|
|
16
21
|
*/
|
|
17
|
-
export interface CctvVideoOverlayMessageParams extends
|
|
22
|
+
export interface CctvVideoOverlayMessageParams extends Omit<
|
|
23
|
+
UseCctvRtcStreamState,
|
|
24
|
+
"streamStage"
|
|
25
|
+
> {
|
|
18
26
|
/**
|
|
19
27
|
* 카메라 데이터
|
|
20
28
|
*/
|
|
@@ -27,6 +35,10 @@ export interface CctvVideoOverlayMessageParams extends UseCctvRtcStreamState {
|
|
|
27
35
|
* 서버에서 cam 리스트를 가져오는 중인지 여부
|
|
28
36
|
*/
|
|
29
37
|
isFetching?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* stream 표시 단계
|
|
40
|
+
*/
|
|
41
|
+
streamStage?: CctvRtcStreamStage;
|
|
30
42
|
/**
|
|
31
43
|
* UDS 내부 허용 사유와 grace/stagger를 통과해 재연결 호출이 가능한지 여부
|
|
32
44
|
* @desc
|
|
@@ -44,7 +56,10 @@ export interface CctvVideoOverlayMessageParams extends UseCctvRtcStreamState {
|
|
|
44
56
|
* @property {boolean} isTokenError 토큰 발급 실패 여부
|
|
45
57
|
* @property {string | null} streamError 스트림 오류 메시지
|
|
46
58
|
*/
|
|
47
|
-
export interface CctvVideoLiveParams extends
|
|
59
|
+
export interface CctvVideoLiveParams extends Omit<
|
|
60
|
+
UseCctvRtcStreamState,
|
|
61
|
+
"streamStage"
|
|
62
|
+
> {
|
|
48
63
|
/**
|
|
49
64
|
* 카메라 데이터
|
|
50
65
|
*/
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
export const CCTV_MESSAGE = {
|
|
8
8
|
selectCam: "카메라를 선택하세요.",
|
|
9
9
|
fetching: "CCTV 데이터를 불러오는 중입니다.",
|
|
10
|
+
queued: "CCTV 연결 순서를 기다리고 있습니다.",
|
|
10
11
|
preparing: "CCTV 영상을 연결하고 있습니다.",
|
|
11
12
|
sessionEnded:
|
|
12
13
|
"장시간 미사용으로 연결이 종료되었습니다. 계속 확인하시려면 다시 연결해 주세요.",
|
|
@@ -47,6 +48,7 @@ export function getOverlayMessage({
|
|
|
47
48
|
isTokenLoading,
|
|
48
49
|
isTokenError,
|
|
49
50
|
isStreaming,
|
|
51
|
+
streamStage,
|
|
50
52
|
streamError,
|
|
51
53
|
}: CctvVideoOverlayMessageParams): string | null {
|
|
52
54
|
if (!cam) {
|
|
@@ -62,6 +64,7 @@ export function getOverlayMessage({
|
|
|
62
64
|
if (canReconnect && RTC_SESSION_ENDED_RECONNECT_STATES.has(connectionState)) {
|
|
63
65
|
return CCTV_MESSAGE.sessionEnded;
|
|
64
66
|
}
|
|
67
|
+
if (streamStage === "queued") return CCTV_MESSAGE.queued;
|
|
65
68
|
// raw disconnected는 useCctvRtcStream에서 post-connected connected로 smoothing한다.
|
|
66
69
|
if (isTokenLoading || isStreaming) return CCTV_MESSAGE.preparing;
|
|
67
70
|
if (RTC_PREPARING_STATES.has(connectionState)) return CCTV_MESSAGE.preparing;
|