BackupMaterialServiceImpl.java
17.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
package com.wondertek.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.ConvertException;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.wondertek.dto.*;
import com.wondertek.dto.image.ImageVideoReq;
import com.wondertek.dto.image.OutPut;
import com.wondertek.entity.BackupConfig;
import com.wondertek.entity.BackupMaterial;
import com.wondertek.entity.StreamTask;
import com.wondertek.enums.MaterialSourceEnum;
import com.wondertek.enums.PlayTypeEnum;
import com.wondertek.exception.ServiceException;
import com.wondertek.mapper.BackupConfigMapper;
import com.wondertek.mapper.BackupMaterialMapper;
import com.wondertek.mapper.StreamTaskMapper;
import com.wondertek.service.BackupMaterialService;
import com.wondertek.service.FileService;
import com.wondertek.service.MediaInfoService;
import com.wondertek.util.*;
import com.wondertek.vo.media.Media;
import com.wondertek.vo.media.MediaType;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
@Slf4j
public class BackupMaterialServiceImpl extends ServiceImpl<BackupMaterialMapper, BackupMaterial> implements BackupMaterialService {
@Resource
private BackupMaterialMapper backupMaterialMapper;
@Resource
private FileService fileService;
@Value("${file.realPath}")
private String realPath;
@Resource
private BackupConfigMapper backupConfigMapper;
@Resource
private StreamTaskMapper streamTaskMapper;
@Value("${transcode.switchStreamUrl}")
private String switchStreamUrl;
@Value("${transcode.swapBackupUrl}")
private String swapBackupUrl;
@Value("${rec.getImageUrl}")
private String recGetImageUrl;
@Value("${file.previewUrl}")
private String previewUrl;
@Resource
private MediaInfoService mediaInfoService;
@Override
public PageBean queryPage(BackupMaterialDto backupMaterialDto) {
Integer page = backupMaterialDto.getPage();
Integer size = backupMaterialDto.getSize();
LambdaQueryWrapper<BackupMaterial> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(BackupMaterial::getDel, 0);
if (ObjectUtil.isNotEmpty(backupMaterialDto.getId())){
queryWrapper.eq(BackupMaterial::getId,backupMaterialDto.getId());
}
if(StringUtils.isNotBlank(backupMaterialDto.getBackupName())){
queryWrapper.like(BackupMaterial::getBackupName,backupMaterialDto.getBackupName());
}
queryWrapper.orderByDesc(BackupMaterial::getCreatedTime);
Page<BackupMaterial> pageInfo = new Page<>(page, size);
IPage<BackupMaterial> resultPage = backupMaterialMapper.selectPage(pageInfo, queryWrapper);
//添加预览地址
resultPage.getRecords().forEach(backupMaterial -> {
backupMaterial.setFileUrl(previewUrl+backupMaterial.getFilePath());
backupMaterial.setCoverUrl(previewUrl+backupMaterial.getCoverPath());
});
return new PageBean(Integer.parseInt(String.valueOf(resultPage.getPages())),resultPage.getTotal(),resultPage.getRecords());
}
@Override
public ResultBean upload(MultipartFile backupFile, BackupUploadVo backupUploadVo) {
//校验
// if(!"mp4".equalsIgnoreCase(backupUploadVo.getFileType())){
// throw new ServiceException("仅支持上传mp4格式的垫片文件!");
// }
//校验backupFile后缀名
String realName = backupFile.getOriginalFilename();
String suffix = realName.substring(realName.lastIndexOf(".") + 1);
if (!suffix.equalsIgnoreCase("mp4")) {
throw new ServiceException("仅支持上传mp4格式的垫片文件!");
}
//对应垫片任务
LambdaQueryWrapper<StreamTask> backupWrapper = new LambdaQueryWrapper<>();
backupWrapper.eq(StreamTask::getRoomId,backupUploadVo.getRoomId());
String palyType = backupUploadVo.getBackupOrder() == 1? PlayTypeEnum.BACKUP1.getCode() : PlayTypeEnum.BACKUP2.getCode();
backupWrapper.eq(StreamTask::getPlayType,palyType);
StreamTask streamTask = streamTaskMapper.selectOne(backupWrapper);
if (streamTask == null){
return ResultBean.error("未找到该直播间对应的垫片任务");
}
//文件上传
String fileId = UUIDUtil.randomUUID();
String fileName = backupUploadVo.getBackupName() + "." + backupUploadVo.getFileType();
///home/wondertek/material_file_assets/dianpian/2025/07/24/roomid/
String filedir = FileUtils.generateDianPianDir(backupUploadVo.getRoomId().toString(), realPath);
String destFilePath = filedir + realName;//文件上传目录,绝对路径
try {
fileService.upload(backupFile, destFilePath);
} catch (Exception e) {
return ResultBean.error("上传文件异常");
}
//保存垫片素材表
BackupMaterial backupMaterial = new BackupMaterial();
backupMaterial.setFileId(fileId);
backupMaterial.setBackupName(backupUploadVo.getBackupName());
String relativePath = destFilePath.replace(realPath, "");
backupMaterial.setFilePath(relativePath);
backupMaterial.setFileType(backupUploadVo.getFileType());
backupMaterial.setCreatedTime(LocalDateTime.now());
backupMaterial.setCreatedBy("admin");
backupMaterialMapper.insert(backupMaterial);
//添加审片间垫片配置
LambdaQueryWrapper<BackupConfig> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(BackupConfig::getRoomId,backupUploadVo.getRoomId());
wrapper.eq(BackupConfig::getBackupOrder,backupUploadVo.getBackupOrder());
BackupConfig backupConfig = backupConfigMapper.selectOne(wrapper);
if(ObjectUtil.isNull(backupConfig)){
backupConfig = new BackupConfig();
}
backupConfig.setRoomId(backupUploadVo.getRoomId());
backupConfig.setBackupId(backupMaterial.getId());
backupConfig.setBackupOrder(backupUploadVo.getBackupOrder());
backupConfig.setBackupStatus(backupUploadVo.getBackupStatus());
backupConfig.setBackupPath(relativePath);
backupConfigMapper.insertOrUpdate(backupConfig);
//对应垫片任务切源(转码平台演示环境已挂载统一存储),请求转码平台修改垫片任务直播源
changeChannelSource(streamTask.getTaskId(), destFilePath);
//判断是否需要修改当前播出流的垫片
if("1".equals(backupConfig.getBackupStatus())){
//切换播出流垫片(主、备)
LambdaQueryWrapper<StreamTask> playWrapper = new LambdaQueryWrapper<>();
playWrapper.eq(StreamTask::getRoomId,backupUploadVo.getRoomId());
playWrapper.eq(StreamTask::getPlayType,"play");
List<StreamTask> playTasks = streamTaskMapper.selectList(playWrapper);
if(CollectionUtil.isNotEmpty(playTasks)){
playTasks.forEach(playTask -> {
changeBackup(playTask.getTaskId(), destFilePath);
});
}
}
return ResultBean.ok("上传成功");
}
/**
* 修改转码任务垫片
* @param taskId
* @param destFilePath
*/
private void changeBackup(String taskId, String destFilePath) {
Map<String, Object> paramsMap = new HashMap<>();
paramsMap.put("taskId", taskId);
paramsMap.put("backupUrl", destFilePath);
HttpResponse execute = null;
try {
execute = HttpRequest.post(swapBackupUrl)
.header("Content-Type", "application/json")
.body(JSONUtils.obj2json(paramsMap)).timeout(30000).execute();
if (execute.isOk()) {
log.info("-->请求转码系统换垫片接口,url:{},paramsMap:{}",swapBackupUrl,paramsMap);
String body = execute.body();
ResponseData response = JSONUtils.jsonToObject(body, new TypeReference<ResponseData>() {
});
if (response.getResultCode().equals("0")) {
log.info("请求转码系统换垫片接口成功");
}else {
log.info("请求转码系统换垫片接口响应失败,response:{}",response);
throw new ServiceException("请求转码系统换垫片接口响应失败");
}
} else {
log.error("请求转码系统换垫片接口失败,url:{},body:{}", swapBackupUrl, execute.body());
}
} catch (Exception e) {
log.error("请求转码系统换垫片接口失败,url:{}", swapBackupUrl, e);
}
}
private void changeChannelSource(String taskId, String destFilePath) {
ChangeSourceParam param = new ChangeSourceParam();
param.setTaskId(taskId);
SourceUrl sourceUrl = new SourceUrl();
sourceUrl.setSourceUrl(destFilePath);
sourceUrl.setPri(0);
param.setSourceUrls(Arrays.asList(sourceUrl));
HttpResponse execute = null;
try {
execute = HttpRequest.post(switchStreamUrl)
.header("Content-Type", "application/json")
.body(JSONUtils.obj2json(param)).timeout(30000).execute();
if (execute.isOk()) {
log.info("-->请求转码系统切换直播源接口,url:{},paramsMap:{}",switchStreamUrl,param);
String body = execute.body();
ResponseData response = JSONUtils.jsonToObject(body, new TypeReference<ResponseData>() {
});
if (response.getResultCode().equals("0")) {
log.info("请求转码系统切换直播源接口成功");
}else {
log.info("请求转码系统切换直播源接口响应失败,response:{}",response);
throw new ServiceException("请求转码系统切换直播源接口响应失败");
}
} else {
log.error("请求转码系统切换直播源接口失败,url:{},body:{}", switchStreamUrl, execute.body());
}
} catch (Exception e) {
log.error("请求转码系统切换直播源接口失败,url:{}", switchStreamUrl, e);
}
}
@Override
public ResultBean getByRoom(Long roomId) {
LambdaQueryWrapper<BackupConfig> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(BackupConfig::getRoomId,roomId);
List<BackupConfig> backupConfigs = backupConfigMapper.selectList(wrapper);
return ResultBean.ok(backupConfigs);
}
@Override
public ResultBean change(BackupChangeDto backupChangeDto) {
LambdaQueryWrapper<BackupConfig> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(BackupConfig::getRoomId,backupChangeDto.getRoomId());
List<BackupConfig> backupConfigs = backupConfigMapper.selectList(wrapper);
if(CollectionUtil.isNotEmpty(backupConfigs)){
backupConfigs.forEach(backupConfig -> {
if(backupConfig.getBackupId().equals(backupChangeDto.getBackupId())){
backupConfig.setBackupStatus("1");
//将播放流的垫片配置切到此垫片上,播放流主、备都切换
LambdaQueryWrapper<StreamTask> playWrapper = new LambdaQueryWrapper<>();
playWrapper.eq(StreamTask::getRoomId,backupChangeDto.getRoomId());
playWrapper.eq(StreamTask::getPlayType,"play");
List<StreamTask> playTasks = streamTaskMapper.selectList(playWrapper);
if(CollectionUtil.isNotEmpty(playTasks)){
String backupPath = realPath + backupConfig.getBackupPath();
playTasks.forEach(playTask -> {
changeBackup(playTask.getTaskId(), backupPath);
});
}
}else {
//将其他垫片配置切到默认状态
backupConfig.setBackupStatus("0");
}
backupConfigMapper.updateById(backupConfig);
});
}
return ResultBean.ok();
}
@Override
public ResultBean uploadToMaterial(MultipartFile backupFile, MaterialUploadVo materialUploadVo) {
//校验backupFile后缀名
String realName = backupFile.getOriginalFilename();
String suffix = realName.substring(realName.lastIndexOf(".") + 1);
if (!suffix.equalsIgnoreCase("mp4")) {
throw new ServiceException("仅支持上传mp4格式的垫片文件!");
}
//文件上传
String fileId = UUIDUtil.randomUUID();
String fileName = materialUploadVo.getBackupName() + "." + materialUploadVo.getFileType();
///home/wondertek/material_file_assets/dianpian/2025/07/24/uuid/
String filedir = FileUtils.generateDianPianDir(fileId, realPath);
String destFilePath = filedir + fileName;//文件上传目录,绝对路径
try {
fileService.upload(backupFile, destFilePath);
} catch (Exception e) {
return ResultBean.error("上传文件异常");
}
//保存垫片素材表
BackupMaterial backupMaterial = new BackupMaterial();
backupMaterial.setFileId(fileId);
backupMaterial.setBackupName(materialUploadVo.getBackupName());
String relativePath = destFilePath.replace(realPath, "");
backupMaterial.setFilePath(relativePath);
backupMaterial.setFileType(materialUploadVo.getFileType());
backupMaterial.setCreatedTime(LocalDateTime.now());
backupMaterial.setCreatedBy("admin");
backupMaterial.setDel("0");
backupMaterial.setSource(MaterialSourceEnum.UPLOAD.getCode());
//抽封面图,调用定帧抽图接口
ImageVideoReq imageVideoReq = new ImageVideoReq();
imageVideoReq.setInput(destFilePath);
imageVideoReq.setOffset(0L);
OutPut outPut = new OutPut();
outPut.setIndex(0);
String coverPath = filedir + realName.substring(0, realName.lastIndexOf(".")) + ".jpg";
outPut.setPath(coverPath);
imageVideoReq.setOutput(Arrays.asList(outPut));
try {
HttpResponse execute = HttpRequest.post(recGetImageUrl)
.header("Content-Type", "application/json")
.body(JSONUtils.obj2json(imageVideoReq)).timeout(30000).execute();
if (execute.isOk()) {
log.info("-->请求能力平台定帧抽图接口,url:{},paramsMap:{}",recGetImageUrl,imageVideoReq);
String body = execute.body();
AbilityResp response = JSONUtils.jsonToObject(body, new TypeReference<AbilityResp>() {
});
if (response.getRet() == 0) {
backupMaterial.setCoverPath(coverPath.replace(realPath, ""));
log.info("请求能力平台定帧抽图接口成功");
}else {
log.info("请求能力平台定帧抽图接口响应失败,response:{}",response);
throw new ServiceException("请求能力平台定帧抽图接口响应失败");
}
} else {
log.error("请求能力平台定帧抽图接口失败,url:{},body:{}", switchStreamUrl, execute.body());
}
} catch (Exception e) {
log.error("请求能力平台定帧抽图接口失败,url:{}", switchStreamUrl, e);
}
//媒体分析获取时长
try {
Media media = mediaInfoService.getMediaInfo(destFilePath, fileId);
String type = media.getType();
Long duration = 0l;//时长
switch (type) {
case MediaType.VIDEO_TYPE:
cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(media);
duration = Double.valueOf(jsonObject.get("duration", Double.class) * 1000).longValue();
backupMaterial.setDuration(duration);
}
} catch (NumberFormatException | ConvertException e) {
log.error("媒体分析获取时长报错:e={}", e);
} catch (Exception e) {
throw new RuntimeException(e);
}
backupMaterialMapper.insert(backupMaterial);
return ResultBean.ok("新增素材成功");
}
@Override
public ResultBean batchDelete(Long[] ids) {
// 创建更新条件构造器
LambdaUpdateWrapper<BackupMaterial> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.in(BackupMaterial::getId, ids);
updateWrapper.set(BackupMaterial::getDel, 1);
int rows = backupMaterialMapper.update(null, updateWrapper);
log.info("删除素材库记录条数:{}", rows);
return rows > 0 ? ResultBean.ok() : ResultBean.error();
}
}