diff --git a/Web/index.html b/Web/index.html index f1d0a86..ee52381 100644 --- a/Web/index.html +++ b/Web/index.html @@ -1,6 +1,5 @@ - - + @@ -9,51 +8,31 @@ -
-
-
-
- 本站点采用限时注册模式,仅登陆用户可使用。当前注册状态: 关闭 -
-
- - - -
-
- -
- -
+ + + + \ No newline at end of file diff --git a/src/main/java/com/yutou/bilibili/Controllers/LiveConfigController.java b/src/main/java/com/yutou/bilibili/Controllers/LiveConfigController.java index da6d4c5..a5bcd56 100644 --- a/src/main/java/com/yutou/bilibili/Controllers/LiveConfigController.java +++ b/src/main/java/com/yutou/bilibili/Controllers/LiveConfigController.java @@ -3,10 +3,13 @@ package com.yutou.bilibili.Controllers; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean; +import com.yutou.bilibili.Tools.Tools; import com.yutou.bilibili.datas.ResultData; import com.yutou.bilibili.datas.ReturnCode; import com.yutou.bilibili.services.LiveConfigService; import jakarta.annotation.Resource; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMapping; @@ -60,7 +63,7 @@ public class LiveConfigController { public JSONObject getAllConfig() { List config = configService.getAllConfig(); if (config != null) { - return ResultData.success(JSONArray.parseArray(JSONArray.toJSONString(config))); + return ResultData.success(config); } return ResultData.fail(ReturnCode.RC999); } @@ -77,4 +80,9 @@ public class LiveConfigController { } return ResultData.fail(ReturnCode.RC999); } + + @RequestMapping(value = "face", method = RequestMethod.GET) + public ResponseEntity getFace(String roomId) { + return Tools.getFile(configService.getFace(roomId)); + } } diff --git a/src/main/java/com/yutou/bilibili/Controllers/TestControllers.java b/src/main/java/com/yutou/bilibili/Controllers/TestControllers.java index 60791ce..76f29cb 100644 --- a/src/main/java/com/yutou/bilibili/Controllers/TestControllers.java +++ b/src/main/java/com/yutou/bilibili/Controllers/TestControllers.java @@ -7,16 +7,9 @@ import org.springframework.web.bind.annotation.ResponseBody; @Controller public class TestControllers { - @RequestMapping("/root/test") + @RequestMapping("/root/all") @ResponseBody public String test(){ - FFmpegUtils.killAll(); return "hello world"; } - @RequestMapping("/root/kill") - @ResponseBody - public String kill(String roomId){ - FFmpegUtils.kill(roomId); - return FFmpegUtils.check(roomId)+""; - } } diff --git a/src/main/java/com/yutou/bilibili/Tools/ApplicationClose.java b/src/main/java/com/yutou/bilibili/Tools/ApplicationClose.java index 269b85a..09edecf 100644 --- a/src/main/java/com/yutou/bilibili/Tools/ApplicationClose.java +++ b/src/main/java/com/yutou/bilibili/Tools/ApplicationClose.java @@ -1,5 +1,6 @@ package com.yutou.bilibili.Tools; +import com.yutou.bilibili.services.LiveVideoService; import com.yutou.bilibili.services.SystemService; import com.yutou.common.utils.FFmpegUtils; import com.yutou.common.utils.Log; @@ -7,15 +8,18 @@ import jakarta.annotation.Resource; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextClosedEvent; import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; @Component public class ApplicationClose implements ApplicationListener { @Resource SystemService systemConfigService; + @Resource + LiveVideoService videoService; @Override public void onApplicationEvent(ContextClosedEvent contextClosedEvent) { Log.i("服务结束"); systemConfigService.stop(); - FFmpegUtils.killAll(); + videoService.stopAll(); } } diff --git a/src/main/java/com/yutou/bilibili/Tools/FFmpegUtils.java b/src/main/java/com/yutou/bilibili/Tools/FFmpegUtils.java index 831f99f..e64a0a2 100644 --- a/src/main/java/com/yutou/bilibili/Tools/FFmpegUtils.java +++ b/src/main/java/com/yutou/bilibili/Tools/FFmpegUtils.java @@ -42,15 +42,15 @@ public class FFmpegUtils { file.getAbsolutePath(), out.getAbsolutePath() + File.separator + file.getName()); Log.i(exec); - Process process= ProcessUtils.exec(exec); + Process process = ProcessUtils.exec(ffmpeg, "-i", file.getAbsolutePath(), "-c:v", "copy", "-y", out.getAbsolutePath() + File.separator + file.getName()); InputStream inputStream = process.getErrorStream(); byte[] bytes = new byte[1024]; while (inputStream.read(bytes) > -1) { } inputStream.close(); - AppTools.copyFileToName(out.getAbsolutePath()+File.separator+file.getName(), file.getParent() + File.separator, file.getName().replace(".mp4", "_ffmpeg.mp4"), true); + AppTools.copyFileToName(out.getAbsolutePath() + File.separator + file.getName(), file.getParent() + File.separator, file.getName().replace(".mp4", "_ffmpeg.mp4"), true); file.delete(); - new File(out.getAbsolutePath()+File.separator+file.getName()).delete(); + new File(out.getAbsolutePath() + File.separator + file.getName()).delete(); } catch (Exception e) { Log.e(e); } diff --git a/src/main/java/com/yutou/bilibili/Tools/ProcessUtils.java b/src/main/java/com/yutou/bilibili/Tools/ProcessUtils.java index 6bef7be..0843265 100644 --- a/src/main/java/com/yutou/bilibili/Tools/ProcessUtils.java +++ b/src/main/java/com/yutou/bilibili/Tools/ProcessUtils.java @@ -7,11 +7,12 @@ import java.io.InputStreamReader; public class ProcessUtils { - public static boolean isRuntimeSystemOfWindow(){ - return System.getProperty ("os.name").contains("Windows"); + public static boolean isRuntimeSystemOfWindow() { + return System.getProperty("os.name").contains("Windows"); } + public static void killProcess(Long pid) throws Exception { - if(pid==null) { + if (pid == null) { return; } if (isRuntimeSystemOfWindow()) { @@ -25,8 +26,9 @@ public class ProcessUtils { } } + public static boolean isProcessRunning(Long pid) throws Exception { - if(pid==null) { + if (pid == null) { return false; } String command; @@ -48,24 +50,19 @@ public class ProcessUtils { } return false; } - public static Process exec(String exec)throws Exception{ - if(isRuntimeSystemOfWindow()) { - return Runtime.getRuntime().exec(new String[]{ - "cmd", - "/c", - exec - } - ); + public static Process exec(String exec) throws Exception { + if(isRuntimeSystemOfWindow()){ + return exec("cmd.exe","/c",exec); }else{ - return Runtime.getRuntime().exec(new String[]{ - "sh", - "-c", - exec - } - ); + return exec("sh","-c",exec); } } + public static Process exec(String... command) throws Exception { + ProcessBuilder pb; + pb = new ProcessBuilder(command); + return pb.start(); + } public static void main(String[] args) throws Exception { - } + } } diff --git a/src/main/java/com/yutou/bilibili/datas/ResultData.java b/src/main/java/com/yutou/bilibili/datas/ResultData.java index f48af0c..747e16c 100644 --- a/src/main/java/com/yutou/bilibili/datas/ResultData.java +++ b/src/main/java/com/yutou/bilibili/datas/ResultData.java @@ -1,14 +1,19 @@ package com.yutou.bilibili.datas; +import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import lombok.Data; +import java.util.Collection; +import java.util.List; + @Data public class ResultData { private int status; private String message; private T data; - private long timestamp ; + private int count; + private long timestamp; public ResultData() { this.timestamp = System.currentTimeMillis(); @@ -20,6 +25,9 @@ public class ResultData { resultData.setStatus(ReturnCode.RC100.getCode()); resultData.setMessage(ReturnCode.RC100.getMessage()); resultData.setData(data); + if (data instanceof Collection) { + resultData.count = ((Collection) data).size(); + } return JSONObject.parseObject(JSONObject.toJSONString(resultData)); } @@ -29,9 +37,11 @@ public class ResultData { resultData.setMessage(message); return JSONObject.parseObject(JSONObject.toJSONString(resultData)); } + public static JSONObject success(ReturnCode code) { return fail(code); } + public static JSONObject fail(ReturnCode code) { ResultData resultData = new ResultData<>(); resultData.setStatus(code.getCode()); diff --git a/src/main/java/com/yutou/bilibili/services/LiveConfigService.java b/src/main/java/com/yutou/bilibili/services/LiveConfigService.java index 4324728..1f63de8 100644 --- a/src/main/java/com/yutou/bilibili/services/LiveConfigService.java +++ b/src/main/java/com/yutou/bilibili/services/LiveConfigService.java @@ -5,10 +5,12 @@ import com.yutou.biliapi.bean.live.MasterInfoBean; import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean; import com.yutou.biliapi.databases.BiliLiveConfigDatabase; import com.yutou.biliapi.net.BiliLiveNetApiManager; +import com.yutou.common.okhttp.HttpDownloadUtils; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; +import java.io.File; import java.io.IOException; import java.math.BigInteger; import java.util.List; @@ -34,6 +36,7 @@ public class LiveConfigService { bean.setAnchorFace(infoBean.getInfo().getFace()); bean.setAnchorName(infoBean.getInfo().getUname()); database.setConfig(bean); + downloadFace(bean); return bean; } catch (IOException e) { throw new RuntimeException(e); @@ -48,6 +51,7 @@ public class LiveConfigService { bean.setRoomId(roomId); bean.setSql_time(config.getSql_time()); database.setConfig(bean); + downloadFace(bean); return bean; } @@ -67,4 +71,22 @@ public class LiveConfigService { public LiveConfigDatabaseBean getConfig(BigInteger roomId) { return database.getConfig(roomId); } + + public File getFace(String roomId){ + LiveConfigDatabaseBean config = database.getConfig(new BigInteger(roomId)); + if (config == null) { + return null; + } + return new File(config.getRecordPath() + File.separator + config.getAnchorName() + File.separator + config.getAnchorFace()); + } + + private void downloadFace(LiveConfigDatabaseBean bean) { + HttpDownloadUtils.download( + new HttpDownloadUtils.Builder() + .setPath(bean.getRecordPath() + File.separator + bean.getAnchorName()) + .setUrl(bean.getAnchorFace()) + .setFileName("face.jpg") + ); + bean.setAnchorFace(bean.getRecordPath() + File.separator + bean.getAnchorName() + File.separator + "face.jpg"); + } } diff --git a/src/main/java/com/yutou/bilibili/services/LiveVideoService.java b/src/main/java/com/yutou/bilibili/services/LiveVideoService.java index c62d57b..1112bbd 100644 --- a/src/main/java/com/yutou/bilibili/services/LiveVideoService.java +++ b/src/main/java/com/yutou/bilibili/services/LiveVideoService.java @@ -27,6 +27,7 @@ import com.yutou.bilibili.datas.VideoFilePath; import com.yutou.bilibili.interfaces.DownloadInterface; import com.yutou.common.okhttp.HttpCallback; import com.yutou.common.okhttp.HttpDownloadUtils; +import com.yutou.common.record.AbsVideoRecord; import com.yutou.common.utils.ConfigTools; import com.yutou.common.utils.FFmpegUtils; import com.yutou.common.utils.Log; @@ -47,12 +48,12 @@ import static com.alibaba.fastjson2.util.DateUtils.DateTimeFormatPattern.DATE_FO @Service public class LiveVideoService { ThreadPoolExecutor executor; - private final Map liveVideoMap = new HashMap<>(); private final List userStopList = new ArrayList<>();//手动停止列表 - + AbsVideoRecord videoRecord; public LiveVideoService() { Log.i("初始化下载服务"); + videoRecord=new FFmpegUtils(); executor = new ThreadPoolExecutor(2, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new ArrayBlockingQueue(100)); } @@ -63,27 +64,22 @@ public class LiveVideoService { if (isUser) { userStopList.remove(bean.getRoomId().toString()); } - if (liveVideoMap.containsKey(bean)) { + if (videoRecord.check(bean.getRoomId().toString())) { return; } - Log.i("添加下载任务:" + liveVideoMap.keySet().size()); - liveVideoMap.put(bean, null); BiliLiveNetApiManager.getInstance().getApi(bean.getRecordUid()).getRoomInfo(bean.getRoomId().toString()).enqueue(new HttpCallback() { @Override public void onResponse(Headers headers, int code, String status, LiveRoomInfo response, String rawResponse) { if (response.getLiveStatus() == 1) { VideoTask task = new VideoTask(bean, response); executor.execute(task); - liveVideoMap.put(bean, task); } else { - liveVideoMap.remove(bean); Log.i("移除下载"); } } @Override public void onFailure(Throwable throwable) { - liveVideoMap.remove(bean); Log.i("移除下载"); } }); @@ -95,36 +91,18 @@ public class LiveVideoService { if (isUser) { userStopList.add(roomId); } - for (Map.Entry entry : liveVideoMap.entrySet()) { - if (entry.getKey().getRoomId().toString().equals(roomId)) { - entry.getValue().isDownload = false; - liveVideoMap.remove(entry.getKey()); - Log.i("移除下载"); - break; - } - } + videoRecord.kill(roomId); } - public boolean check(String roomId) { - for (LiveConfigDatabaseBean bean : liveVideoMap.keySet()) { - if (bean.getRoomId().toString().equals(roomId)) { - return true; - } - } - return false; - } public JSONArray getDownloadTasks() { JSONArray array = new JSONArray(); - array.addAll(liveVideoMap.keySet()); + array.addAll(videoRecord.getRoomIds()); return array; } public void stopAll() { - for (VideoTask task : liveVideoMap.values()) { - task.isDownload = false; - } - liveVideoMap.clear(); + videoRecord.killAll(); } private class VideoTask implements Runnable { @@ -163,7 +141,7 @@ public class LiveVideoService { } private void stop() { - liveVideoMap.remove(bean); + videoRecord.kill(bean.getRoomId().toString()); api.getRoomInfo(config.getRoomId().toString()).enqueue(new HttpCallback() { @Override public void onResponse(Headers headers, int code, String status, LiveRoomInfo response, String rawResponse) { @@ -196,7 +174,7 @@ public class LiveVideoService { HttpDownloadUtils.download(new HttpDownloadUtils.Builder().setUrl(roomInfo.getKeyframe()) .setPath(rootPath.getAbsolutePath()) .setFileName("poster.jpg")); - LiveInfoNfoTools.saveLiveInfoNfo(roomInfo,rootPath.getAbsolutePath(),new File(savePath).getName().replace(".flv",".nfo")); + LiveInfoNfoTools.saveLiveInfoNfo(roomInfo, rootPath.getAbsolutePath(), new File(savePath).getName().replace(".flv", ".nfo")); saveLiveInfo(roomInfo); api.getLiveRoomPlayInfo( bean.getRoomId().toString(), @@ -270,7 +248,7 @@ public class LiveVideoService { .withNotSymbolParam("-threads", "8") .withNotSymbolParam("-c:v", "copy") .withNotSymbolParam("-y", "") - .build(config.getRoomId().toString(),ffmpegPath, url, savePath); + .build(config.getRoomId().toString(), ffmpegPath, url, savePath); Log.i(command.getCommand()); try { command.start(new DownloadInterface() { diff --git a/src/main/java/com/yutou/common/record/AbsVideoRecord.java b/src/main/java/com/yutou/common/record/AbsVideoRecord.java new file mode 100644 index 0000000..35db229 --- /dev/null +++ b/src/main/java/com/yutou/common/record/AbsVideoRecord.java @@ -0,0 +1,14 @@ +package com.yutou.common.record; + +import com.yutou.bilibili.interfaces.DownloadInterface; + +import java.util.List; + +public abstract class AbsVideoRecord { + public abstract List getRoomIds(); + public abstract boolean check(String roomId); + public abstract void kill(String roomId); + public abstract void killAll(); + public abstract void start(DownloadInterface downloadInterface); + public abstract void stop(); +} diff --git a/src/main/java/com/yutou/common/utils/FFmpegUtils.java b/src/main/java/com/yutou/common/utils/FFmpegUtils.java index 8864ec7..5b40322 100644 --- a/src/main/java/com/yutou/common/utils/FFmpegUtils.java +++ b/src/main/java/com/yutou/common/utils/FFmpegUtils.java @@ -2,13 +2,14 @@ package com.yutou.common.utils; import com.yutou.bilibili.Tools.ProcessUtils; import com.yutou.bilibili.interfaces.DownloadInterface; +import com.yutou.common.record.AbsVideoRecord; import lombok.Getter; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.*; -public class FFmpegUtils { +public class FFmpegUtils extends AbsVideoRecord { private static final Map pidMap = new HashMap<>(); @Getter private String command; @@ -16,28 +17,38 @@ public class FFmpegUtils { InputStream inputStream; OutputStream outputStream; - private FFmpegUtils() { + public FFmpegUtils() { } - public static boolean check(String anchorUid) { + @Override + public List getRoomIds() { + pidMap.keySet().removeIf(key -> !check(key)); + return pidMap.keySet().stream().toList(); + } + + @Override + public boolean check(String roomId) { try { - return ProcessUtils.isProcessRunning(pidMap.get(anchorUid)); + return ProcessUtils.isProcessRunning(pidMap.get(roomId)); } catch (Exception e) { Log.e(e); throw new RuntimeException(e); } } - public static void kill(String anchorUid) { + @Override + public void kill(String roomId) { try { - ProcessUtils.killProcess(pidMap.get(anchorUid)); + ProcessUtils.killProcess(pidMap.get(roomId)); + pidMap.remove(roomId); } catch (Exception e) { Log.e(e); throw new RuntimeException(e); } } - public static void killAll() { + @Override + public void killAll() { for (Long pid : pidMap.values()) { try { ProcessUtils.killProcess(pid); @@ -45,6 +56,7 @@ public class FFmpegUtils { Log.e(e); } } + pidMap.clear(); } public static class Builder { @@ -96,15 +108,16 @@ public class FFmpegUtils { } } + @Override public void start(DownloadInterface downloadInterface) { new Thread(() -> { try { if (check(uid)) { kill(uid); } - Process process = ProcessUtils.exec(command); + Process process = ProcessUtils.exec(command.split(" ")); long pid = process.toHandle().pid(); - Log.i("进程id "+pid); + Log.i("进程id " + pid); inputStream = process.getErrorStream(); outputStream = process.getOutputStream(); byte[] bytes = new byte[2048]; @@ -130,6 +143,7 @@ public class FFmpegUtils { }).start(); } + @Override public void stop() { try { outputStream.write("q".getBytes());