diff --git a/src/main/java/com/yutou/biliapi/bean/live/LiveRoomConfig.java b/src/main/java/com/yutou/biliapi/bean/live/LiveRoomConfig.java index f6bf433..65fb0d2 100644 --- a/src/main/java/com/yutou/biliapi/bean/live/LiveRoomConfig.java +++ b/src/main/java/com/yutou/biliapi/bean/live/LiveRoomConfig.java @@ -45,7 +45,7 @@ public class LiveRoomConfig { public static LiveRoomConfig buildConfig(String roomId){ BiliLiveConfigDatabase database = new BiliLiveConfigDatabase(); LiveConfigDatabaseBean bean = database.getConfig(new String(roomId)); - + database.close(); LiveRoomConfig config = new LiveRoomConfig(); config.setLoginUid(bean.getRecordUid()); config.setRoomId(bean.getRoomId()); diff --git a/src/main/java/com/yutou/biliapi/bean/login/LoginCookieDatabaseBean.java b/src/main/java/com/yutou/biliapi/bean/login/LoginCookieDatabaseBean.java index e633f75..4f7f63c 100644 --- a/src/main/java/com/yutou/biliapi/bean/login/LoginCookieDatabaseBean.java +++ b/src/main/java/com/yutou/biliapi/bean/login/LoginCookieDatabaseBean.java @@ -35,6 +35,6 @@ public class LoginCookieDatabaseBean extends AbsDatabasesBean { } public String toCookieString() { - return "SESSDATA=" + sessdta + "; Path=" + path + "; DedeUserID=" + dedeUserID + "; DedeUserID__ckMd5=" + dedeUserIDCkMd5 + "; bili_jct=" + biliJct + "; Expires=" + expires + "; Domain=" + domain + "; sid=" + sid + "; gourl=" + gourl; + return "SESSDATA=" + sessdta + "; Path=" + path + "; DedeUserID=" + dedeUserID + "; DedeUserID__ckMd5=" + dedeUserIDCkMd5 + "; bili_jct=" + biliJct + "; Expires=" + expires + "; Domain=" + domain + "; sid=" + sid; } } diff --git a/src/main/java/com/yutou/biliapi/databases/BiliLiveDatabase.java b/src/main/java/com/yutou/biliapi/databases/BiliLiveDatabase.java index 6dc7a77..64f4e84 100644 --- a/src/main/java/com/yutou/biliapi/databases/BiliLiveDatabase.java +++ b/src/main/java/com/yutou/biliapi/databases/BiliLiveDatabase.java @@ -112,7 +112,7 @@ public class BiliLiveDatabase extends SQLiteManager { } public void addSource(WSData bean) { - Log.i("BiliLiveDatabase.addSource", config.getRoomId()); +// Log.i("BiliLiveDatabase.addSource", config.getRoomId()); add(new LiveSourceDatabaseBean(bean)); addData(bean); } diff --git a/src/main/java/com/yutou/biliapi/net/WebSocketManager.java b/src/main/java/com/yutou/biliapi/net/WebSocketManager.java index 6a0253c..5da78ab 100644 --- a/src/main/java/com/yutou/biliapi/net/WebSocketManager.java +++ b/src/main/java/com/yutou/biliapi/net/WebSocketManager.java @@ -174,7 +174,7 @@ public class WebSocketManager { heartbeatTask.setSocket(this); heartbeatTask.sendInitAuthData(); new Timer().schedule(heartbeatTask, 1000, 30000); - Log.i("WebSocketClientTh.onOpen", roomConfig); + Log.i("WebSocketClientTh.onOpen", roomConfig.getRoomId()); } @Override @@ -296,7 +296,7 @@ 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(); - Log.i("bytes.length = " + bytes.length); +// Log.i("bytes.length = " + bytes.length); Log.i(json); outputStream.write(BytesUtils.toLH(json.toString().length() + 16)); outputStream.write(bytes); diff --git a/src/main/java/com/yutou/bilibili/Controllers/LiveVideoController.java b/src/main/java/com/yutou/bilibili/Controllers/LiveVideoController.java index 270a617..f6f99a6 100644 --- a/src/main/java/com/yutou/bilibili/Controllers/LiveVideoController.java +++ b/src/main/java/com/yutou/bilibili/Controllers/LiveVideoController.java @@ -36,6 +36,7 @@ public class LiveVideoController { public JSONObject startDownload(String roomId) { BiliLiveConfigDatabase liveConfigDatabase = new BiliLiveConfigDatabase(); List list = liveConfigDatabase.getAllConfig(); + liveConfigDatabase.close(); for (LiveConfigDatabaseBean bean : list) { if (bean.getRoomId().toString().equals(roomId)) { videoService.start(bean, true); diff --git a/src/main/java/com/yutou/bilibili/Tools/DateFormatUtils.java b/src/main/java/com/yutou/bilibili/Tools/DateFormatUtils.java index e199d93..5fa9d39 100644 --- a/src/main/java/com/yutou/bilibili/Tools/DateFormatUtils.java +++ b/src/main/java/com/yutou/bilibili/Tools/DateFormatUtils.java @@ -62,6 +62,9 @@ public class DateFormatUtils { public Date parse(String date, String format) { try { + if(date.startsWith("1")){ + return new Date(Long.parseLong(date)); + } return getFormat(format).parse(date); } catch (ParseException e) { System.err.println("Error parsing date: " + e.getMessage()); diff --git a/src/main/java/com/yutou/bilibili/Tools/ProcessUtils.java b/src/main/java/com/yutou/bilibili/Tools/ProcessUtils.java index 0843265..366a84e 100644 --- a/src/main/java/com/yutou/bilibili/Tools/ProcessUtils.java +++ b/src/main/java/com/yutou/bilibili/Tools/ProcessUtils.java @@ -1,9 +1,15 @@ package com.yutou.bilibili.Tools; import com.yutou.common.utils.AppTools; +import com.yutou.common.utils.Base64Tools; +import com.yutou.common.utils.Log; +import org.apache.commons.io.IOUtils; import java.io.BufferedReader; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; public class ProcessUtils { @@ -31,35 +37,46 @@ public class ProcessUtils { if (pid == null) { return false; } - String command; + String[] command; if (isRuntimeSystemOfWindow()) { // Windows 操作系统 - command = "tasklist /FI \"PID eq " + pid + "\""; + command = new String[]{"cmd.exe", "/c", "tasklist /FI \"PID eq " + pid + "\""}; } else { // Unix/Linux 操作系统 - command = "ps -p " + pid; + command = new String[]{"sh", "-c", "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; + Process process = Runtime.getRuntime().exec(command); + boolean isRunning = false; + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)); + String line; + while ((line = reader.readLine()) != null) { + if (line.contains(String.valueOf(pid))) { + isRunning = true; + } } + reader.close(); + process.destroy(); + } catch (Exception e) { + Log.e(e); } - return false; + + return isRunning; } + public static Process exec(String exec) throws Exception { - if(isRuntimeSystemOfWindow()){ - return exec("cmd.exe","/c",exec); - }else{ - return exec("sh","-c",exec); + if (isRuntimeSystemOfWindow()) { + return exec("cmd.exe", "/c", exec); + } else { + return exec("sh", "-c", exec); } } + public static Process exec(String... command) throws Exception { ProcessBuilder pb; - pb = new ProcessBuilder(command); + List list = Arrays.stream(command).map(Base64Tools::decode).toList(); + pb = new ProcessBuilder(list); return pb.start(); } diff --git a/src/main/java/com/yutou/bilibili/services/LiveVideoDownloadService.java b/src/main/java/com/yutou/bilibili/services/LiveVideoDownloadService.java index e6383f9..c2feccc 100644 --- a/src/main/java/com/yutou/bilibili/services/LiveVideoDownloadService.java +++ b/src/main/java/com/yutou/bilibili/services/LiveVideoDownloadService.java @@ -18,9 +18,11 @@ 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.WebSignManager; import com.yutou.bilibili.Tools.DateFormatUtils; import com.yutou.bilibili.Tools.FileServerUtils; import com.yutou.bilibili.Tools.LiveInfoNfoTools; +import com.yutou.bilibili.Tools.ProcessUtils; import com.yutou.bilibili.datas.VideoFilePath; import com.yutou.bilibili.interfaces.DownloadInterface; import com.yutou.common.okhttp.HttpCallback; @@ -36,6 +38,7 @@ import org.springframework.util.StringUtils; import java.io.File; import java.util.*; import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -78,13 +81,13 @@ public class LiveVideoDownloadService { VideoTask task = new VideoTask(bean, response); executor.execute(task); } else { - Log.i("移除下载"); + Log.i(bean.getRoomId(), "没有开播"); } } @Override public void onFailure(Throwable throwable) { - Log.i("移除下载"); + Log.e(throwable, "移除下载"); } }); @@ -195,8 +198,29 @@ public class LiveVideoDownloadService { LiveVideoDefinition.ORIGINAL.getValue()).enqueue(new HttpCallback() { @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(); + Random random = new Random(); + List streams = response.getPlayurlInfo().getPlayurl().getStream(); + List streamList = streams.stream() + .filter(it -> "http_stream".equals(it.getProtocolName())) + .toList(); + + LiveRoomPlayInfo.Stream stream = streamList.get(random.nextInt(streamList.size())); + if (stream == null) { + return; + } + List formats = stream.getFormat().stream().filter(it -> "flv".equals(it.getFormatName())).toList(); + LiveRoomPlayInfo.Format format = formats.get(random.nextInt(formats.size())); + + List codecs = format.getCodec().stream().filter(item -> "avc".equals(item.getCodecName())).toList(); + + LiveRoomPlayInfo.Codec codec = codecs.get(random.nextInt(codecs.size())); + + int urlIndex = random.nextInt(codec.getUrlInfo().size()); + LiveRoomPlayInfo.UrlInfo urlInfo = codec.getUrlInfo().get(urlIndex); + + + String url = urlInfo.getHost() + codec.getBaseUrl() + urlInfo.getExtra(); + Log.i("下载直播",rawResponse,codec.toString(),urlInfo.toString(),"URL:"+url); if (bean.getRecordLiveModel() == 1) { javaRecord(url, response); @@ -253,32 +277,46 @@ public class LiveVideoDownloadService { FFmpegUtils.Builder builder = new FFmpegUtils.Builder() .withParam("-user_agent", ConfigTools.getUserAgent()) - .withParam("-headers", "Referer: https://live.bilibili.com") + .withParam("-headers", "Referer: https://live.bilibili.com/" + playInfo.getRoomId()) +// .withNotSymbolParam("-reconnect", "1") +// .withNotSymbolParam("-reconnect_at_eof", "1") +// .withNotSymbolParam("-reconnect_streamed", "1") +// .withNotSymbolParam("-reconnect_delay_max", "2") +// .withNotSymbolParam("-loglevel", "error") // .withNotSymbolParam("-progress", "-") +// .withNotSymbolParam("-fflags", "+genpts") .withNotSymbolParam("-threads", "8") - .withNotSymbolParam("-c:v", "copy") - .withNotSymbolParam("-fflags", "+genpts") + .withNotSymbolParam("-bufsize", "10M") + .withNotSymbolParam("-c", "copy") + .withNotSymbolParam("-bsf:a", "aac_adtstoasc") +// .withNotSymbolParam("-loglevel", "debug") .withNotSymbolParam("-y", "") //-reconnect 1 -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect_delay_max 2 - .withNotSymbolParam("-reconnect", "1") - .withNotSymbolParam("-reconnect_at_eof", "1") - .withNotSymbolParam("-reconnect_streamed", "1") - .withNotSymbolParam("-reconnect_delay_max", "2") - .withNotSymbolParam("-loglevel", "error") + // .withNotSymbolParam("-progress",new File("cache",config.getRoomId()+".txt").getAbsolutePath()); //输出进度日志,暂时没啥用 ; if (ck != null) { - builder = builder.withParam("-cookies", cookie); + // builder = builder.withParam("-cookies", cookie); } FFmpegUtils command = builder.build(config.getRoomId(), ffmpegPath, url, savePath); - Log.i(command.getCommand()); + Log.i(command.getCommandDecode()); try { command.start(new DownloadInterface() { + TimerTask task = null; + @Override public void onDownloadStart() { super.onDownloadStart(); - VideoTask.this.onStart(); - Log.i("启动录制:" + playInfo.getRoomId()); + task = new TimerTask() { + @Override + public void run() { + VideoTask.this.onStart(); + Log.i("启动录制:" + playInfo.getRoomId()); + task = null; + cancel(); + } + }; + new Timer().schedule(task, 5 * 1000); } @Override @@ -289,6 +327,10 @@ public class LiveVideoDownloadService { @Override public void onDownload(File file) { super.onDownload(file); + if (task != null) { + task.cancel(); + task = null; + } } }); @@ -386,7 +428,7 @@ public class LiveVideoDownloadService { videoFile = new File(videoInfo.getPath()); } FFmpegUtils ffmpeg = FFmpegUtils.segment(videoId, ffmpegPath, videoFile, ConfigTools.load(ConfigTools.CONFIG, "outVideoPath", String.class)); - System.out.println(ffmpeg.getCommand()); + System.out.println(ffmpeg.getCommandDecode()); ffmpeg.start(new DownloadInterface() { @Override public void onDownload(File file) { diff --git a/src/main/java/com/yutou/common/utils/Base64Tools.java b/src/main/java/com/yutou/common/utils/Base64Tools.java index 536687e..bbc197c 100644 --- a/src/main/java/com/yutou/common/utils/Base64Tools.java +++ b/src/main/java/com/yutou/common/utils/Base64Tools.java @@ -13,7 +13,11 @@ public class Base64Tools { } public static String decode(String str) { - return new String(Base64.getDecoder().decode(str)); + try { + return new String(Base64.getDecoder().decode(str)); + } catch (Exception e) { + return str; + } } public static String encode(byte[] bytes) { @@ -31,5 +35,6 @@ public class Base64Tools { } public static void main(String[] args) { + System.out.println(decode("aHR0cHM6Ly9jbi16ampoLWN0LTA0LTA2LmJpbGl2aWRlby5jb20vbGl2ZS1idmMvMzE1MDQ2L2xpdmVfMjY5NDE1MzU3Xzk4Mjc2ODIuZmx2P2V4cGlyZXM9MTczMjYwNDIyOCZwdD13ZWImZGVhZGxpbmU9MTczMjYwNDIyOCZsZW49MCZvaT0yOTM2NjQ2MjYwJnBsYXRmb3JtPXdlYiZxbj0xMDAwMCZ0cmlkPTEwMDAwZTUzYTY2ZjBiNWFjMDgwNGMwMDBhZGI5MTY3NDU2MyZ1aXBrPTEwMCZ1aXB2PTEwMCZuYnM9MSZ1cGFyYW1zPWNkbixkZWFkbGluZSxsZW4sb2kscGxhdGZvcm0scW4sdHJpZCx1aXBrLHVpcHYsbmJzJmNkbj1jbi1nb3RjaGEwMSZ1cHNpZz1iOGZhNjQyNGYxM2E1MDUwZThkNGRkZDc1NDcyOGYwYiZzaXRlPWRhYTczMDFjYmM5YzgwMjA1MTk4MDZmYmM4YzA4MmI2JmZyZWVfdHlwZT0wJm1pZD05NjMwMCZzY2hlPWJhbiZzaWQ9Y24tempqaC1jdC0wNC0wNiZjaGFzaD0xJmJtdD0xJnNnPWxyJnRyYWNlPTE3JmlzcD1jdCZyZz1DZW50cmFsJnB2PUh1bmFuJnNsPTImcHA9cnRtcCZwMnBfdHlwZT0xJmRlcGxveV9lbnY9cHJvZCZzdWZmaXg9b3JpZ2luJm9yaWdpbl9iaXRyYXRlPTc2OTczNiZzb3VyY2U9cHV2M19vbmV0aWVyJnNrPTQ5Yzc3YTNjMDBlY2M0NzI2ZjdlMGU1YTEzZjM5ZjYzJnNjb3JlPTUyJmhvdF9jZG49NTczNTMmdmQ9bmMmem9uZWlkX2w9MTUxMzg4MTYxJnNpZF9sPWxpdmVfMjY5NDE1MzU3Xzk4Mjc2ODImc3JjPXB1djMmb3JkZXI9MQ==")); } } diff --git a/src/main/java/com/yutou/common/utils/FFmpegUtils.java b/src/main/java/com/yutou/common/utils/FFmpegUtils.java index 21d69c2..1c330d7 100644 --- a/src/main/java/com/yutou/common/utils/FFmpegUtils.java +++ b/src/main/java/com/yutou/common/utils/FFmpegUtils.java @@ -10,14 +10,18 @@ import org.springframework.util.StringUtils; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.stream.Collectors; public class FFmpegUtils extends AbsVideoRecord { private static final Map pidMap = new HashMap<>(); - @Getter private String command; @Getter private String outputFilePath; private String uid; + @Getter + private String commandDecode; + @Getter + private Long pid; public FFmpegUtils() { } @@ -88,6 +92,7 @@ public class FFmpegUtils extends AbsVideoRecord { .withParam("-y", outputPath + File.separator + "course-%04d.ts") .getCommand(ffmpegPath, video.getAbsolutePath(), null); ffmpeg.outputFilePath = "video.m3u8"; + ffmpeg.commandDecode= Arrays.stream(ffmpeg.command.split(" ")).map(Base64Tools::decode).collect(Collectors.joining(" ")); return ffmpeg; } // ffmpeg 切片 @@ -111,20 +116,20 @@ public class FFmpegUtils extends AbsVideoRecord { } private String getCommand(String ffmpegPath, String inputUrl, String outputPath) { - StringBuilder command = new StringBuilder(ffmpegPath); + StringBuilder command = new StringBuilder(Base64Tools.encode(ffmpegPath)); - command.append(" -i ").append("\"").append(inputUrl).append("\""); + command.append(" ").append(Base64Tools.encode("-re")).append(" ").append(Base64Tools.encode("-i")).append(" ").append(Base64Tools.encode("\""+inputUrl+"\"")); for (Map.Entry entry : params.entrySet()) { if (StringUtils.hasText(entry.getKey())) { command.append(" "); - command.append(entry.getKey()); + command.append(Base64Tools.encode(entry.getKey())); } if (entry.getValue() != null && !entry.getValue().isEmpty()) { - command.append(" ").append(entry.getValue()); + command.append(" ").append(Base64Tools.encode(entry.getValue())); } } if (outputPath != null) { - command.append(" ").append("\"").append(outputPath).append("\""); + command.append(" ").append(Base64Tools.encode("\""+outputPath+"\"")); } return command.toString(); @@ -133,6 +138,7 @@ public class FFmpegUtils extends AbsVideoRecord { public FFmpegUtils build(String uid, String ffmpegPath, String inputUrl, String outputPath) { FFmpegUtils ffmpeg = new FFmpegUtils(); ffmpeg.command = getCommand(ffmpegPath, inputUrl, outputPath); + ffmpeg.commandDecode= Arrays.stream(ffmpeg.command.split(" ")).map(Base64Tools::decode).collect(Collectors.joining(" ")); ffmpeg.outputFilePath = outputPath; ffmpeg.uid = uid; return ffmpeg; @@ -141,7 +147,9 @@ public class FFmpegUtils extends AbsVideoRecord { @Override public void start(DownloadInterface downloadInterface) { - new Task(downloadInterface).start(); + Task task = new Task(downloadInterface); + task.start(); + pid= task.pid; } @Override @@ -194,7 +202,7 @@ public class FFmpegUtils extends AbsVideoRecord { super.run(); try { if (check(uid)) { - kill(); + return; } Process process = ProcessUtils.exec(command.split(" ")); pid = process.toHandle().pid(); @@ -207,7 +215,10 @@ public class FFmpegUtils extends AbsVideoRecord { } pidMap.put(uid, this); long startTimer = System.currentTimeMillis(); - while (inputStream.read(bytes) > -1) { + ByteArrayOutputStream os=new ByteArrayOutputStream(); + int len; + while ((len=inputStream.read(bytes)) > -1) { + os.write(bytes, 0, len); if (downloadInterface != null) { downloadInterface.onDownloading(System.currentTimeMillis() - startTimer, 0); } @@ -216,11 +227,12 @@ public class FFmpegUtils extends AbsVideoRecord { //获取视频时长:ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4 //获取到结果:5372.432000 if (downloadInterface != null) { - Log.i("触发下载完成", command); + //Log.i("触发下载完成", command); + Log.i("触发下载完成", new String(os.toByteArray())); downloadInterface.onDownload(null); } } catch (Exception e) { - throw new RuntimeException(e); + Log.e(e); } }