alist.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. // import _ from 'https://underscorejs.org/underscore-esm-min.js'
  2. // import {distance} from 'https://unpkg.com/fastest-levenshtein@1.0.16/esm/mod.js'
  3. import {distance} from 'https://gitcode.net/qq_32394351/dr_py/-/raw/master/libs/mod.js'
  4. import {sortListByCN} from 'https://gitcode.net/qq_32394351/dr_py/-/raw/master/libs/sortName.js'
  5. /**
  6. * alist js
  7. * 配置设置 {"key":"Alist","name":"Alist","type":3,"api":"http://xxx.com/alist.js","searchable":0,"quickSearch":0,"filterable":0,"ext":"http://xxx.com/alist.json"}
  8. * alist.json [{
  9. name:'名称',
  10. server:'地址',
  11. startPage:'/', //启动文件夹
  12. showAll: false , //是否显示全部文件,默认false只显示 音视频和文件夹
  13. search: true, // 启用小雅的搜索,搜索只会搜第一个开启此开关的磁盘
  14. params:{ //对应文件夹参数 如设置对应文件夹的密码
  15. '/abc':{ password : '123' },
  16. '/abc/abc':{ password : '123' },
  17. }
  18. }]
  19. * 提示 想要加载文件夹里面全部视频到详情(看剧可以自动播放下一集支持历史记录)
  20. * 需要改软件才能支持,,建议长按文件夹时添加判断 tag == folder 时跳转 DetailActivity
  21. */
  22. String.prototype.rstrip = function (chars) {
  23. let regex = new RegExp(chars + "$");
  24. return this.replace(regex, "");
  25. };
  26. var showMode = 'single';
  27. var searchDriver = '';
  28. var limit_search_show = 200;
  29. var search_type = '';
  30. var detail_order = 'name';
  31. var playRaw = 1; // 播放直链获取,默认0直接拼接/d 填1可以获取阿里oss链接。注意,有时效性
  32. const request_timeout = 5000;
  33. const VERSION = 'alist v2/v3 20221223';
  34. /**
  35. * 打印日志
  36. * @param any 任意变量
  37. */
  38. function print(any){
  39. any = any||'';
  40. if(typeof(any)=='object'&&Object.keys(any).length>0){
  41. try {
  42. any = JSON.stringify(any);
  43. console.log(any);
  44. }catch (e) {
  45. // console.log('print:'+e.message);
  46. console.log(typeof(any)+':'+any.length);
  47. }
  48. }else if(typeof(any)=='object'&&Object.keys(any).length<1){
  49. console.log('null object');
  50. }else{
  51. console.log(any);
  52. }
  53. }
  54. const http = function (url, options = {}) {
  55. if(options.method ==='POST' && options.data){
  56. options.body = JSON.stringify(options.data);
  57. options.headers = Object.assign({'content-type':'application/json'}, options.headers);
  58. }
  59. options.timeout = request_timeout;
  60. try {
  61. const res = req(url, options);
  62. res.json = () => res&&res.content ? JSON.parse(res.content) : null;
  63. res.text = () => res&&res.content ? res.content:'';
  64. return res
  65. }catch (e) {
  66. return {
  67. json() {
  68. return null
  69. }, text() {
  70. return ''
  71. }
  72. }
  73. }
  74. };
  75. ["get", "post"].forEach(method => {
  76. http[method] = function (url, options = {}) {
  77. return http(url, Object.assign(options, {method: method.toUpperCase()}));
  78. }
  79. });
  80. const __drives = {};
  81. function isMedia(file){
  82. return /\.(dff|dsf|mp3|aac|wav|wma|cda|flac|m4a|mid|mka|mp2|mpa|mpc|ape|ofr|ogg|ra|wv|tta|ac3|dts|tak|webm|wmv|mpeg|mov|ram|swf|mp4|avi|rm|rmvb|flv|mpg|mkv|m3u8|ts|3gp|asf)$/.test(file.toLowerCase());
  83. }
  84. function get_drives_path(tid) {
  85. const index = tid.indexOf('$');
  86. const name = tid.substring(0, index);
  87. const path = tid.substring(index + 1);
  88. return { drives: get_drives(name), path };
  89. }
  90. function get_drives(name) {
  91. const { settings, api, server } = __drives[name];
  92. if (settings.v3 == null) { //获取 设置
  93. settings.v3 = false;
  94. const data = http.get(server + '/api/public/settings').json().data;
  95. if (Array.isArray(data)) {
  96. settings.title = data.find(x => x.key === 'title')?.value;
  97. settings.v3 = false;
  98. settings.version = data.find(x => x.key === 'version')?.value;
  99. settings.enableSearch = data.find(x => x.key === 'enable search')?.value === 'true';
  100. } else {
  101. settings.title = data.title;
  102. settings.v3 = true;
  103. settings.version = data.version;
  104. settings.enableSearch = false; //v3 没有找到 搜索配置
  105. }
  106. //不同版本 接口不一样
  107. api.path = settings.v3 ? '/api/fs/list' : '/api/public/path';
  108. api.file = settings.v3 ? '/api/fs/get' : '/api/public/path';
  109. api.search = settings.v3 ? '/api/public/search' : '/api/public/search';
  110. }
  111. return __drives[name]
  112. }
  113. function init(ext) {
  114. console.log("当前版本号:"+VERSION);
  115. let data;
  116. if (typeof ext == 'object'){
  117. data = ext;
  118. print('alist ext:object');
  119. } else if (typeof ext == 'string') {
  120. if (ext.startsWith('http')) {
  121. let alist_data = ext.split(';');
  122. let alist_data_url = alist_data[0];
  123. limit_search_show = alist_data.length>1?Number(alist_data[1])||limit_search_show:limit_search_show;
  124. search_type = alist_data.length>2?alist_data[2]:search_type;
  125. print(alist_data_url);
  126. data = http.get(alist_data_url).json(); // .map(it=>{it.name='🙋丫仙女';return it})
  127. } else {
  128. print('alist ext:json string');
  129. data = JSON.parse(ext);
  130. }
  131. }
  132. // print(data); // 测试证明壳子标题支持emoji,是http请求源码不支持emoji
  133. let drives = [];
  134. if(Array.isArray(data) && data.length > 0 && data[0].hasOwnProperty('server') && data[0].hasOwnProperty('name')){
  135. drives = data;
  136. }else if(!Array.isArray(data)&&data.hasOwnProperty('drives')&&Array.isArray(data.drives)){
  137. drives = data.drives.filter(it=>(it.type&&it.type==='alist')||!it.type);
  138. }
  139. print(drives);
  140. searchDriver = (drives.find(x=>x.search)||{}).name||'';
  141. if(!searchDriver && drives.length > 0){
  142. searchDriver = drives[0].name;
  143. }
  144. print(searchDriver);
  145. drives.forEach(item => {
  146. let _path_param = [];
  147. if(item.params){
  148. _path_param = Object.keys(item.params);
  149. // 升序排列
  150. _path_param.sort((a,b)=>(a.length-b.length));
  151. }
  152. if(item.password){
  153. let pwdObj = {
  154. password: item.password
  155. };
  156. if(!item.params){
  157. item.params = {'/':pwdObj};
  158. }else{
  159. item.params['/'] = pwdObj;
  160. }
  161. _path_param.unshift('/');
  162. }
  163. __drives[item.name] = {
  164. name: item.name,
  165. server: item.server.endsWith("/") ? item.server.rstrip("/") : item.server,
  166. startPage: item.startPage || '/', //首页
  167. showAll: item.showAll === true, //默认只显示 视频和文件夹,如果想显示全部 showAll 设置true
  168. search: !!item.search, //是否支持搜索,只有小丫的可以,多个可搜索只取最前面的一个
  169. params: item.params || {},
  170. _path_param: _path_param,
  171. settings: {},
  172. api: {},
  173. getParams(path) {
  174. const key = this._path_param.find(x => path.startsWith(x));
  175. return Object.assign({}, this.params[key], { path });
  176. },
  177. getPath(path) {
  178. const res = http.post(this.server + this.api.path, { data: this.getParams(path) }).json();
  179. return this.settings.v3 ? res.data.content : res.data.files
  180. },
  181. getFile(path) {
  182. let raw_url = this.server+'/d'+path;
  183. raw_url = encodeURI(raw_url);
  184. let data = {raw_url:raw_url,raw_url1:raw_url};
  185. if(playRaw===1){
  186. try {
  187. const res = http.post(this.server + this.api.file, { data: this.getParams(path) }).json();
  188. data = this.settings.v3 ? res.data : res.data.files[0];
  189. if (!this.settings.v3) {
  190. data.raw_url = data.url; //v2 的url和v3不一样
  191. }
  192. data.raw_url1 = raw_url;
  193. return data
  194. }catch (e) {
  195. return data
  196. }
  197. }else{
  198. return data
  199. }
  200. },
  201. isFolder(data) { return data.type === 1 },
  202. isVideo(data) { //判断是否是 视频文件
  203. // return this.settings.v3 ? data.type === 2 : data.type === 3
  204. // 增加音乐识别 视频,其他,音频
  205. return this.settings.v3 ? (data.type === 2||data.type===0||data.type===3) : (data.type === 3||data.type===0||data.type === 4)
  206. },
  207. is_subt(data) {
  208. if (data.type === 1) {
  209. return false;
  210. }
  211. const ext = /\.(srt|ass|scc|stl|ttml)$/; // [".srt", ".ass", ".scc", ".stl", ".ttml"];
  212. // return ext.some(x => data.name.endsWith(x));
  213. return ext.test(data.name);
  214. },
  215. getPic(data) {
  216. let pic = this.settings.v3 ? data.thumb : data.thumbnail;
  217. return pic || (this.isFolder(data) ? "http://img1.3png.com/281e284a670865a71d91515866552b5f172b.png" : '');
  218. },
  219. getTime(data,isStandard) {
  220. isStandard = isStandard||false;
  221. try {
  222. let tTime = data.updated_at || data.time_str || data.modified || "";
  223. let date = '';
  224. if(tTime){
  225. tTime = tTime.split("T");
  226. date = tTime[0];
  227. if(isStandard){
  228. date = date.replace(/-/g,"/");
  229. }
  230. tTime = tTime[1].split(/Z|\./);
  231. date += " " + tTime[0];
  232. }
  233. return date;
  234. }catch (e) {
  235. // print(e.message);
  236. // print(data);
  237. return ''
  238. }
  239. },
  240. }
  241. }
  242. );
  243. print('init执行完毕');
  244. }
  245. function home(filter) {
  246. let classes = Object.keys(__drives).map(key => ({
  247. type_id: `${key}$${__drives[key].startPage}`,
  248. type_name: key,
  249. type_flag: '1',
  250. }));
  251. let filter_dict = {};
  252. let filters = [{'key': 'order', 'name': '排序', 'value': [{'n': '名称⬆️', 'v': 'vod_name_asc'}, {'n': '名称⬇️', 'v': 'vod_name_desc'},
  253. {'n': '中英⬆️', 'v': 'vod_cn_asc'}, {'n': '中英⬇️', 'v': 'vod_cn_desc'},
  254. {'n': '时间⬆️', 'v': 'vod_time_asc'}, {'n': '时间⬇️', 'v': 'vod_time_desc'},
  255. {'n': '大小⬆️', 'v': 'vod_size_asc'}, {'n': '大小⬇️', 'v': 'vod_size_desc'},{'n': '无', 'v': 'none'}]},
  256. {'key': 'show', 'name': '播放展示', 'value': [{'n': '单集', 'v': 'single'},{'n': '全集', 'v': 'all'}]}
  257. ];
  258. classes.forEach(it=>{
  259. filter_dict[it.type_id] = filters;
  260. });
  261. print("----home----");
  262. print(classes);
  263. return JSON.stringify({ 'class': classes,'filters': filter_dict});
  264. }
  265. function homeVod(params) {
  266. let _post_data = {"pageNum":0,"pageSize":100};
  267. let _post_url = 'https://pbaccess.video.qq.com/trpc.videosearch.hot_rank.HotRankServantHttp/HotRankHttp';
  268. let data = http.post(_post_url,{ data: _post_data }).json();
  269. let _list = [];
  270. try {
  271. data = data['data']['navItemList'][0]['hotRankResult']['rankItemList'];
  272. // print(data);
  273. data.forEach(it=>{
  274. _list.push({
  275. vod_name:it.title,
  276. vod_id:'msearch:'+it.title,
  277. vod_pic:'https://avatars.githubusercontent.com/u/97389433?s=120&v=4',
  278. vod_remarks:it.changeOrder,
  279. });
  280. });
  281. }catch (e) {
  282. print('Alist获取首页推荐发送错误:'+e.message);
  283. }
  284. return JSON.stringify({ 'list': _list });
  285. }
  286. function category(tid, pg, filter, extend) {
  287. let orid = tid.replace(/#all#|#search#/g,'');
  288. let { drives, path } = get_drives_path(orid);
  289. const id = orid.endsWith('/') ? orid : orid + '/';
  290. const list = drives.getPath(path);
  291. let subList = [];
  292. let vodFiles = [];
  293. let allList = [];
  294. let fl = filter?extend:{};
  295. if(fl.show){
  296. showMode = fl.show;
  297. }
  298. list.forEach(item => {
  299. if (drives.is_subt(item)) {
  300. subList.push(item.name);
  301. }
  302. if (!drives.showAll && !drives.isFolder(item) && !drives.isVideo(item)) {
  303. return //只显示视频文件和文件夹
  304. }
  305. let vod_time = drives.getTime(item);
  306. let vod_size = get_size(item.size);
  307. let remark = vod_time.split(' ')[0].substr(3)+'\t'+vod_size;
  308. let vod_id = id + item.name + (drives.isFolder(item) ? '/' : '');
  309. if(showMode==='all'){
  310. vod_id+='#all#';
  311. }
  312. print(vod_id);
  313. const vod = {
  314. 'vod_id': vod_id,
  315. 'vod_name': item.name.replaceAll("$", "").replaceAll("#", ""),
  316. 'vod_pic': drives.getPic(item),
  317. 'vod_time':vod_time ,
  318. 'vod_size':item.size ,
  319. 'vod_tag': drives.isFolder(item) ? 'folder' : 'file',
  320. 'vod_remarks': drives.isFolder(item) ? remark + ' 文件夹' : remark
  321. };
  322. if (drives.isVideo(item)) {
  323. vodFiles.push(vod);
  324. }
  325. allList.push(vod);
  326. });
  327. if (vodFiles.length === 1 && subList.length > 0) { //只有一个视频 一个或者多个字幕 取相似度最高的
  328. // let sub = subList.length === 1 ? subList[0] : _.chain(allList).sortBy(x => (x.includes('chs') ? 100 : 0) + levenshteinDistance(x, vodFiles[0].vod_name)).last().value();
  329. let sub; // 字幕文件名称
  330. if(subList.length === 1){
  331. sub = subList[0];
  332. }else {
  333. let subs = JSON.parse(JSON.stringify(subList));
  334. subs.sort((a,b)=>{
  335. // chs是简体中文字幕
  336. let a_similar = (a.includes('chs') ? 100 : 0) + levenshteinDistance(a, vodFiles[0].vod_name);
  337. let b_similar = (b.includes('chs') ? 100 : 0) + levenshteinDistance(b, vodFiles[0].vod_name);
  338. if(a_similar>b_similar) { // 按相似度正序排列
  339. return 1;
  340. }else{ //否则,位置不变
  341. return -1;
  342. }
  343. });
  344. sub = subs.slice(-1)[0];
  345. }
  346. vodFiles[0].vod_id += "@@@" + sub;
  347. // vodFiles[0].vod_remarks += " 有字幕";
  348. vodFiles[0].vod_remarks += "🏷️";
  349. } else {
  350. vodFiles.forEach(item => {
  351. const lh = 0;
  352. let sub;
  353. subList.forEach(s => {
  354. //编辑距离相似度
  355. const l = levenshteinDistance(s, item.vod_name);
  356. if (l > 60 && l > lh) {
  357. sub = s;
  358. }
  359. });
  360. if (sub) {
  361. item.vod_id += "@@@" + sub;
  362. // item.vod_remarks += " 有字幕";
  363. item.vod_remarks += "🏷️";
  364. }
  365. });
  366. }
  367. if(fl.order){
  368. // print(fl.order);
  369. let key = fl.order.split('_').slice(0,-1).join('_');
  370. let order = fl.order.split('_').slice(-1)[0];
  371. print(`排序key:${key},排序order:${order}`);
  372. if(key.includes('name')){
  373. detail_order = 'name';
  374. allList = sortListByName(allList,key,order);
  375. }else if(key.includes('cn')){
  376. detail_order = 'cn';
  377. allList = sortListByCN(allList,'vod_name',order);
  378. }else if(key.includes('time')){
  379. detail_order = 'time';
  380. allList = sortListByTime(allList,key,order);
  381. }else if(key.includes('size')){
  382. detail_order = 'size';
  383. allList = sortListBySize(allList,key,order);
  384. }else if(fl.order.includes('none')){
  385. detail_order = 'none';
  386. print('不排序');
  387. }
  388. }else{
  389. // 没传order是其他地方调用的,自动按名称正序排序方便追剧,如果传了none进去就不排序,假装云盘里本身文件顺序是正常的
  390. if(detail_order!=='none'){
  391. allList = sortListByName(allList,'vod_name','asc');
  392. }
  393. }
  394. print("----category----"+`tid:${tid},detail_order:${detail_order},showMode:${showMode}`);
  395. // print(allList);
  396. return JSON.stringify({
  397. 'page': 1,
  398. 'pagecount': 1,
  399. 'limit': allList.length,
  400. 'total': allList.length,
  401. 'list': allList,
  402. });
  403. }
  404. function getAll(otid,tid,drives,path){
  405. try {
  406. const content = category(tid, null, false, null);
  407. const isFile = isMedia(otid.replace(/#all#|#search#/g,'').split('@@@')[0]);
  408. const { list } = JSON.parse(content);
  409. let vod_play_url = [];
  410. list.forEach(x => {
  411. if (x.vod_tag === 'file'){
  412. let vid = x.vod_id.replace(/#all#|#search#/g,'');
  413. vod_play_url.push(`${x.vod_name}$${vid.substring(vid.indexOf('$') + 1)}`);
  414. }
  415. });
  416. const pl = path.split("/").filter(it=>it);
  417. let vod_name = pl[pl.length - 1] || drives.name;
  418. if(vod_name === drives.name){
  419. print(pl);
  420. }
  421. if(otid.includes('#search#')){
  422. vod_name+='[搜]';
  423. }
  424. let vod = {
  425. // vod_id: tid,
  426. vod_id: otid,
  427. vod_name: vod_name,
  428. type_name: "文件夹",
  429. vod_pic: "https://avatars.githubusercontent.com/u/97389433?s=120&v=4",
  430. vod_content: tid,
  431. vod_tag: 'folder',
  432. vod_play_from: drives.name,
  433. vod_play_url: vod_play_url.join('#'),
  434. vod_remarks: drives.settings.title,
  435. }
  436. print("----detail1----");
  437. print(vod);
  438. return JSON.stringify({ 'list': [vod] });
  439. }catch (e) {
  440. print(e.message);
  441. let list = [{vod_name:'无数据,防无限请求',type_name: "文件夹",vod_id:'no_data',vod_remarks:'不要点,会崩的',vod_pic:'https://ghproxy.com/https://raw.githubusercontent.com/hjdhnx/dr_py/main/404.jpg',vod_actor:e.message,vod_director: tid,vod_content: otid}];
  442. return JSON.stringify({ 'list': list });
  443. }
  444. }
  445. function detail(tid) {
  446. let isSearch = tid.includes('#search#');
  447. let isAll = tid.includes('#all#');
  448. let otid = tid;
  449. tid = tid.replace(/#all#|#search#/g,'');
  450. let isFile = isMedia(tid.split('@@@')[0]);
  451. print(`isFile:${tid}?${isFile}`);
  452. let { drives, path } = get_drives_path(tid);
  453. print(`drives:${drives},path:${path},`);
  454. if (path.endsWith("/")) { //长按文件夹可以 加载里面全部视频到详情
  455. return getAll(otid,tid,drives,path);
  456. } else {
  457. if(isSearch&&!isFile){ // 搜索结果 当前目录获取所有文件
  458. return getAll(otid,tid,drives,path);
  459. }else if(isAll){ // 上级目录获取所有文件 不管是搜索还是分类,只要不是 搜索到的文件夹,且展示模式为全部,都获取上级目录的所有文件
  460. // 是文件就取上级目录
  461. let new_tid;
  462. if(isFile){
  463. new_tid = tid.split('/').slice(0,-1).join('/')+'/';
  464. }else{
  465. new_tid = tid;
  466. }
  467. print(`全集模式 tid:${tid}=>tid:${new_tid}`);
  468. let { drives, path } = get_drives_path(new_tid);
  469. return getAll(otid,new_tid,drives,path);
  470. } else if(isFile){ // 单文件进入
  471. let paths = path.split("@@@");
  472. let vod_name = paths[0].substring(paths[0].lastIndexOf("/") + 1);
  473. let vod_title = vod_name;
  474. if(otid.includes('#search#')){
  475. vod_title+='[搜]';
  476. }
  477. let vod = {
  478. vod_id: otid,
  479. vod_name: vod_title,
  480. type_name: "文件",
  481. vod_pic: "https://avatars.githubusercontent.com/u/97389433?s=120&v=4",
  482. vod_content: tid,
  483. vod_play_from: drives.name,
  484. vod_play_url: vod_name + "$" + path,
  485. vod_remarks: drives.settings.title,
  486. };
  487. print("----detail2----");
  488. print(vod);
  489. return JSON.stringify({
  490. 'list': [vod]
  491. });
  492. }else{
  493. return JSON.stringify({
  494. 'list': []
  495. });
  496. }
  497. }
  498. }
  499. function play(flag, id, flags) {
  500. const drives = get_drives(flag);
  501. const urls = id.split("@@@"); // @@@ 分割前是 相对文件path,分割后是字幕文件
  502. let vod = {
  503. 'parse': 0,
  504. 'playUrl': '',
  505. // 'url': drives.getFile(urls[0]).raw_url+'#.m3u8' // 加 # 没法播放
  506. 'url': drives.getFile(urls[0]).raw_url
  507. };
  508. if (urls.length >= 2) {
  509. const path = urls[0].substring(0, urls[0].lastIndexOf('/') + 1);
  510. vod.subt = drives.getFile(path + urls[1]).raw_url1;
  511. }
  512. print("----play----");
  513. print(vod);
  514. return JSON.stringify(vod);
  515. }
  516. function search(wd, quick) {
  517. print(__drives);
  518. print('可搜索的alist驱动:'+searchDriver);
  519. if(!searchDriver||!wd){
  520. return JSON.stringify({
  521. 'list': []
  522. });
  523. }else{
  524. let driver = __drives[searchDriver];
  525. wd = wd.split(' ').filter(it=>it.trim()).join('+');
  526. print(driver);
  527. let surl = driver.server + '/search?box='+wd+'&url=';
  528. if(search_type){
  529. surl+='&type='+search_type;
  530. }
  531. print('搜索链接:'+surl);
  532. let html = http.get(surl).text();
  533. let lists = [];
  534. try {
  535. lists = pdfa(html,'div&&ul&&a');
  536. }catch (e) {}
  537. print(`搜索结果数:${lists.length},搜索结果显示数量限制:${limit_search_show}`);
  538. let vods = [];
  539. let excludeReg = /\.(pdf|epub|mobi|txt|doc|lrc)$/; // 过滤后缀文件
  540. let cnt = 0;
  541. lists.forEach(it=>{
  542. let vhref = pdfh(it,'a&&href');
  543. if(vhref){
  544. vhref = unescape(vhref);
  545. }
  546. if(excludeReg.test(vhref)){
  547. return; //跳过本次循环
  548. }
  549. if(cnt < limit_search_show){
  550. print(vhref);
  551. }
  552. cnt ++;
  553. let vid = searchDriver+'$'+vhref+'#search#';
  554. if(showMode==='all'){
  555. vid+='#all#';
  556. }
  557. vods.push({
  558. vod_name:pdfh(it,'a&&Text'),
  559. vod_id:vid,
  560. vod_tag: isMedia(vhref) ? 'file' : 'folder',
  561. vod_pic:'http://img1.3png.com/281e284a670865a71d91515866552b5f172b.png',
  562. vod_remarks:searchDriver
  563. });
  564. });
  565. // 截取搜索结果
  566. vods = vods.slice(0,limit_search_show);
  567. print(vods);
  568. return JSON.stringify({
  569. 'list': vods
  570. });
  571. }
  572. }
  573. function get_size(sz) {
  574. if (sz <= 0) {
  575. return "";
  576. }
  577. let filesize = "";
  578. if (sz > 1024 * 1024 * 1024 * 1024.0) {
  579. sz /= (1024 * 1024 * 1024 * 1024.0);
  580. filesize = "TB";
  581. } else if (sz > 1024 * 1024 * 1024.0) {
  582. sz /= (1024 * 1024 * 1024.0);
  583. filesize = "GB";
  584. } else if (sz > 1024 * 1024.0) {
  585. sz /= (1024 * 1024.0);
  586. filesize = "MB";
  587. } else if( sz > 1024.0){
  588. sz /= 1024.0;
  589. filesize = "KB";
  590. }else{
  591. filesize = "B";
  592. }
  593. // 转成字符串
  594. let sizeStr = sz.toFixed(2) + filesize,
  595. // 获取小数点处的索引
  596. index = sizeStr.indexOf("."),
  597. // 获取小数点后两位的值
  598. dou = sizeStr.substr(index + 1, 2);
  599. if (dou === "00") {
  600. return sizeStr.substring(0, index) + sizeStr.substr(index + 3, 2);
  601. }else{
  602. return sizeStr;
  603. }
  604. }
  605. // 相似度获取
  606. function levenshteinDistance(str1, str2) {
  607. return 100 - 100 * distance(str1, str2) / Math.max(str1.length, str2.length);
  608. }
  609. /**
  610. * 自然排序
  611. * ["第1集","第10集","第20集","第2集","1","2","10","12","23","01","02"].sort(naturalSort())
  612. * @param options {{key,caseSensitive, order: string}}
  613. */
  614. function naturalSort(options) {
  615. if (!options) {
  616. options = {};
  617. }
  618. return function (a, b) {
  619. if(options.key){
  620. a = a[options.key];
  621. b = b[options.key];
  622. }
  623. var EQUAL = 0;
  624. var GREATER = (options.order === 'desc' ?
  625. -1 :
  626. 1
  627. );
  628. var SMALLER = -GREATER;
  629. var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi;
  630. var sre = /(^[ ]*|[ ]*$)/g;
  631. var dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/;
  632. var hre = /^0x[0-9a-f]+$/i;
  633. var ore = /^0/;
  634. var normalize = function normalize(value) {
  635. var string = '' + value;
  636. return (options.caseSensitive ?
  637. string :
  638. string.toLowerCase()
  639. );
  640. };
  641. // Normalize values to strings
  642. var x = normalize(a).replace(sre, '') || '';
  643. var y = normalize(b).replace(sre, '') || '';
  644. // chunk/tokenize
  645. var xN = x.replace(re, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0');
  646. var yN = y.replace(re, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0');
  647. // Return immediately if at least one of the values is empty.
  648. if (!x && !y) return EQUAL;
  649. if (!x && y) return GREATER;
  650. if (x && !y) return SMALLER;
  651. // numeric, hex or date detection
  652. var xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x));
  653. var yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null;
  654. var oFxNcL, oFyNcL;
  655. // first try and sort Hex codes or Dates
  656. if (yD) {
  657. if (xD < yD) return SMALLER;
  658. else if (xD > yD) return GREATER;
  659. }
  660. // natural sorting through split numeric strings and default strings
  661. for (var cLoc = 0, numS = Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
  662. // find floats not starting with '0', string or 0 if not defined (Clint Priest)
  663. oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0;
  664. oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0;
  665. // handle numeric vs string comparison - number < string - (Kyle Adams)
  666. if (isNaN(oFxNcL) !== isNaN(oFyNcL)) return (isNaN(oFxNcL)) ? GREATER : SMALLER;
  667. // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
  668. else if (typeof oFxNcL !== typeof oFyNcL) {
  669. oFxNcL += '';
  670. oFyNcL += '';
  671. }
  672. if (oFxNcL < oFyNcL) return SMALLER;
  673. if (oFxNcL > oFyNcL) return GREATER;
  674. }
  675. return EQUAL;
  676. };
  677. }
  678. // 完整名称排序
  679. const sortListByName = (vodList,key,order) => {
  680. if(!key){
  681. return vodList
  682. }
  683. order = order||'asc'; // 默认正序
  684. // 排序键,顺序,区分大小写
  685. return vodList.sort(naturalSort({key: key, order: order,caseSensitive:true}))
  686. };
  687. const getTimeInt = (timeStr) => {
  688. return (new Date(timeStr)).getTime();
  689. };
  690. // 时间
  691. const sortListByTime = (vodList,key,order) => {
  692. if (!key) {
  693. return vodList
  694. }
  695. let ASCarr = vodList.sort((a, b) => {
  696. a = a[key];
  697. b = b[key];
  698. return getTimeInt(a) - getTimeInt(b);
  699. });
  700. if(order==='desc'){
  701. ASCarr.reverse();
  702. }
  703. return ASCarr
  704. };
  705. // 大小
  706. const sortListBySize = (vodList,key,order) => {
  707. if (!key) {
  708. return vodList
  709. }
  710. let ASCarr = vodList.sort((a, b) => {
  711. a = a[key];
  712. b = b[key];
  713. return (Number(a) || 0) - (Number(b) || 0);
  714. });
  715. if(order==='desc'){
  716. ASCarr.reverse();
  717. }
  718. return ASCarr
  719. };
  720. // 导出函数对象
  721. export default {
  722. init: init,
  723. home: home,
  724. homeVod: homeVod,
  725. category: category,
  726. detail: detail,
  727. play: play,
  728. search: search
  729. }