@give-tech/ec-player-vue 0.0.1-beta.53 → 0.0.1-beta.55
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/dist/composables/usePlayer.d.ts.map +1 -1
- package/dist/ec-player-vue.css +78 -78
- package/dist/index.js +17 -7
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePlayer.d.ts","sourceRoot":"","sources":["../../src/composables/usePlayer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAA0C,KAAK,GAAG,EAAE,MAAM,KAAK,CAAA;AACtE,OAAO,EACL,YAAY,EAIZ,KAAK,aAAa,EAElB,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,UAAU,CAAA;AAG5D;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,kBAAkB;IAClB,SAAS,EAAE,GAAG,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAA;IACxC,YAAY;IACZ,MAAM,EAAE,GAAG,CAAC,YAAY,GAAG,IAAI,CAAC,CAAA;IAChC,YAAY;IACZ,KAAK,EAAE,aAAa,CAAA;IACpB,aAAa;IACb,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IACvB,aAAa;IACb,YAAY,EAAE,GAAG,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAA;IAC5C,aAAa;IACb,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC3B,aAAa;IACb,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IACpB,SAAS;IACT,IAAI,EAAE,CAAC,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACnE,WAAW;IACX,UAAU,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;IAC5C,aAAa;IACb,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,SAAS;IACT,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,WAAW;IACX,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3B,SAAS;IACT,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,WAAW;IACX,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,cAAc;IACd,SAAS,EAAE,MAAM,YAAY,GAAG,IAAI,CAAA;IACpC,WAAW;IACX,QAAQ,EAAE,MAAM,aAAa,CAAA;IAC7B,aAAa;IACb,cAAc,EAAE,MAAM,MAAM,CAAA;IAC5B,YAAY;IACZ,WAAW,EAAE,MAAM,MAAM,CAAA;IACzB,YAAY;IACZ,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAkBD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,eAAe,
|
|
1
|
+
{"version":3,"file":"usePlayer.d.ts","sourceRoot":"","sources":["../../src/composables/usePlayer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAA0C,KAAK,GAAG,EAAE,MAAM,KAAK,CAAA;AACtE,OAAO,EACL,YAAY,EAIZ,KAAK,aAAa,EAElB,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,UAAU,CAAA;AAG5D;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,kBAAkB;IAClB,SAAS,EAAE,GAAG,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAA;IACxC,YAAY;IACZ,MAAM,EAAE,GAAG,CAAC,YAAY,GAAG,IAAI,CAAC,CAAA;IAChC,YAAY;IACZ,KAAK,EAAE,aAAa,CAAA;IACpB,aAAa;IACb,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IACvB,aAAa;IACb,YAAY,EAAE,GAAG,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAA;IAC5C,aAAa;IACb,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC3B,aAAa;IACb,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IACpB,SAAS;IACT,IAAI,EAAE,CAAC,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACnE,WAAW;IACX,UAAU,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;IAC5C,aAAa;IACb,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,SAAS;IACT,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,WAAW;IACX,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3B,SAAS;IACT,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,WAAW;IACX,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,cAAc;IACd,SAAS,EAAE,MAAM,YAAY,GAAG,IAAI,CAAA;IACpC,WAAW;IACX,QAAQ,EAAE,MAAM,aAAa,CAAA;IAC7B,aAAa;IACb,cAAc,EAAE,MAAM,MAAM,CAAA;IAC5B,YAAY;IACZ,WAAW,EAAE,MAAM,MAAM,CAAA;IACzB,YAAY;IACZ,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAkBD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,eAAe,CAqV9D"}
|
package/dist/ec-player-vue.css
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
|
|
2
|
-
.gt-player-container[data-v-
|
|
2
|
+
.gt-player-container[data-v-48277fd4] {
|
|
3
3
|
width: 100%;
|
|
4
4
|
height: 100%;
|
|
5
5
|
}
|
|
6
|
-
.gt-video-wrapper[data-v-
|
|
6
|
+
.gt-video-wrapper[data-v-48277fd4] {
|
|
7
7
|
position: relative;
|
|
8
8
|
width: 100%;
|
|
9
9
|
height: 100%;
|
|
10
10
|
background: #000;
|
|
11
11
|
overflow: hidden;
|
|
12
12
|
}
|
|
13
|
-
.gt-player-canvas[data-v-
|
|
13
|
+
.gt-player-canvas[data-v-48277fd4] {
|
|
14
14
|
width: 100%;
|
|
15
15
|
height: 100%;
|
|
16
16
|
object-fit: contain;
|
|
17
17
|
}
|
|
18
|
-
.gt-placeholder[data-v-
|
|
18
|
+
.gt-placeholder[data-v-48277fd4] {
|
|
19
19
|
position: absolute;
|
|
20
20
|
top: 0;
|
|
21
21
|
left: 0;
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/* 加载中动画 */
|
|
31
|
-
.gt-loading-spinner[data-v-
|
|
31
|
+
.gt-loading-spinner[data-v-48277fd4] {
|
|
32
32
|
position: absolute;
|
|
33
33
|
top: 0;
|
|
34
34
|
left: 0;
|
|
@@ -40,13 +40,13 @@
|
|
|
40
40
|
background: rgba(0, 0, 0, 0.5);
|
|
41
41
|
z-index: 10;
|
|
42
42
|
}
|
|
43
|
-
.gt-loading-content[data-v-
|
|
43
|
+
.gt-loading-content[data-v-48277fd4] {
|
|
44
44
|
display: flex;
|
|
45
45
|
flex-direction: column;
|
|
46
46
|
align-items: center;
|
|
47
47
|
gap: 16px;
|
|
48
48
|
}
|
|
49
|
-
.gt-spinner[data-v-
|
|
49
|
+
.gt-spinner[data-v-48277fd4] {
|
|
50
50
|
width: 10%;
|
|
51
51
|
max-width: 48px;
|
|
52
52
|
min-width: 20px;
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
border: 0.3em solid rgba(255, 255, 255, 0.3);
|
|
55
55
|
border-top-color: #fff;
|
|
56
56
|
border-radius: 50%;
|
|
57
|
-
animation: gt-spin-
|
|
57
|
+
animation: gt-spin-48277fd4 1s linear infinite;
|
|
58
58
|
}
|
|
59
|
-
.gt-loading-text[data-v-
|
|
59
|
+
.gt-loading-text[data-v-48277fd4] {
|
|
60
60
|
color: rgba(255, 255, 255, 0.85);
|
|
61
61
|
font-size: 13px;
|
|
62
62
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/* 加载进度条 */
|
|
68
|
-
.gt-loading-progress[data-v-
|
|
68
|
+
.gt-loading-progress[data-v-48277fd4] {
|
|
69
69
|
display: flex;
|
|
70
70
|
align-items: center;
|
|
71
71
|
gap: 12px;
|
|
@@ -73,20 +73,20 @@
|
|
|
73
73
|
width: 100%;
|
|
74
74
|
max-width: 280px;
|
|
75
75
|
}
|
|
76
|
-
.gt-progress-bar-bg[data-v-
|
|
76
|
+
.gt-progress-bar-bg[data-v-48277fd4] {
|
|
77
77
|
flex: 1;
|
|
78
78
|
height: 4px;
|
|
79
79
|
background: rgba(255, 255, 255, 0.2);
|
|
80
80
|
border-radius: 2px;
|
|
81
81
|
overflow: hidden;
|
|
82
82
|
}
|
|
83
|
-
.gt-progress-bar-fill[data-v-
|
|
83
|
+
.gt-progress-bar-fill[data-v-48277fd4] {
|
|
84
84
|
height: 100%;
|
|
85
85
|
background: linear-gradient(90deg, #ff2d55, #ff6b6b);
|
|
86
86
|
border-radius: 2px;
|
|
87
87
|
transition: width 0.2s ease;
|
|
88
88
|
}
|
|
89
|
-
.gt-progress-text[data-v-
|
|
89
|
+
.gt-progress-text[data-v-48277fd4] {
|
|
90
90
|
color: rgba(255, 255, 255, 0.9);
|
|
91
91
|
font-size: 12px;
|
|
92
92
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
@@ -94,14 +94,14 @@
|
|
|
94
94
|
text-align: right;
|
|
95
95
|
font-variant-numeric: tabular-nums;
|
|
96
96
|
}
|
|
97
|
-
@keyframes gt-spin-
|
|
97
|
+
@keyframes gt-spin-48277fd4 {
|
|
98
98
|
to {
|
|
99
99
|
transform: rotate(360deg);
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
/* 错误提示 */
|
|
104
|
-
.gt-error-message[data-v-
|
|
104
|
+
.gt-error-message[data-v-48277fd4] {
|
|
105
105
|
position: absolute;
|
|
106
106
|
top: 0;
|
|
107
107
|
left: 0;
|
|
@@ -112,9 +112,9 @@ to {
|
|
|
112
112
|
justify-content: center;
|
|
113
113
|
background: rgba(0, 0, 0, 0.75);
|
|
114
114
|
z-index: 15;
|
|
115
|
-
animation: gt-error-fade-in-
|
|
115
|
+
animation: gt-error-fade-in-48277fd4 0.25s ease;
|
|
116
116
|
}
|
|
117
|
-
@keyframes gt-error-fade-in-
|
|
117
|
+
@keyframes gt-error-fade-in-48277fd4 {
|
|
118
118
|
from {
|
|
119
119
|
opacity: 0;
|
|
120
120
|
}
|
|
@@ -122,7 +122,7 @@ to {
|
|
|
122
122
|
opacity: 1;
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
|
-
.gt-error-content[data-v-
|
|
125
|
+
.gt-error-content[data-v-48277fd4] {
|
|
126
126
|
display: flex;
|
|
127
127
|
align-items: flex-start;
|
|
128
128
|
gap: 16px;
|
|
@@ -134,9 +134,9 @@ to {
|
|
|
134
134
|
min-width: 280px;
|
|
135
135
|
backdrop-filter: blur(12px);
|
|
136
136
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.05) inset;
|
|
137
|
-
animation: gt-error-slide-in-
|
|
137
|
+
animation: gt-error-slide-in-48277fd4 0.3s ease;
|
|
138
138
|
}
|
|
139
|
-
@keyframes gt-error-slide-in-
|
|
139
|
+
@keyframes gt-error-slide-in-48277fd4 {
|
|
140
140
|
from {
|
|
141
141
|
opacity: 0;
|
|
142
142
|
transform: translateY(-10px) scale(0.95);
|
|
@@ -146,7 +146,7 @@ to {
|
|
|
146
146
|
transform: translateY(0) scale(1);
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
|
-
.gt-error-icon-wrapper[data-v-
|
|
149
|
+
.gt-error-icon-wrapper[data-v-48277fd4] {
|
|
150
150
|
flex-shrink: 0;
|
|
151
151
|
width: 40px;
|
|
152
152
|
height: 40px;
|
|
@@ -156,28 +156,28 @@ to {
|
|
|
156
156
|
background: rgba(255, 77, 77, 0.2);
|
|
157
157
|
border-radius: 50%;
|
|
158
158
|
}
|
|
159
|
-
.gt-error-icon[data-v-
|
|
159
|
+
.gt-error-icon[data-v-48277fd4] {
|
|
160
160
|
width: 22px;
|
|
161
161
|
height: 22px;
|
|
162
162
|
color: #ff4d4f;
|
|
163
163
|
}
|
|
164
|
-
.gt-error-body[data-v-
|
|
164
|
+
.gt-error-body[data-v-48277fd4] {
|
|
165
165
|
flex: 1;
|
|
166
166
|
min-width: 0;
|
|
167
167
|
}
|
|
168
|
-
.gt-error-title[data-v-
|
|
168
|
+
.gt-error-title[data-v-48277fd4] {
|
|
169
169
|
color: #ff4d4f;
|
|
170
170
|
font-size: 15px;
|
|
171
171
|
font-weight: 600;
|
|
172
172
|
margin-bottom: 6px;
|
|
173
173
|
}
|
|
174
|
-
.gt-error-text[data-v-
|
|
174
|
+
.gt-error-text[data-v-48277fd4] {
|
|
175
175
|
color: rgba(255, 255, 255, 0.85);
|
|
176
176
|
font-size: 13px;
|
|
177
177
|
line-height: 1.5;
|
|
178
178
|
word-break: break-word;
|
|
179
179
|
}
|
|
180
|
-
.gt-error-retry[data-v-
|
|
180
|
+
.gt-error-retry[data-v-48277fd4] {
|
|
181
181
|
flex-shrink: 0;
|
|
182
182
|
width: 32px;
|
|
183
183
|
height: 32px;
|
|
@@ -192,17 +192,17 @@ to {
|
|
|
192
192
|
transition: all 0.2s ease;
|
|
193
193
|
margin-left: 8px;
|
|
194
194
|
}
|
|
195
|
-
.gt-error-retry[data-v-
|
|
195
|
+
.gt-error-retry[data-v-48277fd4]:hover {
|
|
196
196
|
background: rgba(255, 255, 255, 0.25);
|
|
197
197
|
color: white;
|
|
198
198
|
}
|
|
199
|
-
.gt-error-retry svg[data-v-
|
|
199
|
+
.gt-error-retry svg[data-v-48277fd4] {
|
|
200
200
|
width: 20px;
|
|
201
201
|
height: 20px;
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
/* 视频控制条 */
|
|
205
|
-
.gt-video-controls[data-v-
|
|
205
|
+
.gt-video-controls[data-v-48277fd4] {
|
|
206
206
|
position: absolute;
|
|
207
207
|
bottom: 0;
|
|
208
208
|
left: 0;
|
|
@@ -215,13 +215,13 @@ to {
|
|
|
215
215
|
align-items: center;
|
|
216
216
|
gap: 12px;
|
|
217
217
|
}
|
|
218
|
-
.gt-flex-spacer[data-v-
|
|
218
|
+
.gt-flex-spacer[data-v-48277fd4] {
|
|
219
219
|
flex: 1;
|
|
220
220
|
}
|
|
221
|
-
.gt-video-controls.visible[data-v-
|
|
221
|
+
.gt-video-controls.visible[data-v-48277fd4] {
|
|
222
222
|
opacity: 1;
|
|
223
223
|
}
|
|
224
|
-
.gt-control-btn[data-v-
|
|
224
|
+
.gt-control-btn[data-v-48277fd4] {
|
|
225
225
|
width: 28px;
|
|
226
226
|
height: 28px;
|
|
227
227
|
border: none;
|
|
@@ -235,14 +235,14 @@ to {
|
|
|
235
235
|
transition: all 0.2s ease;
|
|
236
236
|
flex-shrink: 0;
|
|
237
237
|
}
|
|
238
|
-
.gt-control-btn[data-v-
|
|
238
|
+
.gt-control-btn[data-v-48277fd4]:hover {
|
|
239
239
|
background: rgba(255, 255, 255, 0.2);
|
|
240
240
|
}
|
|
241
|
-
.gt-control-btn svg[data-v-
|
|
241
|
+
.gt-control-btn svg[data-v-48277fd4] {
|
|
242
242
|
width: 14px;
|
|
243
243
|
height: 14px;
|
|
244
244
|
}
|
|
245
|
-
.gt-progress-container[data-v-
|
|
245
|
+
.gt-progress-container[data-v-48277fd4] {
|
|
246
246
|
flex: 1;
|
|
247
247
|
position: relative;
|
|
248
248
|
cursor: pointer;
|
|
@@ -250,7 +250,7 @@ to {
|
|
|
250
250
|
display: flex;
|
|
251
251
|
align-items: center;
|
|
252
252
|
}
|
|
253
|
-
.gt-progress-bar[data-v-
|
|
253
|
+
.gt-progress-bar[data-v-48277fd4] {
|
|
254
254
|
width: 100%;
|
|
255
255
|
height: 4px;
|
|
256
256
|
background: rgba(255, 255, 255, 0.2);
|
|
@@ -259,10 +259,10 @@ to {
|
|
|
259
259
|
overflow: hidden;
|
|
260
260
|
transition: height 0.2s ease;
|
|
261
261
|
}
|
|
262
|
-
.gt-progress-container:hover .gt-progress-bar[data-v-
|
|
262
|
+
.gt-progress-container:hover .gt-progress-bar[data-v-48277fd4] {
|
|
263
263
|
height: 6px;
|
|
264
264
|
}
|
|
265
|
-
.gt-progress-buffered[data-v-
|
|
265
|
+
.gt-progress-buffered[data-v-48277fd4] {
|
|
266
266
|
position: absolute;
|
|
267
267
|
top: 0;
|
|
268
268
|
left: 0;
|
|
@@ -271,7 +271,7 @@ to {
|
|
|
271
271
|
border-radius: 2px;
|
|
272
272
|
transition: width 0.1s;
|
|
273
273
|
}
|
|
274
|
-
.gt-progress-played[data-v-
|
|
274
|
+
.gt-progress-played[data-v-48277fd4] {
|
|
275
275
|
position: absolute;
|
|
276
276
|
top: 0;
|
|
277
277
|
left: 0;
|
|
@@ -280,7 +280,7 @@ to {
|
|
|
280
280
|
border-radius: 2px;
|
|
281
281
|
transition: width 0.1s;
|
|
282
282
|
}
|
|
283
|
-
.gt-progress-handle[data-v-
|
|
283
|
+
.gt-progress-handle[data-v-48277fd4] {
|
|
284
284
|
position: absolute;
|
|
285
285
|
right: -6px;
|
|
286
286
|
top: 50%;
|
|
@@ -293,11 +293,11 @@ to {
|
|
|
293
293
|
opacity: 0;
|
|
294
294
|
transition: opacity 0.2s, transform 0.2s;
|
|
295
295
|
}
|
|
296
|
-
.gt-progress-container:hover .gt-progress-handle[data-v-
|
|
296
|
+
.gt-progress-container:hover .gt-progress-handle[data-v-48277fd4] {
|
|
297
297
|
opacity: 1;
|
|
298
298
|
transform: translateY(-50%) scale(1.2);
|
|
299
299
|
}
|
|
300
|
-
.gt-progress-tooltip[data-v-
|
|
300
|
+
.gt-progress-tooltip[data-v-48277fd4] {
|
|
301
301
|
position: absolute;
|
|
302
302
|
bottom: calc(100% + 8px);
|
|
303
303
|
transform: translateX(-50%);
|
|
@@ -309,20 +309,20 @@ to {
|
|
|
309
309
|
white-space: nowrap;
|
|
310
310
|
pointer-events: none;
|
|
311
311
|
}
|
|
312
|
-
.gt-time-display[data-v-
|
|
312
|
+
.gt-time-display[data-v-48277fd4] {
|
|
313
313
|
color: white;
|
|
314
314
|
font-size: 12px;
|
|
315
315
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
316
316
|
white-space: nowrap;
|
|
317
317
|
flex-shrink: 0;
|
|
318
318
|
}
|
|
319
|
-
.gt-time-separator[data-v-
|
|
319
|
+
.gt-time-separator[data-v-48277fd4] {
|
|
320
320
|
margin: 0 4px;
|
|
321
321
|
opacity: 0.6;
|
|
322
322
|
}
|
|
323
323
|
|
|
324
324
|
/* 直播指示器 */
|
|
325
|
-
.gt-live-indicator[data-v-
|
|
325
|
+
.gt-live-indicator[data-v-48277fd4] {
|
|
326
326
|
display: flex;
|
|
327
327
|
align-items: center;
|
|
328
328
|
gap: 6px;
|
|
@@ -330,17 +330,17 @@ to {
|
|
|
330
330
|
font-size: 12px;
|
|
331
331
|
font-weight: 500;
|
|
332
332
|
}
|
|
333
|
-
.gt-live-dot[data-v-
|
|
333
|
+
.gt-live-dot[data-v-48277fd4] {
|
|
334
334
|
width: 8px;
|
|
335
335
|
height: 8px;
|
|
336
336
|
background: #ff2d55;
|
|
337
337
|
border-radius: 50%;
|
|
338
|
-
animation: gt-live-pulse-
|
|
338
|
+
animation: gt-live-pulse-48277fd4 1.5s ease-in-out infinite;
|
|
339
339
|
}
|
|
340
|
-
.gt-live-text[data-v-
|
|
340
|
+
.gt-live-text[data-v-48277fd4] {
|
|
341
341
|
color: #ff2d55;
|
|
342
342
|
}
|
|
343
|
-
@keyframes gt-live-pulse-
|
|
343
|
+
@keyframes gt-live-pulse-48277fd4 {
|
|
344
344
|
0%, 100% {
|
|
345
345
|
opacity: 1;
|
|
346
346
|
}
|
|
@@ -350,21 +350,21 @@ to {
|
|
|
350
350
|
}
|
|
351
351
|
|
|
352
352
|
/* 倍速按钮 */
|
|
353
|
-
.gt-playback-rate-container[data-v-
|
|
353
|
+
.gt-playback-rate-container[data-v-48277fd4] {
|
|
354
354
|
position: relative;
|
|
355
355
|
flex-shrink: 0;
|
|
356
356
|
}
|
|
357
|
-
.gt-playback-rate-btn[data-v-
|
|
357
|
+
.gt-playback-rate-btn[data-v-48277fd4] {
|
|
358
358
|
width: auto !important;
|
|
359
359
|
min-width: 28px;
|
|
360
360
|
padding: 0 8px;
|
|
361
361
|
font-size: 12px;
|
|
362
362
|
font-weight: 500;
|
|
363
363
|
}
|
|
364
|
-
.gt-playback-rate-text[data-v-
|
|
364
|
+
.gt-playback-rate-text[data-v-48277fd4] {
|
|
365
365
|
color: white;
|
|
366
366
|
}
|
|
367
|
-
.gt-playback-rate-menu[data-v-
|
|
367
|
+
.gt-playback-rate-menu[data-v-48277fd4] {
|
|
368
368
|
position: absolute;
|
|
369
369
|
bottom: calc(100% + 8px);
|
|
370
370
|
left: 50%;
|
|
@@ -374,15 +374,15 @@ to {
|
|
|
374
374
|
padding: 4px 0;
|
|
375
375
|
min-width: 60px;
|
|
376
376
|
z-index: 20;
|
|
377
|
-
animation: gt-rate-menu-fade-in-
|
|
377
|
+
animation: gt-rate-menu-fade-in-48277fd4 0.15s ease;
|
|
378
378
|
}
|
|
379
|
-
@keyframes gt-rate-menu-fade-in-
|
|
379
|
+
@keyframes gt-rate-menu-fade-in-48277fd4 {
|
|
380
380
|
from { opacity: 0; transform: translateX(-50%) translateY(4px);
|
|
381
381
|
}
|
|
382
382
|
to { opacity: 1; transform: translateX(-50%) translateY(0);
|
|
383
383
|
}
|
|
384
384
|
}
|
|
385
|
-
.gt-playback-rate-option[data-v-
|
|
385
|
+
.gt-playback-rate-option[data-v-48277fd4] {
|
|
386
386
|
display: block;
|
|
387
387
|
width: 100%;
|
|
388
388
|
padding: 6px 12px;
|
|
@@ -394,17 +394,17 @@ to { opacity: 1; transform: translateX(-50%) translateY(0);
|
|
|
394
394
|
text-align: center;
|
|
395
395
|
transition: all 0.15s ease;
|
|
396
396
|
}
|
|
397
|
-
.gt-playback-rate-option[data-v-
|
|
397
|
+
.gt-playback-rate-option[data-v-48277fd4]:hover {
|
|
398
398
|
background: rgba(255, 255, 255, 0.1);
|
|
399
399
|
color: white;
|
|
400
400
|
}
|
|
401
|
-
.gt-playback-rate-option.active[data-v-
|
|
401
|
+
.gt-playback-rate-option.active[data-v-48277fd4] {
|
|
402
402
|
color: #ff2d55;
|
|
403
403
|
font-weight: 500;
|
|
404
404
|
}
|
|
405
405
|
|
|
406
406
|
/* 居中播放按钮 */
|
|
407
|
-
.gt-center-play-btn[data-v-
|
|
407
|
+
.gt-center-play-btn[data-v-48277fd4] {
|
|
408
408
|
position: absolute;
|
|
409
409
|
top: 50%;
|
|
410
410
|
left: 50%;
|
|
@@ -419,11 +419,11 @@ to { opacity: 1; transform: translateX(-50%) translateY(0);
|
|
|
419
419
|
cursor: pointer;
|
|
420
420
|
transition: all 0.2s ease;
|
|
421
421
|
}
|
|
422
|
-
.gt-center-play-btn[data-v-
|
|
422
|
+
.gt-center-play-btn[data-v-48277fd4]:hover {
|
|
423
423
|
background: rgba(0, 0, 0, 0.7);
|
|
424
424
|
transform: translate(-50%, -50%) scale(1.05);
|
|
425
425
|
}
|
|
426
|
-
.gt-center-play-btn svg[data-v-
|
|
426
|
+
.gt-center-play-btn svg[data-v-48277fd4] {
|
|
427
427
|
width: 24px;
|
|
428
428
|
height: 24px;
|
|
429
429
|
color: white;
|
|
@@ -431,7 +431,7 @@ to { opacity: 1; transform: translateX(-50%) translateY(0);
|
|
|
431
431
|
}
|
|
432
432
|
|
|
433
433
|
/* 环境信息面板 */
|
|
434
|
-
.gt-env-panel[data-v-
|
|
434
|
+
.gt-env-panel[data-v-48277fd4] {
|
|
435
435
|
position: absolute;
|
|
436
436
|
top: 12px;
|
|
437
437
|
left: 12px;
|
|
@@ -445,60 +445,60 @@ to { opacity: 1; transform: translateX(-50%) translateY(0);
|
|
|
445
445
|
max-height: calc(100% - 24px);
|
|
446
446
|
overflow-y: auto;
|
|
447
447
|
backdrop-filter: blur(10px);
|
|
448
|
-
animation: gt-env-fade-in-
|
|
448
|
+
animation: gt-env-fade-in-48277fd4 0.15s ease;
|
|
449
449
|
z-index: 15;
|
|
450
450
|
}
|
|
451
|
-
.gt-env-panel[data-v-
|
|
451
|
+
.gt-env-panel[data-v-48277fd4]::-webkit-scrollbar {
|
|
452
452
|
width: 4px;
|
|
453
453
|
}
|
|
454
|
-
.gt-env-panel[data-v-
|
|
454
|
+
.gt-env-panel[data-v-48277fd4]::-webkit-scrollbar-track {
|
|
455
455
|
background: transparent;
|
|
456
456
|
}
|
|
457
|
-
.gt-env-panel[data-v-
|
|
457
|
+
.gt-env-panel[data-v-48277fd4]::-webkit-scrollbar-thumb {
|
|
458
458
|
background: rgba(255, 255, 255, 0.3);
|
|
459
459
|
border-radius: 2px;
|
|
460
460
|
}
|
|
461
|
-
@keyframes gt-env-fade-in-
|
|
461
|
+
@keyframes gt-env-fade-in-48277fd4 {
|
|
462
462
|
from { opacity: 0; transform: translateY(-4px);
|
|
463
463
|
}
|
|
464
464
|
to { opacity: 1; transform: translateY(0);
|
|
465
465
|
}
|
|
466
466
|
}
|
|
467
|
-
.gt-env-panel .gt-env-row[data-v-
|
|
467
|
+
.gt-env-panel .gt-env-row[data-v-48277fd4] {
|
|
468
468
|
display: flex;
|
|
469
469
|
justify-content: space-between;
|
|
470
470
|
align-items: center;
|
|
471
471
|
padding: 2px 0;
|
|
472
472
|
color: rgba(255, 255, 255, 0.7);
|
|
473
473
|
}
|
|
474
|
-
.gt-env-panel .gt-env-row span[data-v-
|
|
474
|
+
.gt-env-panel .gt-env-row span[data-v-48277fd4]:first-child {
|
|
475
475
|
color: rgba(255, 255, 255, 0.5);
|
|
476
476
|
margin-right: 16px;
|
|
477
477
|
}
|
|
478
|
-
.gt-env-panel .gt-env-row span[data-v-
|
|
478
|
+
.gt-env-panel .gt-env-row span[data-v-48277fd4]:last-child {
|
|
479
479
|
font-variant-numeric: tabular-nums;
|
|
480
480
|
}
|
|
481
|
-
.gt-env-panel .gt-env-row .ok[data-v-
|
|
481
|
+
.gt-env-panel .gt-env-row .ok[data-v-48277fd4] { color: #4caf50;
|
|
482
482
|
}
|
|
483
|
-
.gt-env-panel .gt-env-row .warn[data-v-
|
|
483
|
+
.gt-env-panel .gt-env-row .warn[data-v-48277fd4] { color: #ff9800;
|
|
484
484
|
}
|
|
485
|
-
.gt-env-panel .gt-env-row .err[data-v-
|
|
485
|
+
.gt-env-panel .gt-env-row .err[data-v-48277fd4] { color: #f44336;
|
|
486
486
|
}
|
|
487
|
-
.gt-env-divider[data-v-
|
|
487
|
+
.gt-env-divider[data-v-48277fd4] {
|
|
488
488
|
height: 1px;
|
|
489
489
|
background: rgba(255, 255, 255, 0.1);
|
|
490
490
|
margin: 4px 0;
|
|
491
491
|
}
|
|
492
492
|
|
|
493
493
|
/* 播放中缓冲提示 */
|
|
494
|
-
.gt-buffering-indicator[data-v-
|
|
494
|
+
.gt-buffering-indicator[data-v-48277fd4] {
|
|
495
495
|
position: absolute;
|
|
496
496
|
top: 50%;
|
|
497
497
|
left: 50%;
|
|
498
498
|
transform: translate(-50%, -50%);
|
|
499
499
|
z-index: 10;
|
|
500
500
|
}
|
|
501
|
-
.gt-buffering-content[data-v-
|
|
501
|
+
.gt-buffering-content[data-v-48277fd4] {
|
|
502
502
|
display: flex;
|
|
503
503
|
align-items: center;
|
|
504
504
|
gap: 8px;
|
|
@@ -508,11 +508,11 @@ to { opacity: 1; transform: translateY(0);
|
|
|
508
508
|
color: white;
|
|
509
509
|
font-size: 13px;
|
|
510
510
|
}
|
|
511
|
-
.gt-buffering-spinner[data-v-
|
|
511
|
+
.gt-buffering-spinner[data-v-48277fd4] {
|
|
512
512
|
width: 16px;
|
|
513
513
|
height: 16px;
|
|
514
514
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
515
515
|
border-top-color: #fff;
|
|
516
516
|
border-radius: 50%;
|
|
517
|
-
animation: gt-spin-
|
|
517
|
+
animation: gt-spin-48277fd4 1s linear infinite;
|
|
518
518
|
}
|
package/dist/index.js
CHANGED
|
@@ -169,6 +169,10 @@ function usePlayer(emit) {
|
|
|
169
169
|
}
|
|
170
170
|
},
|
|
171
171
|
onError: (error) => {
|
|
172
|
+
if (error?.name === "AbortError") {
|
|
173
|
+
console.log("[EcPlayer] Error ignored (AbortError - expected during stream switch)");
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
172
176
|
console.error("[EcPlayer] Error:", error);
|
|
173
177
|
const currentTime = player.value?.getCurrentTime() ?? 0;
|
|
174
178
|
emit.error(error, currentTime);
|
|
@@ -243,16 +247,22 @@ function usePlayer(emit) {
|
|
|
243
247
|
canvas: canvasRef.value,
|
|
244
248
|
isLive: isLiveParam
|
|
245
249
|
};
|
|
246
|
-
|
|
247
|
-
|
|
250
|
+
const currentPlayer = new EcPlayerCore(config, callbacks);
|
|
251
|
+
player.value = currentPlayer;
|
|
252
|
+
await currentPlayer.load(url, isLiveParam);
|
|
248
253
|
if (currentRequestId !== playRequestId) {
|
|
249
254
|
console.log("[EcPlayer] Request obsolete after load, aborting");
|
|
250
|
-
if (player.value) {
|
|
251
|
-
|
|
255
|
+
if (player.value === currentPlayer) {
|
|
256
|
+
currentPlayer.destroy();
|
|
252
257
|
player.value = null;
|
|
253
258
|
}
|
|
254
259
|
return;
|
|
255
260
|
}
|
|
261
|
+
if (player.value !== currentPlayer) {
|
|
262
|
+
console.log("[EcPlayer] Player replaced by newer request, aborting");
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (!player.value) return;
|
|
256
266
|
detectedFormat.value = player.value.getDetectedFormat();
|
|
257
267
|
state.isLoaded = true;
|
|
258
268
|
await player.value.play();
|
|
@@ -682,7 +692,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
682
692
|
errorMessage.value = null;
|
|
683
693
|
player.play(parsed);
|
|
684
694
|
}
|
|
685
|
-
}, { immediate: true
|
|
695
|
+
}, { immediate: true });
|
|
686
696
|
onMounted(() => {
|
|
687
697
|
document.addEventListener("fullscreenchange", handleFullscreenChange);
|
|
688
698
|
});
|
|
@@ -834,7 +844,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
834
844
|
retry: handleRetry
|
|
835
845
|
}, () => [
|
|
836
846
|
createElementVNode("div", _hoisted_29, [
|
|
837
|
-
_cache[28] || (_cache[28] = createStaticVNode('<div class="gt-error-icon-wrapper" data-v-
|
|
847
|
+
_cache[28] || (_cache[28] = createStaticVNode('<div class="gt-error-icon-wrapper" data-v-48277fd4><svg class="gt-error-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" data-v-48277fd4><circle cx="12" cy="12" r="10" data-v-48277fd4></circle><line x1="12" y1="8" x2="12" y2="12" data-v-48277fd4></line><line x1="12" y1="16" x2="12.01" y2="16" data-v-48277fd4></line></svg></div>', 1)),
|
|
838
848
|
createElementVNode("div", _hoisted_30, [
|
|
839
849
|
_cache[26] || (_cache[26] = createElementVNode("div", { class: "gt-error-title" }, "播放失败", -1)),
|
|
840
850
|
createElementVNode("div", _hoisted_31, toDisplayString(errorMessage.value), 1)
|
|
@@ -985,7 +995,7 @@ const _export_sfc = (sfc, props) => {
|
|
|
985
995
|
}
|
|
986
996
|
return target;
|
|
987
997
|
};
|
|
988
|
-
const EcPlayer = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
998
|
+
const EcPlayer = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-48277fd4"]]);
|
|
989
999
|
export {
|
|
990
1000
|
DEFAULT_EC_PLAYER_PROPS,
|
|
991
1001
|
DEFAULT_PLAYER_OPTIONS,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/types.ts","../src/composables/usePlayer.ts","../src/composables/useControls.ts","../src/composables/useEnvInfo.ts","../src/components/EcPlayer.vue"],"sourcesContent":["/**\n * Vue 组件类型定义\n */\n\nimport type { EcPlayerCoreConfig, EcPlayerState, EnvInfo } from '@give-tech/ec-player'\nexport { LogLevel } from '@give-tech/ec-player'\n\n/**\n * WASM 路径配置\n */\nexport interface WasmPathConfig {\n /**\n * SIMD 版本 WASM 路径(推荐,性能更好)\n * 用于支持 SIMD 的现代浏览器\n */\n simd?: string\n /**\n * 非 SIMD 版本 WASM 路径\n * 用于不支持 SIMD 的旧浏览器作为降级方案\n */\n nonSimd?: string\n}\n\n/**\n * 全局配置\n *\n * 必须在使用 EcPlayer 组件前调用\n *\n * @example\n * ```typescript\n * // main.ts\n * import { configureEcPlayer } from '@give-tech/ec-player-vue'\n *\n * // 配置 SIMD 和非 SIMD 路径,支持自动降级\n * configureEcPlayer({\n * wasm: {\n * simd: '/wasm/decoder-simd.js',\n * nonSimd: '/wasm/decoder.js'\n * }\n * })\n *\n * // 使用 CDN\n * configureEcPlayer({\n * wasm: {\n * simd: 'https://unpkg.com/@give-tech/ec-player@latest/wasm/decoder-simd.js',\n * nonSimd: 'https://unpkg.com/@give-tech/ec-player@latest/wasm/decoder.js'\n * }\n * })\n * ```\n */\nexport interface EcPlayerGlobalConfig {\n /**\n * WASM 路径配置(必填)\n * 支持 SIMD 和非 SIMD 自动降级\n */\n wasm?: WasmPathConfig\n /** 默认是否显示控制条 */\n showControls?: boolean\n /** 默认缓冲目标帧数 */\n targetBufferSize?: number\n /** 默认每批解码帧数 */\n decodeBatchSize?: number\n /** 默认队列上限 */\n maxQueueSize?: number\n}\n\n/**\n * 播放器配置选项\n */\nexport interface PlayerOptions {\n /** 帧缓冲区目标大小 */\n targetBufferSize?: number\n /** 每批解码帧数 */\n decodeBatchSize?: number\n /** NAL/Sample 队列上限 */\n maxQueueSize?: number\n}\n\n/**\n * 视频源配置\n */\nexport interface EcPlayerSource {\n /** 视频 URL */\n url: string\n /** 是否为直播流 */\n isLive?: boolean\n /** 版本号,用于强制重新加载 */\n version?: number\n}\n\n/**\n * 控制条选项\n */\nexport interface EcPlayerControlOption {\n /** 是否显示控制条 */\n show?: boolean\n /** 是否显示全屏按钮 */\n fullscreen?: boolean\n /** 是否显示环境信息按钮 */\n info?: boolean\n /** 是否显示倍速按钮(仅点播) */\n playbackRate?: boolean\n}\n\n/**\n * EcPlayer 组件 Props\n */\nexport interface EcPlayerProps {\n /** 视频源 */\n src?: EcPlayerSource\n /** 控制条选项 */\n controlOption?: EcPlayerControlOption\n}\n\n/**\n * EcPlayer 组件 Emits\n */\nexport interface EcPlayerEmits {\n /** 播放时触发 */\n (e: 'play'): void\n /** 暂停时触发 */\n (e: 'pause'): void\n /** 发生错误时触发 */\n (e: 'error', error: Error, currentTime: number): void\n /** 点击重试时触发 */\n (e: 'retry'): void\n}\n\n/**\n * EcPlayer 组件 Expose 方法\n */\nexport interface EcPlayerExpose {\n /** 播放 */\n play: () => void\n /** 暂停 */\n pause: () => void\n /** 跳转到指定时间(毫秒) */\n seek: (time: number) => Promise<void>\n /** 销毁播放器,释放资源(用于 v-if 切换前手动释放) */\n destroy: () => void\n /** 获取播放器状态 */\n getState: () => EcPlayerState\n}\n\n/**\n * 环境信息面板数据\n */\nexport interface EnvPanelData {\n envInfo: EnvInfo | null\n state: Partial<EcPlayerState>\n}\n\n/**\n * 播放器配置(从 Props 转换)\n */\nexport interface PlayerConfigFromProps extends EcPlayerCoreConfig {\n wasmPath: string\n targetBufferSize: number\n decodeBatchSize: number\n maxQueueSize: number\n}\n\n/**\n * 默认 Props 值\n */\nexport const DEFAULT_EC_PLAYER_PROPS = {\n src: undefined as EcPlayerSource | undefined,\n controlOption: {\n show: true,\n fullscreen: true,\n info: true,\n playbackRate: true\n }\n}\n\n/**\n * 默认播放器配置\n */\nexport const DEFAULT_PLAYER_OPTIONS: Required<PlayerOptions> = {\n targetBufferSize: 60,\n decodeBatchSize: 2,\n maxQueueSize: 200\n}\n\n/**\n * 默认 WASM 配置\n */\nexport const DEFAULT_WASM_CONFIG: WasmPathConfig = {\n simd: '/wasm/decoder-simd.js',\n nonSimd: '/wasm/decoder.js'\n}\n\n/**\n * 全局配置存储\n */\nlet globalConfig: EcPlayerGlobalConfig = {}\n\n/**\n * 配置 EcPlayer 全局默认值\n *\n * @example\n * ```typescript\n * // main.ts\n * import { configureEcPlayer } from '@give-tech/ec-player-vue'\n *\n * // 配置 SIMD 和非 SIMD 路径,支持自动降级\n * configureEcPlayer({\n * wasm: {\n * simd: '/wasm/decoder-simd.js',\n * nonSimd: '/wasm/decoder.js'\n * }\n * })\n *\n * // 使用 CDN\n * configureEcPlayer({\n * wasm: {\n * simd: 'https://unpkg.com/@give-tech/ec-player@latest/wasm/decoder-simd.js',\n * nonSimd: 'https://unpkg.com/@give-tech/ec-player@latest/wasm/decoder.js'\n * }\n * })\n * ```\n */\nexport function configureEcPlayer(config: EcPlayerGlobalConfig): void {\n globalConfig = { ...globalConfig, ...config }\n}\n\n/**\n * 获取全局配置\n */\nexport function getGlobalConfig(): EcPlayerGlobalConfig {\n return { ...globalConfig }\n}\n\n/**\n * 获取 WASM 路径配置\n */\nexport function getWasmConfig(): WasmPathConfig {\n return {\n simd: globalConfig.wasm?.simd ?? DEFAULT_WASM_CONFIG.simd,\n nonSimd: globalConfig.wasm?.nonSimd ?? DEFAULT_WASM_CONFIG.nonSimd\n }\n}\n\n/**\n * 获取合并后的配置(props > 全局配置 > 默认值)\n */\nexport function getMergedConfig(props: EcPlayerProps) {\n return {\n src: props.src ?? DEFAULT_EC_PLAYER_PROPS.src,\n controlOption: {\n show: props.controlOption?.show ?? globalConfig.showControls ?? DEFAULT_EC_PLAYER_PROPS.controlOption.show,\n fullscreen: props.controlOption?.fullscreen ?? DEFAULT_EC_PLAYER_PROPS.controlOption.fullscreen,\n info: props.controlOption?.info ?? DEFAULT_EC_PLAYER_PROPS.controlOption.info,\n playbackRate: props.controlOption?.playbackRate ?? DEFAULT_EC_PLAYER_PROPS.controlOption.playbackRate\n }\n }\n}\n\n/**\n * 获取合并后的播放器配置(options > 全局配置 > 默认值)\n */\nexport function getMergedPlayerOptions(options: PlayerOptions): Required<PlayerOptions> {\n return {\n targetBufferSize: options.targetBufferSize ?? globalConfig.targetBufferSize ?? DEFAULT_PLAYER_OPTIONS.targetBufferSize,\n decodeBatchSize: options.decodeBatchSize ?? globalConfig.decodeBatchSize ?? DEFAULT_PLAYER_OPTIONS.decodeBatchSize,\n maxQueueSize: options.maxQueueSize ?? globalConfig.maxQueueSize ?? DEFAULT_PLAYER_OPTIONS.maxQueueSize\n }\n}\n\n/**\n * 根据环境能力选择合适的 WASM 路径\n *\n * @param hasSimdSupport 是否支持 SIMD\n * @returns 实际使用的 WASM 路径\n */\nexport function resolveWasmPath(hasSimdSupport: boolean): string {\n const wasmConfig = getWasmConfig()\n\n // 根据 SIMD 支持情况选择\n if (hasSimdSupport && wasmConfig.simd) {\n console.log('[EcPlayer] Using SIMD WASM:', wasmConfig.simd)\n return wasmConfig.simd\n }\n\n if (wasmConfig.nonSimd) {\n console.log('[EcPlayer] SIMD not supported, using non-SIMD WASM:', wasmConfig.nonSimd)\n return wasmConfig.nonSimd\n }\n\n // 降级到 SIMD 版本(兜底)\n console.log('[EcPlayer] No non-SIMD path configured, falling back to SIMD:', wasmConfig.simd)\n return wasmConfig.simd!\n}\n\n/**\n * 预加载状态标记(用于幂等性保护)\n */\nlet preloadCalled = false\n\n/**\n * 预加载 WASM 模块\n *\n * 支持幂等性:多次调用只会执行一次预加载\n *\n * @example\n * import { preloadWasm } from '@give-tech/ec-player-vue'\n * preloadWasm() // 预加载所有\n * preloadWasm({ preloadNonSimd: false }) // 只预加载 SIMD\n */\nexport function preloadWasm(options?: {\n preloadSimd?: boolean\n preloadNonSimd?: boolean\n}): void {\n // 幂等性检查:已经调用过则直接返回\n if (preloadCalled) {\n console.log('[EcPlayer] WASM already preloaded, skip')\n return\n }\n preloadCalled = true\n\n const { preloadSimd = true, preloadNonSimd = true } = options ?? {}\n const wasmConfig = getWasmConfig()\n const paths: string[] = []\n\n if (preloadSimd && wasmConfig.simd) {\n paths.push(wasmConfig.simd)\n }\n if (preloadNonSimd && wasmConfig.nonSimd) {\n paths.push(wasmConfig.nonSimd)\n }\n\n if (paths.length > 0) {\n import('@give-tech/ec-player').then(({ WASMLoader }) => {\n WASMLoader.preloadAll(paths)\n console.log('[EcPlayer] Preloading WASM:', paths)\n }).catch(err => {\n console.warn('[EcPlayer] Failed to preload WASM:', err)\n })\n }\n}\n\n/**\n * 重置预加载状态(仅用于测试)\n */\nexport function resetPreloadState(): void {\n preloadCalled = false\n}\n","/**\n * 播放器核心逻辑\n */\n\nimport { ref, shallowRef, reactive, onUnmounted, type Ref } from 'vue'\nimport {\n EcPlayerCore,\n EnvDetector,\n setLogLevel as coreSetLogLevel,\n type EcPlayerCoreConfig,\n type EcPlayerState,\n type EcPlayerCallbacks,\n type EcLoadingStageInfo\n} from '@give-tech/ec-player'\nimport type { EcPlayerProps, PlayerOptions } from '../types'\nimport { resolveWasmPath, DEFAULT_PLAYER_OPTIONS, getGlobalConfig } from '../types'\n\n/**\n * 播放器逻辑返回值\n */\nexport interface UsePlayerReturn {\n /** Canvas 元素引用 */\n canvasRef: Ref<HTMLCanvasElement | null>\n /** 播放器实例 */\n player: Ref<EcPlayerCore | null>\n /** 响应式状态 */\n state: EcPlayerState\n /** 是否正在加载 */\n isLoading: Ref<boolean>\n /** 加载阶段信息 */\n loadingStage: Ref<EcLoadingStageInfo | null>\n /** 检测到的格式 */\n detectedFormat: Ref<string>\n /** 是否为直播流 */\n isLive: Ref<boolean>\n /** 播放 */\n play: (options: { url: string; isLive?: boolean }) => Promise<void>\n /** 设置配置 */\n setOptions: (options: PlayerOptions) => void\n /** 设置日志级别 */\n setLogLevel: (level: number) => void\n /** 暂停 */\n pause: () => void\n /** 恢复播放 */\n resume: () => Promise<void>\n /** 跳转 */\n seek: (time: number) => Promise<void>\n /** 设置倍速 */\n setPlaybackRate: (rate: number) => void\n /** 获取播放器实例 */\n getPlayer: () => EcPlayerCore | null\n /** 获取状态 */\n getState: () => EcPlayerState\n /** 获取当前时间 */\n getCurrentTime: () => number\n /** 获取总时长 */\n getDuration: () => number\n /** 销毁播放器 */\n destroy: () => void\n}\n\n/**\n * 从配置选项创建播放器配置\n */\nfunction createConfig(options: Required<PlayerOptions>): Omit<EcPlayerCoreConfig, 'isLive'> {\n // 检测 SIMD 支持\n const envInfo = EnvDetector.detect()\n const wasmPath = resolveWasmPath(envInfo.capabilities.wasmSimd)\n\n return {\n wasmPath,\n targetBufferSize: options.targetBufferSize,\n decodeBatchSize: options.decodeBatchSize,\n maxQueueSize: options.maxQueueSize\n }\n}\n\n/**\n * 播放器逻辑 Hook 的 emit 参数类型\n */\nexport interface UsePlayerEmit {\n play: () => void\n pause: () => void\n error: (error: Error, currentTime: number) => void\n retry: () => void\n}\n\n/**\n * 播放器逻辑 Hook\n */\nexport function usePlayer(emit: UsePlayerEmit): UsePlayerReturn {\n const canvasRef = ref<HTMLCanvasElement | null>(null)\n const player = shallowRef<EcPlayerCore | null>(null)\n const isLoading = ref(false)\n const loadingStage = ref<EcLoadingStageInfo | null>(null)\n const detectedFormat = ref('')\n const isLive = ref(false) // 跟踪当前是否为直播流\n\n // 用于追踪最新的 play 请求,解决快速切换源时的竞态条件\n let playRequestId = 0\n\n // 超时保护计时器\n let loadingTimeoutId: ReturnType<typeof setTimeout> | null = null\n const LOADING_TIMEOUT_MS = 10000 // 10 秒超时检查间隔\n const MAX_LOADING_TIME_MS = 120000 // 最大加载时间 2 分钟\n\n // 加载开始时间(用于最大超时保护)\n let loadingStartTime = 0\n\n // 检查加载超时(支持下载中延长等待)\n function checkLoadingTimeout(requestId: number): void {\n if (!isLoading.value || requestId !== playRequestId) return\n\n console.log('[EcPlayer] checkLoadingTimeout:', {\n stage: loadingStage.value?.stage,\n elapsed: Date.now() - loadingStartTime,\n isLoading: isLoading.value\n })\n\n // 检查是否超过最大加载时间\n if (Date.now() - loadingStartTime > MAX_LOADING_TIME_MS) {\n console.warn('[EcPlayer] Max loading time exceeded, resetting isLoading state')\n isLoading.value = false\n return\n }\n\n // 如果正在下载数据,继续等待\n if (loadingStage.value?.stage === 'loading') {\n console.log('[EcPlayer] Download in progress, extending wait...')\n loadingTimeoutId = setTimeout(() => checkLoadingTimeout(requestId), LOADING_TIMEOUT_MS)\n return\n }\n\n // 其他情况,超时重置\n console.warn('[EcPlayer] Loading timeout, resetting isLoading state')\n isLoading.value = false\n }\n\n // 当前播放器配置(初始化时合并全局配置和默认值)\n const globalConfig = getGlobalConfig()\n const currentOptions = reactive<Required<PlayerOptions>>({\n targetBufferSize: globalConfig.targetBufferSize ?? DEFAULT_PLAYER_OPTIONS.targetBufferSize,\n decodeBatchSize: globalConfig.decodeBatchSize ?? DEFAULT_PLAYER_OPTIONS.decodeBatchSize,\n maxQueueSize: globalConfig.maxQueueSize ?? DEFAULT_PLAYER_OPTIONS.maxQueueSize\n })\n\n // 响应式状态\n const state = reactive<EcPlayerState>({\n isPlaying: false,\n isLoaded: false,\n isLoading: false,\n isBuffering: false,\n bufferingProgress: undefined,\n fps: 0,\n resolution: '-',\n decoded: 0,\n downloaded: 0,\n droppedFrames: 0,\n isPrefetching: false,\n segmentIndex: 0,\n fetchedSegmentCount: 0,\n totalSegments: 0,\n downloadSpeed: 0,\n currentTime: 0,\n duration: 0,\n playbackRate: 1\n })\n\n // 创建回调\n const callbacks: EcPlayerCallbacks = {\n onStateChange: (partial) => {\n Object.assign(state, partial)\n\n // 派发 play/pause 事件\n if (partial.isPlaying === true) {\n emit.play()\n } else if (partial.isPlaying === false && state.isPlaying) {\n emit.pause()\n }\n },\n onError: (error) => {\n console.error('[EcPlayer] Error:', error)\n // emit 错误事件,携带当前播放进度\n const currentTime = player.value?.getCurrentTime() ?? 0\n emit.error(error, currentTime)\n },\n onReady: () => {\n // 清除超时计时器\n if (loadingTimeoutId) {\n clearTimeout(loadingTimeoutId)\n loadingTimeoutId = null\n }\n // 第一帧已准备好,取消 loading 状态\n isLoading.value = false\n loadingStage.value = null\n console.log('[EcPlayer] First frame ready, loading finished')\n },\n onLoadingStageChange: (info) => {\n loadingStage.value = info\n console.log('[EcPlayer] Loading stage:', info.stage, info.detail || '')\n }\n }\n\n // 播放(加载并播放)\n async function play(options: { url: string; isLive?: boolean }): Promise<void> {\n const { url, isLive: isLiveParam = false } = options\n if (!url) {\n console.error('[EcPlayer] URL is required')\n return\n }\n\n // 递增请求 ID,用于追踪这次调用\n const currentRequestId = ++playRequestId\n console.log('[EcPlayer] play() called, requestId:', currentRequestId)\n\n // 如果正在加载中,先销毁当前播放器\n if (isLoading.value && player.value) {\n console.log('[EcPlayer] Aborting current load to start new one...')\n player.value.destroy()\n player.value = null\n }\n\n // 等待 canvas 准备好\n let retries = 10\n while (!canvasRef.value && retries > 0) {\n await new Promise(resolve => setTimeout(resolve, 100))\n retries--\n }\n\n // 检查是否有更新的请求\n if (currentRequestId !== playRequestId) {\n console.log('[EcPlayer] Request obsolete after waiting for canvas, aborting')\n return\n }\n\n if (!canvasRef.value) {\n console.error('[EcPlayer] Canvas element not found')\n return\n }\n\n isLoading.value = true\n isLive.value = isLiveParam\n loadingStartTime = Date.now()\n\n // 启动超时保护\n if (loadingTimeoutId) {\n clearTimeout(loadingTimeoutId)\n }\n loadingTimeoutId = setTimeout(() => checkLoadingTimeout(currentRequestId), LOADING_TIMEOUT_MS)\n\n try {\n // 销毁旧播放器\n if (player.value) {\n player.value.destroy()\n player.value = null\n }\n\n // 重置状态\n state.isPlaying = false\n state.isLoaded = false\n state.fps = 0\n state.resolution = '-'\n state.decoded = 0\n state.downloaded = 0\n state.droppedFrames = 0\n state.segmentIndex = 0\n state.totalSegments = 0\n state.downloadSpeed = 0\n state.currentTime = 0\n state.duration = 0\n detectedFormat.value = ''\n\n // 创建配置\n const config: EcPlayerCoreConfig & { canvas: HTMLCanvasElement } = {\n ...createConfig(currentOptions),\n canvas: canvasRef.value,\n isLive: isLiveParam\n }\n\n // 创建播放器\n player.value = new EcPlayerCore(config, callbacks)\n\n // 加载视频\n await player.value.load(url, isLiveParam)\n\n // 检查是否有更新的请求\n if (currentRequestId !== playRequestId) {\n console.log('[EcPlayer] Request obsolete after load, aborting')\n // 销毁刚创建的播放器\n if (player.value) {\n player.value.destroy()\n player.value = null\n }\n return\n }\n\n // 获取检测到的格式\n detectedFormat.value = player.value.getDetectedFormat()\n\n // 标记加载完成\n state.isLoaded = true\n\n // 开始播放\n await player.value.play()\n\n console.log('[EcPlayer] Playing stream, format:', detectedFormat.value)\n } catch (error: any) {\n console.error('[EcPlayer] Failed to play:', error?.message || error)\n // 清除超时计时器\n if (loadingTimeoutId) {\n clearTimeout(loadingTimeoutId)\n loadingTimeoutId = null\n }\n // 发生错误时也要取消 loading 状态\n if (currentRequestId === playRequestId) {\n isLoading.value = false\n }\n }\n // 注意:isLoading 不在 finally 中重置,而是等待 onReady 事件\n // 这样可以确保转圈动画持续到第一帧准备好为止\n }\n\n // 设置配置\n function setOptions(options: PlayerOptions): void {\n if (options.targetBufferSize !== undefined) {\n currentOptions.targetBufferSize = options.targetBufferSize\n }\n if (options.decodeBatchSize !== undefined) {\n currentOptions.decodeBatchSize = options.decodeBatchSize\n }\n if (options.maxQueueSize !== undefined) {\n currentOptions.maxQueueSize = options.maxQueueSize\n }\n console.log('[EcPlayer] Options updated:', currentOptions)\n }\n\n // 设置日志级别\n function setLogLevel(level: number): void {\n coreSetLogLevel(level)\n }\n\n // 暂停\n function pause(): void {\n player.value?.pause()\n }\n\n // 恢复播放\n async function resume(): Promise<void> {\n if (!player.value) return\n await player.value.play()\n }\n\n // 跳转\n async function seek(time: number): Promise<void> {\n if (!player.value) return\n await player.value.seek(time)\n }\n\n // 设置倍速\n function setPlaybackRate(rate: number): void {\n if (!player.value) return\n player.value.setPlaybackRate(rate)\n }\n\n // 获取播放器实例\n function getPlayer(): EcPlayerCore | null {\n return player.value\n }\n\n // 获取状态\n function getState(): EcPlayerState {\n return player.value?.getState() ?? { ...state }\n }\n\n // 获取当前时间\n function getCurrentTime(): number {\n return player.value?.getCurrentTime() ?? 0\n }\n\n // 获取总时长\n function getDuration(): number {\n return player.value?.getDuration() ?? 0\n }\n\n // 销毁播放器\n function destroy(): void {\n if (player.value) {\n player.value.destroy()\n player.value = null\n }\n }\n\n // 组件卸载时销毁\n onUnmounted(() => {\n destroy()\n })\n\n return {\n canvasRef,\n player,\n state,\n isLoading,\n loadingStage,\n detectedFormat,\n isLive,\n play,\n setOptions,\n setLogLevel,\n pause,\n resume,\n seek,\n setPlaybackRate,\n getPlayer,\n getState,\n getCurrentTime,\n getDuration,\n destroy\n }\n}\n","/**\n * 播放器控制条逻辑\n */\n\nimport { ref, computed, type Ref } from 'vue'\n\n/**\n * 控制条逻辑参数\n */\nexport interface UseControlsParams {\n /** 是否正在播放 */\n isPlaying: Ref<boolean>\n /** 是否已加载 */\n isLoaded: Ref<boolean>\n /** 当前时间(毫秒) */\n currentTime: Ref<number>\n /** 总时长(毫秒) */\n duration: Ref<number>\n /** 已解析的分片索引 */\n segmentIndex: Ref<number>\n /** 已下载的分片总数 */\n fetchedSegmentCount: Ref<number>\n /** 总分片数 */\n totalSegments: Ref<number>\n /** 是否为直播流 */\n isLive: Ref<boolean>\n /** 跳转方法 */\n seek: (time: number) => Promise<void>\n}\n\n/**\n * 控制条逻辑返回值\n */\nexport interface UseControlsReturn {\n /** 是否显示控制条 */\n showControls: Ref<boolean>\n /** 进度条 hover 时间 */\n hoverTime: Ref<number | null>\n /** 进度条 hover 位置 */\n hoverPosition: Ref<number>\n /** 播放进度百分比 */\n playProgress: Ref<number>\n /** 缓冲进度百分比 */\n bufferProgress: Ref<number>\n /** 显示控制条 */\n showControlsBar: () => void\n /** 隐藏控制条 */\n hideControlsBar: () => void\n /** 处理进度条点击 */\n handleProgressClick: (event: MouseEvent) => Promise<void>\n /** 处理进度条悬停 */\n handleProgressHover: (event: MouseEvent) => void\n /** 格式化时间 */\n formatTime: (ms: number) => string\n}\n\n/**\n * 控制条逻辑 Hook\n */\nexport function useControls(params: UseControlsParams): UseControlsReturn {\n let hideControlsTimer: number | null = null\n const {\n isPlaying,\n isLoaded,\n currentTime,\n duration,\n segmentIndex,\n fetchedSegmentCount,\n totalSegments,\n isLive,\n seek\n } = params\n\n const showControls = ref(true)\n const hoverTime = ref<number | null>(null)\n const hoverPosition = ref(0)\n\n // 计算播放进度\n const playProgress = computed(() => {\n if (duration.value === 0) return 0\n return (currentTime.value / duration.value) * 100\n })\n\n // 计算缓冲进度(已下载的分段数)\n const bufferProgress = computed(() => {\n if (totalSegments.value === 0) return 0\n return (fetchedSegmentCount.value / totalSegments.value) * 100\n })\n\n // 显示控制条\n function showControlsBar(): void {\n showControls.value = true\n if (hideControlsTimer) {\n clearTimeout(hideControlsTimer)\n }\n hideControlsTimer = window.setTimeout(() => {\n if (isPlaying.value) {\n showControls.value = false\n }\n }, 3000)\n }\n\n // 隐藏控制条\n function hideControlsBar(): void {\n if (isPlaying.value) {\n showControls.value = false\n }\n }\n\n // 处理进度条点击\n async function handleProgressClick(event: MouseEvent): Promise<void> {\n if (duration.value === 0) return\n\n const target = event.currentTarget as HTMLElement\n const rect = target.getBoundingClientRect()\n const clickX = event.clientX - rect.left\n const percentage = clickX / rect.width\n const targetTime = percentage * duration.value\n\n await seek(targetTime)\n }\n\n // 处理进度条悬停\n function handleProgressHover(event: MouseEvent): void {\n if (duration.value === 0) return\n\n const target = event.currentTarget as HTMLElement\n const rect = target.getBoundingClientRect()\n const hoverX = event.clientX - rect.left\n const percentage = Math.max(0, Math.min(1, hoverX / rect.width))\n\n hoverTime.value = percentage * duration.value\n hoverPosition.value = percentage * 100\n }\n\n // 格式化时间\n function formatTime(ms: number): string {\n const totalSeconds = Math.floor(ms / 1000)\n const hours = Math.floor(totalSeconds / 3600)\n const minutes = Math.floor((totalSeconds % 3600) / 60)\n const seconds = totalSeconds % 60\n\n if (hours > 0) {\n return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`\n }\n return `${minutes}:${seconds.toString().padStart(2, '0')}`\n }\n\n return {\n showControls,\n hoverTime,\n hoverPosition,\n playProgress,\n bufferProgress,\n showControlsBar,\n hideControlsBar,\n handleProgressClick,\n handleProgressHover,\n formatTime\n }\n}\n","/**\n * 环境信息逻辑\n */\n\nimport { ref, computed, onMounted, type Ref } from 'vue'\nimport { EnvDetector, type EnvInfo } from '@give-tech/ec-player'\n\n/**\n * 环境信息逻辑返回值\n */\nexport interface UseEnvInfoReturn {\n /** 环境信息 */\n envInfo: Ref<EnvInfo | null>\n /** 是否显示环境面板 */\n showEnvPanel: Ref<boolean>\n /** 环境类型文本 */\n envTypeText: Ref<string>\n /** 切换环境面板显示 */\n toggleEnvPanel: () => void\n /** 检测环境 */\n detectEnv: () => void\n}\n\n/**\n * 环境信息逻辑 Hook\n */\nexport function useEnvInfo(\n initialShowPanel: boolean = false\n): UseEnvInfoReturn {\n const envInfo = ref<EnvInfo | null>(null)\n const showEnvPanel = ref(initialShowPanel)\n\n // 环境类型文本\n const envTypeText = computed(() => {\n if (!envInfo.value) return '未知'\n const typeMap: Record<string, string> = {\n 'pc-browser': 'PC 浏览器',\n 'mobile-browser': '移动端浏览器',\n 'wechat-miniprogram': '微信小程序',\n 'alipay-miniprogram': '支付宝小程序',\n 'other-miniprogram': '其他小程序',\n 'unknown': '未知',\n }\n return typeMap[envInfo.value.platform] || envInfo.value.platform\n })\n\n // 切换环境面板显示\n function toggleEnvPanel(): void {\n showEnvPanel.value = !showEnvPanel.value\n }\n\n // 检测环境\n function detectEnv(): void {\n envInfo.value = EnvDetector.detect()\n console.log('[EcPlayer] 环境检测结果:', envInfo.value)\n }\n\n // 组件挂载时检测环境\n onMounted(() => {\n detectEnv()\n })\n\n return {\n envInfo,\n showEnvPanel,\n envTypeText,\n toggleEnvPanel,\n detectEnv\n }\n}\n","<template>\n <div class=\"gt-player-container\" ref=\"containerRef\">\n <div\n class=\"gt-video-wrapper\"\n ref=\"videoWrapperRef\"\n @mouseenter=\"controls.showControlsBar\"\n @mousemove=\"controls.showControlsBar\"\n @mouseleave=\"controls.hideControlsBar\"\n >\n <!-- Canvas 视频渲染 -->\n <canvas ref=\"canvasRef\" class=\"gt-player-canvas\"></canvas>\n\n <!-- 封面/占位内容(未加载时显示) -->\n <div v-if=\"!player.state.isLoaded\" class=\"gt-placeholder\">\n <slot name=\"placeholder\"></slot>\n </div>\n\n <!-- 环境信息面板 -->\n <div v-if=\"showEnvPanel && envInfo\" class=\"gt-env-panel\">\n <div class=\"gt-env-row\">\n <span>platform</span>\n <span :class=\"envInfo.isMiniProgram ? 'warn' : ''\">{{ envTypeText }}</span>\n </div>\n <div class=\"gt-env-divider\"></div>\n <div class=\"gt-env-row\">\n <span>SIMD</span>\n <span :class=\"envInfo.capabilities.wasmSimd ? 'ok' : 'err'\">\n {{ envInfo.capabilities.wasmSimd ? 'Yes' : 'No' }}\n </span>\n </div>\n <div class=\"gt-env-row\">\n <span>WebGL</span>\n <span :class=\"envInfo.capabilities.webGL ? 'ok' : 'err'\">\n {{ envInfo.capabilities.webGL ? 'Yes' : 'No' }}\n </span>\n </div>\n <div class=\"gt-env-row\">\n <span>WebGL2</span>\n <span :class=\"envInfo.capabilities.webGL2 ? 'ok' : 'err'\">\n {{ envInfo.capabilities.webGL2 ? 'Yes' : 'No' }}\n </span>\n </div>\n <div class=\"gt-env-row\">\n <span>SAB</span>\n <span :class=\"envInfo.capabilities.sharedArrayBuffer ? 'ok' : 'err'\">\n {{ envInfo.capabilities.sharedArrayBuffer ? 'Yes' : 'No' }}\n </span>\n </div>\n <div class=\"gt-env-row\">\n <span>Threads</span>\n <span :class=\"envInfo.capabilities.wasmThreads ? 'ok' : 'err'\">\n {{ envInfo.capabilities.wasmThreads ? 'Yes' : 'No' }}\n </span>\n </div>\n <div class=\"gt-env-row\">\n <span>WebAudio</span>\n <span :class=\"envInfo.capabilities.webAudio ? 'ok' : 'err'\">\n {{ envInfo.capabilities.webAudio ? 'Yes' : 'No' }}\n </span>\n </div>\n <div class=\"gt-env-row\">\n <span>Stream</span>\n <span :class=\"envInfo.capabilities.readableStream ? 'ok' : 'err'\">\n {{ envInfo.capabilities.readableStream ? 'Yes' : 'No' }}\n </span>\n </div>\n <template v-if=\"player.state.isLoaded\">\n <div class=\"gt-env-divider\"></div>\n <div class=\"gt-env-row\" v-if=\"player.detectedFormat.value\"><span>Format</span><span>{{ player.detectedFormat.value }}</span></div>\n <div class=\"gt-env-row\"><span>FPS</span><span>{{ player.state.fps }}</span></div>\n <div class=\"gt-env-row\"><span>Resolution</span><span>{{ player.state.resolution }}</span></div>\n <div class=\"gt-env-row\"><span>downloaded</span><span>{{ player.state.downloaded }}</span></div>\n <div class=\"gt-env-row\"><span>decoded</span><span>{{ player.state.decoded }}</span></div>\n <div class=\"gt-env-row\"><span>Dropped</span><span>{{ player.state.droppedFrames }}</span></div>\n <div class=\"gt-env-row\"><span>Speed</span><span>{{ player.state.downloadSpeed || 0 }} KB/s</span></div>\n </template>\n </div>\n\n <!-- 加载中动画 -->\n <div class=\"gt-loading-spinner\" v-if=\"player.isLoading.value\">\n <slot name=\"loading\" :stage=\"player.loadingStage.value\">\n <div class=\"gt-loading-content\">\n <div class=\"gt-spinner\"></div>\n <div class=\"gt-loading-text\" v-if=\"player.loadingStage.value\">\n {{ getLoadingText(player.loadingStage.value) }}\n </div>\n <!-- 下载进度 -->\n <div class=\"gt-loading-progress\" v-if=\"getLoadingProgress(player.loadingStage.value)\">\n <div class=\"gt-progress-bar-bg\">\n <div class=\"gt-progress-bar-fill\" :style=\"{ width: getLoadingProgress(player.loadingStage.value) + '%' }\"></div>\n </div>\n <div class=\"gt-progress-text\">{{ getLoadingProgress(player.loadingStage.value) }}%</div>\n </div>\n </div>\n </slot>\n </div>\n\n <!-- 播放中缓冲提示 -->\n <div class=\"gt-buffering-indicator\" v-if=\"player.state.isBuffering && !player.isLoading.value\">\n <div class=\"gt-buffering-content\">\n <div class=\"gt-buffering-spinner\"></div>\n <span v-if=\"player.state.bufferingProgress !== undefined\">\n 正在缓冲... {{ player.state.bufferingProgress }}%\n </span>\n <span v-else>正在缓冲...</span>\n </div>\n </div>\n\n <!-- 错误提示 -->\n <div class=\"gt-error-message\" v-if=\"errorMessage\">\n <slot name=\"error\" :message=\"errorMessage\" :retry=\"handleRetry\">\n <div class=\"gt-error-content\">\n <div class=\"gt-error-icon-wrapper\">\n <svg class=\"gt-error-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/>\n <line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/>\n </svg>\n </div>\n <div class=\"gt-error-body\">\n <div class=\"gt-error-title\">播放失败</div>\n <div class=\"gt-error-text\">{{ errorMessage }}</div>\n </div>\n <button class=\"gt-error-retry\" @click=\"handleRetry\" title=\"重试\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M17.65 6.35A7.958 7.958 0 0012 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0112 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z\"/>\n </svg>\n </button>\n </div>\n </slot>\n </div>\n\n <!-- 视频控制条 -->\n <div\n v-if=\"controlOption.show\"\n class=\"gt-video-controls\"\n :class=\"{ visible: controls.showControls.value || !player.state.isPlaying }\"\n >\n <button class=\"gt-control-btn\" @click=\"togglePlayback\">\n <svg v-if=\"player.state.isPlaying\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n </svg>\n <svg v-else viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M8 5v14l11-7z\"/>\n </svg>\n </button>\n\n <!-- 直播标识 -->\n <span class=\"gt-live-indicator\" v-if=\"isLive\">\n <span class=\"gt-live-dot\"></span>\n <span class=\"gt-live-text\">实时</span>\n </span>\n\n <!-- 点播时间显示 -->\n <span class=\"gt-time-display\" v-else>\n <span class=\"gt-time-current\">{{ controls.formatTime(player.state.currentTime) }}</span>\n <span class=\"gt-time-separator\">/</span>\n <span class=\"gt-time-duration\">{{ controls.formatTime(player.state.duration) }}</span>\n </span>\n\n <!-- 进度条 (仅点播) -->\n <div\n v-if=\"!isLive\"\n class=\"gt-progress-container\"\n @click=\"controls.handleProgressClick\"\n @mousemove=\"controls.handleProgressHover\"\n @mouseleave=\"controls.hoverTime.value = null\"\n >\n <div class=\"gt-progress-bar\">\n <div class=\"gt-progress-buffered\" :style=\"{ width: controls.bufferProgress.value + '%' }\"></div>\n <div class=\"gt-progress-played\" :style=\"{ width: controls.playProgress.value + '%' }\">\n <div class=\"gt-progress-handle\"></div>\n </div>\n </div>\n <div\n class=\"gt-progress-tooltip\"\n v-if=\"controls.hoverTime.value !== null\"\n :style=\"{ left: controls.hoverPosition.value + '%' }\"\n >\n {{ controls.formatTime(controls.hoverTime.value) }}\n </div>\n </div>\n\n <!-- 直播流占位符 -->\n <div class=\"gt-flex-spacer\" v-else></div>\n\n <!-- 倍速按钮(仅点播) -->\n <div v-if=\"controlOption.playbackRate && !isLive\" class=\"gt-playback-rate-container\">\n <button class=\"gt-control-btn gt-playback-rate-btn\" @click=\"togglePlaybackRateMenu\" :title=\"'倍速: ' + currentPlaybackRate + 'x'\">\n <span class=\"gt-playback-rate-text\">{{ currentPlaybackRate }}x</span>\n </button>\n <div v-if=\"showPlaybackRateMenu\" class=\"gt-playback-rate-menu\">\n <button\n v-for=\"rate in playbackRates\"\n :key=\"rate\"\n class=\"gt-playback-rate-option\"\n :class=\"{ active: currentPlaybackRate === rate }\"\n @click=\"setPlaybackRate(rate)\"\n >\n {{ rate }}x\n </button>\n </div>\n </div>\n\n <!-- 环境信息按钮 -->\n <button v-if=\"controlOption.info\" class=\"gt-control-btn gt-env-info-btn\" @click=\"toggleEnvPanel\" :title=\"showEnvPanel ? '隐藏信息' : '显示信息'\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\"/>\n </svg>\n </button>\n\n <!-- 全屏按钮 -->\n <button v-if=\"controlOption.fullscreen\" class=\"gt-control-btn gt-fullscreen-btn\" @click=\"toggleFullscreen\" :title=\"isFullscreen ? '退出全屏' : '全屏'\">\n <!-- 退出全屏图标 -->\n <svg v-if=\"isFullscreen\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z\"/>\n </svg>\n <!-- 全屏图标 -->\n <svg v-else viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z\"/>\n </svg>\n </button>\n </div>\n\n <!-- 大播放按钮(居中) -->\n <div\n class=\"gt-center-play-btn\"\n v-if=\"player.state.isLoaded && !player.state.isPlaying && !player.isLoading.value\"\n @click=\"togglePlayback\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M8 5v14l11-7z\"/>\n </svg>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref, watch, onMounted, onUnmounted } from 'vue'\nimport { usePlayer } from '../composables/usePlayer'\nimport { useControls } from '../composables/useControls'\nimport { useEnvInfo } from '../composables/useEnvInfo'\nimport type { EcPlayerProps, EcPlayerExpose, EcPlayerSource, EcPlayerControlOption } from '../types'\nimport { DEFAULT_EC_PLAYER_PROPS } from '../types'\n\n// Props\nconst props = withDefaults(defineProps<EcPlayerProps>(), DEFAULT_EC_PLAYER_PROPS)\n\n// Emits\nconst emit = defineEmits<{\n (e: 'play'): void\n (e: 'pause'): void\n (e: 'error', error: Error, currentTime: number): void\n (e: 'retry'): void\n}>()\n\n// Canvas ref(本地)\nconst canvasRef = ref<HTMLCanvasElement | null>(null)\n\n// 容器引用(用于全屏)\nconst containerRef = ref<HTMLElement | null>(null)\n\n// 全屏状态\nconst isFullscreen = ref(false)\n\n// 错误状态\nconst errorMessage = ref<string | null>(null)\n\n// 加载阶段文本映射\nfunction getLoadingText(stage: { stage: string; detail?: string }): string {\n const stageTexts: Record<string, string> = {\n 'connecting': '正在连接...',\n 'detected': `检测到格式: ${stage.detail || ''}`,\n 'loading': stage.detail || '正在加载...',\n 'initializing': '正在初始化...'\n }\n return stageTexts[stage.stage] || '加载中...'\n}\n\n// 从 detail 中提取进度百分比\nfunction getLoadingProgress(stage: { stage: string; detail?: string } | null): number | null {\n if (!stage || stage.stage !== 'loading' || !stage.detail) return null\n // detail 格式: \"45% (12.3/50.0 MB)\"\n const match = stage.detail.match(/^(\\d+)%/)\n return match ? parseInt(match[1], 10) : null\n}\n\n// 清除错误\nfunction clearError(): void {\n errorMessage.value = null\n}\n\n// 重试处理\nfunction handleRetry(): void {\n clearError()\n emit('retry')\n}\n\n// 包装 emit,捕获错误(必须在 errorMessage 声明之后)\nconst wrappedEmit = {\n play: () => emit('play'),\n pause: () => emit('pause'),\n error: (error: Error, currentTime: number) => {\n // 设置错误信息显示\n errorMessage.value = error.message || '播放出错'\n emit('error', error, currentTime)\n },\n retry: () => emit('retry')\n}\n\n// 倍速播放状态\nconst playbackRates = [1, 2, 4]\nconst currentPlaybackRate = ref(1)\nconst showPlaybackRateMenu = ref(false)\n\n// 解析 controlOption\nconst controlOption = computed<Required<EcPlayerControlOption>>(() => ({\n show: props.controlOption?.show ?? DEFAULT_EC_PLAYER_PROPS.controlOption.show,\n fullscreen: props.controlOption?.fullscreen ?? DEFAULT_EC_PLAYER_PROPS.controlOption.fullscreen,\n info: props.controlOption?.info ?? DEFAULT_EC_PLAYER_PROPS.controlOption.info,\n playbackRate: props.controlOption?.playbackRate ?? DEFAULT_EC_PLAYER_PROPS.controlOption.playbackRate\n}))\n\n// 环境信息逻辑\nconst { envInfo, showEnvPanel, envTypeText, toggleEnvPanel } = useEnvInfo(false)\n\n// 播放器逻辑\nconst player = usePlayer(wrappedEmit)\n\n// 同步 canvas ref 到 player\nwatch(canvasRef, (el) => {\n player.canvasRef.value = el\n}, { immediate: true })\n\n// isLive 计算属性(用于模板)\nconst isLive = computed(() => player.isLive.value)\n\n// 控制条逻辑\nconst isPlayingRef = computed(() => player.state.isPlaying)\nconst isLoadedRef = computed(() => player.state.isLoaded)\nconst currentTimeRef = computed(() => player.state.currentTime)\nconst durationRef = computed(() => player.state.duration)\nconst segmentIndexRef = computed(() => player.state.segmentIndex)\nconst fetchedSegmentCountRef = computed(() => player.state.fetchedSegmentCount)\nconst totalSegmentsRef = computed(() => player.state.totalSegments)\n\nconst controls = useControls({\n isPlaying: isPlayingRef,\n isLoaded: isLoadedRef,\n currentTime: currentTimeRef,\n duration: durationRef,\n segmentIndex: segmentIndexRef,\n fetchedSegmentCount: fetchedSegmentCountRef,\n totalSegments: totalSegmentsRef,\n isLive,\n seek: player.seek\n})\n\n// 视频包装器引用\nconst videoWrapperRef = ref<HTMLElement | null>(null)\n\n// 解析 src\nfunction parseSrc(src: EcPlayerSource | undefined): { url: string; isLive: boolean } | null {\n if (!src || !src.url) return null\n return { url: src.url, isLive: src.isLive ?? false }\n}\n\n// 切换播放/暂停\nfunction togglePlayback(): void {\n if (player.state.isPlaying) {\n player.pause()\n } else {\n player.resume()\n }\n}\n\n// 切换全屏\nfunction toggleFullscreen(): void {\n if (!containerRef.value) return\n\n if (!isFullscreen.value) {\n if (containerRef.value.requestFullscreen) {\n containerRef.value.requestFullscreen()\n }\n } else {\n if (document.exitFullscreen) {\n document.exitFullscreen()\n }\n }\n}\n\n// 设置倍速\nfunction setPlaybackRate(rate: number): void {\n currentPlaybackRate.value = rate\n showPlaybackRateMenu.value = false\n player.setPlaybackRate(rate)\n}\n\n// 切换倍速菜单\nfunction togglePlaybackRateMenu(): void {\n showPlaybackRateMenu.value = !showPlaybackRateMenu.value\n}\n\n// 监听全屏变化\nfunction handleFullscreenChange(): void {\n isFullscreen.value = !!document.fullscreenElement\n}\n\n// 暴露的 play 方法(简化版)\nfunction play(): void {\n const parsed = parseSrc(props.src)\n if (parsed) {\n player.play(parsed)\n }\n}\n\n// 监听 src 变化,自动播放\nwatch(() => props.src, (newSrc) => {\n const parsed = parseSrc(newSrc)\n if (parsed && parsed.url) {\n // 清除之前的错误信息\n errorMessage.value = null\n player.play(parsed)\n }\n}, { immediate: true, deep: true })\n\n// 挂载时监听全屏事件\nonMounted(() => {\n document.addEventListener('fullscreenchange', handleFullscreenChange)\n})\n\n// 卸载时移除监听\nonUnmounted(() => {\n document.removeEventListener('fullscreenchange', handleFullscreenChange)\n})\n\n// Expose 方法\ndefineExpose<EcPlayerExpose>({\n play,\n pause: player.pause,\n seek: player.seek,\n destroy: player.destroy,\n getState: player.getState\n})\n</script>\n\n<style scoped>\n.gt-player-container {\n width: 100%;\n height: 100%;\n}\n\n.gt-video-wrapper {\n position: relative;\n width: 100%;\n height: 100%;\n background: #000;\n overflow: hidden;\n}\n\n.gt-player-canvas {\n width: 100%;\n height: 100%;\n object-fit: contain;\n}\n\n.gt-placeholder {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #000;\n}\n\n/* 加载中动画 */\n.gt-loading-spinner {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.5);\n z-index: 10;\n}\n\n.gt-loading-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 16px;\n}\n\n.gt-spinner {\n width: 10%;\n max-width: 48px;\n min-width: 20px;\n aspect-ratio: 1/1;\n border: 0.3em solid rgba(255, 255, 255, 0.3);\n border-top-color: #fff;\n border-radius: 50%;\n animation: gt-spin 1s linear infinite;\n}\n\n.gt-loading-text {\n color: rgba(255, 255, 255, 0.85);\n font-size: 13px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n text-align: center;\n padding: 0 20px;\n}\n\n/* 加载进度条 */\n.gt-loading-progress {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 0 20px;\n width: 100%;\n max-width: 280px;\n}\n\n.gt-progress-bar-bg {\n flex: 1;\n height: 4px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 2px;\n overflow: hidden;\n}\n\n.gt-progress-bar-fill {\n height: 100%;\n background: linear-gradient(90deg, #ff2d55, #ff6b6b);\n border-radius: 2px;\n transition: width 0.2s ease;\n}\n\n.gt-progress-text {\n color: rgba(255, 255, 255, 0.9);\n font-size: 12px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n min-width: 36px;\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n@keyframes gt-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n/* 错误提示 */\n.gt-error-message {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.75);\n z-index: 15;\n animation: gt-error-fade-in 0.25s ease;\n}\n\n@keyframes gt-error-fade-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n.gt-error-content {\n display: flex;\n align-items: flex-start;\n gap: 16px;\n padding: 20px 24px;\n background: rgba(60, 60, 60, 0.95);\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 12px;\n max-width: 85%;\n min-width: 280px;\n backdrop-filter: blur(12px);\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.05) inset;\n animation: gt-error-slide-in 0.3s ease;\n}\n\n@keyframes gt-error-slide-in {\n from {\n opacity: 0;\n transform: translateY(-10px) scale(0.95);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n\n.gt-error-icon-wrapper {\n flex-shrink: 0;\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(255, 77, 77, 0.2);\n border-radius: 50%;\n}\n\n.gt-error-icon {\n width: 22px;\n height: 22px;\n color: #ff4d4f;\n}\n\n.gt-error-body {\n flex: 1;\n min-width: 0;\n}\n\n.gt-error-title {\n color: #ff4d4f;\n font-size: 15px;\n font-weight: 600;\n margin-bottom: 6px;\n}\n\n.gt-error-text {\n color: rgba(255, 255, 255, 0.85);\n font-size: 13px;\n line-height: 1.5;\n word-break: break-word;\n}\n\n.gt-error-retry {\n flex-shrink: 0;\n width: 32px;\n height: 32px;\n border: none;\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.15);\n color: rgba(255, 255, 255, 0.9);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n margin-left: 8px;\n}\n\n.gt-error-retry:hover {\n background: rgba(255, 255, 255, 0.25);\n color: white;\n}\n\n.gt-error-retry svg {\n width: 20px;\n height: 20px;\n}\n\n/* 视频控制条 */\n.gt-video-controls {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n padding: 10px 16px;\n background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));\n opacity: 0;\n transition: opacity 0.3s ease;\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.gt-flex-spacer {\n flex: 1;\n}\n\n.gt-video-controls.visible {\n opacity: 1;\n}\n\n.gt-control-btn {\n width: 28px;\n height: 28px;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: white;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n\n.gt-control-btn:hover {\n background: rgba(255, 255, 255, 0.2);\n}\n\n.gt-control-btn svg {\n width: 14px;\n height: 14px;\n}\n\n.gt-progress-container {\n flex: 1;\n position: relative;\n cursor: pointer;\n height: 20px;\n display: flex;\n align-items: center;\n}\n\n.gt-progress-bar {\n width: 100%;\n height: 4px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 2px;\n position: relative;\n overflow: hidden;\n transition: height 0.2s ease;\n}\n\n.gt-progress-container:hover .gt-progress-bar {\n height: 6px;\n}\n\n.gt-progress-buffered {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: rgba(255, 255, 255, 0.4);\n border-radius: 2px;\n transition: width 0.1s;\n}\n\n.gt-progress-played {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: #ff2d55;\n border-radius: 2px;\n transition: width 0.1s;\n}\n\n.gt-progress-handle {\n position: absolute;\n right: -6px;\n top: 50%;\n transform: translateY(-50%);\n width: 14px;\n height: 14px;\n background: #ff2d55;\n border-radius: 50%;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n opacity: 0;\n transition: opacity 0.2s, transform 0.2s;\n}\n\n.gt-progress-container:hover .gt-progress-handle {\n opacity: 1;\n transform: translateY(-50%) scale(1.2);\n}\n\n.gt-progress-tooltip {\n position: absolute;\n bottom: calc(100% + 8px);\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.85);\n color: white;\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 11px;\n white-space: nowrap;\n pointer-events: none;\n}\n\n.gt-time-display {\n color: white;\n font-size: 12px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.gt-time-separator {\n margin: 0 4px;\n opacity: 0.6;\n}\n\n/* 直播指示器 */\n.gt-live-indicator {\n display: flex;\n align-items: center;\n gap: 6px;\n color: white;\n font-size: 12px;\n font-weight: 500;\n}\n\n.gt-live-dot {\n width: 8px;\n height: 8px;\n background: #ff2d55;\n border-radius: 50%;\n animation: gt-live-pulse 1.5s ease-in-out infinite;\n}\n\n.gt-live-text {\n color: #ff2d55;\n}\n\n@keyframes gt-live-pulse {\n 0%, 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.4;\n }\n}\n\n/* 倍速按钮 */\n.gt-playback-rate-container {\n position: relative;\n flex-shrink: 0;\n}\n\n.gt-playback-rate-btn {\n width: auto !important;\n min-width: 28px;\n padding: 0 8px;\n font-size: 12px;\n font-weight: 500;\n}\n\n.gt-playback-rate-text {\n color: white;\n}\n\n.gt-playback-rate-menu {\n position: absolute;\n bottom: calc(100% + 8px);\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.85);\n border-radius: 6px;\n padding: 4px 0;\n min-width: 60px;\n z-index: 20;\n animation: gt-rate-menu-fade-in 0.15s ease;\n}\n\n@keyframes gt-rate-menu-fade-in {\n from { opacity: 0; transform: translateX(-50%) translateY(4px); }\n to { opacity: 1; transform: translateX(-50%) translateY(0); }\n}\n\n.gt-playback-rate-option {\n display: block;\n width: 100%;\n padding: 6px 12px;\n border: none;\n background: transparent;\n color: rgba(255, 255, 255, 0.8);\n font-size: 12px;\n cursor: pointer;\n text-align: center;\n transition: all 0.15s ease;\n}\n\n.gt-playback-rate-option:hover {\n background: rgba(255, 255, 255, 0.1);\n color: white;\n}\n\n.gt-playback-rate-option.active {\n color: #ff2d55;\n font-weight: 500;\n}\n\n/* 居中播放按钮 */\n.gt-center-play-btn {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 56px;\n height: 56px;\n background: rgba(0, 0, 0, 0.5);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.gt-center-play-btn:hover {\n background: rgba(0, 0, 0, 0.7);\n transform: translate(-50%, -50%) scale(1.05);\n}\n\n.gt-center-play-btn svg {\n width: 24px;\n height: 24px;\n color: white;\n margin-left: 3px;\n}\n\n/* 环境信息面板 */\n.gt-env-panel {\n position: absolute;\n top: 12px;\n left: 12px;\n background: rgba(0, 0, 0, 0.8);\n border-radius: 6px;\n padding: 8px 10px;\n font-size: 11px;\n font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;\n min-width: 160px;\n max-width: 200px;\n max-height: calc(100% - 24px);\n overflow-y: auto;\n backdrop-filter: blur(10px);\n animation: gt-env-fade-in 0.15s ease;\n z-index: 15;\n}\n\n.gt-env-panel::-webkit-scrollbar {\n width: 4px;\n}\n\n.gt-env-panel::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.gt-env-panel::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.3);\n border-radius: 2px;\n}\n\n@keyframes gt-env-fade-in {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.gt-env-panel .gt-env-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 2px 0;\n color: rgba(255, 255, 255, 0.7);\n}\n\n.gt-env-panel .gt-env-row span:first-child {\n color: rgba(255, 255, 255, 0.5);\n margin-right: 16px;\n}\n\n.gt-env-panel .gt-env-row span:last-child {\n font-variant-numeric: tabular-nums;\n}\n\n.gt-env-panel .gt-env-row .ok { color: #4caf50; }\n.gt-env-panel .gt-env-row .warn { color: #ff9800; }\n.gt-env-panel .gt-env-row .err { color: #f44336; }\n\n.gt-env-divider {\n height: 1px;\n background: rgba(255, 255, 255, 0.1);\n margin: 4px 0;\n}\n\n/* 播放中缓冲提示 */\n.gt-buffering-indicator {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 10;\n}\n\n.gt-buffering-content {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n background: rgba(0, 0, 0, 0.6);\n border-radius: 20px;\n color: white;\n font-size: 13px;\n}\n\n.gt-buffering-spinner {\n width: 16px;\n height: 16px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-top-color: #fff;\n border-radius: 50%;\n animation: gt-spin 1s linear infinite;\n}\n</style>\n"],"names":["WASMLoader","globalConfig","setLogLevel","coreSetLogLevel","_createElementBlock","_createElementVNode","_unref","_openBlock","_renderSlot","_normalizeClass","_Fragment","_toDisplayString","_normalizeStyle","_renderList"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/types.ts","../src/composables/usePlayer.ts","../src/composables/useControls.ts","../src/composables/useEnvInfo.ts","../src/components/EcPlayer.vue"],"sourcesContent":["/**\n * Vue 组件类型定义\n */\n\nimport type { EcPlayerCoreConfig, EcPlayerState, EnvInfo } from '@give-tech/ec-player'\nexport { LogLevel } from '@give-tech/ec-player'\n\n/**\n * WASM 路径配置\n */\nexport interface WasmPathConfig {\n /**\n * SIMD 版本 WASM 路径(推荐,性能更好)\n * 用于支持 SIMD 的现代浏览器\n */\n simd?: string\n /**\n * 非 SIMD 版本 WASM 路径\n * 用于不支持 SIMD 的旧浏览器作为降级方案\n */\n nonSimd?: string\n}\n\n/**\n * 全局配置\n *\n * 必须在使用 EcPlayer 组件前调用\n *\n * @example\n * ```typescript\n * // main.ts\n * import { configureEcPlayer } from '@give-tech/ec-player-vue'\n *\n * // 配置 SIMD 和非 SIMD 路径,支持自动降级\n * configureEcPlayer({\n * wasm: {\n * simd: '/wasm/decoder-simd.js',\n * nonSimd: '/wasm/decoder.js'\n * }\n * })\n *\n * // 使用 CDN\n * configureEcPlayer({\n * wasm: {\n * simd: 'https://unpkg.com/@give-tech/ec-player@latest/wasm/decoder-simd.js',\n * nonSimd: 'https://unpkg.com/@give-tech/ec-player@latest/wasm/decoder.js'\n * }\n * })\n * ```\n */\nexport interface EcPlayerGlobalConfig {\n /**\n * WASM 路径配置(必填)\n * 支持 SIMD 和非 SIMD 自动降级\n */\n wasm?: WasmPathConfig\n /** 默认是否显示控制条 */\n showControls?: boolean\n /** 默认缓冲目标帧数 */\n targetBufferSize?: number\n /** 默认每批解码帧数 */\n decodeBatchSize?: number\n /** 默认队列上限 */\n maxQueueSize?: number\n}\n\n/**\n * 播放器配置选项\n */\nexport interface PlayerOptions {\n /** 帧缓冲区目标大小 */\n targetBufferSize?: number\n /** 每批解码帧数 */\n decodeBatchSize?: number\n /** NAL/Sample 队列上限 */\n maxQueueSize?: number\n}\n\n/**\n * 视频源配置\n */\nexport interface EcPlayerSource {\n /** 视频 URL */\n url: string\n /** 是否为直播流 */\n isLive?: boolean\n /** 版本号,用于强制重新加载 */\n version?: number\n}\n\n/**\n * 控制条选项\n */\nexport interface EcPlayerControlOption {\n /** 是否显示控制条 */\n show?: boolean\n /** 是否显示全屏按钮 */\n fullscreen?: boolean\n /** 是否显示环境信息按钮 */\n info?: boolean\n /** 是否显示倍速按钮(仅点播) */\n playbackRate?: boolean\n}\n\n/**\n * EcPlayer 组件 Props\n */\nexport interface EcPlayerProps {\n /** 视频源 */\n src?: EcPlayerSource\n /** 控制条选项 */\n controlOption?: EcPlayerControlOption\n}\n\n/**\n * EcPlayer 组件 Emits\n */\nexport interface EcPlayerEmits {\n /** 播放时触发 */\n (e: 'play'): void\n /** 暂停时触发 */\n (e: 'pause'): void\n /** 发生错误时触发 */\n (e: 'error', error: Error, currentTime: number): void\n /** 点击重试时触发 */\n (e: 'retry'): void\n}\n\n/**\n * EcPlayer 组件 Expose 方法\n */\nexport interface EcPlayerExpose {\n /** 播放 */\n play: () => void\n /** 暂停 */\n pause: () => void\n /** 跳转到指定时间(毫秒) */\n seek: (time: number) => Promise<void>\n /** 销毁播放器,释放资源(用于 v-if 切换前手动释放) */\n destroy: () => void\n /** 获取播放器状态 */\n getState: () => EcPlayerState\n}\n\n/**\n * 环境信息面板数据\n */\nexport interface EnvPanelData {\n envInfo: EnvInfo | null\n state: Partial<EcPlayerState>\n}\n\n/**\n * 播放器配置(从 Props 转换)\n */\nexport interface PlayerConfigFromProps extends EcPlayerCoreConfig {\n wasmPath: string\n targetBufferSize: number\n decodeBatchSize: number\n maxQueueSize: number\n}\n\n/**\n * 默认 Props 值\n */\nexport const DEFAULT_EC_PLAYER_PROPS = {\n src: undefined as EcPlayerSource | undefined,\n controlOption: {\n show: true,\n fullscreen: true,\n info: true,\n playbackRate: true\n }\n}\n\n/**\n * 默认播放器配置\n */\nexport const DEFAULT_PLAYER_OPTIONS: Required<PlayerOptions> = {\n targetBufferSize: 60,\n decodeBatchSize: 2,\n maxQueueSize: 200\n}\n\n/**\n * 默认 WASM 配置\n */\nexport const DEFAULT_WASM_CONFIG: WasmPathConfig = {\n simd: '/wasm/decoder-simd.js',\n nonSimd: '/wasm/decoder.js'\n}\n\n/**\n * 全局配置存储\n */\nlet globalConfig: EcPlayerGlobalConfig = {}\n\n/**\n * 配置 EcPlayer 全局默认值\n *\n * @example\n * ```typescript\n * // main.ts\n * import { configureEcPlayer } from '@give-tech/ec-player-vue'\n *\n * // 配置 SIMD 和非 SIMD 路径,支持自动降级\n * configureEcPlayer({\n * wasm: {\n * simd: '/wasm/decoder-simd.js',\n * nonSimd: '/wasm/decoder.js'\n * }\n * })\n *\n * // 使用 CDN\n * configureEcPlayer({\n * wasm: {\n * simd: 'https://unpkg.com/@give-tech/ec-player@latest/wasm/decoder-simd.js',\n * nonSimd: 'https://unpkg.com/@give-tech/ec-player@latest/wasm/decoder.js'\n * }\n * })\n * ```\n */\nexport function configureEcPlayer(config: EcPlayerGlobalConfig): void {\n globalConfig = { ...globalConfig, ...config }\n}\n\n/**\n * 获取全局配置\n */\nexport function getGlobalConfig(): EcPlayerGlobalConfig {\n return { ...globalConfig }\n}\n\n/**\n * 获取 WASM 路径配置\n */\nexport function getWasmConfig(): WasmPathConfig {\n return {\n simd: globalConfig.wasm?.simd ?? DEFAULT_WASM_CONFIG.simd,\n nonSimd: globalConfig.wasm?.nonSimd ?? DEFAULT_WASM_CONFIG.nonSimd\n }\n}\n\n/**\n * 获取合并后的配置(props > 全局配置 > 默认值)\n */\nexport function getMergedConfig(props: EcPlayerProps) {\n return {\n src: props.src ?? DEFAULT_EC_PLAYER_PROPS.src,\n controlOption: {\n show: props.controlOption?.show ?? globalConfig.showControls ?? DEFAULT_EC_PLAYER_PROPS.controlOption.show,\n fullscreen: props.controlOption?.fullscreen ?? DEFAULT_EC_PLAYER_PROPS.controlOption.fullscreen,\n info: props.controlOption?.info ?? DEFAULT_EC_PLAYER_PROPS.controlOption.info,\n playbackRate: props.controlOption?.playbackRate ?? DEFAULT_EC_PLAYER_PROPS.controlOption.playbackRate\n }\n }\n}\n\n/**\n * 获取合并后的播放器配置(options > 全局配置 > 默认值)\n */\nexport function getMergedPlayerOptions(options: PlayerOptions): Required<PlayerOptions> {\n return {\n targetBufferSize: options.targetBufferSize ?? globalConfig.targetBufferSize ?? DEFAULT_PLAYER_OPTIONS.targetBufferSize,\n decodeBatchSize: options.decodeBatchSize ?? globalConfig.decodeBatchSize ?? DEFAULT_PLAYER_OPTIONS.decodeBatchSize,\n maxQueueSize: options.maxQueueSize ?? globalConfig.maxQueueSize ?? DEFAULT_PLAYER_OPTIONS.maxQueueSize\n }\n}\n\n/**\n * 根据环境能力选择合适的 WASM 路径\n *\n * @param hasSimdSupport 是否支持 SIMD\n * @returns 实际使用的 WASM 路径\n */\nexport function resolveWasmPath(hasSimdSupport: boolean): string {\n const wasmConfig = getWasmConfig()\n\n // 根据 SIMD 支持情况选择\n if (hasSimdSupport && wasmConfig.simd) {\n console.log('[EcPlayer] Using SIMD WASM:', wasmConfig.simd)\n return wasmConfig.simd\n }\n\n if (wasmConfig.nonSimd) {\n console.log('[EcPlayer] SIMD not supported, using non-SIMD WASM:', wasmConfig.nonSimd)\n return wasmConfig.nonSimd\n }\n\n // 降级到 SIMD 版本(兜底)\n console.log('[EcPlayer] No non-SIMD path configured, falling back to SIMD:', wasmConfig.simd)\n return wasmConfig.simd!\n}\n\n/**\n * 预加载状态标记(用于幂等性保护)\n */\nlet preloadCalled = false\n\n/**\n * 预加载 WASM 模块\n *\n * 支持幂等性:多次调用只会执行一次预加载\n *\n * @example\n * import { preloadWasm } from '@give-tech/ec-player-vue'\n * preloadWasm() // 预加载所有\n * preloadWasm({ preloadNonSimd: false }) // 只预加载 SIMD\n */\nexport function preloadWasm(options?: {\n preloadSimd?: boolean\n preloadNonSimd?: boolean\n}): void {\n // 幂等性检查:已经调用过则直接返回\n if (preloadCalled) {\n console.log('[EcPlayer] WASM already preloaded, skip')\n return\n }\n preloadCalled = true\n\n const { preloadSimd = true, preloadNonSimd = true } = options ?? {}\n const wasmConfig = getWasmConfig()\n const paths: string[] = []\n\n if (preloadSimd && wasmConfig.simd) {\n paths.push(wasmConfig.simd)\n }\n if (preloadNonSimd && wasmConfig.nonSimd) {\n paths.push(wasmConfig.nonSimd)\n }\n\n if (paths.length > 0) {\n import('@give-tech/ec-player').then(({ WASMLoader }) => {\n WASMLoader.preloadAll(paths)\n console.log('[EcPlayer] Preloading WASM:', paths)\n }).catch(err => {\n console.warn('[EcPlayer] Failed to preload WASM:', err)\n })\n }\n}\n\n/**\n * 重置预加载状态(仅用于测试)\n */\nexport function resetPreloadState(): void {\n preloadCalled = false\n}\n","/**\n * 播放器核心逻辑\n */\n\nimport { ref, shallowRef, reactive, onUnmounted, type Ref } from 'vue'\nimport {\n EcPlayerCore,\n EnvDetector,\n setLogLevel as coreSetLogLevel,\n type EcPlayerCoreConfig,\n type EcPlayerState,\n type EcPlayerCallbacks,\n type EcLoadingStageInfo\n} from '@give-tech/ec-player'\nimport type { EcPlayerProps, PlayerOptions } from '../types'\nimport { resolveWasmPath, DEFAULT_PLAYER_OPTIONS, getGlobalConfig } from '../types'\n\n/**\n * 播放器逻辑返回值\n */\nexport interface UsePlayerReturn {\n /** Canvas 元素引用 */\n canvasRef: Ref<HTMLCanvasElement | null>\n /** 播放器实例 */\n player: Ref<EcPlayerCore | null>\n /** 响应式状态 */\n state: EcPlayerState\n /** 是否正在加载 */\n isLoading: Ref<boolean>\n /** 加载阶段信息 */\n loadingStage: Ref<EcLoadingStageInfo | null>\n /** 检测到的格式 */\n detectedFormat: Ref<string>\n /** 是否为直播流 */\n isLive: Ref<boolean>\n /** 播放 */\n play: (options: { url: string; isLive?: boolean }) => Promise<void>\n /** 设置配置 */\n setOptions: (options: PlayerOptions) => void\n /** 设置日志级别 */\n setLogLevel: (level: number) => void\n /** 暂停 */\n pause: () => void\n /** 恢复播放 */\n resume: () => Promise<void>\n /** 跳转 */\n seek: (time: number) => Promise<void>\n /** 设置倍速 */\n setPlaybackRate: (rate: number) => void\n /** 获取播放器实例 */\n getPlayer: () => EcPlayerCore | null\n /** 获取状态 */\n getState: () => EcPlayerState\n /** 获取当前时间 */\n getCurrentTime: () => number\n /** 获取总时长 */\n getDuration: () => number\n /** 销毁播放器 */\n destroy: () => void\n}\n\n/**\n * 从配置选项创建播放器配置\n */\nfunction createConfig(options: Required<PlayerOptions>): Omit<EcPlayerCoreConfig, 'isLive'> {\n // 检测 SIMD 支持\n const envInfo = EnvDetector.detect()\n const wasmPath = resolveWasmPath(envInfo.capabilities.wasmSimd)\n\n return {\n wasmPath,\n targetBufferSize: options.targetBufferSize,\n decodeBatchSize: options.decodeBatchSize,\n maxQueueSize: options.maxQueueSize\n }\n}\n\n/**\n * 播放器逻辑 Hook 的 emit 参数类型\n */\nexport interface UsePlayerEmit {\n play: () => void\n pause: () => void\n error: (error: Error, currentTime: number) => void\n retry: () => void\n}\n\n/**\n * 播放器逻辑 Hook\n */\nexport function usePlayer(emit: UsePlayerEmit): UsePlayerReturn {\n const canvasRef = ref<HTMLCanvasElement | null>(null)\n const player = shallowRef<EcPlayerCore | null>(null)\n const isLoading = ref(false)\n const loadingStage = ref<EcLoadingStageInfo | null>(null)\n const detectedFormat = ref('')\n const isLive = ref(false) // 跟踪当前是否为直播流\n\n // 用于追踪最新的 play 请求,解决快速切换源时的竞态条件\n let playRequestId = 0\n\n // 超时保护计时器\n let loadingTimeoutId: ReturnType<typeof setTimeout> | null = null\n const LOADING_TIMEOUT_MS = 10000 // 10 秒超时检查间隔\n const MAX_LOADING_TIME_MS = 120000 // 最大加载时间 2 分钟\n\n // 加载开始时间(用于最大超时保护)\n let loadingStartTime = 0\n\n // 检查加载超时(支持下载中延长等待)\n function checkLoadingTimeout(requestId: number): void {\n if (!isLoading.value || requestId !== playRequestId) return\n\n console.log('[EcPlayer] checkLoadingTimeout:', {\n stage: loadingStage.value?.stage,\n elapsed: Date.now() - loadingStartTime,\n isLoading: isLoading.value\n })\n\n // 检查是否超过最大加载时间\n if (Date.now() - loadingStartTime > MAX_LOADING_TIME_MS) {\n console.warn('[EcPlayer] Max loading time exceeded, resetting isLoading state')\n isLoading.value = false\n return\n }\n\n // 如果正在下载数据,继续等待\n if (loadingStage.value?.stage === 'loading') {\n console.log('[EcPlayer] Download in progress, extending wait...')\n loadingTimeoutId = setTimeout(() => checkLoadingTimeout(requestId), LOADING_TIMEOUT_MS)\n return\n }\n\n // 其他情况,超时重置\n console.warn('[EcPlayer] Loading timeout, resetting isLoading state')\n isLoading.value = false\n }\n\n // 当前播放器配置(初始化时合并全局配置和默认值)\n const globalConfig = getGlobalConfig()\n const currentOptions = reactive<Required<PlayerOptions>>({\n targetBufferSize: globalConfig.targetBufferSize ?? DEFAULT_PLAYER_OPTIONS.targetBufferSize,\n decodeBatchSize: globalConfig.decodeBatchSize ?? DEFAULT_PLAYER_OPTIONS.decodeBatchSize,\n maxQueueSize: globalConfig.maxQueueSize ?? DEFAULT_PLAYER_OPTIONS.maxQueueSize\n })\n\n // 响应式状态\n const state = reactive<EcPlayerState>({\n isPlaying: false,\n isLoaded: false,\n isLoading: false,\n isBuffering: false,\n bufferingProgress: undefined,\n fps: 0,\n resolution: '-',\n decoded: 0,\n downloaded: 0,\n droppedFrames: 0,\n isPrefetching: false,\n segmentIndex: 0,\n fetchedSegmentCount: 0,\n totalSegments: 0,\n downloadSpeed: 0,\n currentTime: 0,\n duration: 0,\n playbackRate: 1\n })\n\n // 创建回调\n const callbacks: EcPlayerCallbacks = {\n onStateChange: (partial) => {\n Object.assign(state, partial)\n\n // 派发 play/pause 事件\n if (partial.isPlaying === true) {\n emit.play()\n } else if (partial.isPlaying === false && state.isPlaying) {\n emit.pause()\n }\n },\n onError: (error: any) => {\n // AbortError 是主动取消(如切换流),不是真正的错误,静默处理\n if (error?.name === 'AbortError') {\n console.log('[EcPlayer] Error ignored (AbortError - expected during stream switch)')\n return\n }\n console.error('[EcPlayer] Error:', error)\n // emit 错误事件,携带当前播放进度\n const currentTime = player.value?.getCurrentTime() ?? 0\n emit.error(error, currentTime)\n },\n onReady: () => {\n // 清除超时计时器\n if (loadingTimeoutId) {\n clearTimeout(loadingTimeoutId)\n loadingTimeoutId = null\n }\n // 第一帧已准备好,取消 loading 状态\n isLoading.value = false\n loadingStage.value = null\n console.log('[EcPlayer] First frame ready, loading finished')\n },\n onLoadingStageChange: (info) => {\n loadingStage.value = info\n console.log('[EcPlayer] Loading stage:', info.stage, info.detail || '')\n }\n }\n\n // 播放(加载并播放)\n async function play(options: { url: string; isLive?: boolean }): Promise<void> {\n const { url, isLive: isLiveParam = false } = options\n if (!url) {\n console.error('[EcPlayer] URL is required')\n return\n }\n\n // 递增请求 ID,用于追踪这次调用\n const currentRequestId = ++playRequestId\n console.log('[EcPlayer] play() called, requestId:', currentRequestId)\n\n // 如果正在加载中,先销毁当前播放器\n if (isLoading.value && player.value) {\n console.log('[EcPlayer] Aborting current load to start new one...')\n player.value.destroy()\n player.value = null\n }\n\n // 等待 canvas 准备好\n let retries = 10\n while (!canvasRef.value && retries > 0) {\n await new Promise(resolve => setTimeout(resolve, 100))\n retries--\n }\n\n // 检查是否有更新的请求\n if (currentRequestId !== playRequestId) {\n console.log('[EcPlayer] Request obsolete after waiting for canvas, aborting')\n return\n }\n\n if (!canvasRef.value) {\n console.error('[EcPlayer] Canvas element not found')\n return\n }\n\n isLoading.value = true\n isLive.value = isLiveParam\n loadingStartTime = Date.now()\n\n // 启动超时保护\n if (loadingTimeoutId) {\n clearTimeout(loadingTimeoutId)\n }\n loadingTimeoutId = setTimeout(() => checkLoadingTimeout(currentRequestId), LOADING_TIMEOUT_MS)\n\n try {\n // 销毁旧播放器\n if (player.value) {\n player.value.destroy()\n player.value = null\n }\n\n // 重置状态\n state.isPlaying = false\n state.isLoaded = false\n state.fps = 0\n state.resolution = '-'\n state.decoded = 0\n state.downloaded = 0\n state.droppedFrames = 0\n state.segmentIndex = 0\n state.totalSegments = 0\n state.downloadSpeed = 0\n state.currentTime = 0\n state.duration = 0\n detectedFormat.value = ''\n\n // 创建配置\n const config: EcPlayerCoreConfig & { canvas: HTMLCanvasElement } = {\n ...createConfig(currentOptions),\n canvas: canvasRef.value,\n isLive: isLiveParam\n }\n\n // 创建播放器(保存局部引用,防止过期请求销毁其他请求创建的播放器)\n const currentPlayer = new EcPlayerCore(config, callbacks)\n player.value = currentPlayer\n\n // 加载视频\n await currentPlayer.load(url, isLiveParam)\n\n // 检查是否有更新的请求\n if (currentRequestId !== playRequestId) {\n console.log('[EcPlayer] Request obsolete after load, aborting')\n // 只销毁自己创建的播放器(避免误销毁其他请求的播放器)\n if (player.value === currentPlayer) {\n currentPlayer.destroy()\n player.value = null\n }\n return\n }\n\n // 再次确认播放器没有被其他请求替换\n if (player.value !== currentPlayer) {\n console.log('[EcPlayer] Player replaced by newer request, aborting')\n return\n }\n\n // 获取检测到的格式\n if (!player.value) return\n detectedFormat.value = player.value.getDetectedFormat()\n\n // 标记加载完成\n state.isLoaded = true\n\n // 开始播放\n await player.value.play()\n\n console.log('[EcPlayer] Playing stream, format:', detectedFormat.value)\n } catch (error: any) {\n console.error('[EcPlayer] Failed to play:', error?.message || error)\n // 清除超时计时器\n if (loadingTimeoutId) {\n clearTimeout(loadingTimeoutId)\n loadingTimeoutId = null\n }\n // 发生错误时也要取消 loading 状态\n if (currentRequestId === playRequestId) {\n isLoading.value = false\n }\n }\n // 注意:isLoading 不在 finally 中重置,而是等待 onReady 事件\n // 这样可以确保转圈动画持续到第一帧准备好为止\n }\n\n // 设置配置\n function setOptions(options: PlayerOptions): void {\n if (options.targetBufferSize !== undefined) {\n currentOptions.targetBufferSize = options.targetBufferSize\n }\n if (options.decodeBatchSize !== undefined) {\n currentOptions.decodeBatchSize = options.decodeBatchSize\n }\n if (options.maxQueueSize !== undefined) {\n currentOptions.maxQueueSize = options.maxQueueSize\n }\n console.log('[EcPlayer] Options updated:', currentOptions)\n }\n\n // 设置日志级别\n function setLogLevel(level: number): void {\n coreSetLogLevel(level)\n }\n\n // 暂停\n function pause(): void {\n player.value?.pause()\n }\n\n // 恢复播放\n async function resume(): Promise<void> {\n if (!player.value) return\n await player.value.play()\n }\n\n // 跳转\n async function seek(time: number): Promise<void> {\n if (!player.value) return\n await player.value.seek(time)\n }\n\n // 设置倍速\n function setPlaybackRate(rate: number): void {\n if (!player.value) return\n player.value.setPlaybackRate(rate)\n }\n\n // 获取播放器实例\n function getPlayer(): EcPlayerCore | null {\n return player.value\n }\n\n // 获取状态\n function getState(): EcPlayerState {\n return player.value?.getState() ?? { ...state }\n }\n\n // 获取当前时间\n function getCurrentTime(): number {\n return player.value?.getCurrentTime() ?? 0\n }\n\n // 获取总时长\n function getDuration(): number {\n return player.value?.getDuration() ?? 0\n }\n\n // 销毁播放器\n function destroy(): void {\n if (player.value) {\n player.value.destroy()\n player.value = null\n }\n }\n\n // 组件卸载时销毁\n onUnmounted(() => {\n destroy()\n })\n\n return {\n canvasRef,\n player,\n state,\n isLoading,\n loadingStage,\n detectedFormat,\n isLive,\n play,\n setOptions,\n setLogLevel,\n pause,\n resume,\n seek,\n setPlaybackRate,\n getPlayer,\n getState,\n getCurrentTime,\n getDuration,\n destroy\n }\n}\n","/**\n * 播放器控制条逻辑\n */\n\nimport { ref, computed, type Ref } from 'vue'\n\n/**\n * 控制条逻辑参数\n */\nexport interface UseControlsParams {\n /** 是否正在播放 */\n isPlaying: Ref<boolean>\n /** 是否已加载 */\n isLoaded: Ref<boolean>\n /** 当前时间(毫秒) */\n currentTime: Ref<number>\n /** 总时长(毫秒) */\n duration: Ref<number>\n /** 已解析的分片索引 */\n segmentIndex: Ref<number>\n /** 已下载的分片总数 */\n fetchedSegmentCount: Ref<number>\n /** 总分片数 */\n totalSegments: Ref<number>\n /** 是否为直播流 */\n isLive: Ref<boolean>\n /** 跳转方法 */\n seek: (time: number) => Promise<void>\n}\n\n/**\n * 控制条逻辑返回值\n */\nexport interface UseControlsReturn {\n /** 是否显示控制条 */\n showControls: Ref<boolean>\n /** 进度条 hover 时间 */\n hoverTime: Ref<number | null>\n /** 进度条 hover 位置 */\n hoverPosition: Ref<number>\n /** 播放进度百分比 */\n playProgress: Ref<number>\n /** 缓冲进度百分比 */\n bufferProgress: Ref<number>\n /** 显示控制条 */\n showControlsBar: () => void\n /** 隐藏控制条 */\n hideControlsBar: () => void\n /** 处理进度条点击 */\n handleProgressClick: (event: MouseEvent) => Promise<void>\n /** 处理进度条悬停 */\n handleProgressHover: (event: MouseEvent) => void\n /** 格式化时间 */\n formatTime: (ms: number) => string\n}\n\n/**\n * 控制条逻辑 Hook\n */\nexport function useControls(params: UseControlsParams): UseControlsReturn {\n let hideControlsTimer: number | null = null\n const {\n isPlaying,\n isLoaded,\n currentTime,\n duration,\n segmentIndex,\n fetchedSegmentCount,\n totalSegments,\n isLive,\n seek\n } = params\n\n const showControls = ref(true)\n const hoverTime = ref<number | null>(null)\n const hoverPosition = ref(0)\n\n // 计算播放进度\n const playProgress = computed(() => {\n if (duration.value === 0) return 0\n return (currentTime.value / duration.value) * 100\n })\n\n // 计算缓冲进度(已下载的分段数)\n const bufferProgress = computed(() => {\n if (totalSegments.value === 0) return 0\n return (fetchedSegmentCount.value / totalSegments.value) * 100\n })\n\n // 显示控制条\n function showControlsBar(): void {\n showControls.value = true\n if (hideControlsTimer) {\n clearTimeout(hideControlsTimer)\n }\n hideControlsTimer = window.setTimeout(() => {\n if (isPlaying.value) {\n showControls.value = false\n }\n }, 3000)\n }\n\n // 隐藏控制条\n function hideControlsBar(): void {\n if (isPlaying.value) {\n showControls.value = false\n }\n }\n\n // 处理进度条点击\n async function handleProgressClick(event: MouseEvent): Promise<void> {\n if (duration.value === 0) return\n\n const target = event.currentTarget as HTMLElement\n const rect = target.getBoundingClientRect()\n const clickX = event.clientX - rect.left\n const percentage = clickX / rect.width\n const targetTime = percentage * duration.value\n\n await seek(targetTime)\n }\n\n // 处理进度条悬停\n function handleProgressHover(event: MouseEvent): void {\n if (duration.value === 0) return\n\n const target = event.currentTarget as HTMLElement\n const rect = target.getBoundingClientRect()\n const hoverX = event.clientX - rect.left\n const percentage = Math.max(0, Math.min(1, hoverX / rect.width))\n\n hoverTime.value = percentage * duration.value\n hoverPosition.value = percentage * 100\n }\n\n // 格式化时间\n function formatTime(ms: number): string {\n const totalSeconds = Math.floor(ms / 1000)\n const hours = Math.floor(totalSeconds / 3600)\n const minutes = Math.floor((totalSeconds % 3600) / 60)\n const seconds = totalSeconds % 60\n\n if (hours > 0) {\n return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`\n }\n return `${minutes}:${seconds.toString().padStart(2, '0')}`\n }\n\n return {\n showControls,\n hoverTime,\n hoverPosition,\n playProgress,\n bufferProgress,\n showControlsBar,\n hideControlsBar,\n handleProgressClick,\n handleProgressHover,\n formatTime\n }\n}\n","/**\n * 环境信息逻辑\n */\n\nimport { ref, computed, onMounted, type Ref } from 'vue'\nimport { EnvDetector, type EnvInfo } from '@give-tech/ec-player'\n\n/**\n * 环境信息逻辑返回值\n */\nexport interface UseEnvInfoReturn {\n /** 环境信息 */\n envInfo: Ref<EnvInfo | null>\n /** 是否显示环境面板 */\n showEnvPanel: Ref<boolean>\n /** 环境类型文本 */\n envTypeText: Ref<string>\n /** 切换环境面板显示 */\n toggleEnvPanel: () => void\n /** 检测环境 */\n detectEnv: () => void\n}\n\n/**\n * 环境信息逻辑 Hook\n */\nexport function useEnvInfo(\n initialShowPanel: boolean = false\n): UseEnvInfoReturn {\n const envInfo = ref<EnvInfo | null>(null)\n const showEnvPanel = ref(initialShowPanel)\n\n // 环境类型文本\n const envTypeText = computed(() => {\n if (!envInfo.value) return '未知'\n const typeMap: Record<string, string> = {\n 'pc-browser': 'PC 浏览器',\n 'mobile-browser': '移动端浏览器',\n 'wechat-miniprogram': '微信小程序',\n 'alipay-miniprogram': '支付宝小程序',\n 'other-miniprogram': '其他小程序',\n 'unknown': '未知',\n }\n return typeMap[envInfo.value.platform] || envInfo.value.platform\n })\n\n // 切换环境面板显示\n function toggleEnvPanel(): void {\n showEnvPanel.value = !showEnvPanel.value\n }\n\n // 检测环境\n function detectEnv(): void {\n envInfo.value = EnvDetector.detect()\n console.log('[EcPlayer] 环境检测结果:', envInfo.value)\n }\n\n // 组件挂载时检测环境\n onMounted(() => {\n detectEnv()\n })\n\n return {\n envInfo,\n showEnvPanel,\n envTypeText,\n toggleEnvPanel,\n detectEnv\n }\n}\n","<template>\n <div class=\"gt-player-container\" ref=\"containerRef\">\n <div\n class=\"gt-video-wrapper\"\n ref=\"videoWrapperRef\"\n @mouseenter=\"controls.showControlsBar\"\n @mousemove=\"controls.showControlsBar\"\n @mouseleave=\"controls.hideControlsBar\"\n >\n <!-- Canvas 视频渲染 -->\n <canvas ref=\"canvasRef\" class=\"gt-player-canvas\"></canvas>\n\n <!-- 封面/占位内容(未加载时显示) -->\n <div v-if=\"!player.state.isLoaded\" class=\"gt-placeholder\">\n <slot name=\"placeholder\"></slot>\n </div>\n\n <!-- 环境信息面板 -->\n <div v-if=\"showEnvPanel && envInfo\" class=\"gt-env-panel\">\n <div class=\"gt-env-row\">\n <span>platform</span>\n <span :class=\"envInfo.isMiniProgram ? 'warn' : ''\">{{ envTypeText }}</span>\n </div>\n <div class=\"gt-env-divider\"></div>\n <div class=\"gt-env-row\">\n <span>SIMD</span>\n <span :class=\"envInfo.capabilities.wasmSimd ? 'ok' : 'err'\">\n {{ envInfo.capabilities.wasmSimd ? 'Yes' : 'No' }}\n </span>\n </div>\n <div class=\"gt-env-row\">\n <span>WebGL</span>\n <span :class=\"envInfo.capabilities.webGL ? 'ok' : 'err'\">\n {{ envInfo.capabilities.webGL ? 'Yes' : 'No' }}\n </span>\n </div>\n <div class=\"gt-env-row\">\n <span>WebGL2</span>\n <span :class=\"envInfo.capabilities.webGL2 ? 'ok' : 'err'\">\n {{ envInfo.capabilities.webGL2 ? 'Yes' : 'No' }}\n </span>\n </div>\n <div class=\"gt-env-row\">\n <span>SAB</span>\n <span :class=\"envInfo.capabilities.sharedArrayBuffer ? 'ok' : 'err'\">\n {{ envInfo.capabilities.sharedArrayBuffer ? 'Yes' : 'No' }}\n </span>\n </div>\n <div class=\"gt-env-row\">\n <span>Threads</span>\n <span :class=\"envInfo.capabilities.wasmThreads ? 'ok' : 'err'\">\n {{ envInfo.capabilities.wasmThreads ? 'Yes' : 'No' }}\n </span>\n </div>\n <div class=\"gt-env-row\">\n <span>WebAudio</span>\n <span :class=\"envInfo.capabilities.webAudio ? 'ok' : 'err'\">\n {{ envInfo.capabilities.webAudio ? 'Yes' : 'No' }}\n </span>\n </div>\n <div class=\"gt-env-row\">\n <span>Stream</span>\n <span :class=\"envInfo.capabilities.readableStream ? 'ok' : 'err'\">\n {{ envInfo.capabilities.readableStream ? 'Yes' : 'No' }}\n </span>\n </div>\n <template v-if=\"player.state.isLoaded\">\n <div class=\"gt-env-divider\"></div>\n <div class=\"gt-env-row\" v-if=\"player.detectedFormat.value\"><span>Format</span><span>{{ player.detectedFormat.value }}</span></div>\n <div class=\"gt-env-row\"><span>FPS</span><span>{{ player.state.fps }}</span></div>\n <div class=\"gt-env-row\"><span>Resolution</span><span>{{ player.state.resolution }}</span></div>\n <div class=\"gt-env-row\"><span>downloaded</span><span>{{ player.state.downloaded }}</span></div>\n <div class=\"gt-env-row\"><span>decoded</span><span>{{ player.state.decoded }}</span></div>\n <div class=\"gt-env-row\"><span>Dropped</span><span>{{ player.state.droppedFrames }}</span></div>\n <div class=\"gt-env-row\"><span>Speed</span><span>{{ player.state.downloadSpeed || 0 }} KB/s</span></div>\n </template>\n </div>\n\n <!-- 加载中动画 -->\n <div class=\"gt-loading-spinner\" v-if=\"player.isLoading.value\">\n <slot name=\"loading\" :stage=\"player.loadingStage.value\">\n <div class=\"gt-loading-content\">\n <div class=\"gt-spinner\"></div>\n <div class=\"gt-loading-text\" v-if=\"player.loadingStage.value\">\n {{ getLoadingText(player.loadingStage.value) }}\n </div>\n <!-- 下载进度 -->\n <div class=\"gt-loading-progress\" v-if=\"getLoadingProgress(player.loadingStage.value)\">\n <div class=\"gt-progress-bar-bg\">\n <div class=\"gt-progress-bar-fill\" :style=\"{ width: getLoadingProgress(player.loadingStage.value) + '%' }\"></div>\n </div>\n <div class=\"gt-progress-text\">{{ getLoadingProgress(player.loadingStage.value) }}%</div>\n </div>\n </div>\n </slot>\n </div>\n\n <!-- 播放中缓冲提示 -->\n <div class=\"gt-buffering-indicator\" v-if=\"player.state.isBuffering && !player.isLoading.value\">\n <div class=\"gt-buffering-content\">\n <div class=\"gt-buffering-spinner\"></div>\n <span v-if=\"player.state.bufferingProgress !== undefined\">\n 正在缓冲... {{ player.state.bufferingProgress }}%\n </span>\n <span v-else>正在缓冲...</span>\n </div>\n </div>\n\n <!-- 错误提示 -->\n <div class=\"gt-error-message\" v-if=\"errorMessage\">\n <slot name=\"error\" :message=\"errorMessage\" :retry=\"handleRetry\">\n <div class=\"gt-error-content\">\n <div class=\"gt-error-icon-wrapper\">\n <svg class=\"gt-error-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/>\n <line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/>\n </svg>\n </div>\n <div class=\"gt-error-body\">\n <div class=\"gt-error-title\">播放失败</div>\n <div class=\"gt-error-text\">{{ errorMessage }}</div>\n </div>\n <button class=\"gt-error-retry\" @click=\"handleRetry\" title=\"重试\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M17.65 6.35A7.958 7.958 0 0012 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0112 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z\"/>\n </svg>\n </button>\n </div>\n </slot>\n </div>\n\n <!-- 视频控制条 -->\n <div\n v-if=\"controlOption.show\"\n class=\"gt-video-controls\"\n :class=\"{ visible: controls.showControls.value || !player.state.isPlaying }\"\n >\n <button class=\"gt-control-btn\" @click=\"togglePlayback\">\n <svg v-if=\"player.state.isPlaying\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\"/>\n </svg>\n <svg v-else viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M8 5v14l11-7z\"/>\n </svg>\n </button>\n\n <!-- 直播标识 -->\n <span class=\"gt-live-indicator\" v-if=\"isLive\">\n <span class=\"gt-live-dot\"></span>\n <span class=\"gt-live-text\">实时</span>\n </span>\n\n <!-- 点播时间显示 -->\n <span class=\"gt-time-display\" v-else>\n <span class=\"gt-time-current\">{{ controls.formatTime(player.state.currentTime) }}</span>\n <span class=\"gt-time-separator\">/</span>\n <span class=\"gt-time-duration\">{{ controls.formatTime(player.state.duration) }}</span>\n </span>\n\n <!-- 进度条 (仅点播) -->\n <div\n v-if=\"!isLive\"\n class=\"gt-progress-container\"\n @click=\"controls.handleProgressClick\"\n @mousemove=\"controls.handleProgressHover\"\n @mouseleave=\"controls.hoverTime.value = null\"\n >\n <div class=\"gt-progress-bar\">\n <div class=\"gt-progress-buffered\" :style=\"{ width: controls.bufferProgress.value + '%' }\"></div>\n <div class=\"gt-progress-played\" :style=\"{ width: controls.playProgress.value + '%' }\">\n <div class=\"gt-progress-handle\"></div>\n </div>\n </div>\n <div\n class=\"gt-progress-tooltip\"\n v-if=\"controls.hoverTime.value !== null\"\n :style=\"{ left: controls.hoverPosition.value + '%' }\"\n >\n {{ controls.formatTime(controls.hoverTime.value) }}\n </div>\n </div>\n\n <!-- 直播流占位符 -->\n <div class=\"gt-flex-spacer\" v-else></div>\n\n <!-- 倍速按钮(仅点播) -->\n <div v-if=\"controlOption.playbackRate && !isLive\" class=\"gt-playback-rate-container\">\n <button class=\"gt-control-btn gt-playback-rate-btn\" @click=\"togglePlaybackRateMenu\" :title=\"'倍速: ' + currentPlaybackRate + 'x'\">\n <span class=\"gt-playback-rate-text\">{{ currentPlaybackRate }}x</span>\n </button>\n <div v-if=\"showPlaybackRateMenu\" class=\"gt-playback-rate-menu\">\n <button\n v-for=\"rate in playbackRates\"\n :key=\"rate\"\n class=\"gt-playback-rate-option\"\n :class=\"{ active: currentPlaybackRate === rate }\"\n @click=\"setPlaybackRate(rate)\"\n >\n {{ rate }}x\n </button>\n </div>\n </div>\n\n <!-- 环境信息按钮 -->\n <button v-if=\"controlOption.info\" class=\"gt-control-btn gt-env-info-btn\" @click=\"toggleEnvPanel\" :title=\"showEnvPanel ? '隐藏信息' : '显示信息'\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\"/>\n </svg>\n </button>\n\n <!-- 全屏按钮 -->\n <button v-if=\"controlOption.fullscreen\" class=\"gt-control-btn gt-fullscreen-btn\" @click=\"toggleFullscreen\" :title=\"isFullscreen ? '退出全屏' : '全屏'\">\n <!-- 退出全屏图标 -->\n <svg v-if=\"isFullscreen\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z\"/>\n </svg>\n <!-- 全屏图标 -->\n <svg v-else viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z\"/>\n </svg>\n </button>\n </div>\n\n <!-- 大播放按钮(居中) -->\n <div\n class=\"gt-center-play-btn\"\n v-if=\"player.state.isLoaded && !player.state.isPlaying && !player.isLoading.value\"\n @click=\"togglePlayback\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M8 5v14l11-7z\"/>\n </svg>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref, watch, onMounted, onUnmounted } from 'vue'\nimport { usePlayer } from '../composables/usePlayer'\nimport { useControls } from '../composables/useControls'\nimport { useEnvInfo } from '../composables/useEnvInfo'\nimport type { EcPlayerProps, EcPlayerExpose, EcPlayerSource, EcPlayerControlOption } from '../types'\nimport { DEFAULT_EC_PLAYER_PROPS } from '../types'\n\n// Props\nconst props = withDefaults(defineProps<EcPlayerProps>(), DEFAULT_EC_PLAYER_PROPS)\n\n// Emits\nconst emit = defineEmits<{\n (e: 'play'): void\n (e: 'pause'): void\n (e: 'error', error: Error, currentTime: number): void\n (e: 'retry'): void\n}>()\n\n// Canvas ref(本地)\nconst canvasRef = ref<HTMLCanvasElement | null>(null)\n\n// 容器引用(用于全屏)\nconst containerRef = ref<HTMLElement | null>(null)\n\n// 全屏状态\nconst isFullscreen = ref(false)\n\n// 错误状态\nconst errorMessage = ref<string | null>(null)\n\n// 加载阶段文本映射\nfunction getLoadingText(stage: { stage: string; detail?: string }): string {\n const stageTexts: Record<string, string> = {\n 'connecting': '正在连接...',\n 'detected': `检测到格式: ${stage.detail || ''}`,\n 'loading': stage.detail || '正在加载...',\n 'initializing': '正在初始化...'\n }\n return stageTexts[stage.stage] || '加载中...'\n}\n\n// 从 detail 中提取进度百分比\nfunction getLoadingProgress(stage: { stage: string; detail?: string } | null): number | null {\n if (!stage || stage.stage !== 'loading' || !stage.detail) return null\n // detail 格式: \"45% (12.3/50.0 MB)\"\n const match = stage.detail.match(/^(\\d+)%/)\n return match ? parseInt(match[1], 10) : null\n}\n\n// 清除错误\nfunction clearError(): void {\n errorMessage.value = null\n}\n\n// 重试处理\nfunction handleRetry(): void {\n clearError()\n emit('retry')\n}\n\n// 包装 emit,捕获错误(必须在 errorMessage 声明之后)\nconst wrappedEmit = {\n play: () => emit('play'),\n pause: () => emit('pause'),\n error: (error: Error, currentTime: number) => {\n // 设置错误信息显示\n errorMessage.value = error.message || '播放出错'\n emit('error', error, currentTime)\n },\n retry: () => emit('retry')\n}\n\n// 倍速播放状态\nconst playbackRates = [1, 2, 4]\nconst currentPlaybackRate = ref(1)\nconst showPlaybackRateMenu = ref(false)\n\n// 解析 controlOption\nconst controlOption = computed<Required<EcPlayerControlOption>>(() => ({\n show: props.controlOption?.show ?? DEFAULT_EC_PLAYER_PROPS.controlOption.show,\n fullscreen: props.controlOption?.fullscreen ?? DEFAULT_EC_PLAYER_PROPS.controlOption.fullscreen,\n info: props.controlOption?.info ?? DEFAULT_EC_PLAYER_PROPS.controlOption.info,\n playbackRate: props.controlOption?.playbackRate ?? DEFAULT_EC_PLAYER_PROPS.controlOption.playbackRate\n}))\n\n// 环境信息逻辑\nconst { envInfo, showEnvPanel, envTypeText, toggleEnvPanel } = useEnvInfo(false)\n\n// 播放器逻辑\nconst player = usePlayer(wrappedEmit)\n\n// 同步 canvas ref 到 player\nwatch(canvasRef, (el) => {\n player.canvasRef.value = el\n}, { immediate: true })\n\n// isLive 计算属性(用于模板)\nconst isLive = computed(() => player.isLive.value)\n\n// 控制条逻辑\nconst isPlayingRef = computed(() => player.state.isPlaying)\nconst isLoadedRef = computed(() => player.state.isLoaded)\nconst currentTimeRef = computed(() => player.state.currentTime)\nconst durationRef = computed(() => player.state.duration)\nconst segmentIndexRef = computed(() => player.state.segmentIndex)\nconst fetchedSegmentCountRef = computed(() => player.state.fetchedSegmentCount)\nconst totalSegmentsRef = computed(() => player.state.totalSegments)\n\nconst controls = useControls({\n isPlaying: isPlayingRef,\n isLoaded: isLoadedRef,\n currentTime: currentTimeRef,\n duration: durationRef,\n segmentIndex: segmentIndexRef,\n fetchedSegmentCount: fetchedSegmentCountRef,\n totalSegments: totalSegmentsRef,\n isLive,\n seek: player.seek\n})\n\n// 视频包装器引用\nconst videoWrapperRef = ref<HTMLElement | null>(null)\n\n// 解析 src\nfunction parseSrc(src: EcPlayerSource | undefined): { url: string; isLive: boolean } | null {\n if (!src || !src.url) return null\n return { url: src.url, isLive: src.isLive ?? false }\n}\n\n// 切换播放/暂停\nfunction togglePlayback(): void {\n if (player.state.isPlaying) {\n player.pause()\n } else {\n player.resume()\n }\n}\n\n// 切换全屏\nfunction toggleFullscreen(): void {\n if (!containerRef.value) return\n\n if (!isFullscreen.value) {\n if (containerRef.value.requestFullscreen) {\n containerRef.value.requestFullscreen()\n }\n } else {\n if (document.exitFullscreen) {\n document.exitFullscreen()\n }\n }\n}\n\n// 设置倍速\nfunction setPlaybackRate(rate: number): void {\n currentPlaybackRate.value = rate\n showPlaybackRateMenu.value = false\n player.setPlaybackRate(rate)\n}\n\n// 切换倍速菜单\nfunction togglePlaybackRateMenu(): void {\n showPlaybackRateMenu.value = !showPlaybackRateMenu.value\n}\n\n// 监听全屏变化\nfunction handleFullscreenChange(): void {\n isFullscreen.value = !!document.fullscreenElement\n}\n\n// 暴露的 play 方法(简化版)\nfunction play(): void {\n const parsed = parseSrc(props.src)\n if (parsed) {\n player.play(parsed)\n }\n}\n\n// 监听 src 变化,自动播放\nwatch(() => props.src, (newSrc) => {\n const parsed = parseSrc(newSrc)\n if (parsed && parsed.url) {\n // 清除之前的错误信息\n errorMessage.value = null\n player.play(parsed)\n }\n}, { immediate: true })\n\n// 挂载时监听全屏事件\nonMounted(() => {\n document.addEventListener('fullscreenchange', handleFullscreenChange)\n})\n\n// 卸载时移除监听\nonUnmounted(() => {\n document.removeEventListener('fullscreenchange', handleFullscreenChange)\n})\n\n// Expose 方法\ndefineExpose<EcPlayerExpose>({\n play,\n pause: player.pause,\n seek: player.seek,\n destroy: player.destroy,\n getState: player.getState\n})\n</script>\n\n<style scoped>\n.gt-player-container {\n width: 100%;\n height: 100%;\n}\n\n.gt-video-wrapper {\n position: relative;\n width: 100%;\n height: 100%;\n background: #000;\n overflow: hidden;\n}\n\n.gt-player-canvas {\n width: 100%;\n height: 100%;\n object-fit: contain;\n}\n\n.gt-placeholder {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #000;\n}\n\n/* 加载中动画 */\n.gt-loading-spinner {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.5);\n z-index: 10;\n}\n\n.gt-loading-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 16px;\n}\n\n.gt-spinner {\n width: 10%;\n max-width: 48px;\n min-width: 20px;\n aspect-ratio: 1/1;\n border: 0.3em solid rgba(255, 255, 255, 0.3);\n border-top-color: #fff;\n border-radius: 50%;\n animation: gt-spin 1s linear infinite;\n}\n\n.gt-loading-text {\n color: rgba(255, 255, 255, 0.85);\n font-size: 13px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n text-align: center;\n padding: 0 20px;\n}\n\n/* 加载进度条 */\n.gt-loading-progress {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 0 20px;\n width: 100%;\n max-width: 280px;\n}\n\n.gt-progress-bar-bg {\n flex: 1;\n height: 4px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 2px;\n overflow: hidden;\n}\n\n.gt-progress-bar-fill {\n height: 100%;\n background: linear-gradient(90deg, #ff2d55, #ff6b6b);\n border-radius: 2px;\n transition: width 0.2s ease;\n}\n\n.gt-progress-text {\n color: rgba(255, 255, 255, 0.9);\n font-size: 12px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n min-width: 36px;\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n@keyframes gt-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n/* 错误提示 */\n.gt-error-message {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.75);\n z-index: 15;\n animation: gt-error-fade-in 0.25s ease;\n}\n\n@keyframes gt-error-fade-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n.gt-error-content {\n display: flex;\n align-items: flex-start;\n gap: 16px;\n padding: 20px 24px;\n background: rgba(60, 60, 60, 0.95);\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 12px;\n max-width: 85%;\n min-width: 280px;\n backdrop-filter: blur(12px);\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.05) inset;\n animation: gt-error-slide-in 0.3s ease;\n}\n\n@keyframes gt-error-slide-in {\n from {\n opacity: 0;\n transform: translateY(-10px) scale(0.95);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n\n.gt-error-icon-wrapper {\n flex-shrink: 0;\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(255, 77, 77, 0.2);\n border-radius: 50%;\n}\n\n.gt-error-icon {\n width: 22px;\n height: 22px;\n color: #ff4d4f;\n}\n\n.gt-error-body {\n flex: 1;\n min-width: 0;\n}\n\n.gt-error-title {\n color: #ff4d4f;\n font-size: 15px;\n font-weight: 600;\n margin-bottom: 6px;\n}\n\n.gt-error-text {\n color: rgba(255, 255, 255, 0.85);\n font-size: 13px;\n line-height: 1.5;\n word-break: break-word;\n}\n\n.gt-error-retry {\n flex-shrink: 0;\n width: 32px;\n height: 32px;\n border: none;\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.15);\n color: rgba(255, 255, 255, 0.9);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n margin-left: 8px;\n}\n\n.gt-error-retry:hover {\n background: rgba(255, 255, 255, 0.25);\n color: white;\n}\n\n.gt-error-retry svg {\n width: 20px;\n height: 20px;\n}\n\n/* 视频控制条 */\n.gt-video-controls {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n padding: 10px 16px;\n background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));\n opacity: 0;\n transition: opacity 0.3s ease;\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.gt-flex-spacer {\n flex: 1;\n}\n\n.gt-video-controls.visible {\n opacity: 1;\n}\n\n.gt-control-btn {\n width: 28px;\n height: 28px;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: white;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n\n.gt-control-btn:hover {\n background: rgba(255, 255, 255, 0.2);\n}\n\n.gt-control-btn svg {\n width: 14px;\n height: 14px;\n}\n\n.gt-progress-container {\n flex: 1;\n position: relative;\n cursor: pointer;\n height: 20px;\n display: flex;\n align-items: center;\n}\n\n.gt-progress-bar {\n width: 100%;\n height: 4px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 2px;\n position: relative;\n overflow: hidden;\n transition: height 0.2s ease;\n}\n\n.gt-progress-container:hover .gt-progress-bar {\n height: 6px;\n}\n\n.gt-progress-buffered {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: rgba(255, 255, 255, 0.4);\n border-radius: 2px;\n transition: width 0.1s;\n}\n\n.gt-progress-played {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: #ff2d55;\n border-radius: 2px;\n transition: width 0.1s;\n}\n\n.gt-progress-handle {\n position: absolute;\n right: -6px;\n top: 50%;\n transform: translateY(-50%);\n width: 14px;\n height: 14px;\n background: #ff2d55;\n border-radius: 50%;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n opacity: 0;\n transition: opacity 0.2s, transform 0.2s;\n}\n\n.gt-progress-container:hover .gt-progress-handle {\n opacity: 1;\n transform: translateY(-50%) scale(1.2);\n}\n\n.gt-progress-tooltip {\n position: absolute;\n bottom: calc(100% + 8px);\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.85);\n color: white;\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 11px;\n white-space: nowrap;\n pointer-events: none;\n}\n\n.gt-time-display {\n color: white;\n font-size: 12px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.gt-time-separator {\n margin: 0 4px;\n opacity: 0.6;\n}\n\n/* 直播指示器 */\n.gt-live-indicator {\n display: flex;\n align-items: center;\n gap: 6px;\n color: white;\n font-size: 12px;\n font-weight: 500;\n}\n\n.gt-live-dot {\n width: 8px;\n height: 8px;\n background: #ff2d55;\n border-radius: 50%;\n animation: gt-live-pulse 1.5s ease-in-out infinite;\n}\n\n.gt-live-text {\n color: #ff2d55;\n}\n\n@keyframes gt-live-pulse {\n 0%, 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.4;\n }\n}\n\n/* 倍速按钮 */\n.gt-playback-rate-container {\n position: relative;\n flex-shrink: 0;\n}\n\n.gt-playback-rate-btn {\n width: auto !important;\n min-width: 28px;\n padding: 0 8px;\n font-size: 12px;\n font-weight: 500;\n}\n\n.gt-playback-rate-text {\n color: white;\n}\n\n.gt-playback-rate-menu {\n position: absolute;\n bottom: calc(100% + 8px);\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.85);\n border-radius: 6px;\n padding: 4px 0;\n min-width: 60px;\n z-index: 20;\n animation: gt-rate-menu-fade-in 0.15s ease;\n}\n\n@keyframes gt-rate-menu-fade-in {\n from { opacity: 0; transform: translateX(-50%) translateY(4px); }\n to { opacity: 1; transform: translateX(-50%) translateY(0); }\n}\n\n.gt-playback-rate-option {\n display: block;\n width: 100%;\n padding: 6px 12px;\n border: none;\n background: transparent;\n color: rgba(255, 255, 255, 0.8);\n font-size: 12px;\n cursor: pointer;\n text-align: center;\n transition: all 0.15s ease;\n}\n\n.gt-playback-rate-option:hover {\n background: rgba(255, 255, 255, 0.1);\n color: white;\n}\n\n.gt-playback-rate-option.active {\n color: #ff2d55;\n font-weight: 500;\n}\n\n/* 居中播放按钮 */\n.gt-center-play-btn {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 56px;\n height: 56px;\n background: rgba(0, 0, 0, 0.5);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.gt-center-play-btn:hover {\n background: rgba(0, 0, 0, 0.7);\n transform: translate(-50%, -50%) scale(1.05);\n}\n\n.gt-center-play-btn svg {\n width: 24px;\n height: 24px;\n color: white;\n margin-left: 3px;\n}\n\n/* 环境信息面板 */\n.gt-env-panel {\n position: absolute;\n top: 12px;\n left: 12px;\n background: rgba(0, 0, 0, 0.8);\n border-radius: 6px;\n padding: 8px 10px;\n font-size: 11px;\n font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;\n min-width: 160px;\n max-width: 200px;\n max-height: calc(100% - 24px);\n overflow-y: auto;\n backdrop-filter: blur(10px);\n animation: gt-env-fade-in 0.15s ease;\n z-index: 15;\n}\n\n.gt-env-panel::-webkit-scrollbar {\n width: 4px;\n}\n\n.gt-env-panel::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.gt-env-panel::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.3);\n border-radius: 2px;\n}\n\n@keyframes gt-env-fade-in {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.gt-env-panel .gt-env-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 2px 0;\n color: rgba(255, 255, 255, 0.7);\n}\n\n.gt-env-panel .gt-env-row span:first-child {\n color: rgba(255, 255, 255, 0.5);\n margin-right: 16px;\n}\n\n.gt-env-panel .gt-env-row span:last-child {\n font-variant-numeric: tabular-nums;\n}\n\n.gt-env-panel .gt-env-row .ok { color: #4caf50; }\n.gt-env-panel .gt-env-row .warn { color: #ff9800; }\n.gt-env-panel .gt-env-row .err { color: #f44336; }\n\n.gt-env-divider {\n height: 1px;\n background: rgba(255, 255, 255, 0.1);\n margin: 4px 0;\n}\n\n/* 播放中缓冲提示 */\n.gt-buffering-indicator {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 10;\n}\n\n.gt-buffering-content {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n background: rgba(0, 0, 0, 0.6);\n border-radius: 20px;\n color: white;\n font-size: 13px;\n}\n\n.gt-buffering-spinner {\n width: 16px;\n height: 16px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-top-color: #fff;\n border-radius: 50%;\n animation: gt-spin 1s linear infinite;\n}\n</style>\n"],"names":["WASMLoader","globalConfig","setLogLevel","coreSetLogLevel","_createElementBlock","_createElementVNode","_unref","_openBlock","_renderSlot","_normalizeClass","_Fragment","_toDisplayString","_normalizeStyle","_renderList"],"mappings":";;;AAqKO,MAAM,0BAA0B;AAAA,EACrC,KAAK;AAAA,EACL,eAAe;AAAA,IACb,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,cAAc;AAAA,EAAA;AAElB;AAKO,MAAM,yBAAkD;AAAA,EAC7D,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,cAAc;AAChB;AAKO,MAAM,sBAAsC;AAAA,EACjD,MAAM;AAAA,EACN,SAAS;AACX;AAKA,IAAI,eAAqC,CAAA;AA2BlC,SAAS,kBAAkB,QAAoC;AACpE,iBAAe,EAAE,GAAG,cAAc,GAAG,OAAA;AACvC;AAKO,SAAS,kBAAwC;AACtD,SAAO,EAAE,GAAG,aAAA;AACd;AAKO,SAAS,gBAAgC;AAC9C,SAAO;AAAA,IACL,MAAM,aAAa,MAAM,QAAQ,oBAAoB;AAAA,IACrD,SAAS,aAAa,MAAM,WAAW,oBAAoB;AAAA,EAAA;AAE/D;AAKO,SAAS,gBAAgB,OAAsB;AACpD,SAAO;AAAA,IACL,KAAK,MAAM,OAAO,wBAAwB;AAAA,IAC1C,eAAe;AAAA,MACb,MAAM,MAAM,eAAe,QAAQ,aAAa,gBAAgB,wBAAwB,cAAc;AAAA,MACtG,YAAY,MAAM,eAAe,cAAc,wBAAwB,cAAc;AAAA,MACrF,MAAM,MAAM,eAAe,QAAQ,wBAAwB,cAAc;AAAA,MACzE,cAAc,MAAM,eAAe,gBAAgB,wBAAwB,cAAc;AAAA,IAAA;AAAA,EAC3F;AAEJ;AAKO,SAAS,uBAAuB,SAAiD;AACtF,SAAO;AAAA,IACL,kBAAkB,QAAQ,oBAAoB,aAAa,oBAAoB,uBAAuB;AAAA,IACtG,iBAAiB,QAAQ,mBAAmB,aAAa,mBAAmB,uBAAuB;AAAA,IACnG,cAAc,QAAQ,gBAAgB,aAAa,gBAAgB,uBAAuB;AAAA,EAAA;AAE9F;AAQO,SAAS,gBAAgB,gBAAiC;AAC/D,QAAM,aAAa,cAAA;AAGnB,MAAI,kBAAkB,WAAW,MAAM;AACrC,YAAQ,IAAI,+BAA+B,WAAW,IAAI;AAC1D,WAAO,WAAW;AAAA,EACpB;AAEA,MAAI,WAAW,SAAS;AACtB,YAAQ,IAAI,uDAAuD,WAAW,OAAO;AACrF,WAAO,WAAW;AAAA,EACpB;AAGA,UAAQ,IAAI,iEAAiE,WAAW,IAAI;AAC5F,SAAO,WAAW;AACpB;AAKA,IAAI,gBAAgB;AAYb,SAAS,YAAY,SAGnB;AAEP,MAAI,eAAe;AACjB,YAAQ,IAAI,yCAAyC;AACrD;AAAA,EACF;AACA,kBAAgB;AAEhB,QAAM,EAAE,cAAc,MAAM,iBAAiB,KAAA,IAAS,WAAW,CAAA;AACjE,QAAM,aAAa,cAAA;AACnB,QAAM,QAAkB,CAAA;AAExB,MAAI,eAAe,WAAW,MAAM;AAClC,UAAM,KAAK,WAAW,IAAI;AAAA,EAC5B;AACA,MAAI,kBAAkB,WAAW,SAAS;AACxC,UAAM,KAAK,WAAW,OAAO;AAAA,EAC/B;AAEA,MAAI,MAAM,SAAS,GAAG;AACpB,WAAO,sBAAsB,EAAE,KAAK,CAAC,EAAE,YAAAA,kBAAiB;AACtD,MAAAA,YAAW,WAAW,KAAK;AAC3B,cAAQ,IAAI,+BAA+B,KAAK;AAAA,IAClD,CAAC,EAAE,MAAM,CAAA,QAAO;AACd,cAAQ,KAAK,sCAAsC,GAAG;AAAA,IACxD,CAAC;AAAA,EACH;AACF;AAKO,SAAS,oBAA0B;AACxC,kBAAgB;AAClB;AC1RA,SAAS,aAAa,SAAsE;AAE1F,QAAM,UAAU,YAAY,OAAA;AAC5B,QAAM,WAAW,gBAAgB,QAAQ,aAAa,QAAQ;AAE9D,SAAO;AAAA,IACL;AAAA,IACA,kBAAkB,QAAQ;AAAA,IAC1B,iBAAiB,QAAQ;AAAA,IACzB,cAAc,QAAQ;AAAA,EAAA;AAE1B;AAeO,SAAS,UAAU,MAAsC;AAC9D,QAAM,YAAY,IAA8B,IAAI;AACpD,QAAM,SAAS,WAAgC,IAAI;AACnD,QAAM,YAAY,IAAI,KAAK;AAC3B,QAAM,eAAe,IAA+B,IAAI;AACxD,QAAM,iBAAiB,IAAI,EAAE;AAC7B,QAAM,SAAS,IAAI,KAAK;AAGxB,MAAI,gBAAgB;AAGpB,MAAI,mBAAyD;AAC7D,QAAM,qBAAqB;AAC3B,QAAM,sBAAsB;AAG5B,MAAI,mBAAmB;AAGvB,WAAS,oBAAoB,WAAyB;AACpD,QAAI,CAAC,UAAU,SAAS,cAAc,cAAe;AAErD,YAAQ,IAAI,mCAAmC;AAAA,MAC7C,OAAO,aAAa,OAAO;AAAA,MAC3B,SAAS,KAAK,IAAA,IAAQ;AAAA,MACtB,WAAW,UAAU;AAAA,IAAA,CACtB;AAGD,QAAI,KAAK,QAAQ,mBAAmB,qBAAqB;AACvD,cAAQ,KAAK,iEAAiE;AAC9E,gBAAU,QAAQ;AAClB;AAAA,IACF;AAGA,QAAI,aAAa,OAAO,UAAU,WAAW;AAC3C,cAAQ,IAAI,oDAAoD;AAChE,yBAAmB,WAAW,MAAM,oBAAoB,SAAS,GAAG,kBAAkB;AACtF;AAAA,IACF;AAGA,YAAQ,KAAK,uDAAuD;AACpE,cAAU,QAAQ;AAAA,EACpB;AAGA,QAAMC,gBAAe,gBAAA;AACrB,QAAM,iBAAiB,SAAkC;AAAA,IACvD,kBAAkBA,cAAa,oBAAoB,uBAAuB;AAAA,IAC1E,iBAAiBA,cAAa,mBAAmB,uBAAuB;AAAA,IACxE,cAAcA,cAAa,gBAAgB,uBAAuB;AAAA,EAAA,CACnE;AAGD,QAAM,QAAQ,SAAwB;AAAA,IACpC,WAAW;AAAA,IACX,UAAU;AAAA,IACV,WAAW;AAAA,IACX,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,KAAK;AAAA,IACL,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,eAAe;AAAA,IACf,cAAc;AAAA,IACd,qBAAqB;AAAA,IACrB,eAAe;AAAA,IACf,eAAe;AAAA,IACf,aAAa;AAAA,IACb,UAAU;AAAA,IACV,cAAc;AAAA,EAAA,CACf;AAGD,QAAM,YAA+B;AAAA,IACnC,eAAe,CAAC,YAAY;AAC1B,aAAO,OAAO,OAAO,OAAO;AAG5B,UAAI,QAAQ,cAAc,MAAM;AAC9B,aAAK,KAAA;AAAA,MACP,WAAW,QAAQ,cAAc,SAAS,MAAM,WAAW;AACzD,aAAK,MAAA;AAAA,MACP;AAAA,IACF;AAAA,IACA,SAAS,CAAC,UAAe;AAEvB,UAAI,OAAO,SAAS,cAAc;AAChC,gBAAQ,IAAI,uEAAuE;AACnF;AAAA,MACF;AACA,cAAQ,MAAM,qBAAqB,KAAK;AAExC,YAAM,cAAc,OAAO,OAAO,eAAA,KAAoB;AACtD,WAAK,MAAM,OAAO,WAAW;AAAA,IAC/B;AAAA,IACA,SAAS,MAAM;AAEb,UAAI,kBAAkB;AACpB,qBAAa,gBAAgB;AAC7B,2BAAmB;AAAA,MACrB;AAEA,gBAAU,QAAQ;AAClB,mBAAa,QAAQ;AACrB,cAAQ,IAAI,gDAAgD;AAAA,IAC9D;AAAA,IACA,sBAAsB,CAAC,SAAS;AAC9B,mBAAa,QAAQ;AACrB,cAAQ,IAAI,6BAA6B,KAAK,OAAO,KAAK,UAAU,EAAE;AAAA,IACxE;AAAA,EAAA;AAIF,iBAAe,KAAK,SAA2D;AAC7E,UAAM,EAAE,KAAK,QAAQ,cAAc,UAAU;AAC7C,QAAI,CAAC,KAAK;AACR,cAAQ,MAAM,4BAA4B;AAC1C;AAAA,IACF;AAGA,UAAM,mBAAmB,EAAE;AAC3B,YAAQ,IAAI,wCAAwC,gBAAgB;AAGpE,QAAI,UAAU,SAAS,OAAO,OAAO;AACnC,cAAQ,IAAI,sDAAsD;AAClE,aAAO,MAAM,QAAA;AACb,aAAO,QAAQ;AAAA,IACjB;AAGA,QAAI,UAAU;AACd,WAAO,CAAC,UAAU,SAAS,UAAU,GAAG;AACtC,YAAM,IAAI,QAAQ,CAAA,YAAW,WAAW,SAAS,GAAG,CAAC;AACrD;AAAA,IACF;AAGA,QAAI,qBAAqB,eAAe;AACtC,cAAQ,IAAI,gEAAgE;AAC5E;AAAA,IACF;AAEA,QAAI,CAAC,UAAU,OAAO;AACpB,cAAQ,MAAM,qCAAqC;AACnD;AAAA,IACF;AAEA,cAAU,QAAQ;AAClB,WAAO,QAAQ;AACf,uBAAmB,KAAK,IAAA;AAGxB,QAAI,kBAAkB;AACpB,mBAAa,gBAAgB;AAAA,IAC/B;AACA,uBAAmB,WAAW,MAAM,oBAAoB,gBAAgB,GAAG,kBAAkB;AAE7F,QAAI;AAEF,UAAI,OAAO,OAAO;AAChB,eAAO,MAAM,QAAA;AACb,eAAO,QAAQ;AAAA,MACjB;AAGA,YAAM,YAAY;AAClB,YAAM,WAAW;AACjB,YAAM,MAAM;AACZ,YAAM,aAAa;AACnB,YAAM,UAAU;AAChB,YAAM,aAAa;AACnB,YAAM,gBAAgB;AACtB,YAAM,eAAe;AACrB,YAAM,gBAAgB;AACtB,YAAM,gBAAgB;AACtB,YAAM,cAAc;AACpB,YAAM,WAAW;AACjB,qBAAe,QAAQ;AAGvB,YAAM,SAA6D;AAAA,QACjE,GAAG,aAAa,cAAc;AAAA,QAC9B,QAAQ,UAAU;AAAA,QAClB,QAAQ;AAAA,MAAA;AAIV,YAAM,gBAAgB,IAAI,aAAa,QAAQ,SAAS;AACxD,aAAO,QAAQ;AAGf,YAAM,cAAc,KAAK,KAAK,WAAW;AAGzC,UAAI,qBAAqB,eAAe;AACtC,gBAAQ,IAAI,kDAAkD;AAE9D,YAAI,OAAO,UAAU,eAAe;AAClC,wBAAc,QAAA;AACd,iBAAO,QAAQ;AAAA,QACjB;AACA;AAAA,MACF;AAGA,UAAI,OAAO,UAAU,eAAe;AAClC,gBAAQ,IAAI,uDAAuD;AACnE;AAAA,MACF;AAGA,UAAI,CAAC,OAAO,MAAO;AACnB,qBAAe,QAAQ,OAAO,MAAM,kBAAA;AAGpC,YAAM,WAAW;AAGjB,YAAM,OAAO,MAAM,KAAA;AAEnB,cAAQ,IAAI,sCAAsC,eAAe,KAAK;AAAA,IACxE,SAAS,OAAY;AACnB,cAAQ,MAAM,8BAA8B,OAAO,WAAW,KAAK;AAEnE,UAAI,kBAAkB;AACpB,qBAAa,gBAAgB;AAC7B,2BAAmB;AAAA,MACrB;AAEA,UAAI,qBAAqB,eAAe;AACtC,kBAAU,QAAQ;AAAA,MACpB;AAAA,IACF;AAAA,EAGF;AAGA,WAAS,WAAW,SAA8B;AAChD,QAAI,QAAQ,qBAAqB,QAAW;AAC1C,qBAAe,mBAAmB,QAAQ;AAAA,IAC5C;AACA,QAAI,QAAQ,oBAAoB,QAAW;AACzC,qBAAe,kBAAkB,QAAQ;AAAA,IAC3C;AACA,QAAI,QAAQ,iBAAiB,QAAW;AACtC,qBAAe,eAAe,QAAQ;AAAA,IACxC;AACA,YAAQ,IAAI,+BAA+B,cAAc;AAAA,EAC3D;AAGA,WAASC,cAAY,OAAqB;AACxCC,gBAAgB,KAAK;AAAA,EACvB;AAGA,WAAS,QAAc;AACrB,WAAO,OAAO,MAAA;AAAA,EAChB;AAGA,iBAAe,SAAwB;AACrC,QAAI,CAAC,OAAO,MAAO;AACnB,UAAM,OAAO,MAAM,KAAA;AAAA,EACrB;AAGA,iBAAe,KAAK,MAA6B;AAC/C,QAAI,CAAC,OAAO,MAAO;AACnB,UAAM,OAAO,MAAM,KAAK,IAAI;AAAA,EAC9B;AAGA,WAAS,gBAAgB,MAAoB;AAC3C,QAAI,CAAC,OAAO,MAAO;AACnB,WAAO,MAAM,gBAAgB,IAAI;AAAA,EACnC;AAGA,WAAS,YAAiC;AACxC,WAAO,OAAO;AAAA,EAChB;AAGA,WAAS,WAA0B;AACjC,WAAO,OAAO,OAAO,SAAA,KAAc,EAAE,GAAG,MAAA;AAAA,EAC1C;AAGA,WAAS,iBAAyB;AAChC,WAAO,OAAO,OAAO,eAAA,KAAoB;AAAA,EAC3C;AAGA,WAAS,cAAsB;AAC7B,WAAO,OAAO,OAAO,YAAA,KAAiB;AAAA,EACxC;AAGA,WAAS,UAAgB;AACvB,QAAI,OAAO,OAAO;AAChB,aAAO,MAAM,QAAA;AACb,aAAO,QAAQ;AAAA,IACjB;AAAA,EACF;AAGA,cAAY,MAAM;AAChB,YAAA;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IAAA,aACAD;AAAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;ACpXO,SAAS,YAAY,QAA8C;AACxE,MAAI,oBAAmC;AACvC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,IACE;AAEJ,QAAM,eAAe,IAAI,IAAI;AAC7B,QAAM,YAAY,IAAmB,IAAI;AACzC,QAAM,gBAAgB,IAAI,CAAC;AAG3B,QAAM,eAAe,SAAS,MAAM;AAClC,QAAI,SAAS,UAAU,EAAG,QAAO;AACjC,WAAQ,YAAY,QAAQ,SAAS,QAAS;AAAA,EAChD,CAAC;AAGD,QAAM,iBAAiB,SAAS,MAAM;AACpC,QAAI,cAAc,UAAU,EAAG,QAAO;AACtC,WAAQ,oBAAoB,QAAQ,cAAc,QAAS;AAAA,EAC7D,CAAC;AAGD,WAAS,kBAAwB;AAC/B,iBAAa,QAAQ;AACrB,QAAI,mBAAmB;AACrB,mBAAa,iBAAiB;AAAA,IAChC;AACA,wBAAoB,OAAO,WAAW,MAAM;AAC1C,UAAI,UAAU,OAAO;AACnB,qBAAa,QAAQ;AAAA,MACvB;AAAA,IACF,GAAG,GAAI;AAAA,EACT;AAGA,WAAS,kBAAwB;AAC/B,QAAI,UAAU,OAAO;AACnB,mBAAa,QAAQ;AAAA,IACvB;AAAA,EACF;AAGA,iBAAe,oBAAoB,OAAkC;AACnE,QAAI,SAAS,UAAU,EAAG;AAE1B,UAAM,SAAS,MAAM;AACrB,UAAM,OAAO,OAAO,sBAAA;AACpB,UAAM,SAAS,MAAM,UAAU,KAAK;AACpC,UAAM,aAAa,SAAS,KAAK;AACjC,UAAM,aAAa,aAAa,SAAS;AAEzC,UAAM,KAAK,UAAU;AAAA,EACvB;AAGA,WAAS,oBAAoB,OAAyB;AACpD,QAAI,SAAS,UAAU,EAAG;AAE1B,UAAM,SAAS,MAAM;AACrB,UAAM,OAAO,OAAO,sBAAA;AACpB,UAAM,SAAS,MAAM,UAAU,KAAK;AACpC,UAAM,aAAa,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,SAAS,KAAK,KAAK,CAAC;AAE/D,cAAU,QAAQ,aAAa,SAAS;AACxC,kBAAc,QAAQ,aAAa;AAAA,EACrC;AAGA,WAAS,WAAW,IAAoB;AACtC,UAAM,eAAe,KAAK,MAAM,KAAK,GAAI;AACzC,UAAM,QAAQ,KAAK,MAAM,eAAe,IAAI;AAC5C,UAAM,UAAU,KAAK,MAAO,eAAe,OAAQ,EAAE;AACrD,UAAM,UAAU,eAAe;AAE/B,QAAI,QAAQ,GAAG;AACb,aAAO,GAAG,KAAK,IAAI,QAAQ,SAAA,EAAW,SAAS,GAAG,GAAG,CAAC,IAAI,QAAQ,SAAA,EAAW,SAAS,GAAG,GAAG,CAAC;AAAA,IAC/F;AACA,WAAO,GAAG,OAAO,IAAI,QAAQ,WAAW,SAAS,GAAG,GAAG,CAAC;AAAA,EAC1D;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;ACtIO,SAAS,WACd,mBAA4B,OACV;AAClB,QAAM,UAAU,IAAoB,IAAI;AACxC,QAAM,eAAe,IAAI,gBAAgB;AAGzC,QAAM,cAAc,SAAS,MAAM;AACjC,QAAI,CAAC,QAAQ,MAAO,QAAO;AAC3B,UAAM,UAAkC;AAAA,MACtC,cAAc;AAAA,MACd,kBAAkB;AAAA,MAClB,sBAAsB;AAAA,MACtB,sBAAsB;AAAA,MACtB,qBAAqB;AAAA,MACrB,WAAW;AAAA,IAAA;AAEb,WAAO,QAAQ,QAAQ,MAAM,QAAQ,KAAK,QAAQ,MAAM;AAAA,EAC1D,CAAC;AAGD,WAAS,iBAAuB;AAC9B,iBAAa,QAAQ,CAAC,aAAa;AAAA,EACrC;AAGA,WAAS,YAAkB;AACzB,YAAQ,QAAQ,YAAY,OAAA;AAC5B,YAAQ,IAAI,sBAAsB,QAAQ,KAAK;AAAA,EACjD;AAGA,YAAU,MAAM;AACd,cAAA;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACmLA,UAAM,QAAQ;AAGd,UAAM,OAAO;AAQb,UAAM,YAAY,IAA8B,IAAI;AAGpD,UAAM,eAAe,IAAwB,IAAI;AAGjD,UAAM,eAAe,IAAI,KAAK;AAG9B,UAAM,eAAe,IAAmB,IAAI;AAG5C,aAAS,eAAe,OAAmD;AACzE,YAAM,aAAqC;AAAA,QACzC,cAAc;AAAA,QACd,YAAY,UAAU,MAAM,UAAU,EAAE;AAAA,QACxC,WAAW,MAAM,UAAU;AAAA,QAC3B,gBAAgB;AAAA,MAAA;AAElB,aAAO,WAAW,MAAM,KAAK,KAAK;AAAA,IACpC;AAGA,aAAS,mBAAmB,OAAiE;AAC3F,UAAI,CAAC,SAAS,MAAM,UAAU,aAAa,CAAC,MAAM,OAAQ,QAAO;AAEjE,YAAM,QAAQ,MAAM,OAAO,MAAM,SAAS;AAC1C,aAAO,QAAQ,SAAS,MAAM,CAAC,GAAG,EAAE,IAAI;AAAA,IAC1C;AAGA,aAAS,aAAmB;AAC1B,mBAAa,QAAQ;AAAA,IACvB;AAGA,aAAS,cAAoB;AAC3B,iBAAA;AACA,WAAK,OAAO;AAAA,IACd;AAGA,UAAM,cAAc;AAAA,MAClB,MAAM,MAAM,KAAK,MAAM;AAAA,MACvB,OAAO,MAAM,KAAK,OAAO;AAAA,MACzB,OAAO,CAAC,OAAc,gBAAwB;AAE5C,qBAAa,QAAQ,MAAM,WAAW;AACtC,aAAK,SAAS,OAAO,WAAW;AAAA,MAClC;AAAA,MACA,OAAO,MAAM,KAAK,OAAO;AAAA,IAAA;AAI3B,UAAM,gBAAgB,CAAC,GAAG,GAAG,CAAC;AAC9B,UAAM,sBAAsB,IAAI,CAAC;AACjC,UAAM,uBAAuB,IAAI,KAAK;AAGtC,UAAM,gBAAgB,SAA0C,OAAO;AAAA,MACrE,MAAM,MAAM,eAAe,QAAQ,wBAAwB,cAAc;AAAA,MACzE,YAAY,MAAM,eAAe,cAAc,wBAAwB,cAAc;AAAA,MACrF,MAAM,MAAM,eAAe,QAAQ,wBAAwB,cAAc;AAAA,MACzE,cAAc,MAAM,eAAe,gBAAgB,wBAAwB,cAAc;AAAA,IAAA,EACzF;AAGF,UAAM,EAAE,SAAS,cAAc,aAAa,eAAA,IAAmB,WAAW,KAAK;AAG/E,UAAM,SAAS,UAAU,WAAW;AAGpC,UAAM,WAAW,CAAC,OAAO;AACvB,aAAO,UAAU,QAAQ;AAAA,IAC3B,GAAG,EAAE,WAAW,MAAM;AAGtB,UAAM,SAAS,SAAS,MAAM,OAAO,OAAO,KAAK;AAGjD,UAAM,eAAe,SAAS,MAAM,OAAO,MAAM,SAAS;AAC1D,UAAM,cAAc,SAAS,MAAM,OAAO,MAAM,QAAQ;AACxD,UAAM,iBAAiB,SAAS,MAAM,OAAO,MAAM,WAAW;AAC9D,UAAM,cAAc,SAAS,MAAM,OAAO,MAAM,QAAQ;AACxD,UAAM,kBAAkB,SAAS,MAAM,OAAO,MAAM,YAAY;AAChE,UAAM,yBAAyB,SAAS,MAAM,OAAO,MAAM,mBAAmB;AAC9E,UAAM,mBAAmB,SAAS,MAAM,OAAO,MAAM,aAAa;AAElE,UAAM,WAAW,YAAY;AAAA,MAC3B,WAAW;AAAA,MACX,UAAU;AAAA,MACV,aAAa;AAAA,MACb,UAAU;AAAA,MACV,cAAc;AAAA,MACd,qBAAqB;AAAA,MACrB,eAAe;AAAA,MACf;AAAA,MACA,MAAM,OAAO;AAAA,IAAA,CACd;AAGD,UAAM,kBAAkB,IAAwB,IAAI;AAGpD,aAAS,SAAS,KAA0E;AAC1F,UAAI,CAAC,OAAO,CAAC,IAAI,IAAK,QAAO;AAC7B,aAAO,EAAE,KAAK,IAAI,KAAK,QAAQ,IAAI,UAAU,MAAA;AAAA,IAC/C;AAGA,aAAS,iBAAuB;AAC9B,UAAI,OAAO,MAAM,WAAW;AAC1B,eAAO,MAAA;AAAA,MACT,OAAO;AACL,eAAO,OAAA;AAAA,MACT;AAAA,IACF;AAGA,aAAS,mBAAyB;AAChC,UAAI,CAAC,aAAa,MAAO;AAEzB,UAAI,CAAC,aAAa,OAAO;AACvB,YAAI,aAAa,MAAM,mBAAmB;AACxC,uBAAa,MAAM,kBAAA;AAAA,QACrB;AAAA,MACF,OAAO;AACL,YAAI,SAAS,gBAAgB;AAC3B,mBAAS,eAAA;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAGA,aAAS,gBAAgB,MAAoB;AAC3C,0BAAoB,QAAQ;AAC5B,2BAAqB,QAAQ;AAC7B,aAAO,gBAAgB,IAAI;AAAA,IAC7B;AAGA,aAAS,yBAA+B;AACtC,2BAAqB,QAAQ,CAAC,qBAAqB;AAAA,IACrD;AAGA,aAAS,yBAA+B;AACtC,mBAAa,QAAQ,CAAC,CAAC,SAAS;AAAA,IAClC;AAGA,aAAS,OAAa;AACpB,YAAM,SAAS,SAAS,MAAM,GAAG;AACjC,UAAI,QAAQ;AACV,eAAO,KAAK,MAAM;AAAA,MACpB;AAAA,IACF;AAGA,UAAM,MAAM,MAAM,KAAK,CAAC,WAAW;AACjC,YAAM,SAAS,SAAS,MAAM;AAC9B,UAAI,UAAU,OAAO,KAAK;AAExB,qBAAa,QAAQ;AACrB,eAAO,KAAK,MAAM;AAAA,MACpB;AAAA,IACF,GAAG,EAAE,WAAW,MAAM;AAGtB,cAAU,MAAM;AACd,eAAS,iBAAiB,oBAAoB,sBAAsB;AAAA,IACtE,CAAC;AAGD,gBAAY,MAAM;AAChB,eAAS,oBAAoB,oBAAoB,sBAAsB;AAAA,IACzE,CAAC;AAGD,aAA6B;AAAA,MAC3B;AAAA,MACA,OAAO,OAAO;AAAA,MACd,MAAM,OAAO;AAAA,MACb,SAAS,OAAO;AAAA,MAChB,UAAU,OAAO;AAAA,IAAA,CAClB;;0BA5bCE,mBA2OM,OAAA;AAAA,QA3OD,OAAM;AAAA,iBAA0B;AAAA,QAAJ,KAAI;AAAA,MAAA;QACnCC,mBAyOM,OAAA;AAAA,UAxOJ,OAAM;AAAA,mBACF;AAAA,UAAJ,KAAI;AAAA,UACH,cAAU,OAAA,CAAA,MAAA,OAAA,CAAA;AAAA,UAAE,IAAA,SAAAC,MAAA,QAAA,EAAS,mBAATA,MAAA,QAAA,EAAS,gBAAe,GAAA,IAAA;AAAA,UACpC,aAAS,OAAA,CAAA,MAAA,OAAA,CAAA;AAAA,UAAE,IAAA,SAAAA,MAAA,QAAA,EAAS,mBAATA,MAAA,QAAA,EAAS,gBAAe,GAAA,IAAA;AAAA,UACnC,cAAU,OAAA,CAAA,MAAA,OAAA,CAAA;AAAA,UAAE,IAAA,SAAAA,MAAA,QAAA,EAAS,mBAATA,MAAA,QAAA,EAAS,gBAAe,GAAA,IAAA;AAAA,QAAA;UAGrCD,mBAA0D,UAAA;AAAA,qBAA9C;AAAA,YAAJ,KAAI;AAAA,YAAY,OAAM;AAAA,UAAA;WAGlBC,MAAA,MAAA,EAAO,MAAM,YAAzBC,aAAAH,mBAEM,OAFN,YAEM;AAAA,YADJI,WAAgC,KAAA,QAAA,eAAA,CAAA,GAAA,QAAA,IAAA;AAAA,UAAA;UAIvBF,MAAA,YAAA,KAAgBA,MAAA,OAAA,KAA3BC,aAAAH,mBA0DM,OA1DN,YA0DM;AAAA,YAzDJC,mBAGM,OAHN,YAGM;AAAA,cAFJ,OAAA,CAAA,MAAA,OAAA,CAAA,IAAAA,mBAAqB,cAAf,YAAQ,EAAA;AAAA,cACdA,mBAA2E,QAAA;AAAA,gBAApE,OAAKI,eAAEH,MAAA,OAAA,EAAQ,gBAAa,SAAA,EAAA;AAAA,cAAA,mBAAmBA,MAAA,WAAA,CAAW,GAAA,CAAA;AAAA,YAAA;wCAEnED,mBAAkC,OAAA,EAA7B,OAAM,iBAAA,GAAgB,MAAA,EAAA;AAAA,YAC3BA,mBAKM,OALN,YAKM;AAAA,cAJJ,OAAA,CAAA,MAAA,OAAA,CAAA,IAAAA,mBAAiB,cAAX,QAAI,EAAA;AAAA,cACVA,mBAEO,QAAA;AAAA,gBAFA,OAAKI,eAAEH,MAAA,OAAA,EAAQ,aAAa,WAAQ,OAAA,KAAA;AAAA,cAAA,mBACtCA,MAAA,OAAA,EAAQ,aAAa,WAAQ,QAAA,IAAA,GAAA,CAAA;AAAA,YAAA;YAGpCD,mBAKM,OALN,YAKM;AAAA,cAJJ,OAAA,CAAA,MAAA,OAAA,CAAA,IAAAA,mBAAkB,cAAZ,SAAK,EAAA;AAAA,cACXA,mBAEO,QAAA;AAAA,gBAFA,OAAKI,eAAEH,MAAA,OAAA,EAAQ,aAAa,QAAK,OAAA,KAAA;AAAA,cAAA,mBACnCA,MAAA,OAAA,EAAQ,aAAa,QAAK,QAAA,IAAA,GAAA,CAAA;AAAA,YAAA;YAGjCD,mBAKM,OALN,YAKM;AAAA,cAJJ,OAAA,EAAA,MAAA,OAAA,EAAA,IAAAA,mBAAmB,cAAb,UAAM,EAAA;AAAA,cACZA,mBAEO,QAAA;AAAA,gBAFA,OAAKI,eAAEH,MAAA,OAAA,EAAQ,aAAa,SAAM,OAAA,KAAA;AAAA,cAAA,mBACpCA,MAAA,OAAA,EAAQ,aAAa,SAAM,QAAA,IAAA,GAAA,CAAA;AAAA,YAAA;YAGlCD,mBAKM,OALN,YAKM;AAAA,cAJJ,OAAA,EAAA,MAAA,OAAA,EAAA,IAAAA,mBAAgB,cAAV,OAAG,EAAA;AAAA,cACTA,mBAEO,QAAA;AAAA,gBAFA,OAAKI,eAAEH,MAAA,OAAA,EAAQ,aAAa,oBAAiB,OAAA,KAAA;AAAA,cAAA,mBAC/CA,MAAA,OAAA,EAAQ,aAAa,oBAAiB,QAAA,IAAA,GAAA,CAAA;AAAA,YAAA;YAG7CD,mBAKM,OALN,YAKM;AAAA,cAJJ,OAAA,EAAA,MAAA,OAAA,EAAA,IAAAA,mBAAoB,cAAd,WAAO,EAAA;AAAA,cACbA,mBAEO,QAAA;AAAA,gBAFA,OAAKI,eAAEH,MAAA,OAAA,EAAQ,aAAa,cAAW,OAAA,KAAA;AAAA,cAAA,mBACzCA,MAAA,OAAA,EAAQ,aAAa,cAAW,QAAA,IAAA,GAAA,CAAA;AAAA,YAAA;YAGvCD,mBAKM,OALN,YAKM;AAAA,cAJJ,OAAA,EAAA,MAAA,OAAA,EAAA,IAAAA,mBAAqB,cAAf,YAAQ,EAAA;AAAA,cACdA,mBAEO,QAAA;AAAA,gBAFA,OAAKI,eAAEH,MAAA,OAAA,EAAQ,aAAa,WAAQ,OAAA,KAAA;AAAA,cAAA,mBACtCA,MAAA,OAAA,EAAQ,aAAa,WAAQ,QAAA,IAAA,GAAA,CAAA;AAAA,YAAA;YAGpCD,mBAKM,OALN,aAKM;AAAA,cAJJ,OAAA,EAAA,MAAA,OAAA,EAAA,IAAAA,mBAAmB,cAAb,UAAM,EAAA;AAAA,cACZA,mBAEO,QAAA;AAAA,gBAFA,OAAKI,eAAEH,MAAA,OAAA,EAAQ,aAAa,iBAAc,OAAA,KAAA;AAAA,cAAA,mBAC5CA,MAAA,OAAA,EAAQ,aAAa,iBAAc,QAAA,IAAA,GAAA,CAAA;AAAA,YAAA;YAG1BA,MAAA,MAAA,EAAO,MAAM,yBAA7BF,mBASWM,UAAA,EAAA,KAAA,EAAA,GAAA;AAAA,0CARTL,mBAAkC,OAAA,EAA7B,OAAM,iBAAA,GAAgB,MAAA,EAAA;AAAA,cACGC,MAAA,MAAA,EAAO,eAAe,SAApDC,aAAAH,mBAAkI,OAAlI,aAAkI;AAAA,gBAAvE,OAAA,EAAA,MAAA,OAAA,EAAA,IAAAC,mBAAmB,cAAb,UAAM,EAAA;AAAA,gBAAOA,mBAA8C,QAAA,MAAAM,gBAArCL,MAAA,MAAA,EAAO,eAAe,KAAK,GAAA,CAAA;AAAA,cAAA;cAClHD,mBAAiF,OAAjF,aAAiF;AAAA,gBAAzD,OAAA,EAAA,MAAA,OAAA,EAAA,IAAAA,mBAAgB,cAAV,OAAG,EAAA;AAAA,gBAAOA,mBAAmC,QAAA,MAAAM,gBAA1BL,MAAA,MAAA,EAAO,MAAM,GAAG,GAAA,CAAA;AAAA,cAAA;cACjED,mBAA+F,OAA/F,aAA+F;AAAA,gBAAvE,OAAA,EAAA,MAAA,OAAA,EAAA,IAAAA,mBAAuB,cAAjB,cAAU,EAAA;AAAA,gBAAOA,mBAA0C,QAAA,MAAAM,gBAAjCL,MAAA,MAAA,EAAO,MAAM,UAAU,GAAA,CAAA;AAAA,cAAA;cAC/ED,mBAA+F,OAA/F,aAA+F;AAAA,gBAAvE,OAAA,EAAA,MAAA,OAAA,EAAA,IAAAA,mBAAuB,cAAjB,cAAU,EAAA;AAAA,gBAAOA,mBAA0C,QAAA,MAAAM,gBAAjCL,MAAA,MAAA,EAAO,MAAM,UAAU,GAAA,CAAA;AAAA,cAAA;cAC/ED,mBAAyF,OAAzF,aAAyF;AAAA,gBAAjE,OAAA,EAAA,MAAA,OAAA,EAAA,IAAAA,mBAAoB,cAAd,WAAO,EAAA;AAAA,gBAAOA,mBAAuC,QAAA,MAAAM,gBAA9BL,MAAA,MAAA,EAAO,MAAM,OAAO,GAAA,CAAA;AAAA,cAAA;cACzED,mBAA+F,OAA/F,aAA+F;AAAA,gBAAvE,OAAA,EAAA,MAAA,OAAA,EAAA,IAAAA,mBAAoB,cAAd,WAAO,EAAA;AAAA,gBAAOA,mBAA6C,QAAA,MAAAM,gBAApCL,MAAA,MAAA,EAAO,MAAM,aAAa,GAAA,CAAA;AAAA,cAAA;cAC/ED,mBAAuG,OAAvG,aAAuG;AAAA,gBAA/E,OAAA,EAAA,MAAA,OAAA,EAAA,IAAAA,mBAAkB,cAAZ,SAAK,EAAA;AAAA,gBAAOA,mBAAuD,8BAA9CC,MAAA,MAAA,EAAO,MAAM,sBAAqB,SAAK,CAAA;AAAA,cAAA;;;UAKxDA,MAAA,MAAA,EAAO,UAAU,SAAvDC,aAAAH,mBAgBM,OAhBN,aAgBM;AAAA,YAfJI,WAcO,KAAA,QAAA,WAAA;AAAA,cAde,OAAOF,MAAA,MAAA,EAAO,aAAa;AAAA,YAAA,GAAjD,MAcO;AAAA,cAbLD,mBAYM,OAZN,aAYM;AAAA,4CAXJA,mBAA8B,OAAA,EAAzB,OAAM,aAAA,GAAY,MAAA,EAAA;AAAA,gBACYC,MAAA,MAAA,EAAO,aAAa,sBAAvDF,mBAEM,OAFN,aAEMO,gBADD,eAAeL,MAAA,MAAA,EAAO,aAAa,KAAK,CAAA,GAAA,CAAA;gBAGN,mBAAmBA,MAAA,MAAA,EAAO,aAAa,KAAK,KAAnFC,aAAAH,mBAKM,OALN,aAKM;AAAA,kBAJJC,mBAEM,OAFN,aAEM;AAAA,oBADJA,mBAAgH,OAAA;AAAA,sBAA3G,OAAM;AAAA,sBAAwB,+BAAgB,mBAAmBC,cAAO,aAAa,KAAK,IAAA,IAAA,CAAA;AAAA,oBAAA;;kBAEjGD,mBAAwF,OAAxF,aAAwFM,gBAAvD,mBAAmBL,MAAA,MAAA,EAAO,aAAa,KAAK,CAAA,IAAI,KAAC,CAAA;AAAA,gBAAA;;;;UAOhDA,MAAA,MAAA,EAAO,MAAM,gBAAgBA,MAAA,MAAA,EAAO,UAAU,SAAxFC,UAAA,GAAAH,mBAQM,OARN,aAQM;AAAA,YAPJC,mBAMM,OANN,aAMM;AAAA,0CALJA,mBAAwC,OAAA,EAAnC,OAAM,uBAAA,GAAsB,MAAA,EAAA;AAAA,cACrBC,MAAA,MAAA,EAAO,MAAM,sBAAsB,uBAA/CF,mBAEO,QAAA,aAFmD,cAChDO,gBAAGL,MAAA,MAAA,EAAO,MAAM,iBAAiB,IAAG,MAC9C,CAAA,MACAC,UAAA,GAAAH,mBAA2B,qBAAd,SAAO;AAAA,YAAA;;UAKY,aAAA,SAApCG,UAAA,GAAAH,mBAqBM,OArBN,aAqBM;AAAA,YApBJI,WAmBO,KAAA,QAAA,SAAA;AAAA,cAnBa,SAAS,aAAA;AAAA,cAAe,OAAO;AAAA,YAAA,GAAnD,MAmBO;AAAA,cAlBLH,mBAiBM,OAjBN,aAiBM;AAAA;gBATJA,mBAGM,OAHN,aAGM;AAAA,kBAFJ,OAAA,EAAA,MAAA,OAAA,EAAA,IAAAA,mBAAsC,OAAA,EAAjC,OAAM,iBAAA,GAAiB,QAAI,EAAA;AAAA,kBAChCA,mBAAmD,OAAnD,aAAmDM,gBAArB,aAAA,KAAY,GAAA,CAAA;AAAA,gBAAA;gBAE5CN,mBAIS,UAAA;AAAA,kBAJD,OAAM;AAAA,kBAAkB,SAAO;AAAA,kBAAa,OAAM;AAAA,gBAAA;kBACxDA,mBAEM,OAAA;AAAA,oBAFD,SAAQ;AAAA,oBAAY,MAAK;AAAA,kBAAA;oBAC5BA,mBAAkN,QAAA,EAA5M,GAAE,0MAAwM;AAAA,kBAAA;;;;;UASlN,cAAA,MAAc,qBADtBD,mBA0FM,OAAA;AAAA;YAxFJ,OAAKK,eAAA,CAAC,qBAAmB,EAAA,SACNH,MAAA,QAAA,EAAS,aAAa,SAAK,CAAKA,MAAA,MAAA,EAAO,MAAM,WAAS,CAAA;AAAA,UAAA;YAEzED,mBAQS,UAAA;AAAA,cARD,OAAM;AAAA,cAAkB,SAAO;AAAA,YAAA;cAC1BC,MAAA,MAAA,EAAO,MAAM,aAAxBC,aAAAH,mBAGM,OAHN,aAGM,CAAA,GAAA,OAAA,EAAA,MAAA,OAAA,EAAA,IAAA;AAAA,gBAFJC,mBAAgD,QAAA;AAAA,kBAA1C,GAAE;AAAA,kBAAI,GAAE;AAAA,kBAAI,OAAM;AAAA,kBAAI,QAAO;AAAA,kBAAK,IAAG;AAAA,gBAAA;gBAC3CA,mBAAiD,QAAA;AAAA,kBAA3C,GAAE;AAAA,kBAAK,GAAE;AAAA,kBAAI,OAAM;AAAA,kBAAI,QAAO;AAAA,kBAAK,IAAG;AAAA,gBAAA;uBAE9CE,aAAAH,mBAEM,OAFN,aAEM,CAAA,GAAA,OAAA,EAAA,MAAA,OAAA,EAAA,IAAA;AAAA,gBADJC,mBAAyB,QAAA,EAAnB,GAAE,gBAAA,GAAe,MAAA,EAAA;AAAA,cAAA;;YAKW,OAAA,SAAtCE,aAAAH,mBAGO,QAHP,aAGO,CAAA,GAAA,OAAA,EAAA,MAAA,OAAA,EAAA,IAAA;AAAA,cAFLC,mBAAiC,QAAA,EAA3B,OAAM,cAAA,GAAa,MAAA,EAAA;AAAA,cACzBA,mBAAoC,QAAA,EAA9B,OAAM,eAAA,GAAe,MAAE,EAAA;AAAA,YAAA,SAI/BE,aAAAH,mBAIO,QAJP,aAIO;AAAA,cAHLC,mBAAwF,QAAxF,aAAwFM,gBAAvDL,MAAA,QAAA,EAAS,WAAWA,MAAA,MAAA,EAAO,MAAM,WAAW,CAAA,GAAA,CAAA;AAAA,cAC7E,OAAA,EAAA,MAAA,OAAA,EAAA,IAAAD,mBAAwC,QAAA,EAAlC,OAAM,oBAAA,GAAoB,KAAC,EAAA;AAAA,cACjCA,mBAAsF,QAAtF,aAAsFM,gBAApDL,MAAA,QAAA,EAAS,WAAWA,MAAA,MAAA,EAAO,MAAM,QAAQ,CAAA,GAAA,CAAA;AAAA,YAAA;aAKpE,OAAA,sBADTF,mBAoBM,OAAA;AAAA;cAlBJ,OAAM;AAAA,cACL,SAAK,OAAA,CAAA,MAAA,OAAA,CAAA;AAAA,cAAE,IAAA,SAAAE,MAAA,QAAA,EAAS,uBAATA,MAAA,QAAA,EAAS,oBAAmB,GAAA,IAAA;AAAA,cACnC,aAAS,OAAA,CAAA,MAAA,OAAA,CAAA;AAAA,cAAE,IAAA,SAAAA,MAAA,QAAA,EAAS,uBAATA,MAAA,QAAA,EAAS,oBAAmB,GAAA,IAAA;AAAA,cACvC,cAAU,OAAA,CAAA,MAAA,OAAA,CAAA,IAAA,CAAA,WAAEA,MAAA,QAAA,EAAS,UAAU,QAAK;AAAA,YAAA;cAErCD,mBAKM,OALN,aAKM;AAAA,gBAJJA,mBAAgG,OAAA;AAAA,kBAA3F,OAAM;AAAA,kBAAwB,OAAKO,eAAA,EAAA,OAAWN,MAAA,QAAA,EAAS,eAAe,QAAK,IAAA,CAAA;AAAA,gBAAA;gBAChFD,mBAEM,OAAA;AAAA,kBAFD,OAAM;AAAA,kBAAsB,OAAKO,eAAA,EAAA,OAAWN,MAAA,QAAA,EAAS,aAAa,QAAK,IAAA,CAAA;AAAA,gBAAA;kBAC1ED,mBAAsC,OAAA,EAAjC,OAAM,qBAAA,GAAoB,MAAA,EAAA;AAAA,gBAAA;;cAK3BC,MAAA,QAAA,EAAS,UAAU,UAAK,qBAFhCF,mBAMM,OAAA;AAAA;gBALJ,OAAM;AAAA,gBAEL,OAAKQ,eAAA,EAAA,MAAUN,MAAA,QAAA,EAAS,cAAc,QAAK,IAAA,CAAA;AAAA,cAAA,GAEzCK,gBAAAL,MAAA,QAAA,EAAS,WAAWA,gBAAS,UAAU,KAAK,CAAA,GAAA,CAAA;uBAKnDC,aAAAH,mBAAyC,OAAzC,WAAyC;AAAA,YAG9B,cAAA,MAAc,gBAAY,CAAK,OAAA,SAA1CG,aAAAH,mBAeM,OAfN,aAeM;AAAA,cAdJC,mBAES,UAAA;AAAA,gBAFD,OAAM;AAAA,gBAAuC,SAAO;AAAA,gBAAyB,gBAAgB,oBAAA,QAAmB;AAAA,cAAA;gBACtHA,mBAAqE,QAArE,aAAqEM,gBAA9B,oBAAA,KAAmB,IAAG,KAAC,CAAA;AAAA,cAAA;cAErD,qBAAA,SAAXJ,UAAA,GAAAH,mBAUM,OAVN,aAUM;AAAA,8BATJA,mBAQSM,UAAA,MAAAG,WAPQ,eAAa,CAArB,SAAI;yBADbR,mBAQS,UAAA;AAAA,oBANN,KAAK;AAAA,oBACN,OAAKI,eAAA,CAAC,2BAAyB,EAAA,QACb,oBAAA,UAAwB,KAAA,CAAI,CAAA;AAAA,oBAC7C,SAAK,CAAA,WAAE,gBAAgB,IAAI;AAAA,kBAAA,GAEzBE,gBAAA,IAAI,IAAG,MACZ,IAAA,WAAA;AAAA;;;YAKU,cAAA,MAAc,qBAA5BP,mBAIS,UAAA;AAAA;cAJyB,OAAM;AAAA,cAAkC,SAAK,OAAA,CAAA,MAAA,OAAA,CAAA;AAAA,2BAAEE,MAAA,cAAA,KAAAA,MAAA,cAAA,EAAA,GAAA,IAAA;AAAA,cAAiB,OAAOA,MAAA,YAAA,IAAY,SAAA;AAAA,YAAA;cACnHD,mBAEM,OAAA;AAAA,gBAFD,SAAQ;AAAA,gBAAY,MAAK;AAAA,cAAA;gBAC5BA,mBAA4G,QAAA,EAAtG,GAAE,oGAAkG;AAAA,cAAA;;YAKhG,cAAA,MAAc,2BAA5BD,mBASS,UAAA;AAAA;cAT+B,OAAM;AAAA,cAAoC,SAAO;AAAA,cAAmB,OAAO,aAAA,QAAY,SAAA;AAAA,YAAA;cAElH,aAAA,SAAXG,aAAAH,mBAEM,OAFN,aAEM,CAAA,GAAA,OAAA,EAAA,MAAA,OAAA,EAAA,IAAA;AAAA,gBADJC,mBAAyF,QAAA,EAAnF,GAAE,gFAAA,GAA+E,MAAA,EAAA;AAAA,cAAA,SAGzFE,aAAAH,mBAEM,OAFN,aAEM,CAAA,GAAA,OAAA,EAAA,MAAA,OAAA,EAAA,IAAA;AAAA,gBADJC,mBAA0F,QAAA,EAApF,GAAE,iFAAA,GAAgF,MAAA,EAAA;AAAA,cAAA;;;UAQtFC,MAAA,MAAA,EAAO,MAAM,aAAaA,MAAA,MAAA,EAAO,MAAM,aAAS,CAAKA,MAAA,MAAA,EAAO,UAAU,sBAF9EF,mBAQM,OAAA;AAAA;YAPJ,OAAM;AAAA,YAEL,SAAO;AAAA,UAAA;YAERC,mBAEM,OAAA;AAAA,cAFD,SAAQ;AAAA,cAAY,MAAK;AAAA,YAAA;cAC5BA,mBAAyB,QAAA,EAAnB,GAAE,iBAAe;AAAA,YAAA;;;;;;;;;;;;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@give-tech/ec-player-vue",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.55",
|
|
4
4
|
"description": "EcPlayer Vue 3 Component - HLS/FLV Streaming Video Player",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"typecheck": "tsc --noEmit"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@give-tech/ec-player": "^0.0.1-beta.
|
|
25
|
+
"@give-tech/ec-player": "^0.0.1-beta.55"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"vue": "^3.3.0"
|