@volcengine/veplayer 1.15.1 → 1.15.2-rc.1

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.

Potentially problematic release.


This version of @volcengine/veplayer might be problematic. Click here for more details.

Files changed (245) hide show
  1. package/.changeset/config.json +11 -0
  2. package/.changeset/curvy-yaks-smoke.md +5 -0
  3. package/.changeset/neat-eyes-search.md +5 -0
  4. package/.codebase/pipelines/create-api-doc.yaml +16 -0
  5. package/.eslintignore +5 -0
  6. package/.eslintrc.json +53 -0
  7. package/.stylelintrc.js +51 -0
  8. package/CHANGELOG.md +5665 -0
  9. package/DEV_README.md +39 -0
  10. package/LICENSE +20 -0
  11. package/README_EN.md +46 -0
  12. package/build.sh +44 -0
  13. package/docg.config.js +65 -0
  14. package/env/byteplus.js +30 -0
  15. package/env/volcengine.js +50 -0
  16. package/fixtures/favicon.ico +0 -0
  17. package/fixtures/index.base.js +59 -0
  18. package/fixtures/index.html +41 -0
  19. package/fixtures/index.js +185 -0
  20. package/libd.config.js +147 -0
  21. package/localhost+2-key.pem +28 -0
  22. package/localhost+2.pem +26 -0
  23. package/lux.config.mjs +37 -0
  24. package/package.json +123 -4
  25. package/sdkhub.config.json +10 -0
  26. package/src/@types/global.d.ts +35 -0
  27. package/src/assets/common/error.svg +3 -0
  28. package/src/assets/common/errorImg.png +0 -0
  29. package/src/assets/common/errorImg.svg +12 -0
  30. package/src/assets/icons/mobile/definition.svg +3 -0
  31. package/src/assets/icons/mobile/line.svg +3 -0
  32. package/src/assets/icons/mobile/mobileDanmu.svg +1 -0
  33. package/src/assets/icons/mobile/mobileDanmuAcitive.svg +1 -0
  34. package/src/assets/icons/mobile/mobileDanmuSetting.svg +1 -0
  35. package/src/assets/icons/mobile/mobileExitFullscreen.svg +1 -0
  36. package/src/assets/icons/mobile/mobileFullscreen.svg +1 -0
  37. package/src/assets/icons/mobile/mobilePause.svg +4 -0
  38. package/src/assets/icons/mobile/mobilePlay.svg +4 -0
  39. package/src/assets/icons/mobile/mobilePlaynext.svg +1 -0
  40. package/src/assets/icons/mobile/mobileRefresh.svg +1 -0
  41. package/src/assets/icons/mobile/more.svg +3 -0
  42. package/src/assets/icons/mobile/muted.svg +22 -0
  43. package/src/assets/icons/mobile/playbackrate.svg +3 -0
  44. package/src/assets/icons/pc/danmu.svg +1 -0
  45. package/src/assets/icons/pc/danmuActive.svg +1 -0
  46. package/src/assets/icons/pc/danmuSettings.svg +1 -0
  47. package/src/assets/icons/pc/download.svg +14 -0
  48. package/src/assets/icons/pc/exitFullscreen.svg +1 -0
  49. package/src/assets/icons/pc/exitMirror.svg +10 -0
  50. package/src/assets/icons/pc/extend.svg +1 -0
  51. package/src/assets/icons/pc/fullscreen.svg +1 -0
  52. package/src/assets/icons/pc/getMirror.svg +10 -0
  53. package/src/assets/icons/pc/list.svg +20 -0
  54. package/src/assets/icons/pc/noPoster.svg +119 -0
  55. package/src/assets/icons/pc/pause.svg +1 -0
  56. package/src/assets/icons/pc/pip.svg +1 -0
  57. package/src/assets/icons/pc/pipExit.svg +1 -0
  58. package/src/assets/icons/pc/play-next-btn.svg +4 -0
  59. package/src/assets/icons/pc/play.svg +1 -0
  60. package/src/assets/icons/pc/playNext.svg +1 -0
  61. package/src/assets/icons/pc/playerLoading.svg +1 -0
  62. package/src/assets/icons/pc/refresh.svg +1 -0
  63. package/src/assets/icons/pc/replay.svg +1 -0
  64. package/src/assets/icons/pc/reset.svg +6 -0
  65. package/src/assets/icons/pc/startPlay.svg +1 -0
  66. package/src/assets/icons/pc/subtitleclose.svg +5 -0
  67. package/src/assets/icons/pc/subtitleopen.svg +3 -0
  68. package/src/assets/icons/pc/theaterEnter.svg +1 -0
  69. package/src/assets/icons/pc/theaterExit.svg +1 -0
  70. package/src/assets/icons/pc/volumeLarge.svg +1 -0
  71. package/src/assets/icons/pc/volumeMuted.svg +1 -0
  72. package/src/assets/icons/pc/volumeSmall.svg +1 -0
  73. package/src/config/defaultPreset.ts +110 -0
  74. package/src/config/playerOptionMobile.ts +166 -0
  75. package/src/config/playerOptionPc.ts +147 -0
  76. package/src/config/playerPreset.ts +242 -0
  77. package/src/constants/api.ts +13 -0
  78. package/src/constants/event.ts +577 -0
  79. package/src/constants/player.ts +84 -0
  80. package/src/constants/plugin.ts +19 -0
  81. package/src/constants/umdMap.ts +137 -0
  82. package/src/constants/umdPlugins.json +68 -0
  83. package/src/core/index.ts +7 -0
  84. package/src/core/player.ts +2713 -0
  85. package/src/core/playerData.ts +812 -0
  86. package/src/env.d.ts +47 -0
  87. package/src/index.ts +24 -0
  88. package/src/index.umd.ts +54 -0
  89. package/src/interface/adaptRange.d.ts +33 -0
  90. package/src/interface/api.ts +132 -0
  91. package/src/interface/autoBitrate.d.ts +41 -0
  92. package/src/interface/index.ts +1685 -0
  93. package/src/interface/rtm.ts +56 -0
  94. package/src/interface/sdkErrorPlugin.ts +145 -0
  95. package/src/interface/subtitle.ts +381 -0
  96. package/src/interface/video.ts +107 -0
  97. package/src/interface/xgplayer.ts +748 -0
  98. package/src/lang/constants.ts +69 -0
  99. package/src/lang/en.ts +98 -0
  100. package/src/lang/index.ts +33 -0
  101. package/src/lang/jp.ts +100 -0
  102. package/src/lang/zh-hk.ts +80 -0
  103. package/src/lang/zh.ts +79 -0
  104. package/src/license/index.ts +315 -0
  105. package/src/license/ttlicense2.js +15 -0
  106. package/src/license/ttlicense2.wasm +0 -0
  107. package/src/music/icons/back.svg +3 -0
  108. package/src/music/icons/forward.svg +3 -0
  109. package/src/music/icons/loop.svg +10 -0
  110. package/src/music/icons/order.svg +10 -0
  111. package/src/music/icons/pause-circle.svg +23 -0
  112. package/src/music/icons/play-circle.svg +22 -0
  113. package/src/music/icons/random.svg +10 -0
  114. package/src/music/icons/sloop.svg +10 -0
  115. package/src/music/index.ts +5 -0
  116. package/src/music/music.ts +550 -0
  117. package/src/music/plugins/index.less +185 -0
  118. package/src/music/plugins/index.ts +6 -0
  119. package/src/music/plugins/musicBackward.ts +82 -0
  120. package/src/music/plugins/musicCover.ts +45 -0
  121. package/src/music/plugins/musicForward.ts +82 -0
  122. package/src/music/plugins/musicMeta.ts +32 -0
  123. package/src/music/plugins/musicMode.ts +152 -0
  124. package/src/music/plugins/musicNext.ts +93 -0
  125. package/src/music/plugins/musicPrev.ts +94 -0
  126. package/src/music/preset.ts +69 -0
  127. package/src/music/xhr.ts +37 -0
  128. package/src/plugins/common/extendPluginFactory.ts +132 -0
  129. package/src/plugins/common/mobilePlayerPanel.ts +253 -0
  130. package/src/plugins/external/LiveInfoPanel.ts +340 -0
  131. package/src/plugins/external/ad/adsPlugin.ts +1 -0
  132. package/src/plugins/external/aiSubtitleIconPlugin.ts +270 -0
  133. package/src/plugins/external/aiSubtitlePlugin.ts +452 -0
  134. package/src/plugins/external/definitionDemotePlugin.ts +591 -0
  135. package/src/plugins/external/memoryPlay.ts +247 -0
  136. package/src/plugins/external/mirrorPlugin.ts +141 -0
  137. package/src/plugins/external/playList/OptionList.ts +204 -0
  138. package/src/plugins/external/playList/index.ts +743 -0
  139. package/src/plugins/external/subtitle/index.ts +672 -0
  140. package/src/plugins/external/subtitle/nativeSubTitle.ts +115 -0
  141. package/src/plugins/external/timeShiftPlugin.ts +484 -0
  142. package/src/plugins/external/watermark/dynamicWatermark.less +13 -0
  143. package/src/plugins/external/watermark/dynamicWatermark.ts +449 -0
  144. package/src/plugins/external/watermark/dynamicWatermarkPlugin.ts +185 -0
  145. package/src/plugins/inner/common/autoplayPlugin.ts +435 -0
  146. package/src/plugins/inner/common/danmu/container.ts +120 -0
  147. package/src/plugins/inner/common/danmu/index.ts +683 -0
  148. package/src/plugins/inner/common/danmu/lang.ts +139 -0
  149. package/src/plugins/inner/common/danmu/panel.ts +20 -0
  150. package/src/plugins/inner/common/danmu/slider.ts +210 -0
  151. package/src/plugins/inner/common/danmu/state.ts +118 -0
  152. package/src/plugins/inner/common/danmu/switch.ts +74 -0
  153. package/src/plugins/inner/common/definitionBasePlugin.ts +353 -0
  154. package/src/plugins/inner/common/errorPlugin.ts +544 -0
  155. package/src/plugins/inner/common/liveLogger.ts +137 -0
  156. package/src/plugins/inner/common/poster/index.less +66 -0
  157. package/src/plugins/inner/common/poster/index.ts +178 -0
  158. package/src/plugins/inner/common/refreshPlugin.ts +88 -0
  159. package/src/plugins/inner/common/rtmPlugin.ts +62 -0
  160. package/src/plugins/inner/common/toastPlugin.ts +90 -0
  161. package/src/plugins/inner/common/unmutePlugin.ts +133 -0
  162. package/src/plugins/inner/common/vodLogger.ts +80 -0
  163. package/src/plugins/inner/mobile/DefinitionMobilePlugin.ts +217 -0
  164. package/src/plugins/inner/mobile/LineMobilePlugins.ts +169 -0
  165. package/src/plugins/inner/mobile/MoreButtonPlugin.ts +176 -0
  166. package/src/plugins/inner/mobile/PlaybackRatePlugin.ts +199 -0
  167. package/src/plugins/inner/pc/definitionPlugin.ts +203 -0
  168. package/src/plugins/inner/pc/multilinePlugin.ts +93 -0
  169. package/src/sdkPlugin/abr.ts +67 -0
  170. package/src/sdkPlugin/adaptRange.ts +49 -0
  171. package/src/sdkPlugin/authToken.ts +557 -0
  172. package/src/sdkPlugin/sdkPlugin.ts +125 -0
  173. package/src/sdkPlugin/sdkPluginManager.ts +157 -0
  174. package/src/sdkPlugin/strategy.ts +47 -0
  175. package/src/strategy/index.ts +740 -0
  176. package/src/strategy/vestrategy-h265-wrapper.ts +34 -0
  177. package/src/strategy/vestrategy-preload-wrapper.ts +414 -0
  178. package/src/streamAdapters/base.ts +89 -0
  179. package/src/streamAdapters/dash.ts +230 -0
  180. package/src/streamAdapters/default.ts +53 -0
  181. package/src/streamAdapters/hls.ts +278 -0
  182. package/src/streamAdapters/index.ts +40 -0
  183. package/src/streamAdapters/mp4.ts +214 -0
  184. package/src/style/bytelive/danmu.less +293 -0
  185. package/src/style/bytelive/definitionIcon.less +80 -0
  186. package/src/style/bytelive/error.less +165 -0
  187. package/src/style/bytelive/index.less +62 -0
  188. package/src/style/bytelive/loading.less +5 -0
  189. package/src/style/bytelive/mobiePlugin.less +2 -0
  190. package/src/style/bytelive/mobile.less +76 -0
  191. package/src/style/bytelive/moreButton.less +79 -0
  192. package/src/style/bytelive/panel.less +259 -0
  193. package/src/style/bytelive/pc.less +161 -0
  194. package/src/style/bytelive/refresh.less +3 -0
  195. package/src/style/bytelive/reset.less +4 -0
  196. package/src/style/bytelive/toast.less +61 -0
  197. package/src/style/bytelive/unmute.less +65 -0
  198. package/src/style/external/LiveInfoPanel.less +41 -0
  199. package/src/style/external/aisub.less +139 -0
  200. package/src/style/external/aisubIcon.less +25 -0
  201. package/src/style/external/index.less +5 -0
  202. package/src/style/external/larkWindow.less +36 -0
  203. package/src/style/external/mirror.less +27 -0
  204. package/src/style/external/playList.less +258 -0
  205. package/src/style/external/timeShift.less +102 -0
  206. package/src/style/external/vttSubtitle.less +25 -0
  207. package/src/utils/debug.ts +62 -0
  208. package/src/utils/definition.ts +61 -0
  209. package/src/utils/escapeHtml.ts +93 -0
  210. package/src/utils/eventMiddleWare.ts +108 -0
  211. package/src/utils/index.ts +621 -0
  212. package/src/utils/intervalTimer.ts +38 -0
  213. package/src/utils/isHijackBrowser.ts +71 -0
  214. package/src/utils/proxy.ts +139 -0
  215. package/src/utils/storage.ts +34 -0
  216. package/src/utils/time.ts +19 -0
  217. package/src/utils/toast/index.less +20 -0
  218. package/src/utils/toast/index.ts +21 -0
  219. package/src/utils/token.ts +395 -0
  220. package/src/utils/u8a.ts +4 -0
  221. package/src/utils/umdLoader.ts +193 -0
  222. package/src/utils/video.ts +43 -0
  223. package/src/utils/xhr.ts +160 -0
  224. package/src/veError/error.ts +301 -0
  225. package/src/veError/index.ts +681 -0
  226. package/src/veError/playerProxy.ts +69 -0
  227. package/tsconfig.json +27 -0
  228. package/index.d.ts +0 -6874
  229. package/index.min.css +0 -1
  230. package/index.min.js +0 -2
  231. package/plugin/DashAbralgo.js +0 -2
  232. package/plugin/XGVideo.js +0 -2
  233. package/plugin/danmuMask.js +0 -2
  234. package/plugin/danmujs.js +0 -3
  235. package/plugin/dash.js +0 -2
  236. package/plugin/flv.js +0 -2
  237. package/plugin/hls.js +0 -2
  238. package/plugin/hlsEncrypt.js +0 -2
  239. package/plugin/mp4Encrypt.js +0 -2
  240. package/plugin/preloader.js +0 -2
  241. package/plugin/streamprobe.js +0 -2
  242. package/plugin/vestrategy.js +0 -1
  243. package/plugin/vestrategy_adapt_range.js +0 -1
  244. package/plugin/vestrategy_h265.js +0 -1
  245. package/plugin/vestrategy_preload.js +0 -1
@@ -0,0 +1,544 @@
1
+ import { PluginEvents } from '@/constants/event';
2
+ import Player, {
3
+ Events,
4
+ IXGI18nText,
5
+ Plugin,
6
+ STATE_CLASS,
7
+ Util,
8
+ } from 'xgplayer';
9
+ import ErrorSvg from '../../../assets/common/error.svg';
10
+ import { VE_DEBUG } from '@/utils';
11
+ import PlayerData from '@/core/playerData';
12
+ import { VE_ERROR_TYPE_MAP, veErrorCodeMap } from '@/veError/error';
13
+ import { VE_ERROR_EVENT, type IVeError } from '@/veError';
14
+ import dayjs from 'dayjs';
15
+ import type { POSITIONS } from '@/interface';
16
+ import type VodLogger from '@byted/xgplayer-app-logger/es/logger';
17
+ import { showToast } from '@/utils/toast';
18
+
19
+ export interface ErrorPluginConfig {
20
+ // 是否展示 error 信息
21
+ showError?: boolean;
22
+ errorImg?: boolean;
23
+ errorTips?: boolean;
24
+ isNeedRefreshButton?: boolean;
25
+ isNeedCopyButton?: boolean;
26
+ isNeedDemoteBack?: boolean;
27
+ oriErrRetryCount?: number;
28
+ // 重试周期
29
+ oriErrRetryCycle?: number;
30
+ // 是否需要异步分析错误
31
+ needSyncAnalyzeError?: boolean;
32
+ // 分析错误超时时间
33
+ analyzeTimeout?: number;
34
+
35
+ // error事件发生后重试一轮
36
+ retryLoopNum?: number;
37
+ errorTipsText?: Record<string, string>;
38
+ [propName: string]: unknown;
39
+ }
40
+
41
+ export default class ErrorPlugin extends Plugin {
42
+ // 点播日志配置错误
43
+ private isVodLogConError: boolean;
44
+ private _errDetailList: (() => void | Record<string, any>)[] = [];
45
+ private errNoteMap: Record<number | string, IXGI18nText>;
46
+
47
+ // 插件的名称,将作为插件实例的唯一key值
48
+ static get pluginName() {
49
+ return 'sdkErrorPlugin';
50
+ }
51
+
52
+ static get defaultConfig(): ErrorPluginConfig {
53
+ return {
54
+ // 是否展示 error 信息
55
+ showError: true,
56
+ errorImg: true,
57
+ errorTips: true,
58
+ isNeedRefreshButton: true,
59
+ isNeedCopyButton: true,
60
+ isNeedDemoteBack: false,
61
+ allowDemoteToDifferentLine: true,
62
+
63
+ // error事件发生后重试一轮
64
+ retryLoopNum: 1,
65
+ errorTipsText: null,
66
+ // 挂载在controls的右侧,如果不指定则默认挂载在播放器根节点上
67
+ position: Plugin.POSITIONS.ROOT as POSITIONS,
68
+ };
69
+ }
70
+
71
+ public playerData: PlayerData;
72
+ public $errorDom: any;
73
+ public veError?: IVeError;
74
+
75
+ public player: Player & {
76
+ vodLogger: VodLogger;
77
+ };
78
+
79
+ readonly langText: {
80
+ clickButton: string;
81
+ errorTips: string;
82
+ errorCode: string;
83
+ did: string;
84
+ playTime: string;
85
+ copyError: string;
86
+ copySuccess: string;
87
+ };
88
+
89
+ public state = {
90
+ showError: false,
91
+ retryTimes: 0,
92
+ idx: 0,
93
+ };
94
+
95
+ constructor(args: any) {
96
+ super(args);
97
+ this.playerData = args.player.config.playerData;
98
+ }
99
+
100
+ afterCreate() {
101
+ this.initEvents();
102
+ const extendLangMap = Object.values(veErrorCodeMap).reduce((pre, cur) => {
103
+ const { code, textKey } = cur;
104
+ pre[code] = this.i18n[textKey];
105
+ return pre;
106
+ }, {});
107
+ this.errNoteMap = {
108
+ other: this.i18n['ERROR_TYPES']?.other?.msg ?? 'error',
109
+ 1: this.i18n['MEDIA_ERR_ABORTED'],
110
+ 2: this.i18n['MEDIA_ERR_NETWORK'],
111
+ 3: this.i18n['MEDIA_ERR_DECODE'],
112
+ ...extendLangMap,
113
+ };
114
+ }
115
+
116
+ registerLanguageTexts() {
117
+ return {
118
+ clickButton: {
119
+ jp: 'リフレッシュ',
120
+ en: 'Refresh',
121
+ zh: '刷新',
122
+ 'zh-hk': '刷新',
123
+ },
124
+ errorTips: {
125
+ jp: '再生中にエラーが発生しました。[更新]をクリックして試してください',
126
+ en: 'An error occurred while playing, click refresh to try',
127
+ zh: '播放发生错误,点击刷新试试吧',
128
+ 'zh-hk': '播放發生錯誤,點擊刷新試試吧',
129
+ },
130
+ errorCode: {
131
+ jp: 'コード',
132
+ en: 'code',
133
+ zh: '错误码',
134
+ 'zh-hk': '錯誤碼',
135
+ },
136
+ did: {
137
+ jp: 'デバイスID',
138
+ en: 'deviceID',
139
+ zh: '设备 ID',
140
+ 'zh-hk': '設備 ID',
141
+ },
142
+ playTime: {
143
+ jp: '遊び時間',
144
+ en: 'playTime',
145
+ zh: '播放时间',
146
+ 'zh-hk': '播放時間',
147
+ },
148
+ copyError: {
149
+ jp: 'コピーエラー',
150
+ en: 'Copy error',
151
+ zh: '复制错误信息',
152
+ 'zh-hk': '複製錯誤訊息',
153
+ },
154
+ copySuccess: {
155
+ jp: 'コピー成功',
156
+ en: 'Copy success',
157
+ zh: '复制成功',
158
+ 'zh-hk': '複製成功',
159
+ },
160
+ };
161
+ }
162
+
163
+ registerIcons() {
164
+ return {
165
+ errorImg: {
166
+ icon: ErrorSvg,
167
+ class: 'xgplayer-error-svg',
168
+ },
169
+ };
170
+ }
171
+
172
+ updateLang() {
173
+ if (!this.$errorDom) return;
174
+
175
+ const $errorTips = this.$errorDom.querySelector('.xgplayer-error-tips');
176
+ if ($errorTips) {
177
+ const tipsTxt =
178
+ this.config.errorTipsText?.[this.lang] ?? this.langText.errorTips;
179
+ $errorTips.innerHTML = tipsTxt;
180
+ }
181
+
182
+ const $errorButton = this.$errorDom.querySelector('.xgplayer-error-button');
183
+ if ($errorButton) {
184
+ $errorButton.innerHTML = this.langText.clickButton;
185
+ }
186
+ }
187
+ initEvents() {
188
+ // 监听veError的事件,该error数据已经被细分
189
+ this.player.on(VE_ERROR_EVENT, this.handleError);
190
+ if (this.player.config.isLive) {
191
+ this.player.on(Events.ENDED, this.handleEnd);
192
+ this.player.on(PluginEvents.REFRESH_CLICK, this.closeError);
193
+ }
194
+ this.on(Events.CANPLAY, this.closeError);
195
+ this.on(Events.PLAYING, this.closeError);
196
+ }
197
+
198
+ handleError = async (veError: IVeError) => {
199
+ this.isVodLogConError =
200
+ veError?.errorCode === veErrorCodeMap.VOD_LOG_NOT_CONFIGURED.code;
201
+ if (this.player) {
202
+ this.player.pause();
203
+ }
204
+ if (this.config.isNeedDemoteBack) {
205
+ // 切换备用地址
206
+ this.state.showError = false;
207
+ this.changeMainOrBackup();
208
+ } else {
209
+ this.state.showError = true;
210
+ }
211
+
212
+ this.renderError(veError);
213
+ };
214
+
215
+ handleEnd = () => {
216
+ if (this.player) {
217
+ this.player.pause();
218
+ }
219
+ if (this.config.isNeedDemoteBack) {
220
+ this.changeMainOrBackup();
221
+ }
222
+ };
223
+
224
+ getRetryListForCurrentDefinition() {
225
+ const stream = this.playerData.getCurrentStreams()[0];
226
+ return [stream?.url, ...(stream?.backUrlList ?? [])];
227
+ }
228
+
229
+ getRetryListForCurrentDefinitionAcrossAllLines() {
230
+ const allStreams = this.playerData.getStreamsMatchCurrentDefinition();
231
+ const res = [];
232
+ allStreams.forEach(item => {
233
+ res.push(item.url, ...(item.backUrlList ?? []));
234
+ });
235
+ return res;
236
+ }
237
+
238
+ changeMainOrBackup() {
239
+ this.player.addClass(STATE_CLASS.ENTER);
240
+ const retryList = this.config.allowDemoteToDifferentLine
241
+ ? this.getRetryListForCurrentDefinitionAcrossAllLines()
242
+ : this.getRetryListForCurrentDefinition();
243
+ VE_DEBUG.log('[errorPlugin] retryList', retryList);
244
+ const maxRetryNum = this.config.retryLoopNum * retryList.length;
245
+ VE_DEBUG.log('[errorPlugin] maxRetryNum', maxRetryNum);
246
+ VE_DEBUG.log(`[errorPlugin] retry times: ${this.state.retryTimes}`);
247
+
248
+ const idx =
249
+ (retryList.indexOf(this.player.config.url) + 1) % retryList.length;
250
+ this.state.idx = idx;
251
+ VE_DEBUG.log('[errorPlugin] next fallback url idx', idx);
252
+ const nextUrl = retryList[idx];
253
+ VE_DEBUG.log('[errorPlugin] next fallback url', nextUrl);
254
+
255
+ if (!nextUrl || this.state.retryTimes === maxRetryNum) {
256
+ this.player.removeClass(STATE_CLASS.ENTER);
257
+ if (this.state.retryTimes === maxRetryNum) {
258
+ this.player.emit(PluginEvents.PLAY_BACKUP_CHANGE_FINISH);
259
+ VE_DEBUG.log('[errorPlugin] all demote ended');
260
+ this.player.emit(PluginEvents.DEMOTE_ALL_DONE);
261
+ }
262
+ this.state.showError = true;
263
+ this.renderError();
264
+ this.resetTimes();
265
+ VE_DEBUG.log(
266
+ '[sdkErrorPlugin] fallback for current definition has ended',
267
+ );
268
+ return;
269
+ }
270
+
271
+ this.player.once('canplay', () => {
272
+ this.state.retryTimes = 0;
273
+ });
274
+
275
+ this.player.emit(PluginEvents.PLAY_BACKUP_CHANGE, {
276
+ currentUrl: this.player.config.url,
277
+ nextUrl: nextUrl,
278
+ retryTimes: this.state.retryTimes,
279
+ });
280
+
281
+ this.changeUrl(nextUrl);
282
+ this.state.retryTimes = this.state.retryTimes + 1;
283
+ }
284
+
285
+ changeUrl(url: string) {
286
+ this.player.config.url = url;
287
+ this.player.switchURL(url, false);
288
+
289
+ this.player.emit(PluginEvents.PlAY_URL_CHANGE, {
290
+ url,
291
+ from: PluginEvents.PLAY_BACKUP_CHANGE,
292
+ });
293
+ }
294
+
295
+ resetTimes() {
296
+ this.state.retryTimes = 0;
297
+ this.state.idx = 0;
298
+ }
299
+
300
+ renderError(error?: IVeError) {
301
+ if (!this.config.showError) {
302
+ return;
303
+ }
304
+ const $contianer: HTMLElement = this.root;
305
+ if (!$contianer) {
306
+ return;
307
+ }
308
+ $contianer.innerHTML = '';
309
+ $contianer.style.display = 'none';
310
+
311
+ // vod的log error会一直展示无法关闭,直到配置正确line_app_id
312
+ if (this.state.showError || this.isVodLogConError) {
313
+ this.player.emit(PluginEvents.SHOW_PLAY_ERROR);
314
+ $contianer.style.display = 'block';
315
+ $contianer.appendChild(this.getErrorDom(error));
316
+ }
317
+ // vod的log error 一直是暂停状态无法播放
318
+ if (this.isVodLogConError) {
319
+ this.player.blur();
320
+ this.player.pause();
321
+ }
322
+ }
323
+
324
+ addErrDetail(err) {
325
+ if (Array.isArray(err)) {
326
+ this._errDetailList = [...this._errDetailList, ...err];
327
+ } else {
328
+ this._errDetailList.push(err);
329
+ }
330
+ }
331
+
332
+ getErrorInfo(error: IVeError) {
333
+ const code = error.veErrorCode || error.errorCode;
334
+ const vodLogger = this.player?.vodLogger;
335
+ const liveLogger = this.player?.getPlugin('liveLogger');
336
+ const did = this.player.config.isLive
337
+ ? liveLogger?._userId
338
+ : vodLogger?.tracker?.getConfig()?.user?.user_unique_id;
339
+ const appId = this.player.config.isLive
340
+ ? liveLogger?.config?.appId
341
+ : vodLogger?.line_app_id;
342
+ const playTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
343
+ return {
344
+ code,
345
+ appId,
346
+ did: did || 'unknown',
347
+ playTime,
348
+ };
349
+ }
350
+
351
+ getErrorInfoDom(error: IVeError) {
352
+ const { code, did, playTime } = this.getErrorInfo(error);
353
+ const infoWrapDom = Util.createDom(
354
+ 'div',
355
+ '',
356
+ undefined,
357
+ 'xgplayer-error-info-wrap',
358
+ );
359
+ const codeEl = `<p class="xgplayer-error-info-p">
360
+ <span class="xgplayer-error-info-title">${
361
+ this.langText.errorCode || 'code'
362
+ }</span>
363
+ <span class="xgplayer-error-info-content">${code}</span>
364
+ </p>`;
365
+ infoWrapDom.appendChild(Util.createDomFromHtml(codeEl));
366
+ const didEl = `<p class="xgplayer-error-info-p">
367
+ <span class="xgplayer-error-info-title">${
368
+ this.langText.did || 'deviceID'
369
+ }</span>
370
+ <span class="xgplayer-error-info-content">${did}</span>
371
+ </p>`;
372
+ infoWrapDom.appendChild(Util.createDomFromHtml(didEl));
373
+ const playTimeEl = `<p class="xgplayer-error-info-p">
374
+ <span class="xgplayer-error-info-title">${
375
+ this.langText.playTime || 'playTime'
376
+ }</span>
377
+ <span class="xgplayer-error-info-content">${playTime}</span>
378
+ </p>`;
379
+ infoWrapDom.appendChild(Util.createDomFromHtml(playTimeEl));
380
+
381
+ return infoWrapDom;
382
+ }
383
+
384
+ handleCopyBtnClick(error: IVeError) {
385
+ const { did, playTime, appId } = this.getErrorInfo(error);
386
+ try {
387
+ const _error = JSON.stringify({
388
+ ...error,
389
+ playTime,
390
+ deviceID: did,
391
+ appId,
392
+ });
393
+ navigator.clipboard.writeText(_error).then(() => {
394
+ showToast(this.langText.copySuccess || 'copySuccess');
395
+ });
396
+ } catch (err) {
397
+ VE_DEBUG.error('copy error info err ', err);
398
+ }
399
+ }
400
+
401
+ getActionDom(error: IVeError) {
402
+ const errorType: string = error.errorType as unknown as string;
403
+ const actionWrapDom = Util.createDomFromHtml(
404
+ '<div class="xgplayer-error-action-wrap"></div>',
405
+ );
406
+
407
+ // token、serve、license错误无法通过重试恢复
408
+ const canRefresh = !(
409
+ errorType &&
410
+ [
411
+ VE_ERROR_TYPE_MAP[10],
412
+ VE_ERROR_TYPE_MAP[11],
413
+ VE_ERROR_TYPE_MAP[12],
414
+ ].includes(errorType)
415
+ );
416
+
417
+ if (this.config.isNeedRefreshButton && canRefresh) {
418
+ const refreshBtnDom = Util.createDom(
419
+ 'div',
420
+ this.langText.clickButton || 'Refresh',
421
+ undefined,
422
+ 'xgplayer-error-button refresh-button',
423
+ );
424
+
425
+ this.bind('.refresh-button', 'click', this.handleRefreshClick);
426
+ actionWrapDom.appendChild(refreshBtnDom);
427
+ }
428
+
429
+ if (this.config.isNeedCopyButton) {
430
+ const copyBtnDom = Util.createDom(
431
+ 'div',
432
+ this.langText.copyError || 'CopyError',
433
+ undefined,
434
+ 'xgplayer-error-button copy-button',
435
+ );
436
+
437
+ this.bind(
438
+ '.copy-button',
439
+ 'click',
440
+ this.handleCopyBtnClick.bind(this, error),
441
+ );
442
+ actionWrapDom.appendChild(copyBtnDom);
443
+ }
444
+
445
+ return actionWrapDom;
446
+ }
447
+
448
+ getErrorDom(error: IVeError) {
449
+ const { errorCode, message, displayMessage } = error;
450
+ if (!this.$errorDom) {
451
+ this.$errorDom = Util.createDom(
452
+ 'div',
453
+ '',
454
+ undefined,
455
+ 'xgplayer-error-cover',
456
+ );
457
+ if (this.config.errorImg) {
458
+ const $errorImg = Util.createDom(
459
+ 'div',
460
+ '',
461
+ undefined,
462
+ 'xgplayer-error-img',
463
+ );
464
+ $errorImg.appendChild((this.icons as any).errorImg);
465
+ this.$errorDom.appendChild($errorImg);
466
+ }
467
+
468
+ if (this.config.errorTips) {
469
+ let tipsTxt = '';
470
+ if (this.config?.errorTipsText?.[this.lang]) {
471
+ tipsTxt = this.config.errorTipsText?.[this.lang];
472
+ } else if (error) {
473
+ // 优先使用displayMessage,其次使用error map字典,再次使用player的自带errorNote,再其次使用error的message,再使用error map的error,最后兜底error
474
+ tipsTxt =
475
+ displayMessage ||
476
+ this.errNoteMap[errorCode] ||
477
+ (this.player.errorNote && this.i18n[this.player.errorNote]) ||
478
+ message ||
479
+ this.errNoteMap.other ||
480
+ 'error';
481
+ } else {
482
+ tipsTxt = this.langText.errorTips;
483
+ }
484
+ const $errorTips = Util.createDom(
485
+ 'div',
486
+ tipsTxt,
487
+ undefined,
488
+ 'xgplayer-error-tips',
489
+ );
490
+ this.$errorDom.appendChild($errorTips);
491
+ }
492
+ this.$errorDom.appendChild(this.getErrorInfoDom(error));
493
+
494
+ this.$errorDom.appendChild(this.getActionDom(error));
495
+
496
+ if (this._errDetailList?.length) {
497
+ this._errDetailList?.forEach(item => {
498
+ const isFunction = typeof item == 'function';
499
+ const errDetail = isFunction ? item() : item;
500
+ if (errDetail) {
501
+ const $errorDetail = Util.createDom(
502
+ 'div',
503
+ `${errDetail.label}: ${errDetail.value}`,
504
+ undefined,
505
+ 'xgplayer-error-detail-info',
506
+ );
507
+ this.$errorDom.appendChild($errorDetail);
508
+ }
509
+ });
510
+ }
511
+ this.emit('sdkerrorplugin_render', this.$errorDom);
512
+ }
513
+ return this.$errorDom;
514
+ }
515
+
516
+ handleRefreshClick = () => {
517
+ this.player?.emit(PluginEvents.PLAY_ERROR_BUTTON_CLICK);
518
+ this.player.removeClass(STATE_CLASS.PAUSED);
519
+ this.closeError();
520
+ this.player?.retry();
521
+ };
522
+
523
+ closeError = () => {
524
+ if (!this.state.showError) {
525
+ return;
526
+ }
527
+ this.state.showError = false;
528
+ this.renderError();
529
+ };
530
+
531
+ destroy() {
532
+ // 播放器销毁的时候一些逻辑
533
+ this.player.off(Events.ERROR, this.handleError);
534
+ if (this.player.config.isLive) {
535
+ this.player.off(Events.ENDED, this.handleEnd);
536
+ }
537
+ }
538
+
539
+ render() {
540
+ return this.config.showError
541
+ ? '<xgplayer-error class="xgplayer-error"></xgplayer-error>'
542
+ : '';
543
+ }
544
+ }
@@ -0,0 +1,137 @@
1
+ import XgplayerLiverLogger from '@byted/xgplayer-live-logger';
2
+ import Tea from '@datarangers/sdk-javascript';
3
+ import { Plugin } from 'xgplayer';
4
+ import pkgJson from '../../../../package.json';
5
+ import { VE_DEBUG } from '@/utils';
6
+
7
+ const deps = pkgJson.dependencies || {};
8
+
9
+ interface Config {
10
+ appid?: string;
11
+ appName?: string;
12
+ channel?: 'cn';
13
+ channelDomain?: string;
14
+ userId?: string;
15
+ deviceId?: string;
16
+ }
17
+
18
+ const CN_APPID = import.meta.env.__LIVE_LOG_TEA_ID__;
19
+ const CHANNEL = import.meta.env.__LIVE_LOG_CHANNEL__;
20
+
21
+ const DEVICE_ID_KEY = 'veplayer_live_device_id';
22
+ const USER_ID_Key = 'veplayer_live_user_id';
23
+
24
+ /**
25
+ * 随机生成几位随机数
26
+ */
27
+ const genRandomID = (length: number) =>
28
+ Math.floor((Math.random() * 9 + 1) * Math.pow(10, length - 1)).toString();
29
+
30
+ /**
31
+ * 生成device_id
32
+ */
33
+ const getDeviceID = () => {
34
+ let deviceId = localStorage.getItem(DEVICE_ID_KEY);
35
+ if (deviceId) {
36
+ return deviceId;
37
+ }
38
+ deviceId = genRandomID(11);
39
+ localStorage.setItem(DEVICE_ID_KEY, deviceId);
40
+ return deviceId;
41
+ };
42
+
43
+ /**
44
+ * 生成user_id
45
+ */
46
+ const getUserId = () => {
47
+ let userId = localStorage.getItem(USER_ID_Key);
48
+ if (userId) {
49
+ return userId;
50
+ }
51
+ userId = genRandomID(12);
52
+ localStorage.setItem(USER_ID_Key, userId);
53
+ return userId;
54
+ };
55
+
56
+ export default class LiveLogger extends Plugin {
57
+ public _userId?: string;
58
+ private _deviceId?: string;
59
+ static get pluginName() {
60
+ return 'liveLogger';
61
+ }
62
+
63
+ static get defaultConfig() {
64
+ return {
65
+ appId: '',
66
+ enable: true,
67
+ };
68
+ }
69
+
70
+ private _liveLogger;
71
+
72
+ afterCreate() {
73
+ if (!this.config.enable) {
74
+ return;
75
+ }
76
+ if (!this.config.appId) {
77
+ VE_DEBUG.warn('not found appid, logger is closed');
78
+ return;
79
+ }
80
+
81
+ this._userId = this.config?.userId || getUserId();
82
+ this._deviceId = this.config?.deviceId || getDeviceID();
83
+ this.openLog(this.config);
84
+ this.player.plugins?.sdkerrorplugin?.addErrDetail(() => {
85
+ if (this._liveLogger) {
86
+ return {
87
+ label: 'userId',
88
+ value: this._liveLogger?.logger?.options?.user_id,
89
+ };
90
+ }
91
+ });
92
+ }
93
+
94
+ private _createTea(config: Config) {
95
+ Tea.init({
96
+ app_id: CN_APPID,
97
+ channel: CHANNEL,
98
+ });
99
+ Tea.start();
100
+ Tea.config({
101
+ user_unique_id: this._userId,
102
+ device_id: this._deviceId,
103
+ });
104
+ }
105
+
106
+ startLog(config) {
107
+ this._createTea(config);
108
+ this._liveLogger = new XgplayerLiverLogger({
109
+ Tea: Tea,
110
+ player: this.player,
111
+ aid: config.appId,
112
+ project_key: config.appId,
113
+ app_name: config.appId,
114
+ user_id: this._userId,
115
+ device_id: this._deviceId,
116
+ ext: {
117
+ veplayer_version: __VERSION__,
118
+ flv_version: deps['xgplayer-flv'],
119
+ hls_version: deps['xgplayer-hls'],
120
+ rts_version: deps['@byted/xgplayer-rts'],
121
+ },
122
+ });
123
+ }
124
+
125
+ closeLog() {
126
+ this._liveLogger.destroy();
127
+ this._liveLogger = null;
128
+ }
129
+
130
+ openLog(config?: Config) {
131
+ !this._liveLogger &&
132
+ this.startLog({
133
+ ...this.config,
134
+ ...config,
135
+ });
136
+ }
137
+ }