This commit is contained in:
2024-10-28 18:27:16 +08:00
parent d6fbe6b59b
commit e6568480b7
38 changed files with 445 additions and 221 deletions

View File

@@ -1,5 +1,6 @@
package com.yutou.bilibili.Controllers;
import com.yutou.common.utils.FFmpegUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@@ -9,6 +10,13 @@ public class TestControllers {
@RequestMapping("/root/test")
@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

@@ -6,6 +6,7 @@ import com.yutou.bilibili.Tools.Tools;
import com.yutou.bilibili.datas.ResultData;
import com.yutou.bilibili.datas.VideoFilePath;
import com.yutou.bilibili.services.LiveVideoService;
import com.yutou.common.utils.Log;
import jakarta.annotation.Resource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.ResponseEntity;
@@ -39,7 +40,7 @@ public class VideoFileController {
@ResponseBody
public ResponseEntity<FileSystemResource> getFile(@PathVariable String base64) {
File file = FileServerUtils.toFile(base64);
System.out.println(file.getAbsolutePath());
Log.i(file.getAbsolutePath());
return Tools.getFile(file);
}

View File

@@ -1,5 +1,7 @@
package com.yutou.bilibili.Tools;
import com.yutou.common.utils.Log;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
@@ -24,7 +26,7 @@ public class AESTools {
byte[] bytes=cipher.doFinal(value.getBytes(StandardCharsets.UTF_8));
return new String(Base64.getEncoder().encode(bytes));
} catch (Exception e) {
e.printStackTrace();
Log.e(e);
}
return null;
}
@@ -44,7 +46,7 @@ public class AESTools {
byte[] bytes=cipher.doFinal(encodeBytes);
return new String(bytes);
} catch (Exception e) {
e.printStackTrace();
Log.e(e);
}
return null;
}

View File

@@ -1,6 +1,7 @@
package com.yutou.bilibili.Tools;
import com.yutou.bilibili.services.SystemService;
import com.yutou.common.utils.FFmpegUtils;
import com.yutou.common.utils.Log;
import jakarta.annotation.Resource;
import org.springframework.context.ApplicationListener;
@@ -15,5 +16,6 @@ public class ApplicationClose implements ApplicationListener<ContextClosedEvent>
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
Log.i("服务结束");
systemConfigService.stop();
FFmpegUtils.killAll();
}
}

View File

@@ -1,6 +1,7 @@
package com.yutou.bilibili.Tools;
import com.yutou.bilibili.datas.DanmuData;
import com.yutou.common.utils.Log;
import java.io.File;
import java.io.FileWriter;
@@ -75,7 +76,7 @@ public class AssTools {
* @return 存储结果
*/
public boolean saveDanmu(String savePath) {
System.out.println("savePath = " + savePath);
Log.i("savePath = " + savePath);
File file = new File(savePath);
try {
if (file.exists()) {
@@ -97,7 +98,7 @@ public class AssTools {
writer.close();
return true;
} catch (IOException e) {
e.printStackTrace();
Log.e(e);
}
return false;
}

View File

@@ -42,7 +42,7 @@ public class FFmpegUtils {
file.getAbsolutePath(),
out.getAbsolutePath() + File.separator + file.getName());
Log.i(exec);
Process process= AppTools.exec(exec);
Process process= ProcessUtils.exec(exec);
InputStream inputStream = process.getErrorStream();
byte[] bytes = new byte[1024];
while (inputStream.read(bytes) > -1) {

View File

@@ -0,0 +1,82 @@
package com.yutou.bilibili.Tools;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.biliapi.bean.live.LiveRoomConfig;
import com.yutou.biliapi.bean.live.LiveRoomInfo;
import com.yutou.biliapi.bean.live.database.LiveVideoDatabaseBean;
import com.yutou.biliapi.databases.BiliLiveDatabase;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
public class LiveInfoNfoTools {
public static void saveLiveInfoNfo(LiveRoomInfo info, String path,String xmlName) {
try {
LiveRoomConfig config = LiveRoomConfig.buildConfig(info.getRoomId().toString());
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = null;
dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.newDocument();
Element movie = doc.createElement("movie");
Element plot = doc.createElement("plot");
CDATASection section = doc.createCDATASection(info.getDescription());
plot.appendChild(section);
movie.appendChild(plot);
Element title = doc.createElement("title");
title.setTextContent(info.getTitle());
movie.appendChild(title);
Element actor = doc.createElement("actor");
Element name = doc.createElement("name");
Element role = doc.createElement("role");
name.setTextContent("UID:" + info.getUid().toString());
role.setTextContent(config.getAnchorName());
actor.appendChild(name);
actor.appendChild(role);
movie.appendChild(actor);
Element year = doc.createElement("year");
year.setTextContent(info.getLiveTime().split("-")[0]);
movie.appendChild(year);
Element aired = doc.createElement("aired");
aired.setTextContent(info.getLiveTime().split(" ")[0]);
movie.appendChild(aired);
movie.appendChild(createTag(doc,info.getParentAreaName()));
movie.appendChild(createTag(doc,info.getAreaName()));
doc.appendChild(movie);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File(path, xmlName));
transformer.transform(source, result);
} catch (Exception e) {
e.printStackTrace();
}
}
private static Element createTag(Document doc,String tag){
Element genre = doc.createElement("genre");
genre.setTextContent(tag);
return genre;
}
public static void main(String[] args) {
}
}

View File

@@ -0,0 +1,71 @@
package com.yutou.bilibili.Tools;
import com.yutou.common.utils.AppTools;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class ProcessUtils {
public static boolean isRuntimeSystemOfWindow(){
return System.getProperty ("os.name").contains("Windows");
}
public static void killProcess(Long pid) throws Exception {
if(pid==null) {
return;
}
if (isRuntimeSystemOfWindow()) {
// 对于 Windows 系统
Process process = exec("taskkill /F /PID " + pid);
process.waitFor();
} else {
// 对于 Linux 和 macOS 系统
Process process = exec("kill -9 " + pid);
process.waitFor();
}
}
public static boolean isProcessRunning(Long pid) throws Exception {
if(pid==null) {
return false;
}
String command;
if (isRuntimeSystemOfWindow()) {
// Windows 操作系统
command = "tasklist /FI \"PID eq " + pid + "\"";
} else {
// Unix/Linux 操作系统
command = "ps -p " + pid;
}
Process process = exec(command);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
if (line.contains(Long.toString(pid))) {
return true;
}
}
return false;
}
public static Process exec(String exec)throws Exception{
if(isRuntimeSystemOfWindow()) {
return Runtime.getRuntime().exec(new String[]{
"cmd",
"/c",
exec
}
);
}else{
return Runtime.getRuntime().exec(new String[]{
"sh",
"-c",
exec
}
);
}
}
public static void main(String[] args) throws Exception {
}
}

View File

@@ -15,7 +15,7 @@ import java.util.List;
@Service
public class LiveConfigService {
BiliLiveConfigDatabase database=new BiliLiveConfigDatabase();
BiliLiveConfigDatabase database = new BiliLiveConfigDatabase();
public LiveConfigDatabaseBean addConfig(String url, LiveConfigDatabaseBean bean) {
if (!StringUtils.hasText(url)) {
@@ -30,6 +30,7 @@ public class LiveConfigService {
LiveRoomInfo body = BiliLiveNetApiManager.getInstance().getApi(null).getRoomInfo(String.valueOf(bean.getRoomId())).execute().body().getData();
MasterInfoBean infoBean = BiliLiveNetApiManager.getInstance().getApi(null).getMasterInfo(String.valueOf(body.getUid())).execute().body().getData();
bean.setAnchorUid(body.getUid());
bean.setRoomId(body.getRoomId());
bean.setAnchorFace(infoBean.getInfo().getFace());
bean.setAnchorName(infoBean.getInfo().getUname());
database.setConfig(bean);

View File

@@ -12,6 +12,7 @@ import com.yutou.bilibili.Tools.AssTools;
import com.yutou.bilibili.Tools.DateFormatUtils;
import com.yutou.bilibili.Tools.Tools;
import com.yutou.common.utils.FFmpegUtils;
import com.yutou.common.utils.Log;
import org.springframework.stereotype.Service;
import java.io.File;
@@ -34,9 +35,6 @@ public class LiveDanmuService {
}
private BiliLiveDatabase getDatabase(String roomId, File file) {
return new BiliLiveDatabase(LiveRoomConfig.buildConfig(roomId), file.getParent() + File.separator + "live.db");
}
public List<File> getDanmuFileList(String roomId) {
BiliLiveConfigDatabase configDatabase=new BiliLiveConfigDatabase();
LiveConfigDatabaseBean bean = configDatabase.getConfig(new BigInteger(roomId));
@@ -47,12 +45,12 @@ public class LiveDanmuService {
public void saveDanmuXML(LiveVideoDatabaseBean videoDatabaseBean, BiliLiveDatabase database) {
File videoFile = new File(videoDatabaseBean.getPath());
long videoTime = FFmpegUtils.getVideoTime(videoFile) + videoDatabaseBean.getStartTime().getTime();
System.out.println("开始时间:" + videoDatabaseBean.getStartTime().getTime());
System.out.println("结束时间:" + videoTime);
Log.i("开始时间:" + videoDatabaseBean.getStartTime().getTime());
Log.i("结束时间:" + videoTime);
String startTime = DateFormatUtils.format(videoDatabaseBean.getStartTime());
String endTime = DateFormatUtils.format(videoTime);
List<LiveDanmuDatabaseBean> danmus = database.getOfTime(startTime, endTime, LiveDanmuDatabaseBean.class);
System.out.println("弹幕数量:" + danmus.size());
Log.i("弹幕数量:" + danmus.size());
AssTools assTools = new AssTools(videoFile.getName().replace(".flv", ""), videoDatabaseBean.getStartTime());
for (LiveDanmuDatabaseBean dm : danmus) {
assTools.addDanmu(dm.createDanmuData());
@@ -74,7 +72,7 @@ public class LiveDanmuService {
LiveDanmuService service = new LiveDanmuService();
List<File> files = service.getDanmuFileList("22047448");
for (File file : files) {
System.out.println(file);
Log.i(file);
}
}

View File

@@ -21,6 +21,7 @@ import com.yutou.biliapi.net.BiliLiveNetApiManager;
import com.yutou.biliapi.net.WebSocketManager;
import com.yutou.bilibili.Tools.DateFormatUtils;
import com.yutou.bilibili.Tools.FileServerUtils;
import com.yutou.bilibili.Tools.LiveInfoNfoTools;
import com.yutou.bilibili.Tools.Tools;
import com.yutou.bilibili.datas.VideoFilePath;
import com.yutou.bilibili.interfaces.DownloadInterface;
@@ -28,6 +29,7 @@ import com.yutou.common.okhttp.HttpCallback;
import com.yutou.common.okhttp.HttpDownloadUtils;
import com.yutou.common.utils.ConfigTools;
import com.yutou.common.utils.FFmpegUtils;
import com.yutou.common.utils.Log;
import okhttp3.Headers;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@@ -50,7 +52,7 @@ public class LiveVideoService {
public LiveVideoService() {
System.out.println("初始化下载服务");
Log.i("初始化下载服务");
executor = new ThreadPoolExecutor(2, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
}
@@ -64,7 +66,7 @@ public class LiveVideoService {
if (liveVideoMap.containsKey(bean)) {
return;
}
System.out.println("添加下载任务:" + liveVideoMap.keySet().size());
Log.i("添加下载任务:" + liveVideoMap.keySet().size());
liveVideoMap.put(bean, null);
BiliLiveNetApiManager.getInstance().getApi(bean.getRecordUid()).getRoomInfo(bean.getRoomId().toString()).enqueue(new HttpCallback<LiveRoomInfo>() {
@Override
@@ -75,14 +77,14 @@ public class LiveVideoService {
liveVideoMap.put(bean, task);
} else {
liveVideoMap.remove(bean);
System.out.println("移除下载");
Log.i("移除下载");
}
}
@Override
public void onFailure(Throwable throwable) {
liveVideoMap.remove(bean);
System.out.println("移除下载");
Log.i("移除下载");
}
});
@@ -97,7 +99,7 @@ public class LiveVideoService {
if (entry.getKey().getRoomId().toString().equals(roomId)) {
entry.getValue().isDownload = false;
liveVideoMap.remove(entry.getKey());
System.out.println("移除下载");
Log.i("移除下载");
break;
}
}
@@ -130,6 +132,7 @@ public class LiveVideoService {
boolean isDownload = true;
LiveApi api;
String savePath;
File rootPath;
LiveConfigDatabaseBean config;
BiliLiveDatabase database;
LiveVideoDatabaseBean videoDatabaseBean;
@@ -146,10 +149,13 @@ public class LiveVideoService {
public void run() {
if (roomInfo.getLiveStatus() == 1) {
String time = DateUtils.format(new Date().getTime(), DATE_FORMAT_10_DASH);
savePath = new File(bean.getRecordPath() + File.separator + bean.getAnchorName() + File.separator + time + File.separator + roomInfo.getTitle()).getAbsolutePath();
savePath += File.separator + "[" +
rootPath = new File(bean.getRecordPath() + File.separator + bean.getAnchorName() + File.separator + time + File.separator + roomInfo.getTitle());
savePath = rootPath.getAbsolutePath() + File.separator + "[" +
DateUtils.format(new Date(),
"yyyy-MM-dd HH-mm-ss") + "]" + roomInfo.getTitle() + ".flv";
if (!rootPath.exists()) {
rootPath.mkdirs();
}
record(bean, roomInfo);
} else {
stop();
@@ -157,6 +163,7 @@ public class LiveVideoService {
}
private void stop() {
liveVideoMap.remove(bean);
api.getRoomInfo(config.getRoomId().toString()).enqueue(new HttpCallback<LiveRoomInfo>() {
@Override
public void onResponse(Headers headers, int code, String status, LiveRoomInfo response, String rawResponse) {
@@ -186,6 +193,11 @@ public class LiveVideoService {
config.setRoomInfo(roomInfo);
config.setRootPath(bean.getRecordPath());
database = new BiliLiveDatabase(config);
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"));
saveLiveInfo(roomInfo);
api.getLiveRoomPlayInfo(
bean.getRoomId().toString(),
LiveProtocol.getAll(),
@@ -206,7 +218,7 @@ public class LiveVideoService {
@Override
public void onFailure(Throwable throwable) {
throwable.printStackTrace();
Log.e(throwable);
}
});
}
@@ -258,8 +270,8 @@ public class LiveVideoService {
.withNotSymbolParam("-threads", "8")
.withNotSymbolParam("-c:v", "copy")
.withNotSymbolParam("-y", "")
.build(ffmpegPath, url, savePath);
System.out.println(command);
.build(config.getRoomId().toString(),ffmpegPath, url, savePath);
Log.i(command.getCommand());
try {
command.start(new DownloadInterface() {
@Override
@@ -279,7 +291,7 @@ public class LiveVideoService {
@Override
public void onDownload(File file) {
super.onDownload(file);
System.err.println("下载完成 ");
Log.e("下载完成 ");
stop();
}
});
@@ -299,6 +311,10 @@ public class LiveVideoService {
}
}
private void saveLiveInfo(LiveRoomInfo roomInfo) {
}
public List<VideoFilePath> getAllVideoPath() {
BiliLiveConfigDatabase configDatabase = new BiliLiveConfigDatabase();
List<LiveConfigDatabaseBean> list = configDatabase.getAllConfig();
@@ -323,21 +339,12 @@ public class LiveVideoService {
File recordDir = new File(recordPath);
if (recordDir.exists()) {
List<File> files = Tools.scanFile(recordDir).stream()
.filter(file -> "live.db".equals(file.getName()))
.toList();
if (!files.isEmpty()) {
for (File db : files) {
VideoFilePath path = createVideoRootFilePath(configBean, db);
BiliLiveDatabase database = new BiliLiveDatabase(LiveRoomConfig.buildConfig(configBean.getRoomId().toString()), db.getAbsolutePath());
List<LiveVideoDatabaseBean> infos = database.getLiveInfos();
database.close();
path.setChildren(getVideoInfo(infos));
filePathList.add(path);
}
}
BiliLiveDatabase database = new BiliLiveDatabase(LiveRoomConfig.buildConfig(configBean.getRoomId().toString()), recordDir.getAbsolutePath());
VideoFilePath path = createVideoRootFilePath(configBean, recordDir);
List<LiveVideoDatabaseBean> infos = database.getLiveInfos();
database.close();
path.setChildren(getVideoInfo(infos));
filePathList.add(path);
}
return filePathList;
}
@@ -360,7 +367,7 @@ public class LiveVideoService {
path.setRoomId(roomInfo.getRoomId().toString());
path.setName(roomInfo.getTitle());
path.setPath(DateFormatUtils.format(bean.getSql_time()));
path.setCover(FileServerUtils.toUrl(new File(bean.getPath()).getParent() + File.separator + "poster.jpg"));
path.setCover(roomInfo.getKeyframe());
path.setParent(false);
path.setChildren(null);
filePathList.add(path);
@@ -371,7 +378,7 @@ public class LiveVideoService {
public static void main(String[] args) {
LiveVideoService service = new LiveVideoService();
List<VideoFilePath> path = service.getAllVideoPath();
System.out.println("path.size() = " + path.size());
System.out.println(JSONArray.toJSONString(path));
Log.i("path.size() = " + path.size());
Log.i(JSONArray.toJSONString(path));
}
}

View File

@@ -10,6 +10,7 @@ import com.yutou.biliapi.net.WebSocketManager;
import com.yutou.bilibili.databases.SystemConfigDatabases;
import com.yutou.bilibili.datas.SystemConfigDatabaseBean;
import com.yutou.common.okhttp.HttpBody;
import com.yutou.common.utils.Log;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@@ -48,21 +49,26 @@ public class SystemService {
if (timer == null) {
timer = Executors.newScheduledThreadPool(1);
}
System.out.println("执行任务");
Log.i("执行任务");
if (scheduled != null) {
scheduled.cancel(true);
}
scheduled = timer.scheduleAtFixedRate(() -> {
List<LiveConfigDatabaseBean> list = liveConfigDatabase.getAllConfig();
System.out.println("循环任务:" + list.size());
Log.i("循环任务:" + list.size());
for (LiveConfigDatabaseBean bean : list) {
if (bean.isRecordDanmu() && bean.checkRecordDanmuTime()) {
recordDanmu(bean);
}
if (bean.isRecordLive() && bean.checkRecordLiveTime()) {
recordVideo(bean);
try {
if (bean.isRecordDanmu() && bean.checkRecordDanmuTime()) {
recordDanmu(bean);
}
if (bean.isRecordLive() && bean.checkRecordLiveTime()) {
recordVideo(bean);
}
} catch (Exception e) {
Log.e(e);
}
}
}, 0, getLoopTimer(), TimeUnit.MILLISECONDS);
}
@@ -73,7 +79,7 @@ public class SystemService {
// 录制弹幕
private void recordDanmu(LiveConfigDatabaseBean bean) {
danmuService.start(bean.getRoomId().toString(),false);
danmuService.start(bean.getRoomId().toString(), false);
}
// 录制视频
@@ -83,10 +89,10 @@ public class SystemService {
public static void main(String[] args) throws InterruptedException {
SystemService service = new SystemService();
System.out.println(new Date());
Log.i(new Date());
service.start();
Thread.sleep(15000);
System.out.println(new Date());
Log.i(new Date());
service.start();
}