alist.js 22 KB

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