|
|
@@ -22,13 +22,12 @@
|
|
|
<span class="text-sm">返回</span>
|
|
|
</button>
|
|
|
|
|
|
- <div class="flex items-center gap-2">
|
|
|
+ <div class="relative">
|
|
|
<button
|
|
|
- @click="toggleFavorite"
|
|
|
+ @click="toggleShare"
|
|
|
class="p-2 rounded-lg bg-white/5 border border-white/10 text-white/70 hover:bg-white/10 hover:text-white transition"
|
|
|
>
|
|
|
<svg
|
|
|
- v-if="!isFavorite"
|
|
|
class="w-5 h-5"
|
|
|
fill="none"
|
|
|
stroke="currentColor"
|
|
|
@@ -38,55 +37,75 @@
|
|
|
stroke-linecap="round"
|
|
|
stroke-linejoin="round"
|
|
|
stroke-width="2"
|
|
|
- d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
|
|
|
- />
|
|
|
- </svg>
|
|
|
- <svg
|
|
|
- v-else
|
|
|
- class="w-5 h-5 fill-red-500 text-red-500"
|
|
|
- viewBox="0 0 24 24"
|
|
|
- >
|
|
|
- <path
|
|
|
- d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"
|
|
|
+ d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.367 2.684 3 3 0 00-5.367-2.684z"
|
|
|
/>
|
|
|
</svg>
|
|
|
</button>
|
|
|
|
|
|
- <button
|
|
|
- @click="toggleShare"
|
|
|
- class="p-2 rounded-lg bg-white/5 border border-white/10 text-white/70 hover:bg-white/10 hover:text-white transition"
|
|
|
+ <!-- 分享提示弹窗 -->
|
|
|
+ <div
|
|
|
+ v-if="showShareModal"
|
|
|
+ class="absolute top-0 left-full ml-2 z-50 bg-emerald-500 text-white px-3 py-1.5 rounded-lg shadow-lg flex items-center gap-1.5 whitespace-nowrap"
|
|
|
>
|
|
|
- <svg
|
|
|
- class="w-5 h-5"
|
|
|
- fill="none"
|
|
|
- stroke="currentColor"
|
|
|
- viewBox="0 0 24 24"
|
|
|
- >
|
|
|
+ <svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
|
|
<path
|
|
|
- stroke-linecap="round"
|
|
|
- stroke-linejoin="round"
|
|
|
- stroke-width="2"
|
|
|
- d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.367 2.684 3 3 0 00-5.367-2.684z"
|
|
|
+ fill-rule="evenodd"
|
|
|
+ d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
|
|
+ clip-rule="evenodd"
|
|
|
/>
|
|
|
</svg>
|
|
|
- </button>
|
|
|
+ <span class="text-xs font-medium">已复制</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 视频播放器区域 -->
|
|
|
<div class="relative rounded-2xl overflow-hidden bg-black">
|
|
|
- <div class="aspect-video">
|
|
|
+ <div class="aspect-video video-container">
|
|
|
<video
|
|
|
ref="videoPlayer"
|
|
|
- :src="videoInfo.m3u8"
|
|
|
:poster="videoInfo.cover"
|
|
|
class="w-full h-full object-contain"
|
|
|
controls
|
|
|
preload="metadata"
|
|
|
playsinline
|
|
|
+ webkit-playsinline
|
|
|
+ x5-playsinline
|
|
|
+ x5-video-player-type="h5"
|
|
|
+ x5-video-player-fullscreen="true"
|
|
|
+ x5-video-orientation="landscape"
|
|
|
+ @loadstart="onVideoLoadStart"
|
|
|
+ @loadeddata="onVideoLoadedData"
|
|
|
+ @error="onVideoError"
|
|
|
+ @canplay="onVideoCanPlay"
|
|
|
>
|
|
|
您的浏览器不支持视频播放
|
|
|
</video>
|
|
|
+
|
|
|
+ <!-- 视频错误提示 -->
|
|
|
+ <div
|
|
|
+ v-if="videoError"
|
|
|
+ class="absolute inset-0 flex items-center justify-center bg-black/80 text-white"
|
|
|
+ >
|
|
|
+ <div class="text-center p-6">
|
|
|
+ <div class="text-4xl mb-4">⚠️</div>
|
|
|
+ <h3 class="text-lg font-semibold mb-2">视频加载失败</h3>
|
|
|
+ <p class="text-sm text-white/70 mb-4">{{ videoError }}</p>
|
|
|
+ <button
|
|
|
+ v-if="showRetryButton"
|
|
|
+ @click="retryVideoLoad"
|
|
|
+ :disabled="!canRetry"
|
|
|
+ class="px-4 py-2 rounded-lg transition"
|
|
|
+ :class="
|
|
|
+ canRetry
|
|
|
+ ? 'bg-emerald-500 text-white hover:bg-emerald-600'
|
|
|
+ : 'bg-gray-500 text-gray-300 cursor-not-allowed'
|
|
|
+ "
|
|
|
+ >
|
|
|
+ 重试
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -175,9 +194,9 @@
|
|
|
<!-- 相关推荐 -->
|
|
|
<div v-if="relatedVideos.length > 0" class="space-y-4">
|
|
|
<h3 class="text-sm font-medium text-white/80">相关推荐</h3>
|
|
|
- <div class="grid grid-cols-3 gap-2">
|
|
|
+ <div class="grid grid-cols-2 md:grid-cols-3 gap-2">
|
|
|
<article
|
|
|
- v-for="video in relatedVideos"
|
|
|
+ v-for="video in relatedVideos.slice(0, 15)"
|
|
|
:key="video.id"
|
|
|
@click="playVideo(video)"
|
|
|
class="group rounded-xl overflow-hidden bg-white/5 border border-white/10 cursor-pointer hover:bg-white/10 transition"
|
|
|
@@ -197,7 +216,7 @@
|
|
|
</div>
|
|
|
<div class="p-2">
|
|
|
<h4
|
|
|
- class="text-xs font-medium text-white/90 truncate leading-tight"
|
|
|
+ class="text-xs font-medium text-white/90 leading-tight line-clamp-2"
|
|
|
>
|
|
|
{{ video.name }}
|
|
|
</h4>
|
|
|
@@ -209,73 +228,14 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
-
|
|
|
- <!-- 分享弹窗 -->
|
|
|
- <div
|
|
|
- v-if="showShareModal"
|
|
|
- class="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
|
|
|
- @click="closeShareModal"
|
|
|
- >
|
|
|
- <div
|
|
|
- class="bg-surface rounded-2xl p-6 max-w-sm w-full mx-4 border border-white/10"
|
|
|
- @click.stop
|
|
|
- >
|
|
|
- <h3 class="text-lg font-semibold text-white mb-4">分享视频</h3>
|
|
|
- <div class="space-y-3">
|
|
|
- <button
|
|
|
- @click="copyVideoLink"
|
|
|
- class="w-full flex items-center gap-3 p-3 rounded-xl bg-white/5 border border-white/10 text-white/80 hover:bg-white/10 transition"
|
|
|
- >
|
|
|
- <svg
|
|
|
- class="w-5 h-5"
|
|
|
- fill="none"
|
|
|
- stroke="currentColor"
|
|
|
- viewBox="0 0 24 24"
|
|
|
- >
|
|
|
- <path
|
|
|
- stroke-linecap="round"
|
|
|
- stroke-linejoin="round"
|
|
|
- stroke-width="2"
|
|
|
- d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
|
|
|
- />
|
|
|
- </svg>
|
|
|
- <span>复制链接</span>
|
|
|
- </button>
|
|
|
- <button
|
|
|
- @click="shareToSocial"
|
|
|
- class="w-full flex items-center gap-3 p-3 rounded-xl bg-white/5 border border-white/10 text-white/80 hover:bg-white/10 transition"
|
|
|
- >
|
|
|
- <svg
|
|
|
- class="w-5 h-5"
|
|
|
- fill="none"
|
|
|
- stroke="currentColor"
|
|
|
- viewBox="0 0 24 24"
|
|
|
- >
|
|
|
- <path
|
|
|
- stroke-linecap="round"
|
|
|
- stroke-linejoin="round"
|
|
|
- stroke-width="2"
|
|
|
- d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.367 2.684 3 3 0 00-5.367-2.684z"
|
|
|
- />
|
|
|
- </svg>
|
|
|
- <span>分享到社交平台</span>
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- <button
|
|
|
- @click="closeShareModal"
|
|
|
- class="w-full mt-4 py-2 text-white/60 hover:text-white transition"
|
|
|
- >
|
|
|
- 取消
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
</section>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, onMounted } from "vue";
|
|
|
+import { ref, onMounted, onUnmounted, computed } from "vue";
|
|
|
import { useRoute, useRouter } from "vue-router";
|
|
|
import { searchVideoByTags } from "@/services/api";
|
|
|
+import Hls from "hls.js";
|
|
|
|
|
|
// 路由相关
|
|
|
const route = useRoute();
|
|
|
@@ -284,6 +244,9 @@ const router = useRouter();
|
|
|
// 视频播放器引用
|
|
|
const videoPlayer = ref<HTMLVideoElement>();
|
|
|
|
|
|
+// HLS实例
|
|
|
+let hls: Hls | null = null;
|
|
|
+
|
|
|
// 视频信息
|
|
|
const videoInfo = ref<any>({
|
|
|
id: "",
|
|
|
@@ -301,8 +264,13 @@ const videoInfo = ref<any>({
|
|
|
const relatedVideos = ref<any[]>([]);
|
|
|
|
|
|
// 状态管理
|
|
|
-const isFavorite = ref(false);
|
|
|
const showShareModal = ref(false);
|
|
|
+const videoError = ref<string>("");
|
|
|
+const retryCount = ref(0);
|
|
|
+const maxRetries = 3;
|
|
|
+const lastRetryTime = ref(0);
|
|
|
+const retryCooldown = 3000; // 3秒冷却时间
|
|
|
+const forceUpdate = ref(0); // 强制更新触发器
|
|
|
|
|
|
// 生成设备标识
|
|
|
const generateMacAddress = (): string => {
|
|
|
@@ -318,6 +286,19 @@ const generateMacAddress = (): string => {
|
|
|
|
|
|
const device = generateMacAddress();
|
|
|
|
|
|
+// 计算属性
|
|
|
+const canRetry = computed(() => {
|
|
|
+ forceUpdate.value; // 依赖forceUpdate来触发重新计算
|
|
|
+ const now = Date.now();
|
|
|
+ const isCooldownActive = now - lastRetryTime.value < retryCooldown;
|
|
|
+ const hasRetriesLeft = retryCount.value < maxRetries;
|
|
|
+ return !isCooldownActive && hasRetriesLeft;
|
|
|
+});
|
|
|
+
|
|
|
+const showRetryButton = computed(() => {
|
|
|
+ return retryCount.value < maxRetries;
|
|
|
+});
|
|
|
+
|
|
|
// 格式化时长
|
|
|
const formatDuration = (duration: string | number): string => {
|
|
|
const seconds = parseInt(String(duration));
|
|
|
@@ -342,45 +323,233 @@ const handleImageError = (event: Event) => {
|
|
|
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgdmlld0JveD0iMCAwIDIwMCAyMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIiBmaWxsPSIjMzMzIi8+CjxwYXRoIGQ9Ik0xMDAgNzVMMTI1IDEwMEgxMDBWMTI1SDc1TDEwMCAxMDBaIiBmaWxsPSIjNjY2Ii8+Cjwvc3ZnPgo=";
|
|
|
};
|
|
|
|
|
|
-// 返回上一页
|
|
|
-const goBack = () => {
|
|
|
- router.back();
|
|
|
+// 视频播放器事件处理
|
|
|
+const onVideoLoadStart = () => {
|
|
|
+ console.log("视频开始加载...");
|
|
|
};
|
|
|
|
|
|
-// 切换收藏状态
|
|
|
-const toggleFavorite = () => {
|
|
|
- isFavorite.value = !isFavorite.value;
|
|
|
- // TODO: 实现收藏功能
|
|
|
+const onVideoLoadedData = () => {
|
|
|
+ console.log("视频数据加载完成");
|
|
|
};
|
|
|
|
|
|
-// 切换分享弹窗
|
|
|
-const toggleShare = () => {
|
|
|
- showShareModal.value = true;
|
|
|
+const onVideoCanPlay = () => {
|
|
|
+ console.log("视频可以播放");
|
|
|
};
|
|
|
|
|
|
-// 关闭分享弹窗
|
|
|
-const closeShareModal = () => {
|
|
|
- showShareModal.value = false;
|
|
|
+const onVideoError = (event: Event) => {
|
|
|
+ const video = event.target as HTMLVideoElement;
|
|
|
+ console.error("视频播放错误:", video.error);
|
|
|
+ console.error("错误详情:", {
|
|
|
+ code: video.error?.code,
|
|
|
+ message: video.error?.message,
|
|
|
+ networkState: video.networkState,
|
|
|
+ readyState: video.readyState,
|
|
|
+ });
|
|
|
+
|
|
|
+ // 设置统一的错误信息
|
|
|
+ videoError.value = "视频加载失败";
|
|
|
+};
|
|
|
+
|
|
|
+// 重试视频加载
|
|
|
+const retryVideoLoad = () => {
|
|
|
+ const now = Date.now();
|
|
|
+
|
|
|
+ // 检查是否在冷却时间内
|
|
|
+ if (now - lastRetryTime.value < retryCooldown) {
|
|
|
+ const remainingTime = Math.ceil(
|
|
|
+ (retryCooldown - (now - lastRetryTime.value)) / 1000
|
|
|
+ );
|
|
|
+ console.log(`重试冷却中,还需等待 ${remainingTime} 秒`);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否超过最大重试次数
|
|
|
+ if (retryCount.value >= maxRetries) {
|
|
|
+ console.log("已达到最大重试次数");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新重试状态
|
|
|
+ retryCount.value++;
|
|
|
+ lastRetryTime.value = now;
|
|
|
+
|
|
|
+ console.log(`第 ${retryCount.value} 次重试`);
|
|
|
+
|
|
|
+ videoError.value = "";
|
|
|
+ destroyHls();
|
|
|
+
|
|
|
+ // 延迟重新初始化
|
|
|
+ setTimeout(() => {
|
|
|
+ initHlsPlayer();
|
|
|
+ }, 500);
|
|
|
};
|
|
|
|
|
|
-// 复制视频链接
|
|
|
-const copyVideoLink = async () => {
|
|
|
+// 初始化HLS播放器
|
|
|
+const initHlsPlayer = () => {
|
|
|
+ if (!videoPlayer.value) return;
|
|
|
+
|
|
|
+ const video = videoPlayer.value;
|
|
|
+ const videoSrc = videoInfo.value.m3u8;
|
|
|
+
|
|
|
+ if (!videoSrc) {
|
|
|
+ console.error("没有视频源地址");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // console.log("初始化HLS播放器,视频源:", videoSrc);
|
|
|
+ console.log("初始化HLS播放器");
|
|
|
+
|
|
|
+ // 检查浏览器是否原生支持HLS
|
|
|
+ if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
|
+ // Safari原生支持HLS
|
|
|
+ console.log("使用原生HLS支持");
|
|
|
+ video.src = videoSrc;
|
|
|
+ } else if (Hls.isSupported()) {
|
|
|
+ // 使用HLS.js
|
|
|
+ console.log("使用HLS.js支持");
|
|
|
+
|
|
|
+ // 先检测URL是否为HLS格式
|
|
|
+ detectVideoFormat(videoSrc)
|
|
|
+ .then((format) => {
|
|
|
+ if (format === "hls") {
|
|
|
+ initHlsJsPlayer(videoSrc, video);
|
|
|
+ } else {
|
|
|
+ console.log("检测到非HLS格式,尝试直接播放");
|
|
|
+ video.src = videoSrc;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch((error) => {
|
|
|
+ console.error("格式检测失败,尝试直接播放:", error);
|
|
|
+ video.src = videoSrc;
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ console.error("浏览器不支持HLS播放");
|
|
|
+ video.src = videoSrc;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 检测视频格式
|
|
|
+const detectVideoFormat = async (url: string): Promise<"hls" | "direct"> => {
|
|
|
+ try {
|
|
|
+ const response = await fetch(url, {
|
|
|
+ method: "HEAD",
|
|
|
+ mode: "cors",
|
|
|
+ });
|
|
|
+
|
|
|
+ const contentType = response.headers.get("content-type") || "";
|
|
|
+ console.log("Content-Type:", contentType);
|
|
|
+
|
|
|
+ // 检查是否为HLS格式
|
|
|
+ if (
|
|
|
+ contentType.includes("application/vnd.apple.mpegurl") ||
|
|
|
+ contentType.includes("application/x-mpegURL") ||
|
|
|
+ url.includes(".m3u8")
|
|
|
+ ) {
|
|
|
+ return "hls";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否为直接视频文件
|
|
|
+ if (contentType.includes("video/")) {
|
|
|
+ return "direct";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果无法确定,尝试获取前几个字节来判断
|
|
|
+ const textResponse = await fetch(url, {
|
|
|
+ method: "GET",
|
|
|
+ mode: "cors",
|
|
|
+ headers: { Range: "bytes=0-1023" },
|
|
|
+ });
|
|
|
+ const text = await textResponse.text();
|
|
|
+
|
|
|
+ if (text.startsWith("#EXTM3U")) {
|
|
|
+ return "hls";
|
|
|
+ }
|
|
|
+
|
|
|
+ return "direct";
|
|
|
+ } catch (error) {
|
|
|
+ console.error("格式检测失败:", error);
|
|
|
+ return "direct";
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 初始化HLS.js播放器
|
|
|
+const initHlsJsPlayer = (videoSrc: string, video: HTMLVideoElement) => {
|
|
|
+ hls = new Hls({
|
|
|
+ debug: false, // 关闭调试日志
|
|
|
+ enableWorker: true,
|
|
|
+ lowLatencyMode: true,
|
|
|
+ });
|
|
|
+
|
|
|
+ hls.loadSource(videoSrc);
|
|
|
+ hls.attachMedia(video);
|
|
|
+
|
|
|
+ hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
|
|
+ console.log("HLS清单解析完成,可以播放");
|
|
|
+ });
|
|
|
+
|
|
|
+ hls.on(Hls.Events.ERROR, (event, data) => {
|
|
|
+ console.error("HLS错误:", data);
|
|
|
+ if (data.fatal) {
|
|
|
+ switch (data.type) {
|
|
|
+ case Hls.ErrorTypes.NETWORK_ERROR:
|
|
|
+ console.error("网络错误,尝试恢复...");
|
|
|
+ if (data.details === "manifestParsingError") {
|
|
|
+ videoError.value = "视频加载失败";
|
|
|
+ // 销毁HLS实例,尝试直接播放
|
|
|
+ hls?.destroy();
|
|
|
+ hls = null;
|
|
|
+ video.src = videoSrc;
|
|
|
+ } else {
|
|
|
+ hls?.startLoad();
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case Hls.ErrorTypes.MEDIA_ERROR:
|
|
|
+ console.error("媒体错误,尝试恢复...");
|
|
|
+ hls?.recoverMediaError();
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ console.error("无法恢复的错误,尝试直接播放");
|
|
|
+ videoError.value = "视频加载失败";
|
|
|
+ // 销毁HLS实例,尝试直接播放
|
|
|
+ hls?.destroy();
|
|
|
+ hls = null;
|
|
|
+ video.src = videoSrc;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+// 清理HLS实例
|
|
|
+const destroyHls = () => {
|
|
|
+ if (hls) {
|
|
|
+ console.log("清理HLS实例");
|
|
|
+ hls.destroy();
|
|
|
+ hls = null;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 返回上一页
|
|
|
+const goBack = () => {
|
|
|
+ router.back();
|
|
|
+};
|
|
|
+
|
|
|
+// 分享视频(直接复制链接并显示提示)
|
|
|
+const toggleShare = async () => {
|
|
|
try {
|
|
|
const videoUrl = window.location.href;
|
|
|
await navigator.clipboard.writeText(videoUrl);
|
|
|
- // TODO: 显示复制成功提示
|
|
|
- closeShareModal();
|
|
|
+ showShareModal.value = true;
|
|
|
+
|
|
|
+ // 1秒后自动隐藏提示
|
|
|
+ setTimeout(() => {
|
|
|
+ showShareModal.value = false;
|
|
|
+ }, 1000);
|
|
|
} catch (error) {
|
|
|
console.error("复制链接失败:", error);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// 分享到社交平台
|
|
|
-const shareToSocial = () => {
|
|
|
- // TODO: 实现社交平台分享
|
|
|
- closeShareModal();
|
|
|
-};
|
|
|
-
|
|
|
// 播放视频
|
|
|
const playVideo = (video: any) => {
|
|
|
router.push({
|
|
|
@@ -412,7 +581,7 @@ const loadVideoInfo = () => {
|
|
|
id: videoId,
|
|
|
name: videoData.name || `视频 ${videoId}`,
|
|
|
cover: videoData.cover || "",
|
|
|
- m3u8: videoData.m3u8 || "",
|
|
|
+ m3u8: "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8",
|
|
|
duration: videoData.duration || 0,
|
|
|
view: videoData.view || 0,
|
|
|
like: videoData.like || 0,
|
|
|
@@ -422,6 +591,8 @@ const loadVideoInfo = () => {
|
|
|
|
|
|
// 设置页面标题
|
|
|
document.title = `${videoInfo.value.name} - 视频播放`;
|
|
|
+
|
|
|
+ // console.log("加载视频信息:", videoInfo.value);
|
|
|
} else {
|
|
|
console.error("未找到视频ID");
|
|
|
}
|
|
|
@@ -484,29 +655,73 @@ const loadRelatedVideos = async () => {
|
|
|
onMounted(() => {
|
|
|
loadVideoInfo();
|
|
|
loadRelatedVideos();
|
|
|
+
|
|
|
+ // 延迟初始化HLS播放器,确保DOM已渲染
|
|
|
+ setTimeout(() => {
|
|
|
+ initHlsPlayer();
|
|
|
+ }, 100);
|
|
|
+
|
|
|
+ // 启动定时器更新按钮状态
|
|
|
+ const timer = setInterval(() => {
|
|
|
+ if (videoError.value && retryCount.value < maxRetries) {
|
|
|
+ forceUpdate.value++; // 触发计算属性重新计算
|
|
|
+ }
|
|
|
+ }, 1000);
|
|
|
+
|
|
|
+ // 组件卸载时清除定时器
|
|
|
+ onUnmounted(() => {
|
|
|
+ clearInterval(timer);
|
|
|
+ });
|
|
|
+});
|
|
|
+
|
|
|
+onUnmounted(() => {
|
|
|
+ destroyHls();
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-/* 自定义视频播放器样式 */
|
|
|
-video::-webkit-media-controls-panel {
|
|
|
- background-color: rgba(0, 0, 0, 0.5);
|
|
|
+/* 视频容器样式 */
|
|
|
+.video-container {
|
|
|
+ position: relative;
|
|
|
}
|
|
|
|
|
|
-video::-webkit-media-controls-play-button,
|
|
|
-video::-webkit-media-controls-pause-button {
|
|
|
- background-color: rgba(255, 255, 255, 0.8);
|
|
|
- border-radius: 50%;
|
|
|
-}
|
|
|
+/* 移动端全屏样式 */
|
|
|
+@media screen and (max-width: 768px) {
|
|
|
+ video {
|
|
|
+ /* 强制横屏全屏 */
|
|
|
+ object-fit: contain;
|
|
|
+ }
|
|
|
|
|
|
-video::-webkit-media-controls-timeline {
|
|
|
- background-color: rgba(255, 255, 255, 0.3);
|
|
|
- border-radius: 2px;
|
|
|
-}
|
|
|
+ /* 全屏时的样式 */
|
|
|
+ video:fullscreen {
|
|
|
+ width: 100vw;
|
|
|
+ height: 100vh;
|
|
|
+ object-fit: contain;
|
|
|
+ background: black;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* WebKit全屏样式 */
|
|
|
+ video:-webkit-full-screen {
|
|
|
+ width: 100vw;
|
|
|
+ height: 100vh;
|
|
|
+ object-fit: contain;
|
|
|
+ background: black;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Mozilla全屏样式 */
|
|
|
+ video:-moz-full-screen {
|
|
|
+ width: 100vw;
|
|
|
+ height: 100vh;
|
|
|
+ object-fit: contain;
|
|
|
+ background: black;
|
|
|
+ }
|
|
|
|
|
|
-video::-webkit-media-controls-current-time-display,
|
|
|
-video::-webkit-media-controls-time-remaining-display {
|
|
|
- color: white;
|
|
|
- font-size: 12px;
|
|
|
+ /* MS全屏样式 */
|
|
|
+ video:-ms-fullscreen {
|
|
|
+ width: 100vw;
|
|
|
+ height: 100vh;
|
|
|
+ object-fit: contain;
|
|
|
+ background: black;
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|