guoyu/log/自动更新视频duration-前端代码.md
2025-12-11 23:28:07 +08:00

301 lines
6.6 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

# 自动更新视频duration - 前端实现
## ✅ **后端接口已添加**
**接口地址:** `POST /study/courseware/updateDuration`
**参数:**
- `coursewareId`: 课件ID
- `duration`: 视频时长(秒)
**特点:**
- ✅ 允许匿名访问(`@Anonymous`
- ✅ 只更新未配置duration的视频
- ✅ 防止重复覆盖已有的duration
---
## 📱 **前端调用代码uni-app**
### **方式1在视频加载完成时调用**
```javascript
// 在视频播放页面
<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**
```javascript
// 在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 APIH5页面**
```javascript
<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后端编译部署**
```bash
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` (或类似文件)
**添加以下代码:**
1.`<video>` 标签添加事件监听:
```html
<video @loadedmetadata="onVideoLoaded"></video>
```
2.`methods` 中添加方法:
```javascript
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测试验证**
1. **打开APP观看一个视频**
2. **查看控制台日志**:应该显示"duration更新成功"
3. **查看数据库**
```sql
SELECT id, title, duration
FROM courseware
WHERE type = 'video' AND duration IS NOT NULL;
```
4. **再次观看同一视频**:应该显示"duration已存在无需更新"
---
## 📊 **预期效果**
### **首次观看视频:**
```
前端:加载视频 → 获取时长300秒 → 调用接口
后端:接收请求 → 检查duration为NULL → 更新为300秒 → 返回成功
日志:✅ 自动更新视频duration: 课件ID=123, 时长=300秒
```
### **再次观看视频:**
```
前端:加载视频 → 获取时长300秒 → 调用接口
后端:接收请求 → 检查duration已存在 → 跳过更新 → 返回"无需更新"
```
### **几天后效果:**
```sql
-- 查询已自动更新的视频
SELECT COUNT(*) FROM courseware
WHERE type = 'video' AND duration IS NOT NULL;
-- 预期结果随着用户观看越来越多视频会自动补充duration
-- 初始: 0个
-- 一周后: 100+个
-- 一个月后: 大部分常看的视频都有了
```
---
## ✅ **优点**
1. **无需批量处理** - 自动渐进式更新
2. **用户无感知** - 后台静默完成
3. **准确可靠** - 真实视频播放器获取的时长
4. **性能友好** - 只更新一次,不重复
5. **自然覆盖** - 常看的视频优先更新
---
## 🎯 **完整流程**
```
用户打开视频
前端加载视频
loadedmetadata事件触发
获取 video.duration
调用后端接口
后端检查并更新数据库
完成视频有了duration
下次计算进度时使用精确的95%标准
```
---
## 📝 **注意事项**
1. **确保视频能正常加载** - 如果视频加载失败无法获取duration
2. **网络请求不阻塞播放** - 接口调用是异步的
3. **接口调用失败不影响观看** - 有try-catch保护
4. **已有duration的不会被覆盖** - 防止误操作
**所有代码已准备好,编译部署即可使用!**