[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
+225
View File
@@ -0,0 +1,225 @@
<template>
<div
class="vertical_video_warp"
:class="{
'vertical_video_warp--hide-progress': props.hideProgress,
'vertical_video_warp--hide-fullscreen': props.hideFullscreen,
'vertical_video_warp--hide-background': props.hideBackground,
}"
>
<div id="video">
<div class="avoid_sliding" @click="pause"></div>
</div>
</div>
</template>
<script lang="ts" setup>
import { onBeforeUnmount, onMounted } from 'vue';
import { useConfigStore } from '@/stores/config';
import { useUserStore } from '@/stores/user';
import HlsJSPlayer from 'xgplayer-hls.js';
import { bindH265PlaybackFallback, resolvePlayResourceKey } from '@/utils/h265Playback';
const props = defineProps<{
sourceURL: string;
/** H.265 资源 key;有值且设备支持时优先播放 */
h265Url?: string | null;
hideProgress?: boolean;
hideFullscreen?: boolean;
hideBackground?: boolean;
}>();
let player: any = null; //播放器实例
let playResourceKey = '';
let isPlayingH265 = false;
let forceH264 = false;
let pendingResumeAt = 0;
let unbindH265Fallback: (() => void) | null = null;
onMounted(() => {
//初始化播放器
initVideo();
});
/**
* @description 暂停
*/
const pause = () => {
player.pause();
};
const resolveCurrentResourceKey = () => {
const resolved = resolvePlayResourceKey({
h264Key: props.sourceURL,
h265Key: props.h265Url,
forceH264,
});
playResourceKey = resolved.resourceKey;
isPlayingH265 = resolved.isH265;
};
const destroyPlayer = () => {
unbindH265Fallback?.();
unbindH265Fallback = null;
if (!player) return;
try {
player.src = '';
player.destroy(true);
} catch {
/* ignore */
}
player = null;
};
const fallbackH265ToH264 = (resumeAt: number) => {
if (forceH264 || !isPlayingH265) return;
forceH264 = true;
isPlayingH265 = false;
pendingResumeAt = resumeAt;
destroyPlayer();
initVideo();
};
/**
* @description 初始化播放器
*/
const initVideo = () => {
if (player) destroyPlayer();
resolveCurrentResourceKey();
player = new HlsJSPlayer({
id: 'video',
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,
closeInactive: true,
enableVideoDbltouch: true,
});
unbindH265Fallback = bindH265PlaybackFallback(player, {
shouldFallback: () => isPlayingH265 && !forceH264,
onFallback: fallbackH265ToH264,
});
player.on('loadedmetadata', () => {
if (pendingResumeAt > 0) {
const resumeAt = pendingResumeAt;
pendingResumeAt = 0;
try {
player.currentTime = resumeAt;
} catch {
/* ignore */
}
}
});
};
/**
* @description 获取播放链接
*/
const getPath = () => {
const { baseUrl } = useConfigStore();
return `${baseUrl}/api/app/vid/h5/m3u8/${playResourceKey || props.sourceURL}?token=${getToken()}`;
};
/**
* @description 获取token
*/
const getToken = () => {
const { token } = useUserStore();
return token;
};
onBeforeUnmount(() => {
destroyPlayer();
});
</script>
<style lang="scss" scoped>
.vertical_video_warp {
width: 100%;
height: 100%;
display: flex;
overflow: hidden;
background: #000000;
align-items: center;
:deep(.xgplayer-progress-played) {
background: rgb(39, 213, 236);
}
:deep(.xgplayer-progress) {
top: 10px;
}
:deep(.xgplayer-start),
:deep(.xgplayer-controls) {
transform: translate3d(0, 0, 0);
-webkit-transform: translate3d(0, 0, 0);
}
:deep(.xgplayer-progress-btn) {
display: none !important;
}
:deep(.xgplayer-time-current:after) {
content: ' ';
}
:deep(.xgplayer-progress) {
left: 43%;
right: 0;
width: 60%;
transform: translate(-50%, 0);
}
:deep(.xgplayer-time) {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
// margin-top: 10px;
font-size: 11px;
}
:deep(.xgplayer-time-current)::after {
display: none;
}
&.vertical_video_warp--hide-progress {
:deep(.xgplayer-progress),
:deep(.xgplayer-time) {
display: none !important;
}
}
&.vertical_video_warp--hide-fullscreen {
:deep(.xgplayer-fullscreen) {
display: none !important;
}
}
&.vertical_video_warp--hide-background {
background: transparent;
}
// :deep(.xgplayer-icon-play) {
// > svg {
// height: 40px;
// width: 40px;
// }
// }
// :deep(.xgplayer-skin-default .xgplayer-start) {
// height: 40px;
// width: 40px;
// margin: -20px;
// }
#video {
width: 100%;
height: 100% !important;
position: absolute;
top: 0;
left: 0;
position: relative;
:deep(.xgplayer-enter-spinner) {
width: 50px;
height: 50px;
}
.avoid_sliding {
position: absolute;
width: 100%;
height: 100%;
background: transparent;
top: 0;
}
}
}
</style>