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,99 @@
1
+ import { nextTick, ref, type Ref } from "vue"
2
+ import { useRouter } from "vue-router"
3
+ import type { FileStat } from "@lazycatcloud/webdav/web"
4
+ import type { VideoInfo } from "@/model"
5
+ import { createClient } from "webdav/web"
6
+ import { useHistoryInfo } from "@/stores/playlist"
7
+
8
+ const webdavClient = createClient("/_lzc/files/home/")
9
+
10
+ function dataTransfer<T>(raw: any) {
11
+ if (!raw) return
12
+ const output = raw.detail[0]
13
+ return JSON.parse(output) as T
14
+ }
15
+
16
+ export default function (ctx: Ref<any>) {
17
+ const router = useRouter()
18
+ const store = useHistoryInfo()
19
+ async function invoke(method: string, ...args: string[]) {
20
+ for (let i = 0; i < 5; i++) {
21
+ await nextTick()
22
+ const target = ctx.value
23
+ const exposed = target?._instance?.exposed ?? target
24
+ if (exposed && typeof exposed[method] === "function") {
25
+ exposed[method](...args)
26
+ return
27
+ }
28
+ await new Promise((r) => setTimeout(r, 50))
29
+ }
30
+ }
31
+
32
+ const openFilePicker = () => {
33
+ invoke("init")
34
+ invoke("open")
35
+ }
36
+
37
+ const closeFilePicker = () => {
38
+ invoke("close")
39
+ }
40
+ const getSourceUrl = (file: any, boxname: string) => {
41
+ const videopath = encodeURIComponent(file.filename)
42
+ const owner = file?.owner || ""
43
+ const _owner = owner ? `?X_LZCAPI_UID=${owner}` : ""
44
+ return `https://video.${boxname}.heiyu.space/_lzc/media/hls${videopath}/quality-720.m3u8${_owner}`
45
+ }
46
+ // 从 filepicker 中选择的文件一定为网盘资源
47
+ const submit = async (e: CustomEvent, boxname: string) => {
48
+ const files = dataTransfer<FileStat[]>(e) || []
49
+ const file = files[0]
50
+ if (!file) return
51
+ const sourceUrl = getSourceUrl(file, boxname)
52
+ const info: VideoInfo = {
53
+ _id: "",
54
+ sourceUrl,
55
+ name: file.basename,
56
+ duration: 0,
57
+ currentTime: 0,
58
+ invalid: false,
59
+ fromNetdisk: true,
60
+ path: file.filename,
61
+ updateTime: undefined,
62
+ subtitles: undefined,
63
+ }
64
+ store.setPendingPlayInfo(info)
65
+ await router.push({
66
+ name: "link",
67
+ query: {
68
+ url: sourceUrl,
69
+ name: file.basename,
70
+ },
71
+ })
72
+ }
73
+ const showList = ref(false)
74
+ const isPlaying = (_: VideoInfo) => {
75
+ return false
76
+ }
77
+ const onOpen = (info: VideoInfo) => {
78
+ store.setPendingPlayInfo(info)
79
+ router.push({
80
+ name: "link",
81
+ query: {
82
+ url: info.sourceUrl,
83
+ name: info.name,
84
+ },
85
+ })
86
+ }
87
+ const onBack = () => {
88
+ showList.value = false
89
+ }
90
+ return {
91
+ openFilePicker,
92
+ closeFilePicker,
93
+ submit,
94
+ showList,
95
+ isPlaying,
96
+ onOpen,
97
+ onBack,
98
+ }
99
+ }
@@ -0,0 +1,246 @@
1
+ <script lang="ts" setup>
2
+ import { ref, onMounted } from "vue"
3
+ import sdk from "@/use/useSdk"
4
+ import Playlist from "@/components/PlayList/index.vue"
5
+ import useHome from "../Home"
6
+ import PCBackgroundSvg from "@/icons/背景.png?inline"
7
+ import BackgroundSvg from "@/icons/移动端_背景.webp?inline"
8
+ import VideoNameSvg from "@/icons/懒猫播放器.png?inline"
9
+ import VideoNameEnSvg from "@/icons/Video_Player.svg?inline"
10
+ import { t } from "@/i18n"
11
+ import i18next from "i18next"
12
+ import { useBackPress } from "@lazycatcloud/lzc-toolkit"
13
+ import type { VideoInfo } from "@/model"
14
+
15
+ const filePickerInstance = ref()
16
+ const showPicker = ref(false)
17
+
18
+ const open = () => {
19
+ showPicker.value = true
20
+ openFilePicker()
21
+ toggleIosButton(false)
22
+ }
23
+
24
+ function close() {
25
+ showPicker.value = false
26
+ toggleIosButton(true)
27
+ }
28
+
29
+ const { openFilePicker, submit, showList, isPlaying, onOpen, onBack } =
30
+ useHome(filePickerInstance)
31
+ const submitFn = async (e: CustomEvent) => {
32
+ await submit(e, boxName.value)
33
+ showPicker.value = false
34
+ }
35
+ const VideoNameIcon = ref(VideoNameSvg)
36
+ const boxName = ref("")
37
+
38
+ const allowIosGestures = async (allows: boolean) => {
39
+ // @ts-expect-error
40
+ return await window?.webkit?.messageHandlers?.[
41
+ "_AllowsNavigationGestures"
42
+ ]?.postMessage({ allows })
43
+ }
44
+
45
+ function toggleIosButton(visible: boolean) {
46
+ // @ts-expect-error
47
+ const bridge = window?.webkit?.messageHandlers?.["SetCloseBtnShowStatus"]
48
+ if (bridge?.postMessage) {
49
+ bridge.postMessage({
50
+ params: [visible],
51
+ })
52
+ }
53
+ }
54
+
55
+ // disable back gestures
56
+ allowIosGestures(false)
57
+ toggleIosButton(true)
58
+
59
+ function openVideo(info: VideoInfo) {
60
+ toggleIosButton(false)
61
+ onOpen(info)
62
+ }
63
+
64
+ useBackPress(() => {
65
+ return true
66
+ })
67
+
68
+ onMounted(async () => {
69
+ if (i18next.language.startsWith("en")) {
70
+ VideoNameIcon.value = VideoNameEnSvg
71
+ } else {
72
+ VideoNameIcon.value = VideoNameSvg
73
+ }
74
+ const QueryInfo = await sdk.box.QueryInfo({})
75
+ boxName.value = QueryInfo.boxName
76
+ })
77
+ </script>
78
+
79
+ <template>
80
+ <div class="content">
81
+ <div style="flex: 4"></div>
82
+ <lzc-file-picker
83
+ v-if="boxName && showPicker"
84
+ class="file-picker-safe-area"
85
+ type="file"
86
+ ref="filePickerInstance"
87
+ base-url="/_lzc/files/home"
88
+ accept="video/*, audio/x-mpegurl, application/vnd.apple.mpegurl, application/vnd.rn-realmedia, application/dvd"
89
+ :boxId="boxName"
90
+ :multiple="false"
91
+ :isModal="true"
92
+ :choiceFileOnly="true"
93
+ :title="t('src.views.mobile.home.title_select_file', '选择网盘文件')"
94
+ :confirm-button-title="
95
+ t('src.views.mobile.home.button_start_play', '开始播放')
96
+ "
97
+ @close="close"
98
+ @submit="submitFn"
99
+ ></lzc-file-picker>
100
+ <div
101
+ class="playlist-modal"
102
+ v-if="showList"
103
+ @click.self.prevent="showList = false"
104
+ >
105
+ <Playlist
106
+ class="playlist"
107
+ :isPlaying="isPlaying"
108
+ @openVideo="openVideo"
109
+ @back="onBack"
110
+ ></Playlist>
111
+ </div>
112
+ <div class="buttons">
113
+ <div class="video-home-logo">
114
+ <q-icon name="svguse:#icon-logo.svg" size="80px"></q-icon>
115
+ <img class="video-text-icon" :src="VideoNameIcon" />
116
+ </div>
117
+ <div class="video-operation-btns">
118
+ <q-btn class="btn video-open-file" @click="open">
119
+ {{ t("src.views.mobile.home.button_open_file", "打开文件") }}
120
+ </q-btn>
121
+ <q-btn class="btn video-play-list" @click="showList = true">
122
+ {{ t("src.views.mobile.home.button_playlist", "播放列表") }}
123
+ </q-btn>
124
+ </div>
125
+ </div>
126
+ </div>
127
+ </template>
128
+
129
+ <style lang="scss" scoped>
130
+ .content {
131
+ display: flex;
132
+ flex-direction: column;
133
+ background-image: v-bind('"url(" + BackgroundSvg + ")"');
134
+ background-repeat: no-repeat;
135
+ background-position: center;
136
+ background-size: cover;
137
+ }
138
+
139
+ @media screen and (min-width: 720px) {
140
+ .content {
141
+ background-image: v-bind('"url(" + PCBackgroundSvg + ")"');
142
+ }
143
+ }
144
+
145
+ .buttons {
146
+ flex: 6;
147
+ display: flex;
148
+ flex-direction: column;
149
+ align-items: center;
150
+
151
+ .video-text-icon {
152
+ width: 14.9rem;
153
+ height: 2.9rem;
154
+ margin-top: 3.8rem;
155
+ }
156
+ }
157
+
158
+ .video-home-logo {
159
+ display: flex;
160
+ flex-direction: column;
161
+ align-items: center;
162
+ }
163
+
164
+ @media screen and (max-height: 500px) {
165
+ .video-operation-btns {
166
+ margin-top: 30px;
167
+ margin-block: 50px;
168
+ position: relative !important;
169
+ bottom: var(--lzc-safe-area-inset-bottom) !important;
170
+ }
171
+ }
172
+
173
+ .video-operation-btns {
174
+ position: fixed;
175
+ bottom: calc(10rem + var(--lzc-safe-area-inset-bottom));
176
+
177
+ .btn {
178
+ width: 15.6rem;
179
+ height: 4.8rem;
180
+ border-radius: 1rem;
181
+ font-size: 1.6rem;
182
+ font-weight: 500;
183
+ line-height: 2.2rem;
184
+ max-width: calc((100vw - 1.5rem - 2.4rem * 2) / 2);
185
+ }
186
+
187
+ .btn + .btn {
188
+ margin-left: 1.5rem;
189
+ }
190
+
191
+ .video-open-file {
192
+ color: #ffffff;
193
+ background: #5f86ff;
194
+ }
195
+
196
+ .video-play-list {
197
+ color: #5f86ff;
198
+ background: transparent;
199
+ border: 1px solid #5f86ff;
200
+ }
201
+ }
202
+
203
+ .file-picker-safe-area {
204
+ position: fixed;
205
+ inset: 0;
206
+ z-index: 9999;
207
+ box-sizing: border-box;
208
+ width: 100%;
209
+ height: 100%;
210
+ padding-top: var(--lzc-safe-area-inset-top);
211
+ padding-bottom: var(--lzc-safe-area-inset-bottom);
212
+ background: #ffffff;
213
+ }
214
+
215
+ .playlist-modal {
216
+ width: 100%;
217
+ height: 100%;
218
+ position: absolute;
219
+ top: 0;
220
+ z-index: 1;
221
+ display: flex;
222
+ background-color: rgba(0, 0, 0, 0.82);
223
+ }
224
+
225
+ @media screen and (min-width: 667px) {
226
+ .playlist-modal {
227
+ flex-direction: row-reverse;
228
+ }
229
+
230
+ .playlist {
231
+ width: 65%;
232
+ }
233
+ }
234
+
235
+ @media screen and (max-width: 666px) {
236
+ .playlist-modal {
237
+ flex-direction: column;
238
+ justify-content: flex-end;
239
+ }
240
+
241
+ .playlist {
242
+ height: 65%;
243
+ width: 100%;
244
+ }
245
+ }
246
+ </style>
@@ -0,0 +1,141 @@
1
+ <script lang="ts" setup>
2
+ import { onUnmounted, watch, withDefaults } from "vue"
3
+ import NativeVideoPlayer from "@/components/Video/NativeVideoPlayer.vue"
4
+ import type { NativePlayerAPI } from "@/components/Video/native/NativePlayer"
5
+ import { useRouter } from "vue-router"
6
+ import { useHistoryInfo } from "@/stores/playlist"
7
+ import type { VideoInfo } from "@/model"
8
+
9
+ const store = useHistoryInfo()
10
+ const props = withDefaults(
11
+ defineProps<{
12
+ url: string
13
+ name?: string
14
+ path?: string
15
+ fromNetdisk?: string
16
+ isUrl?: boolean
17
+ }>(),
18
+ {
19
+ isUrl: false,
20
+ },
21
+ )
22
+ const router = useRouter()
23
+ const back = () => router.replace({ name: "home" })
24
+ let boundPlayer: NativePlayerAPI | null = null
25
+ let unwatchUrl: (() => void) | null = null
26
+ let openVideoHandler: ((e?: any) => void) | null = null
27
+
28
+ const trigger = async (player: NativePlayerAPI) => {
29
+ let info: VideoInfo | undefined
30
+ const pendingInfo = store.pendingPlayInfo
31
+ const pendingMatchesRoute = !!pendingInfo?.sourceUrl
32
+ ? props.path && pendingInfo.path
33
+ ? pendingInfo.path === props.path
34
+ : pendingInfo.sourceUrl === props.url
35
+ : false
36
+
37
+ if (pendingInfo?.sourceUrl && pendingMatchesRoute) {
38
+ info = pendingInfo
39
+ store.setPendingPlayInfo(null)
40
+ await store.ready
41
+ const historyByPath = info.path
42
+ ? store.getHistoryInfoByPath(info.path)
43
+ : undefined
44
+ const historyByUrl = store.getHistoryInfo(info.sourceUrl)
45
+ const shouldUseHistory =
46
+ (info.currentTime ?? 0) <= 0 && (info.duration ?? 0) <= 0
47
+ if (shouldUseHistory) {
48
+ const historyInfo = historyByPath || historyByUrl
49
+ if (historyInfo) {
50
+ info = historyInfo
51
+ }
52
+ }
53
+ } else {
54
+ if (pendingInfo?.sourceUrl && !pendingMatchesRoute) {
55
+ store.setPendingPlayInfo(null)
56
+ }
57
+ await store.ready
58
+ if (props.path) {
59
+ info = store.getHistoryInfoByPath(props.path)
60
+ } else {
61
+ info = store.getHistoryInfo(props.url)
62
+ }
63
+ }
64
+ if (!info) {
65
+ info = {
66
+ _id: "",
67
+ sourceUrl: props.url,
68
+ name: props.name || "",
69
+ duration: 0,
70
+ currentTime: 0,
71
+ invalid: false,
72
+ fromNetdisk: props.fromNetdisk == "true",
73
+ path: props.path || "",
74
+ updateTime: undefined,
75
+ subtitles: undefined,
76
+ }
77
+ }
78
+ player.trigger({ type: "openVideo", info })
79
+ }
80
+
81
+ const onInit = (player: NativePlayerAPI) => {
82
+ if (boundPlayer === player) {
83
+ return
84
+ }
85
+ if (boundPlayer && openVideoHandler) {
86
+ boundPlayer.off("openVideo", openVideoHandler)
87
+ }
88
+ if (unwatchUrl) {
89
+ unwatchUrl()
90
+ unwatchUrl = null
91
+ }
92
+ boundPlayer = player
93
+ openVideoHandler = (e?: any) => {
94
+ const info = e?.info as VideoInfo | undefined
95
+ if (!info?.sourceUrl) return
96
+ const current = router.currentRoute.value
97
+ if (
98
+ current.name === "link" &&
99
+ current.query.url === info.sourceUrl &&
100
+ current.query.name === info.name
101
+ ) {
102
+ return
103
+ }
104
+ router.replace({
105
+ name: "link",
106
+ query: {
107
+ url: info.sourceUrl,
108
+ name: info.name || "",
109
+ path: info.path || undefined,
110
+ fromNetdisk: info.fromNetdisk ? "true" : undefined,
111
+ },
112
+ })
113
+ }
114
+ player.on("openVideo", openVideoHandler)
115
+ trigger(player)
116
+ unwatchUrl = watch(
117
+ () => props.url,
118
+ () => {
119
+ trigger(player)
120
+ },
121
+ )
122
+ }
123
+
124
+ onUnmounted(() => {
125
+ if (boundPlayer && openVideoHandler) {
126
+ boundPlayer.off("openVideo", openVideoHandler)
127
+ }
128
+ if (unwatchUrl) {
129
+ unwatchUrl()
130
+ unwatchUrl = null
131
+ }
132
+ boundPlayer = null
133
+ openVideoHandler = null
134
+ })
135
+ </script>
136
+
137
+ <template>
138
+ <div class="w-full h-full min-h-0 flex flex-col bg-black">
139
+ <NativeVideoPlayer @back="back" :onInit="onInit" :poster="''" />
140
+ </div>
141
+ </template>
@@ -0,0 +1,15 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ export default {
3
+ // Scope utility classes to player root to avoid affecting host app styles.
4
+ important: ".lzc-video-player",
5
+ // Disable Tailwind preflight to prevent global reset leakage in library mode.
6
+ corePlugins: {
7
+ preflight: false,
8
+ container: false,
9
+ },
10
+ content: ["./src/**/*.vue"],
11
+ theme: {
12
+ extend: {},
13
+ },
14
+ plugins: [],
15
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "@vue/tsconfig/tsconfig.node.json",
3
+ "include": ["vite.config.*", "vitest.config.*", "cypress.config.*"],
4
+ "compilerOptions": {
5
+ "composite": true,
6
+ "types": ["node"]
7
+ }
8
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "extends": "@vue/tsconfig/tsconfig.web.json",
3
+ "include": ["env.d.ts", "src/**/*.d.ts", "src/**/*", "src/**/*.vue"],
4
+ "compilerOptions": {
5
+ "declaration": true,
6
+ "module": "ESNext",
7
+ "target": "es2017",
8
+ "baseUrl": ".",
9
+ "paths": {
10
+ "@/*": ["./src/*"]
11
+ },
12
+ "types": ["vite-plugin-svg-icons/client", "vite-plugin-pwa/client"]
13
+ },
14
+
15
+ "references": [
16
+ {
17
+ "path": "./tsconfig.config.json"
18
+ }
19
+ ]
20
+ }
@@ -0,0 +1,88 @@
1
+ import { fileURLToPath, URL } from "node:url"
2
+
3
+ import { defineConfig } from "vite"
4
+ import vue from "@vitejs/plugin-vue"
5
+ import { quasar, transformAssetUrls } from "@quasar/vite-plugin"
6
+ import { createSvgIconsPlugin } from "vite-plugin-svg-icons"
7
+ import dts from "vite-plugin-dts"
8
+ import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js"
9
+ import path from "node:path"
10
+ import remToPx from "@thedutchcoder/postcss-rem-to-px"
11
+ import tailwindcss from "tailwindcss"
12
+
13
+ const version = process.env["VERSION"] || "0.0.0-dev"
14
+
15
+ export default defineConfig({
16
+ define: {
17
+ "process.env.NODE_ENV": '"production"',
18
+ __APP_VERSION__: JSON.stringify(version),
19
+ },
20
+ publicDir: false,
21
+ css: {
22
+ postcss: {
23
+ plugins: [tailwindcss(), remToPx({ baseValue: 10 })],
24
+ },
25
+ },
26
+ build: {
27
+ outDir: "lib/dist",
28
+ lib: {
29
+ // Could also be a dictionary or array of multiple entry points
30
+ entry: "src/lzc-video-player.ts",
31
+ name: "lzc-video-player",
32
+ fileName: "lzc-video-player",
33
+ // the proper extensions will be added
34
+ formats: ["es"],
35
+ },
36
+ rollupOptions: {
37
+ external: ["vue", "pinia"],
38
+ // make sure to externalize deps that shouldn't be bundled
39
+ // into your library
40
+ output: {
41
+ // Provide global variables to use in the UMD build
42
+ // for externalized deps
43
+ globals: {
44
+ vue: "Vue",
45
+ pinia: "pinia",
46
+ },
47
+ },
48
+ },
49
+ },
50
+ plugins: [
51
+ vue({
52
+ template: {
53
+ transformAssetUrls,
54
+ },
55
+ }),
56
+ quasar({
57
+ sassVariables: "./src/quasar-variables.sass",
58
+ }),
59
+ createSvgIconsPlugin({
60
+ // 指定需要缓存的图标文件夹
61
+ iconDirs: [path.resolve(process.cwd(), "./src/icons")],
62
+ // 指定symbolId格式
63
+ symbolId: "icon-[name].svg",
64
+
65
+ /**
66
+ * 自定义插入位置
67
+ * @default: body-last
68
+ */
69
+ inject: "body-last",
70
+
71
+ /**
72
+ * custom dom id
73
+ * @default: __svg__icons__dom__
74
+ */
75
+ customDomId: "__lzc_video_player_svg_dom__",
76
+ }),
77
+ cssInjectedByJsPlugin(),
78
+ dts({
79
+ exclude: ["src/views", "src/router", "src/main.ts", "src/App.vue"],
80
+ outDir: "lib/dist",
81
+ }),
82
+ ],
83
+ resolve: {
84
+ alias: {
85
+ "@": fileURLToPath(new URL("./src", import.meta.url)),
86
+ },
87
+ },
88
+ })