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

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

@@ -21,6 +21,8 @@ import retrofit2.Response;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {

View File

@@ -1,6 +1,9 @@
package com.yutou.biliapi.bean.live;
import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
import com.yutou.biliapi.databases.BiliLiveConfigDatabase;
import lombok.Data;
import org.springframework.util.StringUtils;
import java.math.BigInteger;
import java.util.Objects;
@@ -11,6 +14,7 @@ public class LiveRoomConfig {
BigInteger roomId;
String anchorName;
boolean isLogin;
String rootPath="live";
LiveDanmuInfo liveInfo;
LiveRoomInfo roomInfo;
@@ -37,4 +41,17 @@ public class LiveRoomConfig {
public int hashCode() {
return Objects.hashCode(roomId);
}
public static LiveRoomConfig buildConfig(String roomId){
BiliLiveConfigDatabase database = new BiliLiveConfigDatabase();
LiveConfigDatabaseBean bean = database.getConfig(new BigInteger(roomId));
LiveRoomConfig config = new LiveRoomConfig();
config.setLoginUid(bean.getRecordUid());
config.setRoomId(bean.getRoomId());
config.setAnchorName(bean.getAnchorName());
config.setLogin(StringUtils.hasText(bean.getRecordUid()));
config.setRootPath(bean.getRecordPath());
return config;
}
}

View File

@@ -26,8 +26,12 @@ public class LiveConfigDatabaseBean extends AbsDatabasesBean {
private boolean isRecordDanmu;
@JSONField(name = "keyword")
private List<String> keywordList;
@JSONField(name = "recordPath")
private String recordPath="live";
@JSONField(name = "recordUid")
private String recordUid;
@JSONField(name = "recordLiveModel")
private int recordLiveModel;//0 - ffmpeg 1 - java
@JSONField(name = "recordDanmuDate")
private String recordDanmuDate="* * *";// * * * 分 时 星期 | 周日是1
@JSONField(name = "recordLiveDate")

View File

@@ -2,6 +2,7 @@ package com.yutou.biliapi.bean.live.database;
import com.alibaba.fastjson2.annotation.JSONField;
import com.yutou.biliapi.bean.websocket.live.WSDanmuData;
import com.yutou.bilibili.datas.DanmuData;
import com.yutou.common.databases.AbsDatabasesBean;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -29,13 +30,12 @@ public class LiveDanmuDatabaseBean extends AbsDatabasesBean {
private String uname;
public LiveDanmuDatabaseBean() {
super("danmu",System.currentTimeMillis());
super("danmu", System.currentTimeMillis());
}
public LiveDanmuDatabaseBean(WSDanmuData danmu) {
super("danmu",danmu.getWs_timer());
super("danmu", danmu.getWs_timer());
this.danmu = danmu.getDanmu();
model = danmu.getModel();
fontSize = danmu.getFontSize();
@@ -44,4 +44,15 @@ public class LiveDanmuDatabaseBean extends AbsDatabasesBean {
uid = danmu.getUid();
uname = danmu.getUname();
}
public DanmuData createDanmuData() {
DanmuData data = new DanmuData();
data.setId(id);
data.setDanmu(danmu);
data.setModel(model);
data.setFontSize(fontSize);
data.setFontColor(fontColor);
data.setTime(time);
return data;
}
}

View File

@@ -0,0 +1,23 @@
package com.yutou.biliapi.bean.live.database;
import com.alibaba.fastjson2.annotation.JSONField;
import com.yutou.common.databases.AbsDatabasesBean;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
@EqualsAndHashCode(callSuper = true)
@Data
public class LiveVideoDatabaseBean extends AbsDatabasesBean {
@JSONField(name = "info")
String roomInfoJson;
@JSONField(name = "start_time")
Date startTime;
@JSONField(name = "path")
String path;
public LiveVideoDatabaseBean() {
super("live_video", System.currentTimeMillis());
}
}

View File

@@ -31,4 +31,8 @@ public class LoginCookieDatabaseBean extends AbsDatabasesBean {
public LoginCookieDatabaseBean() {
super("login_cookie", System.currentTimeMillis());
}
public String toCookieString() {
return "SESSDATA=" + sessdta + "; Path=" + path + "; DedeUserID=" + dedeUserID + "; DedeUserID__ckMd5=" + dedeUserIDCkMd5 + "; bili_jct=" + biliJct + "; Expires=" + expires + "; Domain=" + domain + "; sid=" + sid + "; gourl=" + gourl;
}
}

View File

@@ -43,7 +43,7 @@ public class BiliBiliLoginDatabase extends SQLiteManager {
return list.getFirst();
}
for (LoginCookieDatabaseBean bean : list) {
if (bean.getSid().equals(userId)) {
if (bean.getDedeUserID().equals(userId)) {
return bean;
}
}
@@ -66,7 +66,7 @@ public class BiliBiliLoginDatabase extends SQLiteManager {
@Override
public String getFileName() {
return "bilibili_login.db";
return "old_bilibili_login.db";
}
@Override

View File

@@ -2,13 +2,16 @@ package com.yutou.biliapi.databases;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.biliapi.bean.live.LiveRoomConfig;
import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
import com.yutou.common.databases.AbsDatabasesBean;
import com.yutou.common.databases.SQLiteManager;
import org.springframework.util.StringUtils;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class BiliLiveConfigDatabase extends SQLiteManager {
String fileName;
@@ -44,12 +47,13 @@ public class BiliLiveConfigDatabase extends SQLiteManager {
return null;
}
for (LiveConfigDatabaseBean bean : list) {
if (bean.getRoomId() == roomId) {
if (Objects.equals(bean.getRoomId(), roomId)) {
return bean;
}
}
return null;
}
public boolean deleteConfig(BigInteger roomId) {
LiveConfigDatabaseBean config = getConfig(roomId);
if (config == null) {
@@ -57,6 +61,7 @@ public class BiliLiveConfigDatabase extends SQLiteManager {
}
return delete(config);
}
public List<LiveConfigDatabaseBean> getAllConfig() {
List<LiveConfigDatabaseBean> list = get(getDataBean().get(0).getTableName(), LiveConfigDatabaseBean.class);
if (list.isEmpty()) {

View File

@@ -4,36 +4,51 @@ import com.alibaba.fastjson2.util.DateUtils;
import com.yutou.biliapi.bean.live.*;
import com.yutou.biliapi.bean.live.database.*;
import com.yutou.biliapi.bean.websocket.live.*;
import com.yutou.bilibili.Tools.DateFormatUtils;
import com.yutou.common.databases.AbsDatabasesBean;
import com.yutou.common.databases.SQLiteManager;
import com.yutou.common.okhttp.HttpDownloadUtils;
import org.apache.poi.ss.usermodel.DataFormat;
import java.io.File;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import static com.alibaba.fastjson2.util.DateUtils.DateTimeFormatPattern.DATE_FORMAT_10_DASH;
public class BiliLiveDatabase extends SQLiteManager {
LiveInfoDatabaseBean bean;
LiveRoomConfig config;
String fileName;
File rootPath;
public BiliLiveDatabase(LiveRoomConfig roomConfig, String path) {
this.config = roomConfig;
rootPath = new File(path).getParentFile();
fileName =path;
init();
}
public BiliLiveDatabase(LiveRoomConfig roomConfig) {
String time = DateUtils.format(new Date().getTime(), DATE_FORMAT_10_DASH);
rootPath = new File(roomConfig.getAnchorName() + File.separator + time + roomConfig.getRoomInfo().getTitle());
config=roomConfig;
if(!rootPath.exists()){
rootPath = new File(roomConfig.getRootPath() + File.separator + roomConfig.getAnchorName() + File.separator + time + File.separator + roomConfig.getRoomInfo().getTitle());
config = roomConfig;
if (!rootPath.exists()) {
rootPath.mkdirs();
}
fileName=rootPath.getAbsolutePath()+File.separator+"live.db";
fileName = rootPath.getAbsolutePath() + File.separator + "live.db";
init();
}
@Override
public void init() {
super.init();
HttpDownloadUtils.download(config.getRoomInfo().getUserCover(),rootPath.getAbsolutePath(),"poster.jpg");
if(config.getRoomInfo()!=null) {
HttpDownloadUtils.download(new HttpDownloadUtils.Builder().setUrl(config.getRoomInfo().getUserCover())
.setPath(rootPath.getAbsolutePath())
.setFileName("poster.jpg"));
}
}
@Override
@@ -49,17 +64,22 @@ public class BiliLiveDatabase extends SQLiteManager {
new LiveGiftDatabaseBean(),
new LiveInteractWordDatabaseBean(),
new LiveSuperChatDatabaseBean(),
new LiveSourceDatabaseBean()
new LiveSourceDatabaseBean(),
new LiveVideoDatabaseBean()
);
}
public void addLiveInfo(LiveRoomInfo info) {
this.bean = new LiveInfoDatabaseBean(info);
@Override
public void close() {
super.close();
}
List<LiveInfoDatabaseBean> infos = get(bean.getTableName(), LiveInfoDatabaseBean.class);
if (infos.isEmpty()) {
createInfo(bean);
}
public void addLiveInfo(LiveVideoDatabaseBean info) {
createInfo(info);
}
public List<LiveVideoDatabaseBean> getLiveInfos() {
return get(new LiveVideoDatabaseBean().getTableName(), LiveVideoDatabaseBean.class);
}
private void addData(WSData bean) {
@@ -75,30 +95,32 @@ public class BiliLiveDatabase extends SQLiteManager {
}
public void addSource(WSData bean) {
System.out.println("BiliLiveDatabase.addSource");
add(new LiveSourceDatabaseBean(bean));
addData(bean);
}
private void createInfo(LiveInfoDatabaseBean bean) {
add(bean);
private void createInfo(LiveVideoDatabaseBean bean) {
String format = DateFormatUtils.format(bean.getSql_time());
if (get(bean.getTableName(), " `sql_time` = '" + format + "'", LiveVideoDatabaseBean.class).isEmpty()) {
add(bean);
} else {
update(bean);
}
}
public List<LiveSourceDatabaseBean> getSource(long startTime, long entTime) {
return get(startTime, entTime, LiveSourceDatabaseBean.class);
}
public <T extends AbsDatabasesBean> List<T> get(long startTime, long entTime, Class<T> clazz) {
public <T extends AbsDatabasesBean> List<T> getOfTime(String startTime, String entTime, Class<T> clazz) {
String tableName = null;
StringBuilder sb = new StringBuilder();
String where = null;
if (startTime != -1) {
sb.append(" `sql_time` >= ").append(startTime);
if (startTime != null) {
sb.append(" `sql_time` >= ").append("\"").append(startTime).append("\"");
}
if (entTime != -1) {
if (entTime != null) {
if (!sb.isEmpty()) {
sb.append(" and ");
}
sb.append(" `sql_time` <= ").append(entTime);
sb.append(" `sql_time` <= ").append("\"").append(entTime).append("\"");
}
if (!sb.isEmpty()) {
where = sb.toString();

View File

@@ -4,8 +4,8 @@ import com.aayushatharva.brotli4j.Brotli4jLoader;
import com.aayushatharva.brotli4j.decoder.Decoder;
import com.aayushatharva.brotli4j.decoder.DecoderJNI;
import com.aayushatharva.brotli4j.decoder.DirectDecompress;
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.LiveDanmuInfo;
import com.yutou.biliapi.bean.live.LiveRoomConfig;
@@ -26,7 +26,6 @@ import org.java_websocket.handshake.ServerHandshake;
import retrofit2.Response;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URI;
@@ -34,15 +33,19 @@ import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.*;
import static com.alibaba.fastjson2.util.DateUtils.DateTimeFormatPattern.DATE_FORMAT_10_DASH;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class WebSocketManager {
ThreadPoolExecutor executor;
private static WebSocketManager instance;
Map<LiveRoomConfig, WebSocketClientTh> roomMap;
Map<LiveRoomConfig, DanmuTask> roomMap;
private final List<String> userStopList = new ArrayList<>();//手动停止列表
private WebSocketManager() {
roomMap = new HashMap<>();
executor = new ThreadPoolExecutor(2, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
}
public static WebSocketManager getInstance() {
@@ -56,68 +59,107 @@ public class WebSocketManager {
return roomMap.containsKey(roomConfig);
}
public void addRoom(LiveRoomConfig roomConfig) {
public JSONArray getLiveRoomList() {
JSONArray array = new JSONArray();
array.addAll(roomMap.keySet());
return array;
}
public void addRoom(LiveRoomConfig roomConfig, boolean isUser) {
if (!isUser && userStopList.contains(roomConfig.getRoomId().toString())) {
return;
}
if (checkRoom(roomConfig)) {
return;
}
LiveApi api = BiliLiveNetApiManager.getInstance().getApi(roomConfig.getLoginUid());
Response<HttpBody<LiveRoomInfo>> execute = null;
try {
execute = api.getRoomInfo(roomConfig.getRoomId().toString()).execute();
if (execute.isSuccessful()) {
roomConfig.setRoomInfo(execute.body() != null ? execute.body().getData() : null);
}
} catch (IOException e) {
throw new RuntimeException(e);
if (isUser) {
userStopList.remove(roomConfig.getRoomId().toString());
}
api.getLiveRoomDanmuInfo(String.valueOf(roomConfig.getLoginUid())).enqueue(new HttpCallback<LiveDanmuInfo>() {
@Override
public void onResponse(Headers headers, int code, String status, LiveDanmuInfo response, String rawResponse) {
if (!response.getHostList().isEmpty()) {
LiveDanmuInfo.Host host = response.getHostList().get(0);
String url = "wss://" + host.getHost() + ":" + host.getWssPort() + "/sub";
// url="ws://127.0.0.1:8765";
try {
roomConfig.setLiveInfo(response);
new WebSocketClientTh(new URI(url), roomConfig);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
}
@Override
public void onFailure(Throwable throwable) {
throwable.printStackTrace();
}
});
DanmuTask task = new DanmuTask(roomConfig);
roomMap.put(roomConfig, task);
System.out.println("添加websocket任务");
executor.execute(task);
}
public void stopRoom(LiveRoomConfig roomConfig) {
public void stopRoom(String roomId, boolean isUser) {
LiveRoomConfig roomConfig=new LiveRoomConfig();
roomConfig.setRoomId(new BigInteger(roomId));
if (checkRoom(roomConfig)) {
roomMap.get(roomConfig).close();
roomMap.remove(roomConfig);
}
if (isUser) {
userStopList.add(roomConfig.getRoomId().toString());
}
}
private static class DanmuTask implements Runnable {
LiveRoomConfig roomConfig;
WebSocketClientTh client;
public DanmuTask(LiveRoomConfig config) {
this.roomConfig = config;
WebSocketManager.getInstance().roomMap.put(roomConfig, this);
}
@Override
public void run() {
LiveApi api = BiliLiveNetApiManager.getInstance().getApi(roomConfig.getLoginUid());
Response<HttpBody<LiveRoomInfo>> execute = null;
try {
execute = api.getRoomInfo(roomConfig.getRoomId().toString()).execute();
if (execute.isSuccessful()) {
roomConfig.setRoomInfo(execute.body() != null ? execute.body().getData() : null);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
api.getLiveRoomDanmuInfo(String.valueOf(roomConfig.getRoomId())).enqueue(new HttpCallback<LiveDanmuInfo>() {
@Override
public void onResponse(Headers headers, int code, String status, LiveDanmuInfo response, String rawResponse) {
if (!response.getHostList().isEmpty()) {
LiveDanmuInfo.Host host = response.getHostList().get(0);
String url = "wss://" + host.getHost() + ":" + host.getWssPort() + "/sub";
// url="ws://127.0.0.1:8765";
try {
roomConfig.setLiveInfo(response);
client = new WebSocketClientTh(new URI(url), roomConfig);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
}
@Override
public void onFailure(Throwable throwable) {
throwable.printStackTrace();
}
});
}
public void close() {
client.close();
}
}
private static class WebSocketClientTh extends WebSocketClient {
private LiveRoomConfig roomConfig;
private HeartbeatTask heartbeatTask;
BiliLiveDatabase liveDatabase;
private boolean itTmp = true;
public WebSocketClientTh(URI serverUri, LiveRoomConfig roomId) {
super(serverUri);
System.out.println("WebSocketClientTh.WebSocketClientTh : " + serverUri);
this.roomConfig = roomId;
heartbeatTask = new HeartbeatTask();
Brotli4jLoader.ensureAvailability();
liveDatabase = new BiliLiveDatabase(roomConfig);
Brotli4jLoader.ensureAvailability();
heartbeatTask = new HeartbeatTask();
connect();
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
WebSocketManager.getInstance().roomMap.put(roomConfig, this);
heartbeatTask.setSocket(this);
heartbeatTask.sendInitAuthData();
new Timer().schedule(heartbeatTask, 1000, 30000);
@@ -131,7 +173,7 @@ public class WebSocketManager {
@Override
public void onMessage(ByteBuffer bytes) {
System.out.println("WebSocketClientTh.onMessage");
System.out.println("WebSocketClientTh.onMessage: " + roomConfig.getAnchorName());
super.onMessage(bytes);
decompress(bytes.array());
}
@@ -161,7 +203,7 @@ public class WebSocketManager {
byte[] bytes = new byte[data.length - 16];
WebSocketHeader header = new WebSocketHeader(data);
System.arraycopy(data, header.getHeaderSize(), bytes, 0, data.length - header.getHeaderSize());
System.out.println("数据大小:" + header.getDataSize() + " 协议:" + header.getAgree() + " 头部大小:" + header.getHeaderSize() + " 命令:" + header.getCmdData());
// System.out.println("数据大小:" + header.getDataSize() + " 协议:" + header.getAgree() + " 头部大小:" + header.getHeaderSize() + " 命令:" + header.getCmdData());
switch (header.getAgree()) {
case 0:
case 1:
@@ -183,14 +225,14 @@ public class WebSocketManager {
DirectDecompress directDecompress = Decoder.decompress(bytes);
if (directDecompress.getResultStatus() == DecoderJNI.Status.DONE) {
WebSocketBody body = new WebSocketBody(directDecompress.getDecompressedData());
Log.i("3协议:" + useHeader + " 命令数:" + body.getBodyList().size());
// Log.i("协议:" + useHeader + " 命令数:" + body.getBodyList().size());
for (JSONObject json : body.getBodyList()) {
WSData parse = WSData.parse(json);
liveDatabase.addSource(parse);
Log.i("解压:" + parse);
// Log.i("解压:" + parse);
}
System.out.println();
System.out.println();
// System.out.println();
// System.out.println();
} else {
Log.e(new RuntimeException("解压失败"));
}