Update bilibili by use gson

This commit is contained in:
FongMi 2023-07-07 17:27:51 +08:00
parent 7cf962d0d0
commit 650e093dd0
7 changed files with 428 additions and 301 deletions

View File

@ -44,6 +44,5 @@ dependencies {
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'cn.wanghaomiao:JsoupXpath:2.5.1'
implementation 'com.google.zxing:core:3.3.0'
//implementation 'com.orhanobut:logger:2.2.0'
implementation 'org.jsoup:jsoup:1.15.3'
}

View File

@ -26,7 +26,7 @@ public class Class {
}
public Class(String typeId, String typeName) {
this(typeId, typeName, "");
this(typeId, typeName, null);
}
public Class(String typeId, String typeName, String typeFlag) {

View File

@ -34,13 +34,13 @@ public class Result {
@SerializedName("jx")
private int jx;
@SerializedName("page")
private int page;
private Integer page;
@SerializedName("pagecount")
private int pagecount;
private Integer pagecount;
@SerializedName("limit")
private int limit;
private Integer limit;
@SerializedName("total")
private int total;
private Integer total;
public static Result objectFrom(String str) {
return new Gson().fromJson(str, Result.class);

View File

@ -0,0 +1,131 @@
package com.github.catvod.bean.bili;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import java.util.Collections;
import java.util.List;
public class Dash {
@SerializedName("duration")
private String duration;
@SerializedName("minBufferTime")
private String minBufferTime;
@SerializedName("video")
private List<Media> video;
@SerializedName("audio")
private List<Media> audio;
public static Dash objectFrom(String str) {
return new Gson().fromJson(str, Dash.class);
}
public String getDuration() {
return duration == null ? "0" : duration;
}
public String getMinBufferTime() {
return minBufferTime == null ? "0" : minBufferTime;
}
public List<Media> getVideo() {
return video == null ? Collections.emptyList() : video;
}
public List<Media> getAudio() {
return audio == null ? Collections.emptyList() : audio;
}
public static class Media {
@SerializedName("id")
private String id;
@SerializedName("baseUrl")
private String baseUrl;
@SerializedName("bandwidth")
private String bandwidth;
@SerializedName("mimeType")
private String mimeType;
@SerializedName("codecs")
private String codecs;
@SerializedName("width")
private String width;
@SerializedName("height")
private String height;
@SerializedName("frameRate")
private String frameRate;
@SerializedName("sar")
private String sar;
@SerializedName("startWithSap")
private String startWithSap;
@SerializedName("SegmentBase")
private SegmentBase segmentBase;
@SerializedName("codecid")
private String codecid;
public String getId() {
return id;
}
public String getBaseUrl() {
return baseUrl;
}
public String getBandWidth() {
return bandwidth;
}
public String getMimeType() {
return mimeType;
}
public String getCodecs() {
return codecs;
}
public String getWidth() {
return width;
}
public String getHeight() {
return height;
}
public String getFrameRate() {
return frameRate;
}
public String getSar() {
return sar;
}
public String getStartWithSap() {
return startWithSap;
}
public SegmentBase getSegmentBase() {
return segmentBase;
}
public String getCodecId() {
return codecid;
}
}
public static class SegmentBase {
@SerializedName("Initialization")
private String initialization;
@SerializedName("indexRange")
private String indexRange;
public String getInitialization() {
return initialization;
}
public String getIndexRange() {
return indexRange;
}
}
}

View File

@ -0,0 +1,167 @@
package com.github.catvod.bean.bili;
import android.text.TextUtils;
import com.github.catvod.bean.Vod;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import org.jsoup.Jsoup;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
public class Resp {
@SerializedName("code")
private Integer code;
@SerializedName("message")
private String message;
@SerializedName("data")
private Data data;
public static Resp objectFrom(String str) {
return new Gson().fromJson(str, Resp.class);
}
public Data getData() {
return data == null ? new Data() : data;
}
public static class Data {
@SerializedName("result")
private JsonElement result;
@SerializedName("aid")
private String aid;
@SerializedName("cid")
private String cid;
@SerializedName("title")
private String title;
@SerializedName("tname")
private String tname;
@SerializedName("pic")
private String pic;
@SerializedName("duration")
private Long duration;
@SerializedName("desc")
private String desc;
@SerializedName("accept_description")
private List<String> acceptDescription;
@SerializedName("accept_quality")
private List<String> acceptQuality;
@SerializedName("pages")
private List<Page> pages;
@SerializedName("dash")
private Dash dash;
public JsonElement getResult() {
return result;
}
public String getAid() {
return TextUtils.isEmpty(aid) ? "" : aid;
}
public String getCid() {
return TextUtils.isEmpty(cid) ? "" : cid;
}
public String getTitle() {
return TextUtils.isEmpty(title) ? "" : title;
}
public String getType() {
return TextUtils.isEmpty(tname) ? "" : tname;
}
public String getPic() {
return TextUtils.isEmpty(pic) ? "" : pic;
}
public Long getDuration() {
return duration == null ? 0 : duration;
}
public String getDesc() {
return TextUtils.isEmpty(desc) ? "" : desc;
}
public List<String> getAcceptDescription() {
return acceptDescription == null ? Collections.emptyList() : acceptDescription;
}
public List<String> getAcceptQuality() {
return acceptQuality == null ? Collections.emptyList() : acceptQuality;
}
public List<Page> getPages() {
return pages == null ? Collections.emptyList() : pages;
}
public Dash getDash() {
return dash == null ? new Dash() : dash;
}
}
public static class Page {
@SerializedName("cid")
private String cid;
@SerializedName("part")
private String part;
public String getCid() {
return TextUtils.isEmpty(cid) ? "" : cid;
}
public String getPart() {
return TextUtils.isEmpty(part) ? "" : part;
}
}
public static class Result {
@SerializedName("bvid")
private String bvid;
@SerializedName("title")
private String title;
@SerializedName("pic")
private String pic;
@SerializedName("duration")
private String duration;
public static List<Result> arrayFrom(JsonElement str) {
Type listType = new TypeToken<List<Result>>() {}.getType();
return new Gson().fromJson(str, listType);
}
public String getBvId() {
return TextUtils.isEmpty(bvid) ? "" : bvid;
}
public String getTitle() {
return TextUtils.isEmpty(title) ? "" : title;
}
public String getDuration() {
return TextUtils.isEmpty(duration) ? "" : duration;
}
public String getPic() {
return TextUtils.isEmpty(pic) ? "" : pic;
}
public Vod getVod() {
Vod vod = new Vod();
vod.setVodId(getBvId());
vod.setVodName(Jsoup.parse(getTitle()).text());
vod.setVodRemarks(getDuration().split(":")[0] + "分鐘");
vod.setVodPic(getPic().startsWith("//") ? "https:" + getPic() : getPic());
return vod;
}
}
}

View File

@ -15,7 +15,11 @@ public class MainActivity extends Activity {
//Logger.addLogAdapter(new AndroidLogAdapter());
Init.init(getApplicationContext());
new Thread(() -> {
try {
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}

View File

@ -1,100 +1,65 @@
package com.github.catvod.spider;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Base64;
import android.view.Gravity;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.github.catvod.bean.Class;
import com.github.catvod.bean.Filter;
import com.github.catvod.bean.Result;
import com.github.catvod.bean.Vod;
import com.github.catvod.bean.bili.Dash;
import com.github.catvod.bean.bili.Resp;
import com.github.catvod.crawler.Spider;
import com.github.catvod.net.OkHttp;
import com.github.catvod.utils.Prefers;
import com.github.catvod.utils.QRCode;
import com.github.catvod.utils.Utils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.Locale;
import java.util.Map;
/**
* @author ColaMint & FongMi & 唐三
*/
public class Bili extends Spider {
private ScheduledExecutorService service;
private HashMap<String, String> header;
private static JSONObject vod_audio_id = new JSONObject();
private AlertDialog dialog;
private JSONObject ext;
private static final String COOKIE = "buvid3=84B0395D-C9F2-C490-E92E-A09AB48FE26E71636infoc";
private Map<String, String> header;
private Map<String, String> audios;
private String extend;
private boolean login;
private static int vip = 0;
private String getCookie() {
String cache = Prefers.getString("BiliCookie");
if (!TextUtils.isEmpty(cache)) return cache;
if (ext.optString("cookie").startsWith("http"))
return OkHttp.string(ext.optString("cookie")).replace("\n", "").trim();
return ext.optString("cookie", "buvid3=84B0395D-C9F2-C490-E92E-A09AB48FE26E71636infoc");
}
private void setHeader() {
header.put("cookie", getCookie());
header = new HashMap<>();
header.put("cookie", COOKIE);
header.put("Referer", "https://www.bilibili.com");
header.put("User-Agent", Utils.CHROME);
}
private void generate() throws Exception {
ext = new JSONObject();
JSONArray classes = new JSONArray();
JSONObject filters = new JSONObject();
ext.put("classes", classes);
ext.put("filter", filters);
JSONArray filter = new JSONArray("[{\"key\":\"order\",\"name\":\"排序\",\"value\":[{\"n\":\"综合排序\",\"v\":\"0\"},{\"n\":\"最多点击\",\"v\":\"click\"},{\"n\":\"最新发布\",\"v\":\"pubdate\"},{\"n\":\"最多弹幕\",\"v\":\"dm\"},{\"n\":\"最多收藏\",\"v\":\"stow\"}]},{\"key\":\"duration\",\"name\":\"时长\",\"value\":[{\"n\":\"全部时长\",\"v\":\"0\"},{\"n\":\"60分钟以上\",\"v\":\"4\"},{\"n\":\"30~60分钟\",\"v\":\"3\"},{\"n\":\"10~30分钟\",\"v\":\"2\"},{\"n\":\"10分钟以下\",\"v\":\"1\"}]}]");
String[] types = extend.split("#");
for (String type : types) {
JSONObject c = new JSONObject();
c.put("type_name", type);
c.put("type_id", type);
filters.put(type, filter);
classes.put(c);
}
private void setAudio() {
audios = new HashMap<>();
audios.put("30280", "192000");
audios.put("30232", "132000");
audios.put("30216", "64000");
}
private void fetchRule() throws Exception {
if (header.containsKey("cookie") && header.get("cookie").length() > 0) return;
if (extend.startsWith("http")) ext = new JSONObject(extend = OkHttp.string(extend));
else generate();
setHeader();
private List<Filter> getFilter() {
List<Filter> items = new ArrayList<>();
items.add(new Filter("order", "排序", Arrays.asList(new Filter.Value("預設", "totalrank"), new Filter.Value("最多點擊", "click"), new Filter.Value("最新發布", "pubdate"), new Filter.Value("最多彈幕", "dm"), new Filter.Value("最多收藏", "stow"))));
items.add(new Filter("duration", "時長", Arrays.asList(new Filter.Value("全部時長", "0"), new Filter.Value("60分鐘以上", "4"), new Filter.Value("30~60分鐘", "3"), new Filter.Value("10~30分鐘", "2"), new Filter.Value("10分鐘以下", "1"))));
return items;
}
@Override
public void init(Context context, String extend) {
try {
this.extend = extend;
this.header = new HashMap<>();
fetchRule();
vod_audio_id.put("30280", "192000");
vod_audio_id.put("30232", "132000");
vod_audio_id.put("30216", "64000");
setHeader();
setAudio();
} catch (Exception e) {
e.printStackTrace();
}
@ -102,115 +67,64 @@ public class Bili extends Spider {
@Override
public String homeContent(boolean filter) throws Exception {
fetchRule();
return Result.string(Class.arrayFrom(ext.getJSONArray("classes").toString()), ext.getJSONObject("filter"));
List<Class> classes = new ArrayList<>();
LinkedHashMap<String, List<Filter>> filters = new LinkedHashMap<>();
String[] types = extend.split("#");
for (String type : types) {
classes.add(new Class(type));
filters.put(type, getFilter());
}
return Result.string(classes, filters);
}
@Override
public String homeVideoContent() throws Exception {
fetchRule();
return categoryContent(ext.optJSONArray("classes").optJSONObject(0).optString("type_id"), "1", true, new HashMap<>());
return categoryContent(extend.split("#")[0], "1", true, new HashMap<>());
}
@Override
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
String duration = extend.containsKey("duration") ? extend.get("duration") : "4";
if (extend.containsKey("tid")) tid = tid + " " + extend.get("tid");
String url = "https://api.bilibili.com/x/web-interface/search/type?search_type=video&keyword=" + URLEncoder.encode(tid) + "&duration=" + duration + "&page=" + pg;
JSONObject resp = new JSONObject(OkHttp.string(url, header));
JSONArray result = resp.getJSONObject("data").getJSONArray("result");
String order = extend.containsKey("order") ? extend.get("order") : "totalrank";
String duration = extend.containsKey("duration") ? extend.get("duration") : "0";
String api = "https://api.bilibili.com/x/web-interface/search/type?search_type=video&keyword=" + URLEncoder.encode(tid) + "&order=" + order + "&duration=" + duration + "&page=" + pg;
String json = OkHttp.string(api, header);
Resp resp = Resp.objectFrom(json);
List<Vod> list = new ArrayList<>();
for (int i = 0; i < result.length(); ++i) {
JSONObject object = result.getJSONObject(i);
String pic = object.getString("pic");
Vod vod = new Vod();
vod.setVodId(object.getString("bvid"));
vod.setVodName(Jsoup.parse(object.getString("title")).text());
vod.setVodRemarks(object.getString("duration").split(":")[0] + "分鐘");
vod.setVodPic(pic.startsWith("//") ? "https:" + pic : pic);
list.add(vod);
}
for (Resp.Result item : Resp.Result.arrayFrom(resp.getData().getResult())) list.add(item.getVod());
return Result.string(list);
}
@Override
public String detailContent(List<String> ids) throws Exception {
if (!login) checkLogin();
String bvid = ids.get(0);
String bvid2aidUrl = "https://api.bilibili.com/x/web-interface/archive/stat?bvid=" + bvid;
JSONObject bvid2aidResp = new JSONObject(OkHttp.string(bvid2aidUrl, header));
String aid = bvid2aidResp.getJSONObject("data").getLong("aid") + "";
String detailUrl = "https://api.bilibili.com/x/web-interface/view?aid=" + aid;
JSONObject detailResp = new JSONObject(OkHttp.string(detailUrl, header));
JSONObject detailData = detailResp.getJSONObject("data");
String defaultQn = "120";
/*
6 240P 极速 仅mp4方式支持
16 360P 流畅
32 480P 清晰
64 720P 高清 web端默认值
B站前端需要登录才能选择但是直接发送请求可以不登录就拿到720P的取流地址
无720P时则为720P60
74 720P60 高帧率 需要认证登录账号
80 1080P 高清 TV端与APP端默认值
需要认证登录账号
112 1080P+ 高码率 大多情况需求认证大会员账号
116 1080P60 高帧率 大多情况需求认证大会员账号
120 4K 超清 需要fnval&128=128且fourk=1
大多情况需求认证大会员账号
125 HDR 真彩色 仅支持dash方式
需要fnval&64=64
大多情况需求认证大会员账号
126 杜比视界 仅支持dash方式
需要fnval&512=512
大多情况需求认证大会员账号
127 8K 超高清 仅支持dash方式
需要fnval&1024=1024
大多情况需求认证大会员账号
*/
String playurldata = "https://api.bilibili.com/x/player/playurl?avid=" + aid + "&cid=" + detailData.getString("cid") + "&qn=" + defaultQn + "&fnval=4048&fourk=1";
JSONObject response = new JSONObject(OkHttp.string(playurldata, header));
JSONObject data = response.getJSONObject("data");
JSONArray acceptDescription = data.getJSONArray("accept_description");
JSONArray acceptQuality = data.getJSONArray("accept_quality");
ArrayList<String> tempQuality = new ArrayList<>();
ArrayList<String> playFrom = new ArrayList<>();
for (int i = 0; i < acceptQuality.length(); i++) {
if (!login) { // Check login and set up quality
if (acceptQuality.optInt(i) > 32)
continue;
} else if (vip == 0) { // Check vip and set up quality
if (acceptQuality.optInt(i) > 80)
continue;
}
playFrom.add(acceptDescription.getString(i));
tempQuality.add(acceptQuality.getString(i));
}
StringBuilder plist = new StringBuilder();
for (String Quality : tempQuality) {
ArrayList<String> playList = new ArrayList<>();
JSONArray pages = detailData.getJSONArray("pages");
int length = pages.length();
for (int i = 0; i < length; i++) {
JSONObject page = pages.getJSONObject(i);
long cid = page.getLong("cid");
String playUrl = i + "$" + aid + "+" + cid + "+" + Quality;
playList.add(playUrl);
}
plist.append(TextUtils.join("#", playList)).append("$$$");
}
String id = ids.get(0);
String api = "https://api.bilibili.com/x/web-interface/archive/stat?bvid=" + id;
String json = OkHttp.string(api, header);
Resp resp = Resp.objectFrom(json);
String aid = resp.getData().getAid();
api = "https://api.bilibili.com/x/web-interface/view?aid=" + aid;
json = OkHttp.string(api, header);
Resp.Data detail = Resp.objectFrom(json).getData();
Vod vod = new Vod();
vod.setVodId(bvid);
vod.setVodName(detailData.getString("title"));
vod.setVodPic(detailData.getString("pic"));
vod.setTypeName(detailData.getString("tname"));
vod.setVodRemarks(detailData.getLong("duration") / 60 + "分鐘");
vod.setVodContent(detailData.getString("desc"));
vod.setVodPlayFrom(TextUtils.join("$$$", playFrom));
vod.setVodPlayUrl(plist.toString());
vod.setVodId(id);
vod.setVodPic(detail.getPic());
vod.setVodName(detail.getTitle());
vod.setTypeName(detail.getType());
vod.setVodContent(detail.getDesc());
vod.setVodRemarks(detail.getDuration() / 60 + "分鐘");
api = "https://api.bilibili.com/x/player/playurl?avid=" + aid + "&cid=" + detail.getCid() + "&qn=120&fnval=4048&fourk=1";
json = OkHttp.string(api, header);
Resp.Data play = Resp.objectFrom(json).getData();
List<String> playList = new ArrayList<>();
for (String quality : play.getAcceptQuality()) {
List<String> vodItems = new ArrayList<>();
for (Resp.Page page : detail.getPages()) vodItems.add(page.getPart() + "$" + aid + "+" + page.getCid() + "+" + quality);
playList.add(TextUtils.join("#", vodItems));
}
vod.setVodPlayFrom(TextUtils.join("$$$", play.getAcceptDescription()));
vod.setVodPlayUrl(TextUtils.join("$$$", playList));
return Result.string(vod);
}
@ -224,164 +138,76 @@ public class Bili extends Spider {
String[] ids = id.split("\\+");
String aid = ids[0];
String cid = ids[1];
String Qn = ids[2];
String urls = "https://api.bilibili.com/x/player/playurl?avid=" + aid + "&cid=" + cid + "&qn=" + Qn + "&fnval=4048&fourk=1";
// 视频链接时效性限制 2 小时 得重新请求暂不处理1
String qn = ids[2];
String api = "https://api.bilibili.com/x/player/playurl?avid=" + aid + "&cid=" + cid + "&qn=" + qn + "&fnval=4048&fourk=1";
String json = OkHttp.string(api, header);
Resp resp = Resp.objectFrom(json);
Dash dash = resp.getData().getDash();
String videoList = "", audioList = "";
JSONObject resp = new JSONObject(OkHttp.string(urls, header)).optJSONObject("data").optJSONObject("dash");
JSONArray video = resp.optJSONArray("video");
JSONArray audio = resp.optJSONArray("audio");
for (int i = 0; i < video.length(); i++) {
JSONObject videoDash = video.getJSONObject(i);
if (videoDash.getString("id").equals(Qn)) {
videoList = getDashMedia(videoDash);
for (Dash.Media video : dash.getVideo()) {
if (video.getId().equals(qn)) {
videoList = getMedia(video);
break;
}
}
for (int i = 0; i < audio.length(); i++) {
JSONObject audioDash = audio.getJSONObject(i);
Iterator<String> keys = vod_audio_id.keys();
while (keys.hasNext()) {
String key = keys.next();
if (audioDash.getString("id").equals(key)) {
audioList = getDashMedia(audioDash);
for (Dash.Media audio : dash.getAudio()) {
for (String key : audios.keySet()) {
if (audio.getId().equals(key)) {
audioList = getMedia(audio);
break;
}
}
}
String mpd = getDash(resp, videoList, audioList);
String dashUrl = "data:application/dash+xml;base64," + Base64.encodeToString(mpd.getBytes(), 0);
return Result.get().url(dashUrl).header(header).string();
String mpd = getMpd(dash, videoList, audioList);
String url = "data:application/dash+xml;base64," + Base64.encodeToString(mpd.getBytes(), 0);
return Result.get().url(url).header(header).string();
}
public static String getDashMedia(JSONObject dash) throws Exception {
JSONObject vod_audio_id = new JSONObject();
vod_audio_id.put("30280", "192000");
vod_audio_id.put("30232", "132000");
vod_audio_id.put("30216", "64000");
String qnid = dash.getString("id");
int codecid = dash.getInt("codecid");
String media_codecs = dash.getString("codecs");
Integer media_bandwidth = dash.getInt("bandwidth");
String media_startWithSAP = dash.getString("startWithSap");
String media_mimeType = dash.getString("mimeType");
String media_BaseURL = dash.getString("baseUrl").replace("&", "&amp;");
String media_SegmentBase_indexRange = dash.getJSONObject("SegmentBase").getString("indexRange");
String media_SegmentBase_Initialization = dash.getJSONObject("SegmentBase").getString("Initialization");
String mediaType = media_mimeType.split("/")[0];
String media_type_params = "";
if (mediaType.equals("video")) {
Integer media_frameRate = dash.getInt("frameRate");
String media_sar = dash.getString("sar");
Integer media_width = dash.getInt("width");
Integer media_height = dash.getInt("height");
media_type_params = String.format("height='%d' width='%d' frameRate='%d' sar='%s'", media_height, media_width, media_frameRate, media_sar);
} else if (mediaType.equals("audio")) {
String audioSamplingRate = vod_audio_id.getString(qnid);
media_type_params = String.format("numChannels='2' sampleRate='%s'", audioSamplingRate);
private String getMedia(Dash.Media media) {
if (media.getMimeType().startsWith("video")) {
return getAdaptationSet(media, String.format(Locale.getDefault(), "height='%s' width='%s' frameRate='%s' sar='%s'", media.getHeight(), media.getWidth(), media.getFrameRate(), media.getSar()));
} else if (media.getMimeType().startsWith("audio")) {
return getAdaptationSet(media, String.format("numChannels='2' sampleRate='%s'", audios.get(media.getId())));
} else {
return "";
}
}
qnid += "_" + codecid;
return String.format(
private String getAdaptationSet(Dash.Media media, String params) {
String id = media.getId() + "_" + media.getCodecId();
String type = media.getMimeType().split("/")[0];
String baseUrl = media.getBaseUrl().replace("&", "&amp;");
return String.format(Locale.getDefault(),
"<AdaptationSet>\n" +
" <ContentComponent contentType=\"%s\"/>\n" +
" <Representation id=\"%s\" bandwidth=\"%d\" codecs=\"%s\" mimeType=\"%s\" %s startWithSAP=\"%b\">\n" +
" <BaseURL>%s</BaseURL>\n" +
" <SegmentBase indexRange=\"%s\">\n" +
" <Initialization range=\"%s\"/>\n" +
" </SegmentBase>\n" +
" </Representation>\n" +
" </AdaptationSet>",
mediaType, qnid, media_bandwidth, media_codecs, media_mimeType, media_type_params, media_startWithSAP, media_BaseURL,
media_SegmentBase_indexRange, media_SegmentBase_Initialization);
"<ContentComponent contentType=\"%s\"/>\n" +
"<Representation id=\"%s\" bandwidth=\"%s\" codecs=\"%s\" mimeType=\"%s\" %s startWithSAP=\"%s\">\n" +
"<BaseURL>%s</BaseURL>\n" +
"<SegmentBase indexRange=\"%s\">\n" +
"<Initialization range=\"%s\"/>\n" +
"</SegmentBase>\n" +
"</Representation>\n" +
"</AdaptationSet>",
type,
id, media.getBandWidth(), media.getCodecs(), media.getMimeType(), params, media.getStartWithSap(),
baseUrl,
media.getSegmentBase().getIndexRange(),
media.getSegmentBase().getInitialization());
}
public static String getDash(JSONObject ja, String videoList, String audioList) throws Exception {
Integer duration = ja.getInt("duration");
Integer minBufferTime = ja.getInt("minBufferTime");
return String.format(
"<MPD xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"urn:mpeg:dash:schema:mpd:2011\" xsi:schemaLocation=\"urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd\" type=\"static\" mediaPresentationDuration=\"PT%dS\" minBufferTime=\"PT%dS\" profiles=\"urn:mpeg:dash:profile:isoff-on-demand:2011\">\n" +
" <Period duration=\"PT%dS\" start=\"PT0S\">\n" +
" %s\n" +
" %s\n" +
" </Period>\n" +
private String getMpd(Dash dash, String videoList, String audioList) {
return String.format(Locale.getDefault(),
"<MPD xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"urn:mpeg:dash:schema:mpd:2011\" xsi:schemaLocation=\"urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd\" type=\"static\" mediaPresentationDuration=\"PT%sS\" minBufferTime=\"PT%sS\" profiles=\"urn:mpeg:dash:profile:isoff-on-demand:2011\">\n" +
"<Period duration=\"PT%sS\" start=\"PT0S\">\n" +
"%s\n" +
"%s\n" +
"</Period>\n" +
"</MPD>",
duration, minBufferTime, duration, videoList, audioList);
dash.getDuration(), dash.getMinBufferTime(),
dash.getDuration(),
videoList,
audioList);
}
private void checkLogin() throws Exception {
login = new JSONObject(OkHttp.string("https://api.bilibili.com/x/web-interface/nav", header)).getJSONObject("data").getBoolean("isLogin");
boolean qrCode = Prefers.getBoolean("BiliQRCode", true);
if (!login && qrCode) getQRCode();
}
private void getQRCode() {
try {
String json = OkHttp.string("https://passport.bilibili.com/x/passport-login/web/qrcode/generate?source=main-mini", null);
JSONObject data = new JSONObject(json).getJSONObject("data");
Init.run(() -> showQRCode(data));
} catch (Exception ignored) {
}
}
private void showQRCode(JSONObject data) {
try {
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(Utils.dp2px(240), Utils.dp2px(240));
ImageView image = new ImageView(Init.context());
image.setScaleType(ImageView.ScaleType.CENTER_CROP);
image.setImageBitmap(QRCode.getBitmap(data.getString("url"), 240, 2));
FrameLayout frame = new FrameLayout(Init.context());
params.gravity = Gravity.CENTER;
frame.addView(image, params);
dialog = new AlertDialog.Builder(Init.getActivity()).setView(frame).setOnDismissListener(this::dismiss).show();
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
Init.execute(() -> startService(data));
Init.show("請使用 BiliBili App 掃描二維碼");
} catch (Exception ignored) {
}
}
private void startService(JSONObject data) {
service = Executors.newScheduledThreadPool(1);
service.scheduleAtFixedRate(() -> {
try {
String qr = data.getString("qrcode_key");
String url = "https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key=" + qr + "&source=main_mini";
String json = OkHttp.string(url, header);
url = new JSONObject(json).getJSONObject("data").getString("url");
if (!TextUtils.isEmpty(url)) setCookie(url);
} catch (Exception ignored) {
}
}, 1, 1, TimeUnit.SECONDS);
}
private void setCookie(String url) {
StringBuilder cookie = new StringBuilder();
String[] aa = Uri.parse(url).getQuery().split("&");
for (String a : aa) cookie.append(a).append(";");
Prefers.put("BiliCookie", cookie.toString());
Init.show("請重新進入播放頁");
stopService();
}
private void stopService() {
if (service != null) service.shutdownNow();
Init.run(this::dismiss);
}
private void dismiss(DialogInterface dialog) {
Prefers.put("BiliQRCode", false);
stopService();
}
private void dismiss() {
try {
if (dialog != null) dialog.dismiss();
} catch (Exception ignored) {
}
}
}
}