Commit 60dfc69c58f9fcaea1baea2f76cbf78fe19c86dd
1 parent
7360de7f
优化录像路径配置,不再使用zlm默认http服务器
Showing
32 changed files
with
504 additions
and
88 deletions
.gitignore
100644 → 100755
LICENSE
100644 → 100755
README.md
100644 → 100755
pom.xml
100644 → 100755
| @@ -69,10 +69,28 @@ | @@ -69,10 +69,28 @@ | ||
| 69 | </dependency> | 69 | </dependency> |
| 70 | 70 | ||
| 71 | <dependency> | 71 | <dependency> |
| 72 | + <groupId>org.mp4parser</groupId> | ||
| 73 | + <artifactId>muxer</artifactId> | ||
| 74 | + <version>1.9.56</version> | ||
| 75 | + </dependency> | ||
| 76 | + <dependency> | ||
| 77 | + <groupId>org.mp4parser</groupId> | ||
| 78 | + <artifactId>streaming</artifactId> | ||
| 79 | + <version>1.9.56</version> | ||
| 80 | + </dependency> | ||
| 81 | + | ||
| 82 | + <dependency> | ||
| 83 | + <groupId>org.mp4parser</groupId> | ||
| 84 | + <artifactId>isoparser</artifactId> | ||
| 85 | + <version>1.9.27</version> | ||
| 86 | + </dependency> | ||
| 87 | + | ||
| 88 | + <dependency> | ||
| 72 | <groupId>org.springframework.boot</groupId> | 89 | <groupId>org.springframework.boot</groupId> |
| 73 | <artifactId>spring-boot-starter-test</artifactId> | 90 | <artifactId>spring-boot-starter-test</artifactId> |
| 74 | <scope>test</scope> | 91 | <scope>test</scope> |
| 75 | </dependency> | 92 | </dependency> |
| 93 | + | ||
| 76 | </dependencies> | 94 | </dependencies> |
| 77 | 95 | ||
| 78 | <build> | 96 | <build> |
src/main/java/top/panll/assist/WvpProAssistApplication.java
100644 → 100755
src/main/java/top/panll/assist/config/FastJsonRedisSerializer.java
100644 → 100755
src/main/java/top/panll/assist/config/GlobalExceptionHandler.java
100644 → 100755
src/main/java/top/panll/assist/config/GlobalResponseAdvice.java
100644 → 100755
src/main/java/top/panll/assist/config/RedisConfig.java
100644 → 100755
src/main/java/top/panll/assist/config/SpringDocConfig.java
100644 → 100755
src/main/java/top/panll/assist/config/StartConfig.java
100644 → 100755
| @@ -41,29 +41,39 @@ public class StartConfig implements CommandLineRunner { | @@ -41,29 +41,39 @@ public class StartConfig implements CommandLineRunner { | ||
| 41 | if (!record.endsWith(File.separator)) { | 41 | if (!record.endsWith(File.separator)) { |
| 42 | userSettings.setRecord(userSettings.getRecord() + File.separator); | 42 | userSettings.setRecord(userSettings.getRecord() + File.separator); |
| 43 | } | 43 | } |
| 44 | + | ||
| 44 | File recordFile = new File(record); | 45 | File recordFile = new File(record); |
| 45 | - if (!recordFile.exists() || !recordFile.isDirectory()) { | ||
| 46 | - logger.error("[userSettings.record]配置错误,请检查路径是否存在"); | ||
| 47 | - System.exit(1); | ||
| 48 | - } | ||
| 49 | - if (!recordFile.canRead()) { | ||
| 50 | - logger.error("[userSettings.record]路径无法读取"); | ||
| 51 | - System.exit(1); | ||
| 52 | - } | ||
| 53 | - if (!recordFile.canWrite()) { | ||
| 54 | - logger.error("[userSettings.record]路径无法写入"); | ||
| 55 | - System.exit(1); | 46 | + if (!recordFile.exists()){ |
| 47 | + logger.warn("[userSettings.record]路径不存在,开始创建"); | ||
| 48 | + boolean mkResult = recordFile.mkdirs(); | ||
| 49 | + if (!mkResult) { | ||
| 50 | + logger.info("[userSettings.record]目录创建失败"); | ||
| 51 | + System.exit(1); | ||
| 52 | + } | ||
| 53 | + }else { | ||
| 54 | + if ( !recordFile.isDirectory()) { | ||
| 55 | + logger.warn("[userSettings.record]路径是文件,请修改为目录"); | ||
| 56 | + System.exit(1); | ||
| 57 | + } | ||
| 58 | + if (!recordFile.canRead()) { | ||
| 59 | + logger.error("[userSettings.record]路径无法读取"); | ||
| 60 | + System.exit(1); | ||
| 61 | + } | ||
| 62 | + if (!recordFile.canWrite()) { | ||
| 63 | + logger.error("[userSettings.record]路径无法写入"); | ||
| 64 | + System.exit(1); | ||
| 65 | + } | ||
| 56 | } | 66 | } |
| 57 | - // 在zlm目录写入assist下载页面 | ||
| 58 | - writeAssistDownPage(recordFile); | 67 | + |
| 59 | try { | 68 | try { |
| 60 | 69 | ||
| 61 | -// FFmpegExecUtils.getInstance().ffmpeg = ffmpeg; | ||
| 62 | -// FFmpegExecUtils.getInstance().ffprobe = ffprobe; | ||
| 63 | // 对目录进行预整理 | 70 | // 对目录进行预整理 |
| 64 | File[] appFiles = recordFile.listFiles(); | 71 | File[] appFiles = recordFile.listFiles(); |
| 65 | if (appFiles != null && appFiles.length > 0) { | 72 | if (appFiles != null && appFiles.length > 0) { |
| 66 | for (File appFile : appFiles) { | 73 | for (File appFile : appFiles) { |
| 74 | + if (appFile.getName().equals("recordTemp")) { | ||
| 75 | + continue; | ||
| 76 | + } | ||
| 67 | File[] streamFiles = appFile.listFiles(); | 77 | File[] streamFiles = appFile.listFiles(); |
| 68 | if (streamFiles != null && streamFiles.length > 0) { | 78 | if (streamFiles != null && streamFiles.length > 0) { |
| 69 | for (File streamFile : streamFiles) { | 79 | for (File streamFile : streamFiles) { |
| @@ -91,48 +101,48 @@ public class StartConfig implements CommandLineRunner { | @@ -91,48 +101,48 @@ public class StartConfig implements CommandLineRunner { | ||
| 91 | } | 101 | } |
| 92 | } | 102 | } |
| 93 | 103 | ||
| 94 | - private void writeAssistDownPage(File recordFile) { | ||
| 95 | - try { | ||
| 96 | - File file = new File(recordFile.getParentFile().getAbsolutePath(), "download.html"); | ||
| 97 | - if (file.exists()) { | ||
| 98 | - file.delete(); | ||
| 99 | - } | ||
| 100 | - file.createNewFile(); | ||
| 101 | - FileOutputStream fs = new FileOutputStream(file); | ||
| 102 | - StringBuffer stringBuffer = new StringBuffer(); | ||
| 103 | - String content = "<!DOCTYPE html>\n" + | ||
| 104 | - "<html lang=\"en\">\n" + | ||
| 105 | - "<head>\n" + | ||
| 106 | - " <meta charset=\"UTF-8\">\n" + | ||
| 107 | - " <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n" + | ||
| 108 | - " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" + | ||
| 109 | - " <title>下载</title>\n" + | ||
| 110 | - "</head>\n" + | ||
| 111 | - "<body>\n" + | ||
| 112 | - " <a id=\"download\" download />\n" + | ||
| 113 | - " <script>\n" + | ||
| 114 | - " (function(){\n" + | ||
| 115 | - " let searchParams = new URLSearchParams(location.search);\n" + | ||
| 116 | - " var download = document.getElementById(\"download\");\n" + | ||
| 117 | - " download.setAttribute(\"href\", searchParams.get(\"url\"))\n" + | ||
| 118 | - " download.click()\n" + | ||
| 119 | - " setTimeout(()=>{\n" + | ||
| 120 | - " window.location.href=\"about:blank\";\n" + | ||
| 121 | - "\t\t\t window.close();\n" + | ||
| 122 | - " },200)\n" + | ||
| 123 | - " })();\n" + | ||
| 124 | - " \n" + | ||
| 125 | - " </script>\n" + | ||
| 126 | - "</body>\n" + | ||
| 127 | - "</html>"; | ||
| 128 | - fs.write(content.getBytes(StandardCharsets.UTF_8)); | ||
| 129 | - logger.info("已写入html配置页面: " + file.getAbsolutePath()); | ||
| 130 | - } catch (FileNotFoundException e) { | ||
| 131 | - logger.error("写入html页面错误", e); | ||
| 132 | - } catch (IOException e) { | ||
| 133 | - logger.error("写入html页面错误", e); | ||
| 134 | - } | ||
| 135 | - | ||
| 136 | - | ||
| 137 | - } | 104 | +// private void writeAssistDownPage(File recordFile) { |
| 105 | +// try { | ||
| 106 | +// File file = new File(recordFile.getParentFile().getAbsolutePath(), "download.html"); | ||
| 107 | +// if (file.exists()) { | ||
| 108 | +// file.delete(); | ||
| 109 | +// } | ||
| 110 | +// file.createNewFile(); | ||
| 111 | +// FileOutputStream fs = new FileOutputStream(file); | ||
| 112 | +// StringBuffer stringBuffer = new StringBuffer(); | ||
| 113 | +// String content = "<!DOCTYPE html>\n" + | ||
| 114 | +// "<html lang=\"en\">\n" + | ||
| 115 | +// "<head>\n" + | ||
| 116 | +// " <meta charset=\"UTF-8\">\n" + | ||
| 117 | +// " <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n" + | ||
| 118 | +// " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" + | ||
| 119 | +// " <title>下载</title>\n" + | ||
| 120 | +// "</head>\n" + | ||
| 121 | +// "<body>\n" + | ||
| 122 | +// " <a id=\"download\" download />\n" + | ||
| 123 | +// " <script>\n" + | ||
| 124 | +// " (function(){\n" + | ||
| 125 | +// " let searchParams = new URLSearchParams(location.search);\n" + | ||
| 126 | +// " var download = document.getElementById(\"download\");\n" + | ||
| 127 | +// " download.setAttribute(\"href\", searchParams.get(\"url\"))\n" + | ||
| 128 | +// " download.click()\n" + | ||
| 129 | +// " setTimeout(()=>{\n" + | ||
| 130 | +// " window.location.href=\"about:blank\";\n" + | ||
| 131 | +// "\t\t\t window.close();\n" + | ||
| 132 | +// " },200)\n" + | ||
| 133 | +// " })();\n" + | ||
| 134 | +// " \n" + | ||
| 135 | +// " </script>\n" + | ||
| 136 | +// "</body>\n" + | ||
| 137 | +// "</html>"; | ||
| 138 | +// fs.write(content.getBytes(StandardCharsets.UTF_8)); | ||
| 139 | +// logger.info("已写入html配置页面: " + file.getAbsolutePath()); | ||
| 140 | +// } catch (FileNotFoundException e) { | ||
| 141 | +// logger.error("写入html页面错误", e); | ||
| 142 | +// } catch (IOException e) { | ||
| 143 | +// logger.error("写入html页面错误", e); | ||
| 144 | +// } | ||
| 145 | +// | ||
| 146 | +// | ||
| 147 | +// } | ||
| 138 | } | 148 | } |
src/main/java/top/panll/assist/config/ThreadPoolTaskConfig.java
100644 → 100755
src/main/java/top/panll/assist/controller/DownController.java
0 → 100644
| 1 | +package top.panll.assist.controller; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +import org.apache.catalina.connector.ClientAbortException; | ||
| 5 | +import org.mp4parser.BasicContainer; | ||
| 6 | +import org.mp4parser.Container; | ||
| 7 | +import org.mp4parser.muxer.Movie; | ||
| 8 | +import org.mp4parser.muxer.Track; | ||
| 9 | +import org.mp4parser.muxer.builder.DefaultMp4Builder; | ||
| 10 | +import org.mp4parser.muxer.builder.Mp4Builder; | ||
| 11 | +import org.mp4parser.muxer.container.mp4.MovieCreator; | ||
| 12 | +import org.mp4parser.muxer.tracks.AppendTrack; | ||
| 13 | +import org.slf4j.Logger; | ||
| 14 | +import org.slf4j.LoggerFactory; | ||
| 15 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 16 | +import org.springframework.stereotype.Controller; | ||
| 17 | +import org.springframework.web.bind.annotation.GetMapping; | ||
| 18 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
| 19 | +import org.springframework.web.bind.annotation.ResponseBody; | ||
| 20 | +import top.panll.assist.dto.UserSettings; | ||
| 21 | + | ||
| 22 | +import javax.servlet.http.HttpServletRequest; | ||
| 23 | +import javax.servlet.http.HttpServletResponse; | ||
| 24 | +import java.io.*; | ||
| 25 | +import java.nio.channels.Channels; | ||
| 26 | +import java.nio.channels.WritableByteChannel; | ||
| 27 | +import java.nio.charset.StandardCharsets; | ||
| 28 | +import java.util.ArrayList; | ||
| 29 | +import java.util.LinkedList; | ||
| 30 | +import java.util.List; | ||
| 31 | + | ||
| 32 | +@Controller | ||
| 33 | +@RequestMapping("/down") | ||
| 34 | +public class DownController { | ||
| 35 | + | ||
| 36 | + private final static Logger logger = LoggerFactory.getLogger(DownController.class); | ||
| 37 | + | ||
| 38 | + @Autowired | ||
| 39 | + private UserSettings userSettings; | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * 获取app+stream列表 | ||
| 43 | + * | ||
| 44 | + * @return | ||
| 45 | + */ | ||
| 46 | + @GetMapping(value = "/**") | ||
| 47 | + @ResponseBody | ||
| 48 | + public void download(HttpServletRequest request, HttpServletResponse response) throws IOException { | ||
| 49 | + | ||
| 50 | + List<String> videoList = new ArrayList<>(); | ||
| 51 | + videoList.add("/home/lin/server/test/zlm/Debug/www/record/rtp/34020000002000000003_34020000001310000001/2023-03-20/16-09-07.mp4"); | ||
| 52 | + videoList.add("/home/lin/server/test/zlm/Debug/www/record/rtp/34020000002000000003_34020000001310000001/2023-03-20/17-12-10.mp4"); | ||
| 53 | + videoList.add("/home/lin/server/test/zlm/Debug/www/record/rtp/34020000002000000003_34020000001310000001/2023-03-20/17-38-36.mp4"); | ||
| 54 | + List<Movie> sourceMovies = new ArrayList<>(); | ||
| 55 | + for (String video : videoList) { | ||
| 56 | + sourceMovies.add(MovieCreator.build(video)); | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + List<Track> videoTracks = new LinkedList<>(); | ||
| 60 | + List<Track> audioTracks = new LinkedList<>(); | ||
| 61 | + for (Movie movie : sourceMovies) { | ||
| 62 | + for (Track track : movie.getTracks()) { | ||
| 63 | + if ("soun".equals(track.getHandler())) { | ||
| 64 | + audioTracks.add(track); | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + if ("vide".equals(track.getHandler())) { | ||
| 68 | + videoTracks.add(track); | ||
| 69 | + } | ||
| 70 | + } | ||
| 71 | + } | ||
| 72 | + Movie mergeMovie = new Movie(); | ||
| 73 | + if (audioTracks.size() > 0) { | ||
| 74 | + mergeMovie.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()]))); | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + if (videoTracks.size() > 0) { | ||
| 78 | + mergeMovie.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()]))); | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + BasicContainer out = (BasicContainer)new DefaultMp4Builder().build(mergeMovie); | ||
| 82 | + | ||
| 83 | + // 文件名 | ||
| 84 | + String fileName = "测试.mp4"; | ||
| 85 | + // 文件类型 | ||
| 86 | + String contentType = request.getServletContext().getMimeType(fileName); | ||
| 87 | + | ||
| 88 | + // 解决下载文件时文件名乱码问题 | ||
| 89 | + byte[] fileNameBytes = fileName.getBytes(StandardCharsets.UTF_8); | ||
| 90 | + fileName = new String(fileNameBytes, 0, fileNameBytes.length, StandardCharsets.ISO_8859_1); | ||
| 91 | + | ||
| 92 | + response.setHeader("Content-Type", contentType); | ||
| 93 | + response.setHeader("Content-Length", String.valueOf(out)); | ||
| 94 | + //inline表示浏览器直接使用,attachment表示下载,fileName表示下载的文件名 | ||
| 95 | + response.setHeader("Content-Disposition", "inline;filename=" + fileName); | ||
| 96 | + response.setContentType(contentType); | ||
| 97 | + | ||
| 98 | + WritableByteChannel writableByteChannel = Channels.newChannel(response.getOutputStream()); | ||
| 99 | + out.writeContainer(writableByteChannel); | ||
| 100 | + writableByteChannel.close(); | ||
| 101 | + | ||
| 102 | + } | ||
| 103 | +} |
src/main/java/top/panll/assist/controller/DownloadController.java
0 → 100644
| 1 | +package top.panll.assist.controller; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +import io.swagger.v3.oas.annotations.Operation; | ||
| 5 | +import io.swagger.v3.oas.annotations.Parameter; | ||
| 6 | +import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 7 | +import org.apache.catalina.connector.ClientAbortException; | ||
| 8 | +import org.slf4j.Logger; | ||
| 9 | +import org.slf4j.LoggerFactory; | ||
| 10 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 11 | +import org.springframework.stereotype.Controller; | ||
| 12 | +import org.springframework.web.bind.annotation.*; | ||
| 13 | +import top.panll.assist.dto.UserSettings; | ||
| 14 | +import top.panll.assist.utils.PageInfo; | ||
| 15 | + | ||
| 16 | +import javax.servlet.http.HttpServletRequest; | ||
| 17 | +import javax.servlet.http.HttpServletResponse; | ||
| 18 | +import java.io.BufferedOutputStream; | ||
| 19 | +import java.io.File; | ||
| 20 | +import java.io.IOException; | ||
| 21 | +import java.io.RandomAccessFile; | ||
| 22 | +import java.nio.charset.StandardCharsets; | ||
| 23 | +import java.util.List; | ||
| 24 | +import java.util.Map; | ||
| 25 | + | ||
| 26 | +@Controller | ||
| 27 | +@RequestMapping("/download") | ||
| 28 | +public class DownloadController { | ||
| 29 | + | ||
| 30 | + private final static Logger logger = LoggerFactory.getLogger(DownloadController.class); | ||
| 31 | + | ||
| 32 | + @Autowired | ||
| 33 | + private UserSettings userSettings; | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 获取app+stream列表 | ||
| 37 | + * | ||
| 38 | + * @return | ||
| 39 | + */ | ||
| 40 | + @GetMapping(value = "/**") | ||
| 41 | + @ResponseBody | ||
| 42 | + public void download(HttpServletRequest request, HttpServletResponse response) { | ||
| 43 | + | ||
| 44 | + String resourcePath = request.getServletPath(); | ||
| 45 | + System.out.println(resourcePath); | ||
| 46 | + resourcePath = resourcePath.substring("/download".length() + 1, resourcePath.length()); | ||
| 47 | + String record = userSettings.getRecord(); | ||
| 48 | +// if (record.endsWith("/")) { | ||
| 49 | +// record = record.substring(0, record.length() - 1); | ||
| 50 | +// System.out.println(record); | ||
| 51 | +// } | ||
| 52 | + System.out.println(record + resourcePath); | ||
| 53 | + File file = new File(record + resourcePath); | ||
| 54 | + if (!file.exists()) { | ||
| 55 | + response.setStatus(HttpServletResponse.SC_NOT_FOUND); | ||
| 56 | + return; | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + /** | ||
| 60 | + * 参考实现来自: CSDN 进修的CODER SpringBoot Java实现Http方式分片下载断点续传+实现H5大视频渐进式播放 | ||
| 61 | + * https://blog.csdn.net/lovequanquqn/article/details/104562945 | ||
| 62 | + */ | ||
| 63 | + String range = request.getHeader("Range"); | ||
| 64 | + logger.info("current request rang:" + range); | ||
| 65 | + //开始下载位置 | ||
| 66 | + long startByte = 0; | ||
| 67 | + //结束下载位置 | ||
| 68 | + long endByte = file.length() - 1; | ||
| 69 | + logger.info("文件开始位置:{},文件结束位置:{},文件总长度:{}", startByte, endByte, file.length()); | ||
| 70 | + | ||
| 71 | + //有range的话 | ||
| 72 | + if (range != null && range.contains("bytes=") && range.contains("-")) { | ||
| 73 | + range = range.substring(range.lastIndexOf("=") + 1).trim(); | ||
| 74 | + String[] ranges = range.split("-"); | ||
| 75 | + try { | ||
| 76 | + //判断range的类型 | ||
| 77 | + if (ranges.length == 1) { | ||
| 78 | + // 类型一:bytes=-2343, | ||
| 79 | + if (range.startsWith("-")) { | ||
| 80 | + endByte = Long.parseLong(ranges[0]); | ||
| 81 | + } | ||
| 82 | + //类型二:bytes=2343- | ||
| 83 | + else if (range.endsWith("-")) { | ||
| 84 | + startByte = Long.parseLong(ranges[0]); | ||
| 85 | + } | ||
| 86 | + } | ||
| 87 | + //类型三:bytes=22-2343 | ||
| 88 | + else if (ranges.length == 2) { | ||
| 89 | + startByte = Long.parseLong(ranges[0]); | ||
| 90 | + endByte = Long.parseLong(ranges[1]); | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + } catch (NumberFormatException e) { | ||
| 94 | + startByte = 0; | ||
| 95 | + endByte = file.length() - 1; | ||
| 96 | + logger.error("Range Occur Error,Message:{}", e.getLocalizedMessage()); | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + // 要下载的长度 | ||
| 103 | + long contentLength = endByte - startByte + 1; | ||
| 104 | + // 文件名 | ||
| 105 | + String fileName = file.getName(); | ||
| 106 | + // 文件类型 | ||
| 107 | + String contentType = request.getServletContext().getMimeType(fileName); | ||
| 108 | + | ||
| 109 | + // 解决下载文件时文件名乱码问题 | ||
| 110 | + byte[] fileNameBytes = fileName.getBytes(StandardCharsets.UTF_8); | ||
| 111 | + fileName = new String(fileNameBytes, 0, fileNameBytes.length, StandardCharsets.ISO_8859_1); | ||
| 112 | + | ||
| 113 | + response.setHeader("Content-Type", contentType); | ||
| 114 | + response.setHeader("Content-Length", String.valueOf(contentLength)); | ||
| 115 | + //inline表示浏览器直接使用,attachment表示下载,fileName表示下载的文件名 | ||
| 116 | + response.setHeader("Content-Disposition", "inline;filename=" + fileName); | ||
| 117 | + response.setContentType(contentType); | ||
| 118 | + if (range != null) { | ||
| 119 | + //各种响应头设置 | ||
| 120 | + //支持断点续传,获取部分字节内容: | ||
| 121 | + response.setHeader("Accept-Ranges", "bytes"); | ||
| 122 | + //http状态码要为206:表示获取部分内容 | ||
| 123 | + response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); | ||
| 124 | + // Content-Range,格式为:[要下载的开始位置]-[结束位置]/[文件总大小] | ||
| 125 | + response.setHeader("Content-Range", "bytes " + startByte + "-" + endByte + "/" + file.length()); | ||
| 126 | + } else { | ||
| 127 | + response.setStatus(HttpServletResponse.SC_OK); | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + | ||
| 131 | + BufferedOutputStream outputStream = null; | ||
| 132 | + RandomAccessFile randomAccessFile = null; | ||
| 133 | + //已传送数据大小 | ||
| 134 | + long transmitted = 0; | ||
| 135 | + try { | ||
| 136 | + randomAccessFile = new RandomAccessFile(file, "r"); | ||
| 137 | + | ||
| 138 | + outputStream = new BufferedOutputStream(response.getOutputStream()); | ||
| 139 | + byte[] buff = new byte[4096]; | ||
| 140 | + int len = 0; | ||
| 141 | + randomAccessFile.seek(startByte); | ||
| 142 | + //warning:判断是否到了最后不足4096(buff的length)个byte这个逻辑((transmitted + len) <= contentLength)要放前面 | ||
| 143 | + //不然会会先读取randomAccessFile,造成后面读取位置出错; | ||
| 144 | + while ((transmitted + len) <= contentLength && (len = randomAccessFile.read(buff)) != -1) { | ||
| 145 | + outputStream.write(buff, 0, len); | ||
| 146 | + transmitted += len; | ||
| 147 | + } | ||
| 148 | + //处理不足buff.length部分 | ||
| 149 | + if (transmitted < contentLength) { | ||
| 150 | + len = randomAccessFile.read(buff, 0, (int) (contentLength - transmitted)); | ||
| 151 | + outputStream.write(buff, 0, len); | ||
| 152 | + transmitted += len; | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + outputStream.flush(); | ||
| 156 | + response.flushBuffer(); | ||
| 157 | + randomAccessFile.close(); | ||
| 158 | + logger.info("下载完毕:" + startByte + "-" + endByte + ":" + transmitted); | ||
| 159 | + } catch (ClientAbortException e) { | ||
| 160 | + logger.warn("用户停止下载:" + startByte + "-" + endByte + ":" + transmitted); | ||
| 161 | + //捕获此异常表示拥护停止下载 | ||
| 162 | + } catch (IOException e) { | ||
| 163 | + e.printStackTrace(); | ||
| 164 | + logger.error("用户下载IO异常,Message:{}", e.getLocalizedMessage()); | ||
| 165 | + } finally { | ||
| 166 | + try { | ||
| 167 | + if (randomAccessFile != null) { | ||
| 168 | + randomAccessFile.close(); | ||
| 169 | + } | ||
| 170 | + } catch (IOException e) { | ||
| 171 | + e.printStackTrace(); | ||
| 172 | + } | ||
| 173 | + }///end try | ||
| 174 | + } | ||
| 175 | +} |
src/main/java/top/panll/assist/controller/RecordController.java
100644 → 100755
| @@ -24,10 +24,8 @@ import javax.servlet.http.HttpServletRequest; | @@ -24,10 +24,8 @@ import javax.servlet.http.HttpServletRequest; | ||
| 24 | import java.io.File; | 24 | import java.io.File; |
| 25 | import java.text.ParseException; | 25 | import java.text.ParseException; |
| 26 | import java.text.SimpleDateFormat; | 26 | import java.text.SimpleDateFormat; |
| 27 | -import java.util.ArrayList; | ||
| 28 | -import java.util.Date; | ||
| 29 | -import java.util.List; | ||
| 30 | -import java.util.Map; | 27 | +import java.util.*; |
| 28 | + | ||
| 31 | @Tag(name = "录像管理", description = "录像管理") | 29 | @Tag(name = "录像管理", description = "录像管理") |
| 32 | @CrossOrigin | 30 | @CrossOrigin |
| 33 | @RestController | 31 | @RestController |
| @@ -49,6 +47,16 @@ public class RecordController { | @@ -49,6 +47,16 @@ public class RecordController { | ||
| 49 | 47 | ||
| 50 | 48 | ||
| 51 | /** | 49 | /** |
| 50 | + * 获取Assist服务配置信息 | ||
| 51 | + */ | ||
| 52 | + @Operation(summary ="获取Assist服务配置信息") | ||
| 53 | + @GetMapping(value = "/info") | ||
| 54 | + @ResponseBody | ||
| 55 | + public UserSettings getInfo(){ | ||
| 56 | + return userSettings; | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + /** | ||
| 52 | * 获取app+stream列表 | 60 | * 获取app+stream列表 |
| 53 | * @return | 61 | * @return |
| 54 | */ | 62 | */ |
| @@ -58,7 +66,7 @@ public class RecordController { | @@ -58,7 +66,7 @@ public class RecordController { | ||
| 58 | @GetMapping(value = "/list") | 66 | @GetMapping(value = "/list") |
| 59 | @ResponseBody | 67 | @ResponseBody |
| 60 | public PageInfo<Map<String, String>> getList(@RequestParam int page, | 68 | public PageInfo<Map<String, String>> getList(@RequestParam int page, |
| 61 | - @RequestParam int count){ | 69 | + @RequestParam int count){ |
| 62 | List<Map<String, String>> appList = videoFileService.getList(); | 70 | List<Map<String, String>> appList = videoFileService.getList(); |
| 63 | 71 | ||
| 64 | PageInfo<Map<String, String>> stringPageInfo = new PageInfo<>(appList); | 72 | PageInfo<Map<String, String>> stringPageInfo = new PageInfo<>(appList); |
| @@ -84,6 +92,7 @@ public class RecordController { | @@ -84,6 +92,7 @@ public class RecordController { | ||
| 84 | resultData.add(file.getName()); | 92 | resultData.add(file.getName()); |
| 85 | } | 93 | } |
| 86 | } | 94 | } |
| 95 | + Collections.sort(resultData); | ||
| 87 | 96 | ||
| 88 | PageInfo<String> stringPageInfo = new PageInfo<>(resultData); | 97 | PageInfo<String> stringPageInfo = new PageInfo<>(resultData); |
| 89 | stringPageInfo.startPage(page, count); | 98 | stringPageInfo.startPage(page, count); |
| @@ -342,9 +351,10 @@ public class RecordController { | @@ -342,9 +351,10 @@ public class RecordController { | ||
| 342 | ret.put("code", 0); | 351 | ret.put("code", 0); |
| 343 | ret.put("msg", "success"); | 352 | ret.put("msg", "success"); |
| 344 | String file_path = json.getString("file_path"); | 353 | String file_path = json.getString("file_path"); |
| 354 | + | ||
| 345 | String app = json.getString("app"); | 355 | String app = json.getString("app"); |
| 346 | String stream = json.getString("stream"); | 356 | String stream = json.getString("stream"); |
| 347 | - logger.debug("ZLM 录制完成,参数:" + file_path); | 357 | + logger.debug("ZLM 录制完成,文件路径:" + file_path); |
| 348 | 358 | ||
| 349 | if (file_path == null) { | 359 | if (file_path == null) { |
| 350 | return new ResponseEntity<String>(ret.toString(), HttpStatus.OK); | 360 | return new ResponseEntity<String>(ret.toString(), HttpStatus.OK); |
| @@ -356,7 +366,6 @@ public class RecordController { | @@ -356,7 +366,6 @@ public class RecordController { | ||
| 356 | videoFileService.handFile(new File(file_path), app, stream); | 366 | videoFileService.handFile(new File(file_path), app, stream); |
| 357 | } | 367 | } |
| 358 | 368 | ||
| 359 | - | ||
| 360 | return new ResponseEntity<String>(ret.toString(), HttpStatus.OK); | 369 | return new ResponseEntity<String>(ret.toString(), HttpStatus.OK); |
| 361 | } | 370 | } |
| 362 | 371 |
src/main/java/top/panll/assist/controller/bean/ErrorCode.java
100644 → 100755
src/main/java/top/panll/assist/controller/bean/WVPResult.java
100644 → 100755
src/main/java/top/panll/assist/dto/MergeOrCutTaskInfo.java
100644 → 100755
src/main/java/top/panll/assist/dto/SignInfo.java
100644 → 100755
src/main/java/top/panll/assist/dto/SpaceInfo.java
100644 → 100755
src/main/java/top/panll/assist/dto/UserSettings.java
100644 → 100755
src/main/java/top/panll/assist/service/FFmpegExecUtils.java
100644 → 100755
src/main/java/top/panll/assist/service/FileManagerTimer.java
100644 → 100755
| @@ -39,6 +39,9 @@ public class FileManagerTimer { | @@ -39,6 +39,9 @@ public class FileManagerTimer { | ||
| 39 | // @Scheduled(fixedDelay = 2000) //测试 20秒执行一次 | 39 | // @Scheduled(fixedDelay = 2000) //测试 20秒执行一次 |
| 40 | @Scheduled(cron = "0 0 0 * * ?") //每天的0点执行 | 40 | @Scheduled(cron = "0 0 0 * * ?") //每天的0点执行 |
| 41 | public void execute(){ | 41 | public void execute(){ |
| 42 | + if (userSettings.getRecord() == null) { | ||
| 43 | + return; | ||
| 44 | + } | ||
| 42 | int recordDay = userSettings.getRecordDay(); | 45 | int recordDay = userSettings.getRecordDay(); |
| 43 | Date lastDate=new Date(); | 46 | Date lastDate=new Date(); |
| 44 | Calendar lastCalendar = Calendar.getInstance(); | 47 | Calendar lastCalendar = Calendar.getInstance(); |
| @@ -115,7 +118,7 @@ public class FileManagerTimer { | @@ -115,7 +118,7 @@ public class FileManagerTimer { | ||
| 115 | lastTempCalendar.add(Calendar.DAY_OF_MONTH, 0 - recordTempDay); | 118 | lastTempCalendar.add(Calendar.DAY_OF_MONTH, 0 - recordTempDay); |
| 116 | lastTempDate = lastTempCalendar.getTime(); | 119 | lastTempDate = lastTempCalendar.getTime(); |
| 117 | logger.info("[录像巡查]移除合并任务临时文件 {} 之前的文件", formatter.format(lastTempDate)); | 120 | logger.info("[录像巡查]移除合并任务临时文件 {} 之前的文件", formatter.format(lastTempDate)); |
| 118 | - File recordTempFile = new File(recordFileDir.getParentFile().getAbsolutePath() + File.separator + "recordTemp"); | 121 | + File recordTempFile = new File(userSettings.getRecord() + "recordTemp"); |
| 119 | if (recordTempFile.exists() && recordTempFile.isDirectory() && recordTempFile.canWrite()) { | 122 | if (recordTempFile.exists() && recordTempFile.isDirectory() && recordTempFile.canWrite()) { |
| 120 | File[] tempFiles = recordTempFile.listFiles(); | 123 | File[] tempFiles = recordTempFile.listFiles(); |
| 121 | for (File tempFile : tempFiles) { | 124 | for (File tempFile : tempFiles) { |
src/main/java/top/panll/assist/service/VideoFileService.java
100644 → 100755
| @@ -48,7 +48,7 @@ public class VideoFileService { | @@ -48,7 +48,7 @@ public class VideoFileService { | ||
| 48 | if (recordFile.isDirectory()) { | 48 | if (recordFile.isDirectory()) { |
| 49 | File[] files = recordFile.listFiles((File dir, String name) -> { | 49 | File[] files = recordFile.listFiles((File dir, String name) -> { |
| 50 | File currentFile = new File(dir.getAbsolutePath() + File.separator + name); | 50 | File currentFile = new File(dir.getAbsolutePath() + File.separator + name); |
| 51 | - return currentFile.isDirectory(); | 51 | + return currentFile.isDirectory() && !name.equals("recordTemp"); |
| 52 | }); | 52 | }); |
| 53 | List<File> result = Arrays.asList(files); | 53 | List<File> result = Arrays.asList(files); |
| 54 | if (sort != null && sort) { | 54 | if (sort != null && sort) { |
| @@ -144,22 +144,17 @@ public class VideoFileService { | @@ -144,22 +144,17 @@ public class VideoFileService { | ||
| 144 | 144 | ||
| 145 | String key = AssistConstants.STREAM_CALL_INFO + userSettings.getId() + "_" + app + "_" + stream; | 145 | String key = AssistConstants.STREAM_CALL_INFO + userSettings.getId() + "_" + app + "_" + stream; |
| 146 | String callId = (String) redisUtil.get(key); | 146 | String callId = (String) redisUtil.get(key); |
| 147 | - if (callId != null) { | ||
| 148 | - | ||
| 149 | - File newPath = new File(file.getParentFile().getParent() + "_" + callId + File.separator + file.getParentFile().getName()); | ||
| 150 | - if (!newPath.exists()) { | ||
| 151 | - newPath.mkdirs(); | ||
| 152 | - } | ||
| 153 | - String newName = newPath.getAbsolutePath() + File.separator+ simpleDateFormat.format(startTime) + "-" + simpleDateFormat.format(endTime) + "-" + durationLong + ".mp4"; | ||
| 154 | - file.renameTo(new File(newName)); | ||
| 155 | - }else { | ||
| 156 | - String newName = file.getAbsolutePath().replace(file.getName(), | ||
| 157 | - simpleDateFormat.format(startTime) + "-" + simpleDateFormat.format(endTime) + "-" + durationLong + ".mp4"); | ||
| 158 | 147 | ||
| 159 | - file.renameTo(new File(newName)); | 148 | + String streamNew = (callId == null? stream : stream + "_" + callId); |
| 149 | + File newPath = new File(userSettings.getRecord() + File.separator + app + File.separator + streamNew + File.separator + DateUtils.getDateStr(new Date(startTime))); | ||
| 150 | + if (!newPath.exists()) { | ||
| 151 | + newPath.mkdirs(); | ||
| 160 | } | 152 | } |
| 161 | 153 | ||
| 162 | - logger.debug("[处理文件] {}", file.getName()); | 154 | + String newName = newPath.getAbsolutePath() + File.separator+ simpleDateFormat.format(startTime) + "-" + simpleDateFormat.format(endTime) + "-" + durationLong + ".mp4"; |
| 155 | + file.renameTo(new File(newName)); | ||
| 156 | + System.out.println(file.getAbsolutePath()); | ||
| 157 | + logger.info("[处理文件] {}", file.getName()); | ||
| 163 | } catch (IOException e) { | 158 | } catch (IOException e) { |
| 164 | logger.warn("文件可能以损坏[{}]", file.getAbsolutePath()); | 159 | logger.warn("文件可能以损坏[{}]", file.getAbsolutePath()); |
| 165 | } catch (ParseException e) { | 160 | } catch (ParseException e) { |
| @@ -339,13 +334,13 @@ public class VideoFileService { | @@ -339,13 +334,13 @@ public class VideoFileService { | ||
| 339 | public String mergeOrCut(String app, String stream, Date startTime, Date endTime, String remoteHost) { | 334 | public String mergeOrCut(String app, String stream, Date startTime, Date endTime, String remoteHost) { |
| 340 | List<File> filesInTime = this.getFilesInTime(app, stream, startTime, endTime); | 335 | List<File> filesInTime = this.getFilesInTime(app, stream, startTime, endTime); |
| 341 | if (filesInTime== null || filesInTime.size() == 0){ | 336 | if (filesInTime== null || filesInTime.size() == 0){ |
| 342 | - logger.info("此时间段未未找到视频文件"); | 337 | + logger.info("此时间段未未找到视频文件, {}/{} {}->{}", app, stream, DateUtils.getDateTimeStr(startTime), DateUtils.getDateTimeStr(endTime)); |
| 343 | return null; | 338 | return null; |
| 344 | } | 339 | } |
| 345 | String taskId = DigestUtils.md5DigestAsHex(String.valueOf(System.currentTimeMillis()).getBytes()); | 340 | String taskId = DigestUtils.md5DigestAsHex(String.valueOf(System.currentTimeMillis()).getBytes()); |
| 346 | logger.info("[录像合并] 开始合并,APP:{}, STREAM: {}, 任务ID:{}", app, stream, taskId); | 341 | logger.info("[录像合并] 开始合并,APP:{}, STREAM: {}, 任务ID:{}", app, stream, taskId); |
| 347 | String destDir = "recordTemp" + File.separator + taskId + File.separator + app; | 342 | String destDir = "recordTemp" + File.separator + taskId + File.separator + app; |
| 348 | - File recordFile = new File(new File(userSettings.getRecord()).getParentFile().getAbsolutePath() + File.separator + destDir ); | 343 | + File recordFile = new File(userSettings.getRecord() + destDir ); |
| 349 | if (!recordFile.exists()) { | 344 | if (!recordFile.exists()) { |
| 350 | recordFile.mkdirs(); | 345 | recordFile.mkdirs(); |
| 351 | } | 346 | } |
| @@ -374,7 +369,7 @@ public class VideoFileService { | @@ -374,7 +369,7 @@ public class VideoFileService { | ||
| 374 | mergeOrCutTaskInfo.setPercentage("1"); | 369 | mergeOrCutTaskInfo.setPercentage("1"); |
| 375 | // 处理文件路径 | 370 | // 处理文件路径 |
| 376 | String recordFileResultPath = recordFile.getAbsolutePath() + File.separator + stream + ".mp4"; | 371 | String recordFileResultPath = recordFile.getAbsolutePath() + File.separator + stream + ".mp4"; |
| 377 | - Path relativize = Paths.get(userSettings.getRecord()).getParent().relativize(Paths.get(recordFileResultPath)); | 372 | + Path relativize = Paths.get(userSettings.getRecord()).relativize(Paths.get(recordFileResultPath)); |
| 378 | try { | 373 | try { |
| 379 | Files.copy(filesInTime.get(0).toPath(), Paths.get(recordFileResultPath)); | 374 | Files.copy(filesInTime.get(0).toPath(), Paths.get(recordFileResultPath)); |
| 380 | } catch (IOException e) { | 375 | } catch (IOException e) { |
| @@ -384,8 +379,8 @@ public class VideoFileService { | @@ -384,8 +379,8 @@ public class VideoFileService { | ||
| 384 | } | 379 | } |
| 385 | mergeOrCutTaskInfo.setRecordFile(relativize.toString()); | 380 | mergeOrCutTaskInfo.setRecordFile(relativize.toString()); |
| 386 | if (remoteHost != null) { | 381 | if (remoteHost != null) { |
| 387 | - mergeOrCutTaskInfo.setDownloadFile(remoteHost + "/download.html?url=" + relativize); | ||
| 388 | - mergeOrCutTaskInfo.setPlayFile(remoteHost + "/" + relativize); | 382 | + mergeOrCutTaskInfo.setDownloadFile(remoteHost + "/download.html?url=download/" + relativize); |
| 383 | + mergeOrCutTaskInfo.setPlayFile(remoteHost + "/download/" + relativize); | ||
| 389 | } | 384 | } |
| 390 | String key = String.format("%S_%S_%S_%S_%S", AssistConstants.MERGEORCUT , userSettings.getId(), mergeOrCutTaskInfo.getApp(), mergeOrCutTaskInfo.getStream(), mergeOrCutTaskInfo.getId()); | 385 | String key = String.format("%S_%S_%S_%S_%S", AssistConstants.MERGEORCUT , userSettings.getId(), mergeOrCutTaskInfo.getApp(), mergeOrCutTaskInfo.getStream(), mergeOrCutTaskInfo.getId()); |
| 391 | redisUtil.set(key, mergeOrCutTaskInfo); | 386 | redisUtil.set(key, mergeOrCutTaskInfo); |
| @@ -397,7 +392,7 @@ public class VideoFileService { | @@ -397,7 +392,7 @@ public class VideoFileService { | ||
| 397 | mergeOrCutTaskInfo.setPercentage("1"); | 392 | mergeOrCutTaskInfo.setPercentage("1"); |
| 398 | 393 | ||
| 399 | // 处理文件路径 | 394 | // 处理文件路径 |
| 400 | - Path relativize = Paths.get(userSettings.getRecord()).getParent().relativize(Paths.get(result)); | 395 | + Path relativize = Paths.get(userSettings.getRecord()).relativize(Paths.get(result)); |
| 401 | mergeOrCutTaskInfo.setRecordFile(relativize.toString()); | 396 | mergeOrCutTaskInfo.setRecordFile(relativize.toString()); |
| 402 | if (remoteHost != null) { | 397 | if (remoteHost != null) { |
| 403 | mergeOrCutTaskInfo.setDownloadFile(remoteHost + "/download.html?url=" + relativize); | 398 | mergeOrCutTaskInfo.setDownloadFile(remoteHost + "/download.html?url=" + relativize); |
src/main/java/top/panll/assist/utils/DateUtils.java
100644 → 100755
| 1 | package top.panll.assist.utils; | 1 | package top.panll.assist.utils; |
| 2 | 2 | ||
| 3 | +import java.text.SimpleDateFormat; | ||
| 3 | import java.time.Instant; | 4 | import java.time.Instant; |
| 4 | import java.time.LocalDateTime; | 5 | import java.time.LocalDateTime; |
| 5 | import java.time.LocalTime; | 6 | import java.time.LocalTime; |
| 6 | import java.time.ZoneId; | 7 | import java.time.ZoneId; |
| 8 | +import java.time.format.DateTimeFormatter; | ||
| 7 | import java.util.Date; | 9 | import java.util.Date; |
| 10 | +import java.util.Locale; | ||
| 8 | 11 | ||
| 9 | public class DateUtils { | 12 | public class DateUtils { |
| 10 | 13 | ||
| 14 | + public static final String PATTERNForDateTime = "yyyy-MM-dd HH:mm:ss"; | ||
| 15 | + | ||
| 16 | + public static final String PATTERNForDate = "yyyy-MM-dd"; | ||
| 17 | + | ||
| 18 | + public static final String zoneStr = "Asia/Shanghai"; | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 11 | // 获得某天最大时间 2020-02-19 23:59:59 | 22 | // 获得某天最大时间 2020-02-19 23:59:59 |
| 12 | public static Date getEndOfDay(Date date) { | 23 | public static Date getEndOfDay(Date date) { |
| 13 | LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());; | 24 | LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());; |
| @@ -22,4 +33,14 @@ public class DateUtils { | @@ -22,4 +33,14 @@ public class DateUtils { | ||
| 22 | return Date.from(startOfDay.atZone(ZoneId.systemDefault()).toInstant()); | 33 | return Date.from(startOfDay.atZone(ZoneId.systemDefault()).toInstant()); |
| 23 | } | 34 | } |
| 24 | 35 | ||
| 36 | + public static String getDateStr(Date date) { | ||
| 37 | + SimpleDateFormat formatter = new SimpleDateFormat(PATTERNForDate); | ||
| 38 | + return formatter.format(date); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + public static String getDateTimeStr(Date date) { | ||
| 42 | + SimpleDateFormat formatter = new SimpleDateFormat(PATTERNForDateTime); | ||
| 43 | + return formatter.format(date); | ||
| 44 | + } | ||
| 45 | + | ||
| 25 | } | 46 | } |
src/main/java/top/panll/assist/utils/PageInfo.java
100644 → 100755
src/main/java/top/panll/assist/utils/RedisUtil.java
100644 → 100755
src/main/resources/all-application.yml
0 → 100755
| 1 | +spring: | ||
| 2 | + # REDIS数据库配置 | ||
| 3 | + redis: | ||
| 4 | + # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 | ||
| 5 | + host: 127.0.0.1 | ||
| 6 | + # [必须修改] 端口号 | ||
| 7 | + port: 6379 | ||
| 8 | + # [可选] 数据库 DB | ||
| 9 | + database: 8 | ||
| 10 | + # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 | ||
| 11 | + password: | ||
| 12 | + # [可选] 超时时间 | ||
| 13 | + timeout: 10000 | ||
| 14 | + | ||
| 15 | +# [必选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 | ||
| 16 | +server: | ||
| 17 | + port: 18081 | ||
| 18 | + # [可选] HTTPS配置, 默认不开启 | ||
| 19 | + ssl: | ||
| 20 | + # [可选] 是否开启HTTPS访问 | ||
| 21 | + enabled: false | ||
| 22 | + # [可选] 证书文件路径,放置在resource/目录下即可,修改xxx为文件名 | ||
| 23 | + key-store: classpath:xxx.jks | ||
| 24 | + # [可选] 证书密码 | ||
| 25 | + key-password: password | ||
| 26 | + # [可选] 证书类型, 默认为jks,根据实际修改 | ||
| 27 | + key-store-type: JKS | ||
| 28 | + | ||
| 29 | +# [根据业务需求配置] | ||
| 30 | +user-settings: | ||
| 31 | + # [可选 ] zlm配置的录像路径,不配置则使用当前目录下的record目录 即: ./record | ||
| 32 | + record: /media/lin/Server/ZLMediaKit/dev/ZLMediaKit/release/linux/Debug/www/record | ||
| 33 | + # [可选 ] 录像保存时长(单位: 天)每天晚12点自动对过期文件执行清理, 不配置则不删除 | ||
| 34 | + recordDay: 7 | ||
| 35 | + # [可选 ] 录像下载合成临时文件保存时长, 不配置默认取值recordDay(单位: 天)每天晚12点自动对过期文件执行清理 | ||
| 36 | + # recordTempDay: 7 | ||
| 37 | + # [必选 ] ffmpeg路径 | ||
| 38 | + ffmpeg: /usr/bin/ffmpeg | ||
| 39 | + # [必选 ] ffprobe路径, 一般安装ffmpeg就会自带, 一般跟ffmpeg在同一目录,用于查询文件的信息 | ||
| 40 | + ffprobe: /usr/bin/ffprobe | ||
| 41 | + # [可选 ] 限制 ffmpeg 合并文件使用的线程数,间接限制cpu使用率, 默认2 限制到50% | ||
| 42 | + threads: 2 | ||
| 43 | + | ||
| 44 | +swagger-ui: | ||
| 45 | + | ||
| 46 | +# [可选] 日志配置, 一般不需要改 | ||
| 47 | +logging: | ||
| 48 | + file: | ||
| 49 | + name: logs/wvp.log | ||
| 50 | + max-history: 30 | ||
| 51 | + max-size: 10MB | ||
| 52 | + total-size-cap: 300MB | ||
| 53 | + level: | ||
| 54 | + root: WARN | ||
| 55 | + top: | ||
| 56 | + panll: | ||
| 57 | + assist: info | ||
| 0 | \ No newline at end of file | 58 | \ No newline at end of file |
src/main/resources/application-dev.yml
100644 → 100755
| @@ -28,7 +28,7 @@ server: | @@ -28,7 +28,7 @@ server: | ||
| 28 | 28 | ||
| 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: /media/lin/Server/ZLMediaKit/dev/ZLMediaKit/release/linux/Debug/www/record |
| 33 | # [可选 ] 录像保存时长(单位: 天)每天晚12点自动对过期文件执行清理 | 33 | # [可选 ] 录像保存时长(单位: 天)每天晚12点自动对过期文件执行清理 |
| 34 | recordDay: 7 | 34 | recordDay: 7 |
src/main/resources/application.yml
100644 → 100755
src/main/resources/static/download.html
0 → 100644
| 1 | +<!DOCTYPE html> | ||
| 2 | +<html lang="en"> | ||
| 3 | +<head> | ||
| 4 | + <meta charset="UTF-8"> | ||
| 5 | + <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
| 6 | + <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| 7 | + <title>下载</title> | ||
| 8 | +</head> | ||
| 9 | +<body> | ||
| 10 | +<a id="download" download></a> | ||
| 11 | +<script> | ||
| 12 | + (function () { | ||
| 13 | + let searchParams = new URLSearchParams(location.search); | ||
| 14 | + var download = document.getElementById("download"); | ||
| 15 | + download.setAttribute("href", searchParams.get("url")) | ||
| 16 | + download.click() | ||
| 17 | + setTimeout(() => { | ||
| 18 | + window.location.href = "about:blank"; | ||
| 19 | + window.close(); | ||
| 20 | + }, 200) | ||
| 21 | + })(); | ||
| 22 | + | ||
| 23 | +</script> | ||
| 24 | +</body> | ||
| 25 | +</html> | ||
| 0 | \ No newline at end of file | 26 | \ No newline at end of file |
src/test/java/top/panll/assist/WvpProAssistApplicationTests.java
100644 → 100755