[Human] chore(project): 初始化导入 91dj H5 客户端项目代码

从本地工程首次入库;.npmrc(含私有 npm 源 token)与 tsbuildinfo 已加入 .gitignore,不随仓库提交。
This commit is contained in:
谢宇宁
2026-10-08 14:22:35 +07:00
commit eada71e211
1421 changed files with 144498 additions and 0 deletions
+847
View File
@@ -0,0 +1,847 @@
<template>
<div class="vertical_video_warp" :data-hide-progress="state.hideProgress">
<div class="trial-seek-veil" v-show="trialSeekBlocking"></div>
<!-- 点击层必须在 playerShell 外:xgplayer destroy 会 root.innerHTML='',壳内节点会被清掉 -->
<div class="avoid_sliding" @click="togglePlayPause"></div>
<div :id="playerShellId" class="video-shell"></div>
</div>
</template>
<script lang="ts" setup>
import VideoApi from '@/service/video';
import { useConfigStore } from '@/stores/config';
import { useGlobalStore } from '@/stores/global';
import { useUserStore } from '@/stores/user';
import { onActivated, onBeforeUnmount, onDeactivated, onMounted, Ref, ref, toRefs, reactive } from 'vue';
import HlsJSPlayer from 'xgplayer-hls.js';
import UserApi from '@/service/user';
import { addVideoHistory } from '@/utils';
import { HistoryVideoType } from '@/enums/app';
import { analyticsEventVideo } from '@/utils/eventReporter/analytics';
import { judgeTime } from '@/utils/index';
import {
bindXgTrialProgressGuard,
setTrialVideoFrameHidden,
syncXgplayerSeekUi,
XG_HLS_FEED_OPTS,
} from '@/utils/xgHlsFeedOpts';
import { bindH265PlaybackFallback, resolvePlayResourceKey } from '@/utils/h265Playback';
import { setSharedFreeWatchCount } from '@/utils/sharedFreeWatchCount';
const props = defineProps<{
videoInfo: Types.Video.VInfosItem;
hasDarkWebTag: boolean;
searchAccessTicket?: string;
}>();
const { videoInfo } = toRefs(props);
const playerShellId = `horizontal-hls-${props.videoInfo.id}`.replace(/[^a-zA-Z0-9_-]/g, '_');
/** 试看拖动/seek 期间黑场遮罩,避免 iOS 先闪出越界帧 */
const trialSeekBlocking = ref(false);
const { isVip, userInfo } = useUserStore();
const userStore = useUserStore();
const globalStore = useGlobalStore();
let userFreeCount: Ref<Types.Video.UserFreeCount> = ref({
isCan: false,
watchCount: 0,
});
const emit = defineEmits(['getFreeCount', 'showPreviewTime', 'play', 'jumpOverHandle', 'timeupdate']);
let trialUnlockPopupPending = false;
const emitTrialUnlockPopup = () => {
if (trialUnlockPopupPending) return;
trialUnlockPopupPending = true;
emit('jumpOverHandle');
window.setTimeout(() => {
trialUnlockPopupPending = false;
}, 300);
};
let player: any = null; //播放器实例
let isReport = ref(false); // 视频埋点锁
let isPreview = ref(false); // 是否预览视频
let videoUrl = ref(''); // 播放地址
/** 当前是否使用 H.265 资源 */
let isPlayingH265 = false;
/** H.265 失败后强制 H.264,防止死循环 */
let forceH264 = false;
let pendingResumeAt = 0;
let unbindH265Fallback: (() => void) | null = null;
const state = reactive({
beginTime: new Date(), // 统计时使用:开始时间
playTimer: <any>'', // 统计时使用:时间计时器
playTime: 0, // 统计时使用:播放时间
hideProgress: false,
isCompleteReported: false, // 播放完成事件(>=95%)是否已上报,避免重复
isSecondPlayReported: false, // 播放超过 1 分钟后二次 play 埋点锁
});
// 进度达到 95% 视为播放完成(自然播放或拖动到 95% 都算)
const checkVideoComplete = () => {
if (state.isCompleteReported) return;
const duration = Number(player?.video?.duration) || 0;
const currentTime = Number(player?.video?.currentTime) || 0;
if (duration > 0 && currentTime / duration >= 0.9) {
state.isCompleteReported = true;
analyticsEventVideo(videoInfo.value, 'MOVIE', Math.ceil(currentTime || 1), 'video_complete', '播放完成');
}
};
const checkSecondVideoPlay = () => {
if (state.isSecondPlayReported) return;
const duration = Number(player?.video?.duration) || 0;
const currentTime = Number(player?.video?.currentTime) || 0;
if (currentTime / duration < 0.9) {
state.isSecondPlayReported = true;
analyticsEventVideo(videoInfo.value, 'MOVIE', Math.ceil(currentTime || 1), 'video_play', '播放');
}
};
onMounted(() => {
getInfo();
//获取免费观看次数
getFreeWatchCount();
analyticsEventVideo(videoInfo.value, 'MOVIE', 0, 'video_view', '展示');
});
/**
* @description 暂停
*/
const pause = () => {
if (!player) return;
const wasPlaying = !player.paused;
player.pause();
clearInterval(state.playTimer);
state.playTimer = '';
if (wasPlaying) {
analyticsEventVideo(videoInfo.value, 'MOVIE', Math.ceil(player?.video?.currentTime || 1), 'video_pause', '暂停');
}
};
/** 点击画面切换播放/暂停 */
const togglePlayPause = () => {
if (!player) return;
if (player.paused) {
player.play?.();
return;
}
pause();
};
/**
* @description 是否拥有完整片源的观看权限,判定口径与 playCheck 保持一致
*/
const hasFullVideoPermission = () => {
// 已购买
if (videoInfo.value.vidStatus.hasPaid === true) return true;
// 免费视频
if (videoInfo.value.freeArea === true) return true;
// 金币视频免费看权限
if (videoInfo.value.originCoins != 0 && videoInfo.value.coins == 0) return true;
// vip>=2 可直接观看,暗网需 vip4
if (isVip && ((userInfo.vipLevel >= 2 && !props.hasDarkWebTag) || (userInfo.vipLevel == 4 && props.hasDarkWebTag))) {
return true;
}
// 会员视频(金币为 0)
if (videoInfo.value.originCoins === 0 && isVip) return true;
// 自己发布的视频
if (userInfo.uid === videoInfo.value.publisher.uid) return true;
// 免费观影次数
if (userFreeCount.value.isCan === true && videoInfo.value.originCoins === 0) return true;
// 预售金币视频免费次数
if (
useGlobalStore().presaleObj.status != 0 &&
globalStore.advanceStatus.privilegeLimit.hasLimit &&
useGlobalStore().preSaleCoinVideoPlay
) {
return true;
}
return false;
};
/**
* @description 判断是否预览视频
*/
const checkPreview = () => {
if (!hasFullVideoPermission() && videoInfo.value.previewURL) {
isPreview.value = true;
isPlayingH265 = false;
videoUrl.value = videoInfo.value.previewURL;
} else {
isPreview.value = false;
const resolved = resolvePlayResourceKey({
h264Key: videoInfo.value.sourceURL,
h265Key: videoInfo.value.h265Url,
forceH264,
});
isPlayingH265 = resolved.isH265;
videoUrl.value = resolved.resourceKey;
}
};
/**
* 获取用户信息
* @param uid
*/
const getInfo = async () => {
try {
let res: any = await UserApi.queryUserInfo(null);
let userInfo = res;
userStore.saveUserInfo(userInfo);
} catch (e) {
console.log(e);
}
};
/**
* @description 初始化播放器
*/
const previewStartFlag = ref(false); // 是否进行预览时间设置
let suppressPlayAnalytics = false;
let trialLimitHandled = false;
let unbindTrialProgressGuard: (() => void) | null = null;
const pageActive = ref(true);
const teardownTrialGuard = () => {
unbindTrialProgressGuard?.();
unbindTrialProgressGuard = null;
setTrialFrameBlocked(false);
trialLimitHandled = false;
};
let destroySilently = false;
const destroyPlayer = (silent = false) => {
teardownTrialGuard();
unbindH265Fallback?.();
unbindH265Fallback = null;
if (!player) return;
if (!silent) checkSecondVideoPlay();
destroySilently = silent;
try {
player.src = '';
player.destroy(true);
} catch {
/* ignore */
}
destroySilently = false;
clearInterval(state.playTimer);
state.playTimer = '';
player = null;
};
const fallbackH265ToH264 = (resumeAt: number) => {
if (forceH264 || !isPlayingH265) return;
forceH264 = true;
isPlayingH265 = false;
pendingResumeAt = resumeAt;
destroyPlayer(true);
initVideo();
};
const initVideo = () => {
if (player) destroyPlayer();
checkPreview();
player = new HlsJSPlayer({
id: playerShellId,
url: getPath(),
fluid: true,
playsinline: true,
videoInit: true,
loop: true,
autoplay: true,
controls: true,
lang: 'zh-cn',
ignores: ['i18n', 'play', 'volume', 'definition'],
allowPlayAfterEnded: true,
allowSeekAfterEnded: true,
closeVideoPreventDefault: true,
closeVideoStopPropagation: true,
// 由壳外 avoid_sliding 接管单击播停;开启双击会吞掉单击
closeVideoClick: true,
closeInactive: true,
enableVideoDbltouch: false,
closeMoveSeek: needsTrialSeekLimit(),
hlsOpts: XG_HLS_FEED_OPTS,
});
unbindH265Fallback = bindH265PlaybackFallback(player, {
shouldFallback: () => isPlayingH265 && !forceH264,
onFallback: fallbackH265ToH264,
});
// 播放统计
const addPlayCount = () => {
//当视频滑走的时候调用 用作统计
let req: any = {
publisher: videoInfo.value.publisher.uid,
videoID: videoInfo.value.id,
beginTime: state.beginTime,
endTime: new Date(),
playWay: userFreeCount.value.isCan ? (userFreeCount.value.watchCount ? 0 : 1) : 2,
via: 1,
longer: state.playTime,
};
if (videoInfo.value.tags && videoInfo.value.tags.length > 0) {
req.tagID = videoInfo.value.tags[0].id;
}
if (props.searchAccessTicket) {
req.searchAccessTicket = props.searchAccessTicket;
}
VideoApi.getPlayVideo(req);
if (state.playTime > 1) {
// analyticsEventVideo(videoInfo.value, 'MOVIE', state.playTime, 'video_exit', '播放退出');
}
clearInterval(state.playTimer);
state.playTimer = '';
state.playTime = 1;
};
setTimeout(() => {
emit('showPreviewTime', player.video.duration);
bindTrialProgressGuard();
player.config.closeMoveSeek = needsTrialSeekLimit();
}, 500);
player.on('playing', () => {
playing();
});
// 设置开始播放时间
player.on('loadedmetadata', () => {
if (pendingResumeAt > 0) {
const resumeAt = pendingResumeAt;
pendingResumeAt = 0;
try {
player.currentTime = resumeAt;
} catch {
/* ignore */
}
}
//免费视频
if (videoInfo.value.freeArea === true) return;
// 视频已购买
if (videoInfo.value.vidStatus.hasPaid === true) return;
//金币视频免费看权限
if (videoInfo.value.originCoins != 0 && videoInfo.value.coins == 0) return;
// 会员视频(金币为0,freeArea = false)
if (videoInfo.value.originCoins === 0 && videoInfo.value.freeArea === false && isVip) return;
// VIP免费次数
if (userFreeCount.value.isCan === true && videoInfo.value.originCoins === 0) return;
//自己发布的视频
if (userInfo.uid === videoInfo.value.publisher.uid) return;
//预售金币视频免费次数能否观看
if (
useGlobalStore().presaleObj.status != 0 &&
globalStore.advanceStatus.privilegeLimit.hasLimit &&
useGlobalStore().preSaleCoinVideoPlay
)
return;
player.currentTime = videoInfo.value.previewStart;
previewStartFlag.value = true;
});
// 播放
player.on('play', () => {
emit('play');
if (!suppressPlayAnalytics) {
analyticsEventVideo(videoInfo.value, 'MOVIE', Math.ceil(player?.video?.currentTime || 1), 'video_play', '播放');
}
});
/** 仅当明确回到试看区内才清标记;回弹到 limit-ε 时 playCheck 为 true,不能清,否则半屏关了又弹 */
const canResetTrialLimitHandled = () => {
const limit = getBackendTrialLimitSeconds();
if (limit == null) return true;
const t = Number(player?.video?.currentTime) || 0;
return t < limit - Math.max(0.5, TRIAL_SEEK_EPSILON * 2);
};
const handleTrialLimitReached = () => {
if (!pageActive.value) return;
if (!player.paused) {
pause();
}
if (!trialLimitHandled) {
trialLimitHandled = true;
revealTrialFrameForMask();
emitTrialUnlockPopup();
}
};
player.on('timeupdate', () => {
if (!pageActive.value || !player.hasStart) return;
emit('timeupdate', Number(player?.video?.currentTime) || 0);
checkVideoComplete();
if (playCheck()) {
if (canResetTrialLimitHandled()) {
trialLimitHandled = false;
}
return;
}
handleTrialLimitReached();
});
player.on('seeked', () => {
checkVideoComplete();
if (!player.hasStart) return;
const clamped = clampTrialSeek();
clearTrialSeekBlock();
// 钳制后再补发进度:拖动松手时未必有 timeupdate
if (pageActive.value) {
emit('timeupdate', Number(player?.video?.currentTime) || 0);
}
window.setTimeout(() => {
suppressPlayAnalytics = false;
}, 300);
if (clamped) return;
if (!playCheck()) {
handleTrialLimitReached();
}
});
player.on('seeking', () => {
if (!player?.hasStart) return;
suppressPlayAnalytics = true;
if (needsTrialSeekLimit()) {
setTrialFrameBlocked(true);
}
clampTrialSeek();
});
player.on('destroy', () => {
if (destroySilently) return;
addPlayCount();
});
};
/** 次数请求序号:忽略过期响应,避免卸载/重挂竞态把 watchCount 盖成 0 */
let freeCountReqSeq = 0;
const isFreeCountRequestCanceled = (err: unknown) => {
if (!err || typeof err !== 'object') return false;
const e = err as { code?: string; name?: string; message?: string };
return (
e.code === 'ERR_CANCELED' ||
e.name === 'CanceledError' ||
e.name === 'AbortError' ||
/cancel/i.test(String(e.message || ''))
);
};
/**
* @description 获取用户免费观看次数
*/
const getFreeWatchCount = () => {
const reqSeq = ++freeCountReqSeq;
VideoApi.getPlayCount(videoInfo.value.id)
.then((userCount) => {
if (reqSeq !== freeCountReqSeq) return;
// 必须抛接口原值:finally 里读 ref 在取消/竞态时可能仍是初始 { watchCount: 0 }
userFreeCount.value = userCount;
setSharedFreeWatchCount({
watchCount: Number(userCount?.watchCount),
isCan: userCount?.isCan,
videoInfo: videoInfo.value,
});
emit('getFreeCount', userCount);
})
.catch((err) => {
if (reqSeq !== freeCountReqSeq) return;
// 取消不兜底 0,避免 Network 已返回 3 却被后续 cancel 覆盖
if (isFreeCountRequestCanceled(err)) return;
// 请求失败按无免费次数兜底,保证父组件仍能展示跳过预览入口
emit('getFreeCount', { isCan: false, watchCount: 0 });
})
.finally(() => {
if (reqSeq !== freeCountReqSeq) return;
// 无论成功失败都初始化播放器,避免接口异常导致无法播放
initVideo();
});
};
const playing = () => {
if (!state.playTimer) {
state.playTimer = setInterval(() => {
state.playTime++;
}, 1000);
}
};
/**
* @description 获取播放链接
*/
const getPath = () => {
const { baseUrl } = useConfigStore();
return `${baseUrl}/api/app/vid/h5/m3u8/${videoUrl.value}?token=${getToken()}&c=${globalStore.videoRoadLine.url}`;
};
/** 当前视频是否需要试看时长限制(不依赖 video 元素) */
const isTrialPlaybackLimited = () => {
if (isPreview.value) return true;
if (videoInfo.value.vidStatus.hasPaid === true) return false;
if (videoInfo.value.freeArea === true) return false;
if (isVip && ((userInfo.vipLevel >= 2 && !props.hasDarkWebTag) || (userInfo.vipLevel == 4 && props.hasDarkWebTag))) {
return false;
}
if (videoInfo.value.originCoins != 0 && videoInfo.value.coins == 0) return false;
if (videoInfo.value.originCoins === 0 && videoInfo.value.freeArea === false && isVip) return false;
if (userInfo.uid === videoInfo.value.publisher.uid) return false;
if (userFreeCount.value.isCan === true && videoInfo.value.originCoins === 0) return false;
if (
useGlobalStore().presaleObj.status != 0 &&
globalStore.advanceStatus.privilegeLimit.hasLimit &&
useGlobalStore().preSaleCoinVideoPlay
) {
return false;
}
return true;
};
const TRIAL_SEEK_EPSILON = 0.05;
const getTrialMinTime = () => (previewStartFlag.value && !isPreview.value ? videoInfo.value.previewStart || 0 : 0);
/** 后端试看上限(秒):freeTime,有 previewStart 时为 previewStart + freeTime;预览片为 duration-3 */
const getBackendTrialLimitSeconds = (): number | null => {
if (!isTrialPlaybackLimited()) return null;
if (isPreview.value) {
const duration = Number(player?.video?.duration) || 0;
if (!duration) return null;
return Math.max(0, duration - 3);
}
let limit = videoInfo.value.freeTime ?? 0;
if (previewStartFlag.value) {
limit += videoInfo.value.previewStart ?? 0;
}
return limit;
};
/** 与 playCheck 一致:currentTime 须小于该值才可继续播放 */
const getTrialMaxTime = (): number | null => {
if (!player?.video) return null;
return getBackendTrialLimitSeconds();
};
const isSeekTimeBeyondTrialLimit = (time: number) => {
const limit = getBackendTrialLimitSeconds();
if (limit == null) return false;
return time >= limit - TRIAL_SEEK_EPSILON;
};
const isSeekTimeBeforeTrialMin = (time: number) => {
const min = getTrialMinTime();
return min > 0 && time < min - TRIAL_SEEK_EPSILON;
};
const needsTrialSeekLimit = () => isTrialPlaybackLimited();
const setPlayerCurrentTime = (time: number) => {
if (!player?.video) return;
try {
player.currentTime = time;
} catch {
try {
player.video.currentTime = time;
} catch {
/* ignore */
}
}
syncXgplayerSeekUi(player, time);
};
const setTrialFrameBlocked = (blocked: boolean) => {
trialSeekBlocking.value = blocked;
setTrialVideoFrameHidden(player, blocked);
};
/** 试看结束弹窗需要透出暂停画面,拖动防闪帧遮罩需在弹窗出现后撤掉 */
const revealTrialFrameForMask = () => {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
setTrialFrameBlocked(false);
});
});
};
const clearTrialSeekBlock = () => {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
if (!player?.video) return;
if (getTrialMaxTime() == null) {
setTrialFrameBlocked(false);
return;
}
if (
!isSeekTimeBeyondTrialLimit(player.video.currentTime) &&
!isSeekTimeBeforeTrialMin(player.video.currentTime)
) {
setTrialFrameBlocked(false);
}
});
});
};
const handleTrialSeekOverflow = (beyondMax: boolean): boolean => {
const limit = getBackendTrialLimitSeconds();
if (limit == null || !player?.video) return false;
const min = getTrialMinTime();
const snapTime = beyondMax ? Math.max(min, limit - TRIAL_SEEK_EPSILON) : min;
setTrialFrameBlocked(true);
player.pause();
setPlayerCurrentTime(snapTime);
player.config.disableProgress = true;
// 进度条松手后 xgplayer 还会改一次 DOM,延迟再同步时间与进度
requestAnimationFrame(() => syncXgplayerSeekUi(player, snapTime));
if (beyondMax) {
trialLimitHandled = true;
revealTrialFrameForMask();
emitTrialUnlockPopup();
} else {
revealTrialFrameForMask();
}
return true;
};
/** @returns 是否发生试看越界并已回弹 */
const clampTrialSeek = (): boolean => {
if (!player?.video) return false;
const t = player.video.currentTime;
if (isSeekTimeBeyondTrialLimit(t)) return handleTrialSeekOverflow(true);
if (isSeekTimeBeforeTrialMin(t)) return handleTrialSeekOverflow(false);
return false;
};
const bindTrialProgressGuard = () => {
unbindTrialProgressGuard?.();
unbindTrialProgressGuard = bindXgTrialProgressGuard({
getPlayer: () => player,
needsTrialSeekLimit,
getBackendTrialLimitSeconds,
getTrialMinTime,
isSeekTimeBeyondTrialLimit,
handleTrialSeekOverflow,
setPlayerCurrentTime,
setTrialFrameBlocked,
clearTrialSeekBlock,
trialSeekEpsilon: TRIAL_SEEK_EPSILON,
maskOnDragStart: () => isPreview.value,
});
};
/**
* @description 检测视频是否可以播放
*/
const playCheck = () => {
let freeTime = videoInfo.value.freeTime || 0;
if (isPreview.value) {
if (player.video.currentTime < player.video.duration - 3) {
player.config.disableProgress = false;
return true;
} else {
player.config.disableProgress = true;
return false;
}
} else {
//视频已购买
if (videoInfo.value.vidStatus.hasPaid === true) {
player.config.disableProgress = false;
state.hideProgress = false;
return true;
}
//免费视频
if (videoInfo.value.freeArea === true) {
state.hideProgress = false;
return true;
}
// vip>2可以直接观看
if (
isVip &&
((userInfo.vipLevel >= 2 && !props.hasDarkWebTag) || (userInfo.vipLevel == 4 && props.hasDarkWebTag))
) {
player.config.disableProgress = false;
state.hideProgress = false;
return true;
}
//金币视频免费看权限
if (videoInfo.value.originCoins != 0 && videoInfo.value.coins == 0) {
player.config.disableProgress = false;
state.hideProgress = false;
return true;
}
// 会员视频(金币为0,freeArea = false)
if (videoInfo.value.originCoins === 0 && videoInfo.value.freeArea === false && isVip) {
state.hideProgress = false;
player.config.disableProgress = false;
return true;
}
//自己发布的视频
if (userInfo.uid === videoInfo.value.publisher.uid) {
state.hideProgress = false;
player.config.disableProgress = false;
return true;
}
// VIP免费次数
if (userFreeCount.value.isCan === true && videoInfo.value.originCoins === 0) {
state.hideProgress = false;
player.config.disableProgress = false;
return true;
}
//免费时长
if (previewStartFlag.value) {
freeTime += videoInfo.value.previewStart;
if (player.video.currentTime < videoInfo.value.previewStart) {
player.config.disableProgress = true;
return false;
}
if (player.video.currentTime < freeTime) {
player.config.disableProgress = false;
return true;
}
} else {
if (player.video.currentTime < freeTime) {
state.hideProgress = false;
player.config.disableProgress = false;
return true;
}
}
// 预售金币视频免费次数能否观看
if (useGlobalStore().presaleObj.status != 0 && globalStore.advanceStatus.privilegeLimit.hasLimit) {
if (useGlobalStore().preSaleCoinVideoPlay) {
state.hideProgress = false;
return true;
}
player.config.disableProgress = true;
clearInterval(state.playTimer);
state.playTimer = '';
return false;
}
player.config.disableProgress = true;
clearInterval(state.playTimer);
state.playTimer = '';
return false;
}
};
//视频购买成功
function success() {
videoInfo.value.vidStatus.hasPaid = true;
player.config.disableProgress = false;
if (isPreview.value) {
forceH264 = false;
destroyPlayer(true);
initVideo();
// checkPreview();
// player.src = videoUrl.value;
}
player.play();
}
/**
* @description 获取token
*/
const getToken = () => {
const { token } = useUserStore();
return token;
};
onDeactivated(() => {
pageActive.value = false;
destroyPlayer(true);
});
onActivated(() => {
pageActive.value = true;
if (!player && videoUrl.value) {
initVideo();
}
});
onBeforeUnmount(() => {
pageActive.value = false;
if (player?.video) {
addVideoHistory(props.videoInfo, HistoryVideoType.VIDEO, Math.round(player.video.currentTime));
}
destroyPlayer();
});
defineExpose({
success,
pause,
play: () => {
if (!player && videoUrl.value) initVideo();
return player?.play?.();
},
});
</script>
<style lang="scss" scoped>
// @media (min-height: 700px) {
// .vertical_video_warp {
// padding-top: 18px;
// }
// }
.vertical_video_warp {
width: 100%;
height: 100%;
display: flex;
overflow: hidden;
background: #000000;
align-items: center;
position: relative;
.trial-seek-veil {
position: absolute;
inset: 0;
z-index: 4;
background: #000;
pointer-events: none;
}
:deep(.xgplayer-progress-played) {
background: #fff;
}
:deep(.xgplayer-progress) {
top: 9px;
}
// :deep(.xgplayer-start),
:deep(.xgplayer-controls) {
// transform: translate3d(0, 0, 0);
// -webkit-transform: translate3d(0, 0, 0);
z-index: 3;
background-image: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, #000 100%);
}
// :deep(.xgplayer-progress-btn) {
// display: none !important;
// }
:deep(.xgplayer-progress) {
left: 50%;
right: 0;
width: 54%;
transform: translate(-61%, 0);
}
:deep(.xgplayer-time) {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
span::after {
content: '';
display: none !important;
}
}
// :deep(.xgplayer-time-current::after) {
// content: '';
// display: none;
// }
.video-shell {
position: relative;
z-index: 1;
width: 100%;
height: 100% !important;
top: 0;
left: 0;
flex: 1;
min-height: 0;
:deep(video),
:deep(.xgplayer) {
width: 100%;
height: 100% !important;
}
:deep(.xgplayer-enter-spinner) {
width: 44px;
height: 44px;
}
}
.avoid_sliding {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 44px;
z-index: 2;
background: transparent;
}
&[data-hide-progress='true'] {
:deep(.xgplayer-progress),
:deep(.xgplayer-time) {
display: none;
}
}
}
</style>