Bili.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. import { Crypto, jinja2, _ } from 'assets://js/lib/cat.js';
  2. let siteKey = '';
  3. let siteType = 0;
  4. let cookie = '';
  5. let login = '';
  6. let vip = false;
  7. let extendObj = {};
  8. let bili_jct = '';
  9. let vod_audio_id = {
  10. 30280: 192000,
  11. 30232: 132000,
  12. 30216: 64000,
  13. };
  14. let vod_codec = {
  15. // 13: 'AV1',
  16. 12: 'HEVC',
  17. 7: 'AVC',
  18. };
  19. 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';
  20. async function request(reqUrl, ua, buffer) {
  21. let res = await req(reqUrl, {
  22. method: 'get',
  23. headers: ua ? ua : { 'User-Agent': UA },
  24. timeout: 60000,
  25. buffer: buffer ? 1 : 0,
  26. });
  27. return res.content;
  28. }
  29. async function post(reqUrl, postData, ua, posttype) {
  30. let res = await req(reqUrl, {
  31. method: 'post',
  32. headers: ua ? ua : { 'User-Agent': UA },
  33. data: postData,
  34. timeout: 60000,
  35. postType: posttype,
  36. });
  37. return res.content;
  38. }
  39. function getHeaders() {
  40. const headers = {
  41. 'User-Agent': UA,
  42. };
  43. if (!_.isEmpty(cookie)) {
  44. headers.cookie = cookie;
  45. }
  46. return headers;
  47. }
  48. async function getCookie() {
  49. let result = await req('https://www.bilibili.com', {
  50. method: 'get',
  51. headers: { 'User-Agent': UA },
  52. timeout: 60000,
  53. });
  54. const setCookieHeaders = result.headers['set-cookie'];
  55. cookie = setCookieHeaders.map((kk) => kk.split(';')[0] + ';').join('');
  56. }
  57. async function init(cfg) {
  58. siteKey = cfg.skey;
  59. siteType = cfg.stype;
  60. let extend = cfg.ext;
  61. if (typeof cfg.ext == 'string') {
  62. if (cfg.ext.indexOf('http') == 0) {
  63. const res = await req(cfg.ext, getHeaders());
  64. cfg.ext = JSON.parse(res.content);
  65. } else {
  66. cfg.ext = {type: cfg.ext};
  67. }
  68. extend = cfg.ext;
  69. }
  70. if (cfg.ext.hasOwnProperty('categories'))
  71. extend = cfg.ext.categories;
  72. else if (cfg.ext.hasOwnProperty('type'))
  73. extend = cfg.ext.type;
  74. if (extend == '')
  75. extend = '抖音热歌$$$经典无损音乐合集$$$超清MV$$$Java$$$Android';
  76. if (cfg.ext.hasOwnProperty('cookie')) cookie = cfg.ext.cookie;
  77. // 获取csrf
  78. const cookies = cookie.split(';');
  79. cookies.forEach(cookie => {
  80. if (cookie.includes('bili_jct')) {
  81. bili_jct = cookie.split('=')[1];
  82. }
  83. });
  84. if (_.isEmpty(cookie)) await getCookie();
  85. let result = JSON.parse(await request('https://api.bilibili.com/x/web-interface/nav', getHeaders()));
  86. login = result.data.isLogin;
  87. vip = result.data.vipStatus;
  88. const ext = extend.split('$$$');
  89. console.log("ext: " + ext);
  90. const jsonData = [
  91. {
  92. key: 'order',
  93. name: '排序',
  94. value: [
  95. { n: '综合排序', v: '0' },
  96. { n: '最多点击', v: 'click' },
  97. { n: '最新发布', v: 'pubdate' },
  98. { n: '最多弹幕', v: 'dm' },
  99. { n: '最多收藏', v: 'stow' },
  100. ],
  101. },
  102. {
  103. key: 'duration',
  104. name: '时长',
  105. value: [
  106. { n: '全部时长', v: '0' },
  107. { n: '60分钟以上', v: '4' },
  108. { n: '30~60分钟', v: '3' },
  109. { n: '10~30分钟', v: '2' },
  110. { n: '10分钟以下', v: '1' },
  111. ],
  112. },
  113. ];
  114. const newarr = [];
  115. const d = {};
  116. const sc = {
  117. type_name: "首页",
  118. type_id: "首页",
  119. land: 1,
  120. ratio: 1.33,
  121. }
  122. // newarr.push(sc);
  123. for (const kk of ext) {
  124. const cate = kk.split('#');
  125. const value = [];
  126. for (const item of cate) {
  127. const kkk = item.indexOf('$') > 0 ? item : item + "$" + item;
  128. const val = kkk.split('$');
  129. value.push({n: val[0], v: val[1]});
  130. }
  131. const c = {
  132. type_name: value[0].n,
  133. type_id: value[0].v,
  134. land: 1,
  135. ratio: 1.33,
  136. type_flag: '0-0-H'
  137. };
  138. newarr.push(c);
  139. const filter = [];
  140. if (value.length > 1) {
  141. filter.push(
  142. {
  143. key: 'tid',
  144. name: '分类',
  145. value: value
  146. }
  147. );
  148. }
  149. filter.push(jsonData[0]);
  150. filter.push(jsonData[1]);
  151. d[value[0].v] = filter;
  152. }
  153. if (!_.isEmpty(bili_jct)) {
  154. const hc = {
  155. type_name: "历史记录",
  156. type_id: "历史记录",
  157. land: 1,
  158. ratio: 1.33,
  159. }
  160. newarr.push(hc);
  161. }
  162. extendObj = {
  163. classes: newarr,
  164. filter: d,
  165. };
  166. }
  167. function home(filter) {
  168. try {
  169. const jSONObject = {
  170. class: extendObj.classes,
  171. type_flag: '0-0-H'
  172. };
  173. if (filter) {
  174. jSONObject.filters = extendObj.filter;
  175. }
  176. return JSON.stringify(jSONObject);
  177. } catch (e) {
  178. return '';
  179. }
  180. }
  181. async function homeVod() {
  182. return category('白噪音', 1, false, {});
  183. // try {
  184. // const list = [];
  185. // const url = 'https://api.bilibili.com/x/web-interface/index/top/rcmd?ps=14&fresh_idx=1&fresh_idx_1h=1';
  186. // const response = await request(url, getHeaders());
  187. // const responseData = JSON.parse(response);
  188. // const vods = responseData.data.item;
  189. // for (const item of vods) {
  190. // const vod = {};
  191. // let imageUrl = item.pic;
  192. // if (imageUrl.startsWith('//')) {
  193. // imageUrl = 'https:' + imageUrl;
  194. // }
  195. // let cd = getFullTime(item.duration);
  196. // vod.vod_id = item.bvid;
  197. // vod.vod_name = removeTags(item.title);
  198. // vod.vod_pic = imageUrl;
  199. // vod.vod_remarks = cd;
  200. // vod.style = {
  201. // type: 'rect',
  202. // ratio: 1.33,
  203. // },
  204. // list.push(vod);
  205. // }
  206. // const result = { list: list };
  207. // return JSON.stringify(result);
  208. // } catch (e) { }
  209. }
  210. async function category(tid, page, filter, ext) {
  211. if (page < 1) page = 1;
  212. try {
  213. if (Object.keys(ext).length > 0 && ext.hasOwnProperty('tid') && ext['tid'].length > 0) {
  214. tid = ext['tid'];
  215. }
  216. let url = '';
  217. url = `https://api.bilibili.com/x/web-interface/search/type?search_type=video&keyword=${encodeURIComponent(tid)}`;
  218. if (Object.keys(ext).length > 0) {
  219. for (const k in ext) {
  220. if (k == 'tid') {
  221. continue;
  222. }
  223. url += `&${encodeURIComponent(k)}=${encodeURIComponent(ext[k])}`;
  224. }
  225. }
  226. url += `&page=${encodeURIComponent(page)}`;
  227. if (tid == "首页") {
  228. url = "https://api.bilibili.com/x/web-interface/index/top/rcmd?ps=14&fresh_idx=" + page + "&fresh_idx_1h=" + page;
  229. } else if (tid == "历史记录") {
  230. url = "https://api.bilibili.com/x/v2/history?pn=" + page;
  231. }
  232. const data = JSON.parse(await request(url, getHeaders())).data;
  233. let items = data.result;
  234. if (tid == "首页") {
  235. items = data.item;
  236. } else if (tid == "历史记录") {
  237. items = data;
  238. }
  239. const videos = [];
  240. for (const item of items) {
  241. const video = {};
  242. let pic = item.pic;
  243. if (pic.startsWith('//')) {
  244. pic = 'https:' + pic;
  245. }
  246. let cd = getFullTime(item.duration);
  247. video.vod_remarks = cd;
  248. video.vod_id = item.bvid;
  249. video.vod_name = removeTags(item.title);
  250. video.vod_pic = pic;
  251. video.style = {
  252. type: 'rect',
  253. ratio: 1.33,
  254. },
  255. videos.push(video);
  256. }
  257. const result = {
  258. page: page,
  259. pagecount: data.numPages ?? (page + 1),
  260. limit: videos.length,
  261. total: videos.length * (page + 1),
  262. list: videos,
  263. };
  264. return JSON.stringify(result);
  265. } catch (e) { }
  266. return null;
  267. }
  268. async function detail(ids) {
  269. try {
  270. const bvid = ids;
  271. const detailUrl = `https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`;
  272. const detailData = JSON.parse(await request(detailUrl, getHeaders())).data;
  273. // 记录历史
  274. if (!_.isEmpty(bili_jct)) {
  275. const historyReport = 'https://api.bilibili.com/x/v2/history/report';
  276. let dataPost = {
  277. aid: detailData.aid,
  278. cid: detailData.cid,
  279. csrf: bili_jct,
  280. }
  281. await post(historyReport, dataPost, getHeaders(), 'form');
  282. }
  283. let cd = getFullTime(detailData.duration);
  284. const aid = detailData.aid;
  285. const video = {
  286. vod_id: bvid,
  287. vod_name: detailData.title,
  288. vod_pic: detailData.pic,
  289. type_name: detailData.tname,
  290. vod_year: '',
  291. vod_area: '',
  292. vod_remarks: cd,
  293. vod_actor: '',
  294. vod_director: '',
  295. vod_content: detailData.desc,
  296. };
  297. const playurldata = 'https://api.bilibili.com/x/player/playurl?avid=' + aid + '&cid=' + detailData.cid + '&qn=127&fnval=4048&fourk=1';
  298. const playurldatas = JSON.parse(await request(playurldata, getHeaders()));
  299. const playurldatalist = playurldatas.data;
  300. const accept_quality = playurldatalist.accept_quality;
  301. const accept_description = playurldatalist.accept_description;
  302. const qualitylist = [];
  303. const descriptionList = [];
  304. for (let i = 0; i < accept_quality.length; i++) {
  305. if (!vip) {
  306. if (!login) {
  307. if (accept_quality[i] > 32) continue;
  308. } else {
  309. if (accept_quality[i] > 80) continue;
  310. }
  311. }
  312. descriptionList.push(base64Encode(accept_description[i]));
  313. qualitylist.push(accept_quality[i]);
  314. }
  315. let treeMap = {};
  316. const jSONArray = detailData.pages;
  317. let playList = [];
  318. for (let j = 0; j < jSONArray.length; j++) {
  319. const jSONObject6 = jSONArray[j];
  320. const cid = jSONObject6.cid;
  321. const title = jSONObject6.part;
  322. const duration = jSONObject6.duration;
  323. const playUrl = '[' + secondsToTime(duration) + '] ' + title + '$' + aid + '+' + cid + '+' + qualitylist.join(':') + '+' + descriptionList.join(':');
  324. playList.push(playUrl);
  325. }
  326. treeMap['dash'] = playList.join('#');
  327. treeMap['mp4'] = playList.join('#');
  328. const relatedUrl = 'https://api.bilibili.com/x/web-interface/archive/related?bvid=' + bvid;
  329. const relatedData = JSON.parse(await request(relatedUrl, getHeaders())).data;
  330. playList = [];
  331. for (let j = 0; j < relatedData.length; j++) {
  332. const jSONObject6 = relatedData[j];
  333. const cid = jSONObject6.cid;
  334. const title = jSONObject6.title;
  335. const aaid = jSONObject6.aid;
  336. const playUrl = title + '$' + aaid + '+' + cid + '+' + qualitylist.join(':') + '+' + descriptionList.join(':');
  337. playList.push(playUrl);
  338. }
  339. treeMap['相关'] = playList.join('#');
  340. video.vod_play_from = Object.keys(treeMap).join("$$$");
  341. video.vod_play_url = Object.values(treeMap).join("$$$");
  342. // console.log("====>video: " + JSON.stringify(video));
  343. const list = [video];
  344. const result = { list };
  345. return JSON.stringify(result);
  346. } catch (e) { }
  347. return null;
  348. }
  349. async function play(flag, id, flags) {
  350. try {
  351. 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' };
  352. const ids = id.split('+');
  353. const aid = ids[0];
  354. const cid = ids[1];
  355. const qualityIds = ids[2].split(':');
  356. const qualityName = ids[3].split(':');
  357. if (flag == 'dash' || flag == '相关') {
  358. // dash mpd 代理
  359. const js2Base = await js2Proxy(true, siteType, siteKey, 'dash/', {});
  360. let urls = [];
  361. for (let i = 0; i < qualityIds.length; i++) {
  362. urls.push(base64Decode(qualityName[i]), js2Base + base64Encode(aid + '+' + cid + '+' + qualityIds[i]));
  363. }
  364. return JSON.stringify({
  365. parse: 0,
  366. url: urls,
  367. header: playHeaders,
  368. });
  369. } else if (flag == 'mp4') {
  370. // 直链
  371. let urls = [];
  372. for (let i = 0; i < qualityIds.length; i++) {
  373. const url = `https://api.bilibili.com/x/player/playurl?avid=${aid}&cid=${cid}&qn=${qualityIds[i]}&fourk=1`;
  374. const resp = JSON.parse(await request(url, getHeaders()));
  375. const data = resp.data;
  376. if (data.quality != qualityIds[i]) continue;
  377. let durl = data.durl[0].url;
  378. urls.push(base64Decode(qualityName[i]), durl);
  379. }
  380. return JSON.stringify({
  381. parse: 0,
  382. url: urls,
  383. header: playHeaders,
  384. });
  385. } else {
  386. // 音频外挂
  387. let urls = [];
  388. let audios = [];
  389. for (let i = 0; i < qualityIds.length; i++) {
  390. const url = `https://api.bilibili.com/x/player/playurl?avid=${aid}&cid=${cid}&qn=${qualityIds[i]}&fnval=4048&fourk=1`;
  391. let resp = JSON.parse(await request(url, getHeaders()));
  392. const dash = resp.data.dash;
  393. const video = dash.video;
  394. const audio = dash.audio;
  395. for (let j = 0; j < video.length; j++) {
  396. const dashjson = video[j];
  397. if (dashjson.id == qualityIds[i]) {
  398. for (const key in vod_codec) {
  399. if (dashjson.codecid == key) {
  400. urls.push(base64Decode(qualityName[i]) + ' ' + vod_codec[key], dashjson.baseUrl);
  401. }
  402. }
  403. }
  404. }
  405. if (audios.length == 0) {
  406. for (let j = 0; j < audio.length; j++) {
  407. const dashjson = audio[j];
  408. for (const key in vod_audio_id) {
  409. if (dashjson.id == key) {
  410. audios.push({
  411. title: _.floor(parseInt(vod_audio_id[key]) / 1024) + 'Kbps',
  412. bit: vod_audio_id[key],
  413. url: dashjson.baseUrl,
  414. });
  415. }
  416. }
  417. }
  418. audios = _.sortBy(audios, 'bit');
  419. }
  420. }
  421. return JSON.stringify({
  422. parse: 0,
  423. url: urls,
  424. extra: {
  425. audio: audios,
  426. },
  427. header: playHeaders,
  428. });
  429. }
  430. } catch (e) { }
  431. return null;
  432. }
  433. async function search(key, quick, pg) {
  434. let page = pg || 1;
  435. if (page == 0) page = 1;
  436. try {
  437. const ext = {
  438. duration: '0',
  439. };
  440. let resp = JSON.parse(await category(key, page, true, ext));
  441. const catVideos = resp.list;
  442. const pageCount = resp.pagecount;
  443. const videos = [];
  444. for (let i = 0; i < catVideos.length; ++i) {
  445. videos.push(catVideos[i]);
  446. }
  447. const result = {
  448. page: page,
  449. pagecount: pageCount,
  450. land: 1,
  451. ratio: 1.33,
  452. list: videos,
  453. };
  454. return JSON.stringify(result);
  455. } catch (e) { }
  456. return null;
  457. }
  458. async function proxy(segments, headers) {
  459. let what = segments[0];
  460. let url = base64Decode(segments[1]);
  461. if (what == 'dash') {
  462. const ids = url.split('+');
  463. const aid = ids[0];
  464. const cid = ids[1];
  465. const str5 = ids[2];
  466. const urls = `https://api.bilibili.com/x/player/playurl?avid=${aid}&cid=${cid}&qn=${str5}&fnval=4048&fourk=1`;
  467. let videoList = '';
  468. let audioList = '';
  469. let resp = JSON.parse(await request(urls, getHeaders()));
  470. const dash = resp.data.dash;
  471. const video = dash.video;
  472. const audio = dash.audio;
  473. for (let i = 0; i < video.length; i++) {
  474. // if (i > 0) continue; // 只取一个
  475. const dashjson = video[i];
  476. if (dashjson.id == str5) {
  477. videoList += getDashMedia(dashjson);
  478. }
  479. }
  480. for (let i = 0; i < audio.length; i++) {
  481. // if (i > 0) continue;
  482. const ajson = audio[i];
  483. for (const key in vod_audio_id) {
  484. if (ajson.id == key) {
  485. audioList += getDashMedia(ajson);
  486. }
  487. }
  488. }
  489. let mpd = getDash(resp, videoList, audioList);
  490. return JSON.stringify({
  491. code: 200,
  492. content: mpd,
  493. headers: {
  494. 'Content-Type': 'application/dash+xml',
  495. },
  496. });
  497. }
  498. return JSON.stringify({
  499. code: 500,
  500. content: '',
  501. });
  502. }
  503. function getDashMedia(dash) {
  504. try {
  505. let qnid = dash.id;
  506. const codecid = dash.codecid;
  507. const media_codecs = dash.codecs;
  508. const media_bandwidth = dash.bandwidth;
  509. const media_startWithSAP = dash.startWithSap;
  510. const media_mimeType = dash.mimeType;
  511. const media_BaseURL = dash.baseUrl.replace(/&/g, '&amp;');
  512. const media_SegmentBase_indexRange = dash.SegmentBase.indexRange;
  513. const media_SegmentBase_Initialization = dash.SegmentBase.Initialization;
  514. const mediaType = media_mimeType.split('/')[0];
  515. let media_type_params = '';
  516. if (mediaType == 'video') {
  517. const media_frameRate = dash.frameRate;
  518. const media_sar = dash.sar;
  519. const media_width = dash.width;
  520. const media_height = dash.height;
  521. media_type_params = `height='${media_height}' width='${media_width}' frameRate='${media_frameRate}' sar='${media_sar}'`;
  522. } else if (mediaType == 'audio') {
  523. for (const key in vod_audio_id) {
  524. if (qnid == key) {
  525. const audioSamplingRate = vod_audio_id[key];
  526. media_type_params = `numChannels='2' sampleRate='${audioSamplingRate}'`;
  527. }
  528. }
  529. }
  530. qnid += '_' + codecid;
  531. return `<AdaptationSet lang="chi">
  532. <ContentComponent contentType="${mediaType}"/>
  533. <Representation id="${qnid}" bandwidth="${media_bandwidth}" codecs="${media_codecs}" mimeType="${media_mimeType}" ${media_type_params} startWithSAP="${media_startWithSAP}">
  534. <BaseURL>${media_BaseURL}</BaseURL>
  535. <SegmentBase indexRange="${media_SegmentBase_indexRange}">
  536. <Initialization range="${media_SegmentBase_Initialization}"/>
  537. </SegmentBase>
  538. </Representation>
  539. </AdaptationSet>`;
  540. } catch (e) {
  541. // Handle exceptions here
  542. }
  543. }
  544. function getDash(ja, videoList, audioList) {
  545. const duration = ja.data.dash.duration;
  546. const minBufferTime = ja.data.dash.minBufferTime;
  547. 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">
  548. <Period duration="PT${duration}S" start="PT0S">
  549. ${videoList}
  550. ${audioList}
  551. </Period>
  552. </MPD>`;
  553. }
  554. function base64Encode(text) {
  555. return Crypto.enc.Base64.stringify(Crypto.enc.Utf8.parse(text));
  556. }
  557. function base64Decode(text) {
  558. return Crypto.enc.Utf8.stringify(Crypto.enc.Base64.parse(text));
  559. }
  560. function removeTags(input) {
  561. return input.replace(/<[^>]*>/g, '');
  562. }
  563. function getFullTime(numberSec) {
  564. let totalSeconds = '';
  565. try {
  566. var timeParts = numberSec.split(":");
  567. var min = parseInt(timeParts[0]);
  568. var sec = parseInt(timeParts[1]);
  569. totalSeconds = min * 60 + sec;
  570. } catch (e) {
  571. totalSeconds = parseInt(numberSec);
  572. }
  573. if (isNaN(totalSeconds)) {
  574. return '无效输入';
  575. }
  576. if (totalSeconds >= 3600) {
  577. const hours = Math.floor(totalSeconds / 3600);
  578. const remainingSecondsAfterHours = totalSeconds % 3600;
  579. const minutes = Math.floor(remainingSecondsAfterHours / 60);
  580. const seconds = remainingSecondsAfterHours % 60;
  581. return `${hours}小时 ${minutes}分钟 ${seconds}秒`;
  582. } else {
  583. const minutes = Math.floor(totalSeconds / 60);
  584. const seconds = totalSeconds % 60;
  585. return `${minutes}分钟 ${seconds}秒`;
  586. }
  587. }
  588. function secondsToTime(seconds) {
  589. var hours = Math.floor(seconds / 3600);
  590. var minutes = Math.floor((seconds - (hours * 3600)) / 60);
  591. var seconds = seconds - (hours * 3600) - (minutes * 60);
  592. // round seconds
  593. seconds = Math.round(seconds * 100) / 100
  594. var result = (hours < 10 ? "0" + hours : hours);
  595. result += ":" + (minutes < 10 ? "0" + minutes : minutes);
  596. result += ":" + (seconds < 10 ? "0" + seconds : seconds);
  597. return result;
  598. }
  599. export function __jsEvalReturn() {
  600. return {
  601. init: init,
  602. home: home,
  603. homeVod: homeVod,
  604. category: category,
  605. detail: detail,
  606. play: play,
  607. proxy: proxy,
  608. search: search,
  609. };
  610. }