新增B站视频下载功能

新增弹幕转ass功能
优化B站网络请求头代码
This commit is contained in:
2022-05-04 22:30:56 +08:00
parent edfd663743
commit 84c583ad38
9 changed files with 58812 additions and 27 deletions

View File

@@ -0,0 +1,170 @@
package com.yutou.qqbot.bilibili;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
public class AssTools {
private final StringBuilder builder;
private String title;
private int y = 0;
private List<String> filters = new ArrayList<>();
private String alpha="80";
/**
* 弹幕转换ass
* @param title 标题
*/
public AssTools(String title) {
builder = new StringBuilder();
this.title=title;
initAssHeader();
}
/**
* 弹幕过滤器
* @param filter 过滤词
*/
public void addFilter(String... filter) {
filters.addAll(Arrays.asList(filter));
}
/**
* 弹幕透明度
* @param alpha 0 完全不透明 255 完全透明
*/
public void setAlpha(int alpha){
this.alpha=Integer.toHexString(alpha);
}
private void addBuilder(String txt) {
builder.append(txt).append("\n");
}
private void initAssHeader() {
addBuilder("[Script Info]");
addBuilder("Title: " + title);
addBuilder("Original Script: 本字幕由@yutou生成");
addBuilder("ScriptType: v4.00+");
addBuilder("Collisions: Normal");
addBuilder("PlayResX: 560");
addBuilder("PlayResY: 420");
addBuilder("Timer: 10.0000");
addBuilder("");
addBuilder("[V4+ Styles]");
addBuilder("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, ");
addBuilder("MarginL, MarginR, MarginV, Encoding");
addBuilder("Style: Fix,Microsoft YaHei UI,25,&H66FFFFFF,&H66FFFFFF,&H66000000,&H66000000,1,0,0,0,100,100,0,0,1,2,0,2,20,20,2,0");
addBuilder("Style: R2L,Microsoft YaHei UI,14,&H00FFFFFF,&H000000FF,&H00161616,&H00000000,0,0,0,0,100,100,0,0,1,2,0,2,20,20,20,1");
addBuilder("");
addBuilder("[Events]");
addBuilder("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
}
/**
* 保存弹幕文件
* @param savePath 存储路径
* @return 存储结果
*/
public boolean saveDanmu(String savePath) {
System.out.println("savePath = " + savePath);
File file = new File(savePath+File.separator+title+".ass");
FileWriter writer;
try {
if (file.exists()) {
if (!file.delete()) {
return false;
}
}
boolean mkdirs = file.mkdirs();
boolean delete = file.delete();
if (!mkdirs || !delete) {
return false;
}
if (!file.createNewFile()) {
return false;
}
writer = new FileWriter(file);
writer.write(builder.toString());
writer.flush();
writer.close();
return true;
} catch (IOException e) {
e.printStackTrace();
}finally {
}
return false;
}
/**
* 添加弹幕
* @param list 弹幕
*/
public void addDanmu(List<DanmuData> list) {
list.sort((o1, o2) -> {
if (o1.getTime() == o2.getTime()) {
return 0;
}
return o1.getTime() < o2.getTime() ? -1 : 1;
});
for (DanmuData danmuData : list) {
addDanmu(danmuData);
}
}
/**
* 添加弹幕
* @param danmuData 弹幕
*/
public void addDanmu(DanmuData danmuData) {
if (filters.contains(danmuData.getDanmu())) {
return;
}
addY();
long _time = danmuData.getTime();
long h = TimeUnit.MILLISECONDS.toHours(_time);
long m = TimeUnit.MILLISECONDS.toMinutes(_time) % 60;
long s = TimeUnit.MILLISECONDS.toSeconds(_time) % 60;
String sTime = String.format("%s:%s:%s.0",
new DecimalFormat("00").format(h),
new DecimalFormat("00").format(m),
new DecimalFormat("00").format(s));
if (s >= 52) {
s = (s + 8) - 60;
m++;
} else {
s += 8;
}
String eTime = String.format("%s:%s:%s.0",
new DecimalFormat("00").format(h),
new DecimalFormat("00").format(m),
new DecimalFormat("00").format(s));
float x1, x2;
x1 = 560 + (danmuData.getDanmu().length() * 12.5f);
x2 = 0 - (danmuData.getDanmu().length() * 12.5f);
String ass = String.format("Dialogue: 0,%s,%s,R2L,,20,20,2,,{\\move(%.1f,%d,%.1f,%d)\\c&%s\\alpha&H%s}%s",
sTime,
eTime,
x1,
y,
x2,
y,
danmuData.getFontColorHex(),
alpha,
danmuData.getDanmu()
);
addBuilder(ass);
}
private void addY() {
y += 25;
if (y >= 420) {
y = 25;
}
}
}

View File

@@ -2,18 +2,29 @@ package com.yutou.qqbot.bilibili;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.qqbot.utlis.ConfigTools;
import com.yutou.qqbot.utlis.StringUtils;
import com.yutou.qqbot.interfaces.ObjectInterface;
import com.yutou.qqbot.utlis.*;
import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class BiliBiliUtils {
private static long oldBiliBiliHttpTime = 0;
public enum HTTP {
POST, GET
}
public enum RET_MODEL {
BYTE, JSON
}
public synchronized static JSONObject http_get(String url) {
try {
// Log.i("调用url = "+url);
@@ -49,7 +60,15 @@ public class BiliBiliUtils {
}
public static JSONObject http_post(String url, String body) {
return http(url, HTTP.POST, body, RET_MODEL.JSON);
}
public static <T> T http(String url, HTTP model, String body, RET_MODEL ret_model) {
JSONObject json = null;
BufferedInputStream stream = null;
ByteArrayOutputStream outputStream = null;
OutputStream connectionOutputStream = null;
HttpURLConnection connection = null;
try {
if (System.currentTimeMillis() - oldBiliBiliHttpTime < 1000) {
try {
@@ -59,49 +78,68 @@ public class BiliBiliUtils {
}
oldBiliBiliHttpTime = System.currentTimeMillis();
}
HttpURLConnection connection = getBiliHttpPost(url, getCookie());
if (model == HTTP.POST) {
connection = getBiliHttpPost(url, getCookie());
} else {
connection = getBiliHttpGet(url, getCookie());
}
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
OutputStream connectionOutputStream = null;
if (!StringUtils.isEmpty(body)) {
connectionOutputStream = connection.getOutputStream();
connectionOutputStream.write(body.getBytes(StandardCharsets.UTF_8));
connectionOutputStream.flush();
}
connection.connect();
if(connection.getResponseCode()==400){
if (connection.getResponseCode() == 400) {
return null;
}
BufferedInputStream stream = new BufferedInputStream(connection.getInputStream());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
stream = new BufferedInputStream(connection.getInputStream());
outputStream = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int len = 0, size;
while ((len = stream.read(bytes)) != -1) {
outputStream.write(bytes, 0, len);
outputStream.flush();
}
if (ret_model == RET_MODEL.BYTE) {
return (T) outputStream.toByteArray();
}
String str = outputStream.toString(StandardCharsets.UTF_8);
outputStream.close();
try {
json = JSON.parseObject(str);
json.put("cookie", connection.getHeaderField("Set-Cookie"));
return json;
return (T) json;
} catch (Exception e) {
json = new JSONObject();
json.put("html", str);
json.put("cookie", connection.getHeaderField("Set-Cookie"));
return json;
} finally {
stream.close();
if (connectionOutputStream != null) {
connectionOutputStream.close();
}
connection.disconnect();
return (T) json;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (stream != null) {
stream.close();
}
if (outputStream != null) {
outputStream.close();
}
if (connectionOutputStream != null) {
connectionOutputStream.close();
}
if (connection != null) {
connection.disconnect();
}
} catch (Exception ignored) {
}
}
return json;
return null;
}
public static String getCookie() {
@@ -150,15 +188,101 @@ public class BiliBiliUtils {
return connection;
}
public static File download(final String url, final String saveName, boolean isProxy) {
File jar = null;
try {
File savePath = new File(HttpTools.downloadPath+saveName);
Proxy proxy = null;
if (!savePath.exists()) {
savePath.mkdirs();
}
savePath.delete();
Log.i("DOWNLOAD", "下载文件:" + url + " 保存文件:" + saveName);
if (isProxy) {
proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 7890));
}
HttpsURLConnection connection;
if (isProxy) {
connection = (HttpsURLConnection) new URL(url).openConnection(proxy);
} else {
connection = (HttpsURLConnection) new URL(url).openConnection();
}
setConnection(getCookie(), connection);
InputStream inputStream = connection.getInputStream();
jar = new File(HttpTools.downloadPath + saveName + "_tmp.tmp");
jar.createNewFile();
Log.i("DOWNLOAD", "临时保存文件:" + jar.getAbsolutePath());
OutputStream outputStream = new FileOutputStream(jar);
byte[] bytes = new byte[1024];
double size = connection.getContentLength();
double downSize = 0;
int len;
while ((len = inputStream.read(bytes)) > 0) {
outputStream.write(bytes, 0, len);
downSize += len;
}
outputStream.close();
inputStream.close();
File oldJar = new File(HttpTools.downloadPath + saveName);
if (oldJar.exists()) {
oldJar.delete();
}
jar.renameTo(oldJar);
Log.i("DOWNLOAD", "实际保存:" + oldJar.getAbsolutePath() + " " + oldJar.getName());
return oldJar;
} catch (Exception e) {
e.printStackTrace();
if (jar != null) {
jar.delete();
}
}
return null;
}
public static void download_ffmpeg(final List<String> url, final String saveName) {
new Thread(() -> {
StringBuilder builder = new StringBuilder();
builder.append(ConfigTools.load(ConfigTools.CONFIG, "ffmpeg", String.class)).append(" ");
/* builder.append("-user_agent").append(" ");
builder.append("\"").append("User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36 Referer:https://live.bilibili.com").append("\"").append(" ");
builder.append("-cookies").append(" ");
builder.append("\"").append(getCookie()).append("\"").append(" ");
builder.append("-headers").append(" ");
builder.append("\"").append("Referer:https://live.bilibili.com").append("\"").append(" ");*/
for (String _url : url) {
builder.append("-i").append(" ");
builder.append("\"").append(_url).append("\"").append(" ");
}
builder.append("-vcodec").append(" ");
builder.append("copy").append(" ");
builder.append("-acodec").append(" ");
builder.append("copy").append(" ");
builder.append("-threads").append(" ");
builder.append("8").append(" ");
// builder.append("-y").append(" ");
builder.append(new File(HttpTools.downloadPath + saveName + ".mp4").getAbsolutePath()).append(" ");
System.out.println(builder);
AppTools.exec(builder.toString(), new ObjectInterface() {
@Override
public void out(String data) {
super.out(data);
System.out.println("data = " + data);
}
}, false, false);
}).start();
}
private static void setConnection(String cookie, HttpURLConnection connection) {
connection.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
connection.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.8");
connection.setRequestProperty("Cache-Control", "max-age=0");
//connection.setRequestProperty("Referer", ".bilibili.com");
connection.setRequestProperty("Connection", "keep-alive");
connection.setRequestProperty("Upgrade-Insecure-Requests", "1");
connection.setRequestProperty("Cookie", cookie);
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36");
connection.addRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
connection.addRequestProperty("Accept-Language", "zh-CN,zh;q=0.8");
connection.addRequestProperty("Cache-Control", "max-age=0");
connection.setRequestProperty("Referer", "https://www.bilibili.com");
connection.addRequestProperty("Connection", "keep-alive");
connection.addRequestProperty("Upgrade-Insecure-Requests", "1");
connection.addRequestProperty("Cookie", cookie);
connection.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36");
}
public static JSONObject getLoginInfo() {

View File

@@ -0,0 +1,26 @@
package com.yutou.qqbot.bilibili;
import lombok.Data;
import java.util.Date;
@Data
public class DanmuData {
private int id;
private int model;//13 滚动弹幕 4 底端弹幕 5 顶端弹幕 6 逆向弹幕 7 精准定位 8 高级弹幕
private int fontSize;
private int fontColor;
private long time;
private String uCode;
private String danmu;
private long uid;
private String uname;
public Date getTimeDate() {
return new Date(time);
}
public String getFontColorHex() {
return Integer.toHexString(fontColor);
}
}

File diff suppressed because it is too large Load Diff