mirror of
https://github.com/Geniusay/ChopperBot.git
synced 2026-06-20 11:02:13 +08:00
斗鱼直播流下载
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
package org.example;
|
||||
|
||||
import org.apache.poi.ss.formula.functions.T;
|
||||
import org.example.core.manager.TaskManager;
|
||||
import org.example.pojo.live.DouyuLiveConfig;
|
||||
|
||||
|
||||
public class DouyuLiveTest {
|
||||
public static void main(String[] args) {
|
||||
TaskManager taskManager = new TaskManager(5);
|
||||
|
||||
DouyuLiveConfig douyuLiveConfig = new DouyuLiveConfig("3637778", "C:\\Users\\admin\\Desktop\\douyu\\", "DouyuLiveOnline");
|
||||
try {
|
||||
// 向任务管理器中添加任务
|
||||
String taskId = taskManager.addTask(douyuLiveConfig); // 获取任务的标识符
|
||||
|
||||
int cnt = 10;
|
||||
while (cnt > 0) {
|
||||
cnt--;
|
||||
if (taskManager.isConnectionClosed(taskId)) {
|
||||
System.out.println("连接中断,已停止录制...");
|
||||
break;
|
||||
}
|
||||
System.out.println("平均下载速度:" + taskManager.getDownloadSpeedAvg(taskId) + " B/s");
|
||||
System.out.println("瞬时下载速度:" + taskManager.getDownloadSpeed(taskId) + " B/s");
|
||||
System.out.println("已写入数据量:" + taskManager.getDownloadedBytes(taskId) + " bytes");
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,9 @@ package org.example.core.factory;
|
||||
import org.example.core.parser.PlatformVideoUrlParser;
|
||||
import org.example.core.parser.impl.BilibiliFlvUrlParser;
|
||||
import org.example.core.component.LiveStreamTask;
|
||||
import org.example.core.parser.impl.DouyuFlvUrlParser;
|
||||
import org.example.pojo.live.BilibiliLiveConfig;
|
||||
import org.example.pojo.live.DouyuLiveConfig;
|
||||
import org.example.pojo.live.LiveConfig;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -23,6 +25,7 @@ public class LiveTaskFactory {
|
||||
// 添加更多的解析器
|
||||
this.parserMap.put(BilibiliLiveConfig.class, new BilibiliFlvUrlParser());
|
||||
// 如果你有更多的平台,你可以在这里添加它们的解析器
|
||||
this.parserMap.put(DouyuLiveConfig.class, new DouyuFlvUrlParser());
|
||||
}
|
||||
|
||||
// 根据LiveConfig去创建下载任务
|
||||
@@ -43,6 +46,13 @@ public class LiveTaskFactory {
|
||||
headers.put("Referer", "https://live.bilibili.com/");
|
||||
task.setHeaders(headers);
|
||||
}
|
||||
else if (liveConfig instanceof DouyuLiveConfig) {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36");
|
||||
headers.put("Origin", "https://www.douyu.com");
|
||||
headers.put("Referer", "https://www.douyu.com/topic/15ZNQ?rid=3357246&dyshid=0-818074ef9c05a3fe94acdfe500091601");
|
||||
task.setHeaders(headers);
|
||||
}
|
||||
// 如果有其他平台的直播,可以在这里添加相应的请求头
|
||||
return task;
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package org.example.core.parser.impl;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.example.core.parser.PlatformVideoUrlParser;
|
||||
import org.example.pojo.live.DouyuLiveConfig;
|
||||
import org.example.pojo.live.LiveConfig;
|
||||
import org.example.utils.HttpClientUtil;
|
||||
import org.example.utils.RegexUtil;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import java.time.LocalDate;
|
||||
|
||||
|
||||
public class DouyuFlvUrlParser implements PlatformVideoUrlParser {
|
||||
String flvBaseUrl = "http://openflv-huos.douyucdn2.cn/dyliveflv1/";
|
||||
String did = "818074ef9c05a3fe94acdfe500091601";
|
||||
|
||||
@Override
|
||||
public String getUrl(LiveConfig liveConfig) throws Exception {
|
||||
DouyuLiveConfig douyuLiveConfig = (DouyuLiveConfig) liveConfig;
|
||||
String roomId = douyuLiveConfig.getRoomId();
|
||||
int clarity = douyuLiveConfig.getClarity();
|
||||
String html = HttpClientUtil.get("https://www.douyu.com/" + roomId);
|
||||
// 流程: 获取直播间页面
|
||||
// 从页面内获取一个函数和两个变量(每个直播间不同)
|
||||
// 该函数会生成一段JavaScript代码(用于签名)然后执行
|
||||
// 生成的代码用到了CryPtoJS库进行md5加密,所以要在代码执行前将在java中加密好并将原来的部分替换,再执行
|
||||
// 执行后生成一个签名
|
||||
// 将签名作为参数请求/getH5play接口,从响应中获取直播间对应的flv文件名
|
||||
// 得到链接
|
||||
String js_fun = RegexUtil.match(html, "(function ub98484234[\\s\\S]*?return eval[\\s\\S]*?);}")[0];
|
||||
String param_str = js_fun.substring(js_fun.indexOf("(") + 1, js_fun.indexOf(")"));
|
||||
String[] params = param_str.split(",");
|
||||
int index = js_fun.indexOf("){");
|
||||
String js_fun_head = js_fun.substring(0, index + 2);
|
||||
js_fun = js_fun.substring(index + 2);
|
||||
String js_var = RegexUtil.match(html, "(var vdwdae325w_64we[\\s\\S]*?;)")[0];
|
||||
String var2 = RegexUtil.match(js_fun, "v = ([\\s\\S]*).slice[\\s\\S]*?")[0];
|
||||
String var2_name = var2.substring(4, var2.length() - 6);
|
||||
String js_var2 = RegexUtil.match(html, "\\b(var " + var2_name + "[\\s\\S]*?;)")[0];
|
||||
js_fun = js_var + js_var2 + js_fun;
|
||||
js_fun = js_fun_head + js_fun;
|
||||
long time = (long) (System.currentTimeMillis() / 1e3);
|
||||
String cb = roomId + did + time + "2201" + LocalDate.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd"));
|
||||
// cb = "3357246818074ef9c05a3fe94acdfe5000916011691158546220120230804";
|
||||
String rb = DigestUtils.md5Hex(cb);
|
||||
String insert = String.format(".replace(\"CryptoJS.MD5(cb).toString()\",\"'%s'\")", rb); //js代码中用到了CryptoJS库md5加密,需要先加密好然后替换
|
||||
int index2 = js_fun.lastIndexOf("strc");
|
||||
js_fun = js_fun.substring(0, index2 + 4) + insert + js_fun.substring(index2 + 4); //替换
|
||||
|
||||
ScriptEngineManager manager = new ScriptEngineManager(); //执行js签名代码
|
||||
ScriptEngine engine = manager.getEngineByName("js");
|
||||
// String script = String.format("var ub = new Function('%s','%s','%s','%s')",params[0],params[1],params[2],js_fun);
|
||||
engine.eval(js_fun);
|
||||
Invocable inv = (Invocable) engine;
|
||||
Object ret = inv.invokeFunction("ub98484234", roomId, did, time);
|
||||
String resp = HttpClientUtil.post(String.format("https://www.douyu.com/lapi/live/getH5Play/%s?%s&cdn=ws-h5&rate=0", roomId, ret));
|
||||
JSONObject respObj = new JSONObject(resp);
|
||||
if (respObj.getInt("error") == 0) {
|
||||
JSONObject dataObj = respObj.getJSONObject("data");
|
||||
if(dataObj!=null){
|
||||
String fileUrl = dataObj.getString("rtmp_live");
|
||||
if(fileUrl!=null){
|
||||
String name = fileUrl.substring(0,fileUrl.indexOf("."));
|
||||
return String.format(flvBaseUrl+"%s.flv",name);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Exception("没有找到直播流地址");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.example.pojo.live;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DouyuLiveConfig extends LiveConfig{
|
||||
|
||||
// 清晰度,4000蓝光
|
||||
private int clarity;
|
||||
|
||||
public DouyuLiveConfig(String roomId, int clarity, String videoPath, String videoName, boolean convertToMp4) {
|
||||
super(roomId, videoPath, videoName, convertToMp4);
|
||||
this.clarity = clarity;
|
||||
}
|
||||
|
||||
public DouyuLiveConfig(String roomId, String videoPath, String videoName) {
|
||||
super(roomId, videoPath, videoName, true);
|
||||
this.clarity = 4000;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,9 @@ public class HttpClientUtil {
|
||||
public static String post(String url, String json) {
|
||||
return post(url, json, null);
|
||||
}
|
||||
|
||||
public static String post(String url) {
|
||||
return post2(url, null);
|
||||
}
|
||||
// get请求,带请求头
|
||||
public static String get(String url, Map<String, String> headers) {
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
@@ -58,6 +60,16 @@ public class HttpClientUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static String post2(String url, Map<String, String> headers) {
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
addHeaders(httpPost, headers);
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
||||
return handleResponse(response);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 统一处理请求头
|
||||
private static void addHeaders(HttpRequest httpRequest, Map<String, String> headers) {
|
||||
if (headers != null && !headers.isEmpty()) {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.example.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author dhx
|
||||
* @date 2023/8/4 19:36
|
||||
*/
|
||||
public class RegexUtil {
|
||||
public static String[] match(String input, String regex) {
|
||||
List<String> matches = new ArrayList<>();
|
||||
Matcher matcher = Pattern.compile(regex).matcher(input);
|
||||
|
||||
while (matcher.find()) {
|
||||
matches.add(matcher.group());
|
||||
}
|
||||
|
||||
return matches.toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user