Переглянути джерело

优化视频播放器和视频处理组件,增强封面和视频URL变化的处理逻辑,添加中央播放按钮以提升用户体验,确保在路由变化时正确重置视频状态。

wuyi 2 місяців тому
батько
коміт
587ed1b45c
2 змінених файлів з 92 додано та 25 видалено
  1. 27 5
      src/components/VideoProcessor.vue
  2. 65 20
      src/views/VideoPlayer.vue

+ 27 - 5
src/components/VideoProcessor.vue

@@ -1061,8 +1061,19 @@ const showVideoError = (errorCode?: number, errorMessage?: string): void => {
 // 监听 props 变化
 watch(
   () => props.coverUrl,
-  (newUrl) => {
-    if (newUrl) {
+  (newUrl, oldUrl) => {
+    // 只有当URL真正变化时才处理
+    if (newUrl && newUrl !== oldUrl) {
+      console.log("封面URL变化,重新处理封面", { newUrl, oldUrl });
+      // 清理旧的封面URL(如果是blob)
+      if (processedCoverUrl.value?.startsWith("blob:")) {
+        URL.revokeObjectURL(processedCoverUrl.value);
+        processedCoverUrl.value = "";
+      }
+      // 处理新封面
+      processCover(newUrl);
+    } else if (newUrl) {
+      // 初始加载
       processCover(newUrl);
     }
   },
@@ -1074,10 +1085,21 @@ watch(
   (newUrl, oldUrl) => {
     // 如果URL发生变化,先停止当前播放
     if (oldUrl && newUrl !== oldUrl) {
+      console.log("视频URL变化,停止当前播放并加载新视频", { newUrl, oldUrl });
+      // 停止旧视频
       stopVideo();
-    }
-
-    if (newUrl) {
+      // 重置状态
+      isPlaying.value = false;
+      currentTime.value = 0;
+      duration.value = 0;
+      progress.value = 0;
+      buffered.value = 0;
+      // 处理新视频
+      if (newUrl) {
+        processVideo(newUrl);
+      }
+    } else if (newUrl) {
+      // 初始加载
       processVideo(newUrl);
     }
   },

+ 65 - 20
src/views/VideoPlayer.vue

@@ -77,6 +77,21 @@
           @canplay="onVideoCanPlay"
         />
 
+        <!-- iOS兼容的中央播放按钮(当有封面但没有开始播放时显示) -->
+        <div
+          v-if="videoInfo.cover && !isVideoMode"
+          class="absolute inset-0 flex items-center justify-center z-10 cursor-pointer"
+          @click="startPlayVideo"
+        >
+          <div
+            class="w-20 h-20 bg-black/60 rounded-full flex items-center justify-center"
+          >
+            <svg class="w-12 h-12" fill="white" viewBox="0 0 24 24">
+              <path d="M8 5v14l11-7z" />
+            </svg>
+          </div>
+        </div>
+
         <!-- 快进加载动画 -->
         <div
           v-if="isVideoSeeking"
@@ -818,6 +833,9 @@ const onLoginSuccess = async () => {
 };
 
 const onVideoPlay = () => {
+  // 标记为视频模式
+  isVideoMode.value = true;
+
   // 如果是guest或free用户且还没有开始试看,且未购买当前视频,则开始试看
   if (isGuestOrFree.value && !isTrialMode.value && !isSinglePurchased.value) {
     startTrial();
@@ -900,7 +918,26 @@ const goBack = () => {
   router.push("/");
 };
 
-// 播放视频
+// 视频模式状态
+const isVideoMode = ref(false);
+
+// 开始播放当前视频
+const startPlayVideo = () => {
+  // 标记为视频模式
+  isVideoMode.value = true;
+
+  // 如果视频元素存在,尝试播放
+  if (videoProcessorRef.value) {
+    const videoEl = videoProcessorRef.value.$el.querySelector("video");
+    if (videoEl) {
+      videoEl.play().catch((err: Error) => {
+        console.error("自动播放失败:", err);
+      });
+    }
+  }
+};
+
+// 播放推荐视频
 const playVideo = (video: any) => {
   const vipLevel = userStore.getVipLevel();
 
@@ -909,9 +946,14 @@ const playVideo = (video: any) => {
     videoProcessorRef.value.stopVideo();
   }
 
+  // 强制重置视频模式状态
+  isVideoMode.value = false;
+  isTrialMode.value = false;
+
+  // 确保使用replace而不是push,避免浏览器历史堆积
   if (vipLevel === VipLevel.GUEST || vipLevel === VipLevel.FREE) {
     // guest和free用户通过URL参数传递cover和m3u8,同时包含视频ID
-    router.push({
+    router.replace({
       name: "VideoPlayer",
       params: { id: video.id },
       query: {
@@ -921,13 +963,17 @@ const playVideo = (video: any) => {
         duration: video.duration,
         view: video.view,
         like: video.like,
+        _t: Date.now(), // 添加时间戳确保查询参数变化
       },
     });
   } else {
     // 其他VIP用户正常调用详情接口
-    router.push({
+    router.replace({
       name: "VideoPlayer",
       params: { id: video.id },
+      query: {
+        _t: Date.now(), // 添加时间戳确保查询参数变化
+      },
     });
   }
 
@@ -941,6 +987,7 @@ const playVideo = (video: any) => {
 // 开始试看
 const startTrial = () => {
   isTrialMode.value = true;
+  isVideoMode.value = true;
 };
 
 // 处理顶部会员购买按钮点击
@@ -1387,32 +1434,30 @@ const loadRelatedVideos = async () => {
 
 // 监听路由参数变化
 watch(
-  () => route.params.id,
-  async (newId, oldId) => {
-    if (newId && newId !== oldId) {
+  [() => route.params.id, () => route.query],
+  async ([newId, newQuery], [oldId, oldQuery]) => {
+    // 检查路由是否真的变化了
+    const idChanged = newId !== oldId;
+    const queryChanged = JSON.stringify(newQuery) !== JSON.stringify(oldQuery);
+
+    if (idChanged || queryChanged) {
+      console.log("路由变化,重新加载视频", { idChanged, queryChanged });
+
       // 停止当前视频播放
       if (videoProcessorRef.value) {
         videoProcessorRef.value.stopVideo();
       }
 
-      // 重置购买状态
+      // 重置购买状态和视频模式
       isSinglePurchased.value = false;
-      await loadVideoInfo();
-      await loadRelatedVideos();
+      isVideoMode.value = false;
+      isTrialMode.value = false;
 
-      // 检查购买状态(首页点击进入视频时的检查)
-      const vipLevel = userStore.getVipLevel();
-      if (
-        vipLevel !== VipLevel.GUEST &&
-        videoInfo.value.id &&
-        videoInfo.value.id !== "unknown"
-      ) {
-        await checkVideoPurchaseStatus();
-      }
-    } else if (oldId === undefined && newId) {
+      // 重置视频信息和处理封面
       await loadVideoInfo();
       await loadRelatedVideos();
 
+      // 检查购买状态(首页点击进入视频时的检查)
       const vipLevel = userStore.getVipLevel();
       if (
         vipLevel !== VipLevel.GUEST &&
@@ -1423,7 +1468,7 @@ watch(
       }
     }
   },
-  { immediate: true }
+  { immediate: true, deep: true }
 );
 
 onMounted(async () => {