@rxdrag/website-lib-core 0.0.51 → 0.0.53
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rxdrag/website-lib-core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.53",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.ts"
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"lodash-es": "^4.17.21",
|
|
36
36
|
"react": "^19.1.0",
|
|
37
37
|
"react-dom": "^19.1.0",
|
|
38
|
-
"@rxdrag/
|
|
39
|
-
"@rxdrag/
|
|
38
|
+
"@rxdrag/entify-lib": "0.0.10",
|
|
39
|
+
"@rxdrag/rxcms-models": "0.3.79"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"astro": "^4.0.0 || ^5.0.0"
|
|
@@ -47,7 +47,8 @@ export async function queryOneTheme(envVariables: EnvVariables) {
|
|
|
47
47
|
// FrontComponentFields.props,
|
|
48
48
|
// FrontComponentFields.seqValue,
|
|
49
49
|
// FrontComponentFields.slots,
|
|
50
|
-
// FrontComponentFields.
|
|
50
|
+
// FrontComponentFields.propsSchema,
|
|
51
|
+
// FrontComponentFields.propsTest,
|
|
51
52
|
// FrontComponentFields.title,
|
|
52
53
|
// ])
|
|
53
54
|
.config(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useRef, type VideoHTMLAttributes } from "react";
|
|
2
2
|
|
|
3
|
-
export function
|
|
3
|
+
export function BackgroundVideoPlayer(props: VideoHTMLAttributes<HTMLVideoElement>) {
|
|
4
4
|
const { poster, src, className, ...restProps } = props;
|
|
5
5
|
const videoRef = useRef<HTMLVideoElement>(null);
|
|
6
6
|
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { ID } from "@rxdrag/entify-lib";
|
|
2
|
+
import { Media } from "@rxdrag/rxcms-models";
|
|
3
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
4
|
+
import Hls from "hls.js";
|
|
5
|
+
import { Icon } from "@iconify/react";
|
|
6
|
+
|
|
7
|
+
export function VideoPlayer(props: {
|
|
8
|
+
media: Media;
|
|
9
|
+
onToggleSelect?: (id: ID) => void;
|
|
10
|
+
}) {
|
|
11
|
+
const { media, onToggleSelect } = props;
|
|
12
|
+
const videoRef = useRef<HTMLVideoElement>(null);
|
|
13
|
+
const hlsRef = useRef<Hls | null>(null);
|
|
14
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
15
|
+
|
|
16
|
+
const handleContainerClick = React.useCallback(
|
|
17
|
+
(e: React.MouseEvent) => {
|
|
18
|
+
// 如果正在播放,点击暂停并阻止事件冒泡(避免同时触发选择)
|
|
19
|
+
if (isPlaying && videoRef.current) {
|
|
20
|
+
e.stopPropagation();
|
|
21
|
+
videoRef.current.pause();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// 未播放时,触发选择事件
|
|
26
|
+
onToggleSelect?.(media.id!);
|
|
27
|
+
},
|
|
28
|
+
[media.id, onToggleSelect, isPlaying]
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const handlePlayClick = React.useCallback(
|
|
32
|
+
(e: React.MouseEvent) => {
|
|
33
|
+
e.stopPropagation();
|
|
34
|
+
if (videoRef.current) {
|
|
35
|
+
if (isPlaying) {
|
|
36
|
+
videoRef.current.pause();
|
|
37
|
+
} else {
|
|
38
|
+
videoRef.current.play();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
[isPlaying]
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (!videoRef.current || !media.file?.original) return;
|
|
47
|
+
|
|
48
|
+
const video = videoRef.current;
|
|
49
|
+
const videoUrl = media.file.original;
|
|
50
|
+
|
|
51
|
+
// 监听播放状态
|
|
52
|
+
const handlePlay = () => {
|
|
53
|
+
setIsPlaying(true);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const handlePause = () => {
|
|
57
|
+
setIsPlaying(false);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
video.addEventListener("play", handlePlay);
|
|
61
|
+
video.addEventListener("pause", handlePause);
|
|
62
|
+
|
|
63
|
+
// Cloudflare Stream 使用 HLS
|
|
64
|
+
if (media.storageType === "cloudflare_stream") {
|
|
65
|
+
if (!Hls.isSupported()) {
|
|
66
|
+
// Safari 原生支持 HLS
|
|
67
|
+
if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
68
|
+
video.src = videoUrl;
|
|
69
|
+
}
|
|
70
|
+
return () => {
|
|
71
|
+
video.removeEventListener("play", handlePlay);
|
|
72
|
+
video.removeEventListener("pause", handlePause);
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const hls = new Hls({
|
|
77
|
+
enableWorker: true,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
hls.on(Hls.Events.ERROR, (event, data) => {
|
|
81
|
+
if (data.fatal) {
|
|
82
|
+
switch (data.type) {
|
|
83
|
+
case Hls.ErrorTypes.NETWORK_ERROR:
|
|
84
|
+
hls.startLoad();
|
|
85
|
+
break;
|
|
86
|
+
case Hls.ErrorTypes.MEDIA_ERROR:
|
|
87
|
+
hls.recoverMediaError();
|
|
88
|
+
break;
|
|
89
|
+
default:
|
|
90
|
+
hls.destroy();
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
hls.loadSource(videoUrl);
|
|
97
|
+
hls.attachMedia(video);
|
|
98
|
+
hlsRef.current = hls;
|
|
99
|
+
|
|
100
|
+
return () => {
|
|
101
|
+
video.removeEventListener("play", handlePlay);
|
|
102
|
+
video.removeEventListener("pause", handlePause);
|
|
103
|
+
hls.destroy();
|
|
104
|
+
hlsRef.current = null;
|
|
105
|
+
};
|
|
106
|
+
} else {
|
|
107
|
+
// 普通视频使用原生 video
|
|
108
|
+
video.src = videoUrl;
|
|
109
|
+
|
|
110
|
+
return () => {
|
|
111
|
+
video.removeEventListener("play", handlePlay);
|
|
112
|
+
video.removeEventListener("pause", handlePause);
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}, [media.file?.original, media.storageType]);
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<div
|
|
119
|
+
className="z-0 w-full bg-default-100 cursor-pointer aspect-square relative group"
|
|
120
|
+
onClick={handleContainerClick}
|
|
121
|
+
>
|
|
122
|
+
<video
|
|
123
|
+
ref={videoRef}
|
|
124
|
+
preload="metadata"
|
|
125
|
+
poster={media.file?.thumbnail}
|
|
126
|
+
className="absolute inset-0 w-full h-full object-cover"
|
|
127
|
+
/>
|
|
128
|
+
|
|
129
|
+
{/* 播放按钮 - 播放时隐藏 */}
|
|
130
|
+
{!isPlaying && (
|
|
131
|
+
<button
|
|
132
|
+
onClick={handlePlayClick}
|
|
133
|
+
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-10 cursor-pointer"
|
|
134
|
+
>
|
|
135
|
+
<div className="w-12 h-12 rounded-full bg-white/90 hover:bg-white flex items-center justify-center shadow-lg transition-all hover:scale-110">
|
|
136
|
+
<Icon
|
|
137
|
+
icon="mdi:play"
|
|
138
|
+
className="text-2xl text-gray-800"
|
|
139
|
+
style={{ marginLeft: "3px" }}
|
|
140
|
+
/>
|
|
141
|
+
</div>
|
|
142
|
+
</button>
|
|
143
|
+
)}
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
}
|