lzc-video-player 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/.dockerignore +1 -0
  2. package/.eslintrc.cjs +18 -0
  3. package/.prettierrc.json +5 -0
  4. package/AGENTS.md +31 -0
  5. package/README.md +38 -0
  6. package/build.sh +10 -0
  7. package/demo/.vscode/extensions.json +3 -0
  8. package/demo/README.md +40 -0
  9. package/demo/env.d.ts +1 -0
  10. package/demo/index.html +13 -0
  11. package/demo/package-lock.json +2037 -0
  12. package/demo/package.json +25 -0
  13. package/demo/public/favicon.ico +0 -0
  14. package/demo/src/App.vue +25 -0
  15. package/demo/src/assets/base.css +70 -0
  16. package/demo/src/assets/logo.svg +1 -0
  17. package/demo/src/assets/main.css +33 -0
  18. package/demo/src/main.ts +8 -0
  19. package/demo/tsconfig.config.json +8 -0
  20. package/demo/tsconfig.json +16 -0
  21. package/demo/vite.config.ts +14 -0
  22. package/docs/progress-bar-style-analysis.md +87 -0
  23. package/env.d.ts +1 -0
  24. package/error_pages/502.html.tpl +13 -0
  25. package/i18next-parser.config.mjs +147 -0
  26. package/index.html +54 -0
  27. package/lazycat.png +0 -0
  28. package/lib/README.md +48 -0
  29. package/lib/package.json +22 -0
  30. package/lzc-build.local.yml +65 -0
  31. package/lzc-build.yml +65 -0
  32. package/lzc-manifest.yml +53 -0
  33. package/makefile +15 -0
  34. package/package.json +69 -0
  35. package/postcss.config.js +6 -0
  36. package/public/512x512.png +0 -0
  37. package/public/favicon.ico +0 -0
  38. package/public/languages/en/translation.json +125 -0
  39. package/public/languages/zh/translation.json +125 -0
  40. package/public/libass-wasm/4.1.0/default.woff2 +0 -0
  41. package/public/libass-wasm/4.1.0/subtitles-octopus-worker-legacy.js +40 -0
  42. package/public/libass-wasm/4.1.0/subtitles-octopus-worker.js +1 -0
  43. package/public/libass-wasm/4.1.0/subtitles-octopus-worker.wasm +0 -0
  44. package/public/libass-wasm/4.1.0/subtitles-octopus.js +1680 -0
  45. package/public/square-128x128.png +0 -0
  46. package/public/square-256x256.png +0 -0
  47. package/public/square-512x512.png +0 -0
  48. package/src/App.vue +18 -0
  49. package/src/assets/base.scss +104 -0
  50. package/src/assets/cloud.png +0 -0
  51. package/src/assets/logo.svg +1 -0
  52. package/src/components/Dialog/index.vue +96 -0
  53. package/src/components/MultipleEdit/choose.vue +39 -0
  54. package/src/components/PlayList/index.vue +521 -0
  55. package/src/components/Spectrum/index.vue +58 -0
  56. package/src/components/Video/NativeVideoPlayer.vue +748 -0
  57. package/src/components/Video/README.md +3 -0
  58. package/src/components/Video/clientPlayer.ts +348 -0
  59. package/src/components/Video/components/LzcModal/components/simpleList.vue +57 -0
  60. package/src/components/Video/components/LzcModal/list.vue +52 -0
  61. package/src/components/Video/components/LzcModal/playrate.vue +45 -0
  62. package/src/components/Video/components/LzcModal/resolution.vue +117 -0
  63. package/src/components/Video/components/LzcModal/subtitle.vue +499 -0
  64. package/src/components/Video/components/LzcModal/useModal.ts +18 -0
  65. package/src/components/Video/components/LzcOverlay/SubtitleLayer.vue +321 -0
  66. package/src/components/Video/components/LzcOverlay/cast.vue +253 -0
  67. package/src/components/Video/components/LzcOverlay/casting.vue +205 -0
  68. package/src/components/Video/components/LzcOverlay/error.vue +103 -0
  69. package/src/components/Video/components/LzcOverlay/helper.ts +81 -0
  70. package/src/components/Video/components/LzcOverlay/index.vue +99 -0
  71. package/src/components/Video/components/LzcOverlay/playing.vue +496 -0
  72. package/src/components/Video/components/LzcOverlay/playingButtons.vue +122 -0
  73. package/src/components/Video/components/LzcOverlay/playingLayout.vue +287 -0
  74. package/src/components/Video/components/LzcOverlay/useCast.ts +235 -0
  75. package/src/components/Video/components/LzcOverlay/useCommon.ts +41 -0
  76. package/src/components/Video/components/LzcOverlay/useOctopusRenderer.ts +230 -0
  77. package/src/components/Video/components/LzcOverlay/useSubtitleRenderEngine.ts +79 -0
  78. package/src/components/Video/components/LzcOverlay/useSubtitleTrack.ts +139 -0
  79. package/src/components/Video/components/useLzcCommon.ts +16 -0
  80. package/src/components/Video/directPlay.ts +345 -0
  81. package/src/components/Video/getSubtitleInfo.ts +42 -0
  82. package/src/components/Video/native/EventEmitter.ts +62 -0
  83. package/src/components/Video/native/NativeControls.vue +510 -0
  84. package/src/components/Video/native/NativeModal.vue +133 -0
  85. package/src/components/Video/native/NativePlayer.ts +913 -0
  86. package/src/components/Video/native/NativePlayer.vue +53 -0
  87. package/src/components/Video/native/index.ts +9 -0
  88. package/src/components/Video/native/native-player.css +183 -0
  89. package/src/components/Video/native/playerKey.ts +5 -0
  90. package/src/components/Video/native/useNativeCastMiddleware.ts +50 -0
  91. package/src/components/Video/native/useNativePlayer.ts +3 -0
  92. package/src/components/Video/native/useNativePlayerFullscreen.ts +44 -0
  93. package/src/components/Video/native/useNativePlayerHistory.ts +69 -0
  94. package/src/components/Video/native/useNativePlayerModal.ts +68 -0
  95. package/src/components/Video/native/useNativePlayerPlaylist.ts +67 -0
  96. package/src/components/Video/native/useNativePlayerState.ts +225 -0
  97. package/src/components/Video/player.ts +99 -0
  98. package/src/components/Video/theme/index.scss +291 -0
  99. package/src/components/Video/theme/videojs.css +1797 -0
  100. package/src/components/Video/useSource.ts +1431 -0
  101. package/src/components/Video/useSubtitlePreference.ts +66 -0
  102. package/src/components/Video/useWebview.ts +79 -0
  103. package/src/components/Video/videoFrame.ts +58 -0
  104. package/src/env.d.ts +3 -0
  105. package/src/i18n/README.md +392 -0
  106. package/src/i18n/index.ts +49 -0
  107. package/src/icons/Video_Player.svg +69 -0
  108. package/src/icons/box.svg +15 -0
  109. package/src/icons/client.svg +17 -0
  110. package/src/icons/logo.svg +28 -0
  111. package/src/icons//344/270/212/344/270/200/344/270/252.svg +6 -0
  112. package/src/icons//344/270/213/344/270/200/344/270/252.svg +4 -0
  113. package/src/icons//344/272/256/345/272/246.svg +13 -0
  114. package/src/icons//345/200/215/351/200/237.svg +14 -0
  115. package/src/icons//345/205/250/345/261/217.svg +16 -0
  116. package/src/icons//345/205/250/351/200/211_/345/267/262/351/200/211/344/270/255.svg +16 -0
  117. package/src/icons//345/205/250/351/200/211_/346/234/252/351/200/211/344/270/255.svg +15 -0
  118. package/src/icons//345/205/263/351/227/255/345/244/232/351/200/211.svg +14 -0
  119. package/src/icons//345/205/263/351/227/255/346/212/225/345/261/217.svg +11 -0
  120. package/src/icons//345/233/236/346/224/266/347/253/231.svg +15 -0
  121. package/src/icons//345/244/261/346/225/210.svg +17 -0
  122. package/src/icons//346/207/222/347/214/253/346/222/255/346/224/276/345/231/250-icon.png +0 -0
  123. package/src/icons//346/207/222/347/214/253/346/222/255/346/224/276/345/231/250.png +0 -0
  124. package/src/icons//346/212/225/345/261/217.svg +11 -0
  125. package/src/icons//346/212/225/351/200/201/344/270/255.jpg +0 -0
  126. package/src/icons//346/212/225/351/200/201/344/270/255.svg +21 -0
  127. package/src/icons//346/222/255/346/224/276.svg +3 -0
  128. package/src/icons//346/232/202/345/201/234.svg +4 -0
  129. package/src/icons//346/232/202/346/227/240.svg +21 -0
  130. package/src/icons//346/233/264/345/244/232/346/223/215/344/275/234.svg +11 -0
  131. package/src/icons//347/224/265/350/247/206.svg +18 -0
  132. package/src/icons//347/247/273/345/212/250/347/253/257_/350/203/214/346/231/257.webp +0 -0
  133. package/src/icons//350/203/214/346/231/257.png +0 -0
  134. package/src/icons//350/277/224/345/233/236.svg +13 -0
  135. package/src/icons//350/277/233/345/205/245/345/205/250/345/261/217.svg +13 -0
  136. package/src/icons//351/200/200/345/207/272/345/205/250/345/261/217.svg +15 -0
  137. package/src/icons//351/200/211/346/213/251.svg +15 -0
  138. package/src/icons//351/237/263/351/207/217.svg +13 -0
  139. package/src/index.d.ts +9 -0
  140. package/src/lzc-video-player.scss +7 -0
  141. package/src/lzc-video-player.ts +6 -0
  142. package/src/main.ts +62 -0
  143. package/src/model.ts +77 -0
  144. package/src/quasar-variables.sass +10 -0
  145. package/src/router/index.ts +74 -0
  146. package/src/stores/pinia.ts +3 -0
  147. package/src/stores/playlist.ts +146 -0
  148. package/src/use/useKeyBind.ts +61 -0
  149. package/src/use/useMultipleEdit.ts +60 -0
  150. package/src/use/useSdk.ts +5 -0
  151. package/src/use/useSubtitle.ts +39 -0
  152. package/src/use/useUtils.ts +22 -0
  153. package/src/use/useVideoFrame.ts +60 -0
  154. package/src/views/Home.ts +99 -0
  155. package/src/views/mobile/Home.vue +246 -0
  156. package/src/views/mobile/Player.vue +141 -0
  157. package/tailwind.config.js +15 -0
  158. package/tsconfig.config.json +8 -0
  159. package/tsconfig.json +20 -0
  160. package/vite.config.lib.ts +88 -0
  161. package/vite.config.ts +122 -0
  162. package/vue-shim.d.ts +4 -0
@@ -0,0 +1,205 @@
1
+ <script lang="ts" setup>
2
+ import { ref, onMounted, onUnmounted } from "vue"
3
+ import { castDevice, doStopAction } from "./useCast"
4
+ import { useVideoName } from "./useCommon"
5
+ import CastingJPG from "@/icons/投送中.jpg?inline"
6
+ import { t } from "@/i18n"
7
+ import type { LzcPlayer } from "@/components/Video/player"
8
+
9
+ const props = defineProps<{
10
+ player: LzcPlayer
11
+ }>()
12
+ const { videoName } = useVideoName(props.player)
13
+
14
+ const castError = ref(false)
15
+ props.player.on("CastError", () => {
16
+ castError.value = true
17
+ })
18
+ props.player.on("startCasting", () => {
19
+ castError.value = false
20
+ })
21
+
22
+ const back = () => {
23
+ castError.value = false
24
+ props.player.trigger({ type: "closeCasting" })
25
+ }
26
+ const closeCast = async () => {
27
+ castError.value = false
28
+ const wasCastMode = props.player.isCastMode()
29
+ props.player.trigger({ type: "closeCasting" })
30
+ if (!wasCastMode) {
31
+ return
32
+ }
33
+ try {
34
+ await doStopAction()
35
+ } catch (err) {
36
+ console.error(err)
37
+ }
38
+ }
39
+
40
+ onMounted(() => {
41
+ props.player.addClass("vjs-cast")
42
+ if (!props.player.isFullscreen()) {
43
+ props.player.requestFullscreen()
44
+ }
45
+ })
46
+
47
+ onUnmounted(() => {
48
+ props.player.removeClass("vjs-cast")
49
+ if (props.player.isFullscreen()) {
50
+ props.player.exitFullscreen()
51
+ }
52
+ })
53
+ </script>
54
+
55
+ <template>
56
+ <div class="overlay">
57
+ <div class="vjs-overlay-padding top">
58
+ <q-icon name="svguse:#icon-返回.svg" @click="back"></q-icon>
59
+ <div class="casting-tip">
60
+ <q-img :src="CastingJPG">
61
+ <div
62
+ class="absolute-full flex flex-center img-text"
63
+ v-if="!castError"
64
+ >
65
+ <span>{{
66
+ t(
67
+ "src.components.video.components.lzc_overlay.casting.text_casting",
68
+ "投送中",
69
+ )
70
+ }}</span>
71
+ <span>{{ castDevice?.name }}</span>
72
+ </div>
73
+ </q-img>
74
+ </div>
75
+ <q-icon name="svguse:#icon-关闭投屏.svg" @click="closeCast"></q-icon>
76
+ </div>
77
+
78
+ <div class="vjs-overlay-padding text error-text" v-if="castError">
79
+ <span>{{
80
+ t(
81
+ "src.components.video.components.lzc_overlay.casting.screen_casting_failed",
82
+ "投屏失败",
83
+ )
84
+ }}</span>
85
+ <div class="back-button" @click="closeCast">
86
+ {{
87
+ t(
88
+ "src.components.video.components.lzc_overlay.casting.button_back_play",
89
+ "返回播放",
90
+ )
91
+ }}
92
+ </div>
93
+ </div>
94
+ <div class="vjs-overlay-padding text cast-text" v-else>
95
+ {{
96
+ t(
97
+ "src.components.video.components.lzc_overlay.casting.screen_casting_tips_content",
98
+ "正在投送《\{\{videoName\}\}》,您可以在当前设备上进行其他操作,不会影响播放",
99
+ {
100
+ videoName,
101
+ },
102
+ )
103
+ }}
104
+ </div>
105
+
106
+ <div class="vjs-overlay-padding bottom-space"></div>
107
+ </div>
108
+ </template>
109
+
110
+ <style lang="scss" scoped>
111
+ .overlay {
112
+ height: 100%;
113
+ width: 100%;
114
+
115
+ display: flex;
116
+ flex-direction: column;
117
+ }
118
+
119
+ .top {
120
+ display: flex;
121
+ flex-direction: row;
122
+
123
+ > span {
124
+ margin-top: 3rem;
125
+ width: 2.4rem;
126
+ height: 2.4rem;
127
+ }
128
+
129
+ .casting-tip {
130
+ flex: 1;
131
+ text-align: center;
132
+
133
+ > .q-img {
134
+ max-width: 36.9rem;
135
+ height: 11rem;
136
+
137
+ .img-text {
138
+ display: flex;
139
+ flex-direction: column;
140
+
141
+ span:first-of-type {
142
+ height: 2rem;
143
+ font-size: 1.4rem;
144
+ font-weight: 500;
145
+ color: #ffffff;
146
+ line-height: 2rem;
147
+ }
148
+
149
+ span:last-of-type {
150
+ height: 1.7rem;
151
+ font-size: 1.2rem;
152
+ font-weight: 400;
153
+ color: #ffffff;
154
+ line-height: 1.7rem;
155
+ }
156
+ }
157
+ }
158
+ }
159
+ }
160
+
161
+ .text {
162
+ flex: 1;
163
+ flex-shrink: 0;
164
+
165
+ display: flex;
166
+ flex-direction: column;
167
+ justify-content: center;
168
+ align-items: center;
169
+ }
170
+ .cast-text {
171
+ height: 1.7rem;
172
+ font-size: 1.2rem;
173
+ font-weight: 400;
174
+ color: #ffffff;
175
+ line-height: 1.7rem;
176
+ }
177
+ .error-text {
178
+ > span {
179
+ font-size: 1.4rem;
180
+ font-weight: 400;
181
+ color: #ffffff;
182
+ line-height: 2rem;
183
+ margin: 1.3rem 0.2rem;
184
+ }
185
+ .back-button {
186
+ width: 12.8rem;
187
+ height: 3.6rem;
188
+ border-radius: 2.45rem;
189
+ border: 0.05rem solid #979797;
190
+
191
+ display: flex;
192
+ flex-direction: row;
193
+ justify-content: center;
194
+ align-items: center;
195
+
196
+ font-size: 1.4rem;
197
+ font-weight: 500;
198
+ color: #ffffff;
199
+ }
200
+ }
201
+
202
+ .bottom-space {
203
+ height: 9rem;
204
+ }
205
+ </style>
@@ -0,0 +1,103 @@
1
+ <script lang="ts" setup>
2
+ import { computed } from "vue"
3
+ import PlayingLayout from "./playingLayout.vue"
4
+ import { t } from "@/i18n"
5
+ import type { LzcPlayer } from "@/components/Video/player"
6
+
7
+ const props = defineProps<{
8
+ player: LzcPlayer
9
+ }>()
10
+ const errorMsg = computed(() => {
11
+ const e = props.player.error() as MediaError
12
+ switch (e.code) {
13
+ case 1:
14
+ return {
15
+ title: t(
16
+ "src.components.video.components.lzc_overlay.error.request_assets_over_title",
17
+ "请求资源终断,请重新播放",
18
+ ),
19
+ subtitle: "",
20
+ }
21
+ case 2:
22
+ return {
23
+ title: t(
24
+ "src.components.video.components.lzc_overlay.error.paly_failed_title",
25
+ "播放失败,网络错误导致媒体下载中途失败",
26
+ ),
27
+ subtitle: "",
28
+ }
29
+ case 3:
30
+ return {
31
+ title: t(
32
+ "src.components.video.components.lzc_overlay.error.decode_failed_title",
33
+ "视频解码失败",
34
+ ),
35
+ subtitle: t(
36
+ "src.components.video.components.lzc_overlay.error.decode_failed_subtitle",
37
+ "支持的视频格式有: mp4, m3u8, mkv",
38
+ ),
39
+ }
40
+ case 4:
41
+ return {
42
+ title: t(
43
+ "src.components.video.components.lzc_overlay.error.file_not_exist_title",
44
+ "加载视频失败,可能是因为没有访问权限或者文件已经不存在",
45
+ ),
46
+ subtitle: "",
47
+ }
48
+ case 5:
49
+ return {
50
+ title: t(
51
+ "src.components.video.components.lzc_overlay.error.encrypted_video_title",
52
+ "加密视频,没有密码解密",
53
+ ),
54
+ subtitle: "",
55
+ }
56
+ default:
57
+ return {
58
+ title: t(
59
+ "src.components.video.components.lzc_overlay.error.unknown_error_title",
60
+ "未知错误",
61
+ ),
62
+ subtitle: e.message,
63
+ }
64
+ }
65
+ })
66
+ </script>
67
+
68
+ <template>
69
+ <PlayingLayout :player="player">
70
+ <div class="error-content">
71
+ <q-item>
72
+ <q-item-section class="items-center">
73
+ <q-item-label class="title">{{ errorMsg.title }}</q-item-label>
74
+ <q-item-label caption lines="2" class="subtitle">
75
+ {{ errorMsg.subtitle }}
76
+ </q-item-label>
77
+ </q-item-section>
78
+ </q-item>
79
+ </div>
80
+ </PlayingLayout>
81
+ </template>
82
+
83
+ <style lang="scss" scoped>
84
+ .error-content {
85
+ flex: 1;
86
+
87
+ display: flex;
88
+ justify-content: center;
89
+ }
90
+ .title {
91
+ font-size: 1.2rem;
92
+ font-weight: 500;
93
+ color: #ffffff;
94
+ line-height: 1.7rem;
95
+ }
96
+ .subtitle {
97
+ font-size: 1.1rem;
98
+ font-weight: 400;
99
+ color: #ffffff;
100
+ line-height: 1.6rem;
101
+ opacity: 0.5;
102
+ }
103
+ </style>
@@ -0,0 +1,81 @@
1
+ import type { TouchSwipeParams } from "quasar"
2
+
3
+ export interface TouchPanEvent extends TouchSwipeParams {
4
+ isFirst: boolean
5
+ isFinal: boolean
6
+ offset: {
7
+ x: number
8
+ y: number
9
+ }
10
+ delta: {
11
+ x: number
12
+ y: number
13
+ }
14
+ position: {
15
+ top: number
16
+ left: number
17
+ }
18
+ }
19
+
20
+ function verticalChange(event: TouchPanEvent, dim: any, total = 100) {
21
+ const y = Math.floor(event.offset.y / window.devicePixelRatio)
22
+ // total 为范围
23
+ // ratio = total / dim.height
24
+ // 但由于一般滑动都是只滑动一半,所以乘4
25
+ return ((-1 * (y * total)) / (dim.height + 1)) * 4
26
+ }
27
+
28
+ // 亮度范围
29
+ const brightnessMin = 0
30
+ const brightnessMax = 1
31
+ export const computeBrightness = (
32
+ currBrightness: number,
33
+ event: TouchPanEvent,
34
+ dim: any,
35
+ ) => {
36
+ const change = verticalChange(event, dim, 255)
37
+ let b = currBrightness + change / 255
38
+ if (b < brightnessMin) {
39
+ b = brightnessMin
40
+ } else if (b > brightnessMax) {
41
+ b = brightnessMax
42
+ }
43
+ const percent = (b - brightnessMin) / (brightnessMax - brightnessMin)
44
+ return { percent, brightness: b }
45
+ }
46
+
47
+ // 音量范围 0-100
48
+ const volumeMin = 0
49
+ const volumeMax = 1
50
+ export const computeVolume = (
51
+ currentVolume: number,
52
+ event: TouchPanEvent,
53
+ dim: any,
54
+ ) => {
55
+ const change = verticalChange(event, dim)
56
+ let b = currentVolume + change / 100
57
+ if (b < volumeMin) {
58
+ b = volumeMin
59
+ } else if (b > volumeMax) {
60
+ b = volumeMax
61
+ }
62
+ return { percent: b / volumeMax, volume: b }
63
+ }
64
+
65
+ // 进度条范围
66
+ export const computeProgress = (
67
+ total: number,
68
+ currentTime: number,
69
+ event: TouchPanEvent,
70
+ dim: any,
71
+ ) => {
72
+ const x = Math.floor(event.offset.x / window.devicePixelRatio)
73
+ const change = dim.width > total ? x : (x * total) / (dim.width + 1)
74
+ let b = currentTime + change
75
+ if (b < 0) {
76
+ b = 0
77
+ } else if (b > total) {
78
+ b = total
79
+ }
80
+ return Math.trunc(b)
81
+ }
@@ -0,0 +1,99 @@
1
+ <script lang="ts" setup>
2
+ import { ref } from "vue"
3
+ import Playing from "./playing.vue"
4
+ import Casting from "./casting.vue"
5
+ import Error from "./error.vue"
6
+ import { useRefresh } from "./useCast"
7
+ import { useHistoryInfo } from "@/stores/playlist"
8
+ import type { LzcPlayer } from "@/components/Video/player"
9
+
10
+ const props = defineProps<{
11
+ player: LzcPlayer
12
+ active?: boolean
13
+ directMode?: boolean
14
+ }>()
15
+ const store = useHistoryInfo()
16
+ const casting = ref(false)
17
+ props.player.on("startCasting", () => {
18
+ casting.value = true
19
+ })
20
+ props.player.on("closeCasting", () => {
21
+ casting.value = false
22
+ })
23
+
24
+ const error = ref(false)
25
+ const errorTimeoutId = ref<ReturnType<typeof setTimeout>>()
26
+
27
+ function reset() {
28
+ error.value = false
29
+ props.player.removeClass("vjs-error")
30
+ if (errorTimeoutId.value) {
31
+ window.clearTimeout(errorTimeoutId.value)
32
+ }
33
+ }
34
+
35
+ function errorHandler() {
36
+ // 因为 video/lzc-cast 为一个自定义的类型,所以使用 Html5 解析会报错
37
+ // 这里忽略 video/lzc-cast 模式下的错误.
38
+ if (props.player.isCastMode()) {
39
+ return
40
+ }
41
+
42
+ // 把加载好的播放完后才出现播放错误的界面
43
+ const currentTime = props.player.currentTime()
44
+ const buffered = props.player.bufferedEnd()
45
+ if (currentTime! < buffered - 2) {
46
+ reset()
47
+ errorTimeoutId.value = setTimeout(
48
+ () => props.player.trigger({ type: "error" }),
49
+ (buffered - currentTime!) * 1000,
50
+ )
51
+ return
52
+ }
53
+
54
+ const e = props.player.error() as MediaError
55
+ if (e.code == 4) {
56
+ const currentInfo = props.player.currentVideoInfo?.()
57
+ store.markAsInvalid(currentInfo?.sourceUrl || props.player.currentSrc())
58
+ }
59
+ error.value = true
60
+ props.player.addClass("vjs-error")
61
+ }
62
+
63
+ /* 监听错误事件 */
64
+ props.player.on("error", errorHandler)
65
+
66
+ /* 当用户点击进度条后,把错误页面关闭 */
67
+ props.player.on("playing", () => {
68
+ if (error.value) {
69
+ reset()
70
+ }
71
+ })
72
+
73
+ /* 如果加载开始,将错误去掉,并尝试播放 */
74
+ props.player.on("loadstart", reset)
75
+
76
+ props.player.qualityLevels().on("change", (e: { selectedIndex: number }) => {
77
+ if (error.value) {
78
+ reset()
79
+ props.player.reloadSource(e.selectedIndex)
80
+ }
81
+ })
82
+ useRefresh()
83
+ </script>
84
+
85
+ <template>
86
+ <Error :player="player" v-if="error"></Error>
87
+ <Casting
88
+ v-else-if="casting && !props.directMode"
89
+ :player="player"
90
+ ></Casting>
91
+ <Playing
92
+ v-else
93
+ :player="player"
94
+ :active="props.active"
95
+ :direct-mode="props.directMode"
96
+ ></Playing>
97
+ </template>
98
+
99
+ <style lang="scss" scoped></style>