nicecode-v2/packages/meta/lib/VideoPlayer/VideoPlayer.js

593 lines
22 KiB
JavaScript

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;