Commit cfc4df108e2652c1c1975e204d57fe290b5e1bc0

Authored by 64850858
1 parent f3e2bf1e

修改接口bug

.gitignore
  1 +/target/
1 /.idea/ 2 /.idea/
  3 +/logs/
@@ -81,6 +81,14 @@ @@ -81,6 +81,14 @@
81 <groupId>org.springframework.boot</groupId> 81 <groupId>org.springframework.boot</groupId>
82 <artifactId>spring-boot-maven-plugin</artifactId> 82 <artifactId>spring-boot-maven-plugin</artifactId>
83 </plugin> 83 </plugin>
  84 + <plugin>
  85 + <groupId>org.apache.maven.plugins</groupId>
  86 + <artifactId>maven-compiler-plugin</artifactId>
  87 + <configuration>
  88 + <source>1.8</source>
  89 + <target>1.8</target>
  90 + </configuration>
  91 + </plugin>
84 </plugins> 92 </plugins>
85 </build> 93 </build>
86 94
src/main/java/top/panll/assist/config/FastJsonRedisSerializer.java 0 → 100644
  1 +package top.panll.assist.config;
  2 +
  3 +import com.alibaba.fastjson.JSON;
  4 +import com.alibaba.fastjson.parser.ParserConfig;
  5 +import com.alibaba.fastjson.serializer.SerializerFeature;
  6 +import org.springframework.data.redis.serializer.RedisSerializer;
  7 +import org.springframework.data.redis.serializer.SerializationException;
  8 +
  9 +import java.nio.charset.Charset;
  10 +
  11 +public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
  12 + private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
  13 + private Class<T> clazz;
  14 +
  15 + /**
  16 + * 添加autotype白名单
  17 + * 解决redis反序列化对象时报错 :com.alibaba.fastjson.JSONException: autoType is not support
  18 + */
  19 + static {
  20 + ParserConfig.getGlobalInstance().addAccept("top.panll.assist");
  21 + }
  22 +
  23 + public FastJsonRedisSerializer(Class<T> clazz) {
  24 + super();
  25 + this.clazz = clazz;
  26 + }
  27 +
  28 + @Override
  29 + public byte[] serialize(T t) throws SerializationException {
  30 + if (null == t) {
  31 + return new byte[0];
  32 + }
  33 + return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
  34 + }
  35 +
  36 + @Override
  37 + public T deserialize(byte[] bytes) throws SerializationException {
  38 + if (null == bytes || bytes.length <= 0) {
  39 + return null;
  40 + }
  41 + String str = new String(bytes, DEFAULT_CHARSET);
  42 + return JSON.parseObject(str, clazz);
  43 + }
  44 +}
src/main/java/top/panll/assist/config/RedisConfig.java
1 package top.panll.assist.config; 1 package top.panll.assist.config;
2 2
3 import com.alibaba.fastjson.parser.ParserConfig; 3 import com.alibaba.fastjson.parser.ParserConfig;
4 -import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;  
5 -import org.springframework.cache.annotation.CachingConfigurerSupport; 4 +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
  5 +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
  6 +import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
  7 +import org.springframework.boot.context.properties.EnableConfigurationProperties;
6 import org.springframework.context.annotation.Bean; 8 import org.springframework.context.annotation.Bean;
7 import org.springframework.context.annotation.Configuration; 9 import org.springframework.context.annotation.Configuration;
8 import org.springframework.data.redis.connection.RedisConnectionFactory; 10 import org.springframework.data.redis.connection.RedisConnectionFactory;
  11 +import org.springframework.data.redis.core.RedisOperations;
9 import org.springframework.data.redis.core.RedisTemplate; 12 import org.springframework.data.redis.core.RedisTemplate;
10 import org.springframework.data.redis.listener.RedisMessageListenerContainer; 13 import org.springframework.data.redis.listener.RedisMessageListenerContainer;
11 import org.springframework.data.redis.serializer.StringRedisSerializer; 14 import org.springframework.data.redis.serializer.StringRedisSerializer;
@@ -17,14 +20,21 @@ import org.springframework.data.redis.serializer.StringRedisSerializer; @@ -17,14 +20,21 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
17 * 20 *
18 */ 21 */
19 @Configuration 22 @Configuration
20 -public class RedisConfig extends CachingConfigurerSupport { 23 +@ConditionalOnClass(RedisOperations.class)
  24 +@EnableConfigurationProperties(RedisProperties.class)
  25 +public class RedisConfig {
21 26
22 - @Bean("redisTemplate") 27 + static {
  28 + ParserConfig.getGlobalInstance().addAccept("top.panll.assist");
  29 + }
  30 +
  31 + @Bean
  32 + @ConditionalOnMissingBean(name = "redisTemplate")
23 public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { 33 public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
24 RedisTemplate<Object, Object> template = new RedisTemplate<>(); 34 RedisTemplate<Object, Object> template = new RedisTemplate<>();
25 template.setConnectionFactory(redisConnectionFactory); 35 template.setConnectionFactory(redisConnectionFactory);
26 // 使用fastjson进行序列化处理,提高解析效率 36 // 使用fastjson进行序列化处理,提高解析效率
27 - FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<Object>(Object.class); 37 + FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<>(Object.class);
28 // value值的序列化采用fastJsonRedisSerializer 38 // value值的序列化采用fastJsonRedisSerializer
29 template.setValueSerializer(serializer); 39 template.setValueSerializer(serializer);
30 template.setHashValueSerializer(serializer); 40 template.setHashValueSerializer(serializer);
@@ -33,8 +43,9 @@ public class RedisConfig extends CachingConfigurerSupport { @@ -33,8 +43,9 @@ public class RedisConfig extends CachingConfigurerSupport {
33 template.setHashKeySerializer(new StringRedisSerializer()); 43 template.setHashKeySerializer(new StringRedisSerializer());
34 template.setConnectionFactory(redisConnectionFactory); 44 template.setConnectionFactory(redisConnectionFactory);
35 // 使用fastjson时需设置此项,否则会报异常not support type 45 // 使用fastjson时需设置此项,否则会报异常not support type
36 - ParserConfig.getGlobalInstance().setAutoTypeSupport(true); 46 +// ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
37 return template; 47 return template;
  48 +
38 } 49 }
39 50
40 /** 51 /**
src/main/java/top/panll/assist/config/StartConfig.java
@@ -53,7 +53,7 @@ public class StartConfig implements CommandLineRunner { @@ -53,7 +53,7 @@ public class StartConfig implements CommandLineRunner {
53 String ffprobePath = userSettings.getFfprobe(); 53 String ffprobePath = userSettings.getFfprobe();
54 FFmpeg ffmpeg = new FFmpeg(ffmpegPath); 54 FFmpeg ffmpeg = new FFmpeg(ffmpegPath);
55 FFprobe ffprobe = new FFprobe(ffprobePath); 55 FFprobe ffprobe = new FFprobe(ffprobePath);
56 - logger.info("wvp-pro辅助程序启动成功"); 56 + logger.info("wvp-pro辅助程序启动成功。 \n{}\n{} ", ffmpeg.version(), ffprobe.version());
57 FFmpegExecUtils.getInstance().ffmpeg = ffmpeg; 57 FFmpegExecUtils.getInstance().ffmpeg = ffmpeg;
58 FFmpegExecUtils.getInstance().ffprobe = ffprobe; 58 FFmpegExecUtils.getInstance().ffprobe = ffprobe;
59 // 对目录进行预整理 59 // 对目录进行预整理
src/main/java/top/panll/assist/config/TaskConfig.java
@@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired;
6 import org.springframework.context.annotation.Configuration; 6 import org.springframework.context.annotation.Configuration;
7 import org.springframework.scheduling.annotation.EnableScheduling; 7 import org.springframework.scheduling.annotation.EnableScheduling;
8 import org.springframework.scheduling.annotation.Scheduled; 8 import org.springframework.scheduling.annotation.Scheduled;
  9 +import org.springframework.stereotype.Component;
9 import top.panll.assist.service.VideoFileService; 10 import top.panll.assist.service.VideoFileService;
10 11
11 import java.io.File; 12 import java.io.File;
@@ -16,7 +17,7 @@ import java.util.Calendar; @@ -16,7 +17,7 @@ import java.util.Calendar;
16 import java.util.Date; 17 import java.util.Date;
17 import java.util.List; 18 import java.util.List;
18 19
19 -@Configuration 20 +@Component
20 @EnableScheduling 21 @EnableScheduling
21 public class TaskConfig { 22 public class TaskConfig {
22 23
src/main/java/top/panll/assist/controller/RecordController.java
1 package top.panll.assist.controller; 1 package top.panll.assist.controller;
2 2
  3 +import com.alibaba.fastjson.JSONObject;
3 import org.slf4j.Logger; 4 import org.slf4j.Logger;
4 import org.slf4j.LoggerFactory; 5 import org.slf4j.LoggerFactory;
5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.beans.factory.annotation.Autowired;
  7 +import org.springframework.http.HttpStatus;
  8 +import org.springframework.http.ResponseEntity;
6 import org.springframework.web.bind.annotation.*; 9 import org.springframework.web.bind.annotation.*;
7 import top.panll.assist.controller.bean.WVPResult; 10 import top.panll.assist.controller.bean.WVPResult;
  11 +import top.panll.assist.dto.MergeOrCutTaskInfo;
8 import top.panll.assist.service.VideoFileService; 12 import top.panll.assist.service.VideoFileService;
9 import top.panll.assist.utils.PageInfo; 13 import top.panll.assist.utils.PageInfo;
10 14
@@ -105,11 +109,11 @@ public class RecordController { @@ -105,11 +109,11 @@ public class RecordController {
105 */ 109 */
106 @GetMapping(value = "/date/list") 110 @GetMapping(value = "/date/list")
107 @ResponseBody 111 @ResponseBody
108 - public WVPResult<PageInfo<String>> getDateList(@RequestParam int page,  
109 - @RequestParam int count,  
110 - @RequestParam String app,  
111 - @RequestParam String stream ){  
112 - WVPResult<PageInfo<String>> result = new WVPResult<>(); 112 + public WVPResult<List<String>> getDateList( @RequestParam(required = false) Integer year,
  113 + @RequestParam(required = false) Integer month,
  114 + @RequestParam String app,
  115 + @RequestParam String stream ){
  116 + WVPResult<List<String>> result = new WVPResult<>();
113 List<String> resultData = new ArrayList<>(); 117 List<String> resultData = new ArrayList<>();
114 if (app == null) { 118 if (app == null) {
115 result.setCode(400); 119 result.setCode(400);
@@ -121,15 +125,13 @@ public class RecordController { @@ -121,15 +125,13 @@ public class RecordController {
121 result.setMsg("stream不能为空"); 125 result.setMsg("stream不能为空");
122 return result; 126 return result;
123 } 127 }
124 - List<File> dateList = videoFileService.getDateList(app, stream); 128 + List<File> dateList = videoFileService.getDateList(app, stream, year, month);
125 for (File file : dateList) { 129 for (File file : dateList) {
126 resultData.add(file.getName()); 130 resultData.add(file.getName());
127 } 131 }
128 result.setCode(0); 132 result.setCode(0);
129 result.setMsg("success"); 133 result.setMsg("success");
130 - PageInfo<String> stringPageInfo = new PageInfo<>(resultData);  
131 - stringPageInfo.startPage(page, count);  
132 - result.setData(stringPageInfo); 134 + result.setData(resultData);
133 return result; 135 return result;
134 } 136 }
135 137
@@ -188,7 +190,7 @@ public class RecordController { @@ -188,7 +190,7 @@ public class RecordController {
188 * @param endTime 190 * @param endTime
189 * @return 191 * @return
190 */ 192 */
191 - @GetMapping(value = "/file/download/task") 193 + @GetMapping(value = "/file/download/task/add")
192 @ResponseBody 194 @ResponseBody
193 public WVPResult<String> addTaskForDownload(@RequestParam String app, 195 public WVPResult<String> addTaskForDownload(@RequestParam String app,
194 @RequestParam String stream, 196 @RequestParam String stream,
@@ -210,35 +212,63 @@ public class RecordController { @@ -210,35 +212,63 @@ public class RecordController {
210 } 212 }
211 String id = videoFileService.mergeOrCut(app, stream, startTimeDate, endTimeDate); 213 String id = videoFileService.mergeOrCut(app, stream, startTimeDate, endTimeDate);
212 result.setCode(0); 214 result.setCode(0);
213 - result.setMsg(id!= null?"success":"error"); 215 + result.setMsg(id!= null?"success":"error: 可能未找到视频文件");
214 result.setData(id); 216 result.setData(id);
215 return result; 217 return result;
216 } 218 }
217 219
218 /** 220 /**
219 - * 录制完成的通知 221 + * 查询视频裁剪合并任务列表
220 * @return 222 * @return
221 */ 223 */
222 - @GetMapping(value = "/end") 224 + @GetMapping(value = "/file/download/task/list")
223 @ResponseBody 225 @ResponseBody
224 - public WVPResult<String> recordEnd(@RequestParam String path  
225 - ){  
226 - File file = new File(path);  
227 - WVPResult<String> result = new WVPResult<>();  
228 - if (file.exists()) {  
229 - try {  
230 - videoFileService.handFile(file);  
231 - result.setCode(0);  
232 - result.setMsg("success");  
233 - } catch (ParseException e) {  
234 - e.printStackTrace();  
235 - result.setCode(500);  
236 - result.setMsg("error");  
237 - }  
238 - }else {  
239 - result.setCode(400);  
240 - result.setMsg("路径不存在"); 226 + public WVPResult<List<MergeOrCutTaskInfo>> getTaskListForDownload(@RequestParam Boolean isEnd){
  227 + if (isEnd == null) {
  228 + isEnd = false;
241 } 229 }
  230 + List<MergeOrCutTaskInfo> taskList = videoFileService.getTaskListForDownload(isEnd);
  231 + WVPResult<List<MergeOrCutTaskInfo>> result = new WVPResult<>();
  232 + result.setCode(0);
  233 + result.setMsg(taskList != null?"success":"error");
  234 + result.setData(taskList);
242 return result; 235 return result;
243 } 236 }
  237 +
  238 + /**
  239 + * TODO 中止视频裁剪合并任务列表
  240 + * @return
  241 + */
  242 + @GetMapping(value = "/file/download/task/stop")
  243 + @ResponseBody
  244 + public WVPResult<String> stopTaskForDownload(@RequestParam String taskId){
  245 +// WVPResult<String> result = new WVPResult<>();
  246 +// if (taskId == null) {
  247 +// result.setCode(400);
  248 +// result.setMsg("taskId 不能为空");
  249 +// return result;
  250 +// }
  251 +// boolean stopResult = videoFileService.stopTask(taskId);
  252 +// result.setCode(0);
  253 +// result.setMsg(stopResult ? "success": "fail");
  254 + return null;
  255 + }
  256 +
  257 + /**
  258 + * 录制完成的通知, 对用zlm的hook
  259 + * @return
  260 + */
  261 + @ResponseBody
  262 + @PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8")
  263 + public ResponseEntity<String> onRecordMp4(@RequestBody JSONObject json) {
  264 + JSONObject ret = new JSONObject();
  265 + ret.put("code", 0);
  266 + ret.put("msg", "success");
  267 + String file_path = json.getString("file_path");
  268 + logger.debug("ZLM 录制完成,参数:" + file_path);
  269 + if (file_path == null) return new ResponseEntity<String>(ret.toString(), HttpStatus.OK);
  270 + videoFileService.handFile(new File(file_path));
  271 +
  272 + return new ResponseEntity<String>(ret.toString(), HttpStatus.OK);
  273 + }
244 } 274 }
src/main/java/top/panll/assist/dto/MergeOrCutTaskInfo.java 0 → 100644
  1 +package top.panll.assist.dto;
  2 +
  3 +import java.util.Date;
  4 +
  5 +public class MergeOrCutTaskInfo {
  6 + private String id;
  7 + private String startTime;
  8 + private String endTime;
  9 + private String app;
  10 + private String stream;
  11 +
  12 + private String percentage;
  13 +
  14 + private String recordFile;
  15 +
  16 + public String getId() {
  17 + return id;
  18 + }
  19 +
  20 + public void setId(String id) {
  21 + this.id = id;
  22 + }
  23 +
  24 + public String getStartTime() {
  25 + return startTime;
  26 + }
  27 +
  28 + public void setStartTime(String startTime) {
  29 + this.startTime = startTime;
  30 + }
  31 +
  32 + public String getEndTime() {
  33 + return endTime;
  34 + }
  35 +
  36 + public void setEndTime(String endTime) {
  37 + this.endTime = endTime;
  38 + }
  39 +
  40 + public String getApp() {
  41 + return app;
  42 + }
  43 +
  44 + public void setApp(String app) {
  45 + this.app = app;
  46 + }
  47 +
  48 + public String getStream() {
  49 + return stream;
  50 + }
  51 +
  52 + public void setStream(String stream) {
  53 + this.stream = stream;
  54 + }
  55 +
  56 + public String getPercentage() {
  57 + return percentage;
  58 + }
  59 +
  60 + public void setPercentage(String percentage) {
  61 + this.percentage = percentage;
  62 + }
  63 +
  64 + public String getRecordFile() {
  65 + return recordFile;
  66 + }
  67 +
  68 + public void setRecordFile(String recordFile) {
  69 + this.recordFile = recordFile;
  70 + }
  71 +}
src/main/java/top/panll/assist/service/FFmpegExecUtils.java
@@ -51,7 +51,6 @@ public class FFmpegExecUtils { @@ -51,7 +51,6 @@ public class FFmpegExecUtils {
51 return null; 51 return null;
52 } 52 }
53 53
54 -  
55 File tempFile = new File(dest.getAbsolutePath() + File.separator + temp); 54 File tempFile = new File(dest.getAbsolutePath() + File.separator + temp);
56 if (!tempFile.exists()) { 55 if (!tempFile.exists()) {
57 tempFile.mkdirs(); 56 tempFile.mkdirs();
@@ -66,14 +65,12 @@ public class FFmpegExecUtils { @@ -66,14 +65,12 @@ public class FFmpegExecUtils {
66 if (split.length != 3) continue; 65 if (split.length != 3) continue;
67 String durationStr = split[2].replace(".mp4", ""); 66 String durationStr = split[2].replace(".mp4", "");
68 Double duration = Double.parseDouble(durationStr)/1000; 67 Double duration = Double.parseDouble(durationStr)/1000;
69 - System.out.println(duration);  
70 bw.write("file " + file.getAbsolutePath()); 68 bw.write("file " + file.getAbsolutePath());
71 bw.newLine(); 69 bw.newLine();
72 durationAll += duration; 70 durationAll += duration;
73 } 71 }
74 bw.flush(); 72 bw.flush();
75 bw.close(); 73 bw.close();
76 - System.out.println(durationAll);  
77 } catch (IOException e) { 74 } catch (IOException e) {
78 e.printStackTrace(); 75 e.printStackTrace();
79 callBack.run("error", 0.0, null); 76 callBack.run("error", 0.0, null);
@@ -91,7 +88,6 @@ public class FFmpegExecUtils { @@ -91,7 +88,6 @@ public class FFmpegExecUtils {
91 .setFormat("mp4") 88 .setFormat("mp4")
92 .done(); 89 .done();
93 90
94 -  
95 double finalDurationAll = durationAll; 91 double finalDurationAll = durationAll;
96 FFmpegJob job = executor.createJob(builder, (Progress progress) -> { 92 FFmpegJob job = executor.createJob(builder, (Progress progress) -> {
97 final double duration_ns = finalDurationAll * TimeUnit.SECONDS.toNanos(1); 93 final double duration_ns = finalDurationAll * TimeUnit.SECONDS.toNanos(1);
@@ -107,6 +103,7 @@ public class FFmpegExecUtils { @@ -107,6 +103,7 @@ public class FFmpegExecUtils {
107 // progress.fps.doubleValue(), 103 // progress.fps.doubleValue(),
108 // progress.speed 104 // progress.speed
109 // )); 105 // ));
  106 +
110 if (progress.status.equals(Progress.Status.END)){ 107 if (progress.status.equals(Progress.Status.END)){
111 callBack.run(progress.status.name(), percentage,dest.getName() + File.separator + temp + File.separator + "record.mp4"); 108 callBack.run(progress.status.name(), percentage,dest.getName() + File.separator + temp + File.separator + "record.mp4");
112 System.out.println(System.currentTimeMillis() - startTime); 109 System.out.println(System.currentTimeMillis() - startTime);
@@ -114,7 +111,6 @@ public class FFmpegExecUtils { @@ -114,7 +111,6 @@ public class FFmpegExecUtils {
114 callBack.run(progress.status.name(), percentage, null); 111 callBack.run(progress.status.name(), percentage, null);
115 } 112 }
116 }); 113 });
117 -  
118 job.run(); 114 job.run();
119 return temp; 115 return temp;
120 } 116 }
src/main/java/top/panll/assist/service/VideoFileService.java
1 package top.panll.assist.service; 1 package top.panll.assist.service;
2 2
3 -import com.alibaba.fastjson.JSON; 3 +import com.alibaba.fastjson.JSONObject;
4 import net.bramp.ffmpeg.FFprobe; 4 import net.bramp.ffmpeg.FFprobe;
5 import net.bramp.ffmpeg.probe.FFmpegProbeResult; 5 import net.bramp.ffmpeg.probe.FFmpegProbeResult;
6 import net.bramp.ffmpeg.progress.Progress; 6 import net.bramp.ffmpeg.progress.Progress;
@@ -11,12 +11,15 @@ import org.springframework.context.annotation.Bean; @@ -11,12 +11,15 @@ import org.springframework.context.annotation.Bean;
11 import org.springframework.data.redis.core.StringRedisTemplate; 11 import org.springframework.data.redis.core.StringRedisTemplate;
12 import org.springframework.stereotype.Service; 12 import org.springframework.stereotype.Service;
13 import org.springframework.util.DigestUtils; 13 import org.springframework.util.DigestUtils;
14 -import top.panll.assist.config.RedisUtil; 14 +import top.panll.assist.utils.RedisUtil;
  15 +import top.panll.assist.dto.MergeOrCutTaskInfo;
15 import top.panll.assist.dto.UserSettings; 16 import top.panll.assist.dto.UserSettings;
16 import top.panll.assist.utils.DateUtils; 17 import top.panll.assist.utils.DateUtils;
17 18
18 import java.io.File; 19 import java.io.File;
19 import java.io.IOException; 20 import java.io.IOException;
  21 +import java.nio.file.Files;
  22 +import java.nio.file.attribute.BasicFileAttributes;
20 import java.text.ParseException; 23 import java.text.ParseException;
21 import java.text.SimpleDateFormat; 24 import java.text.SimpleDateFormat;
22 import java.util.*; 25 import java.util.*;
@@ -41,7 +44,9 @@ public class VideoFileService { @@ -41,7 +44,9 @@ public class VideoFileService {
41 private ThreadPoolExecutor processThreadPool; 44 private ThreadPoolExecutor processThreadPool;
42 45
43 private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); 46 private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
  47 + private SimpleDateFormat simpleDateFormatForTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
44 48
  49 + private String keyStr = "MERGEORCUT";
45 50
46 @Bean("threadPoolExecutor") 51 @Bean("threadPoolExecutor")
47 private ThreadPoolExecutor iniThreadPool() { 52 private ThreadPoolExecutor iniThreadPool() {
@@ -84,9 +89,9 @@ public class VideoFileService { @@ -84,9 +89,9 @@ public class VideoFileService {
84 * @param file 89 * @param file
85 * @throws ParseException 90 * @throws ParseException
86 */ 91 */
87 - public void handFile(File file) throws ParseException { 92 + public void handFile(File file) {
88 FFprobe ffprobe = FFmpegExecUtils.getInstance().ffprobe; 93 FFprobe ffprobe = FFmpegExecUtils.getInstance().ffprobe;
89 - if(file.isFile() && !file.getName().startsWith(".")&& file.getName().endsWith(".mp4") && file.getName().indexOf(":") < 0) { 94 + if(file.exists() && file.isFile() && !file.getName().startsWith(".")&& file.getName().endsWith(".mp4") && file.getName().indexOf(":") < 0) {
90 try { 95 try {
91 FFmpegProbeResult in = null; 96 FFmpegProbeResult in = null;
92 in = ffprobe.probe(file.getAbsolutePath()); 97 in = ffprobe.probe(file.getAbsolutePath());
@@ -107,8 +112,11 @@ public class VideoFileService { @@ -107,8 +112,11 @@ public class VideoFileService {
107 simpleDateFormat.format(startTime) + "-" + simpleDateFormat.format(endTime) + "-" + durationLong + ".mp4"); 112 simpleDateFormat.format(startTime) + "-" + simpleDateFormat.format(endTime) + "-" + durationLong + ".mp4");
108 file.renameTo(new File(newName)); 113 file.renameTo(new File(newName));
109 System.out.println(newName); 114 System.out.println(newName);
110 - } catch (IOException exception) {  
111 - exception.printStackTrace(); 115 + } catch (IOException e) {
  116 + logger.warn("文件可能以损坏[{}]", file.getAbsolutePath());
  117 +// e.printStackTrace();
  118 + } catch (ParseException e) {
  119 + logger.error("时间格式化失败", e.getMessage());
112 } 120 }
113 } 121 }
114 } 122 }
@@ -127,19 +135,30 @@ public class VideoFileService { @@ -127,19 +135,30 @@ public class VideoFileService {
127 data.put("app", appFile.getName()); 135 data.put("app", appFile.getName());
128 data.put("stream", streamFile.getName()); 136 data.put("stream", streamFile.getName());
129 137
130 -// BasicFileAttributes bAttributes = null;  
131 -// try {  
132 -// bAttributes = Files.readAttributes(streamFile.toPath(),  
133 -// BasicFileAttributes.class);  
134 -// } catch (IOException e) {  
135 -// e.printStackTrace();  
136 -// }  
137 -// data.put("time", simpleDateFormat.format(new Date(bAttributes.lastModifiedTime().toMillis()))); 138 + BasicFileAttributes bAttributes = null;
  139 + try {
  140 + bAttributes = Files.readAttributes(streamFile.toPath(),
  141 + BasicFileAttributes.class);
  142 + } catch (IOException e) {
  143 + e.printStackTrace();
  144 + }
  145 + data.put("time", simpleDateFormatForTime.format(new Date(bAttributes.lastModifiedTime().toMillis())));
138 result.add(data); 146 result.add(data);
139 } 147 }
140 } 148 }
141 } 149 }
142 } 150 }
  151 + result.sort((Map f1, Map f2)->{
  152 + Date time1 = null;
  153 + Date time2 = null;
  154 + try {
  155 + time1 = simpleDateFormatForTime.parse(f1.get("time").toString());
  156 + time2 = simpleDateFormatForTime.parse(f2.get("time").toString());
  157 + } catch (ParseException e) {
  158 + logger.error("时间格式化失败", e.getMessage());
  159 + }
  160 + return time1.compareTo(time2) * -1;
  161 + });
143 return result; 162 return result;
144 } 163 }
145 164
@@ -175,14 +194,16 @@ public class VideoFileService { @@ -175,14 +194,16 @@ public class VideoFileService {
175 } 194 }
176 File[] dateFiles = streamFile.listFiles((File dir, String name) -> { 195 File[] dateFiles = streamFile.listFiles((File dir, String name) -> {
177 Date fileDate = null; 196 Date fileDate = null;
  197 + Date startDate = new Date(startTime.getTime() - ((startTime.getTime() + 28800000) % (86400000)));
  198 + Date endDate = new Date(endTime.getTime() - ((endTime.getTime() + 28800000) % (86400000)));
178 try { 199 try {
179 fileDate = formatterForDate.parse(name); 200 fileDate = formatterForDate.parse(name);
180 } catch (ParseException e) { 201 } catch (ParseException e) {
181 logger.error("过滤日期文件时异常: {}-{}", name, e.getMessage()); 202 logger.error("过滤日期文件时异常: {}-{}", name, e.getMessage());
182 return false; 203 return false;
183 } 204 }
184 - return (DateUtils.getStartOfDay(fileDate).compareTo(startTime) >= 0  
185 - && DateUtils.getStartOfDay(fileDate).compareTo(endTime) <= 0) ; 205 + return (DateUtils.getStartOfDay(fileDate).compareTo(startDate) <= 0
  206 + && DateUtils.getStartOfDay(fileDate).compareTo(endDate) >= 0) ;
186 }); 207 });
187 208
188 if (dateFiles != null && dateFiles.length > 0) { 209 if (dateFiles != null && dateFiles.length > 0) {
@@ -235,50 +256,131 @@ public class VideoFileService { @@ -235,50 +256,131 @@ public class VideoFileService {
235 256
236 public String mergeOrCut(String app, String stream, Date startTime, Date endTime) { 257 public String mergeOrCut(String app, String stream, Date startTime, Date endTime) {
237 List<File> filesInTime = this.getFilesInTime(app, stream, startTime, endTime); 258 List<File> filesInTime = this.getFilesInTime(app, stream, startTime, endTime);
  259 + if (filesInTime== null || filesInTime.size() == 0){
  260 + logger.info("此时间段未未找到视频文件");
  261 + return null;
  262 + }
238 File recordFile = new File(new File(userSettings.getRecord()).getParentFile().getAbsolutePath() + File.separator + "recordTemp"); 263 File recordFile = new File(new File(userSettings.getRecord()).getParentFile().getAbsolutePath() + File.separator + "recordTemp");
239 if (!recordFile.exists()) recordFile.mkdirs(); 264 if (!recordFile.exists()) recordFile.mkdirs();
240 265
241 - String temp = DigestUtils.md5DigestAsHex(String.valueOf(System.currentTimeMillis()).getBytes());  
242 - processThreadPool.execute(() -> {  
243 - FFmpegExecUtils.getInstance().mergeOrCutFile(filesInTime, recordFile, temp, (String status, double percentage, String result)->{  
244 - Map<String, String> data = new HashMap<>();  
245 - data.put("id", temp); 266 + String taskId = DigestUtils.md5DigestAsHex(String.valueOf(System.currentTimeMillis()).getBytes());
  267 + MergeOrCutTaskInfo mergeOrCutTaskInfo = new MergeOrCutTaskInfo();
  268 + mergeOrCutTaskInfo.setId(taskId);
  269 + mergeOrCutTaskInfo.setApp(app);
  270 + mergeOrCutTaskInfo.setStream(stream);
  271 + mergeOrCutTaskInfo.setStartTime(simpleDateFormatForTime.format(startTime));
  272 + mergeOrCutTaskInfo.setEndTime(simpleDateFormatForTime.format(endTime));
  273 +
  274 + Runnable task = () -> {
  275 + FFmpegExecUtils.getInstance().mergeOrCutFile(filesInTime, recordFile, taskId, (String status, double percentage, String result)->{
  276 +
246 // 发出redis通知 277 // 发出redis通知
247 if (status.equals(Progress.Status.END.name())) { 278 if (status.equals(Progress.Status.END.name())) {
248 - data.put("percentage", "1");  
249 - data.put("recordFile", result);  
250 - redisUtil.set(app + "_" + stream + "_" + temp, data, 3*60*60);  
251 - stringRedisTemplate.convertAndSend("topic_mergeorcut_end", JSON.toJSONString(data)); 279 + mergeOrCutTaskInfo.setPercentage("1");
  280 + mergeOrCutTaskInfo.setRecordFile(result);
  281 + stringRedisTemplate.convertAndSend("topic_mergeorcut_end", JSONObject.toJSONString(mergeOrCutTaskInfo));
252 }else { 282 }else {
253 - data.put("percentage", percentage + "");  
254 - redisUtil.set(app + "_" + stream + "_" + temp, data, 3*60*60);  
255 - stringRedisTemplate.convertAndSend("topic_mergeorcut_continue", JSON.toJSONString(data)); 283 + mergeOrCutTaskInfo.setPercentage(percentage + "");
  284 + stringRedisTemplate.convertAndSend("topic_mergeorcut_continue", JSONObject.toJSONString(mergeOrCutTaskInfo));
256 } 285 }
  286 + String key = String.format("%S_%S_%S_%S", keyStr, app, stream, taskId);
  287 + redisUtil.set(key, mergeOrCutTaskInfo);
257 }); 288 });
258 - });  
259 - return temp; 289 + };
  290 + processThreadPool.execute(task);
  291 + return taskId;
260 } 292 }
261 293
262 294
263 - public List<File> getDateList(String app, String stream) { 295 + public List<File> getDateList(String app, String stream, Integer year, Integer month) {
264 File recordFile = new File(userSettings.getRecord()); 296 File recordFile = new File(userSettings.getRecord());
265 File streamFile = new File(recordFile.getAbsolutePath() + File.separator + app + File.separator + stream); 297 File streamFile = new File(recordFile.getAbsolutePath() + File.separator + app + File.separator + stream);
266 if (!streamFile.exists()) { 298 if (!streamFile.exists()) {
267 logger.warn("获取[app: {}, stream: {}]的视频时未找到目录: {}", app, stream, stream); 299 logger.warn("获取[app: {}, stream: {}]的视频时未找到目录: {}", app, stream, stream);
268 return null; 300 return null;
269 } 301 }
270 - File[] dateFiles = streamFile.listFiles(); 302 + File[] dateFiles = streamFile.listFiles((File dir, String name)->{
  303 + Date date = null;
  304 + try {
  305 + date = simpleDateFormat.parse(name);
  306 + } catch (ParseException e) {
  307 + logger.error("格式化时间{}错误", name);
  308 + }
  309 + Calendar c = Calendar.getInstance();
  310 + c.setTime(date);
  311 + int y = c.get(Calendar.YEAR);
  312 + int m = c.get(Calendar.MONTH);
  313 + if (year != null) {
  314 + if (month != null) {
  315 + return y == year && m == month;
  316 + }else {
  317 + return y == year;
  318 + }
  319 + }else {
  320 + return true;
  321 + }
  322 +
  323 + });
271 List<File> dateFileList = Arrays.asList(dateFiles); 324 List<File> dateFileList = Arrays.asList(dateFiles);
  325 +
272 dateFileList.sort((File f1, File f2)->{ 326 dateFileList.sort((File f1, File f2)->{
273 int sortResult = 0; 327 int sortResult = 0;
274 - SimpleDateFormat formatterForDate = new SimpleDateFormat("yyyy-MM-dd"); 328 +
275 try { 329 try {
276 - sortResult = formatterForDate.parse(f1.getName()).compareTo(formatterForDate.parse(f2.getName())); 330 + sortResult = simpleDateFormat.parse(f1.getName()).compareTo(simpleDateFormat.parse(f2.getName()));
277 } catch (ParseException e) { 331 } catch (ParseException e) {
278 - e.printStackTrace(); 332 + logger.error("格式化时间{}/{}错误", f1.getName(), f2.getName());
279 } 333 }
280 return sortResult; 334 return sortResult;
281 }); 335 });
282 return dateFileList; 336 return dateFileList;
283 } 337 }
  338 +
  339 + public List<MergeOrCutTaskInfo> getTaskListForDownload(boolean idEnd) {
  340 + ArrayList<MergeOrCutTaskInfo> result = new ArrayList<>();
  341 + List<Object> taskCatch = redisUtil.scan(String.format("%S_*_*_*", keyStr));
  342 + for (int i = 0; i < taskCatch.size(); i++) {
  343 + String keyItem = taskCatch.get(i).toString();
  344 + MergeOrCutTaskInfo mergeOrCutTaskInfo = (MergeOrCutTaskInfo)redisUtil.get(keyItem);
  345 + if (mergeOrCutTaskInfo != null){
  346 + if (idEnd) {
  347 + if (Double.parseDouble(mergeOrCutTaskInfo.getPercentage()) == 1){
  348 + result.add(mergeOrCutTaskInfo);
  349 + }
  350 + }else {
  351 + if (Double.parseDouble(mergeOrCutTaskInfo.getPercentage()) < 1){
  352 + result.add((MergeOrCutTaskInfo)redisUtil.get(keyItem));
  353 + }
  354 + }
  355 + }
  356 + }
  357 + result.sort((MergeOrCutTaskInfo m1, MergeOrCutTaskInfo m2)->{
  358 + int sortResult = 0;
  359 + try {
  360 + sortResult = simpleDateFormatForTime.parse(m1.getStartTime()).compareTo(simpleDateFormatForTime.parse(m2.getStartTime()));
  361 + if (sortResult == 0) {
  362 + sortResult = simpleDateFormatForTime.parse(m1.getEndTime()).compareTo(simpleDateFormatForTime.parse(m2.getEndTime()));
  363 + }
  364 + } catch (ParseException e) {
  365 + e.printStackTrace();
  366 + }
  367 + return sortResult * -1;
  368 + });
  369 + return result;
  370 + }
  371 +
  372 + public boolean stopTask(String taskId) {
  373 +// Runnable task = taskList.get(taskId);
  374 +// boolean result = false;
  375 +// if (task != null) {
  376 +// processThreadPool.remove(task);
  377 +// taskList.remove(taskId);
  378 +// List<Object> taskCatch = redisUtil.scan(String.format("%S_*_*_%S", keyStr, taskId));
  379 +// if (taskCatch.size() == 1) {
  380 +// redisUtil.del((String) taskCatch.get(0));
  381 +// result = true;
  382 +// }
  383 +// }
  384 + return false;
  385 + }
284 } 386 }
src/main/java/top/panll/assist/config/RedisUtil.java renamed to src/main/java/top/panll/assist/utils/RedisUtil.java
1 -package top.panll.assist.config; 1 +package top.panll.assist.utils;
2 2
3 import org.springframework.beans.factory.annotation.Autowired; 3 import org.springframework.beans.factory.annotation.Autowired;
4 import org.springframework.data.redis.core.*; 4 import org.springframework.data.redis.core.*;
@@ -18,7 +18,7 @@ import java.util.concurrent.TimeUnit; @@ -18,7 +18,7 @@ import java.util.concurrent.TimeUnit;
18 public class RedisUtil { 18 public class RedisUtil {
19 19
20 @Autowired 20 @Autowired
21 - private RedisTemplate redisTemplate; 21 + private RedisTemplate<Object, Object> redisTemplate;
22 22
23 /** 23 /**
24 * 指定缓存失效时间 24 * 指定缓存失效时间
@@ -474,7 +474,7 @@ public class RedisUtil { @@ -474,7 +474,7 @@ public class RedisUtil {
474 * @param end 474 * @param end
475 * @return 475 * @return
476 */ 476 */
477 - public Set<ZSetOperations.TypedTuple<String>> zRangeWithScore(Object key, int start, int end) { 477 + public Set<ZSetOperations.TypedTuple<Object>> zRangeWithScore(Object key, int start, int end) {
478 return redisTemplate.opsForZSet().rangeWithScores(key, start, end); 478 return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
479 } 479 }
480 /** 480 /**
@@ -487,7 +487,7 @@ public class RedisUtil { @@ -487,7 +487,7 @@ public class RedisUtil {
487 * @param end 487 * @param end
488 * @return 488 * @return
489 */ 489 */
490 - public Set<String> zRevRange(Object key, int start, int end) { 490 + public Set<Object> zRevRange(Object key, int start, int end) {
491 return redisTemplate.opsForZSet().reverseRange(key, start, end); 491 return redisTemplate.opsForZSet().reverseRange(key, start, end);
492 } 492 }
493 /** 493 /**
@@ -498,7 +498,7 @@ public class RedisUtil { @@ -498,7 +498,7 @@ public class RedisUtil {
498 * @param max 498 * @param max
499 * @return 499 * @return
500 */ 500 */
501 - public Set<String> zSortRange(Object key, int min, int max) { 501 + public Set<Object> zSortRange(Object key, int min, int max) {
502 return redisTemplate.opsForZSet().rangeByScore(key, min, max); 502 return redisTemplate.opsForZSet().rangeByScore(key, min, max);
503 } 503 }
504 504
@@ -666,7 +666,7 @@ public class RedisUtil { @@ -666,7 +666,7 @@ public class RedisUtil {
666 */ 666 */
667 public List<Object> keys(String key) { 667 public List<Object> keys(String key) {
668 try { 668 try {
669 - Set<String> set = redisTemplate.keys(key); 669 + Set<Object> set = redisTemplate.keys(key);
670 return new ArrayList<>(set); 670 return new ArrayList<>(set);
671 } catch (Exception e) { 671 } catch (Exception e) {
672 e.printStackTrace(); 672 e.printStackTrace();
src/main/resources/application-dev.yml
@@ -46,8 +46,7 @@ logging: @@ -46,8 +46,7 @@ logging:
46 max-size: 10MB 46 max-size: 10MB
47 total-size-cap: 300MB 47 total-size-cap: 300MB
48 level: 48 level:
49 - com:  
50 - genersoft:  
51 - iot: info  
52 - net:  
53 - bramp: warning  
54 \ No newline at end of file 49 \ No newline at end of file
  50 + root: WARN
  51 + top:
  52 + panll:
  53 + assist: info
55 \ No newline at end of file 54 \ No newline at end of file
src/main/resources/application-local.yml
@@ -6,7 +6,7 @@ spring: @@ -6,7 +6,7 @@ spring:
6 # [必须修改] 端口号 6 # [必须修改] 端口号
7 port: 6379 7 port: 6379
8 # [可选] 数据库 DB 8 # [可选] 数据库 DB
9 - database: 8 9 + database: 10
10 # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 10 # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接
11 password: 11 password:
12 # [可选] 超时时间 12 # [可选] 超时时间
@@ -29,7 +29,7 @@ server: @@ -29,7 +29,7 @@ server:
29 # [根据业务需求配置] 29 # [根据业务需求配置]
30 userSettings: 30 userSettings:
31 # [必选 ] zlm配置的录像路径 31 # [必选 ] zlm配置的录像路径
32 - record: /media/lin/Server/ZLMediaKit/dev/ZLMediaKit/release/linux/Debug/www/record 32 + record: /home/lin/server/ZLMediaKit/release/linux/Debug/www/record/
33 # [可选 ] 录像保存时长(单位: 天)每天晚12点自动对过期文件执行清理 33 # [可选 ] 录像保存时长(单位: 天)每天晚12点自动对过期文件执行清理
34 recordDay: 7 34 recordDay: 7
35 # [必选 ] ffmpeg路径 35 # [必选 ] ffmpeg路径
@@ -45,8 +45,7 @@ logging: @@ -45,8 +45,7 @@ logging:
45 max-size: 10MB 45 max-size: 10MB
46 total-size-cap: 300MB 46 total-size-cap: 300MB
47 level: 47 level:
48 - com:  
49 - genersoft:  
50 - iot: info  
51 - net:  
52 - bramp: warning  
53 \ No newline at end of file 48 \ No newline at end of file
  49 + root: WARN
  50 + top:
  51 + panll:
  52 + assist: debug
54 \ No newline at end of file 53 \ No newline at end of file