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,225 @@
1
+ import { ref, type Ref } from "vue"
2
+ import type { NativePlayerAPI } from "./NativePlayer"
3
+ import throttle from "lodash.throttle"
4
+
5
+ export function useNativePlayerState(player: Ref<NativePlayerAPI | null>) {
6
+ const showBigPlay = ref(true)
7
+ const showSpinner = ref(false)
8
+ const isSourceLoading = ref(false)
9
+ const videoName = ref("")
10
+ const isPlaying = ref(true)
11
+ const currentTime = ref(0)
12
+ const duration = ref(0)
13
+ const prevEnabled = ref(false)
14
+ const nextEnabled = ref(false)
15
+ const isFullscreen = ref(false)
16
+
17
+ let unbind: (() => void) | null = null
18
+ let alive = true
19
+
20
+ function bind(p: NativePlayerAPI) {
21
+ alive = true
22
+ let subtitleSwitching = false
23
+ const canUpdate = () => {
24
+ if (!alive) return false
25
+ if (p.isDisposed?.()) return false
26
+ try {
27
+ const video = p.$video?.()
28
+ if (video && !video.isConnected) return false
29
+ } catch {
30
+ return false
31
+ }
32
+ return true
33
+ }
34
+
35
+ const updateVideoName = () => {
36
+ if (!canUpdate()) return
37
+ videoName.value = p.currentVideoName() || ""
38
+ }
39
+ const throttledVideoName = throttle(updateVideoName, 3000, {
40
+ leading: true,
41
+ })
42
+
43
+ const onPlay = () => {
44
+ if (!canUpdate()) return
45
+ showBigPlay.value = false
46
+ isPlaying.value = true
47
+ updateVideoName()
48
+ }
49
+ const onPause = () => {
50
+ if (!canUpdate()) return
51
+ if (p.currentSrc()) showBigPlay.value = true
52
+ isPlaying.value = false
53
+ }
54
+ const onEnded = () => {
55
+ if (!canUpdate()) return
56
+ showBigPlay.value = true
57
+ isPlaying.value = false
58
+ }
59
+ const onWaiting = () => {
60
+ if (!canUpdate()) return
61
+ showSpinner.value = true
62
+ }
63
+ const onLoadstart = () => {
64
+ if (!canUpdate()) return
65
+ isSourceLoading.value = true
66
+ }
67
+ const onSeeking = () => {
68
+ if (!canUpdate()) return
69
+ showSpinner.value = true
70
+ }
71
+ const onSeeked = () => {
72
+ if (!canUpdate()) return
73
+ showSpinner.value = false
74
+ }
75
+ const onPlaying = () => {
76
+ if (!canUpdate()) return
77
+ isSourceLoading.value = false
78
+ showSpinner.value = false
79
+ }
80
+ const onLoadedData = () => {
81
+ if (!canUpdate()) return
82
+ isSourceLoading.value = false
83
+ }
84
+ const onCanplay = () => {
85
+ if (!canUpdate()) return
86
+ isSourceLoading.value = false
87
+ showSpinner.value = false
88
+ }
89
+ const readNumber = (reader: () => number | void) => {
90
+ try {
91
+ const value = reader()
92
+ return Number.isFinite(value as number) ? (value as number) : 0
93
+ } catch {
94
+ return 0
95
+ }
96
+ }
97
+
98
+ const syncTimeline = () => {
99
+ if (!canUpdate()) return
100
+ if (typeof p.currentTime !== "function" || typeof p.duration !== "function")
101
+ return
102
+ const nextTime = readNumber(() => p.currentTime())
103
+ const nextDuration = readNumber(() => p.duration())
104
+
105
+ // Subtitle switching temporarily reloads source and may report 0/0.
106
+ // Keep previous timeline until metadata is stable again.
107
+ if (subtitleSwitching) {
108
+ if (nextDuration <= 0) {
109
+ return
110
+ }
111
+ if (currentTime.value > 1 && nextTime === 0) {
112
+ duration.value = nextDuration
113
+ return
114
+ }
115
+ subtitleSwitching = false
116
+ }
117
+
118
+ currentTime.value = nextTime
119
+ duration.value = nextDuration
120
+ }
121
+
122
+ const onTimeupdate = () => {
123
+ syncTimeline()
124
+ }
125
+ const onSeekedSync = () => {
126
+ if (!canUpdate()) return
127
+ if (typeof p.currentTime !== "function") return
128
+ currentTime.value = readNumber(() => p.currentTime())
129
+ }
130
+ const onLoadedMetadata = () => {
131
+ syncTimeline()
132
+ }
133
+ const onSubtitleChange = () => {
134
+ subtitleSwitching = true
135
+ }
136
+ const onOpenVideo = () => {
137
+ subtitleSwitching = false
138
+ }
139
+ const onError = () => {
140
+ subtitleSwitching = false
141
+ isSourceLoading.value = false
142
+ }
143
+ const onPrevNextChange = () => {
144
+ if (!canUpdate()) return
145
+ prevEnabled.value = p.prevEnabled
146
+ nextEnabled.value = p.nextEnabled
147
+ }
148
+ const onFullscreenChange = () => {
149
+ if (!canUpdate()) return
150
+ isFullscreen.value = p.isFullscreen()
151
+ }
152
+
153
+ p.on("loadstart", onLoadstart)
154
+ p.on("play", onPlay)
155
+ p.on("pause", onPause)
156
+ p.on("ended", onEnded)
157
+ p.on("waiting", onWaiting)
158
+ p.on("seeking", onSeeking)
159
+ p.on("seeked", onSeeked)
160
+ p.on("playing", onPlaying)
161
+ p.on("loadeddata", onLoadedData)
162
+ p.on("canplay", onCanplay)
163
+ p.on("loadedmetadata", onLoadedMetadata)
164
+ p.on("timeupdate", onTimeupdate)
165
+ p.on("seeked", onSeekedSync)
166
+ p.on("subtitlechange", onSubtitleChange)
167
+ p.on("openVideo", onOpenVideo)
168
+ p.on("error", onError)
169
+ p.on("prevNextChange", onPrevNextChange)
170
+ p.on("fullscreenchange", onFullscreenChange)
171
+ p.on("play", updateVideoName)
172
+ p.on("timeupdate", throttledVideoName)
173
+
174
+ onTimeupdate()
175
+ updateVideoName()
176
+ onPrevNextChange()
177
+ onFullscreenChange()
178
+
179
+ unbind = () => {
180
+ alive = false
181
+ p.off("loadstart", onLoadstart)
182
+ p.off("play", onPlay)
183
+ p.off("pause", onPause)
184
+ p.off("ended", onEnded)
185
+ p.off("waiting", onWaiting)
186
+ p.off("seeking", onSeeking)
187
+ p.off("seeked", onSeeked)
188
+ p.off("playing", onPlaying)
189
+ p.off("loadeddata", onLoadedData)
190
+ p.off("canplay", onCanplay)
191
+ p.off("loadedmetadata", onLoadedMetadata)
192
+ p.off("timeupdate", onTimeupdate)
193
+ p.off("seeked", onSeekedSync)
194
+ p.off("subtitlechange", onSubtitleChange)
195
+ p.off("openVideo", onOpenVideo)
196
+ p.off("error", onError)
197
+ p.off("prevNextChange", onPrevNextChange)
198
+ p.off("fullscreenchange", onFullscreenChange)
199
+ p.off("play", updateVideoName)
200
+ p.off("timeupdate", throttledVideoName)
201
+ throttledVideoName.cancel()
202
+ }
203
+ }
204
+
205
+ function unbindIfNeeded() {
206
+ alive = false
207
+ unbind?.()
208
+ unbind = null
209
+ }
210
+
211
+ return {
212
+ showBigPlay,
213
+ showSpinner,
214
+ isSourceLoading,
215
+ videoName,
216
+ isPlaying,
217
+ currentTime,
218
+ duration,
219
+ prevEnabled,
220
+ nextEnabled,
221
+ isFullscreen,
222
+ bind,
223
+ unbindIfNeeded,
224
+ }
225
+ }
@@ -0,0 +1,99 @@
1
+ import type {
2
+ PreviewInfo,
3
+ Subtitle,
4
+ VideoInfo,
5
+ VideoQualityLevel,
6
+ VideoResolution,
7
+ } from "@/model"
8
+
9
+ export type SourceObject = { src: string; type?: string }
10
+
11
+ export interface QualityLevelList {
12
+ levels_: VideoQualityLevel[]
13
+ selectedIndex_: number
14
+ on: (event: string, fn: (e: any) => void) => void
15
+ trigger: (e: any) => void
16
+ }
17
+
18
+ export interface LzcModalHandle {
19
+ openModal: (page: string) => void
20
+ close: () => void
21
+ }
22
+
23
+ export interface LzcPlayer {
24
+ options_: {
25
+ mediaPrefix: string
26
+ showTopBar?: boolean
27
+ playMode?: "standard" | "direct"
28
+ }
29
+ qualityLevels(): QualityLevelList
30
+ play(): Promise<void>
31
+ pause(): void
32
+ paused(): boolean
33
+ currentTime(seconds?: number): number | void
34
+ duration(seconds?: number): number | void
35
+ volume(percent?: number): number | Promise<number> | void
36
+ muted(muted?: boolean): boolean | void
37
+ currentSrc(): string
38
+ src(sources?: string | SourceObject | SourceObject[] | null): string | void
39
+ poster(url?: string): string | void
40
+ on(event: string, fn: (e?: any) => void): void
41
+ one(event: string, fn: (e?: any) => void): void
42
+ off(event: string, fn?: (e?: any) => void): void
43
+ ready?(fn: () => void): void
44
+ trigger(
45
+ eventOrName: string | { type: string; [k: string]: unknown },
46
+ data?: unknown,
47
+ ): void
48
+ currentVideoInfo(): VideoInfo | null
49
+ currentVideoName(): string | undefined
50
+ currentPreview(): PreviewInfo | undefined
51
+ supportResolution(): VideoQualityLevel[]
52
+ currentResolution(): VideoResolution | undefined
53
+ changeResolution(level: VideoQualityLevel): void
54
+ logicalQualityLevels?(): VideoQualityLevel[]
55
+ getAvaliableSubtitles(): Subtitle[] | undefined
56
+ isSubtitleLoading(): boolean
57
+ currentSubtitle(): Subtitle | undefined
58
+ changeSubtitle(sub: Subtitle | number): void
59
+ clearSubtitle(): void
60
+ toggleSubtitleVisibility(): void
61
+ isSubtitleHidden(): boolean
62
+ setSubtitleHidden(value: boolean): void
63
+ isUsingNativeSubtitleFallback?(): boolean
64
+ currentBrightness(): Promise<number>
65
+ isCastMode(): boolean
66
+ isNetdiskSource(): boolean
67
+ reloadSource(index?: number): void
68
+ playPrev?: () => void
69
+ playNext?: () => void
70
+ enablePrev(value: boolean): void
71
+ enableNext(value: boolean): void
72
+ readonly prevEnabled: boolean
73
+ readonly nextEnabled: boolean
74
+ setFullscreenTarget(el: HTMLElement | null): void
75
+ requestFullscreen(): void
76
+ exitFullscreen(): void
77
+ isFullscreen(): boolean
78
+ $video(): HTMLVideoElement
79
+ playbackRate(rate?: number): number | void
80
+ playbackRates(rates?: number[]): number[]
81
+ lzcModal?: () => LzcModalHandle
82
+ id(): string
83
+ isDisposed(): boolean
84
+ dispose(): void
85
+ networkState(): number
86
+ currentSource(): { src: string; type?: string }
87
+ currentType(): string
88
+ currentWidth(): number
89
+ currentDimensions(): { width: number; height: number }
90
+ addClass(name: string): void
91
+ removeClass(name: string): void
92
+ hasClass(name: string): boolean
93
+ ended(): boolean
94
+ seeking(): boolean
95
+ scrubbing(): boolean
96
+ setScrubbing(value: boolean): void
97
+ bufferedEnd(): number
98
+ error(): MediaError | null
99
+ }
@@ -0,0 +1,291 @@
1
+ $text-color: #ffffff;
2
+ $button-color: #ffffff;
3
+ $progress-played-color: #5f86ff;
4
+ $progress-color: rgba(255, 255, 255, 0.3);
5
+
6
+ // videojs.css 中
7
+ // - 去掉 .vjs-loading-spinner
8
+ @use "videojs.css";
9
+
10
+ @mixin progress-text() {
11
+ font-size: 1.2rem;
12
+ font-family: PingFangSC-Semibold, PingFang SC;
13
+ font-weight: 600;
14
+ color: $text-color;
15
+ line-height: 2rem;
16
+ }
17
+
18
+ @mixin control-bar-text() {
19
+ font-size: 1.4rem;
20
+ font-family: PingFangSC-Semibold, PingFang SC;
21
+ font-weight: 600;
22
+ color: #ffffff;
23
+ line-height: 2rem;
24
+ }
25
+
26
+ .video-js {
27
+ .vjs-load-progress {
28
+ background: $progress-color;
29
+ }
30
+
31
+ .vjs-control {
32
+ width: unset;
33
+ }
34
+ .vjs-text-track-display div {
35
+ font-size: 1.6rem;
36
+ }
37
+ }
38
+
39
+ // 在播放的时候隐藏进度条
40
+ .vjs-has-started.vjs-user-inactive.vjs-playing .vjs-progress {
41
+ visibility: visible;
42
+ opacity: 0;
43
+ pointer-events: none;
44
+ transition: visibility 0.1s, opacity 0.1s;
45
+ }
46
+
47
+ // 投屏的时候展示进度条
48
+ .vjs-has-started.vjs-user-inactive.vjs-playing.vjs-cast .vjs-progress,
49
+ .vjs-has-started.vjs-user-inactive.vjs-playing.vjs-cast .vjs-control-bar {
50
+ opacity: 1;
51
+ visibility: visible;
52
+ }
53
+
54
+ .vjs-modal-dialog {
55
+ .vjs-modal-dialog-content {
56
+ display: none;
57
+ }
58
+ }
59
+
60
+ // 在 modal 的时候隐藏进度条
61
+ .vjs-controls-disabled .vjs-progress,
62
+ .vjs-using-native-controls .vjs-progress {
63
+ display: none !important;
64
+ }
65
+
66
+ // 加载时显示 loading
67
+ .vjs-waiting:not(.vjs-cast) .vjs-loading-spinner,
68
+ .vjs-seeking:not(.vjs-scrubbing):not(.vjs-error):not(.vjs-cast)
69
+ .vjs-loading-spinner {
70
+ visibility: visible;
71
+ animation: rotation 1s linear infinite;
72
+ @keyframes rotation {
73
+ to {
74
+ visibility: visible;
75
+ }
76
+ 0% {
77
+ transform: rotate(0deg);
78
+ }
79
+ 100% {
80
+ transform: rotate(360deg);
81
+ }
82
+ }
83
+ }
84
+ .vjs-cast .vjs-overlay {
85
+ background: #000000;
86
+ }
87
+
88
+ // 错误的时候也需要显示 control-bar 和 progress
89
+ .vjs-error .vjs-control-bar,
90
+ .vjs-error .vjs-progress {
91
+ display: flex !important;
92
+ }
93
+ .vjs-error .vjs-loading-spinner {
94
+ display: none;
95
+ }
96
+ .vjs-error .vjs-overlay {
97
+ background: #000000;
98
+ }
99
+
100
+ .vjs-theme-lzc {
101
+ .vjs-loading-spinner {
102
+ visibility: hidden;
103
+ position: absolute;
104
+ top: 50%;
105
+ left: 50%;
106
+ width: 36px;
107
+ height: 36px;
108
+ border-radius: 50%;
109
+ border-top: 3px solid $progress-played-color;
110
+ border-right: 3px solid transparent;
111
+ margin: -22px 0 0 -22px;
112
+
113
+ &:after {
114
+ content: "";
115
+ position: absolute;
116
+ left: 0;
117
+ top: 0;
118
+ width: 36px;
119
+ height: 36px;
120
+ border-radius: 50%;
121
+ border-bottom: 3px solid $progress-played-color;
122
+ border-left: 3px solid transparent;
123
+ }
124
+ }
125
+
126
+ .vjs-control-bar {
127
+ display: flex;
128
+ flex-direction: row;
129
+ padding: 0 2rem;
130
+ height: 4rem;
131
+ bottom: 2rem;
132
+
133
+ background: none;
134
+ gap: 0px 12px;
135
+ @media screen and (width > 480px) {
136
+ gap: 0px 16px;
137
+ }
138
+
139
+ .vjs-custom-control-spacer {
140
+ flex: 1;
141
+ display: block;
142
+ }
143
+
144
+ .vjs-playlist-button-control,
145
+ .vjs-playrate-button-control,
146
+ .vjs-fullscreen-button-control,
147
+ .vjs-resolution-button-control,
148
+ .vjs-subtitle-button-control {
149
+ @include control-bar-text;
150
+ }
151
+ }
152
+
153
+ .vjs-button {
154
+ &:hover {
155
+ color: $button-color;
156
+ background: none;
157
+ }
158
+
159
+ > .vjs-icon-placeholder {
160
+ &::before {
161
+ font-size: 2.4rem;
162
+ }
163
+ }
164
+ }
165
+
166
+ .vjs-time-control {
167
+ padding-left: 0px;
168
+ padding-right: 0px;
169
+ }
170
+
171
+ .vjs-volume-panel {
172
+ display: none;
173
+ }
174
+
175
+ .vjs-picture-in-picture-control {
176
+ display: none;
177
+ }
178
+
179
+ .vjs-modal-dialog {
180
+ z-index: 60;
181
+ overflow: hidden;
182
+ }
183
+
184
+ .vjs-progress {
185
+ width: 100%;
186
+ position: absolute;
187
+ bottom: 6.3rem;
188
+ padding: 0px 2rem;
189
+
190
+ display: flex;
191
+ flex-direction: row;
192
+ align-items: center;
193
+
194
+ // 上下居中
195
+ .vjs-current-time-display,
196
+ .vjs-remaining-time-display {
197
+ display: flex;
198
+ flex-direction: column;
199
+ justify-content: center;
200
+
201
+ @include progress-text;
202
+ }
203
+ .vjs-current-time-display {
204
+ margin-right: 1.7rem;
205
+ }
206
+ .vjs-remaining-time-display {
207
+ margin-left: 1.7rem;
208
+ }
209
+
210
+ // 调整padding高度,增大事件触发的范围
211
+ .vjs-progress-holder {
212
+ height: 0.2rem;
213
+ padding: 1.2rem 0rem;
214
+ border-radius: 0.15rem;
215
+ cursor: pointer;
216
+ margin: 0;
217
+ // 使用这个class来画未加载的进度条
218
+ &:after {
219
+ content: "";
220
+ position: absolute;
221
+ width: 100%;
222
+ height: 0.2rem;
223
+ background: $progress-color;
224
+ }
225
+
226
+ .vjs-load-progress,
227
+ .vjs-play-progress {
228
+ height: 0.2rem;
229
+ top: 1.2rem;
230
+ }
231
+
232
+ .vjs-play-progress {
233
+ background: $progress-played-color;
234
+ &:before {
235
+ content: "";
236
+ width: 2.6rem;
237
+ height: 2.6rem;
238
+ position: absolute;
239
+ right: -1.3rem;
240
+ top: -1.3rem;
241
+ background: $progress-played-color;
242
+ border-radius: 1.8rem;
243
+ opacity: 0.15;
244
+ z-index: 1;
245
+ }
246
+ &:after {
247
+ content: "";
248
+ width: 1.2rem;
249
+ height: 1.2rem;
250
+ position: absolute;
251
+ right: -0.6rem;
252
+ top: -0.6rem;
253
+ background: $progress-played-color;
254
+ border-radius: 0.65rem;
255
+ z-index: 1;
256
+ }
257
+ }
258
+ }
259
+ }
260
+
261
+ .vjs-overlay {
262
+ display: flex;
263
+ position: absolute;
264
+ width: 100%;
265
+ height: 100%;
266
+ pointer-events: auto;
267
+ left: 0;
268
+ top: 0;
269
+
270
+ .vjs-overlay-padding {
271
+ padding: 0 2rem;
272
+ background-origin: border-box;
273
+ }
274
+ }
275
+ }
276
+
277
+ @media screen and (min-width: 480px) {
278
+ .vjs-theme-lzc {
279
+ .vjs-control-bar {
280
+ padding: 0 4.4rem;
281
+ }
282
+ .vjs-progress {
283
+ padding: 0 4.4rem;
284
+ }
285
+ .vjs-overlay {
286
+ .vjs-overlay-padding {
287
+ padding: 0 4.4rem;
288
+ }
289
+ }
290
+ }
291
+ }