index.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const axios_1 = require("axios");
  4. const dayjs = require("dayjs");
  5. const he = require("he");
  6. const CryptoJs = require("crypto-js");
  7. const headers = {
  8. "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.63",
  9. accept: "*/*",
  10. "accept-encoding": "gzip, deflate, br",
  11. "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
  12. };
  13. let cookie;
  14. async function getCid(bvid, aid) {
  15. const params = bvid
  16. ? {
  17. bvid: bvid,
  18. }
  19. : {
  20. aid: aid,
  21. };
  22. const cidRes = (await axios_1.default.get("https://api.bilibili.com/x/web-interface/view?%s", {
  23. headers: headers,
  24. params: params,
  25. })).data;
  26. return cidRes;
  27. }
  28. function durationToSec(duration) {
  29. if (typeof duration === "number") {
  30. return duration;
  31. }
  32. if (typeof duration === "string") {
  33. var dur = duration.split(":");
  34. return dur.reduce(function (prev, curr) {
  35. return 60 * prev + +curr;
  36. }, 0);
  37. }
  38. return 0;
  39. }
  40. const searchHeaders = {
  41. "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.63",
  42. accept: "application/json, text/plain, */*",
  43. "accept-encoding": "gzip, deflate, br",
  44. origin: "https://search.bilibili.com",
  45. "sec-fetch-site": "same-site",
  46. "sec-fetch-mode": "cors",
  47. "sec-fetch-dest": "empty",
  48. referer: "https://search.bilibili.com/",
  49. "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
  50. };
  51. async function getCookie() {
  52. if (!cookie) {
  53. cookie = (await axios_1.default.get("https://api.bilibili.com/x/frontend/finger/spi", {
  54. headers: {
  55. "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/114.0.0.0",
  56. },
  57. })).data.data;
  58. }
  59. }
  60. const pageSize = 20;
  61. async function searchBase(keyword, page, searchType) {
  62. await getCookie();
  63. const params = {
  64. context: "",
  65. page: page,
  66. order: "",
  67. page_size: pageSize,
  68. keyword: keyword,
  69. duration: "",
  70. tids_1: "",
  71. tids_2: "",
  72. __refresh__: true,
  73. _extra: "",
  74. highlight: 1,
  75. single_column: 0,
  76. platform: "pc",
  77. from_source: "",
  78. search_type: searchType,
  79. dynamic_offset: 0,
  80. };
  81. const res = (await axios_1.default.get("https://api.bilibili.com/x/web-interface/search/type", {
  82. headers: Object.assign(Object.assign({}, searchHeaders), { cookie: `buvid3=${cookie.b_3};buvid4=${cookie.b_4}` }),
  83. params: params,
  84. })).data;
  85. return res.data;
  86. }
  87. async function getFavoriteList(id) {
  88. const result = [];
  89. const pageSize = 20;
  90. let page = 1;
  91. while (true) {
  92. try {
  93. const { data: { data: { medias, has_more }, }, } = await axios_1.default.get("https://api.bilibili.com/x/v3/fav/resource/list", {
  94. params: {
  95. media_id: id,
  96. platform: "web",
  97. ps: pageSize,
  98. pn: page,
  99. },
  100. });
  101. result.push(...medias);
  102. if (!has_more) {
  103. break;
  104. }
  105. page += 1;
  106. }
  107. catch (error) {
  108. console.warn(error);
  109. break;
  110. }
  111. }
  112. return result;
  113. }
  114. function formatMedia(result) {
  115. var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
  116. const title = he.decode((_b = (_a = result.title) === null || _a === void 0 ? void 0 : _a.replace(/(\<em(.*?)\>)|(\<\/em\>)/g, "")) !== null && _b !== void 0 ? _b : "");
  117. return {
  118. id: (_d = (_c = result.cid) !== null && _c !== void 0 ? _c : result.bvid) !== null && _d !== void 0 ? _d : result.aid,
  119. aid: result.aid,
  120. bvid: result.bvid,
  121. artist: (_e = result.author) !== null && _e !== void 0 ? _e : (_f = result.owner) === null || _f === void 0 ? void 0 : _f.name,
  122. title,
  123. alias: (_g = title.match(/《(.+?)》/)) === null || _g === void 0 ? void 0 : _g[1],
  124. album: (_h = result.bvid) !== null && _h !== void 0 ? _h : result.aid,
  125. artwork: ((_j = result.pic) === null || _j === void 0 ? void 0 : _j.startsWith("//"))
  126. ? "http:".concat(result.pic)
  127. : result.pic,
  128. duration: durationToSec(result.duration),
  129. tags: (_k = result.tag) === null || _k === void 0 ? void 0 : _k.split(","),
  130. date: dayjs.unix(result.pubdate || result.created).format("YYYY-MM-DD"),
  131. };
  132. }
  133. async function searchAlbum(keyword, page) {
  134. const resultData = await searchBase(keyword, page, "video");
  135. const albums = resultData.result.map(formatMedia);
  136. return {
  137. isEnd: resultData.numResults <= page * pageSize,
  138. data: albums,
  139. };
  140. }
  141. async function searchArtist(keyword, page) {
  142. const resultData = await searchBase(keyword, page, "bili_user");
  143. const artists = resultData.result.map((result) => {
  144. var _a;
  145. return ({
  146. name: result.uname,
  147. id: result.mid,
  148. fans: result.fans,
  149. description: result.usign,
  150. avatar: ((_a = result.upic) === null || _a === void 0 ? void 0 : _a.startsWith("//"))
  151. ? `https://${result.upic}`
  152. : result.upic,
  153. worksNum: result.videos,
  154. });
  155. });
  156. return {
  157. isEnd: resultData.numResults <= page * pageSize,
  158. data: artists,
  159. };
  160. }
  161. function getMixinKey(e) {
  162. var t = [];
  163. return ([
  164. 46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5,
  165. 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55,
  166. 40, 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57,
  167. 62, 11, 36, 20, 34, 44, 52,
  168. ].forEach(function (r) {
  169. e.charAt(r) && t.push(e.charAt(r));
  170. }),
  171. t.join("").slice(0, 32));
  172. }
  173. function getRid(params) {
  174. const npi = "7cd084941338484aae1ad9425b84077c4932caff0ff746eab6f01bf08b70ac45";
  175. const o = getMixinKey(npi);
  176. const l = Object.keys(params).sort();
  177. let c = [];
  178. for (let d = 0, u = /[!'\(\)*]/g; d < l.length; ++d) {
  179. let [h, p] = [l[d], params[l[d]]];
  180. p && "string" == typeof p && (p = p.replace(u, "")),
  181. null != p &&
  182. c.push("".concat(encodeURIComponent(h), "=").concat(encodeURIComponent(p)));
  183. }
  184. const f = c.join("&");
  185. const w_rid = CryptoJs.MD5(f + o).toString();
  186. return w_rid;
  187. }
  188. async function getArtistWorks(artistItem, page, type) {
  189. const queryHeaders = {
  190. "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.63",
  191. accept: "application/json, text/plain, */*",
  192. "accept-encoding": "gzip, deflate, br",
  193. origin: "https://space.bilibili.com",
  194. "sec-fetch-site": "same-site",
  195. "sec-fetch-mode": "cors",
  196. "sec-fetch-dest": "empty",
  197. referer: `https://space.bilibili.com/${artistItem.id}/video`,
  198. };
  199. await getCookie();
  200. const now = Math.round(Date.now() / 1e3);
  201. const params = {
  202. mid: artistItem.id,
  203. ps: 30,
  204. tid: 0,
  205. pn: page,
  206. web_location: 1550101,
  207. order_avoided: true,
  208. order: "pubdate",
  209. keyword: "",
  210. platform: "web",
  211. dm_img_list: "[]",
  212. dm_img_str: "V2ViR0wgMS4wIChPcGVuR0wgRVMgMi4wIENocm9taXVtKQ",
  213. dm_cover_img_str: "QU5HTEUgKE5WSURJQSwgTlZJRElBIEdlRm9yY2UgR1RYIDE2NTAgKDB4MDAwMDFGOTEpIERpcmVjdDNEMTEgdnNfNV8wIHBzXzVfMCwgRDNEMTEpR29vZ2xlIEluYy4gKE5WSURJQS",
  214. dm_img_inter: '{"ds":[],"wh":[0,0,0],"of":[0,0,0]}',
  215. wts: now.toString(),
  216. };
  217. const w_rid = getRid(params);
  218. const res = (await axios_1.default.get("https://api.bilibili.com/x/space/wbi/arc/search", {
  219. headers: Object.assign(Object.assign({}, queryHeaders), { cookie: `buvid3=${cookie.b_3};buvid4=${cookie.b_4}` }),
  220. params: Object.assign(Object.assign({}, params), { w_rid }),
  221. })).data;
  222. const resultData = res.data;
  223. const albums = resultData.list.vlist.map(formatMedia);
  224. return {
  225. isEnd: resultData.page.pn * resultData.page.ps >= resultData.page.count,
  226. data: albums,
  227. };
  228. }
  229. async function getMediaSource(musicItem, quality) {
  230. var _a;
  231. let cid = musicItem.cid;
  232. if (!cid) {
  233. cid = (await getCid(musicItem.bvid, musicItem.aid)).data.cid;
  234. }
  235. const _params = musicItem.bvid
  236. ? {
  237. bvid: musicItem.bvid,
  238. }
  239. : {
  240. aid: musicItem.aid,
  241. };
  242. const res = (await axios_1.default.get("https://api.bilibili.com/x/player/playurl", {
  243. headers: headers,
  244. params: Object.assign(Object.assign({}, _params), { cid: cid, fnval: 16 }),
  245. })).data;
  246. let url;
  247. if (res.data.dash) {
  248. const audios = res.data.dash.audio;
  249. audios.sort((a, b) => a.bandwidth - b.bandwidth);
  250. switch (quality) {
  251. case "low":
  252. url = audios[0].baseUrl;
  253. break;
  254. case "standard":
  255. url = audios[1].baseUrl;
  256. break;
  257. case "high":
  258. url = audios[2].baseUrl;
  259. break;
  260. case "super":
  261. url = audios[3].baseUrl;
  262. break;
  263. }
  264. }
  265. else {
  266. url = res.data.durl[0].url;
  267. }
  268. const hostUrl = url.substring(url.indexOf("/") + 2);
  269. const _headers = {
  270. "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.63",
  271. accept: "*/*",
  272. host: hostUrl.substring(0, hostUrl.indexOf("/")),
  273. "accept-encoding": "gzip, deflate, br",
  274. connection: "keep-alive",
  275. referer: "https://www.bilibili.com/video/".concat((_a = (musicItem.bvid !== null && musicItem.bvid !== undefined
  276. ? musicItem.bvid
  277. : musicItem.aid)) !== null && _a !== void 0 ? _a : ""),
  278. };
  279. return {
  280. url: url,
  281. headers: _headers,
  282. };
  283. }
  284. async function getTopLists() {
  285. const precious = {
  286. title: "入站必刷",
  287. data: [
  288. {
  289. id: "popular/precious?page_size=100&page=1",
  290. title: "入站必刷",
  291. coverImg: "https://s1.hdslb.com/bfs/static/jinkela/popular/assets/icon_history.png",
  292. },
  293. ],
  294. };
  295. const weekly = {
  296. title: "每周必看",
  297. data: [],
  298. };
  299. const weeklyRes = await axios_1.default.get("https://api.bilibili.com/x/web-interface/popular/series/list", {
  300. headers: {
  301. "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
  302. },
  303. });
  304. weekly.data = weeklyRes.data.data.list.slice(0, 8).map((e) => ({
  305. id: `popular/series/one?number=${e.number}`,
  306. title: e.subject,
  307. description: e.name,
  308. coverImg: "https://s1.hdslb.com/bfs/static/jinkela/popular/assets/icon_weekly.png",
  309. }));
  310. const boardKeys = [
  311. {
  312. id: "ranking/v2?rid=0&type=all",
  313. title: "全站",
  314. },
  315. {
  316. id: "ranking/v2?rid=3&type=all",
  317. title: "音乐",
  318. },
  319. {
  320. id: "ranking/v2?rid=1&type=all",
  321. title: "动画",
  322. },
  323. {
  324. id: "ranking/v2?rid=119&type=all",
  325. title: "鬼畜",
  326. },
  327. {
  328. id: "ranking/v2?rid=168&type=all",
  329. title: "国创相关",
  330. },
  331. {
  332. id: "ranking/v2?rid=129&type=all",
  333. title: "舞蹈",
  334. },
  335. {
  336. id: "ranking/v2?rid=4&type=all",
  337. title: "游戏",
  338. },
  339. {
  340. id: "ranking/v2?rid=36&type=all",
  341. title: "知识",
  342. },
  343. {
  344. id: "ranking/v2?rid=188&type=all",
  345. title: "科技",
  346. },
  347. {
  348. id: "ranking/v2?rid=234&type=all",
  349. title: "运动",
  350. },
  351. {
  352. id: "ranking/v2?rid=223&type=all",
  353. title: "汽车",
  354. },
  355. {
  356. id: "ranking/v2?rid=160&type=all",
  357. title: "生活",
  358. },
  359. {
  360. id: "ranking/v2?rid=211&type=all",
  361. title: "美食",
  362. },
  363. {
  364. id: "ranking/v2?rid=217&type=all",
  365. title: "动物圈",
  366. },
  367. {
  368. id: "ranking/v2?rid=155&type=all",
  369. title: "时尚",
  370. },
  371. {
  372. id: "ranking/v2?rid=5&type=all",
  373. title: "娱乐",
  374. },
  375. {
  376. id: "ranking/v2?rid=181&type=all",
  377. title: "影视",
  378. },
  379. {
  380. id: "ranking/v2?rid=0&type=origin",
  381. title: "原创",
  382. },
  383. {
  384. id: "ranking/v2?rid=0&type=rookie",
  385. title: "新人",
  386. },
  387. ];
  388. const board = {
  389. title: "排行榜",
  390. data: boardKeys.map((_) => (Object.assign(Object.assign({}, _), { coverImg: "https://s1.hdslb.com/bfs/static/jinkela/popular/assets/icon_rank.png" }))),
  391. };
  392. return [weekly, precious, board];
  393. }
  394. async function getTopListDetail(topListItem) {
  395. const res = await axios_1.default.get(`https://api.bilibili.com/x/web-interface/${topListItem.id}`, {
  396. headers: Object.assign(Object.assign({}, headers), { referer: "https://www.bilibili.com/" }),
  397. });
  398. return Object.assign(Object.assign({}, topListItem), { musicList: res.data.data.list.map(formatMedia) });
  399. }
  400. async function importMusicSheet(urlLike) {
  401. var _a, _b, _c, _d;
  402. let id;
  403. if (!id) {
  404. id = (_a = urlLike.match(/^\s*(\d+)\s*$/)) === null || _a === void 0 ? void 0 : _a[1];
  405. }
  406. if (!id) {
  407. id = (_b = urlLike.match(/^(?:.*)fid=(\d+).*$/)) === null || _b === void 0 ? void 0 : _b[1];
  408. }
  409. if (!id) {
  410. id = (_c = urlLike.match(/\/playlist\/pl(\d+)/i)) === null || _c === void 0 ? void 0 : _c[1];
  411. }
  412. if (!id) {
  413. id = (_d = urlLike.match(/\/list\/ml(\d+)/i)) === null || _d === void 0 ? void 0 : _d[1];
  414. }
  415. if (!id) {
  416. return;
  417. }
  418. const musicSheet = await getFavoriteList(id);
  419. return musicSheet.map((_) => {
  420. var _a, _b;
  421. return ({
  422. id: _.id,
  423. aid: _.aid,
  424. bvid: _.bvid,
  425. artwork: _.cover,
  426. title: _.title,
  427. artist: (_a = _.upper) === null || _a === void 0 ? void 0 : _a.name,
  428. album: (_b = _.bvid) !== null && _b !== void 0 ? _b : _.aid,
  429. duration: durationToSec(_.duration),
  430. });
  431. });
  432. }
  433. module.exports = {
  434. platform: "bilibili",
  435. appVersion: ">=0.0",
  436. version: "0.1.15",
  437. author: "猫头猫",
  438. cacheControl: "no-cache",
  439. srcUrl: "https://gitee.com/maotoumao/MusicFreePlugins/raw/v0.1/dist/bilibili/index.js",
  440. primaryKey: ["id", "aid", "bvid", "cid"],
  441. hints: {
  442. importMusicSheet: [
  443. "bilibili 移动端:APP点击我的,空间,右上角分享,复制链接,浏览器打开切换桌面版网站,点击播放全部视频,复制链接",
  444. "bilibili H5/PC端:复制收藏夹URL,或者直接输入ID即可",
  445. "非公开收藏夹无法导入,编辑收藏夹改为公开即可",
  446. "导入时间和歌单大小有关,请耐心等待",
  447. ],
  448. },
  449. supportedSearchType: ["music", "album", "artist"],
  450. async search(keyword, page, type) {
  451. if (type === "album" || type === "music") {
  452. return await searchAlbum(keyword, page);
  453. }
  454. if (type === "artist") {
  455. return await searchArtist(keyword, page);
  456. }
  457. },
  458. getMediaSource,
  459. async getAlbumInfo(albumItem) {
  460. var _a;
  461. const cidRes = await getCid(albumItem.bvid, albumItem.aid);
  462. const _ref2 = (_a = cidRes === null || cidRes === void 0 ? void 0 : cidRes.data) !== null && _a !== void 0 ? _a : {};
  463. const cid = _ref2.cid;
  464. const pages = _ref2.pages;
  465. let musicList;
  466. if (pages.length === 1) {
  467. musicList = [Object.assign(Object.assign({}, albumItem), { cid: cid })];
  468. }
  469. else {
  470. musicList = pages.map(function (_) {
  471. return Object.assign(Object.assign({}, albumItem), { cid: _.cid, title: _.part, duration: durationToSec(_.duration), id: _.cid });
  472. });
  473. }
  474. return {
  475. musicList,
  476. };
  477. },
  478. getArtistWorks,
  479. getTopLists,
  480. getTopListDetail,
  481. importMusicSheet,
  482. };