6.6 KiB
6.6 KiB
自动更新视频duration - 前端实现
✅ 后端接口已添加
接口地址: POST /study/courseware/updateDuration
参数:
coursewareId: 课件IDduration: 视频时长(秒)
特点:
- ✅ 允许匿名访问(
@Anonymous) - ✅ 只更新未配置duration的视频
- ✅ 防止重复覆盖已有的duration
📱 前端调用代码(uni-app)
方式1:在视频加载完成时调用
// 在视频播放页面
<template>
<view>
<video
:src="videoUrl"
@loadedmetadata="onVideoLoaded"
@timeupdate="onTimeUpdate"
></video>
</view>
</template>
<script>
export default {
data() {
return {
coursewareId: null, // 从路由参数获取
videoUrl: '',
videoDuration: 0,
durationUpdated: false // 防止重复调用
}
},
onLoad(options) {
this.coursewareId = options.coursewareId;
this.videoUrl = options.videoUrl;
},
methods: {
// 视频元数据加载完成(可获取时长)
onVideoLoaded(e) {
console.log('视频加载完成', e);
// 获取视频时长
const duration = Math.floor(e.detail.duration);
this.videoDuration = duration;
console.log('视频时长:', duration, '秒');
// 自动更新到后端
if (!this.durationUpdated && duration > 0) {
this.updateVideoDuration(duration);
}
},
// 或者在第一次播放位置更新时调用
onTimeUpdate(e) {
// 只在第一次调用
if (!this.durationUpdated && e.detail.duration > 0) {
const duration = Math.floor(e.detail.duration);
this.videoDuration = duration;
this.updateVideoDuration(duration);
}
},
// 调用后端接口更新duration
updateVideoDuration(duration) {
if (this.durationUpdated) return;
this.$http.post('/study/courseware/updateDuration', {
coursewareId: this.coursewareId,
duration: duration
}).then(res => {
if (res.code === 200) {
console.log('✅ duration更新成功:', duration, '秒');
this.durationUpdated = true;
} else {
console.log('ℹ️ duration响应:', res.msg);
}
}).catch(err => {
console.error('❌ duration更新失败:', err);
});
}
}
}
</script>
方式2:使用uni.createVideoContext
// 在mounted或onReady中
onReady() {
// 创建video上下文
this.videoContext = uni.createVideoContext('myVideo', this);
// 延迟获取视频信息(等待加载)
setTimeout(() => {
this.getVideoDuration();
}, 1000);
},
methods: {
getVideoDuration() {
// 注意:uni-app的video组件可能需要通过事件获取duration
// 推荐使用 @loadedmetadata 事件(方式1)
}
}
方式3:直接使用HTML5 Video API(H5页面)
<video
id="myVideo"
:src="videoUrl"
@loadedmetadata="handleLoadedMetadata"
></video>
<script>
export default {
methods: {
handleLoadedMetadata(event) {
const video = event.target;
const duration = Math.floor(video.duration);
console.log('视频时长:', duration);
// 调用后端更新
this.updateDuration(duration);
},
updateDuration(duration) {
fetch('/study/courseware/updateDuration', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
coursewareId: this.coursewareId,
duration: duration
})
})
.then(res => res.json())
.then(data => {
console.log('更新结果:', data);
});
}
}
}
</script>
🔧 集成步骤
步骤1:后端编译部署
cd Study-Vue-redis
mvn clean package -DskipTests
# 重启服务
java -jar ry-study-admin/target/ry-study-admin.jar
步骤2:前端代码修改
找到视频播放页面: fronted_uniapp/pages/study/video.vue (或类似文件)
添加以下代码:
- 在
<video>标签添加事件监听:
<video @loadedmetadata="onVideoLoaded"></video>
- 在
methods中添加方法:
onVideoLoaded(e) {
const duration = Math.floor(e.detail.duration);
if (duration > 0) {
this.updateVideoDuration(duration);
}
},
updateVideoDuration(duration) {
this.$http.post('/study/courseware/updateDuration', {
coursewareId: this.coursewareId,
duration: duration
}).then(res => {
console.log('duration更新:', res.msg);
});
}
步骤3:测试验证
- 打开APP,观看一个视频
- 查看控制台日志:应该显示"duration更新成功"
- 查看数据库:
SELECT id, title, duration
FROM courseware
WHERE type = 'video' AND duration IS NOT NULL;
- 再次观看同一视频:应该显示"duration已存在,无需更新"
📊 预期效果
首次观看视频:
前端:加载视频 → 获取时长300秒 → 调用接口
后端:接收请求 → 检查duration为NULL → 更新为300秒 → 返回成功
日志:✅ 自动更新视频duration: 课件ID=123, 时长=300秒
再次观看视频:
前端:加载视频 → 获取时长300秒 → 调用接口
后端:接收请求 → 检查duration已存在 → 跳过更新 → 返回"无需更新"
几天后效果:
-- 查询已自动更新的视频
SELECT COUNT(*) FROM courseware
WHERE type = 'video' AND duration IS NOT NULL;
-- 预期结果:随着用户观看,越来越多视频会自动补充duration
-- 初始: 0个
-- 一周后: 100+个
-- 一个月后: 大部分常看的视频都有了
✅ 优点
- 无需批量处理 - 自动渐进式更新
- 用户无感知 - 后台静默完成
- 准确可靠 - 真实视频播放器获取的时长
- 性能友好 - 只更新一次,不重复
- 自然覆盖 - 常看的视频优先更新
🎯 完整流程
用户打开视频
↓
前端加载视频
↓
loadedmetadata事件触发
↓
获取 video.duration
↓
调用后端接口
↓
后端检查并更新数据库
↓
完成!视频有了duration
↓
下次计算进度时使用精确的95%标准
📝 注意事项
- 确保视频能正常加载 - 如果视频加载失败,无法获取duration
- 网络请求不阻塞播放 - 接口调用是异步的
- 接口调用失败不影响观看 - 有try-catch保护
- 已有duration的不会被覆盖 - 防止误操作
所有代码已准备好,编译部署即可使用! ✨