diff --git a/pom.xml b/pom.xml index 095a3ed..6c20c7c 100644 --- a/pom.xml +++ b/pom.xml @@ -119,6 +119,12 @@ retrofit 2.11.0 + + + org.apache.logging.log4j + log4j-core + 2.24.1 + diff --git a/src/main/java/com/yutou/biliapi/Main.java b/src/main/java/com/yutou/biliapi/Main.java index d9a69d3..46c1186 100644 --- a/src/main/java/com/yutou/biliapi/Main.java +++ b/src/main/java/com/yutou/biliapi/Main.java @@ -48,22 +48,22 @@ public class Main { outputStream.write(new byte[]{0, 0, 1, 68, 0, 16, 0, 1, 0, 0, 0, 7, 0, 0, 0, 1}); outputStream.write(json.toJSONString().getBytes(StandardCharsets.UTF_8)); outputStream.flush(); - System.out.println("\n\n\n"); + Log.i("\n\n\n"); String str = DatatypeConverter.printHexBinary(outputStream.toByteArray()); for (int i = 0; i < str.length(); i = i + 4) { if (i % 32 == 0 && i != 0) { - System.out.println(); + Log.i(); } if (str.length() - i > 4) { System.out.print(str.substring(i, i + 4) + " "); } else { - System.out.println(str.substring(i)); + Log.i(str.substring(i)); } } - System.out.println("\n\n\n"); + Log.i("\n\n\n"); } catch (Exception e) { - e.printStackTrace(); + Log.e(e); } } @@ -97,27 +97,27 @@ public class Main { // WebSocketManager.getInstance().addRoom(config); // // /*String url = codec.getUrlInfo().get(0).getHost() + codec.getBaseUrl() + codec.getUrlInfo().get(0).getExtra(); -// System.out.println("下载url = " + url); +// Log.i("下载url = " + url); // api.downloadLive(url).enqueue(new FileCallback<>(response) { // @Override // public void onStart(LiveRoomPlayInfo bean) { -// System.out.println("开始下载"); +// Log.i("开始下载"); // } // // @Override // public boolean onDownload(Headers headers, LiveRoomPlayInfo bean, long len, long total) { -// System.out.println("下载中:"+len+"|"+total); +// Log.i("下载中:"+len+"|"+total); // return true; // } // // @Override // public void onFinish(LiveRoomPlayInfo bean) { -// System.out.println("下载结束"); +// Log.i("下载结束"); // } // // @Override // public void onFailure(LiveRoomPlayInfo bean, Throwable throwable) { -// System.out.println("下载失败"); +// Log.i("下载失败"); // } // });*/ // @@ -141,7 +141,7 @@ public class Main { BiliLoginNetApiManager.getInstance().login(new HttpCallback() { @Override public void onResponse(Headers headers, int code, String status, LoginCookieDatabaseBean response, String rawResponse) { - System.out.println("二维码地址: "+rawResponse); + Log.i("二维码地址: "+rawResponse); if (code == BiliLoginNetApiManager.LOGIN_SUCCESS) { Response> execute = null; try { diff --git a/src/main/java/com/yutou/biliapi/bean/live/database/LiveConfigDatabaseBean.java b/src/main/java/com/yutou/biliapi/bean/live/database/LiveConfigDatabaseBean.java index 987ef65..058a268 100644 --- a/src/main/java/com/yutou/biliapi/bean/live/database/LiveConfigDatabaseBean.java +++ b/src/main/java/com/yutou/biliapi/bean/live/database/LiveConfigDatabaseBean.java @@ -1,14 +1,19 @@ package com.yutou.biliapi.bean.live.database; import com.alibaba.fastjson2.annotation.JSONField; +import com.alibaba.fastjson2.util.DateUtils; import com.yutou.common.databases.AbsDatabasesBean; import lombok.Data; import lombok.EqualsAndHashCode; +import java.io.File; import java.math.BigInteger; import java.util.Calendar; +import java.util.Date; import java.util.List; +import static com.alibaba.fastjson2.util.DateUtils.DateTimeFormatPattern.DATE_FORMAT_10_DASH; + @EqualsAndHashCode(callSuper = true) @Data public class LiveConfigDatabaseBean extends AbsDatabasesBean { @@ -27,52 +32,55 @@ public class LiveConfigDatabaseBean extends AbsDatabasesBean { @JSONField(name = "keyword") private List keywordList; @JSONField(name = "recordPath") - private String recordPath="live"; + private String recordPath = "live"; @JSONField(name = "recordUid") private String recordUid; @JSONField(name = "recordLiveModel") private int recordLiveModel;//0 - ffmpeg 1 - java @JSONField(name = "recordDanmuDate") - private String recordDanmuDate="* * *";// * * * 分 时 星期 | 周日是1 + private String recordDanmuDate = "* * *";// * * * 分 时 星期 | 周日是1 @JSONField(name = "recordLiveDate") - private String recordLiveDate="* * *";// * * * 分 时 星期 | 周日是1 + private String recordLiveDate = "* * *";// * * * 分 时 星期 | 周日是1 public LiveConfigDatabaseBean() { - super("live_config",System.currentTimeMillis()); + super("live_config", System.currentTimeMillis()); } - public boolean checkRecordDanmuTime(){ + + public boolean checkRecordDanmuTime() { return checkRecordTime(recordDanmuDate); } - public boolean checkRecordLiveTime(){ + + public boolean checkRecordLiveTime() { return checkRecordTime(recordLiveDate); } - private boolean checkRecordTime(String recordDate){ + + private boolean checkRecordTime(String recordDate) { int _length = recordDate.length(); - boolean isFullDate=(_length-recordDate.replace("*","").length())==3; - if(isFullDate){ + boolean isFullDate = (_length - recordDate.replace("*", "").length()) == 3; + if (isFullDate) { return true; } - String[] split=recordDate.split(" "); - String minute=split[0]; - String hour=split[1]; - String day=split[2]; - boolean isFullMinute= "*".equals(minute); - boolean isFullHour= "*".equals(hour); - boolean isFullDay= "*".equals(day); - Calendar today=Calendar.getInstance(); - if(!isFullDay){ - if(today.get(Calendar.DAY_OF_WEEK)!=Integer.parseInt(day)){ + String[] split = recordDate.split(" "); + String minute = split[0]; + String hour = split[1]; + String day = split[2]; + boolean isFullMinute = "*".equals(minute); + boolean isFullHour = "*".equals(hour); + boolean isFullDay = "*".equals(day); + Calendar today = Calendar.getInstance(); + if (!isFullDay) { + if (today.get(Calendar.DAY_OF_WEEK) != Integer.parseInt(day)) { return false; } } - if(!isFullHour) { + if (!isFullHour) { if (today.get(Calendar.HOUR_OF_DAY) != Integer.parseInt(hour)) { return false; } } - if(!isFullMinute){ - if(today.get(Calendar.MINUTE)!=Integer.parseInt(minute)) { + if (!isFullMinute) { + if (today.get(Calendar.MINUTE) != Integer.parseInt(minute)) { return false; } } diff --git a/src/main/java/com/yutou/biliapi/bean/live/database/LiveInfoDatabaseBean.java b/src/main/java/com/yutou/biliapi/bean/live/database/LiveInfoDatabaseBean.java index d162af1..711c171 100644 --- a/src/main/java/com/yutou/biliapi/bean/live/database/LiveInfoDatabaseBean.java +++ b/src/main/java/com/yutou/biliapi/bean/live/database/LiveInfoDatabaseBean.java @@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.annotation.JSONField; import com.yutou.biliapi.bean.live.LiveRoomInfo; import com.yutou.common.databases.AbsDatabasesBean; +import com.yutou.common.utils.Log; import lombok.Data; import lombok.EqualsAndHashCode; @@ -49,7 +50,7 @@ public class LiveInfoDatabaseBean extends AbsDatabasesBean { public static void main(String[] args) { List list = new LiveInfoDatabaseBean().getFields(); for (Field field : list) { - System.out.println(field.getName()); + Log.i(field.getName()); } diff --git a/src/main/java/com/yutou/biliapi/bean/websocket/WebSocketBody.java b/src/main/java/com/yutou/biliapi/bean/websocket/WebSocketBody.java index e19028f..9e2bc22 100644 --- a/src/main/java/com/yutou/biliapi/bean/websocket/WebSocketBody.java +++ b/src/main/java/com/yutou/biliapi/bean/websocket/WebSocketBody.java @@ -1,6 +1,7 @@ package com.yutou.biliapi.bean.websocket; import com.alibaba.fastjson2.JSONObject; +import com.yutou.common.utils.Log; import lombok.Data; import java.util.ArrayList; @@ -27,7 +28,7 @@ public class WebSocketBody { try { bodyList.add(JSONObject.parseObject(new String(data))); } catch (Exception e) { - System.out.println(header + "|" + new String(data)); + Log.i(header + "|" + new String(data)); } addBody(bytes, offset + header.dataSize); } diff --git a/src/main/java/com/yutou/biliapi/bean/websocket/live/WSDmInteraction.java b/src/main/java/com/yutou/biliapi/bean/websocket/live/WSDmInteraction.java index 548edae..1a8dbb9 100644 --- a/src/main/java/com/yutou/biliapi/bean/websocket/live/WSDmInteraction.java +++ b/src/main/java/com/yutou/biliapi/bean/websocket/live/WSDmInteraction.java @@ -1,6 +1,7 @@ package com.yutou.biliapi.bean.websocket.live; import com.alibaba.fastjson2.JSONObject; +import com.yutou.common.utils.Log; import lombok.Data; import lombok.EqualsAndHashCode; @@ -22,7 +23,7 @@ public class WSDmInteraction extends WSData{ public static void main(String[] args) { JSONObject json=JSONObject.parseObject("{\"cmd\":\"DM_INTERACTION\",\"data\":{\"data\":\"{\\\"fade_duration\\\":10000,\\\"cnt\\\":5,\\\"card_appear_interval\\\":0,\\\"suffix_text\\\":\\\"人正在点赞\\\",\\\"reset_cnt\\\":1,\\\"display_flag\\\":1}\",\"dmscore\":36,\"id\":53793047788032,\"status\":4,\"type\":106}}"); WSDmInteraction wsDmInteraction=new WSDmInteraction(json); - System.out.println(wsDmInteraction); + Log.i(wsDmInteraction); } public WSDmInteraction(JSONObject json) { super(json); diff --git a/src/main/java/com/yutou/biliapi/bean/websocket/live/WSGuardBuy.java b/src/main/java/com/yutou/biliapi/bean/websocket/live/WSGuardBuy.java index cb55410..0a7e179 100644 --- a/src/main/java/com/yutou/biliapi/bean/websocket/live/WSGuardBuy.java +++ b/src/main/java/com/yutou/biliapi/bean/websocket/live/WSGuardBuy.java @@ -1,6 +1,7 @@ package com.yutou.biliapi.bean.websocket.live; import com.alibaba.fastjson2.JSONObject; +import com.yutou.common.utils.Log; import lombok.Data; import lombok.EqualsAndHashCode; @@ -24,7 +25,7 @@ public class WSGuardBuy extends WSData{ public static void main(String[] args) { JSONObject json=JSONObject.parseObject("{\"cmd\":\"GUARD_BUY\",\"data\":{\"uid\":5427372,\"username\":\"李湜渰\",\"guard_level\":3,\"num\":1,\"price\":198000,\"gift_id\":10003,\"gift_name\":\"舰长\",\"start_time\":1724985039,\"end_time\":1724985039}}"); WSGuardBuy wsguardBuy = new WSGuardBuy(json); - System.out.println(wsguardBuy); + Log.i(wsguardBuy); } public WSGuardBuy(JSONObject json) { super(json); diff --git a/src/main/java/com/yutou/biliapi/bean/websocket/live/WSSuperChatMessage.java b/src/main/java/com/yutou/biliapi/bean/websocket/live/WSSuperChatMessage.java index 62d09e3..afd5808 100644 --- a/src/main/java/com/yutou/biliapi/bean/websocket/live/WSSuperChatMessage.java +++ b/src/main/java/com/yutou/biliapi/bean/websocket/live/WSSuperChatMessage.java @@ -1,6 +1,7 @@ package com.yutou.biliapi.bean.websocket.live; import com.alibaba.fastjson2.JSONObject; +import com.yutou.common.utils.Log; import lombok.Data; import lombok.EqualsAndHashCode; @@ -25,7 +26,7 @@ public class WSSuperChatMessage extends WSData{ public static void main(String[] args) { JSONObject json=JSONObject.parseObject("{\"cmd\":\"SUPER_CHAT_MESSAGE\",\"data\":{\"background_bottom_color\":\"#2A60B2\",\"background_color\":\"#EDF5FF\",\"background_color_end\":\"#405D85\",\"background_color_start\":\"#3171D2\",\"background_icon\":\"\",\"background_image\":\"\",\"background_price_color\":\"#7497CD\",\"color_point\":0.7,\"dmscore\":616,\"end_time\":1724997230,\"gift\":{\"gift_id\":12000,\"gift_name\":\"醒目留言\",\"num\":1},\"group_medal\":{\"is_lighted\":0,\"medal_id\":0,\"name\":\"\"},\"id\":10427329,\"is_mystery\":false,\"is_ranked\":0,\"is_send_audit\":1,\"medal_info\":{\"anchor_roomid\":81004,\"anchor_uname\":\"艾尔莎_Channel\",\"guard_level\":0,\"icon_id\":0,\"is_lighted\":1,\"medal_color\":\"#1a544b\",\"medal_color_border\":1725515,\"medal_color_end\":5414290,\"medal_color_start\":1725515,\"medal_level\":21,\"medal_name\":\"艾薯条\",\"special\":\"\",\"target_id\":1521415},\"message\":\"莎莎,想安利你个植物大战僵尸的改版叫植物大战僵尸:肉鸽,具体情况私信你了,辛苦了\",\"message_font_color\":\"#A3F6FF\",\"message_trans\":\"サーシャ、あなたのPlantsvs.Zombiesの改版をPlantsvs.Zombiesと言いたい:肉鳩、具体的な状況は私的にあなたを信じて、お疲れ様でした\",\"price\":30,\"rate\":1000,\"start_time\":1724997170,\"time\":60,\"token\":\"9925C118\",\"trans_mark\":0,\"ts\":1724997170,\"uid\":100002175,\"uinfo\":{\"base\":{\"face\":\"https://i1.hdslb.com/bfs/face/b5ec3b1f7025b5546225ae0f36941d55ddef405b.jpg\",\"is_mystery\":false,\"name\":\"中吴同学\",\"name_color\":0,\"name_color_str\":\"#666666\",\"official_info\":{\"desc\":\"\",\"role\":0,\"title\":\"\",\"type\":-1},\"origin_info\":{\"face\":\"https://i1.hdslb.com/bfs/face/b5ec3b1f7025b5546225ae0f36941d55ddef405b.jpg\",\"name\":\"中吴同学\"}},\"guard\":{\"expired_str\":\"\",\"level\":0},\"medal\":{\"color\":1725515,\"color_border\":1725515,\"color_end\":5414290,\"color_start\":1725515,\"guard_icon\":\"\",\"guard_level\":0,\"honor_icon\":\"\",\"id\":0,\"is_light\":1,\"level\":21,\"name\":\"艾薯条\",\"ruid\":1521415,\"score\":50001980,\"typ\":0,\"user_receive_count\":0,\"v2_medal_color_border\":\"#5FC7F4FF\",\"v2_medal_color_end\":\"#43B3E3CC\",\"v2_medal_color_level\":\"#00308C99\",\"v2_medal_color_start\":\"#43B3E3CC\",\"v2_medal_color_text\":\"#FFFFFFFF\"},\"title\":{\"old_title_css_id\":\"\",\"title_css_id\":\"\"},\"uid\":100002175},\"user_info\":{\"face\":\"https://i1.hdslb.com/bfs/face/b5ec3b1f7025b5546225ae0f36941d55ddef405b.jpg\",\"face_frame\":\"\",\"guard_level\":0,\"is_main_vip\":1,\"is_svip\":0,\"is_vip\":0,\"level_color\":\"#5896de\",\"manager\":0,\"name_color\":\"#666666\",\"title\":\"\",\"uname\":\"中吴同学\",\"user_level\":25}},\"is_report\":true,\"msg_id\":\"19106780029655552:1000:1000\",\"p_is_ack\":true,\"p_msg_type\":1,\"send_time\":1724997170767}"); WSSuperChatMessage message=new WSSuperChatMessage(json); - System.out.println(message); + Log.i(message); } public WSSuperChatMessage(JSONObject json) { super(json); diff --git a/src/main/java/com/yutou/biliapi/databases/BiliLiveDatabase.java b/src/main/java/com/yutou/biliapi/databases/BiliLiveDatabase.java index 3efe787..5f48131 100644 --- a/src/main/java/com/yutou/biliapi/databases/BiliLiveDatabase.java +++ b/src/main/java/com/yutou/biliapi/databases/BiliLiveDatabase.java @@ -8,6 +8,7 @@ import com.yutou.bilibili.Tools.DateFormatUtils; import com.yutou.common.databases.AbsDatabasesBean; import com.yutou.common.databases.SQLiteManager; import com.yutou.common.okhttp.HttpDownloadUtils; +import com.yutou.common.utils.Log; import org.apache.poi.ss.usermodel.DataFormat; import java.io.File; @@ -26,13 +27,12 @@ public class BiliLiveDatabase extends SQLiteManager { public BiliLiveDatabase(LiveRoomConfig roomConfig, String path) { this.config = roomConfig; rootPath = new File(path).getParentFile(); - fileName =path; + fileName = path + File.separator + "live.db"; init(); } public BiliLiveDatabase(LiveRoomConfig roomConfig) { - String time = DateUtils.format(new Date().getTime(), DATE_FORMAT_10_DASH); - rootPath = new File(roomConfig.getRootPath() + File.separator + roomConfig.getAnchorName() + File.separator + time + File.separator + roomConfig.getRoomInfo().getTitle()); + rootPath = new File(roomConfig.getRootPath() + File.separator + roomConfig.getAnchorName()); config = roomConfig; if (!rootPath.exists()) { rootPath.mkdirs(); @@ -44,7 +44,7 @@ public class BiliLiveDatabase extends SQLiteManager { @Override public void init() { super.init(); - if(config.getRoomInfo()!=null) { + if (config.getRoomInfo() != null) { HttpDownloadUtils.download(new HttpDownloadUtils.Builder().setUrl(config.getRoomInfo().getUserCover()) .setPath(rootPath.getAbsolutePath()) .setFileName("poster.jpg")); @@ -95,7 +95,7 @@ public class BiliLiveDatabase extends SQLiteManager { } public void addSource(WSData bean) { - System.out.println("BiliLiveDatabase.addSource"); + Log.i("BiliLiveDatabase.addSource"); add(new LiveSourceDatabaseBean(bean)); addData(bean); } @@ -137,7 +137,7 @@ public class BiliLiveDatabase extends SQLiteManager { public static void main(String[] args) { /* List list = getInstance().get(-1, 1727515148800L, LiveDanmuDatabaseBean.class); for (LiveDanmuDatabaseBean bean : list) { - System.out.println(bean.getSql_time() + "|" + bean); + Log.i(bean.getSql_time() + "|" + bean); }*/ } } diff --git a/src/main/java/com/yutou/biliapi/net/BiliCookieManager.java b/src/main/java/com/yutou/biliapi/net/BiliCookieManager.java index d0a98ab..28130ad 100644 --- a/src/main/java/com/yutou/biliapi/net/BiliCookieManager.java +++ b/src/main/java/com/yutou/biliapi/net/BiliCookieManager.java @@ -3,6 +3,7 @@ package com.yutou.biliapi.net; import com.yutou.biliapi.bean.login.CheckCookieBean; import com.yutou.common.inter.IHttpApiCheckCallback; import com.yutou.common.okhttp.HttpCallback; +import com.yutou.common.utils.Log; import com.yutou.common.utils.RSAUtils; import okhttp3.Headers; @@ -82,7 +83,7 @@ public class BiliCookieManager { // Convert the encrypted bytes to a Base64-encoded string String encrypted = new BigInteger(1, encryptedBytes).toString(16); }catch (Exception e){ - e.printStackTrace(); + Log.e(e); } } diff --git a/src/main/java/com/yutou/biliapi/net/BiliLoginNetApiManager.java b/src/main/java/com/yutou/biliapi/net/BiliLoginNetApiManager.java index 749b748..fe6f13c 100644 --- a/src/main/java/com/yutou/biliapi/net/BiliLoginNetApiManager.java +++ b/src/main/java/com/yutou/biliapi/net/BiliLoginNetApiManager.java @@ -90,7 +90,7 @@ public class BiliLoginNetApiManager extends BaseApi { for (String cookie : list) { String[] split = cookie.split(";"); for (String string : split) { - if (!ck.containsKey(string) && !StringUtils.isEmpty(string) && string.contains("=")) { + if (!ck.containsKey(string) && StringUtils.hasText(string) && string.contains("=")) { String key = string.split("=")[0].trim(); String value = string.split("=")[1].trim(); if (key.contains("Expires")) { diff --git a/src/main/java/com/yutou/biliapi/net/WebSocketManager.java b/src/main/java/com/yutou/biliapi/net/WebSocketManager.java index baa2e4d..d07025d 100644 --- a/src/main/java/com/yutou/biliapi/net/WebSocketManager.java +++ b/src/main/java/com/yutou/biliapi/net/WebSocketManager.java @@ -17,8 +17,10 @@ import com.yutou.biliapi.databases.BiliBiliLoginDatabase; import com.yutou.biliapi.databases.BiliLiveDatabase; import com.yutou.biliapi.utils.BiliUserUtils; import com.yutou.biliapi.utils.BytesUtils; +import com.yutou.bilibili.Tools.Tools; import com.yutou.common.okhttp.HttpBody; import com.yutou.common.okhttp.HttpCallback; +import com.yutou.common.utils.ConfigTools; import com.yutou.common.utils.Log; import okhttp3.Headers; import org.java_websocket.client.WebSocketClient; @@ -77,12 +79,12 @@ public class WebSocketManager { } DanmuTask task = new DanmuTask(roomConfig); roomMap.put(roomConfig, task); - System.out.println("添加websocket任务"); + Log.i("添加websocket任务"); executor.execute(task); } public void stopRoom(String roomId, boolean isUser) { - LiveRoomConfig roomConfig=new LiveRoomConfig(); + LiveRoomConfig roomConfig = new LiveRoomConfig(); roomConfig.setRoomId(new BigInteger(roomId)); if (checkRoom(roomConfig)) { roomMap.get(roomConfig).close(); @@ -118,7 +120,7 @@ public class WebSocketManager { @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); + LiveDanmuInfo.Host host = response.getHostList().get(new Random().nextInt(response.getHostList().size())); String url = "wss://" + host.getHost() + ":" + host.getWssPort() + "/sub"; // url="ws://127.0.0.1:8765"; try { @@ -132,7 +134,7 @@ public class WebSocketManager { @Override public void onFailure(Throwable throwable) { - throwable.printStackTrace(); + Log.e(throwable); } }); } @@ -150,46 +152,48 @@ public class WebSocketManager { public WebSocketClientTh(URI serverUri, LiveRoomConfig roomId) { super(serverUri); - System.out.println("WebSocketClientTh.WebSocketClientTh : " + serverUri); + Log.i("WebSocketClientTh.WebSocketClientTh : " + serverUri); this.roomConfig = roomId; liveDatabase = new BiliLiveDatabase(roomConfig); Brotli4jLoader.ensureAvailability(); heartbeatTask = new HeartbeatTask(); + addHeader("User-Agent", ConfigTools.getUserAgent()); connect(); } + @Override public void onOpen(ServerHandshake serverHandshake) { heartbeatTask.setSocket(this); heartbeatTask.sendInitAuthData(); new Timer().schedule(heartbeatTask, 1000, 30000); - System.out.println("WebSocketClientTh.onOpen"); + Log.i("WebSocketClientTh.onOpen"); } @Override public void onMessage(String s) { - System.out.println("s = " + s); + Log.i("s = " + s); } @Override public void onMessage(ByteBuffer bytes) { - System.out.println("WebSocketClientTh.onMessage: " + roomConfig.getAnchorName()); + Log.i("WebSocketClientTh.onMessage: " + roomConfig.getAnchorName()); super.onMessage(bytes); decompress(bytes.array()); } @Override public void onClose(int i, String s, boolean b) { - System.out.println("WebSocketClientTh.onClose"); - System.out.println("i = " + i + ", s = " + s + ", b = " + b); + Log.i("WebSocketClientTh.onClose"); + Log.i("i = " + i + ", s = " + s + ", b = " + b); WebSocketManager.getInstance().roomMap.remove(roomConfig); heartbeatTask.cancel(); } @Override public void onError(Exception e) { - System.out.println("WebSocketClientTh.onError"); - e.printStackTrace(); + Log.i("WebSocketClientTh.onError"); + Log.e(e); WebSocketManager.getInstance().roomMap.remove(roomConfig); heartbeatTask.cancel(); } @@ -203,7 +207,7 @@ public class WebSocketManager { byte[] bytes = new byte[data.length - 16]; WebSocketHeader header = new WebSocketHeader(data); System.arraycopy(data, header.getHeaderSize(), bytes, 0, data.length - header.getHeaderSize()); - // System.out.println("数据大小:" + header.getDataSize() + " 协议:" + header.getAgree() + " 头部大小:" + header.getHeaderSize() + " 命令:" + header.getCmdData()); + // Log.i("数据大小:" + header.getDataSize() + " 协议:" + header.getAgree() + " 头部大小:" + header.getHeaderSize() + " 命令:" + header.getCmdData()); switch (header.getAgree()) { case 0: case 1: @@ -231,13 +235,13 @@ public class WebSocketManager { liveDatabase.addSource(parse); // Log.i("解压:" + parse); } - // System.out.println(); - // System.out.println(); + // Log.i(); + // Log.i(); } else { Log.e(new RuntimeException("解压失败")); } } catch (Exception e) { - e.printStackTrace(); + Log.e(e); } } @@ -259,7 +263,7 @@ public class WebSocketManager { outputStream.flush(); socket.send(outputStream.toByteArray()); } catch (Exception e) { - e.printStackTrace(); + Log.e(e); } } @@ -282,18 +286,18 @@ public class WebSocketManager { json.put("key", roomConfig.getLiveInfo().getToken()); byte[] bytes = {0, 16, 0, 1, 0, 0, 0, 7, 0, 0, 0, 1}; ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - System.out.println("bytes.length = " + bytes.length); + Log.i("bytes.length = " + bytes.length); Log.i(json); outputStream.write(BytesUtils.toLH(json.toString().length() + 16)); outputStream.write(bytes); outputStream.write(json.toJSONString().getBytes(StandardCharsets.UTF_8)); outputStream.flush(); // BytesUtils.printHex(outputStream.toByteArray()); - System.out.println(socket.isOpen()); + Log.i(socket.isOpen()); socket.send(outputStream.toByteArray()); } catch (Exception e) { - e.printStackTrace(); + Log.e(e); } } diff --git a/src/main/java/com/yutou/biliapi/utils/BytesUtils.java b/src/main/java/com/yutou/biliapi/utils/BytesUtils.java index c5ea5d0..125c75b 100644 --- a/src/main/java/com/yutou/biliapi/utils/BytesUtils.java +++ b/src/main/java/com/yutou/biliapi/utils/BytesUtils.java @@ -15,20 +15,20 @@ public class BytesUtils { } public static void printHex(byte[] byteArray) { - System.out.println("\n\n\n"); + Log.i("\n\n\n"); String str = DatatypeConverter.printHexBinary(byteArray); Log.i(str); for (int i = 0; i < str.length(); i = i + 4) { if (i % 32 == 0 && i != 0) { - System.out.println(); + Log.i(); } if (str.length() - i > 4) { System.out.print(str.substring(i, i + 4) + " "); } else { - System.out.println(str.substring(i)); + Log.i(str.substring(i)); } } - System.out.println("\n\n\n"); + Log.i("\n\n\n"); } public static byte[] toLH(int n) { byte[] b = new byte[4]; diff --git a/src/main/java/com/yutou/bilibili/Controllers/TestControllers.java b/src/main/java/com/yutou/bilibili/Controllers/TestControllers.java index 443a95c..60791ce 100644 --- a/src/main/java/com/yutou/bilibili/Controllers/TestControllers.java +++ b/src/main/java/com/yutou/bilibili/Controllers/TestControllers.java @@ -1,5 +1,6 @@ package com.yutou.bilibili.Controllers; +import com.yutou.common.utils.FFmpegUtils; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @@ -9,6 +10,13 @@ public class TestControllers { @RequestMapping("/root/test") @ResponseBody public String test(){ + FFmpegUtils.killAll(); return "hello world"; } + @RequestMapping("/root/kill") + @ResponseBody + public String kill(String roomId){ + FFmpegUtils.kill(roomId); + return FFmpegUtils.check(roomId)+""; + } } diff --git a/src/main/java/com/yutou/bilibili/Controllers/VideoFileController.java b/src/main/java/com/yutou/bilibili/Controllers/VideoFileController.java index 21d1b21..fec6781 100644 --- a/src/main/java/com/yutou/bilibili/Controllers/VideoFileController.java +++ b/src/main/java/com/yutou/bilibili/Controllers/VideoFileController.java @@ -6,6 +6,7 @@ import com.yutou.bilibili.Tools.Tools; import com.yutou.bilibili.datas.ResultData; import com.yutou.bilibili.datas.VideoFilePath; import com.yutou.bilibili.services.LiveVideoService; +import com.yutou.common.utils.Log; import jakarta.annotation.Resource; import org.springframework.core.io.FileSystemResource; import org.springframework.http.ResponseEntity; @@ -39,7 +40,7 @@ public class VideoFileController { @ResponseBody public ResponseEntity getFile(@PathVariable String base64) { File file = FileServerUtils.toFile(base64); - System.out.println(file.getAbsolutePath()); + Log.i(file.getAbsolutePath()); return Tools.getFile(file); } diff --git a/src/main/java/com/yutou/bilibili/Tools/AESTools.java b/src/main/java/com/yutou/bilibili/Tools/AESTools.java index 0a6b5b3..40837ac 100644 --- a/src/main/java/com/yutou/bilibili/Tools/AESTools.java +++ b/src/main/java/com/yutou/bilibili/Tools/AESTools.java @@ -1,5 +1,7 @@ package com.yutou.bilibili.Tools; +import com.yutou.common.utils.Log; + import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.spec.SecretKeySpec; @@ -24,7 +26,7 @@ public class AESTools { byte[] bytes=cipher.doFinal(value.getBytes(StandardCharsets.UTF_8)); return new String(Base64.getEncoder().encode(bytes)); } catch (Exception e) { - e.printStackTrace(); + Log.e(e); } return null; } @@ -44,7 +46,7 @@ public class AESTools { byte[] bytes=cipher.doFinal(encodeBytes); return new String(bytes); } catch (Exception e) { - e.printStackTrace(); + Log.e(e); } return null; } diff --git a/src/main/java/com/yutou/bilibili/Tools/ApplicationClose.java b/src/main/java/com/yutou/bilibili/Tools/ApplicationClose.java index 9718536..269b85a 100644 --- a/src/main/java/com/yutou/bilibili/Tools/ApplicationClose.java +++ b/src/main/java/com/yutou/bilibili/Tools/ApplicationClose.java @@ -1,6 +1,7 @@ package com.yutou.bilibili.Tools; import com.yutou.bilibili.services.SystemService; +import com.yutou.common.utils.FFmpegUtils; import com.yutou.common.utils.Log; import jakarta.annotation.Resource; import org.springframework.context.ApplicationListener; @@ -15,5 +16,6 @@ public class ApplicationClose implements ApplicationListener public void onApplicationEvent(ContextClosedEvent contextClosedEvent) { Log.i("服务结束"); systemConfigService.stop(); + FFmpegUtils.killAll(); } } diff --git a/src/main/java/com/yutou/bilibili/Tools/AssTools.java b/src/main/java/com/yutou/bilibili/Tools/AssTools.java index 585053c..4577d33 100644 --- a/src/main/java/com/yutou/bilibili/Tools/AssTools.java +++ b/src/main/java/com/yutou/bilibili/Tools/AssTools.java @@ -1,6 +1,7 @@ package com.yutou.bilibili.Tools; import com.yutou.bilibili.datas.DanmuData; +import com.yutou.common.utils.Log; import java.io.File; import java.io.FileWriter; @@ -75,7 +76,7 @@ public class AssTools { * @return 存储结果 */ public boolean saveDanmu(String savePath) { - System.out.println("savePath = " + savePath); + Log.i("savePath = " + savePath); File file = new File(savePath); try { if (file.exists()) { @@ -97,7 +98,7 @@ public class AssTools { writer.close(); return true; } catch (IOException e) { - e.printStackTrace(); + Log.e(e); } return false; } diff --git a/src/main/java/com/yutou/bilibili/Tools/FFmpegUtils.java b/src/main/java/com/yutou/bilibili/Tools/FFmpegUtils.java index 76f8c4b..831f99f 100644 --- a/src/main/java/com/yutou/bilibili/Tools/FFmpegUtils.java +++ b/src/main/java/com/yutou/bilibili/Tools/FFmpegUtils.java @@ -42,7 +42,7 @@ public class FFmpegUtils { file.getAbsolutePath(), out.getAbsolutePath() + File.separator + file.getName()); Log.i(exec); - Process process= AppTools.exec(exec); + Process process= ProcessUtils.exec(exec); InputStream inputStream = process.getErrorStream(); byte[] bytes = new byte[1024]; while (inputStream.read(bytes) > -1) { diff --git a/src/main/java/com/yutou/bilibili/Tools/LiveInfoNfoTools.java b/src/main/java/com/yutou/bilibili/Tools/LiveInfoNfoTools.java new file mode 100644 index 0000000..69382ac --- /dev/null +++ b/src/main/java/com/yutou/bilibili/Tools/LiveInfoNfoTools.java @@ -0,0 +1,82 @@ +package com.yutou.bilibili.Tools; + +import com.alibaba.fastjson2.JSONObject; +import com.yutou.biliapi.bean.live.LiveRoomConfig; +import com.yutou.biliapi.bean.live.LiveRoomInfo; +import com.yutou.biliapi.bean.live.database.LiveVideoDatabaseBean; +import com.yutou.biliapi.databases.BiliLiveDatabase; +import org.w3c.dom.CDATASection; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Text; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.File; + +public class LiveInfoNfoTools { + public static void saveLiveInfoNfo(LiveRoomInfo info, String path,String xmlName) { + try { + LiveRoomConfig config = LiveRoomConfig.buildConfig(info.getRoomId().toString()); + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = null; + dBuilder = dbFactory.newDocumentBuilder(); + Document doc = dBuilder.newDocument(); + Element movie = doc.createElement("movie"); + Element plot = doc.createElement("plot"); + CDATASection section = doc.createCDATASection(info.getDescription()); + plot.appendChild(section); + movie.appendChild(plot); + + Element title = doc.createElement("title"); + title.setTextContent(info.getTitle()); + movie.appendChild(title); + + Element actor = doc.createElement("actor"); + Element name = doc.createElement("name"); + Element role = doc.createElement("role"); + name.setTextContent("UID:" + info.getUid().toString()); + role.setTextContent(config.getAnchorName()); + actor.appendChild(name); + actor.appendChild(role); + movie.appendChild(actor); + + Element year = doc.createElement("year"); + year.setTextContent(info.getLiveTime().split("-")[0]); + movie.appendChild(year); + Element aired = doc.createElement("aired"); + aired.setTextContent(info.getLiveTime().split(" ")[0]); + movie.appendChild(aired); + + movie.appendChild(createTag(doc,info.getParentAreaName())); + movie.appendChild(createTag(doc,info.getAreaName())); + + + + doc.appendChild(movie); + + + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + DOMSource source = new DOMSource(doc); + StreamResult result = new StreamResult(new File(path, xmlName)); + transformer.transform(source, result); + + } catch (Exception e) { + e.printStackTrace(); + } + } + private static Element createTag(Document doc,String tag){ + Element genre = doc.createElement("genre"); + genre.setTextContent(tag); + return genre; + } + + public static void main(String[] args) { + } +} diff --git a/src/main/java/com/yutou/bilibili/Tools/ProcessUtils.java b/src/main/java/com/yutou/bilibili/Tools/ProcessUtils.java new file mode 100644 index 0000000..6bef7be --- /dev/null +++ b/src/main/java/com/yutou/bilibili/Tools/ProcessUtils.java @@ -0,0 +1,71 @@ +package com.yutou.bilibili.Tools; + +import com.yutou.common.utils.AppTools; + +import java.io.BufferedReader; +import java.io.InputStreamReader; + + +public class ProcessUtils { + public static boolean isRuntimeSystemOfWindow(){ + return System.getProperty ("os.name").contains("Windows"); + } + public static void killProcess(Long pid) throws Exception { + if(pid==null) { + return; + } + if (isRuntimeSystemOfWindow()) { + // 对于 Windows 系统 + Process process = exec("taskkill /F /PID " + pid); + process.waitFor(); + } else { + // 对于 Linux 和 macOS 系统 + Process process = exec("kill -9 " + pid); + process.waitFor(); + } + + } + public static boolean isProcessRunning(Long pid) throws Exception { + if(pid==null) { + return false; + } + String command; + if (isRuntimeSystemOfWindow()) { + // Windows 操作系统 + command = "tasklist /FI \"PID eq " + pid + "\""; + } else { + // Unix/Linux 操作系统 + command = "ps -p " + pid; + } + + Process process = exec(command); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + if (line.contains(Long.toString(pid))) { + return true; + } + } + return false; + } + public static Process exec(String exec)throws Exception{ + if(isRuntimeSystemOfWindow()) { + return Runtime.getRuntime().exec(new String[]{ + "cmd", + "/c", + exec + } + ); + }else{ + return Runtime.getRuntime().exec(new String[]{ + "sh", + "-c", + exec + } + ); + } + } + + public static void main(String[] args) throws Exception { + } +} diff --git a/src/main/java/com/yutou/bilibili/services/LiveConfigService.java b/src/main/java/com/yutou/bilibili/services/LiveConfigService.java index 9c7414f..4324728 100644 --- a/src/main/java/com/yutou/bilibili/services/LiveConfigService.java +++ b/src/main/java/com/yutou/bilibili/services/LiveConfigService.java @@ -15,7 +15,7 @@ import java.util.List; @Service public class LiveConfigService { - BiliLiveConfigDatabase database=new BiliLiveConfigDatabase(); + BiliLiveConfigDatabase database = new BiliLiveConfigDatabase(); public LiveConfigDatabaseBean addConfig(String url, LiveConfigDatabaseBean bean) { if (!StringUtils.hasText(url)) { @@ -30,6 +30,7 @@ public class LiveConfigService { LiveRoomInfo body = BiliLiveNetApiManager.getInstance().getApi(null).getRoomInfo(String.valueOf(bean.getRoomId())).execute().body().getData(); MasterInfoBean infoBean = BiliLiveNetApiManager.getInstance().getApi(null).getMasterInfo(String.valueOf(body.getUid())).execute().body().getData(); bean.setAnchorUid(body.getUid()); + bean.setRoomId(body.getRoomId()); bean.setAnchorFace(infoBean.getInfo().getFace()); bean.setAnchorName(infoBean.getInfo().getUname()); database.setConfig(bean); diff --git a/src/main/java/com/yutou/bilibili/services/LiveDanmuService.java b/src/main/java/com/yutou/bilibili/services/LiveDanmuService.java index 778b477..c5f32b7 100644 --- a/src/main/java/com/yutou/bilibili/services/LiveDanmuService.java +++ b/src/main/java/com/yutou/bilibili/services/LiveDanmuService.java @@ -12,6 +12,7 @@ import com.yutou.bilibili.Tools.AssTools; import com.yutou.bilibili.Tools.DateFormatUtils; import com.yutou.bilibili.Tools.Tools; import com.yutou.common.utils.FFmpegUtils; +import com.yutou.common.utils.Log; import org.springframework.stereotype.Service; import java.io.File; @@ -34,9 +35,6 @@ public class LiveDanmuService { } - private BiliLiveDatabase getDatabase(String roomId, File file) { - return new BiliLiveDatabase(LiveRoomConfig.buildConfig(roomId), file.getParent() + File.separator + "live.db"); - } public List getDanmuFileList(String roomId) { BiliLiveConfigDatabase configDatabase=new BiliLiveConfigDatabase(); LiveConfigDatabaseBean bean = configDatabase.getConfig(new BigInteger(roomId)); @@ -47,12 +45,12 @@ public class LiveDanmuService { public void saveDanmuXML(LiveVideoDatabaseBean videoDatabaseBean, BiliLiveDatabase database) { File videoFile = new File(videoDatabaseBean.getPath()); long videoTime = FFmpegUtils.getVideoTime(videoFile) + videoDatabaseBean.getStartTime().getTime(); - System.out.println("开始时间:" + videoDatabaseBean.getStartTime().getTime()); - System.out.println("结束时间:" + videoTime); + Log.i("开始时间:" + videoDatabaseBean.getStartTime().getTime()); + Log.i("结束时间:" + videoTime); String startTime = DateFormatUtils.format(videoDatabaseBean.getStartTime()); String endTime = DateFormatUtils.format(videoTime); List danmus = database.getOfTime(startTime, endTime, LiveDanmuDatabaseBean.class); - System.out.println("弹幕数量:" + danmus.size()); + Log.i("弹幕数量:" + danmus.size()); AssTools assTools = new AssTools(videoFile.getName().replace(".flv", ""), videoDatabaseBean.getStartTime()); for (LiveDanmuDatabaseBean dm : danmus) { assTools.addDanmu(dm.createDanmuData()); @@ -74,7 +72,7 @@ public class LiveDanmuService { LiveDanmuService service = new LiveDanmuService(); List files = service.getDanmuFileList("22047448"); for (File file : files) { - System.out.println(file); + Log.i(file); } } diff --git a/src/main/java/com/yutou/bilibili/services/LiveVideoService.java b/src/main/java/com/yutou/bilibili/services/LiveVideoService.java index 07cdca9..c62d57b 100644 --- a/src/main/java/com/yutou/bilibili/services/LiveVideoService.java +++ b/src/main/java/com/yutou/bilibili/services/LiveVideoService.java @@ -21,6 +21,7 @@ import com.yutou.biliapi.net.BiliLiveNetApiManager; import com.yutou.biliapi.net.WebSocketManager; import com.yutou.bilibili.Tools.DateFormatUtils; import com.yutou.bilibili.Tools.FileServerUtils; +import com.yutou.bilibili.Tools.LiveInfoNfoTools; import com.yutou.bilibili.Tools.Tools; import com.yutou.bilibili.datas.VideoFilePath; import com.yutou.bilibili.interfaces.DownloadInterface; @@ -28,6 +29,7 @@ import com.yutou.common.okhttp.HttpCallback; import com.yutou.common.okhttp.HttpDownloadUtils; import com.yutou.common.utils.ConfigTools; import com.yutou.common.utils.FFmpegUtils; +import com.yutou.common.utils.Log; import okhttp3.Headers; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; @@ -50,7 +52,7 @@ public class LiveVideoService { public LiveVideoService() { - System.out.println("初始化下载服务"); + Log.i("初始化下载服务"); executor = new ThreadPoolExecutor(2, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new ArrayBlockingQueue(100)); } @@ -64,7 +66,7 @@ public class LiveVideoService { if (liveVideoMap.containsKey(bean)) { return; } - System.out.println("添加下载任务:" + liveVideoMap.keySet().size()); + Log.i("添加下载任务:" + liveVideoMap.keySet().size()); liveVideoMap.put(bean, null); BiliLiveNetApiManager.getInstance().getApi(bean.getRecordUid()).getRoomInfo(bean.getRoomId().toString()).enqueue(new HttpCallback() { @Override @@ -75,14 +77,14 @@ public class LiveVideoService { liveVideoMap.put(bean, task); } else { liveVideoMap.remove(bean); - System.out.println("移除下载"); + Log.i("移除下载"); } } @Override public void onFailure(Throwable throwable) { liveVideoMap.remove(bean); - System.out.println("移除下载"); + Log.i("移除下载"); } }); @@ -97,7 +99,7 @@ public class LiveVideoService { if (entry.getKey().getRoomId().toString().equals(roomId)) { entry.getValue().isDownload = false; liveVideoMap.remove(entry.getKey()); - System.out.println("移除下载"); + Log.i("移除下载"); break; } } @@ -130,6 +132,7 @@ public class LiveVideoService { boolean isDownload = true; LiveApi api; String savePath; + File rootPath; LiveConfigDatabaseBean config; BiliLiveDatabase database; LiveVideoDatabaseBean videoDatabaseBean; @@ -146,10 +149,13 @@ public class LiveVideoService { public void run() { if (roomInfo.getLiveStatus() == 1) { String time = DateUtils.format(new Date().getTime(), DATE_FORMAT_10_DASH); - savePath = new File(bean.getRecordPath() + File.separator + bean.getAnchorName() + File.separator + time + File.separator + roomInfo.getTitle()).getAbsolutePath(); - savePath += File.separator + "[" + + rootPath = new File(bean.getRecordPath() + File.separator + bean.getAnchorName() + File.separator + time + File.separator + roomInfo.getTitle()); + savePath = rootPath.getAbsolutePath() + File.separator + "[" + DateUtils.format(new Date(), "yyyy-MM-dd HH-mm-ss") + "]" + roomInfo.getTitle() + ".flv"; + if (!rootPath.exists()) { + rootPath.mkdirs(); + } record(bean, roomInfo); } else { stop(); @@ -157,6 +163,7 @@ public class LiveVideoService { } private void stop() { + liveVideoMap.remove(bean); api.getRoomInfo(config.getRoomId().toString()).enqueue(new HttpCallback() { @Override public void onResponse(Headers headers, int code, String status, LiveRoomInfo response, String rawResponse) { @@ -186,6 +193,11 @@ public class LiveVideoService { config.setRoomInfo(roomInfo); config.setRootPath(bean.getRecordPath()); database = new BiliLiveDatabase(config); + HttpDownloadUtils.download(new HttpDownloadUtils.Builder().setUrl(roomInfo.getKeyframe()) + .setPath(rootPath.getAbsolutePath()) + .setFileName("poster.jpg")); + LiveInfoNfoTools.saveLiveInfoNfo(roomInfo,rootPath.getAbsolutePath(),new File(savePath).getName().replace(".flv",".nfo")); + saveLiveInfo(roomInfo); api.getLiveRoomPlayInfo( bean.getRoomId().toString(), LiveProtocol.getAll(), @@ -206,7 +218,7 @@ public class LiveVideoService { @Override public void onFailure(Throwable throwable) { - throwable.printStackTrace(); + Log.e(throwable); } }); } @@ -258,8 +270,8 @@ public class LiveVideoService { .withNotSymbolParam("-threads", "8") .withNotSymbolParam("-c:v", "copy") .withNotSymbolParam("-y", "") - .build(ffmpegPath, url, savePath); - System.out.println(command); + .build(config.getRoomId().toString(),ffmpegPath, url, savePath); + Log.i(command.getCommand()); try { command.start(new DownloadInterface() { @Override @@ -279,7 +291,7 @@ public class LiveVideoService { @Override public void onDownload(File file) { super.onDownload(file); - System.err.println("下载完成 "); + Log.e("下载完成 "); stop(); } }); @@ -299,6 +311,10 @@ public class LiveVideoService { } } + private void saveLiveInfo(LiveRoomInfo roomInfo) { + + } + public List getAllVideoPath() { BiliLiveConfigDatabase configDatabase = new BiliLiveConfigDatabase(); List list = configDatabase.getAllConfig(); @@ -323,21 +339,12 @@ public class LiveVideoService { File recordDir = new File(recordPath); if (recordDir.exists()) { - List 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 infos = database.getLiveInfos(); - database.close(); - path.setChildren(getVideoInfo(infos)); - filePathList.add(path); - } - - } + BiliLiveDatabase database = new BiliLiveDatabase(LiveRoomConfig.buildConfig(configBean.getRoomId().toString()), recordDir.getAbsolutePath()); + VideoFilePath path = createVideoRootFilePath(configBean, recordDir); + List infos = database.getLiveInfos(); + database.close(); + path.setChildren(getVideoInfo(infos)); + filePathList.add(path); } return filePathList; } @@ -360,7 +367,7 @@ public class LiveVideoService { path.setRoomId(roomInfo.getRoomId().toString()); path.setName(roomInfo.getTitle()); path.setPath(DateFormatUtils.format(bean.getSql_time())); - path.setCover(FileServerUtils.toUrl(new File(bean.getPath()).getParent() + File.separator + "poster.jpg")); + path.setCover(roomInfo.getKeyframe()); path.setParent(false); path.setChildren(null); filePathList.add(path); @@ -371,7 +378,7 @@ public class LiveVideoService { public static void main(String[] args) { LiveVideoService service = new LiveVideoService(); List path = service.getAllVideoPath(); - System.out.println("path.size() = " + path.size()); - System.out.println(JSONArray.toJSONString(path)); + Log.i("path.size() = " + path.size()); + Log.i(JSONArray.toJSONString(path)); } } diff --git a/src/main/java/com/yutou/bilibili/services/SystemService.java b/src/main/java/com/yutou/bilibili/services/SystemService.java index 22bdb60..e5d5f5a 100644 --- a/src/main/java/com/yutou/bilibili/services/SystemService.java +++ b/src/main/java/com/yutou/bilibili/services/SystemService.java @@ -10,6 +10,7 @@ import com.yutou.biliapi.net.WebSocketManager; import com.yutou.bilibili.databases.SystemConfigDatabases; import com.yutou.bilibili.datas.SystemConfigDatabaseBean; import com.yutou.common.okhttp.HttpBody; +import com.yutou.common.utils.Log; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; @@ -48,21 +49,26 @@ public class SystemService { if (timer == null) { timer = Executors.newScheduledThreadPool(1); } - System.out.println("执行任务"); + Log.i("执行任务"); if (scheduled != null) { scheduled.cancel(true); } scheduled = timer.scheduleAtFixedRate(() -> { List list = liveConfigDatabase.getAllConfig(); - System.out.println("循环任务:" + list.size()); + Log.i("循环任务:" + list.size()); for (LiveConfigDatabaseBean bean : list) { - if (bean.isRecordDanmu() && bean.checkRecordDanmuTime()) { - recordDanmu(bean); - } - if (bean.isRecordLive() && bean.checkRecordLiveTime()) { - recordVideo(bean); + try { + if (bean.isRecordDanmu() && bean.checkRecordDanmuTime()) { + recordDanmu(bean); + } + if (bean.isRecordLive() && bean.checkRecordLiveTime()) { + recordVideo(bean); + } + } catch (Exception e) { + Log.e(e); } } + }, 0, getLoopTimer(), TimeUnit.MILLISECONDS); } @@ -73,7 +79,7 @@ public class SystemService { // 录制弹幕 private void recordDanmu(LiveConfigDatabaseBean bean) { - danmuService.start(bean.getRoomId().toString(),false); + danmuService.start(bean.getRoomId().toString(), false); } // 录制视频 @@ -83,10 +89,10 @@ public class SystemService { public static void main(String[] args) throws InterruptedException { SystemService service = new SystemService(); - System.out.println(new Date()); + Log.i(new Date()); service.start(); Thread.sleep(15000); - System.out.println(new Date()); + Log.i(new Date()); service.start(); } diff --git a/src/main/java/com/yutou/common/databases/AbsDatabasesBean.java b/src/main/java/com/yutou/common/databases/AbsDatabasesBean.java index e31bfb4..0f89d28 100644 --- a/src/main/java/com/yutou/common/databases/AbsDatabasesBean.java +++ b/src/main/java/com/yutou/common/databases/AbsDatabasesBean.java @@ -40,7 +40,7 @@ public class AbsDatabasesBean { json.put(key,""); } } - //System.out.println("创建" + tableName + "表 json:" + json); + //Log.i("创建" + tableName + "表 json:" + json); return json; } diff --git a/src/main/java/com/yutou/common/databases/SQLiteManager.java b/src/main/java/com/yutou/common/databases/SQLiteManager.java index 2d13de6..631a96a 100644 --- a/src/main/java/com/yutou/common/databases/SQLiteManager.java +++ b/src/main/java/com/yutou/common/databases/SQLiteManager.java @@ -146,8 +146,8 @@ public abstract class SQLiteManager { statement.execute(); statement.close(); } catch (SQLException e) { - e.printStackTrace(); - System.err.println(sb); + Log.e(e); + Log.e(sb); throw new RuntimeException(e); } } @@ -177,7 +177,7 @@ public abstract class SQLiteManager { statement.executeUpdate(sb.toString()); statement.close(); } catch (Exception e) { - e.printStackTrace(); + Log.e(e); throw new RuntimeException(e); } @@ -219,7 +219,7 @@ public abstract class SQLiteManager { resultSet.close(); statement.closeOnCompletion(); } catch (Exception e) { - e.printStackTrace(); + Log.e(e); throw new RuntimeException(e); } return list; @@ -236,7 +236,7 @@ public abstract class SQLiteManager { statement.close(); return ret != 0; } catch (Exception e) { - e.printStackTrace(); + Log.e(e); throw new RuntimeException(e); } } @@ -285,7 +285,7 @@ public abstract class SQLiteManager { sql.append(builder.toString()); } sql.append(");"); - System.out.println("创建表 > " + sql.toString()); + Log.i("创建表 > " + sql.toString()); statement.execute(sql.toString().replace(",);", ");")); statement.closeOnCompletion(); } catch (Exception e) { @@ -327,7 +327,7 @@ public abstract class SQLiteManager { bean.setRecordLive(true); bean.setKeywordList(List.of("111", "22")); JSONObject json = bean.toJson(); - System.out.println("json = " + json); + Log.i("json = " + json); } @Data diff --git a/src/main/java/com/yutou/common/okhttp/FileCallback.java b/src/main/java/com/yutou/common/okhttp/FileCallback.java index 10a879a..de4c86d 100644 --- a/src/main/java/com/yutou/common/okhttp/FileCallback.java +++ b/src/main/java/com/yutou/common/okhttp/FileCallback.java @@ -1,5 +1,6 @@ package com.yutou.common.okhttp; +import com.yutou.common.utils.Log; import okhttp3.Headers; import okhttp3.HttpUrl; import retrofit2.Call; @@ -45,12 +46,12 @@ public abstract class FileCallback implements Callback> { @Override public void run() { try { - System.out.println("开始下载"); + Log.i("开始下载"); File file = new File(savePath); onStart(bean); if (!file.exists()) { boolean mkdirs = file.getParentFile().mkdirs(); - System.out.println("mkdirs = " + mkdirs); + Log.i("mkdirs = " + mkdirs); } FileOutputStream outputStream = new FileOutputStream(file); int len; @@ -64,17 +65,17 @@ public abstract class FileCallback implements Callback> { outputStream.flush(); isDownload = onDownload(headers, bean, total, available); } - System.out.println("下载完成"); + Log.i("下载完成"); outputStream.close(); } catch (Exception e) { - e.printStackTrace(); + Log.e(e); onFailure(bean,e); } finally { onFinish(bean); try { inputStream.close(); } catch (IOException e) { - e.printStackTrace(); + Log.e(e); } } @@ -96,7 +97,7 @@ public abstract class FileCallback implements Callback> { try { executor.execute(new DownloadTask(bean, response.headers(), call.request().url(), response.body().getInputStream())); } catch (Exception e) { - e.printStackTrace(); + Log.e(e); onFailure(bean,e); call.cancel(); } diff --git a/src/main/java/com/yutou/common/okhttp/HttpDownloadUtils.java b/src/main/java/com/yutou/common/okhttp/HttpDownloadUtils.java index 6213a37..e72ceb0 100644 --- a/src/main/java/com/yutou/common/okhttp/HttpDownloadUtils.java +++ b/src/main/java/com/yutou/common/okhttp/HttpDownloadUtils.java @@ -68,7 +68,7 @@ public class HttpDownloadUtils { } fileOutputStream.flush(); } catch (IOException e) { - Log.getLogger("download").log(Level.FINE, "download error:" + builder.url, e); + Log.e("download error:" + builder.url, e); } finally { if (builder.downloadInterface != null) { builder.downloadInterface.onDownload(target); diff --git a/src/main/java/com/yutou/common/okhttp/PostRequestParams.java b/src/main/java/com/yutou/common/okhttp/PostRequestParams.java index 0a3c253..5898f80 100644 --- a/src/main/java/com/yutou/common/okhttp/PostRequestParams.java +++ b/src/main/java/com/yutou/common/okhttp/PostRequestParams.java @@ -2,6 +2,7 @@ package com.yutou.common.okhttp; import com.alibaba.fastjson2.JSONObject; +import com.yutou.common.utils.Log; import okhttp3.*; import java.net.URLEncoder; @@ -46,7 +47,7 @@ public class PostRequestParams implements IRequestParam { try { string.append("&").append(key).append("=").append(URLEncoder.encode(json.getString(key), "UTF-8")); } catch (Exception e) { - e.printStackTrace(); + Log.e(e); try { string.append("&").append(URLEncoder.encode(key, "UTF-8")).append("="); // string += "&" + key + "="; diff --git a/src/main/java/com/yutou/common/okhttp/converter/JsonResponseBodyConverter.java b/src/main/java/com/yutou/common/okhttp/converter/JsonResponseBodyConverter.java index de5b176..7601191 100644 --- a/src/main/java/com/yutou/common/okhttp/converter/JsonResponseBodyConverter.java +++ b/src/main/java/com/yutou/common/okhttp/converter/JsonResponseBodyConverter.java @@ -6,6 +6,7 @@ import com.google.gson.Gson; import com.google.gson.TypeAdapter; import com.yutou.common.okhttp.FileBody; import com.yutou.common.okhttp.HttpBody; +import com.yutou.common.utils.Log; import okhttp3.ResponseBody; import org.jetbrains.annotations.Nullable; import retrofit2.Converter; @@ -36,7 +37,7 @@ public class JsonResponseBodyConverter implements Converter body.setSrc(string); return (T) body; } catch (Exception e) { - e.printStackTrace(); + Log.e(e); body = new HttpBody(); body.setSrc(string); } diff --git a/src/main/java/com/yutou/common/utils/AppTools.java b/src/main/java/com/yutou/common/utils/AppTools.java index 7306292..57df6a2 100644 --- a/src/main/java/com/yutou/common/utils/AppTools.java +++ b/src/main/java/com/yutou/common/utils/AppTools.java @@ -259,10 +259,10 @@ public class AppTools { File srcFile = new File(srcFileName); // 判断源文件是否存在 if (!srcFile.exists()) { - System.err.println("源文件不存在:"+srcFile.getAbsolutePath()+" > "+destFileName); + Log.e("源文件不存在:"+srcFile.getAbsolutePath()+" > "+destFileName); return false; } else if (!srcFile.isFile()) { - System.err.println("源文件是目录:"+srcFile.getAbsolutePath()); + Log.e("源文件是目录:"+srcFile.getAbsolutePath()); return false; } @@ -273,7 +273,7 @@ public class AppTools { // 目标文件所在目录不存在 if (!destFile.mkdirs()) { // 复制文件失败:创建目标文件所在目录失败 - System.err.println("创建文件夹失败:"+destFile.getAbsolutePath()); + Log.e("创建文件夹失败:"+destFile.getAbsolutePath()); return false; } @@ -311,25 +311,4 @@ public class AppTools { } } - public static boolean isRuntimeSystemOfWindow(){ - return System.getProperty ("os.name").contains("Windows"); - } - - public static Process exec(String exec)throws Exception{ - if(AppTools.isRuntimeSystemOfWindow()) { - return Runtime.getRuntime().exec(new String[]{ - "cmd", - "/c", - exec - } - ); - }else{ - return Runtime.getRuntime().exec(new String[]{ - "sh", - "-c", - exec - } - ); - } - } } diff --git a/src/main/java/com/yutou/common/utils/Base64Tools.java b/src/main/java/com/yutou/common/utils/Base64Tools.java index 6c80e6b..536687e 100644 --- a/src/main/java/com/yutou/common/utils/Base64Tools.java +++ b/src/main/java/com/yutou/common/utils/Base64Tools.java @@ -25,7 +25,7 @@ public class Base64Tools { byte[] fileContent = Files.readAllBytes(Paths.get(file.getAbsolutePath())); return encode(fileContent); } catch (IOException e) { - e.printStackTrace(); + Log.e(e); } return null; } diff --git a/src/main/java/com/yutou/common/utils/FFmpegUtils.java b/src/main/java/com/yutou/common/utils/FFmpegUtils.java index d817581..8864ec7 100644 --- a/src/main/java/com/yutou/common/utils/FFmpegUtils.java +++ b/src/main/java/com/yutou/common/utils/FFmpegUtils.java @@ -1,21 +1,52 @@ package com.yutou.common.utils; +import com.yutou.bilibili.Tools.ProcessUtils; import com.yutou.bilibili.interfaces.DownloadInterface; +import lombok.Getter; import java.io.*; import java.nio.charset.StandardCharsets; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.*; public class FFmpegUtils { - + private static final Map pidMap = new HashMap<>(); + @Getter private String command; + private String uid; InputStream inputStream; OutputStream outputStream; private FFmpegUtils() { } + public static boolean check(String anchorUid) { + try { + return ProcessUtils.isProcessRunning(pidMap.get(anchorUid)); + } catch (Exception e) { + Log.e(e); + throw new RuntimeException(e); + } + } + + public static void kill(String anchorUid) { + try { + ProcessUtils.killProcess(pidMap.get(anchorUid)); + } catch (Exception e) { + Log.e(e); + throw new RuntimeException(e); + } + } + + public static void killAll() { + for (Long pid : pidMap.values()) { + try { + ProcessUtils.killProcess(pid); + } catch (Exception e) { + Log.e(e); + } + } + } + public static class Builder { private final Map params = new LinkedHashMap<>(); private final Map paramsNotSymbol = new LinkedHashMap<>(); @@ -57,9 +88,10 @@ public class FFmpegUtils { } - public FFmpegUtils build(String ffmpegPath, String inputUrl, String outputPath) { + public FFmpegUtils build(String uid, String ffmpegPath, String inputUrl, String outputPath) { FFmpegUtils ffmpeg = new FFmpegUtils(); ffmpeg.command = getCommand(ffmpegPath, inputUrl, outputPath); + ffmpeg.uid = uid; return ffmpeg; } } @@ -67,22 +99,29 @@ public class FFmpegUtils { public void start(DownloadInterface downloadInterface) { new Thread(() -> { try { - Process process = AppTools.exec(command); + if (check(uid)) { + kill(uid); + } + Process process = ProcessUtils.exec(command); + long pid = process.toHandle().pid(); + Log.i("进程id "+pid); inputStream = process.getErrorStream(); outputStream = process.getOutputStream(); byte[] bytes = new byte[2048]; if (downloadInterface != null) { downloadInterface.onDownloadStart(); } + pidMap.put(uid, pid); while (inputStream.read(bytes) > -1) { if (downloadInterface != null) { downloadInterface.onDownloading(0, 0); } } + pidMap.remove(uid); //获取视频时长:ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4 //获取到结果:5372.432000 if (downloadInterface != null) { - System.out.println("触发下载完成"); + Log.i("触发下载完成"); downloadInterface.onDownload(null); } } catch (Exception e) { @@ -102,6 +141,7 @@ public class FFmpegUtils { /** * 获取视频时长 + * * @param video 视频文件 * @return 毫秒 */ @@ -109,8 +149,8 @@ public class FFmpegUtils { 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); + Log.i(exec); + Process process = ProcessUtils.exec(exec); InputStream inputStream = process.getInputStream(); byte[] bytes = new byte[2048]; String data; @@ -119,9 +159,9 @@ public class FFmpegUtils { inputStream.close(); process.destroy(); process.exitValue(); - return (long) (Double.parseDouble(data)*1000); + return (long) (Double.parseDouble(data) * 1000); } catch (Exception e) { - e.printStackTrace(); + Log.e(e); throw new RuntimeException(e); } } diff --git a/src/main/java/com/yutou/common/utils/Log.java b/src/main/java/com/yutou/common/utils/Log.java index 83f04ec..2eb1061 100644 --- a/src/main/java/com/yutou/common/utils/Log.java +++ b/src/main/java/com/yutou/common/utils/Log.java @@ -1,14 +1,15 @@ package com.yutou.common.utils; -import java.io.File; -import java.io.IOException; -import java.util.logging.FileHandler; -import java.util.logging.Level; -import java.util.logging.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Log { - private static Logger logger=Logger.getLogger("Biliob"); + private static Logger logger = LoggerFactory.getLogger("Biliob"); + + public static void i() { + i("\n"); + } public static void i(String tag, Object log) { i('[' + tag + ']' + log); @@ -18,48 +19,44 @@ public class Log { if (!((boolean) ConfigTools.load(ConfigTools.CONFIG, "logcat"))) { return; } - // logger.log(Level.INFO, log.toString()); - System.out.printf("[%s]%s%n", - AppTools.getToDayNowTimeToString(), - log - ); - + logger.info(log.toString()); } - public static void e(Exception e) { - if (!ConfigTools.load(ConfigTools.CONFIG, "logcat", Boolean.class)){ + public static void e(Object log) { + if (!ConfigTools.load(ConfigTools.CONFIG, "logcat", Boolean.class)) { return; } - i(e.getMessage()); - e.printStackTrace(); + logger.error(getStackTrace(),log); } - public static void i(String timer, int roomId, Object log) { - String logFlag = RedisTools.get("live-log", 1); - if (logFlag != null && logFlag.startsWith("true")) { - if (logFlag.contains("|")) { - if (log.toString().contains(logFlag.split("\\|")[1])) { - getLogger("[" + timer.replace(":", "_") + "]" + roomId).log(Level.INFO, log.toString()); - } - } else { - getLogger("[" + timer.replace(":", "_") + "]" + roomId).log(Level.INFO, log.toString()); - } + public static void e(Throwable e) { + if (!ConfigTools.load(ConfigTools.CONFIG, "logcat", Boolean.class)) { + return; + } + logger.error(getStackTrace(), e); + } + + public static void e(String tag, Throwable e) { + logger.error("{}\n{}", tag, getStackTrace(), e); + } + + public static void d(Object... log) { + if (!ConfigTools.load(ConfigTools.CONFIG, "logcat", Boolean.class)) { + return; + } + if (log.length > 1) { + logger.debug(log[0].toString(), log); + } else { + logger.debug(log[0].toString()); } } - public static Logger getLogger(String fileName) { - if (logger == null) { - try { - if (!new File("logs").exists()) { - new File("logs").mkdirs(); - } - logger = Logger.getLogger("Live-Log"); - FileHandler handler = new FileHandler("logs" + File.separator + fileName + ".log"); - logger.addHandler(handler); - } catch (IOException e) { - e.printStackTrace(); - } + private static String getStackTrace() { + StackTraceElement[] stackTrace = new Throwable().getStackTrace(); + if (stackTrace.length > 3) { + StackTraceElement element = stackTrace[3]; + return " " + element.getClassName() + "." + element.getMethodName() + "#" + element.getLineNumber(); } - return logger; + return ""; } } diff --git a/src/main/java/com/yutou/common/utils/RedisTools.java b/src/main/java/com/yutou/common/utils/RedisTools.java index 9f4b391..c36012f 100644 --- a/src/main/java/com/yutou/common/utils/RedisTools.java +++ b/src/main/java/com/yutou/common/utils/RedisTools.java @@ -42,7 +42,7 @@ public class RedisTools { jedis.close(); } catch (Exception e) { // TODO: handle exception - e.printStackTrace(); + Log.e(e); return false; } return true; @@ -66,7 +66,7 @@ public class RedisTools { jedis.close(); } catch (Exception e) { // TODO: handle exception - e.printStackTrace(); + Log.e(e); return false; } return true; @@ -83,7 +83,7 @@ public class RedisTools { jedis.close(); } catch (Exception e) { // TODO: handle exception - // e.printStackTrace(); + // Log.e(e); } return value; } @@ -155,7 +155,7 @@ public class RedisTools { } properties.load(is); } catch (IOException e) { - e.printStackTrace(); + Log.e(e); } return properties; } diff --git a/src/main/java/com/yutou/qqbot/NapCatQQ.java b/src/main/java/com/yutou/qqbot/NapCatQQ.java index 62dbffd..7f05fdd 100644 --- a/src/main/java/com/yutou/qqbot/NapCatQQ.java +++ b/src/main/java/com/yutou/qqbot/NapCatQQ.java @@ -1,5 +1,6 @@ package com.yutou.qqbot; +import com.yutou.common.utils.Log; import com.yutou.qqbot.handle.MessageHandleBuild; import com.yutou.qqbot.handle.Text; import com.yutou.qqbot.http.NapCatApi; @@ -38,12 +39,12 @@ public class NapCatQQ { ).enqueue(new HttpCallback() { @Override public void onResponse(Headers headers, int code, String status, SendMessageResponse response, String rawResponse) { - System.out.println("code = " + code + ", status = " + status + ", response = " + response + ", rawResponse = " + rawResponse); + Log.i("code = " + code + ", status = " + status + ", response = " + response + ", rawResponse = " + rawResponse); } @Override public void onFailure(Throwable throwable) { - throwable.printStackTrace(); + Log.e(throwable); } }); } diff --git a/src/main/java/com/yutou/qqbot/event/MessageEvent.java b/src/main/java/com/yutou/qqbot/event/MessageEvent.java index e6a4787..96599a0 100644 --- a/src/main/java/com/yutou/qqbot/event/MessageEvent.java +++ b/src/main/java/com/yutou/qqbot/event/MessageEvent.java @@ -3,6 +3,7 @@ package com.yutou.qqbot.event; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.annotation.JSONField; +import com.yutou.common.utils.Log; import com.yutou.qqbot.QQDatabase; import com.yutou.qqbot.enums.MessageEnum; import com.yutou.qqbot.handle.*; @@ -135,7 +136,7 @@ public class MessageEvent { } } } catch (Exception e) { - e.printStackTrace(); + Log.e(e); } return tmp; }