更新弹幕、直播的下载,以及弹幕的转换还有部分接口

This commit is contained in:
2024-10-25 17:31:14 +08:00
parent ac7f076721
commit 590c54b777
35 changed files with 1197 additions and 151 deletions

View File

@@ -24,7 +24,7 @@ public class LiveConfigController {
@RequestMapping(value = "set", method = RequestMethod.POST)
@ResponseBody
public ResultData<JSONObject> setConfig(String url, LiveConfigDatabaseBean bean) {
public JSONObject setConfig(String url, LiveConfigDatabaseBean bean) {
LiveConfigDatabaseBean config = configService.addConfig(url, bean);
if (config != null) {
return ResultData.success(config.toJson());
@@ -34,7 +34,7 @@ public class LiveConfigController {
@RequestMapping(value = "update", method = RequestMethod.POST)
@ResponseBody
public ResultData<JSONObject> updateConfig(String roomId, LiveConfigDatabaseBean bean) {
public JSONObject updateConfig(String roomId, LiveConfigDatabaseBean bean) {
LiveConfigDatabaseBean config = configService.updateConfig(new BigInteger(roomId), bean);
if (config != null) {
return ResultData.success(config.toJson());
@@ -44,7 +44,7 @@ public class LiveConfigController {
@RequestMapping(value = "get", method = RequestMethod.GET)
@ResponseBody
public ResultData<JSONObject> getConfig(String roomId) {
public JSONObject getConfig(String roomId) {
if ("0".equals(roomId) || !StringUtils.hasText(roomId)) {
return ResultData.fail(ReturnCode.RC999);
}
@@ -57,7 +57,7 @@ public class LiveConfigController {
@RequestMapping(value = "all", method = RequestMethod.GET)
@ResponseBody
public ResultData<JSONArray> getAllConfig() {
public JSONObject getAllConfig() {
List<LiveConfigDatabaseBean> config = configService.getAllConfig();
if (config != null) {
return ResultData.success(JSONArray.parseArray(JSONArray.toJSONString(config)));
@@ -67,7 +67,7 @@ public class LiveConfigController {
@RequestMapping(value = "delete", method = RequestMethod.GET)
@ResponseBody
public ResultData<JSONObject> deleteConfig(BigInteger roomId) {
public JSONObject deleteConfig(BigInteger roomId) {
if (roomId.equals(BigInteger.ZERO)) {
return ResultData.fail(ReturnCode.RC999);
}

View File

@@ -0,0 +1,43 @@
package com.yutou.bilibili.Controllers;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.biliapi.bean.live.LiveRoomConfig;
import com.yutou.biliapi.net.WebSocketManager;
import com.yutou.bilibili.datas.ResultData;
import com.yutou.bilibili.datas.ReturnCode;
import com.yutou.bilibili.services.LiveDanmuService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class LiveDanmuController {
@Resource
LiveDanmuService service;
@ResponseBody
@RequestMapping("/live/danmu/list")
public JSONObject getLiveDanmuList() {
return ResultData.success(service.getLiveRoomList());
}
@ResponseBody
@RequestMapping("/live/danmu/stop")
public JSONObject stopLiveDanmu(String roomId) {
service.stop(roomId);
return ResultData.success(ReturnCode.RC100);
}
@ResponseBody
@RequestMapping("/live/danmu/start")
public JSONObject startLiveDanmu(String roomId) {
service.start(roomId);
return ResultData.success(ReturnCode.RC100);
}
@ResponseBody
@RequestMapping("/live/danmu/file/list")
public JSONObject getDanmuList(String roomId) {
return ResultData.success(service.getDanmuFileList(roomId));
}
}

View File

@@ -0,0 +1,49 @@
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.biliapi.databases.BiliLiveConfigDatabase;
import com.yutou.biliapi.net.WebSocketManager;
import com.yutou.bilibili.datas.ResultData;
import com.yutou.bilibili.datas.ReturnCode;
import com.yutou.bilibili.services.LiveVideoService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
public class LiveVideoController {
@Resource
LiveVideoService videoService;
@RequestMapping("/live/video/list")
@ResponseBody
public JSONObject getLiveVideoList() {
return ResultData.success(videoService.getDownloadTasks());
}
@RequestMapping("/live/video/stop")
@ResponseBody
public JSONObject stopDownload(String roomId) {
videoService.stop(roomId, true);
return ResultData.success(true);
}
@RequestMapping("/live/video/start")
@ResponseBody
public JSONObject startDownload(String roomId) {
BiliLiveConfigDatabase liveConfigDatabase = new BiliLiveConfigDatabase();
List<LiveConfigDatabaseBean> list = liveConfigDatabase.getAllConfig();
for (LiveConfigDatabaseBean bean : list) {
if (bean.getRoomId().toString().equals(roomId)) {
videoService.start(bean, true);
return ResultData.success(true);
}
}
return ResultData.fail(ReturnCode.RC999);
}
}

View File

@@ -0,0 +1,46 @@
package com.yutou.bilibili.Controllers;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.bilibili.Tools.FileServerUtils;
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 jakarta.annotation.Resource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.File;
import java.util.List;
@Controller
public class VideoFileController {
@Resource
LiveVideoService videoService;
@ResponseBody
@RequestMapping("/file/list")
public JSONObject getFileList(String roomId) {
List<VideoFilePath> list;
if (StringUtils.hasText(roomId)) {
list = videoService.getVideoPath(roomId);
} else {
list = videoService.getAllVideoPath();
}
return ResultData.success(list);
}
@RequestMapping("/file/{base64}")
@ResponseBody
public ResponseEntity<FileSystemResource> getFile(@PathVariable String base64) {
File file = FileServerUtils.toFile(base64);
System.out.println(file.getAbsolutePath());
return Tools.getFile(file);
}
}

View File

@@ -1,14 +1,19 @@
package com.yutou.bilibili.Tools;
import com.yutou.bilibili.services.SystemService;
import com.yutou.common.utils.Log;
import jakarta.annotation.Resource;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;
@Component
public class ApplicationClose implements ApplicationListener<ContextClosedEvent> {
@Resource
SystemService systemConfigService;
@Override
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
Log.i("服务结束");
systemConfigService.stop();
}
}

View File

@@ -17,7 +17,7 @@ public class AssTools {
private final Date startTime;
private int y = 0;
private List<String> filters = new ArrayList<>();
private String alpha="80";
private String alpha="100";
/**
* 弹幕转换ass
@@ -140,7 +140,7 @@ public class AssTools {
y,
x2,
y,
danmuData.getFontColorHex(),
danmuData.getFontColor(),
alpha,
danmuData.getDanmu()
);

View File

@@ -0,0 +1,22 @@
package com.yutou.bilibili.Tools;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateFormatUtils {
public static String format(Date date,String format){
return new SimpleDateFormat(format).format(date);
}
public static String format(long time,String format){
return new SimpleDateFormat(format).format(new Date(time));
}
public static String format(long time){
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(time));
}
public static String format(Date date){
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(date);
}
public static String format(){
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
}
}

View File

@@ -0,0 +1,18 @@
package com.yutou.bilibili.Tools;
import com.yutou.common.utils.Base64Tools;
import java.io.File;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class FileServerUtils {
public static String toUrl(String file) {
return "/file/" + URLEncoder.encode(Base64Tools.encode(file), StandardCharsets.UTF_8);
}
public static File toFile(String base64) {
return new File(URLDecoder.decode(Base64Tools.decode(base64), StandardCharsets.UTF_8));
}
}

View File

@@ -13,11 +13,11 @@ import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.Random;
import java.util.*;
public class Tools {
@@ -203,4 +203,24 @@ public class Tools {
public static String getToDayTime() {
return new SimpleDateFormat("yyyy-MM-dd").format(new Date());
}
//扫描文件夹
public static List<File> scanFile(File file){
List<File> list = new ArrayList<>();
FileVisitor<Path> visitor = new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if ("live.db".equals(file.toFile().getName())) {
list.add(file.toFile());
}
return super.visitFile(file, attrs);
}
};
try {
Files.walkFileTree(Paths.get(file.getAbsolutePath()), EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, visitor);
return list;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -9,7 +9,7 @@ public class DanmuData {
private int id;
private int model;//13 滚动弹幕 4 底端弹幕 5 顶端弹幕 6 逆向弹幕 7 精准定位 8 高级弹幕
private int fontSize;
private int fontColor;
private String fontColor;
private long time;
private String uCode;
private String danmu;
@@ -20,7 +20,4 @@ public class DanmuData {
return new Date(time);
}
public String getFontColorHex() {
return Integer.toHexString(fontColor);
}
}

View File

@@ -1,5 +1,6 @@
package com.yutou.bilibili.datas;
import com.alibaba.fastjson2.JSONObject;
import lombok.Data;
@Data
@@ -9,33 +10,33 @@ public class ResultData<T> {
private T data;
private long timestamp ;
public ResultData (){
public ResultData() {
this.timestamp = System.currentTimeMillis();
}
public static <T> ResultData<T> success(T data) {
public static <T> JSONObject success(T data) {
ResultData<T> resultData = new ResultData<>();
resultData.setStatus(ReturnCode.RC100.getCode());
resultData.setMessage(ReturnCode.RC100.getMessage());
resultData.setData(data);
return resultData;
return JSONObject.parseObject(JSONObject.toJSONString(resultData));
}
public static <T> ResultData<T> fail(int code, String message) {
public static <T> JSONObject fail(int code, String message) {
ResultData<T> resultData = new ResultData<>();
resultData.setStatus(code);
resultData.setMessage(message);
return resultData;
return JSONObject.parseObject(JSONObject.toJSONString(resultData));
}
public static <T> ResultData<T> success(ReturnCode code) {
public static JSONObject success(ReturnCode code) {
return fail(code);
}
public static <T> ResultData<T> fail(ReturnCode code) {
public static <T> JSONObject fail(ReturnCode code) {
ResultData<T> resultData = new ResultData<>();
resultData.setStatus(code.getCode());
resultData.setMessage(code.getMessage());
return resultData;
return JSONObject.parseObject(JSONObject.toJSONString(resultData));
}
}

View File

@@ -9,7 +9,7 @@ import lombok.EqualsAndHashCode;
@Data
public class SystemConfigDatabaseBean extends AbsDatabasesBean {
@JSONField(name = "timer_loop")
private long timerLoop = 5000;
private long timerLoop = 30000;
public SystemConfigDatabaseBean() {

View File

@@ -0,0 +1,15 @@
package com.yutou.bilibili.datas;
import lombok.Data;
import java.util.List;
@Data
public class VideoFilePath {
private String name;
private String roomId;
private String path;
private String cover;
private boolean isParent;
private List<VideoFilePath> children;
}

View File

@@ -3,7 +3,8 @@ package com.yutou.bilibili.interfaces;
import java.io.File;
public abstract class DownloadInterface {
public void onDownloading(double soFarBytes, double totalBytes){};
public void onDownload(File file){};
public void onError(Exception e){};
public void onDownloadStart(){}
public boolean onDownloading(double soFarBytes, double totalBytes){return true;}
public void onDownload(File file){}
public void onError(Exception e){}
}

View File

@@ -1,7 +1,83 @@
package com.yutou.bilibili.services;
import com.alibaba.fastjson2.JSONArray;
import com.yutou.biliapi.bean.live.LiveRoomConfig;
import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
import com.yutou.biliapi.bean.live.database.LiveDanmuDatabaseBean;
import com.yutou.biliapi.bean.live.database.LiveVideoDatabaseBean;
import com.yutou.biliapi.databases.BiliLiveConfigDatabase;
import com.yutou.biliapi.databases.BiliLiveDatabase;
import com.yutou.biliapi.net.WebSocketManager;
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 org.springframework.stereotype.Service;
import java.io.File;
import java.math.BigInteger;
import java.util.List;
@Service
public class LiveDanmuService {
public void start(String roomId) {
WebSocketManager.getInstance().addRoom(LiveRoomConfig.buildConfig(roomId), true);
}
public void stop(String roomId) {
WebSocketManager.getInstance().stopRoom(roomId, true);
}
public JSONArray getLiveRoomList() {
return WebSocketManager.getInstance().getLiveRoomList();
}
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));
configDatabase.close();
return Tools.scanFile(new File(bean.getRecordPath() + File.separator + bean.getAnchorName()));
}
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);
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());
AssTools assTools = new AssTools(videoFile.getName().replace(".flv", ""), videoDatabaseBean.getStartTime());
for (LiveDanmuDatabaseBean dm : danmus) {
assTools.addDanmu(dm.createDanmuData());
}
assTools.saveDanmu(videoFile.getAbsolutePath().replace(".flv", ".ass"));
}
public String toTimeString(long videoTime) {
long seconds = videoTime / 1000;
long hours = seconds / 3600;
long remainingSecondsAfterHours = seconds % 3600;
long minutes = remainingSecondsAfterHours / 60;
long finalRemainingSeconds = remainingSecondsAfterHours % 60;
// long finalRemainingMilliseconds = videoTime % 1000;
return String.format("%d小时%d分钟%d秒", hours, minutes, finalRemainingSeconds);
}
public static void main(String[] args) {
LiveDanmuService service = new LiveDanmuService();
List<File> files = service.getDanmuFileList("22047448");
for (File file : files) {
System.out.println(file);
}
}
}

View File

@@ -0,0 +1,377 @@
package com.yutou.bilibili.services;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.util.DateUtils;
import com.yutou.biliapi.api.LiveApi;
import com.yutou.biliapi.bean.live.LiveRoomConfig;
import com.yutou.biliapi.bean.live.LiveRoomInfo;
import com.yutou.biliapi.bean.live.LiveRoomPlayInfo;
import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
import com.yutou.biliapi.bean.live.database.LiveVideoDatabaseBean;
import com.yutou.biliapi.bean.login.LoginCookieDatabaseBean;
import com.yutou.biliapi.databases.BiliBiliLoginDatabase;
import com.yutou.biliapi.databases.BiliLiveConfigDatabase;
import com.yutou.biliapi.databases.BiliLiveDatabase;
import com.yutou.biliapi.enums.LiveProtocol;
import com.yutou.biliapi.enums.LiveVideoCodec;
import com.yutou.biliapi.enums.LiveVideoDefinition;
import com.yutou.biliapi.enums.LiveVideoFormat;
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.Tools;
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.utils.ConfigTools;
import com.yutou.common.utils.FFmpegUtils;
import okhttp3.Headers;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.io.File;
import java.io.FileFilter;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import static com.alibaba.fastjson2.util.DateUtils.DateTimeFormatPattern.DATE_FORMAT_10_DASH;
@Service
public class LiveVideoService {
ThreadPoolExecutor executor;
private final Map<LiveConfigDatabaseBean, VideoTask> liveVideoMap = new HashMap<>();
private final List<String> userStopList = new ArrayList<>();//手动停止列表
public LiveVideoService() {
System.out.println("初始化下载服务");
executor = new ThreadPoolExecutor(2, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
}
public void start(LiveConfigDatabaseBean bean, boolean isUser) {
if (!isUser && userStopList.contains(bean.getRoomId().toString())) {
return;
}
if (isUser) {
userStopList.remove(bean.getRoomId().toString());
}
if (liveVideoMap.containsKey(bean)) {
return;
}
System.out.println("添加下载任务:" + 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);
System.out.println("移除下载");
}
}
@Override
public void onFailure(Throwable throwable) {
liveVideoMap.remove(bean);
System.out.println("移除下载");
}
});
}
public void stop(String roomId, boolean isUser) {
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());
System.out.println("移除下载");
break;
}
}
}
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());
return array;
}
public void stopAll() {
for (VideoTask task : liveVideoMap.values()) {
task.isDownload = false;
}
liveVideoMap.clear();
}
private class VideoTask implements Runnable {
LiveConfigDatabaseBean bean;
boolean isDownload = true;
LiveApi api;
String savePath;
LiveConfigDatabaseBean config;
BiliLiveDatabase database;
LiveVideoDatabaseBean videoDatabaseBean;
LiveRoomInfo roomInfo;
public VideoTask(LiveConfigDatabaseBean bean, LiveRoomInfo roomInfo) {
this.bean = bean;
this.roomInfo = roomInfo;
api = BiliLiveNetApiManager.getInstance().getApi(bean.getRecordUid());
}
@Override
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 + "[" +
DateUtils.format(new Date(),
"yyyy-MM-dd HH-mm-ss") + "]" + roomInfo.getTitle() + ".flv";
record(bean, roomInfo);
} else {
stop();
}
}
private void stop() {
api.getRoomInfo(config.getRoomId().toString()).enqueue(new HttpCallback<LiveRoomInfo>() {
@Override
public void onResponse(Headers headers, int code, String status, LiveRoomInfo response, String rawResponse) {
if (response.getLiveStatus() == 1) {
LiveVideoService.this.start(bean, false);
} else {
LiveVideoService.this.stop(bean.getRoomId().toString(), false);
}
}
@Override
public void onFailure(Throwable throwable) {
}
});
}
private void record(LiveConfigDatabaseBean bean, LiveRoomInfo roomInfo) {
this.config = bean;
isDownload = true;
LiveRoomConfig config = new LiveRoomConfig();
config.setLoginUid(bean.getRecordUid());
config.setRoomId(bean.getRoomId());
config.setAnchorName(bean.getAnchorName());
config.setLogin(StringUtils.hasText(bean.getRecordUid()));
config.setRoomInfo(roomInfo);
config.setRootPath(bean.getRecordPath());
database = new BiliLiveDatabase(config);
api.getLiveRoomPlayInfo(
bean.getRoomId().toString(),
LiveProtocol.getAll(),
LiveVideoFormat.getAll(),
LiveVideoCodec.getAll(),
LiveVideoDefinition.ORIGINAL.getValue()).enqueue(new HttpCallback<LiveRoomPlayInfo>() {
@Override
public void onResponse(Headers headers, int code, String status, LiveRoomPlayInfo response, String rawResponse) {
LiveRoomPlayInfo.Codec codec = response.getPlayurlInfo().getPlayurl().getStream().get(0).getFormat().get(0).getCodec().get(0);
String url = codec.getUrlInfo().get(0).getHost() + codec.getBaseUrl() + codec.getUrlInfo().get(0).getExtra();
if (bean.getRecordLiveModel() == 1) {
javaRecord(url, response);
} else {
ffmpeg(url, response);
}
}
@Override
public void onFailure(Throwable throwable) {
throwable.printStackTrace();
}
});
}
private void javaRecord(String url, LiveRoomPlayInfo playInfo) {
HttpDownloadUtils.download(new HttpDownloadUtils.Builder()
.setUrl(url)
.setPath(savePath)
.setDownloadInterface(new DownloadInterface() {
@Override
public void onDownloadStart() {
super.onDownloadStart();
VideoTask.this.onStart();
}
@Override
public boolean onDownloading(double soFarBytes, double totalBytes) {
return isDownload;
}
@Override
public void onDownload(File file) {
super.onDownload(file);
stop();
}
@Override
public void onError(Exception e) {
super.onError(e);
stop();
}
}));
}
private void ffmpeg(String url, LiveRoomPlayInfo playInfo) {
String ffmpegPath = ConfigTools.load(ConfigTools.CONFIG, "ffmpeg", String.class);
String cookie = "";
LoginCookieDatabaseBean ck = BiliBiliLoginDatabase.getInstance().getCookie(config.getRecordUid());
if (ck != null) {
cookie = ck.toCookieString();
}
FFmpegUtils command = new FFmpegUtils.Builder()
.withParam("-user_agent", ConfigTools.getUserAgent())
.withParam("-cookies", cookie)
.withParam("-headers", "Referer: https://live.bilibili.com")
// .withNotSymbolParam("-progress", "-")
.withNotSymbolParam("-threads", "8")
.withNotSymbolParam("-c:v", "copy")
.withNotSymbolParam("-y", "")
.build(ffmpegPath, url, savePath);
System.out.println(command);
try {
command.start(new DownloadInterface() {
@Override
public void onDownloadStart() {
super.onDownloadStart();
VideoTask.this.onStart();
}
@Override
public boolean onDownloading(double soFarBytes, double totalBytes) {
if (!isDownload) {
command.stop();
}
return super.onDownloading(soFarBytes, totalBytes);
}
@Override
public void onDownload(File file) {
super.onDownload(file);
System.err.println("下载完成 ");
stop();
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void onStart() {
videoDatabaseBean = new LiveVideoDatabaseBean();
videoDatabaseBean.setPath(savePath);
videoDatabaseBean.setRoomInfoJson(JSONObject.toJSONString(roomInfo));
videoDatabaseBean.setStartTime(new Date());
database.addLiveInfo(videoDatabaseBean);
}
}
public List<VideoFilePath> getAllVideoPath() {
BiliLiveConfigDatabase configDatabase = new BiliLiveConfigDatabase();
List<LiveConfigDatabaseBean> list = configDatabase.getAllConfig();
configDatabase.close();
List<VideoFilePath> filePathList = new ArrayList<>();
for (LiveConfigDatabaseBean bean : list) {
filePathList.addAll(getVideoFilePath(bean));
}
return filePathList;
}
public List<VideoFilePath> getVideoPath(String roomId) {
BiliLiveConfigDatabase configDatabase = new BiliLiveConfigDatabase();
LiveConfigDatabaseBean bean = configDatabase.getConfig(new BigInteger(roomId));
configDatabase.close();
return new ArrayList<>(getVideoFilePath(bean));
}
private List<VideoFilePath> getVideoFilePath(LiveConfigDatabaseBean configBean) {
List<VideoFilePath> filePathList = new ArrayList<>();
String recordPath = configBean.getRecordPath() + File.separator + configBean.getAnchorName();
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);
}
}
}
return filePathList;
}
private VideoFilePath createVideoRootFilePath(LiveConfigDatabaseBean config, File db) {
VideoFilePath path = new VideoFilePath();
path.setRoomId(config.getRoomId().toString());
path.setCover(config.getAnchorFace());
path.setName(config.getAnchorName());
path.setParent(true);
path.setPath(FileServerUtils.toUrl(db.getParent()));
return path;
}
private List<VideoFilePath> getVideoInfo(List<LiveVideoDatabaseBean> videoList) {
List<VideoFilePath> filePathList = new ArrayList<>();
for (LiveVideoDatabaseBean bean : videoList) {
VideoFilePath path = new VideoFilePath();
LiveRoomInfo roomInfo = JSONObject.parseObject(bean.getRoomInfoJson(), LiveRoomInfo.class);
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.setParent(false);
path.setChildren(null);
filePathList.add(path);
}
return filePathList;
}
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));
}
}

View File

@@ -27,6 +27,8 @@ import java.util.concurrent.TimeUnit;
@Service
public class SystemService {
@Resource
LiveVideoService videoService;
SystemConfigDatabases databases = new SystemConfigDatabases();
private ScheduledExecutorService timer;
private ScheduledFuture<?> scheduled;
@@ -50,6 +52,7 @@ public class SystemService {
}
scheduled = timer.scheduleAtFixedRate(() -> {
List<LiveConfigDatabaseBean> list = liveConfigDatabase.getAllConfig();
System.out.println("循环任务:"+list.size());
for (LiveConfigDatabaseBean bean : list) {
if (bean.isRecordDanmu() && bean.checkRecordDanmuTime()) {
recordDanmu(bean);
@@ -60,7 +63,10 @@ public class SystemService {
}
}, 0, getLoopTimer(), TimeUnit.MILLISECONDS);
}
public void stop() {
scheduled.cancel(true);
videoService.stopAll();
}
// 录制弹幕
private void recordDanmu(LiveConfigDatabaseBean bean) {
LiveRoomConfig config = new LiveRoomConfig();
@@ -68,12 +74,13 @@ public class SystemService {
config.setRoomId(bean.getRoomId());
config.setAnchorName(bean.getAnchorName());
config.setLogin(StringUtils.hasText(bean.getRecordUid()));
WebSocketManager.getInstance().addRoom(config);
config.setRootPath(bean.getRecordPath());
WebSocketManager.getInstance().addRoom(config,false);
}
// 录制视频
private void recordVideo(LiveConfigDatabaseBean bean) {
videoService.start(bean, false);
}
public static void main(String[] args) throws InterruptedException {
@@ -85,4 +92,5 @@ public class SystemService {
service.start();
}
}

View File

@@ -56,7 +56,7 @@ public class BiliBiliLiveDatabasesManager extends SQLiteManager {
"values (%d,%d,%d,%d,'%s','%s',%d,'%s')"
,data.getModel()
,data.getFontSize()
,data.getFontColor()
// ,data.getFontColor()
,data.getTime()
,data.getUCode()
,data.getDanmu()
@@ -87,7 +87,7 @@ public class BiliBiliLiveDatabasesManager extends SQLiteManager {
data.setId(set.getInt("id"));
data.setModel(set.getInt("model"));
data.setFontSize(set.getInt("fontSize"));
data.setFontColor(set.getInt("fontColor"));
// data.setFontColor(set.getInt("fontColor"));
data.setTime(set.getLong("time"));
data.setUCode(set.getString("uCode"));
data.setDanmu(set.getString("danmu"));