This commit is contained in:
zlzw 2024-10-29 18:29:43 +08:00
parent e6568480b7
commit 129e407646
11 changed files with 132 additions and 113 deletions

View File

@ -1,6 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
@ -9,51 +8,31 @@
</head>
<body>
<div class="layui-layout layui-layout-admin">
<div id="header"></div>
<div class="layui-body" style="left: 0px;">
<div style="width: 50%;margin-left: 25%; margin-top: 2%;" >
<blockquote class="layui-elem-quote" style="background-color: #FFFFFF">
本站点采用限时注册模式,仅登陆用户可使用。当前注册状态: <span style="color: #FD482C" id="model">关闭</span>
</blockquote>
<div align="center">
<img src="https://s1.hdslb.com/bfs/static/jinkela/international-home/assets/bgm-nodata.png" style="float: contour">
</div>
</div>
</div>
<div id="footer"></div>
</div>
<button id="btn" type="button" class="layui-btn" onclick="onButton()">默认按钮</button>
<script src="/layui/layui.js"></script>
<script src="/js/jquery-3.2.1.js"></script>
<script src="/js/CommonConfig.js"></script>
<script src="/js/httpUtils.js"></script>
<script>
layui.use(['layer', 'form', 'element'], function () {
var layer = layui.layer
, form = layui.form;
});
headerModel=0;
$('#header').load("/html/header.html");
$('#footer').load("/html/footer.html");
$.post("/system/public/reg.do",function (json) {
if(json.data.userReg){
$('#model').css('color','#499C54')
$('#model').text('开启')
}else{
$('#model').css('color','#FD482C')
$('#model').text('关闭')
}
})
</script>
<script>
function onButton(){
getLiveVideoList({
success:function(json){
console.log(json)
},
error:function(error){
</body>
}
})
}
</script>
<style>
.layui-body {
position: relative;
z-index: 0;
background-color: #F2F2F2;
}
</style>
</html>

View File

@ -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<LiveConfigDatabaseBean> 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<FileSystemResource> getFace(String roomId) {
return Tools.getFile(configService.getFace(roomId));
}
}

View File

@ -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)+"";
}
}

View File

@ -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<ContextClosedEvent> {
@Resource
SystemService systemConfigService;
@Resource
LiveVideoService videoService;
@Override
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
Log.i("服务结束");
systemConfigService.stop();
FFmpegUtils.killAll();
videoService.stopAll();
}
}

View File

@ -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);
}

View File

@ -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 {
}
}
}

View File

@ -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<T> {
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<T> {
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<T> {
resultData.setMessage(message);
return JSONObject.parseObject(JSONObject.toJSONString(resultData));
}
public static JSONObject success(ReturnCode code) {
return fail(code);
}
public static <T> JSONObject fail(ReturnCode code) {
ResultData<T> resultData = new ResultData<>();
resultData.setStatus(code.getCode());

View File

@ -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");
}
}

View File

@ -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<LiveConfigDatabaseBean, VideoTask> liveVideoMap = new HashMap<>();
private final List<String> userStopList = new ArrayList<>();//手动停止列表
AbsVideoRecord videoRecord;
public LiveVideoService() {
Log.i("初始化下载服务");
videoRecord=new FFmpegUtils();
executor = new ThreadPoolExecutor(2, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(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<LiveRoomInfo>() {
@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<LiveConfigDatabaseBean, VideoTask> 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<LiveRoomInfo>() {
@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() {

View File

@ -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<String> 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();
}

View File

@ -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<String, Long> 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<String> 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());