@ray-js/ipc-player-integration 0.0.53-beta.0 → 0.0.53-beta.10

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.
Files changed (36) hide show
  1. package/lib/ctx/ctx.autoClarity.test.d.ts +1 -0
  2. package/lib/ctx/ctx.autoClarity.test.js +150 -0
  3. package/lib/ctx/ctx.composition.js +1 -6
  4. package/lib/ctx/ctx.js +94 -7
  5. package/lib/features/initPlayerWidgets/index.js +0 -9
  6. package/lib/i18n/index.d.ts +20 -4
  7. package/lib/i18n/strings.d.ts +10 -2
  8. package/lib/i18n/strings.js +10 -2
  9. package/lib/interface.d.ts +4 -7
  10. package/lib/ui/constant.d.ts +0 -7
  11. package/lib/ui/constant.js +0 -8
  12. package/lib/ui/ui.js +50 -7
  13. package/lib/utils/captureResult.d.ts +6 -1
  14. package/lib/utils/captureResult.js +15 -13
  15. package/lib/utils/captureResult.test.js +42 -15
  16. package/lib/utils/ttt.autoClarity.test.d.ts +1 -0
  17. package/lib/utils/ttt.autoClarity.test.js +75 -0
  18. package/lib/utils/ttt.d.ts +18 -4
  19. package/lib/utils/ttt.js +83 -32
  20. package/lib/widgets/captureResult/captureResult.js +6 -10
  21. package/lib/widgets/captureResult/captureResult.less +59 -14
  22. package/lib/widgets/captureResult/captureResult.test.d.ts +1 -0
  23. package/lib/widgets/captureResult/captureResult.test.js +80 -0
  24. package/lib/widgets/recordVideo/recordVideo.js +5 -1
  25. package/lib/widgets/resolution/index.d.ts +0 -1
  26. package/lib/widgets/resolution/index.js +1 -2
  27. package/lib/widgets/resolution/resolution.js +0 -1
  28. package/lib/widgets/resolution/resolution.less +0 -4
  29. package/lib/widgets/screenshot/screenshot.js +5 -1
  30. package/lib/widgets/videoBitKBP/videoBitKBP.js +36 -37
  31. package/lib/widgets/videoBitKBP/videoBitKBP.less +2 -5
  32. package/lib/widgets/videoBitKBP/videoBitKBP.test.d.ts +1 -0
  33. package/lib/widgets/videoBitKBP/videoBitKBP.test.js +112 -0
  34. package/package.json +2 -2
  35. package/lib/widgets/resolution/autoClarity.d.ts +0 -13
  36. package/lib/widgets/resolution/autoClarity.js +0 -110
@@ -1,22 +1,36 @@
1
1
  .ipc-player-plugin-capture-result-toast,
2
2
  .ipc-player-plugin-record-success-toast,
3
3
  .ipc-player-plugin-screenshot-toast {
4
+ --capture-result-text-color: var(--fontColor, #fff);
5
+ --capture-result-near-backdrop-color: rgba(255, 255, 255, 0.2);
6
+ --capture-result-far-backdrop-color: rgba(255, 255, 255, 0.1);
7
+
4
8
  z-index: 100;
5
9
  display: flex;
6
10
  flex: 1;
7
11
  align-items: center;
8
- padding: calc(8px * var(--ipc-player-size-scale, 1))
12
+ padding: calc(7px * var(--ipc-player-size-scale, 1))
13
+ calc(16px * var(--ipc-player-size-scale, 1))
14
+ calc(7px * var(--ipc-player-size-scale, 1))
9
15
  calc(12px * var(--ipc-player-size-scale, 1));
10
16
  background-color: var(--shot-card-bg-color);
11
17
  border-radius: calc(8px * var(--ipc-player-size-scale, 1));
12
18
  }
13
19
 
20
+ :root[theme='light'] .ipc-player-plugin-capture-result-toast {
21
+ --capture-result-text-color: #1a1a1a;
22
+ --capture-result-near-backdrop-color: rgba(0, 0, 0, 0.1);
23
+ --capture-result-far-backdrop-color: rgba(0, 0, 0, 0.05);
24
+
25
+ background-color: #fff;
26
+ }
27
+
14
28
  .ipc-player-plugin-capture-result-image-box,
15
29
  .ipc-player-plugin-record-success-toast-image-box,
16
30
  .ipc-player-plugin-screenshot-toast-image-box {
17
31
  position: relative;
18
32
  width: calc(66px * var(--ipc-player-size-scale, 1));
19
- height: calc(66px * 0.57 * var(--ipc-player-size-scale, 1));
33
+ height: calc(39px * var(--ipc-player-size-scale, 1));
20
34
  overflow: hidden;
21
35
  border-radius: calc(4px * var(--ipc-player-size-scale, 1));
22
36
  }
@@ -29,17 +43,39 @@
29
43
  object-fit: cover;
30
44
  }
31
45
 
32
- .ipc-player-plugin-capture-result-image-box-multi .ipc-player-plugin-capture-result-image,
33
- .ipc-player-plugin-record-success-toast-image-box-multi .ipc-player-plugin-record-success-toast-image,
34
- .ipc-player-plugin-screenshot-toast-image-box-multi .ipc-player-plugin-screenshot-toast-image {
46
+ .ipc-player-plugin-capture-result-backdrop,
47
+ .ipc-player-plugin-capture-result-image-front {
35
48
  position: absolute;
36
- top: 0;
37
- width: calc(56px * var(--ipc-player-size-scale, 1));
38
49
  box-sizing: border-box;
39
- border: 1px solid var(--shot-card-bg-color);
40
50
  border-radius: calc(4px * var(--ipc-player-size-scale, 1));
41
51
  }
42
52
 
53
+ .ipc-player-plugin-capture-result-image-back {
54
+ top: calc(1px * var(--ipc-player-size-scale, 1));
55
+ left: calc(5px * var(--ipc-player-size-scale, 1));
56
+ z-index: 1;
57
+ width: calc(56px * var(--ipc-player-size-scale, 1));
58
+ height: calc(28px * var(--ipc-player-size-scale, 1));
59
+ background: var(--capture-result-far-backdrop-color);
60
+ }
61
+
62
+ .ipc-player-plugin-capture-result-image-middle {
63
+ top: calc(3px * var(--ipc-player-size-scale, 1));
64
+ left: calc(2.5px * var(--ipc-player-size-scale, 1));
65
+ z-index: 2;
66
+ width: calc(61px * var(--ipc-player-size-scale, 1));
67
+ height: calc(30.5px * var(--ipc-player-size-scale, 1));
68
+ background: var(--capture-result-near-backdrop-color);
69
+ }
70
+
71
+ .ipc-player-plugin-capture-result-image-front {
72
+ top: calc(5px * var(--ipc-player-size-scale, 1));
73
+ left: 0;
74
+ z-index: 3;
75
+ width: calc(66px * var(--ipc-player-size-scale, 1));
76
+ height: calc(33px * var(--ipc-player-size-scale, 1));
77
+ }
78
+
43
79
  .ipc-player-plugin-capture-result-play-icon-box,
44
80
  .ipc-player-plugin-record-success-play-icon-box {
45
81
  position: absolute;
@@ -49,18 +85,23 @@
49
85
  display: flex;
50
86
  align-items: center;
51
87
  justify-content: center;
52
- width: 30px;
53
- height: 30px;
88
+ width: calc(16px * var(--ipc-player-size-scale, 1));
89
+ height: calc(16px * var(--ipc-player-size-scale, 1));
54
90
  overflow: hidden;
55
91
  background-color: rgba(0, 0, 0, 0.2);
56
92
  border-radius: 50%;
57
93
  transform: translate(-50%, -50%);
58
94
  }
59
95
 
96
+ .ipc-player-plugin-capture-result-image-box-multi
97
+ .ipc-player-plugin-capture-result-play-icon-box {
98
+ top: calc(50% + 2px * var(--ipc-player-size-scale, 1));
99
+ }
100
+
60
101
  .ipc-player-plugin-capture-result-play-icon,
61
102
  .ipc-player-plugin-record-success-play-icon {
62
103
  color: #ffffff;
63
- font-size: calc(18px * var(--ipc-player-size-scale, 1)) !important;
104
+ font-size: calc(9px * var(--ipc-player-size-scale, 1)) !important;
64
105
  }
65
106
 
66
107
  .ipc-player-plugin-capture-result-title,
@@ -69,17 +110,20 @@
69
110
  display: flex;
70
111
  flex: 1;
71
112
  flex-direction: column;
113
+ min-width: 0;
72
114
  margin-right: calc(12px * var(--ipc-player-size-scale, 1));
73
- margin-left: calc(12px * var(--ipc-player-size-scale, 1));
74
- color: var(--fontColor);
115
+ margin-left: calc(8px * var(--ipc-player-size-scale, 1));
116
+ color: var(--capture-result-text-color, var(--fontColor));
75
117
  font-size: calc(14px * var(--ipc-player-size-scale, 1));
118
+ line-height: calc(20px * var(--ipc-player-size-scale, 1));
76
119
  word-break: break-word;
77
120
  }
78
121
 
79
122
  .ipc-player-plugin-capture-result-count {
80
123
  margin-top: calc(2px * var(--ipc-player-size-scale, 1));
81
- color: var(--fontColor);
124
+ color: var(--capture-result-text-color, var(--fontColor));
82
125
  font-size: calc(12px * var(--ipc-player-size-scale, 1));
126
+ line-height: calc(17px * var(--ipc-player-size-scale, 1));
83
127
  opacity: 0.5;
84
128
  }
85
129
 
@@ -88,6 +132,7 @@
88
132
  .ipc-player-plugin-screenshot-toast-bottom {
89
133
  min-width: calc(52px * var(--ipc-player-size-scale, 1));
90
134
  max-width: calc(100px * var(--ipc-player-size-scale, 1));
135
+ flex-shrink: 0;
91
136
  padding: calc(5px * var(--ipc-player-size-scale, 1))
92
137
  calc(11px * var(--ipc-player-size-scale, 1));
93
138
  overflow: hidden;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,80 @@
1
+ import React from 'react';
2
+ import { renderToStaticMarkup } from 'react-dom/server';
3
+ jest.mock('@ray-js/ray', () => {
4
+ const React = require('react');
5
+ return {
6
+ Image: _ref => {
7
+ let {
8
+ className,
9
+ src
10
+ } = _ref;
11
+ return /*#__PURE__*/React.createElement("img", {
12
+ alt: "",
13
+ className: className,
14
+ src: src
15
+ });
16
+ },
17
+ Text: _ref2 => {
18
+ let {
19
+ children,
20
+ className
21
+ } = _ref2;
22
+ return /*#__PURE__*/React.createElement("span", {
23
+ className: className
24
+ }, children);
25
+ },
26
+ View: _ref3 => {
27
+ let {
28
+ children,
29
+ className
30
+ } = _ref3;
31
+ return /*#__PURE__*/React.createElement("div", {
32
+ className: className
33
+ }, children);
34
+ }
35
+ };
36
+ });
37
+ jest.mock('../../i18n', () => ({
38
+ __esModule: true,
39
+ default: {
40
+ getLang: key => key,
41
+ formatString: (template, count) => `${template}:${count}`
42
+ }
43
+ }));
44
+ jest.mock('./captureResult.less', () => ({}));
45
+ const {
46
+ CaptureResultToast
47
+ } = require('./captureResult');
48
+ const renderToast = count => renderToStaticMarkup(/*#__PURE__*/React.createElement(CaptureResultToast, {
49
+ brandColor: "#ff592a",
50
+ count: count,
51
+ kind: "screenshot",
52
+ onCheck: () => undefined,
53
+ thumbnailPath: "thingfile://capture.jpg"
54
+ }));
55
+ const countClass = (markup, className) => [...markup.matchAll(/class="([^"]*)"/g)].filter(_ref4 => {
56
+ let [, names] = _ref4;
57
+ return names.split(/\s+/).includes(className);
58
+ }).length;
59
+ describe('CaptureResultToast', () => {
60
+ it('keeps the single-item thumbnail unchanged', () => {
61
+ const markup = renderToast(1);
62
+ expect(countClass(markup, 'ipc-player-plugin-capture-result-backdrop')).toBe(0);
63
+ expect(countClass(markup, 'ipc-player-plugin-capture-result-image-front')).toBe(0);
64
+ expect(countClass(markup, 'ipc-player-plugin-capture-result-image')).toBe(1);
65
+ });
66
+ it('renders both backdrops for two captured items', () => {
67
+ const markup = renderToast(2);
68
+ expect(countClass(markup, 'ipc-player-plugin-capture-result-backdrop')).toBe(2);
69
+ expect(countClass(markup, 'ipc-player-plugin-capture-result-image-back')).toBe(1);
70
+ expect(countClass(markup, 'ipc-player-plugin-capture-result-image-middle')).toBe(1);
71
+ expect(countClass(markup, 'ipc-player-plugin-capture-result-image-front')).toBe(1);
72
+ });
73
+ it('caps the backdrop count at two', () => {
74
+ const markup = renderToast(4);
75
+ expect(countClass(markup, 'ipc-player-plugin-capture-result-backdrop')).toBe(2);
76
+ expect(countClass(markup, 'ipc-player-plugin-capture-result-image-back')).toBe(1);
77
+ expect(countClass(markup, 'ipc-player-plugin-capture-result-image-middle')).toBe(1);
78
+ expect(countClass(markup, 'ipc-player-plugin-capture-result-image-front')).toBe(1);
79
+ });
80
+ });
@@ -117,7 +117,11 @@ export function RecordVideo(props) {
117
117
  var _props$multiCameraCtx;
118
118
  const requestId = ++recordResultRequestIdRef.current;
119
119
  const splitNum = (_props$multiCameraCtx = props.multiCameraCtx) === null || _props$multiCameraCtx === void 0 || (_props$multiCameraCtx = _props$multiCameraCtx.protocol) === null || _props$multiCameraCtx === void 0 ? void 0 : _props$multiCameraCtx.total_split_num;
120
- const summary = await getCaptureResultSummary(recordInfo, splitNum, devId);
120
+ const summary = await getCaptureResultSummary(recordInfo, {
121
+ multiLens: props.multiLens,
122
+ protocolCount: splitNum,
123
+ devId
124
+ });
121
125
  if (requestId !== recordResultRequestIdRef.current) return;
122
126
  if (!summary) return;
123
127
  setState({
@@ -1,2 +1 @@
1
1
  export * from './resolution';
2
- export * from './autoClarity';
@@ -1,2 +1 @@
1
- export * from './resolution';
2
- export * from './autoClarity';
1
+ export * from './resolution';
@@ -11,7 +11,6 @@ import { useStore } from '../../ctx/store';
11
11
  import './resolution.less';
12
12
  export const Resolution = props => {
13
13
  const {
14
- IPCPlayerContext,
15
14
  setResolution,
16
15
  addContent,
17
16
  hasContent,
@@ -21,10 +21,6 @@
21
21
  border-radius: calc(5px * var(--ipc-player-size-scale, 1));
22
22
  }
23
23
 
24
- .ipc-player-plugin-resolution-active {
25
- background-color: var(--brandColor, rgba(255, 89, 42, 0.9));
26
- }
27
-
28
24
  .ipc-player-plugin-full-resolution-control {
29
25
  height: 100%;
30
26
  width: 200px;
@@ -139,7 +139,11 @@ export function Screenshot(props) {
139
139
  var _props$multiCameraCtx;
140
140
  setIgnoreHideStopPreview(false);
141
141
  const splitNum = (_props$multiCameraCtx = props.multiCameraCtx) === null || _props$multiCameraCtx === void 0 || (_props$multiCameraCtx = _props$multiCameraCtx.protocol) === null || _props$multiCameraCtx === void 0 ? void 0 : _props$multiCameraCtx.total_split_num;
142
- const summary = await getCaptureResultSummary(res, splitNum, devId);
142
+ const summary = await getCaptureResultSummary(res, {
143
+ multiLens: props.multiLens,
144
+ protocolCount: splitNum,
145
+ devId
146
+ });
143
147
  if (requestId !== snapshotRequestIdRef.current) return;
144
148
  if (!summary) {
145
149
  handleSnapshotFail({});
@@ -1,15 +1,12 @@
1
- import React, { useContext, useEffect, useRef, useState } from 'react';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
  import { View, Text, usePageEvent } from '@ray-js/ray';
3
3
  import clsx from 'clsx';
4
4
  import { getVideoBitrateKbps, requestWifiSignal } from '@ray-js/ray-ipc-utils';
5
+ import { ClarityType } from '@ray-js/ray-ipc-utils/lib/interface';
5
6
  import { PlayState } from '../../interface';
6
7
  import './videoBitKBP.less';
7
8
  import { useStore } from '../../ctx/store';
8
9
  import Strings from '../../i18n';
9
- import { UIEventContext } from '../../ui/context';
10
- import { autoClarityChange } from '../../ui/constant';
11
- import { storage } from '../../utils/storage';
12
- import { AUTO_CLARITY_STORAGE_KEY } from '../resolution/autoClarity';
13
10
  export const VideoBitKBP = props => {
14
11
  const {
15
12
  devId,
@@ -20,30 +17,12 @@ export const VideoBitKBP = props => {
20
17
  } = props;
21
18
  const [bitKBP, setBitKBP] = useState('');
22
19
  const [signalStrength, setSignalStrength] = useState(null);
23
- // 自动清晰度当前档位:null 表示未开启自动模式,不展示
24
- const [autoClarity, setAutoClarity] = useState(() => {
25
- var _cached$clarity;
26
- const cached = storage.getSync(AUTO_CLARITY_STORAGE_KEY);
27
- return cached !== null && cached !== void 0 && cached.enabled ? (_cached$clarity = cached.clarity) !== null && _cached$clarity !== void 0 ? _cached$clarity : null : null;
28
- });
29
20
  const store = useStore({
30
- playState
21
+ playState,
22
+ resolution: props.resolution,
23
+ autoClarity: props.autoClarity
31
24
  });
32
- const {
33
- event
34
- } = useContext(UIEventContext);
35
25
  const timerRef = useRef();
36
-
37
- // 监听 AutoClarity 控件广播的自动清晰度状态
38
- useEffect(() => {
39
- const onAutoClarityChange = payload => {
40
- setAutoClarity(payload.enabled ? payload.clarity : null);
41
- };
42
- event.on(autoClarityChange, onAutoClarityChange);
43
- return () => {
44
- event.off(autoClarityChange, onAutoClarityChange);
45
- };
46
- }, [event]);
47
26
  useEffect(() => {
48
27
  if (store.playState === PlayState.PLAYING && (!hideKbsMenu || !hideSignalMenu)) {
49
28
  init();
@@ -77,12 +56,34 @@ export const VideoBitKBP = props => {
77
56
  }
78
57
  }
79
58
  };
80
- const hasKbps = !hideKbsMenu && bitKBP !== '' && bitKBP !== '0';
81
- const hasSignal = !hideSignalMenu && signalStrength !== null;
82
- if (!hasKbps && !hasSignal || store.playState !== PlayState.PLAYING) {
59
+ const isPlaying = store.playState === PlayState.PLAYING;
60
+ const hasKbps = isPlaying && !hideKbsMenu && bitKBP !== '' && bitKBP !== '0';
61
+ const hasSignal = isPlaying && !hideSignalMenu && signalStrength !== null;
62
+ const activeClarity = store.resolution === ClarityType.AUTO ? store.autoClarity : null;
63
+ const hasClarity = Boolean(activeClarity);
64
+ const bitrateLabel = [activeClarity ? (clarity => {
65
+ switch (clarity) {
66
+ case ClarityType.SS:
67
+ return Strings.getLang('ipc_player_resolution_SS');
68
+ case ClarityType.SD:
69
+ return Strings.getLang('ipc_player_resolution_SD');
70
+ case ClarityType.HD:
71
+ return Strings.getLang('ipc_player_resolution_HD');
72
+ case ClarityType.UD:
73
+ return Strings.getLang('ipc_player_resolution_UD');
74
+ case ClarityType.SSP:
75
+ return Strings.getLang('ipc_player_resolution_SSP');
76
+ case ClarityType.AUTO:
77
+ return Strings.getLang('ipc_player_resolution_AUTO');
78
+ case ClarityType.AUDIO:
79
+ return Strings.getLang('ipc_player_resolution_AUDIO');
80
+ default:
81
+ return clarity;
82
+ }
83
+ })(activeClarity) : '', hasKbps ? `${bitKBP} KB/S` : ''].filter(Boolean).join(' ');
84
+ if (!hasKbps && !hasSignal && !hasClarity) {
83
85
  return null;
84
86
  }
85
- if (hideKbsMenu && hideSignalMenu) return null;
86
87
  return /*#__PURE__*/React.createElement(View, {
87
88
  className: clsx({
88
89
  'ipc-player-plugin-videoBitKBP-container': true,
@@ -96,13 +97,11 @@ export const VideoBitKBP = props => {
96
97
  className: clsx('icon-panel', 'icon-panel-wi-Fi', 'ipc-player-plugin-videoBitKBP-signal-icon')
97
98
  }), /*#__PURE__*/React.createElement(Text, null, signalStrength), /*#__PURE__*/React.createElement(Text, {
98
99
  className: "ipc-player-plugin-videoBitKBP-unit-percent"
99
- }, signalStrength > 0 ? '%' : 'dBm')), hasSignal && hasKbps && /*#__PURE__*/React.createElement(View, {
100
+ }, signalStrength > 0 ? '%' : 'dBm')), hasSignal && (hasKbps || hasClarity) && /*#__PURE__*/React.createElement(View, {
100
101
  className: "ipc-player-plugin-videoBitKBP-divider"
101
- }), hasKbps && /*#__PURE__*/React.createElement(View, {
102
+ }), (hasKbps || hasClarity) && /*#__PURE__*/React.createElement(View, {
102
103
  className: "ipc-player-plugin-videoBitKBP-kbps"
103
- }, autoClarity && /*#__PURE__*/React.createElement(Text, {
104
- className: "ipc-player-plugin-videoBitKBP-clarity"
105
- }, autoClarity === 'HD' ? Strings.getLang('ipc_player_resolution_HD') : Strings.getLang('ipc_player_resolution_SD')), /*#__PURE__*/React.createElement(Text, null, bitKBP), /*#__PURE__*/React.createElement(Text, {
106
- className: "ipc-player-plugin-videoBitKBP-unit-kbs"
107
- }, "KB/S"))));
104
+ }, /*#__PURE__*/React.createElement(Text, {
105
+ className: "ipc-player-plugin-videoBitKBP-kbps-text"
106
+ }, bitrateLabel))));
108
107
  };
@@ -36,11 +36,8 @@
36
36
  display: flex;
37
37
  align-items: center;
38
38
  }
39
- .ipc-player-plugin-videoBitKBP-clarity {
40
- margin-right: calc(4px * var(--ipc-player-size-scale, 1));
41
- }
42
- .ipc-player-plugin-videoBitKBP-unit-kbs {
43
- margin-left: calc(4px * var(--ipc-player-size-scale, 1));
39
+ .ipc-player-plugin-videoBitKBP-kbps-text {
40
+ white-space: nowrap;
44
41
  }
45
42
  .ipc-player-plugin-videoBitKBP-unit-percent {
46
43
  margin-left: calc(2px * var(--ipc-player-size-scale, 1));
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,112 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom';
3
+ import { renderToStaticMarkup } from 'react-dom/server';
4
+ import { act } from 'react-dom/test-utils';
5
+ import { getVideoBitrateKbps } from '@ray-js/ray-ipc-utils';
6
+ let mockStoreState;
7
+ jest.mock('@ray-js/ray', () => ({
8
+ View: _ref => {
9
+ let {
10
+ children,
11
+ className
12
+ } = _ref;
13
+ return /*#__PURE__*/React.createElement("div", {
14
+ className: className
15
+ }, children);
16
+ },
17
+ Text: _ref2 => {
18
+ let {
19
+ children,
20
+ className
21
+ } = _ref2;
22
+ return /*#__PURE__*/React.createElement("span", {
23
+ className: className
24
+ }, children);
25
+ },
26
+ usePageEvent: () => undefined
27
+ }));
28
+ jest.mock('@ray-js/ray-ipc-utils', () => ({
29
+ getVideoBitrateKbps: jest.fn(),
30
+ requestWifiSignal: jest.fn()
31
+ }));
32
+ jest.mock('@ray-js/ray-ipc-utils/lib/interface', () => ({
33
+ ClarityType: {
34
+ SS: 'SS',
35
+ SD: 'SD',
36
+ HD: 'HD',
37
+ UD: 'UD',
38
+ SSP: 'SSP',
39
+ AUTO: 'AUTO',
40
+ AUDIO: 'AUDIO'
41
+ }
42
+ }));
43
+ jest.mock('../../ctx/store', () => ({
44
+ useStore: () => mockStoreState
45
+ }));
46
+ jest.mock('../../i18n', () => ({
47
+ __esModule: true,
48
+ default: {
49
+ getLang: key => key
50
+ }
51
+ }));
52
+ jest.mock('./videoBitKBP.less', () => ({}));
53
+ const {
54
+ VideoBitKBP
55
+ } = require('./videoBitKBP');
56
+ const renderWidget = function (resolution, autoClarity) {
57
+ let playState = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
58
+ let hideKbsMenu = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
59
+ mockStoreState = {
60
+ resolution,
61
+ autoClarity,
62
+ playState
63
+ };
64
+ return renderToStaticMarkup(/*#__PURE__*/React.createElement(VideoBitKBP, {
65
+ devId: 'device',
66
+ playState: {},
67
+ resolution: {},
68
+ autoClarity: {},
69
+ hideKbsMenu,
70
+ hideSignalMenu: true
71
+ }));
72
+ };
73
+ describe('VideoBitKBP auto clarity', () => {
74
+ it('renders clarity, bitrate and unit in one text line', async () => {
75
+ const container = document.createElement('div');
76
+ getVideoBitrateKbps.mockResolvedValue({
77
+ code: 0,
78
+ data: {
79
+ kbps: '128'
80
+ }
81
+ });
82
+ mockStoreState = {
83
+ resolution: 'AUTO',
84
+ autoClarity: 'HD',
85
+ playState: 1
86
+ };
87
+ try {
88
+ await act(async () => {
89
+ ReactDOM.render(/*#__PURE__*/React.createElement(VideoBitKBP, {
90
+ devId: 'device',
91
+ hideKbsMenu: false,
92
+ hideSignalMenu: true
93
+ }), container);
94
+ });
95
+ expect(container.innerHTML).toContain('<span class="ipc-player-plugin-videoBitKBP-kbps-text">ipc_player_resolution_HD 128 KB/S</span>');
96
+ } finally {
97
+ ReactDOM.unmountComponentAtNode(container);
98
+ }
99
+ });
100
+ it('shows actual clarity independently of bitrate, signal and play state', () => {
101
+ const markup = renderWidget('AUTO', 'HD', 2);
102
+ expect(markup).toContain('ipc_player_resolution_HD');
103
+ expect(markup).not.toContain('KB/S');
104
+ expect(markup).toContain('<span class="ipc-player-plugin-videoBitKBP-kbps-text">ipc_player_resolution_HD</span>');
105
+ });
106
+ it('does not show clarity outside auto mode', () => {
107
+ expect(renderWidget('SD', 'HD')).toBe('');
108
+ });
109
+ it('waits for the native clarity report in auto mode', () => {
110
+ expect(renderWidget('AUTO', null)).toBe('');
111
+ });
112
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ray-js/ipc-player-integration",
3
- "version": "0.0.53-beta.0",
3
+ "version": "0.0.53-beta.10",
4
4
  "description": "IPC 融合播放器",
5
5
  "main": "lib/index",
6
6
  "files": [
@@ -37,7 +37,7 @@
37
37
  "dependencies": {
38
38
  "@ray-js/direction-control": "^0.0.8",
39
39
  "@ray-js/ipc-ptz-zoom": "^0.0.3",
40
- "@ray-js/ray-ipc-player": "^2.1.3",
40
+ "@ray-js/ray-ipc-player": "2.1.4-beta.0",
41
41
  "@ray-js/ray-ipc-utils": "^1.1.16",
42
42
  "@ray-js/svg": "0.2.0",
43
43
  "clsx": "^1.2.1",
@@ -1,13 +0,0 @@
1
- import React from 'react';
2
- import { ComponentConfigProps } from '../../interface';
3
- import './resolution.less';
4
- type Props = ComponentConfigProps & {
5
- className?: string;
6
- };
7
- export declare const AUTO_CLARITY_STORAGE_KEY = "autoClarity";
8
- export type AutoClarityMemory = {
9
- enabled: boolean;
10
- clarity: 'HD' | 'SD' | null;
11
- };
12
- export declare const AutoClarity: (props: Props) => React.JSX.Element;
13
- export {};
@@ -1,110 +0,0 @@
1
- import React, { useContext, useEffect, useState } from 'react';
2
- import { View, Text } from '@ray-js/ray';
3
- import clsx from 'clsx';
4
- import Strings from '../../i18n';
5
- import { useStore } from '../../ctx/store';
6
- import { storage } from '../../utils/storage';
7
- import { UIEventContext } from '../../ui/context';
8
- import { widgetClick, widgetLabs, autoClarityChange } from '../../ui/constant';
9
- import { clarityChanged } from '../../utils/ttt';
10
- import './resolution.less';
11
- // 自动清晰度记忆 storage key(按设备维度隔离由 storage 内部 prefix 处理)
12
- export const AUTO_CLARITY_STORAGE_KEY = 'autoClarity';
13
- export const AutoClarity = props => {
14
- const {
15
- className,
16
- devId
17
- } = props;
18
- const {
19
- screenType,
20
- resolutionList
21
- } = useStore({
22
- screenType: props.screenType,
23
- resolutionList: props.resolutionList
24
- });
25
- const {
26
- event
27
- } = useContext(UIEventContext);
28
-
29
- // 是否处于自动模式
30
- const [auto, setAuto] = useState(false);
31
-
32
- // 广播自动清晰度状态给 VideoBitKBP 等监听方
33
- const emitClarity = (enabled, clarity) => {
34
- event.emit(autoClarityChange, {
35
- enabled,
36
- clarity
37
- });
38
- };
39
-
40
- // 进入时恢复上次退出时的选择
41
- useEffect(() => {
42
- const cached = storage.getSync(AUTO_CLARITY_STORAGE_KEY);
43
- if (cached !== null && cached !== void 0 && cached.enabled) {
44
- var _cached$clarity;
45
- setAuto(true);
46
- emitClarity(true, (_cached$clarity = cached.clarity) !== null && _cached$clarity !== void 0 ? _cached$clarity : null);
47
- // 恢复自动模式后重新拉取一次当前清晰度
48
- clarityChanged(devId).then(res => {
49
- if (res !== null && res !== void 0 && res.clarity) {
50
- emitClarity(true, res.clarity);
51
- storage.set(AUTO_CLARITY_STORAGE_KEY, {
52
- enabled: true,
53
- clarity: res.clarity
54
- });
55
- }
56
- });
57
- } else {
58
- emitClarity(false, null);
59
- }
60
- // eslint-disable-next-line react-hooks/exhaustive-deps
61
- }, [devId]);
62
-
63
- // TODO: 后面取消注释 宿主不存在 ttt 方法 clarityChanged 时整个入口不渲染
64
- // if (!isClarityChangedSupported()) {
65
- // return null;
66
- // }
67
-
68
- // TODO: 后面取消注释 设备支持的清晰度档位数量 ≥ 2 时才展示自动
69
- // if (!resolutionList || resolutionList.length < 2) {
70
- // return null;
71
- // }
72
-
73
- return /*#__PURE__*/React.createElement(View, {
74
- onClick: async () => {
75
- var _res$clarity;
76
- event.emit(widgetClick, {
77
- widgetId: widgetLabs.AUTO_CLARITY
78
- });
79
-
80
- // 关闭自动模式
81
- if (auto) {
82
- setAuto(false);
83
- emitClarity(false, null);
84
- storage.set(AUTO_CLARITY_STORAGE_KEY, {
85
- enabled: false,
86
- clarity: null
87
- });
88
- return;
89
- }
90
-
91
- // 开启自动模式并读取回调结果
92
- setAuto(true);
93
- const res = await clarityChanged(devId);
94
- const next = (_res$clarity = res === null || res === void 0 ? void 0 : res.clarity) !== null && _res$clarity !== void 0 ? _res$clarity : null;
95
- emitClarity(true, next);
96
- storage.set(AUTO_CLARITY_STORAGE_KEY, {
97
- enabled: true,
98
- clarity: next
99
- });
100
- },
101
- className: clsx(className)
102
- }, /*#__PURE__*/React.createElement(View, {
103
- className: clsx('ipc-player-plugin-resolution', {
104
- 'ipc-player-plugin-resolution-full': screenType === 'full',
105
- 'ipc-player-plugin-resolution-active': auto
106
- })
107
- }, /*#__PURE__*/React.createElement(Text, {
108
- className: "resolutionText"
109
- }, Strings.getLang('ipc_player_resolution_auto'))));
110
- };