import axios from "axios"; import CryptoJS from "crypto-js"; class YunDrive { constructor() { this.regex = /https:\/\/yun.139.com\/shareweb\/#\/w\/i\/([^&]+)/; this.x = CryptoJS.enc.Utf8.parse("PVGDwmcvfs1uV3d1"); this.baseUrl = 'https://share-kd-njs.yun.139.com/yun-share/richlifeApp/devapp/IOutLink/'; this.baseHeader = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36', 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/json', 'hcy-cool-flag': '1', 'x-deviceinfo': '||3|12.27.0|chrome|131.0.0.0|5c7c68368f048245e1ce47f1c0f8f2d0||windows 10|1536X695|zh-CN|||' }; this.linkID = ''; this.cache = {}; // 添加缓存对象 } encrypt(data) { let t = CryptoJS.lib.WordArray.random(16), n = ""; if ("string" == typeof data) { const o = CryptoJS.enc.Utf8.parse(data); n = CryptoJS.AES.encrypt(o, this.x, {iv: t, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7}); } else if (typeof data === 'object' && data !== null) { const a = JSON.stringify(data), s = CryptoJS.enc.Utf8.parse(a); n = CryptoJS.AES.encrypt(s, this.x, {iv: t, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7}); } return CryptoJS.enc.Base64.stringify(t.concat(n.ciphertext)); } decrypt(data) { const t = CryptoJS.enc.Base64.parse(data), n = t.clone(), i = n.words.splice(4); n.init(n.words), t.init(i); const o = CryptoJS.enc.Base64.stringify(t), a = CryptoJS.AES.decrypt(o, this.x, {iv: n, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7}), s = a.toString(CryptoJS.enc.Utf8); return s.toString(); } async getShareID(url) { const matches = this.regex.exec(url) || /https:\/\/caiyun.139.com\/m\/i\?([^&]+)/.exec(url); if (matches && matches[1]) { this.linkID = matches[1]; } } async getShareInfo(pCaID) { if (!this.linkID) { console.error('linkID is not set. Please call getShareID first.'); return null; } const cacheKey = `${this.linkID}-${pCaID}`; if (this.cache[cacheKey]) { return this.cache[cacheKey]; } let data = JSON.stringify(this.encrypt(JSON.stringify({ "getOutLinkInfoReq": { "account": "", "linkID": this.linkID, "passwd": "", "caSrt": 0, "coSrt": 0, "srtDr": 1, "bNum": 1, "pCaID": pCaID, "eNum": 200 }, "commonAccountInfo": {"account": "", "accountType": 1} }))); try { const resp = await axios.post(this.baseUrl + 'getOutLinkInfoV6', data, {headers: this.baseHeader}); if (resp.status !== 200) { return null; } const json = JSON.parse(this.decrypt(resp.data)).data; this.cache[cacheKey] = json; // 缓存结果 return json; } catch (error) { console.error('Error processing share info:', error); return null; } } async getShareData(url) { if (!url) { return {}; } const isValidUrl = url.startsWith('http'); let pCaID = isValidUrl ? 'root' : url; if (isValidUrl) { await this.getShareID(url); } let file = {}; let fileInfo = await this.getShareFile(pCaID); if (fileInfo && Array.isArray(fileInfo)) { await Promise.all(fileInfo.map(async (item) => { if (!(item.name in file)) { file[item.name] = []; } let filelist = await this.getShareUrl(item.path); if (filelist && filelist.length > 0) { file[item.name].push(...filelist); } })); } for (let key in file) { if (file[key].length === 0) { delete file[key]; } } if (Object.keys(file).length === 0) { file['root'] = await this.getShareFile(url); if (file['root'] && Array.isArray(file['root'])) { file['root'] = file['root'].filter(item => item && Object.keys(item).length > 0); } } return file; } async getShareFile(pCaID) { if (!pCaID) { return null; } try { const isValidUrl = pCaID.startsWith('http'); pCaID = isValidUrl ? 'root' : pCaID; const json = await this.getShareInfo(pCaID); if (!json || !json.caLst) { return null; } const caLst = json?.caLst; const names = caLst.map(it => it.caName); const rootPaths = caLst.map(it => it.path); const filterRegex = /App|活动中心|免费|1T空间|免流/; const videos = []; if (caLst && caLst.length > 0) { names.forEach((name, index) => { if (!filterRegex.test(name)) { videos.push({name: name, path: rootPaths[index]}); } }); let result = await Promise.all(rootPaths.map(async (path) => this.getShareFile(path))); result = result.filter(item => item !== undefined && item !== null); return [...videos, ...result.flat()]; } } catch (error) { console.error('Error processing share data:', error); return null; } } async getShareUrl(pCaID) { try { const json = await this.getShareInfo(pCaID); if (!json || !('coLst' in json)) { return null; } const coLst = json.coLst; if (coLst !== null) { const filteredItems = coLst.filter(it => it && it.coType === 3); return filteredItems.map(it => ({ name: it.coName, contentId: it.coID, linkID: this.linkID })); } else if (json.caLst !== null) { const rootPaths = json.caLst.map(it => it.path); let result = await Promise.all(rootPaths.map(path => this.getShareUrl(path))); result = result.filter(item => item && item.length > 0); return result.flat(); } } catch (error) { console.error('Error processing share URL:', error); return null; } } async getSharePlay(contentId, linkID) { let data = { "getContentInfoFromOutLinkReq": { "contentId": contentId, "linkID": linkID, "account": "" }, "commonAccountInfo": { "account": "", "accountType": 1 } }; let resp = await axios.post(this.baseUrl + 'getContentInfoFromOutLink', data, { headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36', 'Accept': 'application/json, text/plain, */*', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Content-Type': 'application/json' } }) if (resp.status === 200 && resp.data.data !== null) { let data = resp.data return data.data.contentInfo.presentURL } } } export const Yun = new YunDrive();