新增B站视频下载功能
新增弹幕转ass功能 优化B站网络请求头代码
This commit is contained in:
271
src/main/java/com/yutou/qqbot/models/BiliBili/BiliVideo.java
Normal file
271
src/main/java/com/yutou/qqbot/models/BiliBili/BiliVideo.java
Normal file
@@ -0,0 +1,271 @@
|
||||
package com.yutou.qqbot.models.BiliBili;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.google.protobuf.TextFormat;
|
||||
import com.google.protobuf.UnknownFieldSet;
|
||||
import com.google.protobuf.util.JsonFormat;
|
||||
import com.yutou.qqbot.bilibili.*;
|
||||
import com.yutou.qqbot.interfaces.DownloadInterface;
|
||||
import com.yutou.qqbot.interfaces.ObjectInterface;
|
||||
import com.yutou.qqbot.models.Model;
|
||||
import com.yutou.qqbot.utlis.AppTools;
|
||||
import com.yutou.qqbot.utlis.ConfigTools;
|
||||
import com.yutou.qqbot.utlis.HttpTools;
|
||||
import com.yutou.qqbot.utlis.StringUtils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
public class BiliVideo extends Model {
|
||||
public String downloadPath = "tmp";
|
||||
|
||||
@Override
|
||||
public boolean isUserPublic() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getUsePowers() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModelName() {
|
||||
return "B站视频下载";
|
||||
}
|
||||
|
||||
private void downVideo(String url) {
|
||||
if (!new BiliLogin().testLogin()) {
|
||||
System.err.println("未登录");
|
||||
return;
|
||||
}
|
||||
if (!url.contains("?")) {
|
||||
url += "?";
|
||||
}
|
||||
JSONObject info = getVideoInfo(url);
|
||||
if (info.getInteger("code") == 0) {
|
||||
JSONObject infoData = info.getJSONObject("data");
|
||||
JSONObject eps = new JSONObject();
|
||||
if (infoData.containsKey("ugc_season")) {
|
||||
JSONObject ugc = infoData.getJSONObject("ugc_season");
|
||||
eps.put("title", ugc.getString("title"));
|
||||
JSONArray ep = new JSONArray();
|
||||
for (Object o : ugc.getJSONArray("sections")) {
|
||||
JSONObject season = (JSONObject) o;
|
||||
for (Object episodes : season.getJSONArray("episodes")) {
|
||||
JSONObject _epi = (JSONObject) episodes;
|
||||
JSONObject _item = new JSONObject();
|
||||
_item.put("title", season.getString("title") + "-" + _epi.getString("title"));
|
||||
_item.put("cid", _epi.getLong("cid"));
|
||||
_item.put("aid", _epi.getLong("aid"));
|
||||
ep.add(_item);
|
||||
}
|
||||
}
|
||||
eps.put("eps", ep);
|
||||
} else if (infoData.getInteger("videos") != 1) {
|
||||
JSONArray pages = infoData.getJSONArray("pages");
|
||||
JSONArray ep = new JSONArray();
|
||||
for (Object o : pages) {
|
||||
JSONObject page = (JSONObject) o;
|
||||
JSONObject _item = new JSONObject();
|
||||
_item.put("title", infoData.getString("title") + "-" + page.getString("part"));
|
||||
_item.put("cid", page.getLong("cid"));
|
||||
_item.put("aid", infoData.getLong("aid"));
|
||||
ep.add(_item);
|
||||
}
|
||||
eps.put("title", infoData.getString("title"));
|
||||
eps.put("eps", ep);
|
||||
} else {
|
||||
eps.put("title", infoData.getString("title"));
|
||||
eps.put("cid", infoData.getLong("cid"));
|
||||
}
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("avid", infoData.getLong("aid"));
|
||||
if (eps.containsKey("cid")) {
|
||||
json.put("cid", eps.getLong("cid"));
|
||||
json.put("qn", 127);
|
||||
json.put("fnval", 80);
|
||||
json.put("fourk", 1);
|
||||
downVideo(json, eps);
|
||||
} else {
|
||||
for (Object o : eps.getJSONArray("eps")) {
|
||||
JSONObject item = (JSONObject) o;
|
||||
json.put("avid", item.getLong("aid"));
|
||||
json.put("cid", item.getLong("cid"));
|
||||
json.put("qn", 127);
|
||||
json.put("fnval", 80);
|
||||
json.put("fourk", 1);
|
||||
item.put("title", eps.getString("title") + "$(File.separator)" + item.getString("title"));
|
||||
downVideo(json, item);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void downVideo(JSONObject json, JSONObject eps) {
|
||||
eps.put("title", StringUtils.toSaveFileName(eps.getString("title")));
|
||||
File tmp = new File(HttpTools.downloadPath + eps.getString("title") + ".mp4");
|
||||
downDanmu(json.getLong("cid"), json.getLong("avid"), eps.getString("title"), 1);
|
||||
if (tmp.exists()) {
|
||||
return;
|
||||
}
|
||||
JSONObject http = BiliBiliUtils.http("https://api.bilibili.com/x/player/playurl?" + HttpTools.toUrlParams(json), BiliBiliUtils.HTTP.GET, null, BiliBiliUtils.RET_MODEL.JSON);
|
||||
if (http.getInteger("code") == 0) {
|
||||
JSONObject data = http.getJSONObject("data");
|
||||
JSONObject dash = data.getJSONObject("dash");
|
||||
JSONObject video = dash.getJSONArray("video").getJSONObject(0);
|
||||
JSONObject audio = dash.getJSONArray("audio").getJSONObject(0);
|
||||
|
||||
File videoFile = BiliBiliUtils.download(video.getString("baseUrl"), eps.getString("title") + "_video.mp4", false);
|
||||
if (videoFile == null) {
|
||||
downVideo(json, eps);
|
||||
return;
|
||||
}
|
||||
File audioFile = BiliBiliUtils.download(audio.getString("baseUrl"), eps.getString("title") + "_audio.mp3", false);
|
||||
if (audioFile == null) {
|
||||
videoFile.delete();
|
||||
downVideo(json, eps);
|
||||
return;
|
||||
}
|
||||
save(eps.getString("title"), videoFile, audioFile);
|
||||
}
|
||||
}
|
||||
|
||||
private void downDanmu(long cid, long avid, String title, int segment_index) {
|
||||
System.out.println("cid = " + cid + ", avid = " + avid + ", title = " + title + ", segment_index = " + segment_index);
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("type", 1);
|
||||
json.put("oid", cid);
|
||||
json.put("avid", avid);
|
||||
json.put("segment_index", segment_index);
|
||||
byte[] http = BiliBiliUtils.http("https://api.bilibili.com/x/v2/dm/web/seg.so?" + HttpTools.toUrlParams(json), BiliBiliUtils.HTTP.GET, null, BiliBiliUtils.RET_MODEL.BYTE);
|
||||
try {
|
||||
VideoDanMu.DmSegMobileReply parse = VideoDanMu.DmSegMobileReply.parseFrom(http);
|
||||
AssTools tools = new AssTools(title);
|
||||
List<DanmuData> list = new ArrayList<>();
|
||||
for (VideoDanMu.DanmakuElem elem : parse.getElemsList()) {
|
||||
DanmuData danmuData = new DanmuData();
|
||||
danmuData.setDanmu(elem.getContent());
|
||||
danmuData.setFontSize(elem.getFontsize());
|
||||
danmuData.setTime(elem.getProgress());
|
||||
danmuData.setFontColor(elem.getColor());
|
||||
danmuData.setModel(elem.getMode());
|
||||
list.add(danmuData);
|
||||
}
|
||||
tools.addDanmu(list);
|
||||
tools.saveDanmu("tmp");
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void save(String name, File videoFile, File audioFile) {
|
||||
List<String> urls = new ArrayList<>();
|
||||
urls.add(videoFile.getAbsolutePath());
|
||||
urls.add(audioFile.getAbsolutePath());
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(ConfigTools.load(ConfigTools.CONFIG, "ffmpeg", String.class)).append(" ");
|
||||
for (String _url : urls) {
|
||||
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("\"").append(new File(HttpTools.downloadPath + name + ".mp4").getAbsolutePath()).append("\"").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);
|
||||
videoFile.delete();
|
||||
audioFile.delete();
|
||||
}
|
||||
|
||||
}, false, false);
|
||||
}
|
||||
|
||||
public JSONObject getVideoInfo(String url) {
|
||||
if (!new BiliLogin().testLogin()) {
|
||||
System.err.println("未登录");
|
||||
return null;
|
||||
}
|
||||
JSONObject json = buildJSON(url);
|
||||
if (json != null) {
|
||||
return BiliBiliUtils.http("https://api.bilibili.com/x/web-interface/view?" + HttpTools.toUrlParams(json), BiliBiliUtils.HTTP.GET, null, BiliBiliUtils.RET_MODEL.JSON);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private JSONObject buildJSON(String url) {
|
||||
if (!url.contains("?")) {
|
||||
url += "?";
|
||||
}
|
||||
Pattern pattern = Pattern.compile("(?<=video/).*?(?=\\?)");
|
||||
Matcher matcher = pattern.matcher(url);
|
||||
String id = null;
|
||||
if (matcher.find()) {
|
||||
id = matcher.group();
|
||||
}
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
if (id.contains("/")) {
|
||||
id = id.replace("/", "");
|
||||
}
|
||||
JSONObject json = new JSONObject();
|
||||
if (id.startsWith("BV")) {
|
||||
json.put("bvid", id);
|
||||
} else {
|
||||
json.put("avid", id.toLowerCase().replace("av", ""));
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
BiliVideo video = new BiliVideo();
|
||||
//岚少 480
|
||||
//video.downVideo("https://www.bilibili.com/video/BV1Ps411m7pt?spm_id_from=333.999.0.0");
|
||||
//唐诱 合集
|
||||
//video.downVideo("https://www.bilibili.com/video/BV1Vv4y1K7ox?spm_id_from=444.41.top_right_bar_window_default_collection.content.click");
|
||||
//邦邦 长视频
|
||||
// video.downVideo("https://www.bilibili.com/video/BV1w5411271A?spm_id_from=444.41.list.card_archive.click");
|
||||
//LK 超清4k hdr
|
||||
//video.downVideo("https://www.bilibili.com/video/BV1uZ4y1U7h8/?spm_id_from=333.788.recommend_more_video.-1");
|
||||
// hdr
|
||||
// video.downVideo("https://www.bilibili.com/video/BV1rp4y1e745/?spm_id_from=333.788.recommend_more_video.-1");
|
||||
// 1080+ 60fps
|
||||
//video.downVideo("https://www.bilibili.com/video/BV1qF411T7Vf?spm_id_from=444.41.list.card_archive.click");
|
||||
//唐诱正片
|
||||
//video.downVideo("https://www.bilibili.com/video/BV1L44y147zR?spm_id_from=333.337.search-card.all.click");
|
||||
video.downVideo("https://www.bilibili.com/video/BV18L4y1H7rz?spm_id_from=333.999.0.0");
|
||||
// int a=16|2048;
|
||||
// System.out.println("a = " + a);
|
||||
//video.downDanmu(428855000L,976216102L,"【都市_情感】《唐可可的诱惑》第一集",1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user