diff --git a/app/src/main/java/com/github/catvod/api/QuarkApi.java b/app/src/main/java/com/github/catvod/api/QuarkApi.java new file mode 100644 index 00000000..a51817ed --- /dev/null +++ b/app/src/main/java/com/github/catvod/api/QuarkApi.java @@ -0,0 +1,326 @@ +package com.github.catvod.api; + +import java.util.Map; + +public class QuarkApi { + /* 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'; + + } + + async initQuark(cookie) { + this.ckey = Crypto.enc.Hex.stringify(Crypto.MD5(cookie)).toString(); + let localCfg = await local.get("quark", "cookie"); + if (!_.isEmpty(localCfg)) { + this.cookie = JSON.parse(localCfg)[this.ckey] + } else { + this.cookie = cookie + } + } + + private Map getHeaders() { + return Map.of( + "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] + }`); + let cookieDic = {} + cookieDic[this.ckey] = this.cookie + await local.set("quark", this.cookie, JSON.stringify(cookieDic)); + } + } + } + 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 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) { + 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) { + return transcoding.data.video_list; + } + 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; + } +*/ +} diff --git a/app/src/main/java/com/github/catvod/spider/Libvio.java b/app/src/main/java/com/github/catvod/spider/Libvio.java index 69792ce9..b1108b58 100644 --- a/app/src/main/java/com/github/catvod/spider/Libvio.java +++ b/app/src/main/java/com/github/catvod/spider/Libvio.java @@ -3,7 +3,6 @@ package com.github.catvod.spider; import android.content.Context; import com.github.catvod.bean.Class; -import com.github.catvod.bean.Filter; import com.github.catvod.bean.Result; import com.github.catvod.bean.Vod; import com.github.catvod.crawler.Spider; @@ -17,13 +16,11 @@ import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; -import java.net.URLDecoder; import java.net.URLEncoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -31,21 +28,20 @@ import java.util.regex.Pattern; public class Libvio extends Spider { private static String siteUrl = ""; - private static final String cateUrl = siteUrl + "/list/"; - private static final String detailUrl = siteUrl + "/play/"; - private static final String playUrl = siteUrl + "/play/"; - private static final String searchUrl = siteUrl + "/search--------------.html?wd="; + + + private static final String MOBILE_UA = "Mozilla/5.0 (Linux; Android 11; M2007J3SC Build/RKQ1.200826.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/6.2 TBS/045714 Mobile Safari/537.36"; private HashMap getHeaders() { HashMap headers = new HashMap<>(); - headers.put("User-Agent", Util.CHROME); + headers.put("User-Agent", MOBILE_UA); return headers; } private HashMap getHeaders(String refer) { HashMap headers = new HashMap<>(); - headers.put("User-Agent", Util.CHROME); + headers.put("User-Agent", MOBILE_UA); headers.put("Referer", refer); return headers; @@ -148,6 +144,9 @@ public class Libvio extends Spider { String PlayUrl = ""; for (int i = 0; i < tabs.size(); i++) { String tabName = tabs.get(i).text(); + if (tabName.contains("夸克")) { + continue; + } if (!"".equals(PlayFrom)) { PlayFrom = PlayFrom + "$$$" + tabName; } else { @@ -181,19 +180,18 @@ public class Libvio extends Spider { @Override public String searchContent(String key, boolean quick) throws Exception { List list = new ArrayList<>(); - Document doc = Jsoup.parse(OkHttp.string(searchUrl.concat(URLEncoder.encode(key)), getHeaders())); - for (Element element : doc.select("div.searchlist_img")) { - try { - String pic = element.select("a").attr("data-original"); - String url = element.select("a").attr("href"); - String name = element.select("a").attr("title"); - if (!pic.startsWith("http")) { - pic = siteUrl + pic; - } - String id = url.replace("/video/", "").replace(".html", "-1-1.html"); - list.add(new Vod(id, name, pic)); - } catch (Exception e) { + Document doc = Jsoup.parse(OkHttp.string(siteUrl.concat("/search/-------------.html?wd=").concat(URLEncoder.encode(key)), getHeaders())); + for (Element element : doc.select("ul.stui-vodlist > li > div > a")) { + + String pic = element.attr("data-original"); + String url = element.attr("href"); + String name = element.attr("title"); + if (!pic.startsWith("http")) { + pic = siteUrl + pic; } + String id = url.split("/")[2]; + list.add(new Vod(id, name, pic)); + } return Result.string(list); } diff --git a/app/src/test/java/LibvioTest.java b/app/src/test/java/LibvioTest.java index 7b81b92d..6686c33b 100644 --- a/app/src/test/java/LibvioTest.java +++ b/app/src/test/java/LibvioTest.java @@ -74,8 +74,8 @@ public class LibvioTest { @org.junit.Test public void playerContent() throws Exception { - String froms = "BD播放$$$视频下载 (夸克)"; - String urls = "第01集$714891769-1-1.html#第02集$714891769-1-2.html#第03集$714891769-1-3.html#第04集$714891769-1-4.html$$$合集$714891769-2-1.html"; + String froms = "BD播放"; + String urls = "第01集$714891769-1-1.html#第02集$714891769-1-2.html#第03集$714891769-1-3.html#第04集$714891769-1-4.html"; for (int i = 0; i < urls.split("\\$\\$\\$").length; i++) { String content = spider.playerContent(froms.split("\\$\\$\\$")[i], urls.split("\\$\\$\\$")[i].split("#")[0].split("\\$")[1], new ArrayList<>()); JsonObject map = Json.safeObject(content); @@ -87,7 +87,7 @@ public class LibvioTest { @org.junit.Test public void searchContent() throws Exception { - String content = spider.searchContent("红海", false); + String content = spider.searchContent("唐朝", false); JsonObject map = Json.safeObject(content); Gson gson = new GsonBuilder().setPrettyPrinting().create(); System.out.println("searchContent--" + gson.toJson(map)); diff --git a/jar/custom_spider.jar b/jar/custom_spider.jar index 11b6ac1c..8cfe4356 100644 Binary files a/jar/custom_spider.jar and b/jar/custom_spider.jar differ diff --git a/jar/custom_spider.jar.md5 b/jar/custom_spider.jar.md5 index 84cb9e4a..8e7fe768 100644 --- a/jar/custom_spider.jar.md5 +++ b/jar/custom_spider.jar.md5 @@ -1 +1 @@ -01e1c90f1873a3e193bc494b9eab2d83 +e3bc7843d766de40c20a38dd72c989cd diff --git a/json/index.json b/json/index.json index 002ef7e2..0d687868 100644 --- a/json/index.json +++ b/json/index.json @@ -1,5 +1,5 @@ { - "spider": "../jar/custom_spider.jar;md5;01e1c90f1873a3e193bc494b9eab2d83", + "spider": "../jar/custom_spider.jar;md5;e3bc7843d766de40c20a38dd72c989cd", "lives": [ { "name": "直播ipv6", @@ -70,7 +70,6 @@ "searchable": 1, "filterable": 1 }, - { "key": "W55Movie", "name": "\uD83C\uDF9E\uFE0F 555电影 | 搜索×", @@ -111,26 +110,23 @@ "searchable": 1, "filterable": 1 }, - { "key": "ikanbot", "name": "🤖┃爱看机器人┃🤖", - "type": 3, "api": "csp_Ikanbot", - + "searchable": 1, + "filterable": 0, "ext": "{\"box\": \"TVBox\", \"danmu\": false}" }, { - "key": "nangua", - "name": "🎃┃南瓜影视┃🎃", - "playerType": 0, + "key": "Libvio", + "name": "立播", "type": 3, - "api": "./js/nangua.js", - "timeout": 30, - "ext": "{\"box\": \"TVBox\", \"danmu\": false}" + "api": "csp_Libvio", + "searchable": 1, + "filterable": 0 }, - { "key": "Ysj", "name": "\uD83C\uDF0F\uFE0F 异世界动漫 | 动漫(不稳定)",