diff --git a/components/Video/AliPlayer.d.ts b/components/Video/AliPlayer.d.ts new file mode 100644 index 0000000..a03faf1 --- /dev/null +++ b/components/Video/AliPlayer.d.ts @@ -0,0 +1,42 @@ +import type { ForwardRefExoticComponent, RefAttributes } from "react"; + +export interface AliPlayerProps { + /** 播放地址 */ + source?: string | string[]; + /** 阿里的 vid */ + vid?: string; + /** 阿里的 playAuth */ + playAuth?: string; + /** 封面地址 */ + cover?: string; + /** 播放器宽度,默认 100% */ + width?: string; + /** 播放器高度,默认 600px */ + height?: string; + /** 是否自动播放 */ + autoplay?: boolean; + /** 是否显示进度条 */ + showProgress?: boolean; + /** 是否直播模式 */ + isLive?: boolean; + /** 播放开始时间 */ + playTime?: number; + /** 播放结束事件 */ + onEnded?: () => void; + /** 播放进度更新事件 */ + onTimeupdate?: (currentTime: number) => void; +} + +export interface AliPlayerRef { + play: () => void; + pause: () => void; +} + +/** + * 视频播放组件 + */ +declare const AliPlayer: ForwardRefExoticComponent< + AliPlayerProps & RefAttributes +>; + +export default AliPlayer; diff --git a/components/Video/AliPlayer.js b/components/Video/AliPlayer.js new file mode 100644 index 0000000..1a64676 --- /dev/null +++ b/components/Video/AliPlayer.js @@ -0,0 +1,159 @@ +import { useDocumentVisibility } from "ahooks"; +import { uniqueId } from "lodash-es"; +import { forwardRef, useEffect, useImperativeHandle, useRef } from "react"; +import { dynamicLoadCss, dynamicLoadJs } from "../../utils"; + +/** + * 视频播放组件 + */ +const AliPlayer = forwardRef(({ + source = "", + vid = "", + playAuth = "", + cover = "", + width = "100%", + height = "600px", + autoplay = true, + showProgress = true, + isLive = false, + playTime = 0, + onEnded, + onTimeupdate, +}, ref) => { + const playerRef = useRef(null); + const containerRef = useRef(null); + const visibility = useDocumentVisibility(); + const className = useRef(uniqueId("_")).current; + + useImperativeHandle(ref, () => ({ + play: () => { + playerRef.current && playerRef.current.play(); + }, + pause: () => { + playerRef.current && playerRef.current.pause(); + }, + })); + + const onDisposeAliPlayer = () => { + if (!playerRef.current) + return; + playerRef.current.dispose(); + playerRef.current = null; + }; + + const onCreateAliPlayer = async () => { + if (!containerRef.current) + return; + await dynamicLoadJs("https://g.alicdn.com/apsara-media-box/imp-web-player/2.16.3/aliplayer-min.js"); + await dynamicLoadCss("https://g.alicdn.com/apsara-media-box/imp-web-player/2.16.3/skins/default/aliplayer-min.css"); + onDisposeAliPlayer(); + + const skinLayout = [ + { name: "bigPlayButton", align: "blabs", x: 30, y: 80 }, + { name: "H5Loading", align: "cc" }, + { name: "errorDisplay", align: "tlabs", x: 0, y: 0 }, + { name: "infoDisplay" }, + { name: "tooltip", align: "blabs", x: 0, y: 56 }, + { name: "thumbnail" }, + { + name: "controlBar", + align: "blabs", + x: 0, + y: 0, + children: [ + { name: "playButton", align: "tl", x: 15, y: 12 }, + { name: "timeDisplay", align: "tl", x: 10, y: 7 }, + { name: "fullScreenButton", align: "tr", x: 10, y: 12 }, + { name: "setting", align: "tr", x: 15, y: 12 }, + { name: "volume", align: "tr", x: 5, y: 10 }, + ], + }, + ]; + + if (showProgress) { + skinLayout[skinLayout.length - 1].children.unshift({ + name: "progress", + align: "blabs", + x: 0, + y: 44, + }); + } + + if (typeof window.Aliplayer !== "undefined") { + playerRef.current = new window.Aliplayer( + { + id: className, + ...(source + ? { source } + : { + vid, + playauth: playAuth, + qualitySort: "asc", + format: "m3u8", + encryptType: 1, + mediaType: "video", + isLive: true, + rePlay: false, + playsinline: true, + controlBarVisibility: "hover", + }), + cover, + width, + height, + autoplay, + isLive, + useH5Prism: true, + skinLayout, + }, + (player) => { + if (autoplay) { + player.play(); + } + + player.on("ended", () => { + onEnded && onEnded(); + }); + + player.on("timeupdate", () => { + onTimeupdate && onTimeupdate(player.getCurrentTime()); + }); + + if (playTime > 0) { + player.seek(playTime); + } + }, + ); + } + }; + + useEffect(() => { + if (source || (vid && playAuth)) { + onCreateAliPlayer(); + } + + return () => { + onDisposeAliPlayer(); + }; + }, [source, vid, playAuth]); + + useEffect(() => { + if (visibility === "hidden") { + playerRef.current && playerRef.current.pause(); + } + else { + playerRef.current && playerRef.current.play(); + } + }, [visibility]); + + return ( +
+ ); +}); + +AliPlayer.displayName = "AliPlayer"; + +export default AliPlayer; diff --git a/components/Video/index.d.ts b/components/Video/index.d.ts new file mode 100644 index 0000000..c806abb --- /dev/null +++ b/components/Video/index.d.ts @@ -0,0 +1,20 @@ +import type { ForwardRefExoticComponent, RefAttributes } from "react"; +import type { AliPlayerProps, AliPlayerRef } from "./AliPlayer"; + +export interface VideoProps extends Omit { + /** 是否显示弹窗 */ + visible?: boolean; + /** 是否内联模式 */ + inline?: boolean; + /** 弹窗关闭事件 */ + onCancel?: () => void; +} + +/** + * 视频播放组件 + */ +declare const Video: ForwardRefExoticComponent< + VideoProps & RefAttributes +>; + +export default Video; diff --git a/components/Video/index.js b/components/Video/index.js new file mode 100644 index 0000000..5b10547 --- /dev/null +++ b/components/Video/index.js @@ -0,0 +1,89 @@ +import { Modal } from "antd"; +import { useEffect, useRef, useState } from "react"; +import { getFileUrl } from "../../utils"; +import AliPlayer from "./AliPlayer"; + +/** + * 视频播放组件 + */ +const Video = ({ + source = "", + vid = "", + playAuth = "", + cover = "", + autoplay = true, + showProgress = true, + playTime = 0, + inline = false, + isLive = false, + width = "100%", + height = "600px", + title = "视频", + visible: externalVisible = false, + onCancel, + ...restProps +}) => { + const [internalVisible, setInternalVisible] = useState(false); + const playerRef = useRef(null); + + const visible = onCancel ? externalVisible : internalVisible; + const setVisible = onCancel || setInternalVisible; + + const getSource = () => { + if (!source) + return; + if (source.includes("http") || source.includes("https")) + return source; + else return getFileUrl() + source; + }; + + useEffect(() => { + if (!inline) { + if (visible) { + playerRef.current && playerRef.current.play(); + } + else { + playerRef.current && playerRef.current.pause(); + } + } + }, [visible, inline]); + + const playerElement = ( + + ); + + if (!inline) { + return ( + setVisible(false)}> + 取消 + , + ]} + maskClosable={false} + onCancel={() => setVisible(false)} + {...restProps} + > + {playerElement} + + ); + } + + return playerElement; +}; + +export default Video;