bili_open.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. import { Crypto, _ } from 'assets://js/lib/cat.js';
  2. let siteKey = '';
  3. let siteType = 0;
  4. let cookie = '';
  5. let login = '';
  6. let vip = '';
  7. let extendObj = {};
  8. let vod_audio_id = {
  9. 30280: 192000,
  10. 30232: 132000,
  11. 30216: 64000,
  12. };
  13. let vod_codec = {
  14. // 13: 'AV1',
  15. 12: 'HEVC',
  16. 7: 'AVC',
  17. };
  18. const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36';
  19. async function request(reqUrl, ua) {
  20. let res = await req(reqUrl, {
  21. method: 'get',
  22. headers: ua ? ua : { 'User-Agent': UA },
  23. timeout: 60000,
  24. });
  25. return res.content;
  26. }
  27. function getHeaders() {
  28. const headers = {
  29. 'User-Agent': UA,
  30. };
  31. if (!_.isEmpty(cookie)) {
  32. headers.cookie = cookie;
  33. }
  34. return headers;
  35. }
  36. async function getCookie() {
  37. let result = await req('https://www.bilibili.com', {
  38. method: 'get',
  39. headers: { 'User-Agent': UA },
  40. timeout: 60000,
  41. });
  42. const setCookieHeaders = result.headers['set-cookie'];
  43. cookie = setCookieHeaders.map((kk) => kk.split(';')[0] + ';').join('');
  44. }
  45. async function init(cfg) {
  46. siteKey = cfg.skey;
  47. siteType = cfg.stype;
  48. let extend = cfg.ext;
  49. if (cfg.ext.hasOwnProperty('categories')) extend = cfg.ext.categories;
  50. if (cfg.ext.hasOwnProperty('cookie')) cookie = cfg.ext.cookie;
  51. if (_.isEmpty(cookie)) await getCookie();
  52. let result = JSON.parse(await request('https://api.bilibili.com/x/web-interface/nav', getHeaders()));
  53. login = result.data.isLogin;
  54. vip = result.data.hasOwnProperty('vipStatus');
  55. const ext = extend.split('#');
  56. const jsonData = [
  57. {
  58. key: 'order',
  59. name: '排序',
  60. value: [
  61. { n: '综合排序', v: '0' },
  62. { n: '最多点击', v: 'click' },
  63. { n: '最新发布', v: 'pubdate' },
  64. { n: '最多弹幕', v: 'dm' },
  65. { n: '最多收藏', v: 'stow' },
  66. ],
  67. },
  68. {
  69. key: 'duration',
  70. name: '时长',
  71. value: [
  72. { n: '全部时长', v: '0' },
  73. { n: '60分钟以上', v: '4' },
  74. { n: '30~60分钟', v: '3' },
  75. { n: '10~30分钟', v: '2' },
  76. { n: '10分钟以下', v: '1' },
  77. ],
  78. },
  79. ];
  80. const newarr = [];
  81. const d = {};
  82. for (const kk of ext) {
  83. const c = {
  84. type_name: kk,
  85. type_id: kk,
  86. land: 1,
  87. ratio: 1.78,
  88. };
  89. newarr.push(c);
  90. d[kk] = jsonData;
  91. }
  92. extendObj = {
  93. classes: newarr,
  94. filter: d,
  95. };
  96. }
  97. function home(filter) {
  98. try {
  99. const jSONObject = {
  100. class: extendObj.classes,
  101. };
  102. if (filter) {
  103. jSONObject.filters = extendObj.filter;
  104. }
  105. return JSON.stringify(jSONObject);
  106. } catch (e) {
  107. return '';
  108. }
  109. }
  110. async function homeVod() {
  111. try {
  112. const list = [];
  113. const type_id = extendObj.classes[0].type_id;
  114. const url = `https://api.bilibili.com/x/web-interface/search/type?search_type=video&keyword=${type_id}&duration=4`;
  115. const response = await request(url, getHeaders());
  116. const responseData = JSON.parse(response);
  117. const vods = responseData.data.result;
  118. for (const item of vods) {
  119. const vod = {};
  120. let imageUrl = item.pic;
  121. if (imageUrl.startsWith('//')) {
  122. imageUrl = 'https:' + imageUrl;
  123. }
  124. vod.vod_id = item.bvid;
  125. vod.vod_name = removeTags(item.title);
  126. vod.vod_pic = imageUrl;
  127. vod.vod_remarks = item.duration.split(':')[0] + '分钟';
  128. list.push(vod);
  129. }
  130. const result = { list: list };
  131. return JSON.stringify(result);
  132. } catch (e) {}
  133. }
  134. async function category(tid, page, filter, ext) {
  135. if (page < 1) page = 1;
  136. try {
  137. if (Object.keys(ext).length > 0 && ext.hasOwnProperty('tid') && ext['tid'].length > 0) {
  138. tid = ext['tid'];
  139. }
  140. let url = `https://api.bilibili.com/x/web-interface/search/type?search_type=video&keyword=${encodeURIComponent(tid)}`;
  141. if (Object.keys(ext).length > 0) {
  142. for (const k in ext) {
  143. if (k == 'tid') {
  144. continue;
  145. }
  146. url += `&${encodeURIComponent(k)}=${encodeURIComponent(ext[k])}`;
  147. }
  148. }
  149. url += `&page=${encodeURIComponent(page)}`;
  150. const response = await request(url, getHeaders());
  151. const resp = JSON.parse(response);
  152. const data = resp.data;
  153. const videos = [];
  154. const items = data.result;
  155. for (const item of items) {
  156. const video = {};
  157. let pic = item.pic;
  158. if (pic.startsWith('//')) {
  159. pic = 'https:' + pic;
  160. }
  161. video.vod_id = item.bvid;
  162. video.vod_name = removeTags(item.title);
  163. video.vod_pic = pic;
  164. video.vod_remarks = item.duration.split(':')[0] + '分钟';
  165. videos.push(video);
  166. }
  167. const result = {
  168. page: page,
  169. pagecount: data.numPages,
  170. limit: data.pagesize,
  171. total: data.numResults,
  172. list: videos,
  173. };
  174. return JSON.stringify(result);
  175. } catch (e) {}
  176. return null;
  177. }
  178. async function detail(ids) {
  179. try {
  180. const bvid = ids;
  181. const bvid2aidUrl = `https://api.bilibili.com/x/web-interface/archive/stat?bvid=${bvid}`;
  182. const bvid2aidResp = JSON.parse(await request(bvid2aidUrl, getHeaders()));
  183. const aid = bvid2aidResp.data.aid + '';
  184. const detailUrl = `https://api.bilibili.com/x/web-interface/view?aid=${aid}`;
  185. const detailData = JSON.parse(await request(detailUrl, getHeaders())).data;
  186. const video = {
  187. vod_id: bvid,
  188. vod_name: detailData.title,
  189. vod_pic: detailData.pic,
  190. type_name: detailData.tname,
  191. vod_year: '',
  192. vod_area: '',
  193. vod_remarks: `${Math.floor(detailData.duration / 60)}分钟`,
  194. vod_actor: '',
  195. vod_director: '',
  196. vod_content: detailData.desc,
  197. };
  198. const playurldata = 'https://api.bilibili.com/x/player/playurl?avid=' + aid + '&cid=' + detailData.cid + '&qn=127&fnval=4048&fourk=1';
  199. const playurldatas = JSON.parse(await request(playurldata, getHeaders()));
  200. const playurldatalist = playurldatas.data;
  201. const a = playurldatalist.accept_description;
  202. const accept_quality = playurldatalist.accept_quality;
  203. const Aq = [];
  204. const pFrom = [];
  205. for (let i = 0; i < accept_quality.length; i++) {
  206. if (!login) {
  207. if (accept_quality[i] > 32) continue;
  208. } else if (!vip && login) {
  209. if (accept_quality[i] > 80) continue;
  210. } else {
  211. if (accept_quality[i] > 32) continue;
  212. }
  213. pFrom.push(a[i]);
  214. Aq.push(accept_quality[i]);
  215. }
  216. const jSONArray = detailData.pages;
  217. const playList = [];
  218. for (let j = 0; j < jSONArray.length; j++) {
  219. const jSONObject6 = jSONArray[j];
  220. const j2 = jSONObject6.cid;
  221. const playUrl = j + '$' + aid + '+' + j2 + '+' + Aq.join(':') + '+' + pFrom.join(':');
  222. playList.push(playUrl);
  223. }
  224. video.vod_play_from = 'external$$$dash$$$mp4';
  225. video.vod_play_url = playList.join('#');
  226. video.vod_play_url = [video.vod_play_url, video.vod_play_url, video.vod_play_url].join('$$$');
  227. const list = [video];
  228. const result = { list };
  229. return JSON.stringify(result);
  230. } catch (e) {}
  231. return null;
  232. }
  233. async function play(flag, id, flags) {
  234. try {
  235. const playHeaders = { Referer: 'https://www.bilibili.com', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36' };
  236. const ids = id.split('+');
  237. const aid = ids[0];
  238. const cid = ids[1];
  239. const qualityIds = ids[2].split(':');
  240. const qualityName = ids[3].split(':');
  241. if (flag == 'dash') {
  242. // dash mpd 代理
  243. const js2Base = await js2Proxy(true, siteType, siteKey, 'dash/', {});
  244. let urls = [];
  245. for (let i = 0; i < qualityIds.length; i++) {
  246. urls.push(qualityName[i], js2Base + base64Encode(aid + '+' + cid + '+' + qualityIds[i]));
  247. }
  248. return JSON.stringify({
  249. parse: 0,
  250. url: urls,
  251. header: playHeaders,
  252. });
  253. } else if (flag == 'mp4') {
  254. // 直链
  255. let urls = [];
  256. for (let i = 0; i < qualityIds.length; i++) {
  257. const url = `https://api.bilibili.com/x/player/playurl?avid=${aid}&cid=${cid}&qn=${qualityIds[i]}&fourk=1`;
  258. const resp = JSON.parse(await request(url, getHeaders()));
  259. const data = resp.data;
  260. if (data.quality != qualityIds[i]) continue;
  261. let durl = data.durl[0].url;
  262. urls.push(qualityName[i], durl);
  263. }
  264. return JSON.stringify({
  265. parse: 0,
  266. url: urls,
  267. header: playHeaders,
  268. });
  269. } else {
  270. // 音频外挂
  271. let urls = [];
  272. let audios = [];
  273. for (let i = 0; i < qualityIds.length; i++) {
  274. const url = `https://api.bilibili.com/x/player/playurl?avid=${aid}&cid=${cid}&qn=${qualityIds[i]}&fnval=4048&fourk=1`;
  275. let resp = JSON.parse(await request(url, getHeaders()));
  276. const dash = resp.data.dash;
  277. const video = dash.video;
  278. const audio = dash.audio;
  279. for (let j = 0; j < video.length; j++) {
  280. const dashjson = video[j];
  281. if (dashjson.id == qualityIds[i]) {
  282. for (const key in vod_codec) {
  283. if (dashjson.codecid == key) {
  284. urls.push(qualityName[i] + ' ' + vod_codec[key], dashjson.baseUrl);
  285. }
  286. }
  287. }
  288. }
  289. if (audios.length == 0) {
  290. for (let j = 0; j < audio.length; j++) {
  291. const dashjson = audio[j];
  292. for (const key in vod_audio_id) {
  293. if (dashjson.id == key) {
  294. audios.push({
  295. title: _.floor(parseInt(vod_audio_id[key]) / 1024) + 'Kbps',
  296. bit: vod_audio_id[key],
  297. url: dashjson.baseUrl,
  298. });
  299. }
  300. }
  301. }
  302. audios = _.sortBy(audios, 'bit');
  303. }
  304. }
  305. return JSON.stringify({
  306. parse: 0,
  307. url: urls,
  308. extra: {
  309. audio: audios,
  310. },
  311. header: playHeaders,
  312. });
  313. }
  314. } catch (e) {}
  315. return null;
  316. }
  317. async function search(key, quick, pg) {
  318. let page = pg || 1;
  319. if (page == 0) page = 1;
  320. try {
  321. const ext = {
  322. duration: '4',
  323. };
  324. let resp = JSON.parse(await category(key, page, true, ext));
  325. const catVideos = resp.list;
  326. const pageCount = resp.pagecount;
  327. const videos = [];
  328. for (let i = 0; i < catVideos.length; ++i) {
  329. videos.push(catVideos[i]);
  330. }
  331. const result = {
  332. page: page,
  333. pagecount: pageCount,
  334. land: 1,
  335. ratio: 1.78,
  336. list: videos,
  337. };
  338. return JSON.stringify(result);
  339. } catch (e) {}
  340. return null;
  341. }
  342. async function proxy(segments, headers) {
  343. let what = segments[0];
  344. let url = base64Decode(segments[1]);
  345. if (what == 'dash') {
  346. const ids = url.split('+');
  347. const aid = ids[0];
  348. const cid = ids[1];
  349. const str5 = ids[2];
  350. const urls = `https://api.bilibili.com/x/player/playurl?avid=${aid}&cid=${cid}&qn=${str5}&fnval=4048&fourk=1`;
  351. let videoList = '';
  352. let audioList = '';
  353. let resp = JSON.parse(await request(urls, getHeaders()));
  354. const dash = resp.data.dash;
  355. const video = dash.video;
  356. const audio = dash.audio;
  357. for (let i = 0; i < video.length; i++) {
  358. // if (i > 0) continue; // 只取一个
  359. const dashjson = video[i];
  360. if (dashjson.id == str5) {
  361. videoList += getDashMedia(dashjson);
  362. }
  363. }
  364. for (let i = 0; i < audio.length; i++) {
  365. // if (i > 0) continue;
  366. const ajson = audio[i];
  367. for (const key in vod_audio_id) {
  368. if (ajson.id == key) {
  369. audioList += getDashMedia(ajson);
  370. }
  371. }
  372. }
  373. let mpd = getDash(resp, videoList, audioList);
  374. return JSON.stringify({
  375. code: 200,
  376. content: mpd,
  377. headers: {
  378. 'Content-Type': 'application/dash+xml',
  379. },
  380. });
  381. }
  382. return JSON.stringify({
  383. code: 500,
  384. content: '',
  385. });
  386. }
  387. function getDashMedia(dash) {
  388. try {
  389. let qnid = dash.id;
  390. const codecid = dash.codecid;
  391. const media_codecs = dash.codecs;
  392. const media_bandwidth = dash.bandwidth;
  393. const media_startWithSAP = dash.startWithSap;
  394. const media_mimeType = dash.mimeType;
  395. const media_BaseURL = dash.baseUrl.replace(/&/g, '&amp;');
  396. const media_SegmentBase_indexRange = dash.SegmentBase.indexRange;
  397. const media_SegmentBase_Initialization = dash.SegmentBase.Initialization;
  398. const mediaType = media_mimeType.split('/')[0];
  399. let media_type_params = '';
  400. if (mediaType == 'video') {
  401. const media_frameRate = dash.frameRate;
  402. const media_sar = dash.sar;
  403. const media_width = dash.width;
  404. const media_height = dash.height;
  405. media_type_params = `height='${media_height}' width='${media_width}' frameRate='${media_frameRate}' sar='${media_sar}'`;
  406. } else if (mediaType == 'audio') {
  407. for (const key in vod_audio_id) {
  408. if (qnid == key) {
  409. const audioSamplingRate = vod_audio_id[key];
  410. media_type_params = `numChannels='2' sampleRate='${audioSamplingRate}'`;
  411. }
  412. }
  413. }
  414. qnid += '_' + codecid;
  415. return `<AdaptationSet lang="chi">
  416. <ContentComponent contentType="${mediaType}"/>
  417. <Representation id="${qnid}" bandwidth="${media_bandwidth}" codecs="${media_codecs}" mimeType="${media_mimeType}" ${media_type_params} startWithSAP="${media_startWithSAP}">
  418. <BaseURL>${media_BaseURL}</BaseURL>
  419. <SegmentBase indexRange="${media_SegmentBase_indexRange}">
  420. <Initialization range="${media_SegmentBase_Initialization}"/>
  421. </SegmentBase>
  422. </Representation>
  423. </AdaptationSet>`;
  424. } catch (e) {
  425. // Handle exceptions here
  426. }
  427. }
  428. function getDash(ja, videoList, audioList) {
  429. const duration = ja.data.dash.duration;
  430. const minBufferTime = ja.data.dash.minBufferTime;
  431. return `<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${duration}S" minBufferTime="PT${minBufferTime}S" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011">
  432. <Period duration="PT${duration}S" start="PT0S">
  433. ${videoList}
  434. ${audioList}
  435. </Period>
  436. </MPD>`;
  437. }
  438. function base64Encode(text) {
  439. return Crypto.enc.Base64.stringify(Crypto.enc.Utf8.parse(text));
  440. }
  441. function base64Decode(text) {
  442. return Crypto.enc.Utf8.stringify(Crypto.enc.Base64.parse(text));
  443. }
  444. function removeTags(input) {
  445. return input.replace(/<[^>]*>/g, '');
  446. }
  447. export function __jsEvalReturn() {
  448. return {
  449. init: init,
  450. home: home,
  451. homeVod: homeVod,
  452. category: category,
  453. detail: detail,
  454. play: play,
  455. proxy: proxy,
  456. search: search,
  457. };
  458. }