804 lines
23 KiB
Vue
804 lines
23 KiB
Vue
<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 { 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 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,
|
||
});
|
||
|
||
onMounted(() => {
|
||
getInfo();
|
||
//获取免费观看次数
|
||
getFreeWatchCount();
|
||
});
|
||
|
||
/**
|
||
* @description 暂停
|
||
*/
|
||
const pause = () => {
|
||
if (!player) return;
|
||
player.pause();
|
||
clearInterval(state.playTimer);
|
||
state.playTimer = '';
|
||
};
|
||
|
||
/** 点击画面切换播放/暂停 */
|
||
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 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;
|
||
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);
|
||
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');
|
||
});
|
||
|
||
/** 仅当明确回到试看区内才清标记;回弹到 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);
|
||
if (playCheck()) {
|
||
if (canResetTrialLimitHandled()) {
|
||
trialLimitHandled = false;
|
||
}
|
||
return;
|
||
}
|
||
handleTrialLimitReached();
|
||
});
|
||
player.on('seeked', () => {
|
||
if (!player.hasStart) return;
|
||
const clamped = clampTrialSeek();
|
||
clearTrialSeekBlock();
|
||
// 钳制后再补发进度:拖动松手时未必有 timeupdate
|
||
if (pageActive.value) {
|
||
emit('timeupdate', Number(player?.video?.currentTime) || 0);
|
||
}
|
||
if (clamped) return;
|
||
if (!playCheck()) {
|
||
handleTrialLimitReached();
|
||
}
|
||
});
|
||
player.on('seeking', () => {
|
||
if (!player?.hasStart) return;
|
||
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>
|