duidui_mini_program/pages/camp_detail/modules/video-controller.js
2026-03-27 10:41:46 +08:00

320 lines
9.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 视频控制模块
* 负责所有视频播放相关的逻辑
*/
import { campApi } from '../../../config/camp_api.js';
import { resolveVideoCompletionPercent } from './utils.js';
import * as dataFetcher from './data-fetcher.js';
/**
* 视频播放结束
* @param {Object} context - 页面上下文
*/
export function onVideoEnded(context) {
context.setData({
isVideoTaskPlaying: false
});
}
/**
* 视频播放错误
* @param {Object} context - 页面上下文
*/
export function onVideoError(context) {
context.setData({
showVideo: false,
isVideoTaskPlaying: false
});
wx.showToast({
title: '视频播放失败',
icon: 'none'
});
}
/**
* 视频开始播放
* 区分任务视频和介绍视频:只有视频任务才标记 isVideoTaskPlaying
* @param {Object} context - 页面上下文
*/
export function onVideoPlay(context) {
var isTaskVideo = !!context.data.currentPlayingTaskId;
context.setData({
hasVideoPlayed: true,
isAutoPlay: false,
_pendingVideoPlay: false,
isVideoTaskPlaying: isTaskVideo // 只有视频任务才标记为 true
});
}
/**
* 视频暂停
* @param {Object} context - 页面上下文
*/
export function onVideoPause(context) {
context.setData({
isVideoTaskPlaying: false
});
}
/**
* 监听视频播放进度 请求接口
* @param {Object} context - 页面上下文
* @param {Object} e - 事件对象
*/
export async function onVideoTimeUpdate(context, e) {
var currentVideoCompletionPercent = context.data.currentVideoCompletionPercent;
var hasLogged10Percent = context.data.hasLogged10Percent;
var currentPlayingTaskId = context.data.currentPlayingTaskId;
var currentSelectedCourseId = context.data.currentSelectedCourseId;
var currentSelectedCourseIndex = context.data.currentSelectedCourseIndex;
var requiredPercent = 100;
if (typeof currentVideoCompletionPercent === 'number' && !isNaN(currentVideoCompletionPercent) && currentVideoCompletionPercent > 0) {
requiredPercent = currentVideoCompletionPercent;
}
if (requiredPercent <= 0) {
requiredPercent = 100;
}
if (requiredPercent > 100) {
requiredPercent = 100;
}
if (requiredPercent > 0 && requiredPercent <= 1) {
requiredPercent = requiredPercent * 100;
}
var persent = requiredPercent / 100;
var { currentTime, duration } = e.detail;
// 检查是否已经发送过请求
if (hasLogged10Percent) {
return;
}
// 检查是否达到进度要求或视频结束
var isProgressReached = duration > 0 && currentTime / duration >= persent;
var isVideoEnded = duration > 0 && currentTime >= duration;
if ((isProgressReached || isVideoEnded) && currentPlayingTaskId) {
var userId = String(wx.getStorageSync('wxuserid') || '');
var taskId = String(currentPlayingTaskId || '');
if (!userId) {
return;
}
if (!taskId) {
return;
}
try {
var payload = {
user_id: userId,
task_id: taskId,
is_completed: true,
completed_at: String(Math.floor(Date.now() / 1000))
};
var res = await campApi.updateCampProgress(payload);
if ((res && res.success === true) || (res && res.code === 200)) {
// 先立即更新任务状态立即更新UI不中断视频播放
if (context.updateTaskStatus) {
context.updateTaskStatus(taskId, 'Completed');
}
// 视频任务进度已上报成功,静默请求一次营地详情接口,拉取最新任务列表与 can_start下一任务可解锁
var campId = context.data.campId;
if (campId && dataFetcher.fetchCampData) {
dataFetcher.fetchCampData(context, campId, true);
} else if (context.refreshTaskListStatus) {
context.refreshTaskListStatus(currentSelectedCourseId, currentSelectedCourseIndex, true);
}
}
} catch (error) {
}
// 标记已发送请求
context.setData({ hasLogged10Percent: true });
}
}
/**
* 处理视频任务
* 注意:无论任务是否已完成,都允许用户观看视频
* @param {Object} context - 页面上下文
* @param {Object} sendParams - 任务参数
*/
export function handleVideoTask(context, sendParams) {
var taskIdStr = sendParams.taskId ? String(sendParams.taskId) : '';
var completionPercent = resolveVideoCompletionPercent(sendParams.task);
var videoCompletionMap = Object.assign({}, context.data.videoCompletionMap || {});
if (completionPercent === null && taskIdStr && videoCompletionMap.hasOwnProperty(taskIdStr)) {
completionPercent = videoCompletionMap[taskIdStr];
}
// 使用按任务ID查询的新接口仅保留新结构
// 注意:不检查任务状态,即使任务已完成也允许播放视频
campApi.getTaskDetailById(sendParams.taskId).then(function(res){
var videoUrl = '';
if (!res || res.success !== true || !res.task) {
wx.showToast({
title: '获取视频任务失败',
icon: 'none'
});
wx.hideLoading();
return;
}
var task = res.task || {};
// 兼容多种格式task.content.video_url 或 task.content.video.video_url
var content = task.content || {};
var videoContent = content.video || {};
videoUrl = content.video_url || videoContent.video_url || '';
var detailPercent = resolveVideoCompletionPercent(task);
if (detailPercent !== null) {
completionPercent = detailPercent;
}
if (completionPercent === null) {
completionPercent = 100;
}
if (typeof completionPercent !== 'number' || isNaN(completionPercent)) {
completionPercent = 100;
}
if (completionPercent <= 0) {
completionPercent = 100;
}
completionPercent = Math.min(100, Math.max(1, Math.round(completionPercent)));
if (taskIdStr) {
videoCompletionMap[taskIdStr] = completionPercent;
}
if (videoUrl) {
// 先暂停当前视频(如果有),避免播放冲突
try {
if (context.videoContext) {
context.videoContext.pause();
}
} catch (e) {
}
// 准备新的视频数据(注意:不覆盖 introType保留打卡营原始介绍类型
var newVideoData = {
topContent: videoUrl,
topContentType: 'INTRO_TYPE_VIDEO',
// 注意:不设置 introType保留打卡营原始的介绍类型以便退出时正确恢复
showVideo: true,
hasLogged10Percent: false,
currentPlayingTaskId: sendParams.taskId,
currentPlayingTaskType: 'TASK_TYPE_VIDEO',
videoTitle: sendParams.taskTitle,
isAutoPlay: true,
currentVideoCompletionPercent: completionPercent,
videoCompletionMap: videoCompletionMap,
_pendingVideoPlay: true, // 标记需要自动播放
hasVideoPlayed: false // 重置播放状态
};
// 如果当前已经有视频在播放(比如介绍视频),需要先销毁再重建 video 组件
// 否则微信小程序的 <video> 组件可能不会正确切换视频源
var isVideoAlreadyShowing = context.data.showVideo && context.data.topContentType === 'INTRO_TYPE_VIDEO';
if (isVideoAlreadyShowing) {
// 先隐藏视频组件(销毁),再显示新的视频
context.setData({ showVideo: false }, function() {
setTimeout(function() {
context.setData(newVideoData, function() {
// 重新创建 videoContext因为旧的组件已销毁
context.videoContext = wx.createVideoContext('bannerVideo', context);
setTimeout(function() {
try {
if (context.videoContext) {
var playResult = context.videoContext.play();
if (playResult && typeof playResult.catch === 'function') {
playResult.catch(function(err) {});
}
}
} catch (e) {}
}, 300);
});
}, 100); // 等待组件销毁完成
});
} else {
// 当前没有视频在播放,直接设置
// 确保 videoContext 已初始化
if (!context.videoContext) {
context.videoContext = wx.createVideoContext('bannerVideo', context);
}
context.setData(newVideoData, function() {
setTimeout(function() {
try {
// 重新获取 videoContext组件可能是新创建的
context.videoContext = wx.createVideoContext('bannerVideo', context);
if (context.videoContext) {
var playResult = context.videoContext.play();
if (playResult && typeof playResult.catch === 'function') {
playResult.catch(function(err) {});
}
}
} catch (e) {}
}, 500);
});
}
wx.hideLoading();
} else {
wx.showToast({
title: '视频地址不存在',
icon: 'none'
});
wx.hideLoading();
}
});
}
/**
* 开始播放顶部视频
* @param {Object} context - 页面上下文
*/
export function startTopVideo(context) {
context.setData({
showVideo: true,
});
}
/**
* 暂停视频(用于页面隐藏或跳转时)
* @param {Object} context - 页面上下文
*/
export function pauseVideo(context) {
try {
if (context.videoContext) {
context.videoContext.pause();
}
} catch (e) {
}
context.setData({
isVideoTaskPlaying: false
});
}
/**
* 暂停并隐藏视频(用于切换到非视频任务时)
* @param {Object} context - 页面上下文
*/
export function pauseAndHideVideo(context) {
try {
// 先暂停视频播放
if (context.videoContext) {
context.videoContext.pause();
}
} catch (e) {
}
// 隐藏视频组件并清除自动播放标记
context.setData({
showVideo: false,
_pendingVideoPlay: false,
isAutoPlay: false,
isVideoTaskPlaying: false
});
}