123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- /**
- * File: /workspaces/TVSpider/lib/quark_api.js
- * Project: /workspaces/TVSpider
- * Created Date: Monday, May 20th 2024, 6:38:12 am
- * Author: jade
- * -----
- * Last Modified: Tue May 21 2024
- * Modified By: jade
- * -----
- * Copyright (c) 2024 samples
- * ------------------------------------
- * Javascript will save your soul!
- */
- import {_,Crypto} from "../lib/cat.js";
- import * as Utils from "../lib/utils.js"
- import {Item} from "../lib/quark_object.js"
- class Quark{
- constructor(){
- this.apiUrl = "https://drive-pc.quark.cn/1/clouddrive/"
- this.cookie = ""
- this.ckey = ""
- this.shareTokenCache = {}
- this.pr = "pr=ucpro&fr=pc"
- this.subtitleExts = ['.srt', '.ass', '.scc', '.stl', '.ttml'];
- this.saveFileIdCaches = {}
- this.saveDirId = null
- this.saveDirName = 'TV';
- this.isVip = false;
- }
- async initQuark(cookie) {
- this.ckey = Crypto.enc.Hex.stringify(Crypto.MD5(cookie)).toString();
- this.cookie = cookie
- this.isVip = await this.getVip()
- }
- getHeaders(){
- return {
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) quark-cloud-drive/2.5.20 Chrome/100.0.4896.160 Electron/18.3.5.4-b478491100 Safari/537.36 Channel/pckk_other_ch',
- 'Referer': 'https://pan.quark.cn/',
- "Content-Type":"application/json",
- "Cookie":this.cookie,
- "Host":"drive-pc.quark.cn"
- };
-
- }
- async api(url,data,retry,method){
- const leftRetry = retry || 3;
- let resp = await req(this.apiUrl + url,{
- method:method || "post",
- data:data,
- headers:this.getHeaders()
- })
- if (resp.headers['set-cookie']) {
- const puus = [resp.headers['set-cookie']].join(';;;').match(/__puus=([^;]+)/);
- if (puus) {
- if (this.cookie.match(/__puus=([^;]+)/)[1] != puus[1]) {
- this.cookie = this.cookie.replace(/__puus=[^;]+/, `__puus=${puus[1]}`);
- }
- }
- }
- if (resp.code !== 200 && leftRetry > 0) {
- Utils.sleep(1)
- return await this.api(url, data, leftRetry - 1);
- }
- return JSON.parse(resp.content) || {};
- }
- getShareData(url) {
- let regex = /https:\/\/pan\.quark\.cn\/s\/([^\\|#/]+)/;
- let matches = regex.exec(url);
- if (matches) {
- return {
- shareId: matches[1],
- folderId: '0',
- };
- }
- return null;
- }
- async getVip(){
- const listData = await this.api(`member?pr=ucpro&fr=pc&uc_param_str=&fetch_subscribe=true&_ch=home&fetch_identity=true`, null,null, 'get');
- if (listData.data.member_type==="EXP_SVIP"){
- return true
- }else{
- return false
- }
- }
- getPlayFormatList(){
- if(this.isVip){
- return ["4K","超清","高清","普画"]
- }else{
- return ["普画"]
- }
- }
- getPlayFormtQuarkList(){
- if(this.isVip){
- return ["4k","2k","super","high","normal","low"]
- }{
- return ["low"]
- }
- }
- async getShareToken(shareData) {
- if (!this.shareTokenCache[shareData.shareId]) {
- delete this.shareTokenCache[shareData.shareId];
- const shareToken = await this.api(`share/sharepage/token?${this.pr}`, {
- pwd_id: shareData.shareId,
- passcode: shareData.sharePwd || '',
- });
- if (shareToken.data && shareToken.data.stoken) {
- this.shareTokenCache[shareData.shareId] = shareToken.data;
- }
- }
- }
- async listFile (shareIndex,shareData,videos,subtitles,shareId, folderId, page) {
- const prePage = 200;
- page = page || 1;
- const listData = await this.api(`share/sharepage/detail?${this.pr}&pwd_id=${shareId}&stoken=${encodeURIComponent(this.shareTokenCache[shareId].stoken)}&pdir_fid=${folderId}&force=0&_page=${page}&_size=${prePage}&_sort=file_type:asc,file_name:asc`, null,null, 'get');
- if (!listData.data) return [];
- const items = listData.data.list;
- if (!items) return [];
- const subDir = [];
- for (const item of items) {
- if (item.dir === true) {
- subDir.push(item);
- } else if (item.file === true && item.obj_category === 'video') {
- if (item.size < 1024 * 1024 * 5) continue;
- item.stoken = this.shareTokenCache[shareData.shareId].stoken;
- videos.push(Item.objectFrom(item,shareData.shareId,shareIndex));
- } else if (item.type === 'file' && this.subtitleExts.some((x) => item.file_name.endsWith(x))) {
- subtitles.push(Item.objectFrom(item,shareData,shareIndex));
- }
- }
- if (page < Math.ceil(listData.metadata._total / prePage)) {
- const nextItems = await this.listFile(shareIndex,shareData.shareId,videos,subtitles,shareId, folderId, page + 1);
- for (const item of nextItems) {
- items.push(item);
- }
- }
- for (const dir of subDir) {
- const subItems = await this.listFile(shareIndex,shareData,videos,subtitles,shareId, dir.fid);
- for (const item of subItems) {
- items.push(item);
- }
- }
- return items;
- };
- findBestLCS(mainItem, targetItems) {
- const results = [];
- let bestMatchIndex = 0;
- for (let i = 0; i < targetItems.length; i++) {
- const currentLCS = Utils.lcs(mainItem.name, targetItems[i].name);
- results.push({ target: targetItems[i], lcs: currentLCS });
- if (currentLCS.length > results[bestMatchIndex].lcs.length) {
- bestMatchIndex = i;
- }
- }
- const bestMatch = results[bestMatchIndex];
- return { allLCS: results, bestMatch: bestMatch, bestMatchIndex: bestMatchIndex };
- }
- async getFilesByShareUrl(shareIndex,shareInfo,videos,subtitles){
- const shareData = typeof shareInfo === 'string' ? this.getShareData(shareInfo) : shareInfo;
- if (!shareData) return [];
- await this.getShareToken(shareData);
- if (!this.shareTokenCache[shareData.shareId]) return [];
- await this.listFile(shareIndex,shareData,videos,subtitles,shareData.shareId, shareData.folderId);
- if (subtitles.length > 0) {
- videos.forEach((item) => {
- var matchSubtitle = this.findBestLCS(item, subtitles);
- if (matchSubtitle.bestMatch) {
- item.subtitle = matchSubtitle.bestMatch.target;
- }
- });
- }
- }
- clean(){
- const saves = Object.keys(this.saveFileIdCaches);
- for (const save of saves) {
- delete this.saveFileIdCaches[save];
- }
- }
- async clearSaveDir() {
- const listData = await this.api(`file/sort?${this.pr}&pdir_fid=${this.saveDirId}&_page=1&_size=200&_sort=file_type:asc,updated_at:desc`, {}, {}, 'get');
- if (listData.data && listData.data.list && listData.data.list.length > 0) {
- await this.api(`file/delete?${this.pr}`, {
- action_type: 2,
- filelist: listData.data.list.map((v) => v.fid),
- exclude_fids: [],
- });
- }
- }
- async createSaveDir(clean) {
- if (this.saveDirId) {
- // 删除所有子文件
- if (clean) await this.clearSaveDir();
- return;
- }
- const listData = await this.api(`file/sort?${this.pr}&pdir_fid=0&_page=1&_size=200&_sort=file_type:asc,updated_at:desc`, {}, {}, 'get');
- if (listData.data && listData.data.list)
- for (const item of listData.data.list) {
- if (item.file_name === this.saveDirName) {
- this.saveDirId = item.fid;
- await this.clearSaveDir();
- break;
- }
- }
- if (!this.saveDirId) {
- const create = await this.api(`file?${this.pr}`, {
- pdir_fid: '0',
- file_name: this.saveDirName,
- dir_path: '',
- dir_init_lock: false,
- });
- if (create.data && create.data.fid) {
- this.saveDirId = create.data.fid;
- }
- }
- }
- async save(shareId, stoken, fileId, fileToken, clean) {
- await this.createSaveDir(clean);
- if (clean) {
- this.clean()
- }
- if (!this.saveDirId) return null;
- if (!stoken) {
- await this.getShareToken({
- shareId: shareId,
- });
- if (!this.shareTokenCache[shareId]) return null;
- }
- const saveResult = await this.api(`share/sharepage/save?${this.pr}`, {
- fid_list: [fileId],
- fid_token_list: [fileToken],
- to_pdir_fid: this.saveDirId,
- pwd_id: shareId,
- stoken: stoken || this.shareTokenCache[shareId].stoken,
- pdir_fid: '0',
- scene: 'link',
- });
- if (saveResult.data && saveResult.data.task_id) {
- let retry = 0;
- // wait task finish
- while (true) {
- const taskResult = await this.api(`task?${this.pr}&task_id=${saveResult.data.task_id}&retry_index=${retry}`, {}, {}, 'get');
- if (taskResult.data && taskResult.data.save_as && taskResult.data.save_as.save_as_top_fids && taskResult.data.save_as.save_as_top_fids.length > 0) {
- return taskResult.data.save_as.save_as_top_fids[0];
- }
- retry++;
- if (retry > 2) break;
- Utils.sleep(1);
- }
- }
- return false;
- }
-
- async getLiveTranscoding(shareId, stoken, fileId, fileToken,flag) {
- if (!this.saveFileIdCaches[fileId]) {
- const saveFileId = await this.save(shareId, stoken, fileId, fileToken, true);
- if (!saveFileId) return null;
- this.saveFileIdCaches[fileId] = saveFileId;
- }
- const transcoding = await this.api(`file/v2/play?${this.pr}`, {
- fid: this.saveFileIdCaches[fileId],
- resolutions: 'normal,low,high,super,2k,4k',
- supports: 'fmp4',
- });
-
- if (transcoding.data && transcoding.data.video_list) {
- let flag_id = flag.split("-").slice(-1)[0]
- let index = Utils.findAllIndexes(this.getPlayFormatList(),flag_id);
- let quarkFormat = this.getPlayFormtQuarkList()[index]
- for (const video of transcoding.data.video_list){
- if (video.resolution === quarkFormat){
- return video.video_info.url
- }
- }
- return transcoding.data.video_list[index].video_info.url
- }
- return null;
- }
- async getDownload(shareId, stoken, fileId, fileToken, clean) {
- if (!this.saveFileIdCaches[fileId]) {
- const saveFileId = await this.save(shareId, stoken, fileId, fileToken, clean);
- if (!saveFileId) return null;
- this.saveFileIdCaches[fileId] = saveFileId;
- }
- const down = await this.api(`file/download?${this.pr}&uc_param_str=`, {
- fids: [this.saveFileIdCaches[fileId]],
- });
- if (down.data) {
- return down.data[0];
- }
- return null;
- }
- }
- export {Quark}
|