优化下载
This commit is contained in:
parent
44d23b6bac
commit
d9919f4d26
@ -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());
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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=="));
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user