优化下载

This commit is contained in:
zlzw 2024-11-26 17:47:27 +08:00
parent 44d23b6bac
commit d9919f4d26
10 changed files with 129 additions and 49 deletions

View File

@ -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());

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -36,6 +36,7 @@ public class LiveVideoController {
public JSONObject startDownload(String roomId) {
BiliLiveConfigDatabase liveConfigDatabase = new BiliLiveConfigDatabase();
List<LiveConfigDatabaseBean> list = liveConfigDatabase.getAllConfig();
liveConfigDatabase.close();
for (LiveConfigDatabaseBean bean : list) {
if (bean.getRoomId().toString().equals(roomId)) {
videoService.start(bean, true);

View File

@ -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());

View File

@ -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<String> list = Arrays.stream(command).map(Base64Tools::decode).toList();
pb = new ProcessBuilder(list);
return pb.start();
}

View File

@ -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<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();
Random random = new Random();
List<LiveRoomPlayInfo.Stream> streams = response.getPlayurlInfo().getPlayurl().getStream();
List<LiveRoomPlayInfo.Stream> 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<LiveRoomPlayInfo.Format> formats = stream.getFormat().stream().filter(it -> "flv".equals(it.getFormatName())).toList();
LiveRoomPlayInfo.Format format = formats.get(random.nextInt(formats.size()));
List<LiveRoomPlayInfo.Codec> 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) {

View File

@ -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=="));
}
}

View File

@ -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<String, Task> 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<String, String> 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);
}
}