更新弹幕、直播的下载,以及弹幕的转换还有部分接口
This commit is contained in:
parent
ac7f076721
commit
590c54b777
@ -21,6 +21,8 @@ import retrofit2.Response;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package com.yutou.biliapi.bean.live;
|
package com.yutou.biliapi.bean.live;
|
||||||
|
|
||||||
|
import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
|
||||||
|
import com.yutou.biliapi.databases.BiliLiveConfigDatabase;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -11,6 +14,7 @@ public class LiveRoomConfig {
|
|||||||
BigInteger roomId;
|
BigInteger roomId;
|
||||||
String anchorName;
|
String anchorName;
|
||||||
boolean isLogin;
|
boolean isLogin;
|
||||||
|
String rootPath="live";
|
||||||
LiveDanmuInfo liveInfo;
|
LiveDanmuInfo liveInfo;
|
||||||
LiveRoomInfo roomInfo;
|
LiveRoomInfo roomInfo;
|
||||||
|
|
||||||
@ -37,4 +41,17 @@ public class LiveRoomConfig {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hashCode(roomId);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,12 @@ public class LiveConfigDatabaseBean extends AbsDatabasesBean {
|
|||||||
private boolean isRecordDanmu;
|
private boolean isRecordDanmu;
|
||||||
@JSONField(name = "keyword")
|
@JSONField(name = "keyword")
|
||||||
private List<String> keywordList;
|
private List<String> keywordList;
|
||||||
|
@JSONField(name = "recordPath")
|
||||||
|
private String recordPath="live";
|
||||||
@JSONField(name = "recordUid")
|
@JSONField(name = "recordUid")
|
||||||
private String recordUid;
|
private String recordUid;
|
||||||
|
@JSONField(name = "recordLiveModel")
|
||||||
|
private int recordLiveModel;//0 - ffmpeg 1 - java
|
||||||
@JSONField(name = "recordDanmuDate")
|
@JSONField(name = "recordDanmuDate")
|
||||||
private String recordDanmuDate="* * *";// * * * 分 时 星期 | 周日是1
|
private String recordDanmuDate="* * *";// * * * 分 时 星期 | 周日是1
|
||||||
@JSONField(name = "recordLiveDate")
|
@JSONField(name = "recordLiveDate")
|
||||||
|
@ -2,6 +2,7 @@ package com.yutou.biliapi.bean.live.database;
|
|||||||
|
|
||||||
import com.alibaba.fastjson2.annotation.JSONField;
|
import com.alibaba.fastjson2.annotation.JSONField;
|
||||||
import com.yutou.biliapi.bean.websocket.live.WSDanmuData;
|
import com.yutou.biliapi.bean.websocket.live.WSDanmuData;
|
||||||
|
import com.yutou.bilibili.datas.DanmuData;
|
||||||
import com.yutou.common.databases.AbsDatabasesBean;
|
import com.yutou.common.databases.AbsDatabasesBean;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
@ -29,13 +30,12 @@ public class LiveDanmuDatabaseBean extends AbsDatabasesBean {
|
|||||||
private String uname;
|
private String uname;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public LiveDanmuDatabaseBean() {
|
public LiveDanmuDatabaseBean() {
|
||||||
super("danmu",System.currentTimeMillis());
|
super("danmu", System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveDanmuDatabaseBean(WSDanmuData danmu) {
|
public LiveDanmuDatabaseBean(WSDanmuData danmu) {
|
||||||
super("danmu",danmu.getWs_timer());
|
super("danmu", danmu.getWs_timer());
|
||||||
this.danmu = danmu.getDanmu();
|
this.danmu = danmu.getDanmu();
|
||||||
model = danmu.getModel();
|
model = danmu.getModel();
|
||||||
fontSize = danmu.getFontSize();
|
fontSize = danmu.getFontSize();
|
||||||
@ -44,4 +44,15 @@ public class LiveDanmuDatabaseBean extends AbsDatabasesBean {
|
|||||||
uid = danmu.getUid();
|
uid = danmu.getUid();
|
||||||
uname = danmu.getUname();
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
@ -31,4 +31,8 @@ public class LoginCookieDatabaseBean extends AbsDatabasesBean {
|
|||||||
public LoginCookieDatabaseBean() {
|
public LoginCookieDatabaseBean() {
|
||||||
super("login_cookie", System.currentTimeMillis());
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ public class BiliBiliLoginDatabase extends SQLiteManager {
|
|||||||
return list.getFirst();
|
return list.getFirst();
|
||||||
}
|
}
|
||||||
for (LoginCookieDatabaseBean bean : list) {
|
for (LoginCookieDatabaseBean bean : list) {
|
||||||
if (bean.getSid().equals(userId)) {
|
if (bean.getDedeUserID().equals(userId)) {
|
||||||
return bean;
|
return bean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,7 +66,7 @@ public class BiliBiliLoginDatabase extends SQLiteManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFileName() {
|
public String getFileName() {
|
||||||
return "bilibili_login.db";
|
return "old_bilibili_login.db";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -2,13 +2,16 @@ package com.yutou.biliapi.databases;
|
|||||||
|
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.yutou.biliapi.bean.live.LiveRoomConfig;
|
||||||
import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
|
import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
|
||||||
import com.yutou.common.databases.AbsDatabasesBean;
|
import com.yutou.common.databases.AbsDatabasesBean;
|
||||||
import com.yutou.common.databases.SQLiteManager;
|
import com.yutou.common.databases.SQLiteManager;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class BiliLiveConfigDatabase extends SQLiteManager {
|
public class BiliLiveConfigDatabase extends SQLiteManager {
|
||||||
String fileName;
|
String fileName;
|
||||||
@ -44,12 +47,13 @@ public class BiliLiveConfigDatabase extends SQLiteManager {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for (LiveConfigDatabaseBean bean : list) {
|
for (LiveConfigDatabaseBean bean : list) {
|
||||||
if (bean.getRoomId() == roomId) {
|
if (Objects.equals(bean.getRoomId(), roomId)) {
|
||||||
return bean;
|
return bean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean deleteConfig(BigInteger roomId) {
|
public boolean deleteConfig(BigInteger roomId) {
|
||||||
LiveConfigDatabaseBean config = getConfig(roomId);
|
LiveConfigDatabaseBean config = getConfig(roomId);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
@ -57,6 +61,7 @@ public class BiliLiveConfigDatabase extends SQLiteManager {
|
|||||||
}
|
}
|
||||||
return delete(config);
|
return delete(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<LiveConfigDatabaseBean> getAllConfig() {
|
public List<LiveConfigDatabaseBean> getAllConfig() {
|
||||||
List<LiveConfigDatabaseBean> list = get(getDataBean().get(0).getTableName(), LiveConfigDatabaseBean.class);
|
List<LiveConfigDatabaseBean> list = get(getDataBean().get(0).getTableName(), LiveConfigDatabaseBean.class);
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
|
@ -4,36 +4,51 @@ import com.alibaba.fastjson2.util.DateUtils;
|
|||||||
import com.yutou.biliapi.bean.live.*;
|
import com.yutou.biliapi.bean.live.*;
|
||||||
import com.yutou.biliapi.bean.live.database.*;
|
import com.yutou.biliapi.bean.live.database.*;
|
||||||
import com.yutou.biliapi.bean.websocket.live.*;
|
import com.yutou.biliapi.bean.websocket.live.*;
|
||||||
|
import com.yutou.bilibili.Tools.DateFormatUtils;
|
||||||
import com.yutou.common.databases.AbsDatabasesBean;
|
import com.yutou.common.databases.AbsDatabasesBean;
|
||||||
import com.yutou.common.databases.SQLiteManager;
|
import com.yutou.common.databases.SQLiteManager;
|
||||||
import com.yutou.common.okhttp.HttpDownloadUtils;
|
import com.yutou.common.okhttp.HttpDownloadUtils;
|
||||||
|
import org.apache.poi.ss.usermodel.DataFormat;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static com.alibaba.fastjson2.util.DateUtils.DateTimeFormatPattern.DATE_FORMAT_10_DASH;
|
import static com.alibaba.fastjson2.util.DateUtils.DateTimeFormatPattern.DATE_FORMAT_10_DASH;
|
||||||
|
|
||||||
public class BiliLiveDatabase extends SQLiteManager {
|
public class BiliLiveDatabase extends SQLiteManager {
|
||||||
LiveInfoDatabaseBean bean;
|
|
||||||
LiveRoomConfig config;
|
LiveRoomConfig config;
|
||||||
String fileName;
|
String fileName;
|
||||||
File rootPath;
|
File rootPath;
|
||||||
|
|
||||||
|
public BiliLiveDatabase(LiveRoomConfig roomConfig, String path) {
|
||||||
|
this.config = roomConfig;
|
||||||
|
rootPath = new File(path).getParentFile();
|
||||||
|
fileName =path;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
public BiliLiveDatabase(LiveRoomConfig roomConfig) {
|
public BiliLiveDatabase(LiveRoomConfig roomConfig) {
|
||||||
String time = DateUtils.format(new Date().getTime(), DATE_FORMAT_10_DASH);
|
String time = DateUtils.format(new Date().getTime(), DATE_FORMAT_10_DASH);
|
||||||
rootPath = new File(roomConfig.getAnchorName() + File.separator + time + roomConfig.getRoomInfo().getTitle());
|
rootPath = new File(roomConfig.getRootPath() + File.separator + roomConfig.getAnchorName() + File.separator + time + File.separator + roomConfig.getRoomInfo().getTitle());
|
||||||
config=roomConfig;
|
config = roomConfig;
|
||||||
if(!rootPath.exists()){
|
if (!rootPath.exists()) {
|
||||||
rootPath.mkdirs();
|
rootPath.mkdirs();
|
||||||
}
|
}
|
||||||
fileName=rootPath.getAbsolutePath()+File.separator+"live.db";
|
fileName = rootPath.getAbsolutePath() + File.separator + "live.db";
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
super.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
|
@Override
|
||||||
@ -49,17 +64,22 @@ public class BiliLiveDatabase extends SQLiteManager {
|
|||||||
new LiveGiftDatabaseBean(),
|
new LiveGiftDatabaseBean(),
|
||||||
new LiveInteractWordDatabaseBean(),
|
new LiveInteractWordDatabaseBean(),
|
||||||
new LiveSuperChatDatabaseBean(),
|
new LiveSuperChatDatabaseBean(),
|
||||||
new LiveSourceDatabaseBean()
|
new LiveSourceDatabaseBean(),
|
||||||
|
new LiveVideoDatabaseBean()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addLiveInfo(LiveRoomInfo info) {
|
@Override
|
||||||
this.bean = new LiveInfoDatabaseBean(info);
|
public void close() {
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
|
||||||
List<LiveInfoDatabaseBean> infos = get(bean.getTableName(), LiveInfoDatabaseBean.class);
|
public void addLiveInfo(LiveVideoDatabaseBean info) {
|
||||||
if (infos.isEmpty()) {
|
createInfo(info);
|
||||||
createInfo(bean);
|
}
|
||||||
}
|
|
||||||
|
public List<LiveVideoDatabaseBean> getLiveInfos() {
|
||||||
|
return get(new LiveVideoDatabaseBean().getTableName(), LiveVideoDatabaseBean.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addData(WSData bean) {
|
private void addData(WSData bean) {
|
||||||
@ -75,30 +95,32 @@ public class BiliLiveDatabase extends SQLiteManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addSource(WSData bean) {
|
public void addSource(WSData bean) {
|
||||||
|
System.out.println("BiliLiveDatabase.addSource");
|
||||||
add(new LiveSourceDatabaseBean(bean));
|
add(new LiveSourceDatabaseBean(bean));
|
||||||
addData(bean);
|
addData(bean);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createInfo(LiveInfoDatabaseBean bean) {
|
private void createInfo(LiveVideoDatabaseBean bean) {
|
||||||
add(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) {
|
public <T extends AbsDatabasesBean> List<T> getOfTime(String startTime, String entTime, Class<T> clazz) {
|
||||||
return get(startTime, entTime, LiveSourceDatabaseBean.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends AbsDatabasesBean> List<T> get(long startTime, long entTime, Class<T> clazz) {
|
|
||||||
String tableName = null;
|
String tableName = null;
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
String where = null;
|
String where = null;
|
||||||
if (startTime != -1) {
|
if (startTime != null) {
|
||||||
sb.append(" `sql_time` >= ").append(startTime);
|
sb.append(" `sql_time` >= ").append("\"").append(startTime).append("\"");
|
||||||
}
|
}
|
||||||
if (entTime != -1) {
|
if (entTime != null) {
|
||||||
if (!sb.isEmpty()) {
|
if (!sb.isEmpty()) {
|
||||||
sb.append(" and ");
|
sb.append(" and ");
|
||||||
}
|
}
|
||||||
sb.append(" `sql_time` <= ").append(entTime);
|
sb.append(" `sql_time` <= ").append("\"").append(entTime).append("\"");
|
||||||
}
|
}
|
||||||
if (!sb.isEmpty()) {
|
if (!sb.isEmpty()) {
|
||||||
where = sb.toString();
|
where = sb.toString();
|
||||||
|
@ -4,8 +4,8 @@ import com.aayushatharva.brotli4j.Brotli4jLoader;
|
|||||||
import com.aayushatharva.brotli4j.decoder.Decoder;
|
import com.aayushatharva.brotli4j.decoder.Decoder;
|
||||||
import com.aayushatharva.brotli4j.decoder.DecoderJNI;
|
import com.aayushatharva.brotli4j.decoder.DecoderJNI;
|
||||||
import com.aayushatharva.brotli4j.decoder.DirectDecompress;
|
import com.aayushatharva.brotli4j.decoder.DirectDecompress;
|
||||||
|
import com.alibaba.fastjson2.JSONArray;
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.alibaba.fastjson2.util.DateUtils;
|
|
||||||
import com.yutou.biliapi.api.LiveApi;
|
import com.yutou.biliapi.api.LiveApi;
|
||||||
import com.yutou.biliapi.bean.live.LiveDanmuInfo;
|
import com.yutou.biliapi.bean.live.LiveDanmuInfo;
|
||||||
import com.yutou.biliapi.bean.live.LiveRoomConfig;
|
import com.yutou.biliapi.bean.live.LiveRoomConfig;
|
||||||
@ -26,7 +26,6 @@ import org.java_websocket.handshake.ServerHandshake;
|
|||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
@ -34,15 +33,19 @@ import java.net.URISyntaxException;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
import static com.alibaba.fastjson2.util.DateUtils.DateTimeFormatPattern.DATE_FORMAT_10_DASH;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class WebSocketManager {
|
public class WebSocketManager {
|
||||||
|
ThreadPoolExecutor executor;
|
||||||
private static WebSocketManager instance;
|
private static WebSocketManager instance;
|
||||||
Map<LiveRoomConfig, WebSocketClientTh> roomMap;
|
Map<LiveRoomConfig, DanmuTask> roomMap;
|
||||||
|
private final List<String> userStopList = new ArrayList<>();//手动停止列表
|
||||||
|
|
||||||
private WebSocketManager() {
|
private WebSocketManager() {
|
||||||
roomMap = new HashMap<>();
|
roomMap = new HashMap<>();
|
||||||
|
executor = new ThreadPoolExecutor(2, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WebSocketManager getInstance() {
|
public static WebSocketManager getInstance() {
|
||||||
@ -56,68 +59,107 @@ public class WebSocketManager {
|
|||||||
return roomMap.containsKey(roomConfig);
|
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)) {
|
if (checkRoom(roomConfig)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LiveApi api = BiliLiveNetApiManager.getInstance().getApi(roomConfig.getLoginUid());
|
if (isUser) {
|
||||||
Response<HttpBody<LiveRoomInfo>> execute = null;
|
userStopList.remove(roomConfig.getRoomId().toString());
|
||||||
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.getLoginUid())).enqueue(new HttpCallback<LiveDanmuInfo>() {
|
DanmuTask task = new DanmuTask(roomConfig);
|
||||||
@Override
|
roomMap.put(roomConfig, task);
|
||||||
public void onResponse(Headers headers, int code, String status, LiveDanmuInfo response, String rawResponse) {
|
System.out.println("添加websocket任务");
|
||||||
if (!response.getHostList().isEmpty()) {
|
executor.execute(task);
|
||||||
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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopRoom(LiveRoomConfig roomConfig) {
|
public void stopRoom(String roomId, boolean isUser) {
|
||||||
|
LiveRoomConfig roomConfig=new LiveRoomConfig();
|
||||||
|
roomConfig.setRoomId(new BigInteger(roomId));
|
||||||
if (checkRoom(roomConfig)) {
|
if (checkRoom(roomConfig)) {
|
||||||
roomMap.get(roomConfig).close();
|
roomMap.get(roomConfig).close();
|
||||||
roomMap.remove(roomConfig);
|
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 static class WebSocketClientTh extends WebSocketClient {
|
||||||
private LiveRoomConfig roomConfig;
|
private LiveRoomConfig roomConfig;
|
||||||
private HeartbeatTask heartbeatTask;
|
private HeartbeatTask heartbeatTask;
|
||||||
BiliLiveDatabase liveDatabase;
|
BiliLiveDatabase liveDatabase;
|
||||||
|
private boolean itTmp = true;
|
||||||
|
|
||||||
public WebSocketClientTh(URI serverUri, LiveRoomConfig roomId) {
|
public WebSocketClientTh(URI serverUri, LiveRoomConfig roomId) {
|
||||||
super(serverUri);
|
super(serverUri);
|
||||||
|
System.out.println("WebSocketClientTh.WebSocketClientTh : " + serverUri);
|
||||||
this.roomConfig = roomId;
|
this.roomConfig = roomId;
|
||||||
heartbeatTask = new HeartbeatTask();
|
|
||||||
Brotli4jLoader.ensureAvailability();
|
|
||||||
liveDatabase = new BiliLiveDatabase(roomConfig);
|
liveDatabase = new BiliLiveDatabase(roomConfig);
|
||||||
|
Brotli4jLoader.ensureAvailability();
|
||||||
|
heartbeatTask = new HeartbeatTask();
|
||||||
connect();
|
connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOpen(ServerHandshake serverHandshake) {
|
public void onOpen(ServerHandshake serverHandshake) {
|
||||||
WebSocketManager.getInstance().roomMap.put(roomConfig, this);
|
|
||||||
heartbeatTask.setSocket(this);
|
heartbeatTask.setSocket(this);
|
||||||
heartbeatTask.sendInitAuthData();
|
heartbeatTask.sendInitAuthData();
|
||||||
new Timer().schedule(heartbeatTask, 1000, 30000);
|
new Timer().schedule(heartbeatTask, 1000, 30000);
|
||||||
@ -131,7 +173,7 @@ public class WebSocketManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(ByteBuffer bytes) {
|
public void onMessage(ByteBuffer bytes) {
|
||||||
System.out.println("WebSocketClientTh.onMessage");
|
System.out.println("WebSocketClientTh.onMessage: " + roomConfig.getAnchorName());
|
||||||
super.onMessage(bytes);
|
super.onMessage(bytes);
|
||||||
decompress(bytes.array());
|
decompress(bytes.array());
|
||||||
}
|
}
|
||||||
@ -161,7 +203,7 @@ public class WebSocketManager {
|
|||||||
byte[] bytes = new byte[data.length - 16];
|
byte[] bytes = new byte[data.length - 16];
|
||||||
WebSocketHeader header = new WebSocketHeader(data);
|
WebSocketHeader header = new WebSocketHeader(data);
|
||||||
System.arraycopy(data, header.getHeaderSize(), bytes, 0, data.length - header.getHeaderSize());
|
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()) {
|
switch (header.getAgree()) {
|
||||||
case 0:
|
case 0:
|
||||||
case 1:
|
case 1:
|
||||||
@ -183,14 +225,14 @@ public class WebSocketManager {
|
|||||||
DirectDecompress directDecompress = Decoder.decompress(bytes);
|
DirectDecompress directDecompress = Decoder.decompress(bytes);
|
||||||
if (directDecompress.getResultStatus() == DecoderJNI.Status.DONE) {
|
if (directDecompress.getResultStatus() == DecoderJNI.Status.DONE) {
|
||||||
WebSocketBody body = new WebSocketBody(directDecompress.getDecompressedData());
|
WebSocketBody body = new WebSocketBody(directDecompress.getDecompressedData());
|
||||||
Log.i("3协议:" + useHeader + " 命令数:" + body.getBodyList().size());
|
// Log.i("协议:" + useHeader + " 命令数:" + body.getBodyList().size());
|
||||||
for (JSONObject json : body.getBodyList()) {
|
for (JSONObject json : body.getBodyList()) {
|
||||||
WSData parse = WSData.parse(json);
|
WSData parse = WSData.parse(json);
|
||||||
liveDatabase.addSource(parse);
|
liveDatabase.addSource(parse);
|
||||||
Log.i("解压:" + parse);
|
// Log.i("解压:" + parse);
|
||||||
}
|
}
|
||||||
System.out.println();
|
// System.out.println();
|
||||||
System.out.println();
|
// System.out.println();
|
||||||
} else {
|
} else {
|
||||||
Log.e(new RuntimeException("解压失败"));
|
Log.e(new RuntimeException("解压失败"));
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ public class LiveConfigController {
|
|||||||
|
|
||||||
@RequestMapping(value = "set", method = RequestMethod.POST)
|
@RequestMapping(value = "set", method = RequestMethod.POST)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public ResultData<JSONObject> setConfig(String url, LiveConfigDatabaseBean bean) {
|
public JSONObject setConfig(String url, LiveConfigDatabaseBean bean) {
|
||||||
LiveConfigDatabaseBean config = configService.addConfig(url, bean);
|
LiveConfigDatabaseBean config = configService.addConfig(url, bean);
|
||||||
if (config != null) {
|
if (config != null) {
|
||||||
return ResultData.success(config.toJson());
|
return ResultData.success(config.toJson());
|
||||||
@ -34,7 +34,7 @@ public class LiveConfigController {
|
|||||||
|
|
||||||
@RequestMapping(value = "update", method = RequestMethod.POST)
|
@RequestMapping(value = "update", method = RequestMethod.POST)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public ResultData<JSONObject> updateConfig(String roomId, LiveConfigDatabaseBean bean) {
|
public JSONObject updateConfig(String roomId, LiveConfigDatabaseBean bean) {
|
||||||
LiveConfigDatabaseBean config = configService.updateConfig(new BigInteger(roomId), bean);
|
LiveConfigDatabaseBean config = configService.updateConfig(new BigInteger(roomId), bean);
|
||||||
if (config != null) {
|
if (config != null) {
|
||||||
return ResultData.success(config.toJson());
|
return ResultData.success(config.toJson());
|
||||||
@ -44,7 +44,7 @@ public class LiveConfigController {
|
|||||||
|
|
||||||
@RequestMapping(value = "get", method = RequestMethod.GET)
|
@RequestMapping(value = "get", method = RequestMethod.GET)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public ResultData<JSONObject> getConfig(String roomId) {
|
public JSONObject getConfig(String roomId) {
|
||||||
if ("0".equals(roomId) || !StringUtils.hasText(roomId)) {
|
if ("0".equals(roomId) || !StringUtils.hasText(roomId)) {
|
||||||
return ResultData.fail(ReturnCode.RC999);
|
return ResultData.fail(ReturnCode.RC999);
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ public class LiveConfigController {
|
|||||||
|
|
||||||
@RequestMapping(value = "all", method = RequestMethod.GET)
|
@RequestMapping(value = "all", method = RequestMethod.GET)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public ResultData<JSONArray> getAllConfig() {
|
public JSONObject getAllConfig() {
|
||||||
List<LiveConfigDatabaseBean> config = configService.getAllConfig();
|
List<LiveConfigDatabaseBean> config = configService.getAllConfig();
|
||||||
if (config != null) {
|
if (config != null) {
|
||||||
return ResultData.success(JSONArray.parseArray(JSONArray.toJSONString(config)));
|
return ResultData.success(JSONArray.parseArray(JSONArray.toJSONString(config)));
|
||||||
@ -67,7 +67,7 @@ public class LiveConfigController {
|
|||||||
|
|
||||||
@RequestMapping(value = "delete", method = RequestMethod.GET)
|
@RequestMapping(value = "delete", method = RequestMethod.GET)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public ResultData<JSONObject> deleteConfig(BigInteger roomId) {
|
public JSONObject deleteConfig(BigInteger roomId) {
|
||||||
if (roomId.equals(BigInteger.ZERO)) {
|
if (roomId.equals(BigInteger.ZERO)) {
|
||||||
return ResultData.fail(ReturnCode.RC999);
|
return ResultData.fail(ReturnCode.RC999);
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,14 +1,19 @@
|
|||||||
package com.yutou.bilibili.Tools;
|
package com.yutou.bilibili.Tools;
|
||||||
|
|
||||||
|
import com.yutou.bilibili.services.SystemService;
|
||||||
import com.yutou.common.utils.Log;
|
import com.yutou.common.utils.Log;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.context.ApplicationListener;
|
import org.springframework.context.ApplicationListener;
|
||||||
import org.springframework.context.event.ContextClosedEvent;
|
import org.springframework.context.event.ContextClosedEvent;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class ApplicationClose implements ApplicationListener<ContextClosedEvent> {
|
public class ApplicationClose implements ApplicationListener<ContextClosedEvent> {
|
||||||
|
@Resource
|
||||||
|
SystemService systemConfigService;
|
||||||
@Override
|
@Override
|
||||||
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
|
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
|
||||||
Log.i("服务结束");
|
Log.i("服务结束");
|
||||||
|
systemConfigService.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ public class AssTools {
|
|||||||
private final Date startTime;
|
private final Date startTime;
|
||||||
private int y = 0;
|
private int y = 0;
|
||||||
private List<String> filters = new ArrayList<>();
|
private List<String> filters = new ArrayList<>();
|
||||||
private String alpha="80";
|
private String alpha="100";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 弹幕转换ass
|
* 弹幕转换ass
|
||||||
@ -140,7 +140,7 @@ public class AssTools {
|
|||||||
y,
|
y,
|
||||||
x2,
|
x2,
|
||||||
y,
|
y,
|
||||||
danmuData.getFontColorHex(),
|
danmuData.getFontColor(),
|
||||||
alpha,
|
alpha,
|
||||||
danmuData.getDanmu()
|
danmuData.getDanmu()
|
||||||
);
|
);
|
||||||
|
22
src/main/java/com/yutou/bilibili/Tools/DateFormatUtils.java
Normal file
22
src/main/java/com/yutou/bilibili/Tools/DateFormatUtils.java
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
18
src/main/java/com/yutou/bilibili/Tools/FileServerUtils.java
Normal file
18
src/main/java/com/yutou/bilibili/Tools/FileServerUtils.java
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
@ -13,11 +13,11 @@ import java.net.HttpURLConnection;
|
|||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.file.*;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Base64;
|
import java.util.*;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
public class Tools {
|
public class Tools {
|
||||||
|
|
||||||
@ -203,4 +203,24 @@ public class Tools {
|
|||||||
public static String getToDayTime() {
|
public static String getToDayTime() {
|
||||||
return new SimpleDateFormat("yyyy-MM-dd").format(new Date());
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ public class DanmuData {
|
|||||||
private int id;
|
private int id;
|
||||||
private int model;//1~3 滚动弹幕 4 底端弹幕 5 顶端弹幕 6 逆向弹幕 7 精准定位 8 高级弹幕
|
private int model;//1~3 滚动弹幕 4 底端弹幕 5 顶端弹幕 6 逆向弹幕 7 精准定位 8 高级弹幕
|
||||||
private int fontSize;
|
private int fontSize;
|
||||||
private int fontColor;
|
private String fontColor;
|
||||||
private long time;
|
private long time;
|
||||||
private String uCode;
|
private String uCode;
|
||||||
private String danmu;
|
private String danmu;
|
||||||
@ -20,7 +20,4 @@ public class DanmuData {
|
|||||||
return new Date(time);
|
return new Date(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFontColorHex() {
|
|
||||||
return Integer.toHexString(fontColor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.yutou.bilibili.datas;
|
package com.yutou.bilibili.datas;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@ -9,33 +10,33 @@ public class ResultData<T> {
|
|||||||
private T data;
|
private T data;
|
||||||
private long timestamp ;
|
private long timestamp ;
|
||||||
|
|
||||||
public ResultData (){
|
public ResultData() {
|
||||||
this.timestamp = System.currentTimeMillis();
|
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<T> resultData = new ResultData<>();
|
||||||
resultData.setStatus(ReturnCode.RC100.getCode());
|
resultData.setStatus(ReturnCode.RC100.getCode());
|
||||||
resultData.setMessage(ReturnCode.RC100.getMessage());
|
resultData.setMessage(ReturnCode.RC100.getMessage());
|
||||||
resultData.setData(data);
|
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<T> resultData = new ResultData<>();
|
||||||
resultData.setStatus(code);
|
resultData.setStatus(code);
|
||||||
resultData.setMessage(message);
|
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);
|
return fail(code);
|
||||||
}
|
}
|
||||||
public static <T> ResultData<T> fail(ReturnCode code) {
|
public static <T> JSONObject fail(ReturnCode code) {
|
||||||
ResultData<T> resultData = new ResultData<>();
|
ResultData<T> resultData = new ResultData<>();
|
||||||
resultData.setStatus(code.getCode());
|
resultData.setStatus(code.getCode());
|
||||||
resultData.setMessage(code.getMessage());
|
resultData.setMessage(code.getMessage());
|
||||||
return resultData;
|
return JSONObject.parseObject(JSONObject.toJSONString(resultData));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import lombok.EqualsAndHashCode;
|
|||||||
@Data
|
@Data
|
||||||
public class SystemConfigDatabaseBean extends AbsDatabasesBean {
|
public class SystemConfigDatabaseBean extends AbsDatabasesBean {
|
||||||
@JSONField(name = "timer_loop")
|
@JSONField(name = "timer_loop")
|
||||||
private long timerLoop = 5000;
|
private long timerLoop = 30000;
|
||||||
|
|
||||||
|
|
||||||
public SystemConfigDatabaseBean() {
|
public SystemConfigDatabaseBean() {
|
||||||
|
15
src/main/java/com/yutou/bilibili/datas/VideoFilePath.java
Normal file
15
src/main/java/com/yutou/bilibili/datas/VideoFilePath.java
Normal 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;
|
||||||
|
}
|
@ -3,7 +3,8 @@ package com.yutou.bilibili.interfaces;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
public abstract class DownloadInterface {
|
public abstract class DownloadInterface {
|
||||||
public void onDownloading(double soFarBytes, double totalBytes){};
|
public void onDownloadStart(){}
|
||||||
public void onDownload(File file){};
|
public boolean onDownloading(double soFarBytes, double totalBytes){return true;}
|
||||||
public void onError(Exception e){};
|
public void onDownload(File file){}
|
||||||
|
public void onError(Exception e){}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,83 @@
|
|||||||
package com.yutou.bilibili.services;
|
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 org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class LiveDanmuService {
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
377
src/main/java/com/yutou/bilibili/services/LiveVideoService.java
Normal file
377
src/main/java/com/yutou/bilibili/services/LiveVideoService.java
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,8 @@ import java.util.concurrent.TimeUnit;
|
|||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class SystemService {
|
public class SystemService {
|
||||||
|
@Resource
|
||||||
|
LiveVideoService videoService;
|
||||||
SystemConfigDatabases databases = new SystemConfigDatabases();
|
SystemConfigDatabases databases = new SystemConfigDatabases();
|
||||||
private ScheduledExecutorService timer;
|
private ScheduledExecutorService timer;
|
||||||
private ScheduledFuture<?> scheduled;
|
private ScheduledFuture<?> scheduled;
|
||||||
@ -50,6 +52,7 @@ public class SystemService {
|
|||||||
}
|
}
|
||||||
scheduled = timer.scheduleAtFixedRate(() -> {
|
scheduled = timer.scheduleAtFixedRate(() -> {
|
||||||
List<LiveConfigDatabaseBean> list = liveConfigDatabase.getAllConfig();
|
List<LiveConfigDatabaseBean> list = liveConfigDatabase.getAllConfig();
|
||||||
|
System.out.println("循环任务:"+list.size());
|
||||||
for (LiveConfigDatabaseBean bean : list) {
|
for (LiveConfigDatabaseBean bean : list) {
|
||||||
if (bean.isRecordDanmu() && bean.checkRecordDanmuTime()) {
|
if (bean.isRecordDanmu() && bean.checkRecordDanmuTime()) {
|
||||||
recordDanmu(bean);
|
recordDanmu(bean);
|
||||||
@ -60,7 +63,10 @@ public class SystemService {
|
|||||||
}
|
}
|
||||||
}, 0, getLoopTimer(), TimeUnit.MILLISECONDS);
|
}, 0, getLoopTimer(), TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
public void stop() {
|
||||||
|
scheduled.cancel(true);
|
||||||
|
videoService.stopAll();
|
||||||
|
}
|
||||||
// 录制弹幕
|
// 录制弹幕
|
||||||
private void recordDanmu(LiveConfigDatabaseBean bean) {
|
private void recordDanmu(LiveConfigDatabaseBean bean) {
|
||||||
LiveRoomConfig config = new LiveRoomConfig();
|
LiveRoomConfig config = new LiveRoomConfig();
|
||||||
@ -68,12 +74,13 @@ public class SystemService {
|
|||||||
config.setRoomId(bean.getRoomId());
|
config.setRoomId(bean.getRoomId());
|
||||||
config.setAnchorName(bean.getAnchorName());
|
config.setAnchorName(bean.getAnchorName());
|
||||||
config.setLogin(StringUtils.hasText(bean.getRecordUid()));
|
config.setLogin(StringUtils.hasText(bean.getRecordUid()));
|
||||||
WebSocketManager.getInstance().addRoom(config);
|
config.setRootPath(bean.getRecordPath());
|
||||||
|
WebSocketManager.getInstance().addRoom(config,false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 录制视频
|
// 录制视频
|
||||||
private void recordVideo(LiveConfigDatabaseBean bean) {
|
private void recordVideo(LiveConfigDatabaseBean bean) {
|
||||||
|
videoService.start(bean, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws InterruptedException {
|
public static void main(String[] args) throws InterruptedException {
|
||||||
@ -85,4 +92,5 @@ public class SystemService {
|
|||||||
service.start();
|
service.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ public class BiliBiliLiveDatabasesManager extends SQLiteManager {
|
|||||||
"values (%d,%d,%d,%d,'%s','%s',%d,'%s')"
|
"values (%d,%d,%d,%d,'%s','%s',%d,'%s')"
|
||||||
,data.getModel()
|
,data.getModel()
|
||||||
,data.getFontSize()
|
,data.getFontSize()
|
||||||
,data.getFontColor()
|
// ,data.getFontColor()
|
||||||
,data.getTime()
|
,data.getTime()
|
||||||
,data.getUCode()
|
,data.getUCode()
|
||||||
,data.getDanmu()
|
,data.getDanmu()
|
||||||
@ -87,7 +87,7 @@ public class BiliBiliLiveDatabasesManager extends SQLiteManager {
|
|||||||
data.setId(set.getInt("id"));
|
data.setId(set.getInt("id"));
|
||||||
data.setModel(set.getInt("model"));
|
data.setModel(set.getInt("model"));
|
||||||
data.setFontSize(set.getInt("fontSize"));
|
data.setFontSize(set.getInt("fontSize"));
|
||||||
data.setFontColor(set.getInt("fontColor"));
|
// data.setFontColor(set.getInt("fontColor"));
|
||||||
data.setTime(set.getLong("time"));
|
data.setTime(set.getLong("time"));
|
||||||
data.setUCode(set.getString("uCode"));
|
data.setUCode(set.getString("uCode"));
|
||||||
data.setDanmu(set.getString("danmu"));
|
data.setDanmu(set.getString("danmu"));
|
||||||
|
@ -40,7 +40,7 @@ public class AbsDatabasesBean {
|
|||||||
json.put(key,"");
|
json.put(key,"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.out.println("创建" + tableName + "表 json:" + json);
|
//System.out.println("创建" + tableName + "表 json:" + json);
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import com.yutou.common.inter.ISqlDatabaseBean;
|
|||||||
import com.yutou.common.utils.Log;
|
import com.yutou.common.utils.Log;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import org.sqlite.SQLiteConfig;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
@ -36,7 +37,9 @@ public abstract class SQLiteManager {
|
|||||||
items = new ArrayList<>();
|
items = new ArrayList<>();
|
||||||
for (Field field : fields) {
|
for (Field field : fields) {
|
||||||
String name = field.getAnnotation(JSONField.class).name();
|
String name = field.getAnnotation(JSONField.class).name();
|
||||||
if (name.equals("tableName")) continue;
|
if ("tableName".equals(name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
String type = BuildSqlItem.TYPE_STRING;
|
String type = BuildSqlItem.TYPE_STRING;
|
||||||
if (field.getType() == int.class) {
|
if (field.getType() == int.class) {
|
||||||
type = BuildSqlItem.TYPE_INT;
|
type = BuildSqlItem.TYPE_INT;
|
||||||
@ -97,10 +100,10 @@ public abstract class SQLiteManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends AbsDatabasesBean> void add(T t) {
|
protected <T extends AbsDatabasesBean> void add(T t) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
try {
|
try {
|
||||||
Statement statement = conn.createStatement();
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
StringBuilder value = new StringBuilder();
|
StringBuilder value = new StringBuilder();
|
||||||
sb.append("INSERT INTO `").append(t.getTableName()).append("` ");
|
sb.append("INSERT INTO `").append(t.getTableName()).append("` ");
|
||||||
sb.append("(");
|
sb.append("(");
|
||||||
@ -112,22 +115,44 @@ public abstract class SQLiteManager {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
sb.append("`").append(key).append("`,");
|
sb.append("`").append(key).append("`,");
|
||||||
value.append("'").append(json.get(key)).append("',");
|
//value.append("'").append(json.get(key)).append("',");
|
||||||
|
value.append("?").append(",");
|
||||||
}
|
}
|
||||||
sb.deleteCharAt(sb.length() - 1);
|
sb.deleteCharAt(sb.length() - 1);
|
||||||
value.deleteCharAt(value.length() - 1);
|
value.deleteCharAt(value.length() - 1);
|
||||||
value.append(")");
|
value.append(")");
|
||||||
sb.append(") VALUES ");
|
sb.append(") VALUES ");
|
||||||
sb.append(value);
|
sb.append(value);
|
||||||
statement.executeUpdate(sb.toString());
|
PreparedStatement statement = conn.prepareStatement(sb.toString());
|
||||||
|
int i = 1;
|
||||||
|
for (String key : keySet) {
|
||||||
|
if ("id".equals(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (json.get(key) instanceof String) {
|
||||||
|
statement.setString(i++, json.get(key).toString());
|
||||||
|
} else if (json.get(key) instanceof Integer) {
|
||||||
|
statement.setInt(i++, json.getInteger(key));
|
||||||
|
} else if (json.get(key) instanceof Long) {
|
||||||
|
statement.setLong(i++, json.getLong(key));
|
||||||
|
} else if (json.get(key) instanceof BigInteger) {
|
||||||
|
statement.setObject(i++, json.get(key));
|
||||||
|
} else if (json.get(key) instanceof Boolean) {
|
||||||
|
statement.setBoolean(i++, json.getBoolean(key));
|
||||||
|
} else {
|
||||||
|
statement.setObject(i++, json.get(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statement.execute();
|
||||||
statement.close();
|
statement.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
System.err.println(sb);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends AbsDatabasesBean> void update(T t) {
|
protected <T extends AbsDatabasesBean> void update(T t) {
|
||||||
try {
|
try {
|
||||||
String id = DateUtils.format(t.getSql_time(), "yyyy-MM-dd HH:mm:ss.SSS");
|
String id = DateUtils.format(t.getSql_time(), "yyyy-MM-dd HH:mm:ss.SSS");
|
||||||
Statement statement = conn.createStatement();
|
Statement statement = conn.createStatement();
|
||||||
@ -158,11 +183,11 @@ public abstract class SQLiteManager {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends AbsDatabasesBean> List<T> get(String table, Class<T> tClass) {
|
protected <T extends AbsDatabasesBean> List<T> get(String table, Class<T> tClass) {
|
||||||
return get(table, null, tClass);
|
return get(table, null, tClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends AbsDatabasesBean> List<T> get(String table, String where, Class<T> tClass) {
|
protected <T extends AbsDatabasesBean> List<T> get(String table, String where, Class<T> tClass) {
|
||||||
List<T> list = new ArrayList<>();
|
List<T> list = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
Statement statement = conn.createStatement();
|
Statement statement = conn.createStatement();
|
||||||
@ -177,9 +202,16 @@ public abstract class SQLiteManager {
|
|||||||
String name = resultSet.getMetaData().getColumnName(i + 1);
|
String name = resultSet.getMetaData().getColumnName(i + 1);
|
||||||
Object value = resultSet.getObject(name);
|
Object value = resultSet.getObject(name);
|
||||||
if (value instanceof String && ((String) value).startsWith("[")) {
|
if (value instanceof String && ((String) value).startsWith("[")) {
|
||||||
json.put(name, JSON.parseArray((String) value));
|
try {
|
||||||
|
json.put(name, JSON.parseArray((String) value));
|
||||||
|
} catch (Exception e) {
|
||||||
|
json.put(name, value);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
json.put(name, resultSet.getObject(name));
|
json.put(name, value);
|
||||||
|
if (json.get(name) == null || "null".equals(json.get(name).toString())) {
|
||||||
|
json.put(name, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list.add(json.to(tClass));
|
list.add(json.to(tClass));
|
||||||
@ -214,7 +246,7 @@ public abstract class SQLiteManager {
|
|||||||
try {
|
try {
|
||||||
sql.mkdirs();
|
sql.mkdirs();
|
||||||
sql.delete();
|
sql.delete();
|
||||||
conn = DriverManager.getConnection(url + sql.getAbsolutePath());
|
linkDB();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(e);
|
Log.e(e);
|
||||||
}
|
}
|
||||||
@ -273,28 +305,17 @@ public abstract class SQLiteManager {
|
|||||||
if (!sql.exists()) {
|
if (!sql.exists()) {
|
||||||
createSql(buildSql);
|
createSql(buildSql);
|
||||||
} else {
|
} else {
|
||||||
conn = DriverManager.getConnection(url + sql.getAbsolutePath());
|
linkDB();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(e);
|
Log.e(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean setDB(String fileName) {
|
private void linkDB() throws SQLException {
|
||||||
try {
|
SQLiteConfig config = new SQLiteConfig();
|
||||||
Class.forName("org.sqlite.JDBC");
|
config.enforceForeignKeys(true);
|
||||||
sql = new File("db" + File.separator + fileName);
|
conn = config.createConnection(url + sql.getAbsolutePath());
|
||||||
if (sql.exists()) {
|
|
||||||
if (conn != null && !conn.isClosed()) {
|
|
||||||
conn.close();
|
|
||||||
}
|
|
||||||
conn = DriverManager.getConnection(url + sql.getAbsolutePath());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(e);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,10 +18,11 @@ public abstract class FileCallback<T> implements Callback<FileBody<T>> {
|
|||||||
|
|
||||||
private static ThreadPoolExecutor executor;
|
private static ThreadPoolExecutor executor;
|
||||||
private final T bean;
|
private final T bean;
|
||||||
|
private String savePath;
|
||||||
|
|
||||||
|
public FileCallback(T bean, String savePath) {
|
||||||
public FileCallback(T bean) {
|
|
||||||
this.bean = bean;
|
this.bean = bean;
|
||||||
|
this.savePath = savePath;
|
||||||
if (executor == null) {
|
if (executor == null) {
|
||||||
executor = new ThreadPoolExecutor(2, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
|
executor = new ThreadPoolExecutor(2, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
|
||||||
}
|
}
|
||||||
@ -45,7 +46,7 @@ public abstract class FileCallback<T> implements Callback<FileBody<T>> {
|
|||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
System.out.println("开始下载");
|
System.out.println("开始下载");
|
||||||
File file = new File("download" + File.separator + System.currentTimeMillis() + ".flv");
|
File file = new File(savePath);
|
||||||
onStart(bean);
|
onStart(bean);
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
boolean mkdirs = file.getParentFile().mkdirs();
|
boolean mkdirs = file.getParentFile().mkdirs();
|
||||||
@ -67,6 +68,7 @@ public abstract class FileCallback<T> implements Callback<FileBody<T>> {
|
|||||||
outputStream.close();
|
outputStream.close();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
onFailure(bean,e);
|
||||||
} finally {
|
} finally {
|
||||||
onFinish(bean);
|
onFinish(bean);
|
||||||
try {
|
try {
|
||||||
@ -91,7 +93,13 @@ public abstract class FileCallback<T> implements Callback<FileBody<T>> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call<FileBody<T>> call, Response<FileBody<T>> response) {
|
public void onResponse(Call<FileBody<T>> call, Response<FileBody<T>> response) {
|
||||||
executor.execute(new DownloadTask(bean, response.headers(), call.request().url(), response.body().getInputStream()));
|
try {
|
||||||
|
executor.execute(new DownloadTask(bean, response.headers(), call.request().url(), response.body().getInputStream()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
onFailure(bean,e);
|
||||||
|
call.cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,48 +1,115 @@
|
|||||||
package com.yutou.common.okhttp;
|
package com.yutou.common.okhttp;
|
||||||
|
|
||||||
|
import com.yutou.bilibili.interfaces.DownloadInterface;
|
||||||
|
import com.yutou.common.utils.ConfigTools;
|
||||||
import com.yutou.common.utils.Log;
|
import com.yutou.common.utils.Log;
|
||||||
|
import lombok.Data;
|
||||||
import okhttp3.*;
|
import okhttp3.*;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
public class HttpDownloadUtils {
|
public class HttpDownloadUtils {
|
||||||
public static void download(String url, String path,String fileName) {
|
public static void download(Builder builder) {
|
||||||
OkHttpClient okHttpClient = new OkHttpClient();
|
OkHttpClient okHttpClient = new OkHttpClient.Builder()
|
||||||
Request request = new Request.Builder()
|
.connectTimeout(2, TimeUnit.MINUTES)
|
||||||
.get()
|
.readTimeout(2, TimeUnit.MINUTES)
|
||||||
.url(url)
|
|
||||||
.build();
|
.build();
|
||||||
|
Request.Builder rb = new Request.Builder()
|
||||||
|
.get()
|
||||||
|
.addHeader("User-Agent", ConfigTools.getUserAgent())
|
||||||
|
.url(builder.url);
|
||||||
|
if (StringUtils.hasText(builder.cookie)) {
|
||||||
|
rb.addHeader("Set-Cookie", builder.cookie);
|
||||||
|
}
|
||||||
|
Request request = rb.build();
|
||||||
Call call = okHttpClient.newCall(request);
|
Call call = okHttpClient.newCall(request);
|
||||||
call.enqueue(new Callback() {
|
call.enqueue(new Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NotNull Call call, @NotNull IOException e) {
|
public void onFailure(@NotNull Call call, @NotNull IOException e) {
|
||||||
|
if (builder.downloadInterface != null) {
|
||||||
|
builder.downloadInterface.onError(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
|
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
|
||||||
InputStream inputStream = Objects.requireNonNull(response.body()).byteStream();
|
InputStream inputStream = Objects.requireNonNull(response.body()).byteStream();
|
||||||
File target = new File(path, fileName);
|
File target;
|
||||||
|
if (StringUtils.hasText(builder.fileName)) {
|
||||||
|
target = new File(builder.path, builder.fileName);
|
||||||
|
} else {
|
||||||
|
target = new File(builder.path);
|
||||||
|
}
|
||||||
FileOutputStream fileOutputStream = new FileOutputStream(target);
|
FileOutputStream fileOutputStream = new FileOutputStream(target);
|
||||||
|
if (builder.downloadInterface != null) {
|
||||||
|
builder.downloadInterface.onDownloadStart();
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
byte[] buffer = new byte[2048];
|
byte[] buffer = new byte[2048];
|
||||||
int len;
|
int len;
|
||||||
|
long soFarBytes = 0;
|
||||||
|
long totalBytes = inputStream.available();
|
||||||
while ((len = inputStream.read(buffer)) != -1) {
|
while ((len = inputStream.read(buffer)) != -1) {
|
||||||
fileOutputStream.write(buffer, 0, len);
|
fileOutputStream.write(buffer, 0, len);
|
||||||
|
soFarBytes += len;
|
||||||
|
if (builder.downloadInterface != null) {
|
||||||
|
if (!builder.downloadInterface.onDownloading(soFarBytes, totalBytes)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fileOutputStream.flush();
|
fileOutputStream.flush();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.getLogger("download").log(Level.FINE, "download error:"+url, e);
|
Log.getLogger("download").log(Level.FINE, "download error:" + builder.url, e);
|
||||||
|
} finally {
|
||||||
|
if (builder.downloadInterface != null) {
|
||||||
|
builder.downloadInterface.onDownload(target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Builder {
|
||||||
|
String url;
|
||||||
|
String path;
|
||||||
|
String fileName;
|
||||||
|
DownloadInterface downloadInterface;
|
||||||
|
String cookie;
|
||||||
|
|
||||||
|
public Builder setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setPath(String path) {
|
||||||
|
this.path = path;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setFileName(String fileName) {
|
||||||
|
this.fileName = fileName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setDownloadInterface(DownloadInterface downloadInterface) {
|
||||||
|
this.downloadInterface = downloadInterface;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setCookie(String cookie) {
|
||||||
|
this.cookie = cookie;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.yutou.common.okhttp;
|
package com.yutou.common.okhttp;
|
||||||
|
|
||||||
|
import com.yutou.common.utils.ConfigTools;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -38,7 +39,7 @@ public class ParamsContext {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
headerMap.remove("tableName");
|
headerMap.remove("tableName");
|
||||||
headerMap.put("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36");
|
headerMap.put("User-Agent", ConfigTools.getUserAgent());
|
||||||
return iRequestParam.getRequest(headerMap, map, request);
|
return iRequestParam.getRequest(headerMap, map, request);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,6 +2,7 @@ package com.yutou.common.utils;
|
|||||||
|
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
@ -93,4 +94,16 @@ public class ConfigTools {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getUserAgent() {
|
||||||
|
String ua=load(CONFIG,"userAgent",String.class);
|
||||||
|
if(!StringUtils.hasText(ua)){
|
||||||
|
ua="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36";
|
||||||
|
}
|
||||||
|
return ua;
|
||||||
|
}
|
||||||
|
public static boolean saveUserAgent(String ua) {
|
||||||
|
return save(CONFIG,"userAgent",ua);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
128
src/main/java/com/yutou/common/utils/FFmpegUtils.java
Normal file
128
src/main/java/com/yutou/common/utils/FFmpegUtils.java
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package com.yutou.common.utils;
|
||||||
|
|
||||||
|
import com.yutou.bilibili.interfaces.DownloadInterface;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class FFmpegUtils {
|
||||||
|
|
||||||
|
private String command;
|
||||||
|
InputStream inputStream;
|
||||||
|
OutputStream outputStream;
|
||||||
|
|
||||||
|
private FFmpegUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private final Map<String, String> params = new LinkedHashMap<>();
|
||||||
|
private final Map<String, String> paramsNotSymbol = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
public Builder withParam(String key, String value) {
|
||||||
|
if (value != null) {
|
||||||
|
params.put(key, value);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder withNotSymbolParam(String key, String value) {
|
||||||
|
if (value != null) {
|
||||||
|
paramsNotSymbol.put(key, value);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCommand(String ffmpegPath, String inputUrl, String outputPath) {
|
||||||
|
StringBuilder command = new StringBuilder(ffmpegPath);
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||||
|
command.append(" ").append(entry.getKey());
|
||||||
|
if (entry.getValue() != null && !entry.getValue().isEmpty()) {
|
||||||
|
command.append(" ").append("\"").append(entry.getValue()).append("\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command.append(" -i ").append("\"").append(inputUrl).append("\"");
|
||||||
|
for (Map.Entry<String, String> entry : paramsNotSymbol.entrySet()) {
|
||||||
|
command.append(" ").append(entry.getKey());
|
||||||
|
if (entry.getValue() != null && !entry.getValue().isEmpty()) {
|
||||||
|
command.append(" ").append(entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
command.append(" ").append("\"").append(outputPath).append("\"");
|
||||||
|
|
||||||
|
return command.toString();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public FFmpegUtils build(String ffmpegPath, String inputUrl, String outputPath) {
|
||||||
|
FFmpegUtils ffmpeg = new FFmpegUtils();
|
||||||
|
ffmpeg.command = getCommand(ffmpegPath, inputUrl, outputPath);
|
||||||
|
return ffmpeg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start(DownloadInterface downloadInterface) {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
Process process = AppTools.exec(command);
|
||||||
|
inputStream = process.getErrorStream();
|
||||||
|
outputStream = process.getOutputStream();
|
||||||
|
byte[] bytes = new byte[2048];
|
||||||
|
if (downloadInterface != null) {
|
||||||
|
downloadInterface.onDownloadStart();
|
||||||
|
}
|
||||||
|
while (inputStream.read(bytes) > -1) {
|
||||||
|
if (downloadInterface != null) {
|
||||||
|
downloadInterface.onDownloading(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//获取视频时长:ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4
|
||||||
|
//获取到结果:5372.432000
|
||||||
|
if (downloadInterface != null) {
|
||||||
|
System.out.println("触发下载完成");
|
||||||
|
downloadInterface.onDownload(null);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
try {
|
||||||
|
outputStream.write("q".getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取视频时长
|
||||||
|
* @param video 视频文件
|
||||||
|
* @return 毫秒
|
||||||
|
*/
|
||||||
|
public static long getVideoTime(File video) {
|
||||||
|
String ffprobe = ConfigTools.load(ConfigTools.CONFIG, "ffprobe", String.class);
|
||||||
|
String exec = ffprobe + " -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 \"" + video.getAbsolutePath() + "\"";
|
||||||
|
try {
|
||||||
|
System.out.println(exec);
|
||||||
|
Process process = AppTools.exec(exec);
|
||||||
|
InputStream inputStream = process.getInputStream();
|
||||||
|
byte[] bytes = new byte[2048];
|
||||||
|
String data;
|
||||||
|
inputStream.read(bytes);
|
||||||
|
data = new String(bytes);
|
||||||
|
inputStream.close();
|
||||||
|
process.destroy();
|
||||||
|
process.exitValue();
|
||||||
|
return (long) (Double.parseDouble(data)*1000);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user