var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/VideoPlayer/VideoPlayer.tsx var VideoPlayer_exports = {}; __export(VideoPlayer_exports, { default: () => VideoPlayer_default }); module.exports = __toCommonJS(VideoPlayer_exports); var import_react = __toESM(require("react")); var import_func = require("@zhst/func"); var import_rc_align = __toESM(require("rc-align")); var import_hooks = require("@zhst/hooks"); var import_classnames = __toESM(require("classnames")); var import_downloadjs = __toESM(require("downloadjs")); var import__ = require(".."); var import_iconfont = __toESM(require("../iconfont")); var import_ImageEditor = require("../ImageEditor"); var import_FlvPlayer = __toESM(require("./components/FlvPlayer")); var import_Progress = __toESM(require("./components/Progress")); var import_Loading = __toESM(require("./components/Loading")); var import_constants = require("../utils/constants"); var import_videoPlayerHelper = require("./videoPlayerHelper"); var import_index = require("./index.less"); var componentName = `zhst-image__video-view`; var VideoPlayer = (0, import_react.forwardRef)((props, ref) => { const { url, maxDuration = 20, screenshotButtonAlign = { points: ["bl", "br"], offset: [6, 0], overflow: { adjustX: true, adjustY: true } }, screenshotButtonRender = () => /* @__PURE__ */ import_react.default.createElement("div", { style: { color: "#fff", width: "80px", top: 0 } }, "回调DOM"), onCropChange, defautlNormalizationRect: defaultNormalizationRect } = props; const containerRef = (0, import_react.useRef)(null); const videoRef = (0, import_react.useRef)(null); const videoInsRef = (0, import_react.useRef)(null); const [playSeq, setPlaySeq] = (0, import_react.useState)(0); const videoRemoveListener = (0, import_react.useRef)(import_func.noop); const loadingTimeRef = (0, import_react.useRef)(0); const delayLoadingTimer = (0, import_react.useRef)(null); const [isReady, setIsReady] = (0, import_react.useState)(false); const [isPlay, setIsPlay] = (0, import_react.useState)(false); const [isEnd, setIsEnd] = (0, import_react.useState)(false); const [isError, setIsError] = (0, import_react.useState)(false); const [isVideoLoadFinished, setIsVideoLoadFinish] = (0, import_react.useState)(false); const [playTime, setPlayTime] = (0, import_react.useState)(0); const [isLoadingVideo, setIsLoadingVideo] = (0, import_react.useState)(true); const [isDelayLoading, setIsDelayLoading] = (0, import_react.useState)(false); const latestIsLoadingVideo = (0, import_hooks.useLatest)(isLoadingVideo); const setIsLoadingVideoWrapper = (isLoading) => { setIsLoadingVideo((preLoading) => { if (!preLoading && isLoading) { loadingTimeRef.current = (/* @__PURE__ */ new Date()).getTime(); } if (!isLoading) { loadingTimeRef.current = null; } if (!isLoading) { setIsDelayLoading(false); } if (!delayLoadingTimer.current && preLoading) { delayLoadingTimer.current = setTimeout(() => { if (latestIsLoadingVideo.current) { setIsDelayLoading(true); } delayLoadingTimer.current = null; }, 200); } return isLoading; }); }; (0, import_react.useEffect)(() => { let timer = setInterval(() => { if (loadingTimeRef.current) { if ((/* @__PURE__ */ new Date()).getTime() - loadingTimeRef.current > 1e3 * 30) { checkIsErr(); } } }, 1e3); return () => { clearInterval(timer); }; }, []); (0, import_hooks.useUpdateEffect)(() => { var _a, _b; if (isEnd) { (_b = (_a = videoInsRef == null ? void 0 : videoInsRef.current) == null ? void 0 : _a.pause) == null ? void 0 : _b.call(_a); } }, [isEnd]); const checkIsErr = () => { var _a, _b; setIsError(true); try { (_b = (_a = videoInsRef == null ? void 0 : videoInsRef.current) == null ? void 0 : _a.destroy) == null ? void 0 : _b.call(_a); } catch (error) { console.error(error); } }; const latestMaxDuration = (0, import_hooks.useLatest)(maxDuration); const initPlayer = (0, import_react.useCallback)((ins, dom) => { videoRef.current = dom; videoInsRef.current = ins; const maxDuration2 = latestMaxDuration.current || 0; let video = dom; let errorLister = (e) => { checkIsErr(); console.error("视频出错了", e, video.currentTime); }; let waitingListener = () => { setIsLoadingVideoWrapper(true); }; let playingListener = () => { setIsLoadingVideoWrapper(false); setIsError(false); }; let playLister = () => { setIsPlay(true); setIsError(false); }; let pauseListener = () => { setIsPlay(false); }; let endedListner = () => { setIsEnd(true); setIsVideoLoadFinish(true); }; let timeupdateListner = () => { let nowTime = video.currentTime; if (nowTime >= maxDuration2) { setIsEnd(true); setIsVideoLoadFinish(true); } setPlayTime(nowTime); }; let windowErrorHandle = (errorEvent) => { try { if (errorEvent["message"] == "Uncaught TypeError: Cannot read property 'flushStashedSamples' of null") { checkIsErr(); console.error("视频出错了 window监听", errorEvent); } } catch (error) { console.error(error); } }; video.addEventListener("error", errorLister); video.addEventListener("waiting", waitingListener); video.addEventListener("playing", playingListener); video.addEventListener("play", playLister); video.addEventListener("pause", pauseListener); video.addEventListener("ended", endedListner); video.addEventListener("timeupdate", timeupdateListner); window.addEventListener("error", windowErrorHandle); videoRemoveListener.current = () => { video.removeEventListener("error", errorLister); video.removeEventListener("waiting", waitingListener); video.removeEventListener("playing", playingListener); video.removeEventListener("play", playLister); video.removeEventListener("pause", pauseListener); video.removeEventListener("ended", endedListner); video.removeEventListener("timeupdate", timeupdateListner); window.removeEventListener("error", windowErrorHandle); }; videoInsRef == null ? void 0 : videoInsRef.current.on(import_FlvPlayer.FLV_EVENT.ERROR, (type, errDetail, info) => { checkIsErr(); console.error("videoInsRef 错误", type, errDetail, info, video.currentTime); }); let playPromise = videoInsRef == null ? void 0 : videoInsRef.current.play(); setIsReady(true); playPromise.then(() => { setIsReady(true); }).catch((...arg) => { try { } catch (error) { } console.error("playPromise视频出错了", arg); }); }, []); (0, import_hooks.useUnmount)(() => { try { videoRemoveListener.current(); } catch (e) { console.error(e); } }); const reload = async () => { if (videoInsRef.current) { let oldTime = videoInsRef.current.currentTime; videoInsRef.current.currentTime = 0; if (oldTime === videoInsRef.current.currentTime) { setIsReady(false); setIsPlay(false); setIsLoadingVideoWrapper(false); setIsReady(false); setIsEnd(false); setIsVideoLoadFinish(false); setPlayTime(0); try { videoRemoveListener.current(); } catch (error) { console.error(error); } setPlaySeq((pre) => pre + 1); return; } videoInsRef.current.play(); } setPlayTime(0); setIsEnd(false); }; const seek = (v) => { if (videoInsRef.current && isVideoLoadFinished) { setPlayTime(parseFloat(v)); videoInsRef.current.currentTime = parseFloat(v); } else { import__.message.warning("待视频加载完,才可操作进度条"); } }; const [isFullscreen, { toggleFullscreen }] = (0, import_hooks.useFullscreen)(containerRef, { pageFullscreen: true }); const showMaxDuration = !!maxDuration ? maxDuration : (0, import_func.toRealNumber)((0, import_func.get)(videoRef, "current.duration", 0)); const showSlider = videoInsRef.current && isVideoLoadFinished; const showStatus = (0, import_videoPlayerHelper.getShowStatus)(isDelayLoading, isEnd, isError); const corpContainerRef = (0, import_react.useRef)(); const cropInsRef = (0, import_react.useRef)(null); const [showCrop, setShowCrop] = (0, import_react.useState)(false); const isFirstFlagRef = (0, import_react.useRef)(true); (0, import_react.useEffect)(() => { const isFirst = isFirstFlagRef.current; if (!isLoadingVideo && isReady && isFirst && defaultNormalizationRect && !showStatus) { (0, import_func.nextTick)(() => { setShowCrop(true); }); } }, [isLoadingVideo, showStatus]); const alginContainerRef = (0, import_react.useRef)(null); const alignRef = (0, import_react.useRef)(null); const [cropRect, setCropRect] = (0, import_react.useState)(null); (0, import_react.useEffect)(() => { var _a, _b; showCrop ? (_a = videoInsRef == null ? void 0 : videoInsRef.current) == null ? void 0 : _a.pause() : (_b = videoInsRef == null ? void 0 : videoInsRef.current) == null ? void 0 : _b.play(); }, [showCrop]); (0, import_react.useEffect)(() => { var _a; let handlerCropStart; let handlerCropEnd; setCropRect(null); if (!isReady) return; if (showCrop) { handlerCropStart = (0, import_func.addEventListenerWrapper)(corpContainerRef.current, import_ImageEditor.EVENT_CROP_START, () => { setCropRect(null); }); handlerCropEnd = (0, import_func.addEventListenerWrapper)(corpContainerRef.current, import_ImageEditor.EVENT_CROP_END, (event) => { var _a2, _b; const data = event.detail; setCropRect({ x: data.left, y: data.top, w: data.width, h: data.height }); (_b = (_a2 = alignRef == null ? void 0 : alignRef.current) == null ? void 0 : _a2.forceAlign) == null ? void 0 : _b.call(_a2); }); let video = videoRef.current; let scale = Math.min( video.offsetWidth / video.videoWidth, video.offsetHeight / video.videoHeight ); let finalVideoWidth = video.videoWidth * scale; let finalVideoHeight = video.videoHeight * scale; let cropBoxLimited = { width: finalVideoWidth, height: finalVideoHeight, top: (video.offsetHeight - finalVideoHeight) / 2, left: (video.offsetWidth - finalVideoWidth) / 2 }; let canvas = document.createElement("canvas"); canvas.width = video.offsetWidth; canvas.height = video.offsetHeight; canvas.style.display = "none"; document.body.appendChild(canvas); let ctx = canvas.getContext("2d"); ctx == null ? void 0 : ctx.drawImage( video, (video.offsetWidth - finalVideoWidth) / 2, (video.offsetHeight - finalVideoHeight) / 2, finalVideoWidth, finalVideoHeight ); let imageData = canvas.toDataURL("image/png"); (_a = canvas.parentNode) == null ? void 0 : _a.removeChild(canvas); const isFirst = isFirstFlagRef.current; let initialCropBoxData = null; if (isFirst && defaultNormalizationRect) { initialCropBoxData = { left: defaultNormalizationRect.x * finalVideoWidth + cropBoxLimited.left, top: defaultNormalizationRect.y * finalVideoHeight + cropBoxLimited.top, width: defaultNormalizationRect.w * finalVideoWidth, height: defaultNormalizationRect.h * finalVideoHeight }; } isFirstFlagRef.current = false; cropInsRef.current = new import_ImageEditor.Cropper(corpContainerRef.current, { showMask: true, cropBoxLimited, img: imageData, initialCropBoxData }); } return () => { var _a2, _b; handlerCropStart == null ? void 0 : handlerCropStart.remove(); handlerCropEnd == null ? void 0 : handlerCropEnd.remove(); (_b = (_a2 = cropInsRef == null ? void 0 : cropInsRef.current) == null ? void 0 : _a2.destroy) == null ? void 0 : _b.call(_a2); cropInsRef.current = null; }; }, [showCrop, isReady]); const latestCropRect = (0, import_hooks.useLatest)(cropRect); const getCropInfo = async () => { var _a; const cropRect2 = latestCropRect.current; let video = videoRef.current; if (!video) return; let rectList = []; let extendRectList = []; let selectIndex = 0; let scale = Math.min( video.offsetWidth / video.videoWidth, video.offsetHeight / video.videoHeight ); let finalVideoWidth = video.videoWidth * scale; let finalVideoHeight = video.videoHeight * scale; let canvas = document.createElement("canvas"); canvas.width = finalVideoWidth; canvas.height = finalVideoHeight; canvas.style.display = "none"; document.body.appendChild(canvas); let ctx = canvas.getContext("2d"); ctx.drawImage( video, 0, 0, finalVideoWidth, finalVideoHeight ); let base64 = canvas.toDataURL("image/jpeg"); const blobData = (0, import_func.dataURLToBlob)(base64); (_a = canvas.parentNode) == null ? void 0 : _a.removeChild(canvas); const file = new window.File([blobData], `${(/* @__PURE__ */ new Date()).getTime()}`); let newRect = { w: cropRect2.w / finalVideoWidth, h: cropRect2.h / finalVideoHeight, x: (cropRect2.x - (video.offsetWidth - finalVideoWidth) / 2) / finalVideoWidth, y: (cropRect2.y - (video.offsetHeight - finalVideoHeight) / 2) / finalVideoHeight }; rectList.push(newRect); extendRectList.push(newRect); extendRectList.forEach(async (rect, index) => { extendRectList[index] = { ...rect }; }); return { rectList, extendRectList, selectIndex, file }; }; (0, import_react.useEffect)(() => { let normalizationRect = null; if (showCrop && cropRect) { let video = videoRef.current; let scale = Math.min( video.offsetWidth / video.videoWidth, video.offsetHeight / video.videoHeight ); let finalVideoWidth = video.videoWidth * scale; let finalVideoHeight = video.videoHeight * scale; let cropBoxLimited = { width: finalVideoWidth, height: finalVideoHeight, top: (video.offsetHeight - finalVideoHeight) / 2, left: (video.offsetWidth - finalVideoWidth) / 2 }; normalizationRect = { x: (cropRect.x - cropBoxLimited.left) / cropBoxLimited.width, y: (cropRect.y - cropBoxLimited.top) / cropBoxLimited.height, w: cropRect.w / cropBoxLimited.width, h: cropRect.h / cropBoxLimited.height }; } onCropChange == null ? void 0 : onCropChange(showCrop, normalizationRect); }, [showCrop, cropRect]); const downloadVideoframe = (0, import_react.useCallback)(async () => { var _a, _b; try { (_b = (_a = videoInsRef == null ? void 0 : videoInsRef.current) == null ? void 0 : _a.pause) == null ? void 0 : _b.call(_a); let video = videoRef.current; var canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); let base64; if (video.readyState === 0) { ctx == null ? void 0 : ctx.clearRect(0, 0, canvas.width, canvas.height); canvas.width = video.offsetWidth; canvas.height = video.offsetHeight; ctx.fillStyle = "black"; ctx == null ? void 0 : ctx.fillRect(0, 0, canvas.width, canvas.height); base64 = canvas.toDataURL(); } else { canvas.width = video.videoWidth; canvas.height = video.videoHeight; ctx == null ? void 0 : ctx.drawImage(video, 0, 0, canvas.width, canvas.height); base64 = canvas.toDataURL("image/png"); } (0, import_downloadjs.default)(base64); } catch (error) { console.error(error); } }, []); const latestIsReady = (0, import_hooks.useLatest)(isReady); const cropAble = !showStatus && isReady; (0, import_react.useImperativeHandle)(ref, () => ({ cropAble, setShowCrop: (dispatch) => { const isReady2 = latestIsReady.current; if (!isReady2) return; setShowCrop(dispatch); }, downloadVideoframe })); return /* @__PURE__ */ import_react.default.createElement("div", { className: (0, import_classnames.default)(`${componentName}`), ref: containerRef }, url && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement( import_FlvPlayer.default, { playId: playSeq, autoPlay: true, className: (0, import_classnames.default)(`${componentName}-flv`), type: url.startsWith("http") ? "mp4" : "flv", url, config: { enableStashBuffer: true, stashInitialSize: 1024 * 700, isLive: true, hasAudio: false, hasVideo: true }, onCreat: initPlayer } ), /* @__PURE__ */ import_react.default.createElement( "div", { className: (0, import_classnames.default)(`${componentName}-crop-container`), ref: corpContainerRef, style: { display: isFullscreen ? "none" : "block" } } ), showCrop && cropRect && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement( "div", { ref: alginContainerRef, className: (0, import_classnames.default)(`${componentName}-align`), style: Object.assign( { width: cropRect.w, height: cropRect.h }, (0, import_func.getTransforms)({ translateX: cropRect.x, translateY: cropRect.y }) ) } ), /* @__PURE__ */ import_react.default.createElement( import_rc_align.default, { ref: alignRef, monitorWindowResize: true, align: screenshotButtonAlign, target: function() { return alginContainerRef.current; } }, screenshotButtonRender({ model: "IMAGE", getCropInfo, setShowCrop, cropType: import_constants.CROP_TYPE["CUSTOM"] }) )), !showCrop && /* @__PURE__ */ import_react.default.createElement("div", { className: `${componentName}-opt` }, /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement( import__.Button, { type: "text", onClick: () => { var _a, _b; if (!isPlay) { (_a = videoInsRef == null ? void 0 : videoInsRef.current) == null ? void 0 : _a.play(); setShowCrop(false); } else { (_b = videoInsRef == null ? void 0 : videoInsRef.current) == null ? void 0 : _b.pause(); } } }, /* @__PURE__ */ import_react.default.createElement( import_iconfont.default, { styles: { color: "#fff", display: "flex" }, color: "#1890ff", icon: !isPlay ? "icon-shipinbofang" : "icon-shipinzanting" } ) )), /* @__PURE__ */ import_react.default.createElement( "div", { className: `${componentName}-opt-range`, onClick: (e) => { e.stopPropagation(); } }, /* @__PURE__ */ import_react.default.createElement( import_Progress.default, { value: playTime, min: 0, max: showMaxDuration, showSlider, onChange: seek } ), /* @__PURE__ */ import_react.default.createElement("div", null, (0, import_func.formatDurationTime)(playTime), "/", (0, import_func.formatDurationTime)(showMaxDuration)) ), /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement( import__.Button, { type: "text", onClick: (e) => { e.stopPropagation(); toggleFullscreen(); } }, /* @__PURE__ */ import_react.default.createElement( import_iconfont.default, { styles: { color: "#fff", display: "flex" }, size: 18, icon: isFullscreen ? "icon-cancle_fullscreen" : "icon-fullscreen" } ) ))), !!showStatus && /* @__PURE__ */ import_react.default.createElement(import_Loading.default, { status: showStatus, reload: () => reload() }))); }); var VideoPlayer_default = VideoPlayer;