This commit is contained in:
2024-11-01 18:21:57 +08:00
parent d7cf86507a
commit 579fd2eaf6
17 changed files with 650 additions and 500 deletions

View File

@@ -81,7 +81,12 @@ public class LiveConfigController {
@RequestMapping(value = "all", method = RequestMethod.GET)
@ResponseBody
public JSONObject getAllConfig(int page,int limit) {
List<LiveConfigDatabaseBean> config = configService.getConfigs(page, limit);
List<LiveConfigDatabaseBean> config ;
if(page==-1||limit==-1) {
config=configService.getAllConfig();
}else{
config=configService.getConfigs(page, limit);
}
if (config != null) {
return ResultData.success(config,configService.getConfigCount());
}

View File

@@ -2,22 +2,18 @@ package com.yutou.bilibili.Controllers;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.bilibili.datas.ResultData;
import com.yutou.bilibili.datas.VideoFilePath;
import com.yutou.bilibili.services.LiveDanmuService;
import com.yutou.bilibili.services.LiveService;
import com.yutou.bilibili.services.LiveVideoService;
import com.yutou.bilibili.services.LiveVideoDownloadService;
import jakarta.annotation.Resource;
import lombok.val;
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 LiveController {
@Resource
LiveVideoService videoService;
LiveVideoDownloadService videoService;
@Resource
LiveDanmuService danmuService;
@Resource

View File

@@ -1,13 +1,11 @@
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 com.yutou.bilibili.services.LiveVideoDownloadService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -18,7 +16,7 @@ import java.util.List;
@Controller
public class LiveVideoController {
@Resource
LiveVideoService videoService;
LiveVideoDownloadService videoService;
@RequestMapping("/live/video/list")
@ResponseBody

View File

@@ -5,17 +5,14 @@ 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 com.yutou.bilibili.services.LiveVideoDownloadService;
import com.yutou.common.okhttp.HttpDownloadUtils;
import com.yutou.common.utils.AppTools;
import com.yutou.common.utils.Base64Tools;
import com.yutou.common.utils.Log;
import jakarta.annotation.Resource;
import lombok.val;
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;
@@ -34,16 +31,16 @@ import java.util.List;
@Controller
public class VideoFileController {
@Resource
LiveVideoService videoService;
LiveVideoDownloadService videoService;
@ResponseBody
@RequestMapping("/file/list")
public JSONObject getFileList(String roomId) {
public JSONObject getFileList(int page,int limit,String roomId) {
List<VideoFilePath> list;
if (StringUtils.hasText(roomId)) {
list = videoService.getVideoPath(roomId);
} else {
list = videoService.getAllVideoPath();
list = videoService.getAllVideoPath(page,limit);
}
return ResultData.success(list);
}

View File

@@ -1,21 +1,19 @@
package com.yutou.bilibili.Tools;
import com.yutou.bilibili.services.LiveVideoService;
import com.yutou.bilibili.services.LiveVideoDownloadService;
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;
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;
LiveVideoDownloadService videoService;
@Override
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
Log.i("服务结束");

View File

@@ -8,6 +8,7 @@ import java.util.List;
public class VideoFilePath {
private String name;
private String roomId;
private String uid;
private String path;
private String cover;
private boolean isParent;

View File

@@ -27,7 +27,7 @@ public class LiveConfigService {
if (!StringUtils.hasText(roomId)) {
return null;
}
bean.setRoomId(new String(roomId));
bean.setRoomId(roomId);
try {
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();

View File

@@ -6,11 +6,8 @@ import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
import com.yutou.biliapi.databases.BiliLiveConfigDatabase;
import com.yutou.biliapi.net.BiliLiveNetApiManager;
import com.yutou.bilibili.datas.web.LiveData;
import com.yutou.common.okhttp.HttpBody;
import com.yutou.common.utils.AppTools;
import com.yutou.common.utils.Log;
import jakarta.annotation.Resource;
import lombok.val;
import org.springframework.stereotype.Service;
import java.io.IOException;
@@ -22,7 +19,7 @@ import java.util.List;
public class LiveService {
BiliLiveConfigDatabase liveConfigDatabase;
@Resource
LiveVideoService videoService;
LiveVideoDownloadService videoDownloadService;
@Resource
LiveDanmuService danmuService;
LiveApi api;
@@ -43,7 +40,7 @@ public class LiveService {
liveData.setAnchorUid(config.getAnchorUid());
liveData.setAnchorName(config.getAnchorName());
liveData.setAnchorFace(config.getAnchorFace());
liveData.setDownloadVideo(videoService.checkDownload(config.getRoomId()));
liveData.setDownloadVideo(videoDownloadService.checkDownload(config.getRoomId()));
liveData.setDanmu(danmuService.check(config.getRoomId()));
try {
LiveRoomInfo body = api.getRoomInfo(config.getRoomId()).execute().body().getData();

View File

@@ -0,0 +1,369 @@
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.bilibili.Tools.DateFormatUtils;
import com.yutou.bilibili.Tools.FileServerUtils;
import com.yutou.bilibili.Tools.LiveInfoNfoTools;
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;
import okhttp3.Headers;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.io.File;
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 LiveVideoDownloadService {
private final ThreadPoolExecutor executor;
private final List<String> userStopList = new ArrayList<>();//手动停止列表
private final AbsVideoRecord videoRecord;
public LiveVideoDownloadService() {
Log.i("初始化下载服务");
videoRecord = new FFmpegUtils();
executor = new ThreadPoolExecutor(2, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
}
public boolean checkDownload(String roomId) {
return videoRecord.check(roomId);
}
public void clearUserStopList() {
userStopList.clear();
}
public void start(LiveConfigDatabaseBean bean, boolean isUser) {
if (!isUser && userStopList.contains(bean.getRoomId().toString())) {
return;
}
if (isUser) {
userStopList.remove(bean.getRoomId().toString());
}
if (videoRecord.check(bean.getRoomId().toString())) {
return;
}
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);
} else {
Log.i("移除下载");
}
}
@Override
public void onFailure(Throwable throwable) {
Log.i("移除下载");
}
});
}
public void stop(String roomId, boolean isUser) {
if (isUser) {
userStopList.add(roomId);
}
videoRecord.kill(roomId);
}
public JSONArray getDownloadTasks() {
JSONArray array = new JSONArray();
array.addAll(videoRecord.getRoomIds());
return array;
}
public void stopAll() {
videoRecord.killAll();
}
private class VideoTask implements Runnable {
LiveConfigDatabaseBean bean;
boolean isDownload = true;
LiveApi api;
String savePath;
File rootPath;
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);
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();
}
}
private void stop() {
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) {
if (response.getLiveStatus() == 1) {
LiveVideoDownloadService.this.start(bean, false);
} else {
LiveVideoDownloadService.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);
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(),
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) {
Log.e(throwable);
}
});
}
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.Builder builder = new FFmpegUtils.Builder()
.withParam("-user_agent", ConfigTools.getUserAgent())
.withParam("-headers", "Referer: https://live.bilibili.com")
// .withNotSymbolParam("-progress", "-")
.withNotSymbolParam("-threads", "8")
.withNotSymbolParam("-c:v", "copy")
.withNotSymbolParam("-y", "");
if (ck != null) {
builder = builder.withParam("-cookies", cookie);
}
FFmpegUtils command = builder.build(config.getRoomId(), ffmpegPath, url, savePath);
Log.i(command.getCommand());
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);
Log.e("下载完成 ");
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);
}
}
private void saveLiveInfo(LiveRoomInfo roomInfo) {
}
public List<VideoFilePath> getAllVideoPath(int page, int limit) {
BiliLiveConfigDatabase configDatabase = new BiliLiveConfigDatabase();
List<LiveConfigDatabaseBean> list = configDatabase.getConfigs(page, limit);
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(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);
BiliLiveDatabase database = new BiliLiveDatabase(LiveRoomConfig.buildConfig(configBean.getRoomId()), recordDir.getAbsolutePath());
VideoFilePath path = createVideoRootFilePath(configBean, recordDir);
if (recordDir.exists()) {
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());
path.setCover(config.getAnchorFace());
path.setName(config.getAnchorName());
path.setUid(config.getAnchorUid());
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());
path.setName(roomInfo.getTitle());
path.setUid(roomInfo.getUid());
path.setPath(DateFormatUtils.format(bean.getSql_time()));
path.setCover(roomInfo.getKeyframe());
path.setParent(false);
path.setChildren(null);
filePathList.add(path);
}
return filePathList;
}
public static void main(String[] args) {
LiveVideoDownloadService service = new LiveVideoDownloadService();
List<VideoFilePath> path = service.getAllVideoPath(1, 8);
Log.i("path.size() = " + path.size());
Log.i(JSONArray.toJSONString(path));
}
}

View File

@@ -1,371 +1,8 @@
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.LiveInfoNfoTools;
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.record.AbsVideoRecord;
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;
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 {
private final ThreadPoolExecutor executor;
private final List<String> userStopList = new ArrayList<>();//手动停止列表
private final AbsVideoRecord videoRecord;
public LiveVideoService() {
Log.i("初始化下载服务");
videoRecord = new FFmpegUtils();
executor = new ThreadPoolExecutor(2, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
}
public boolean checkDownload(String roomId) {
return videoRecord.check(roomId);
}
public void clearUserStopList() {
userStopList.clear();
}
public void start(LiveConfigDatabaseBean bean, boolean isUser) {
if (!isUser && userStopList.contains(bean.getRoomId().toString())) {
return;
}
if (isUser) {
userStopList.remove(bean.getRoomId().toString());
}
if (videoRecord.check(bean.getRoomId().toString())) {
return;
}
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);
} else {
Log.i("移除下载");
}
}
@Override
public void onFailure(Throwable throwable) {
Log.i("移除下载");
}
});
}
public void stop(String roomId, boolean isUser) {
if (isUser) {
userStopList.add(roomId);
}
videoRecord.kill(roomId);
}
public JSONArray getDownloadTasks() {
JSONArray array = new JSONArray();
array.addAll(videoRecord.getRoomIds());
return array;
}
public void stopAll() {
videoRecord.killAll();
}
private class VideoTask implements Runnable {
LiveConfigDatabaseBean bean;
boolean isDownload = true;
LiveApi api;
String savePath;
File rootPath;
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);
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();
}
}
private void stop() {
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) {
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);
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(),
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) {
Log.e(throwable);
}
});
}
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.Builder builder = new FFmpegUtils.Builder()
.withParam("-user_agent", ConfigTools.getUserAgent())
.withParam("-headers", "Referer: https://live.bilibili.com")
// .withNotSymbolParam("-progress", "-")
.withNotSymbolParam("-threads", "8")
.withNotSymbolParam("-c:v", "copy")
.withNotSymbolParam("-y", "");
if (ck != null) {
builder = builder.withParam("-cookies", cookie);
}
FFmpegUtils command = builder.build(config.getRoomId(), ffmpegPath, url, savePath);
Log.i(command.getCommand());
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);
Log.e("下载完成 ");
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);
}
}
private void saveLiveInfo(LiveRoomInfo roomInfo) {
}
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 String(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()) {
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;
}
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(roomInfo.getKeyframe());
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();
Log.i("path.size() = " + path.size());
Log.i(JSONArray.toJSONString(path));
}
}

View File

@@ -1,27 +1,16 @@
package com.yutou.bilibili.services;
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.database.LiveConfigDatabaseBean;
import com.yutou.biliapi.databases.BiliLiveConfigDatabase;
import com.yutou.biliapi.net.BiliLiveNetApiManager;
import com.yutou.biliapi.net.WebSocketManager;
import com.yutou.bilibili.Tools.DateFormatUtils;
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;
import retrofit2.Response;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
@@ -30,7 +19,7 @@ import java.util.concurrent.TimeUnit;
@Service
public class SystemService {
@Resource
LiveVideoService videoService;
LiveVideoDownloadService videoService;
@Resource
LiveDanmuService danmuService;
SystemConfigDatabases databases = new SystemConfigDatabases();
@@ -59,7 +48,7 @@ public class SystemService {
scheduled = timer.scheduleAtFixedRate(() -> {
List<LiveConfigDatabaseBean> list = liveConfigDatabase.getAllConfig();
Log.i("循环任务:" + list.size());
if(DateFormatUtils.checkTime(null,resetTimer)){
if (DateFormatUtils.checkTime(null, resetTimer)) {
videoService.clearUserStopList();
danmuService.clearUserList();
}
@@ -67,6 +56,8 @@ public class SystemService {
try {
if (bean.isRecordDanmu() && bean.checkRecordDanmuTime()) {
recordDanmu(bean);
} else if (!bean.checkRecordDanmuTime()) {
stopRecordDanmu(bean);
}
if (bean.isRecordLive() && bean.checkRecordLiveTime()) {
recordVideo(bean);
@@ -79,6 +70,12 @@ public class SystemService {
}, 0, getLoopTimer(), TimeUnit.MILLISECONDS);
}
private void stopRecordDanmu(LiveConfigDatabaseBean bean) {
if (!videoService.checkDownload(bean.getRoomId())) {
danmuService.stop(bean.getRoomId(), false);
}
}
public void stop() {
scheduled.cancel(true);
videoService.stopAll();