├── XPTV ├── readme.md ├── XPTV-sources.snippet ├── XPTV-sources.plugin ├── XPTV-sources.sgmodule └── spider │ ├── xptv-misc.js │ └── archive.js ├── js ├── Blank_Dict.json ├── ofiii.js ├── reddit.js ├── fix-youtube-login.js ├── reload.js ├── twitchAdBlock.js ├── real-time-debug.js ├── TikTok.js ├── qq_redirect.js ├── exchange.js ├── listenify.js ├── cmsAdblock.js └── BahamutDailyBonus.js ├── README.md ├── Bili ├── README.md ├── bilibiliShareFix.sgmodule ├── b23.js ├── BiliBili.Upos.Redirect.js ├── BiliBili.CC.FanHuaJi.js ├── BiliBili.Enhanced.sgmodule └── BiliBili.boxjs.json ├── module ├── Listenify.sgmodule ├── Reddit.sgmodule ├── fix-youtube-login.sgmodule ├── BahamutDailyBonus.sgmodule ├── ofiii.sgmodule ├── Netflix.Real.IP.sgmodule ├── TwitchAdBlock.sgmodule ├── TikTok.sgmodule ├── YoutubeAds.sgmodule ├── Search.sgmodule └── cmsAdblock.sgmodule └── loon-plugin └── cmsAdblock.plugin /XPTV/readme.md: -------------------------------------------------------------------------------- 1 | 停止更新 -------------------------------------------------------------------------------- /js/Blank_Dict.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 一些自用的Surge模組/規則 2 | -------------------------------------------------------------------------------- /Bili/README.md: -------------------------------------------------------------------------------- 1 | 原作者 @VirgilClyne 2 | 此修改僅供個人使用 3 | -------------------------------------------------------------------------------- /module/Listenify.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=Listenify 2 | #!desc=從網易雲音樂獲取歌詞與藝人封面 3 | 4 | [Script] 5 | listenify = type=http-request,pattern=https:\/\/api\.lrc\.cx\/(lyrics|cover),script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/listenify.js,script-update-interval=0 6 | 7 | [MITM] 8 | hostname = %APPEND% api.lrc.cx -------------------------------------------------------------------------------- /module/Reddit.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=Reddit 2 | #!desc=去廣告 3 | 4 | [URL Rewrite] 5 | ^https:\/\/api\.zuihuimai\.com - reject 6 | 7 | [Script] 8 | Reddit去廣告 = type=http-response,pattern=^https:\/\/gql-fed\.reddit\.com,requires-body=1,max-size=-1,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/reddit.js 9 | 10 | [MITM] 11 | hostname = %APPEND% gql-fed.reddit.com 12 | -------------------------------------------------------------------------------- /module/fix-youtube-login.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=Youtube login 2 | #!desc=解決 uyou,cercube 等無法登入的問題 3 | 4 | [Script] 5 | fix-youtube-login = type=http-request,pattern=^https:\/\/accounts\.google\.com\/embedded\/setup\/v2\/safarivc,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/fix-youtube-login.js 6 | 7 | [MITM] 8 | hostname = %APPEND% accounts.google.com -------------------------------------------------------------------------------- /Bili/bilibiliShareFix.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=BilibiliShareFix 2 | #!desc=將 分享->複製連結 裡的b23.tv轉換為b23.wtf處理過的原始連結 3 | #!category=Bilibili 4 | #!arguments=b23:https://b23.wtf 5 | 6 | [Script] 7 | bilibiliShareFix = type=http-response,pattern=https://api.(bilibili|biliapi).(com|net)/x/share/click,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/b23.js,script-update-interval=0,argument={{{b23}}} 8 | 9 | [MITM] 10 | hostname = %APPEND% api.bilibili.com, api.biliapi.net, api.biliapi.com -------------------------------------------------------------------------------- /module/BahamutDailyBonus.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=巴哈姆特自動簽到 2 | #!desc=需要 Surge 版本 ≥ 5.9.0 (測試版 build ≥ 3027) 3 | #!arguments=Cron:10 0 * * *,帳號,密碼,二步驗證Token:false,是否開啟廣告簽到:false,是否開啟公會簽到:true,是否自動答題動畫瘋:true 4 | #!arguments-desc=如果有開啟二步驗證請填入16位數Token\n預設關閉廣告簽到(耗時過長) 5 | 6 | [Script] 7 | 巴哈姆特自動簽到 = type=cron,cronexp={{{Cron}}},wake-system=1,timeout=300,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/BahamutDailyBonus.js,argument=uid={{{帳號}}}&pwd={{{密碼}}}&totp={{{二步驗證Token}}}&needSignAds={{{是否開啟廣告簽到}}}&needSignGuild={{{是否開啟公會簽到}}}&needAnswer={{{是否自動答題動畫瘋}}} -------------------------------------------------------------------------------- /js/ofiii.js: -------------------------------------------------------------------------------- 1 | if (!$response.status === 200) $done({}) 2 | if ($response.body === undefined) $done({}) 3 | 4 | let body = JSON.parse($response.body) 5 | 6 | // 直播頻道不修改 7 | if (body.id === '2') $done({}) 8 | 9 | body.section[0].item = body.section[0].item.filter((e) => e.isVod === true) 10 | 11 | //body.section.forEach((e) => { 12 | // Banner輪播 13 | // if (e.title.includes('輪播')) { 14 | // let banners = e.item 15 | // let cleanItem = [] 16 | // banners.forEach((e) => { 17 | // if (e.isVod === true) cleanItem.push(e) 18 | // }) 19 | 20 | // e.item = cleanItem 21 | // } 22 | //}) 23 | 24 | $done({ body: JSON.stringify(body) }) 25 | -------------------------------------------------------------------------------- /Bili/b23.js: -------------------------------------------------------------------------------- 1 | const obj = JSON.parse($response.body); 2 | const regex = /https:\/\/b23\.tv\/\w+/; 3 | const b23 = regex.exec(obj.data.content); 4 | const b23wtf = $argument; 5 | const request = { 6 | url: `${b23wtf}/api?full=${b23}&status=200`, 7 | headers: null 8 | }; 9 | 10 | $httpClient.get(request,function(error, response, data){ 11 | if (response.headers.hasOwnProperty("Location")) { 12 | obj.data.content = response.headers.Location; 13 | //$notification.post("b23","成功",response.headers.Location); 14 | $done({ 15 | body: JSON.stringify(obj) 16 | }); 17 | } else { 18 | $notification.post("error"); 19 | $done({}); 20 | } 21 | }); -------------------------------------------------------------------------------- /module/ofiii.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=去廣告|ofiii 2 | #!desc=移除歐飛廣告,需重新安裝 3 | 4 | [Rule] 5 | # APP 啟動廣告 6 | URL-REGEX,^https:\/\/acs2\.svc\.litv\.tv\/api\/v1\/banner$,REJECT-TINYGIF 7 | 8 | [Body Rewrite] 9 | # 播放前廣告與暫停廣告 10 | http-response https:\/\/acs2\.svc\.litv\.tv\/api\/v1\/(detail|channel) elements _elements 11 | 12 | [Script] 13 | # 移除輪播圖不是 VOD 的內容 14 | ofiii = type=http-response,pattern=^https:\/\/fino\.svc\.litv\.tv\/ofiii\/mobile\/topic\/\d+\.json$,requires-body=1,max-size=0,binary-body-mode=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/ofiii.js 15 | 16 | [MITM] 17 | hostname = %APPEND% fino.svc.litv.tv, acs2.svc.litv.tv -------------------------------------------------------------------------------- /js/reddit.js: -------------------------------------------------------------------------------- 1 | let body = JSON.parse($response.body) 2 | 3 | for (let key in body.data) { 4 | let edges = body.data[key]?.elements?.edges 5 | 6 | if (edges) { 7 | edges = edges.filter((e) => { 8 | if (e.node.adPayload) { 9 | console.log('Remove feed Ads: ' + e.node.id) 10 | return false 11 | } else if (e.node.__typename === 'AdPost') { 12 | return false 13 | } 14 | return true 15 | }) 16 | body.data[key].elements.edges = edges 17 | } 18 | } 19 | 20 | if (body.data?.children?.commentsPageAds?.length) { 21 | body.data.children.commentsPageAds = [] 22 | console.log('Remove comment page Ads.') 23 | } 24 | 25 | $done({ 26 | body: JSON.stringify(body), 27 | }) 28 | -------------------------------------------------------------------------------- /module/Netflix.Real.IP.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=Netflix Real IP 2 | #!desc=防 ban ip 3 | #!arguments=PROXY 4 | #!arguments-desc=填入 Netflix 的策略組或代理名稱,直連為 DIRECT 5 | 6 | [General] 7 | always-real-ip = %APPEND% ios.prod.ftl.netflix.com, ios.prod.cloud.netflix.com, ios.ngp.prod.cloud.netflix.com, appboot.netflix.com 8 | hijack-dns = %APPEND% 8.8.8.8:53, 8.8.4.4:53 9 | 10 | [Rule] 11 | RULE-SET,Netflix,{{{PROXY}}} 12 | RULE-SET,https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Surge/Netflix/Netflix.list,{{{PROXY}}} 13 | 14 | [Ruleset Netflix] 15 | DOMAIN,ios.prod.cloud.netflix.com,extended-matching 16 | DOMAIN,ios.prod.ftl.netflix.com,extended-matching 17 | DOMAIN,ios.ngp.prod.cloud.netflix.com,extended-matching 18 | DOMAIN,appboot.netflix.com,extended-matching 19 | -------------------------------------------------------------------------------- /module/TwitchAdBlock.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=Twitch去廣告 2 | #!desc=Avoid Twitch ads by grabbing video playlists from Russia 3 | #!arguments=luminous:https://as.luminous.dev 4 | #!arguments-desc=預設使用luminous-ttv作者提供的實例,更多請參考作者repo或者自建\nhttps://lb-eu.cdn-perfprod.com\nhttps://lb-eu2.cdn-perfprod.com\nhttps://lb-na.cdn-perfprod.com\nhttps://lb-as.cdn-perfprod.com\nhttps://lb-sa.cdn-perfprod.com\nhttps://twitch.zzls.xyz\nhttps://eu.luminous.dev\nhttps://eu2.luminous.dev\nhttps://as.luminous.dev 5 | 6 | [Script] 7 | Twitch去廣告 = type=http-response,pattern=https://usher.ttvnw.net/(api/channel/hls|vod),requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/twitchAdBlock.js,argument={{{luminous}}},binary-body-mode=0 8 | 9 | [MITM] 10 | hostname = %APPEND% usher.ttvnw.net -------------------------------------------------------------------------------- /Bili/BiliBili.Upos.Redirect.js: -------------------------------------------------------------------------------- 1 | const hk = [ 2 | "cn-hk-eq-01-01.bilivideo.com", 3 | "cn-hk-eq-01-02.bilivideo.com", 4 | "cn-hk-eq-01-03.bilivideo.com", 5 | "cn-hk-eq-01-04.bilivideo.com", 6 | "cn-hk-eq-01-05.bilivideo.com", 7 | "cn-hk-eq-01-06.bilivideo.com", 8 | "cn-hk-eq-01-07.bilivideo.com", 9 | "cn-hk-eq-01-08.bilivideo.com", 10 | "cn-hk-eq-01-09.bilivideo.com", 11 | "cn-hk-eq-01-10.bilivideo.com", 12 | "cn-hk-eq-01-11.bilivideo.com", 13 | "cn-hk-eq-01-12.bilivideo.com", 14 | "cn-hk-eq-01-13.bilivideo.com", 15 | ]; 16 | 17 | let path = $request.url.split("/").slice(3).join("/"); 18 | let newHost = hk[Math.floor(Math.random() * hk.length)]; 19 | let newUrl = `http://${newHost}/${path}`; 20 | 21 | $done({ 22 | response: { 23 | status: 302, 24 | headers: { 25 | Location: newUrl, 26 | }, 27 | }, 28 | }); -------------------------------------------------------------------------------- /js/fix-youtube-login.js: -------------------------------------------------------------------------------- 1 | function removeUrlParameter(url,parameter) { 2 | var urlParts = url.split('?'); 3 | if(urlParts.length >= 2) { 4 | var urlBase = urlParts.shift(); 5 | var queryString = urlParts.join('?'); 6 | var prefix = encodeURIComponent(parameter) + '='; 7 | var parts = queryString.split(/[&;]/g); 8 | for(var i = parts.length;i-->0;) { 9 | if(parts[i].lastIndexOf(prefix,0) !==-1) { 10 | parts.splice(i,1); 11 | } 12 | } 13 | url = urlBase + '?' + parts.join('&'); 14 | } 15 | return url; 16 | } 17 | 18 | var url = $request.url; 19 | if(url.indexOf("system_version") !=-1) { 20 | url = removeUrlParameter(url, "system_version"); 21 | url = removeUrlParameter(url, "app_version"); 22 | url = removeUrlParameter(url, "kdlc"); 23 | url = removeUrlParameter(url, "kss"); 24 | url = removeUrlParameter(url, "lib_ver"); 25 | url = removeUrlParameter(url, "device_model") 26 | $done({ response: { status: 302, headers: { Location: url } } }); 27 | } else { 28 | $done({}) 29 | } -------------------------------------------------------------------------------- /js/reload.js: -------------------------------------------------------------------------------- 1 | !(async () => { 2 | let panel = { title: "Profile Reload" }, 3 | showServer = true, 4 | dnsCache; 5 | if (typeof $argument != "undefined") { 6 | let arg = Object.fromEntries($argument.split("&").map((item) => item.split("="))); 7 | if (arg.title) panel.title = arg.title; 8 | if (arg.icon) panel.icon = arg.icon; 9 | if (arg.color) panel["icon-color"] = arg.color; 10 | if (arg.server == "false") showServer = false; 11 | } 12 | if (showServer) { 13 | dnsCache = (await httpAPI("/v1/dns", "GET")).dnsCache; 14 | dnsCache = [...new Set(dnsCache.map((d) => d.server))].toString().replace(/,/g, "\n"); 15 | } 16 | if ($trigger == "button") { 17 | await httpAPI("/v1/profiles/reload"); 18 | await httpAPI("/v1/dns/flush"); 19 | $notification.post("Reload Profile", "配置重載成功,DNS 緩存已清理"); 20 | } 21 | let delay = ((await httpAPI("/v1/test/dns_delay")).delay * 1000).toFixed(0); 22 | panel.content = `DNS delay: ${delay}ms${dnsCache ? `\nserver:\n${dnsCache}` : ""}`; 23 | $done(panel); 24 | })(); 25 | 26 | function httpAPI(path = "", method = "POST", body = null) { 27 | return new Promise((resolve) => { 28 | $httpAPI(method, path, body, (result) => { 29 | resolve(result); 30 | }); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /Bili/BiliBili.CC.FanHuaJi.js: -------------------------------------------------------------------------------- 1 | const api = 'https://api.zhconvert.org/convert' 2 | const body = JSON.parse($response.body) 3 | const lines = body.body.map((line) => line.content.replace(/\n/g, 'ykusu')) 4 | 5 | convert(lines.join('\n')) 6 | .then((text) => { 7 | let newlines = text.split('\n') 8 | body.body.forEach((line, index) => { 9 | line.content = newlines[index].replace(/ykusu/g, '\n') 10 | }) 11 | $done({ status: 200, body: JSON.stringify(body) }) 12 | }) 13 | .catch((error) => { 14 | console.error(error) 15 | $done({}) 16 | }) 17 | 18 | function convert(text) { 19 | return new Promise((resolve, reject) => { 20 | const request = { 21 | url: api, 22 | headers: { 23 | 'Content-Type': 'application/x-www-form-urlencoded', 24 | }, 25 | body: `converter=Taiwan&text=${encodeURIComponent(text)}`, 26 | } 27 | $httpClient.post(request, (error, response, data) => { 28 | if (error) { 29 | reject(error) 30 | } else { 31 | try { 32 | let obj = JSON.parse(data) 33 | let convertedText = obj.data.text 34 | resolve(convertedText) 35 | } catch (error) { 36 | reject(error) 37 | } 38 | } 39 | }) 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /module/TikTok.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=TikTok 2 | #!desc=新版 TikTok 禁止 MITM ,需先降為21.1.0 3 | 4 | [Rule] 5 | DOMAIN,log.tiktokv.com,REJECT 6 | DOMAIN,log22-normal-useast1a.tiktokv.com,REJECT 7 | DOMAIN,rtlog22-normal-useast1a.tiktokv.com,REJECT 8 | 9 | [URL Rewrite] 10 | ^https?:\/\/(.+)\.tiktokv\.com\/api\/ad\/v1\/splash - reject 11 | ^https:\/\/api\.tiktokv\.com\/tiktok\/v1\/fyp\/user\/recommendations - reject 12 | ^https:\/\/api\.tiktokv\.com\/location\/region - reject 13 | # KR 14 | (?<=_region=)TW(?=&) KR 307 15 | (?<=&mcc_mnc=)466[0-1][0-9](?=&) 45005 307 16 | (?<=&carrier=)[%A-Z0-9]+(?=&) SKT 307 17 | (?<=tz_name=)Asia/Taipei(?=&) Asia/Seoul 307 18 | (?<=timezone=)8(?=&) 9 307 19 | ^(https?:\/\/(tnc|dm)[\w-]+\.\w+\.com\/.+)(\?)(.+) $1$3 302 20 | (^https?:\/\/*\.\w{4}okv.com\/.+&.+)(\d{2}\.3\.\d)(.+) $118.0$3 302 21 | # 去水印 22 | (?<=eme\/v)2(?=\/f\w{2}d\/\?.*) 1 302 23 | 24 | [Script] 25 | 去水印 = type=http-response,pattern=https?:\/\/.*\.tiktokv\.com\/aweme\/v\d\/(feed|mix\/aweme|aweme\/post|(multi\/)?aweme\/detail|follow\/feed|nearby\/feed|search\/item|general\/search\/single|hot\/search\/video\/list|aweme\/favorite),requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/TikTok.js,script-update-interval=0 26 | 27 | [MITM] 28 | hostname = %APPEND% *.tiktokv.com, *.byteoversea.com, *.tik-tokapi.com -------------------------------------------------------------------------------- /module/YoutubeAds.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=Youtube (Music) Enhance 2 | #!desc=Surge-Build ≥ 2700 and IOS ≥ 15 3 | #!arguments=移除上傳按鈕:true,移除選段按鈕:true,字幕翻譯語言:zh-Hant,歌詞翻譯語言:zh-Hant 4 | 5 | # 作者: @Maasea,@VirgilClyne 6 | # 僅繁化 7 | 8 | [Rule] 9 | # 禁用 YouTube QUIC 10 | AND,((DOMAIN-SUFFIX,googlevideo.com), (PROTOCOL,UDP)),REJECT 11 | AND,((DOMAIN,youtubei.googleapis.com), (PROTOCOL,UDP)),REJECT 12 | 13 | [Script] 14 | youtube.request = type=http-request,pattern=^https:\/\/youtubei\.googleapis\.com\/youtubei\/v1\/(browse|next|player|reel\/reel_watch_sequence),requires-body=1,max-size=-1,binary-body-mode=1,script-path=https://raw.githubusercontent.com/Maasea/sgmodule/master/Script/Youtube/dist/youtube.request.preview.js 15 | youtube.response = type=http-response,pattern=^https:\/\/youtubei\.googleapis\.com\/youtubei\/v1\/(browse|next|player|search|reel\/reel_watch_sequence|guide|account\/get_setting),requires-body=1,max-size=-1,binary-body-mode=1,script-path=https://raw.githubusercontent.com/Maasea/sgmodule/master/Script/Youtube/dist/youtube.response.preview.js,argument="{"lyricLang":"{{{歌詞翻譯語言}}}","captionLang":"{{{字幕翻譯語言}}}","blockUpload":{{{移除上傳按鈕}}},"immersive":{{{移除選段按鈕}}}}" 16 | 17 | [Map Local] 18 | ^https?:\/\/[\w-]+\.googlevideo\.com\/initplayback.+&oad data="https://raw.githubusercontent.com/Maasea/sgmodule/master/Script/Youtube/blank.txt" 19 | 20 | [MITM] 21 | hostname = %APPEND% *.googlevideo.com, youtubei.googleapis.com -------------------------------------------------------------------------------- /XPTV/XPTV-sources.snippet: -------------------------------------------------------------------------------- 1 | #!name=XPTV Sources 2 | #!desc=Format video data into CMS api structure 3 | 4 | [rewrite_local] 5 | ^https:\/\/tencent-1257389134\.cos\.ap-nanjing\.myqcloud\.com\.qqhd\.net\/fangdao\.php url response-header (\r\n)Content-Type:.*(\r\n) response-header $1Content-Type: application/vnd.apple.mpegURL$2 6 | 7 | https?:\/\/ykusu\.ykusu\/(.*)\/provide\/vod url script-request-header https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/spider.js 8 | https?:\/\/ykusu\.ykusu\/getJSON\/(.*).json url script-request-header https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/spider.js 9 | 10 | api\.emasmr\.com.*mp4 url script-request-header https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js 11 | ppvod01\.blbtgg\.com.*(ts|m3u8) url script-request-header https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js 12 | https?:\/\/hd\.suxun\.site\/api\.php\/provide\/vod(.*)&ids url script-response-body https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js 13 | https?:\/\/vipcj\.timizy\.top\/api\.php\/provide\/vod\/from\/mgtv(.*)&ids url script-response-body https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js 14 | https?:\/\/dsakf23665\.com\/api\.php\/provide\/vod(.*)&ids url script-response-body https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js 15 | https:\/\/42\.157\.129\.29:2222\/.*\.png url script-response-body https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js 16 | 17 | [mitm] 18 | hostname = ykusu.ykusu, tencent-1257389134.cos.ap-nanjing.myqcloud.com.qqhd.net, api.emasmr.com, ppvod01.blbtgg.com, vipcj.timizy.top, 42.157.129.29:2222 -------------------------------------------------------------------------------- /js/twitchAdBlock.js: -------------------------------------------------------------------------------- 1 | const luminous = $argument; 2 | const ping = luminous + "/ping"; 3 | const url = $request.url; 4 | const cleanURL = removeUrlParameters(url, ["token", "sig", "p", "play_session_id"]); // 移除網址中的敏感資訊 5 | 6 | if (url.includes("https://usher.ttvnw.net/api/channel/hls")) { 7 | var live = cleanURL.slice("https://usher.ttvnw.net/api/channel/hls/".length); 8 | redirect(live); 9 | } else if (url.includes("https://usher.ttvnw.net/vod")) { 10 | var vod = cleanURL.slice("https://usher.ttvnw.net/vod/".length); 11 | redirect(vod); 12 | } 13 | 14 | function redirect(path) { 15 | $httpClient.get(ping, function (error, response) { 16 | if (response.status != 200) { 17 | $done({}); 18 | } else if (live) { 19 | const proxy = `${luminous}/live/${encodeURIComponent(path)}`; 20 | $done({ 21 | status: 302, 22 | headers: { 23 | Location: proxy, 24 | }, 25 | }); 26 | } else if (vod) { 27 | const proxy = `${luminous}/vod/${encodeURIComponent(path)}`; 28 | $done({ 29 | status: 302, 30 | headers: { 31 | Location: proxy, 32 | }, 33 | }); 34 | } 35 | }); 36 | } 37 | 38 | function removeUrlParameters(url, parameters) { 39 | let urlParts = url.split("?"); 40 | if (urlParts.length >= 2) { 41 | let urlBase = urlParts.shift(); 42 | let queryString = urlParts.join("?"); 43 | let parts = queryString.split(/[&;]/g); 44 | parts = parts.filter((part) => { 45 | for (let j = 0; j < parameters.length; j++) { 46 | const parameter = parameters[j]; 47 | let prefix = encodeURIComponent(parameter) + "="; 48 | if (part.lastIndexOf(prefix, 0) === 0) { 49 | return false; 50 | } 51 | } 52 | return true; 53 | }); 54 | url = urlBase + "?" + parts.join("&"); 55 | } 56 | return url; 57 | } -------------------------------------------------------------------------------- /js/real-time-debug.js: -------------------------------------------------------------------------------- 1 | /* 2 | * LAN script real-time debug 3 | * 4 | * PC: Use "Live Server" plugin in VSCode to create a LAN backend 5 | * APP: After backend address is modified in script, use this script as script path 6 | */ 7 | 8 | !async function() { 9 | const _$ = new nobyda(); 10 | const _r = await new Promise(e => { 11 | _$.get({ 12 | url: 'http://192.168.50.136:5500/debug.js' // LAN backend address 13 | }, (t, c, o) => { 14 | if (c && c.status == 200 && o) { 15 | _$.write(o, 'Real-time-debug'); 16 | e(o); 17 | } 18 | }); 19 | setTimeout(e, 100); 20 | }); 21 | if (_r) { 22 | console.log("🌐 Run local network script..."); 23 | eval(_r); 24 | } else { 25 | console.log("⚠️ Run cache script..."); 26 | eval(_$.read('Real-time-debug')) 27 | } 28 | 29 | function nobyda() { 30 | const isSurge = typeof $httpClient != "undefined"; 31 | const isQuanX = typeof $task != "undefined"; 32 | const adapterStatus = (response) => { 33 | if (response) { 34 | if (response.status) { 35 | response["statusCode"] = response.status 36 | } else if (response.statusCode) { 37 | response["status"] = response.statusCode 38 | } 39 | } 40 | return response 41 | }; 42 | this.write = (value, key) => { 43 | if (isQuanX) return $prefs.setValueForKey(value, key); 44 | if (isSurge) return $persistentStore.write(value, key); 45 | }; 46 | this.read = (key) => { 47 | if (isQuanX) return $prefs.valueForKey(key); 48 | if (isSurge) return $persistentStore.read(key); 49 | }; 50 | this.get = (options, callback) => { 51 | if (isQuanX) { 52 | $task.fetch(options).then(response => { 53 | callback(null, adapterStatus(response), response.body) 54 | }, reason => callback(reason.error, null, null)) 55 | } 56 | if (isSurge) { 57 | $httpClient.get(options, (error, response, body) => { 58 | callback(error, adapterStatus(response), body) 59 | }) 60 | } 61 | }; 62 | this.done = (value = {}) => $done(value) 63 | } 64 | }(); -------------------------------------------------------------------------------- /XPTV/XPTV-sources.plugin: -------------------------------------------------------------------------------- 1 | #!name=XPTV Sources 2 | #!desc=Format video data into CMS api structure 3 | #!icon=https://raw.githubusercontent.com/sooyaaabo/Loon/main/Icon/XPTV.png 4 | 5 | [Rewrite] 6 | #^https:\/\/tencent-1257389134\.cos\.ap-nanjing\.myqcloud\.com\.qqhd\.net\/fangdao\.php header-replace Content-Type application/vnd.apple.mpegURL 7 | 8 | [Script] 9 | http-request https?:\/\/ykusu\.ykusu\/(.*)\/provide\/vod script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/spider.js, tag=XPTV-sources 10 | http-request https?:\/\/ykusu\.ykusu\/getJSON\/(.*).json script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/spider.js, tag=XPTV-getJSON 11 | http-response ^https:\/\/tencent-1257389134\.cos\.ap-nanjing\.myqcloud\.com\.qqhd\.net\/fangdao\.php script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js, requires-body=true, timeout=10, tag=美劇星球 12 | http-request api\.emasmr\.com.*mp4 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js, requires-body=true, timeout=10, tag=短劇天堂 13 | http-response https?:\/\/hd\.suxun\.site\/api\.php\/provide\/vod(.*)&ids script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js, requires-body=true, timeout=10, tag=sx 14 | http-request ppvod01\.blbtgg\.com.*(ts|m3u8) script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js, requires-body=true, timeout=10, tag=文才 15 | http-response https?:\/\/vipcj\.timizy\.top\/api\.php\/provide\/vod\/from\/mgtv(.*)&ids script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js, requires-body=true, timeout=10, tag=mg 16 | http-response https?:\/\/dsakf23665\.com\/api\.php\/provide\/vod(.*)&ids script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js, requires-body=true, timeout=10, tag=dsakf23665 17 | http-response https:\/\/42\.157\.129\.29:2222\/.*\.png script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js, requires-body=true, timeout=10, tag=jx 18 | 19 | [MITM] 20 | hostname = ykusu.ykusu, tencent-1257389134.cos.ap-nanjing.myqcloud.com.qqhd.net, api.emasmr.com, ppvod01.blbtgg.com, vipcj.timizy.top, 42.157.129.29:2222 -------------------------------------------------------------------------------- /module/Search.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=Safari Search Add-ons 2 | #!desc=須將預設搜尋引擎改為Duckduckgo 3 | 4 | [URL Rewrite] 5 | # DeepL 翻譯為中文 觸發詞: dl DeepL 6 | ^https:\/\/duckduckgo\.com\/\?q=(?i:dl|DeepL)\+([^&]+)&.+ https://www.deepl.com/translator#../zh/$1 302 7 | ^https:\/\/duckduckgo\.com\/\?q=(.+)\+(?i:dl|DeepL)&.+ https://www.deepl.com/translator#../zh/$1 302 8 | # DeepL 翻譯為英語 觸發詞: dle dlen 9 | ^https:\/\/duckduckgo\.com\/\?q=(?i:dlen?)\+([^&]+)&.+ https://www.deepl.com/translator#../en/$1 302 10 | ^https:\/\/duckduckgo\.com\/\?q=(.+)\+(?i:dlen?)&.+ https://www.deepl.com/translator#../en/$1 302 11 | # wk xxx/xxx wk (搜尋wiki) 12 | ^https:\/\/duckduckgo.com\/\?q=wk\+([^&]+).+ https://zh.wikipedia.org/wiki/$1 302 13 | ^https:\/\/duckduckgo.com\/\?q=([^+]+)\+wk.+ https://zh.wikipedia.org/wiki/$1 302 14 | # trc xxx/xxx trc (Google翻譯成繁體中文) 15 | ^https:\/\/duckduckgo.com\/\?q=([^+]+)\+trc.+ https://translate.google.com/#view=home&op=translate&sl=auto&tl=zh-TW&text=$1 302 16 | ^https:\/\/duckduckgo.com\/\?q=trc\+([^&]+).+ https://translate.google.com/#view=home&op=translate&sl=auto&tl=zh-TW&text=$1 302 17 | # tw (App store台區) 18 | ^https:\/\/duckduckgo.com\/\?q=tw&.+ https://itunes.apple.com/WebObjects/MZStore.woa/wa/resetAndRedirect?dsf=143470&mt=8&url=/WebObjects/MZStore.woa/wa/viewSoftware?mt=8&id=1108187390&cc=tw&urlDesc= 302 19 | # us (App store美區) 20 | ^https:\/\/duckduckgo.com\/\?q=us&.+ https://itunes.apple.com/WebObjects/MZStore.woa/wa/resetAndRedirect?dsf=143441&mt=8&url=/WebObjects/MZStore.woa/wa/viewSoftware?mt=8&id=1108187390&cc=us&urlDesc= 302 21 | # jp (App store日區) 22 | ^https:\/\/duckduckgo.com\/\?q=jp&.+ https://itunes.apple.com/WebObjects/MZStore.woa/wa/resetAndRedirect?dsf=143462&mt=8&url=/WebObjects/MZStore.woa/wa/viewSoftware?mt=8&id=1108187390&cc=jp&urlDesc= 302 23 | # kr (App store韓區) 24 | ^https:\/\/duckduckgo.com\/\?q=kr&.+ https://itunes.apple.com/WebObjects/MZStore.woa/wa/resetAndRedirect?dsf=143466&mt=8&url=/WebObjects/MZStore.woa/wa/viewSoftware?mt=8&id=1108187390&cc=kr&urlDesc= 302 25 | # tr (App store土區) 26 | ^https:\/\/duckduckgo.com\/\?q=tr&.+ https://itunes.apple.com/WebObjects/MZStore.woa/wa/resetAndRedirect?dsf=143480&mt=8&url=/WebObjects/MZStore.woa/wa/viewSoftware?mt=8&id=1108187390&cc=tr&urlDesc= 302 27 | # 預設Google搜尋 28 | ^https:\/\/duckduckgo.com\/\?q=([^&]+).+ https://www.google.com/search?q=$1 302 29 | 30 | [MITM] 31 | hostname = %APPEND% duckduckgo.com -------------------------------------------------------------------------------- /XPTV/XPTV-sources.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=XPTV Sources 2 | #!desc=Format video data into CMS api structure 3 | 4 | [Script] 5 | XPTV-sources = type=http-request,pattern=https?:\/\/ykusu\.ykusu\/(.*)\/provide\/vod,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/spider.js,script-update-interval=0,requires-body=0 6 | XPTV-getJSON = type=http-request,pattern=https?:\/\/ykusu\.ykusu\/getJSON\/(.*).json,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/spider.js,script-update-interval=0,requires-body=0 7 | 美劇星球 = type=http-response,pattern=^https:\/\/tencent-1257389134\.cos\.ap-nanjing\.myqcloud\.com\.qqhd\.net\/fangdao\.php,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js,max-size=0,requires-body=true,timeout=10,script-update-interval=0 8 | 短劇天堂 = type=http-request,pattern=api\.emasmr\.com.*mp4,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js,max-size=0,requires-body=true,timeout=10,script-update-interval=0 9 | sx = type=http-response,timeout=30,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js,script-update-interval=0,pattern=https?:\/\/hd\.suxun\.site\/api\.php\/provide\/vod(.*)&ids,requires-body=1,max-size=0 10 | 文才 = type=http-request,pattern=ppvod01\.blbtgg\.com.*(ts|m3u8),script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js,max-size=0,requires-body=true,timeout=10,script-update-interval=0 11 | mg = type=http-response,timeout=30,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js,script-update-interval=0,pattern=https?:\/\/vipcj\.timizy\.top\/api\.php\/provide\/vod\/from\/mgtv(.*)&ids,requires-body=true,max-size=0 12 | dsakf23665 = type=http-response,timeout=30,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js,script-update-interval=0,pattern=https?:\/\/dsakf23665\.com\/api\.php\/provide\/vod(.*)&ids,requires-body=true,max-size=0 13 | jx = type=http-response,timeout=30,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/XPTV/spider/xptv-misc.js,script-update-interval=0,pattern=https:\/\/42\.157\.129\.29:2222\/.*\.png,requires-body=true,max-size=0 14 | 15 | [MITM] 16 | hostname = %APPEND% ykusu.ykusu, tencent-1257389134.cos.ap-nanjing.myqcloud.com.qqhd.net, api.emasmr.com, ppvod01.blbtgg.com, vipcj.timizy.top, 42.157.129.29:2222 -------------------------------------------------------------------------------- /js/TikTok.js: -------------------------------------------------------------------------------- 1 | var watermark = body => { 2 | try { 3 | body.replace(/\"room_id\":(\d{2,})/g, '"room_id":"$1"'); 4 | let obj = JSON.parse(body); 5 | if (obj.data) obj.data = Follow(obj.data); 6 | if (obj.aweme_list) obj.aweme_list = Feed(obj.aweme_list); 7 | if (obj.aweme_detail) obj.aweme_detail = Share(obj.aweme_detail); 8 | if (obj.aweme_details) obj.aweme_details = Feed(obj.aweme_details); 9 | $done({ body: JSON.stringify(obj) }); 10 | } catch (err) { 11 | console.log("替換錯誤:" + err); 12 | $done({}); 13 | } 14 | } 15 | 16 | watermark($response.body); 17 | 18 | function Follow(data) { 19 | if (data && data.length > 0) { 20 | for (let i in data) { 21 | if (data[i].aweme.video) video_lists(data[i].aweme); 22 | } 23 | } 24 | return data; 25 | } 26 | 27 | function Feed(aweme_list) { 28 | if (aweme_list && aweme_list.length > 0) { 29 | for (let i in aweme_list) { 30 | if (aweme_list[i].is_ads == true) { 31 | aweme_list.splice(i, 1); 32 | } else if (aweme_list[i].video) { 33 | video_lists(aweme_list[i]); 34 | } else { 35 | if (!enabled_live) aweme_list.splice(i, 1); 36 | } 37 | } 38 | } 39 | return aweme_list; 40 | } 41 | 42 | function Share(aweme_detail) { 43 | if (aweme_detail.video) video_lists(aweme_detail); 44 | return aweme_detail; 45 | } 46 | 47 | function video_lists(lists) { 48 | lists.prevent_download = false; 49 | lists.status.reviewed = 1; 50 | lists.video_control.allow_download = true; 51 | lists.video_control.prevent_download_type = 0; 52 | lists.video_control.show_progress_bar = 1; 53 | lists.video_control.draft_progress_bar = 1; 54 | delete lists.video.misc_download_addrs; 55 | lists.video.download_addr = lists.video.play_addr; 56 | lists.video.download_suffix_logo_addr = lists.video.play_addr; 57 | lists.aweme_acl.download_general.mute = false; 58 | if (lists.aweme_acl.download_general.extra) { 59 | delete lists.aweme_acl.download_general.extra; 60 | lists.aweme_acl.download_general.code = 0; 61 | lists.aweme_acl.download_general.show_type = 2; 62 | lists.aweme_acl.download_general.transcode = 3; 63 | lists.aweme_acl.download_mask_panel = lists.aweme_acl.download_general; 64 | lists.aweme_acl.share_general = lists.aweme_acl.download_general; 65 | } 66 | return lists; 67 | } -------------------------------------------------------------------------------- /XPTV/spider/xptv-misc.js: -------------------------------------------------------------------------------- 1 | const { url } = $request 2 | let headers 3 | 4 | switch (true) { 5 | // 美劇星球播放防盜 6 | case url.includes('tencent-1257389134'): 7 | headers = getHeaders($response) 8 | headers['content-type'] = 'application/vnd.apple.mpegURL' 9 | $done({ headers: headers }) 10 | break 11 | case url.includes('42.157.129.29:2222'): 12 | headers = getHeaders($response) 13 | headers['content-type'] = 'application/vnd.apple.mpegURL' 14 | $done({ headers: headers }) 15 | break 16 | // 短劇天堂添加 referer 17 | case url.includes('api.emasmr.com'): 18 | headers = getHeaders($request) 19 | headers['user-agent'] = 20 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36' 21 | headers['referer'] = 'https://duanjutt.tv/' 22 | $done({ headers: headers }) 23 | break 24 | case url.includes('hd.suxun.site'): 25 | let obj = JSON.parse($response.body) 26 | 27 | let playlist = obj.list[0].vod_play_url.split('#') 28 | let newPlaylist = [] 29 | playlist.forEach((e) => { 30 | let url = e.split('$')[1] 31 | url = 32 | 'https://ykusu.ykusu/sx/provide/vod?ac=play&url=' + 33 | encodeURIComponent(url) + 34 | '&n=.m3u8' 35 | newPlaylist.push(e.split('$')[0] + '$' + url) 36 | }) 37 | obj.list[0].vod_play_url = newPlaylist.join('#') 38 | 39 | $done({ body: JSON.stringify(obj) }) 40 | break 41 | case url.includes('ppvod01.blbtgg.com'): 42 | headers = getHeaders($request) 43 | headers['user-agent'] = 44 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36' 45 | headers['referer'] = 'https://www.ghw9zwp5.com/' 46 | $done({ headers: headers }) 47 | break 48 | case url.includes('vipcj.timizy.top'): 49 | try { 50 | let obj = JSON.parse($response.body) 51 | 52 | let playlist = obj.list[0].vod_play_url.split('#') 53 | let newPlaylist = [] 54 | playlist.forEach((e) => { 55 | let url = e.split('$')[1] 56 | url = 57 | 'https://ykusu.ykusu/mgtimi/provide/vod?ac=play&url=' + 58 | encodeURIComponent(url) + 59 | '&n=.m3u8' 60 | newPlaylist.push(e.split('$')[0] + '$' + url) 61 | }) 62 | obj.list[0].vod_play_url = newPlaylist.join('#') 63 | 64 | $done({ body: JSON.stringify(obj) }) 65 | } catch (e) { 66 | $.log(e) 67 | $.done({}) 68 | } 69 | 70 | break 71 | case url.includes('dsakf23665'): 72 | try { 73 | let obj = JSON.parse($response.body) 74 | 75 | let playlist = obj.list[0].vod_play_url.split('#') 76 | let from = obj.list[0].vod_play_from 77 | let id = obj.list[0].vod_id 78 | let newPlaylist = [] 79 | playlist.forEach((e) => { 80 | let url = e.split('$')[1] 81 | url = 82 | 'https://ykusu.ykusu/dsakf23665/provide/vod?ac=play&url=' + 83 | id + 84 | '@@@' + 85 | from + 86 | '@@@' + 87 | encodeURIComponent(url) + 88 | '&n=.m3u8' 89 | newPlaylist.push(e.split('$')[0] + '$' + url) 90 | }) 91 | obj.list[0].vod_play_url = newPlaylist.join('#') 92 | 93 | $done({ body: JSON.stringify(obj) }) 94 | } catch (e) { 95 | $.log(e) 96 | $.done({}) 97 | } 98 | 99 | break 100 | default: 101 | $done({}) 102 | break 103 | } 104 | 105 | function getHeaders(obj) { 106 | let headers = {} 107 | for (let key in obj.headers) { 108 | headers[key.toLowerCase()] = obj.headers[key] 109 | } 110 | return headers 111 | } 112 | -------------------------------------------------------------------------------- /loon-plugin/cmsAdblock.plugin: -------------------------------------------------------------------------------- 1 | #!name=去廣告|CMS 2 | #!desc=去除插入式廣告 3 | #!icon=https://raw.githubusercontent.com/sooyaaabo/Loon/main/Icon/XPTV.png 4 | #!select=彈窗通知,true,false 5 | 6 | [Rewrite] 7 | # 暴風跑馬燈 8 | (^https?:\/\/s\d+\.bfengbf\.com\/video)(.+)_z5q\.ts $1$2.ts 302 9 | 10 | [Script] 11 | http-response ^https?:\/\/m3u8\.(hmrvideo|heimuertv)\.com\/play\/(.+).m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|黑木耳 12 | 13 | http-response ^https?:\/\/((?:v\.lzcdn|v\.cdnlz|vip.*\.lz[-]?cdn)\d+\.com)\/.+\/hls\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|量子資源 14 | 15 | http-response ^https?:\/\/(super|svipsvip|vip)\.ffzy[A-Za-z0-9-]+\.com\/.+\/hls\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|非凡資源 16 | 17 | http-response ^https?:\/\/c\d+\.rrcdnbf\d+\.com\/video\/(.+)\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|暴風影視 18 | 19 | http-response ^https?:\/\/vip\.kuaikan-(cdn|play)\d+\.com\/.+\/hls\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|快看資源 20 | 21 | http-response ^https:\/\/bfikuncdn\.com\/.+\/hls\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|ikun 22 | 23 | http-response ^https:\/\/play\.modujx\d+\.com\/.+\/hls\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|魔都動漫 24 | 25 | http-response ^https:\/\/vod\.lyhuicheng\.com\/.+\/hls\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|360 26 | 27 | http-response ^https:\/\/ukzy\.ukubf\d+\.com\/.+\/hls\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|U酷資源 28 | 29 | http-response ^https:\/\/.+\.wgslsw\.com\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|櫻花資源 30 | 31 | http-response ^https:\/\/leshiyuncdn\.36s\.top\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|樂視資源 32 | 33 | http-response ^https:\/\/.+\.feidaozy\.com\/.+\/hls\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|飛刀資源 34 | 35 | http-response ^https:\/\/askzycdn\.com\/.+\/hls\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|奧斯卡資源 36 | 37 | http-response ^https:\/\/.+\.97img\.com\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|91麻豆 38 | 39 | http-response ^https:\/\/.+\.bfbfhao\.com\/.+\/hls\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|番號資源 40 | 41 | http-response ^https:\/\/player\.cl9987\.com:188\/.+\/hls\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|草榴資源 42 | 43 | http-response ^https:\/\/v\.ykv3\.com\/.+\/hls\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|易看資源 44 | 45 | http-response ^https:\/\/.+\.3sybf\.com\/.+\/hls\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|鯊魚資源 46 | 47 | http-response ^https:\/\/bfnxxcdn\.com\/.+\/hls\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|奶香香 48 | 49 | http-response ^https:\/\/player\.huangguam3u\.com\/.+\/hls\/.+\.m3u8 script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js, requires-body=true, tag=去廣告|黃瓜資源 50 | 51 | [MITM] 52 | hostname = m3u8.heimuertv.com, m3u8.hmrvideo.com, v.cdnlz*.com, v.lzcdn*.com, vip*.lz-cdn*.com, vip.lzcdn*.com, *.ffzy*.com, *.rrcdnbf*.com, vip.kuaikan*.com, bfikuncdn.com, play.modujx*.com, vod.lyhuicheng.com, ukzy.ukubf*.com, *.wgslsw.com, askzycdn.com, t0.97img.com, *.bfbfhao.com, player.cl9987.com:188, v.ykv3.com, *.3sybf.com, bfnxxcdn.com, player.huangguam3u.com, leshiyuncdn.36s.top, cs1.feidaozy.com -------------------------------------------------------------------------------- /js/qq_redirect.js: -------------------------------------------------------------------------------- 1 | // https://github.com/NanoCat-Me/utils/blob/main/URL.mjs 2 | class URL { 3 | constructor(url, base = undefined) { 4 | const name = 'URL' 5 | const version = '2.1.2' 6 | console.log(`\n🟧 ${name} v${version}\n`) 7 | url = this.#parse(url, base) 8 | return this 9 | } 10 | 11 | #parse(url, base = undefined) { 12 | const URLRegex = 13 | /(?:(?\w+:)\/\/(?:(?[^\s:"]+)(?::(?[^\s:"]+))?@)?(?[^\s@/]+))?(?\/?[^\s@?]+)?(?\?[^\s?]+)?/ 14 | const PortRegex = /(?.+):(?\d+)$/ 15 | url = url.match(URLRegex)?.groups || {} 16 | if (base) { 17 | base = base?.match(URLRegex)?.groups || {} 18 | if (!base.protocol || !base.hostname) throw new Error(`🚨 ${name}, ${base} is not a valid URL`) 19 | } 20 | if (url.protocol || base?.protocol) this.protocol = url.protocol || base.protocol 21 | if (url.username || base?.username) this.username = url.username || base.username 22 | if (url.password || base?.password) this.password = url.password || base.password 23 | if (url.host || base?.host) { 24 | this.host = url.host || base.host 25 | Object.freeze(this.host) 26 | this.hostname = this.host.match(PortRegex)?.groups.hostname ?? this.host 27 | this.port = this.host.match(PortRegex)?.groups.port ?? '' 28 | } 29 | if (url.pathname || base?.pathname) { 30 | this.pathname = url.pathname || base?.pathname 31 | if (!this.pathname.startsWith('/')) this.pathname = '/' + this.pathname 32 | this.paths = this.pathname.split('/').filter(Boolean) 33 | Object.freeze(this.paths) 34 | if (this.paths) { 35 | const fileName = this.paths[this.paths.length - 1] 36 | if (fileName?.includes('.')) { 37 | const list = fileName.split('.') 38 | this.format = list[list.length - 1] 39 | Object.freeze(this.format) 40 | } 41 | } 42 | } else this.pathname = '' 43 | if (url.search || base?.search) { 44 | this.search = url.search || base.search 45 | Object.freeze(this.search) 46 | if (this.search) 47 | this.searchParams = this.search 48 | .slice(1) 49 | .split('&') 50 | .map((param) => param.split('=')) 51 | } 52 | this.searchParams = new Map(this.searchParams || []) 53 | this.harf = this.toString() 54 | Object.freeze(this.harf) 55 | return this 56 | } 57 | 58 | toString() { 59 | let string = '' 60 | if (this.protocol) string += this.protocol + '//' 61 | if (this.username) string += this.username + (this.password ? ':' + this.password : '') + '@' 62 | if (this.hostname) string += this.hostname 63 | if (this.port) string += ':' + this.port 64 | if (this.pathname) string += this.pathname 65 | if (this.searchParams.size !== 0) 66 | string += 67 | '?' + 68 | Array.from(this.searchParams) 69 | .map((param) => param.join('=')) 70 | .join('&') 71 | return string 72 | } 73 | 74 | toJSON() { 75 | return JSON.stringify({ ...this }) 76 | } 77 | } 78 | 79 | const url = new URL($request.url) 80 | const { host, pathname: path } = url 81 | 82 | let origin = undefined 83 | switch (host) { 84 | case 'c.pc.qq.com': 85 | switch (path) { 86 | case '/middlect.html': 87 | case '/middlem.html': 88 | case '/index.html': 89 | origin = decodeURIComponent(url.searchParams.get('pfurl')) 90 | break 91 | case '/ios.html': 92 | origin = decodeURIComponent(url.searchParams.get('url')) 93 | break 94 | default: 95 | console.log('Unknown') 96 | break 97 | } 98 | break 99 | case 'cgi.connect.qq.com': 100 | origin = decodeURIComponent(url.searchParams.get('url')) 101 | break 102 | case 'pingtas.qq.com': 103 | console.log('Unknown') 104 | break 105 | default: 106 | console.log('Unknown') 107 | break 108 | } 109 | 110 | $done({ 111 | response: { 112 | status: 302, 113 | headers: { 114 | Location: origin, 115 | }, 116 | }, 117 | }) 118 | -------------------------------------------------------------------------------- /module/cmsAdblock.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=去廣告|CMS_DEBUG 2 | #!desc=去除插入式廣告 3 | #!arguments=彈窗通知:false 4 | 5 | [URL Rewrite] 6 | # 去處暴風跑馬燈 7 | (^https?:\/\/s\d+\.bfengbf\.com\/video)(.+)_z5q\.ts $1$2.ts 302 8 | 9 | [Script] 10 | 去廣告|黑木耳 = type=http-response,pattern=^https?:\/\/m3u8\.(hmrvideo|heimuertv)\.com\/play\/(.+).m3u8,requires-body=1,max-size=0,timeout=30,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 11 | 去廣告|量子資源 = type=http-response,pattern=^https?:\/\/((?:v\.lzcdn|v\.cdnlz|vip.*\.lz[-]?cdn)\d+\.com)\/.+\/hls\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 12 | 去廣告|非凡資源 = type=http-response,pattern=^https?:\/\/(super|svipsvip|vip)\.ffzy[A-Za-z0-9-]+\.com\/.+\/hls\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 13 | 去廣告|暴風影視 = type=http-response,pattern=^https?:\/\/c\d+\.rrcdnbf\d+\.com\/video\/(.+)\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 14 | 去廣告|快看資源 = type=http-response,pattern=^https?:\/\/vip\.kuaikan-(cdn|play)\d+\.com\/.+\/hls\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 15 | 去廣告|ikun = type=http-response,pattern=^https:\/\/bfikuncdn\.com\/.+\/hls\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 16 | 去廣告|魔都動漫 = type=http-response,pattern=^https:\/\/play\.modujx\d+\.com\/.+\/hls\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 17 | 去廣告|360 = type=http-response,pattern=^https:\/\/vod\.lyhuicheng\.com\/.+\/hls\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 18 | 去廣告|U酷資源 = type=http-response,pattern=^https:\/\/ukzy\.ukubf\d+\.com\/.+\/hls\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 19 | 去廣告|櫻花資源 = type=http-response,pattern=^https:\/\/.+\.wgslsw\.com\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 20 | 去廣告|樂視資源 = type=http-response,pattern=^https:\/\/leshiyuncdn\.36s\.top\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 21 | 去廣告|飛刀資源 = type=http-response,pattern=^https:\/\/m3u8\.juasu\.buzz\/.+\/hls\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 22 | 23 | 去廣告|奧斯卡資源 = type=http-response,pattern=^https:\/\/askzycdn\.com\/.+\/hls\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 24 | 去廣告|91麻豆 = type=http-response,pattern=^https:\/\/.+\.97img\.com\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 25 | 去廣告|番號資源 = type=http-response,pattern=^https:\/\/.+\.bfbfhao\.com\/.+\/hls\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 26 | 去廣告|草榴資源 = type=http-response,pattern=^https:\/\/player\.cl9987\.com:188\/.+\/hls\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 27 | 去廣告|易看資源 = type=http-response,pattern=^https:\/\/v\.ykv3\.com\/.+\/hls\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 28 | 去廣告|鯊魚資源 = type=http-response,pattern=^https:\/\/.+\.3sybf\.com\/.+\/hls\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 29 | 去廣告|奶香香 = type=http-response,pattern=^https:\/\/bfnxxcdn\.com\/.+\/hls\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 30 | 去廣告|黃瓜資源 = type=http-response,pattern=^https:\/\/player\.huangguam3u\.com\/.+\/hls\/.+\.m3u8,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/cmsAdblock.js,argument={{{彈窗通知}}} 31 | 32 | [MITM] 33 | hostname = %APPEND% v.cdnlz*.com, v.lzcdn*.com, vip*.lz-cdn*.com, vip.lzcdn*.com,*.ffzy*.com, *.rrcdnbf*.com, vip.kuaikan*.com, bfikuncdn.com, play.modujx*.com, vod.lyhuicheng.com, ukzy.ukubf*.com, *.wgslsw.com, askzycdn.com, t0.97img.com, *.bfbfhao.com, player.cl9987.com:188, v.ykv3.com, *.3sybf.com, bfnxxcdn.com, player.huangguam3u.com, leshiyuncdn.36s.top, m3u8.jiasu.buzz,m3u8.hmrvideo.com, m3u8.heimuertv.com -------------------------------------------------------------------------------- /js/exchange.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 監控匯率變化 3 | * @author: Peng-YM 4 | * 更新地址:https://raw.githubusercontent.com/Peng-YM/QuanX/master/Tasks/exchange.js 5 | * 配置方法: 6 | * 1. 設置基準貨幣。 7 | * 2. 設置保留幾位小數。 8 | * @update :YangZhaocool 9 | */ 10 | 11 | const base = "TWD"; // 基準貨幣,可以改成其他幣種 12 | const digits = 2; // 保留幾位有效數字 13 | 14 | const $ = API("exchange"); 15 | const currencyNames = { 16 | TWD: ["新台幣", "🇹🇼"], 17 | USD: ["美元", "🇺🇸"], 18 | CNY: ["人民幣", "🇨🇳"], 19 | HKD: ["港幣", "🇭🇰"], 20 | JPY: ["日幣", "🇯🇵"], 21 | KRW: ["韓元", "🇰🇷"], 22 | EUR: ["歐元", "🇪🇺"], 23 | GBP: ["英鎊", "🇬🇧"], 24 | }; 25 | 26 | 27 | $.http.get({ 28 | url: "https://api.exchangerate-api.com/v4/latest/TWD" 29 | }) 30 | .then((response) => { 31 | const data = JSON.parse(response.body); 32 | const source = currencyNames[base]; 33 | 34 | const info = Object.keys(currencyNames).reduce((accumulator, key) => { 35 | let line = ""; 36 | if (key !== base && data.rates.hasOwnProperty(key)) { 37 | const rate = parseFloat(data.rates[key]); 38 | const target = currencyNames[key]; 39 | if (rate > 1) { 40 | line = `${target[1]} 1${source[0]}兌${roundNumber(rate, digits)}${ 41 | target[0] 42 | }\n`; 43 | } else { 44 | line = `${target[1]} 1${target[0]}兌${roundNumber(1 / rate, digits)}${ 45 | source[0] 46 | }\n`; 47 | } 48 | } 49 | return accumulator + line; 50 | }, ""); 51 | $.notify( 52 | `[今日匯率] 基準:${source[1]} ${source[0]}`, 53 | `⏰ 更新時間:${data.date}`, 54 | `📈 匯率情況:\n${info}` 55 | ); 56 | }) 57 | .then(() => $.done()); 58 | 59 | function roundNumber(num, scale) { 60 | if (!("" + num).includes("e")) { 61 | return +(Math.round(num + "e+" + scale) + "e-" + scale); 62 | } else { 63 | let arr = ("" + num).split("e"); 64 | let sig = ""; 65 | if (+arr[1] + scale > 0) { 66 | sig = "+"; 67 | } 68 | return +( 69 | Math.round(+arr[0] + "e" + sig + (+arr[1] + scale)) + 70 | "e-" + 71 | scale 72 | ); 73 | } 74 | } 75 | 76 | // prettier-ignore 77 | /*********************************** API *************************************/ 78 | function ENV(){const e="undefined"!=typeof $task,t="undefined"!=typeof $loon,s="undefined"!=typeof $httpClient&&!t,i="function"==typeof require&&"undefined"!=typeof $jsbox;return{isQX:e,isLoon:t,isSurge:s,isNode:"function"==typeof require&&!i,isJSBox:i,isRequest:"undefined"!=typeof $request,isScriptable:"undefined"!=typeof importModule}}function HTTP(e={baseURL:""}){const{isQX:t,isLoon:s,isSurge:i,isScriptable:n,isNode:o}=ENV(),r=/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/\/=]*)/;const u={};return["GET","POST","PUT","DELETE","HEAD","OPTIONS","PATCH"].forEach(l=>u[l.toLowerCase()]=(u=>(function(u,l){l="string"==typeof l?{url:l}:l;const h=e.baseURL;h&&!r.test(l.url||"")&&(l.url=h?h+l.url:l.url);const a=(l={...e,...l}).timeout,c={onRequest:()=>{},onResponse:e=>e,onTimeout:()=>{},...l.events};let f,d;if(c.onRequest(u,l),t)f=$task.fetch({method:u,...l});else if(s||i||o)f=new Promise((e,t)=>{(o?require("request"):$httpClient)[u.toLowerCase()](l,(s,i,n)=>{s?t(s):e({statusCode:i.status||i.statusCode,headers:i.headers,body:n})})});else if(n){const e=new Request(l.url);e.method=u,e.headers=l.headers,e.body=l.body,f=new Promise((t,s)=>{e.loadString().then(s=>{t({statusCode:e.response.statusCode,headers:e.response.headers,body:s})}).catch(e=>s(e))})}const p=a?new Promise((e,t)=>{d=setTimeout(()=>(c.onTimeout(),t(`${u} URL: ${l.url} exceeds the timeout ${a} ms`)),a)}):null;return(p?Promise.race([p,f]).then(e=>(clearTimeout(d),e)):f).then(e=>c.onResponse(e))})(l,u))),u}function API(e="untitled",t=!1){const{isQX:s,isLoon:i,isSurge:n,isNode:o,isJSBox:r,isScriptable:u}=ENV();return new class{constructor(e,t){this.name=e,this.debug=t,this.http=HTTP(),this.env=ENV(),this.node=(()=>{if(o){return{fs:require("fs")}}return null})(),this.initCache();Promise.prototype.delay=function(e){return this.then(function(t){return((e,t)=>new Promise(function(s){setTimeout(s.bind(null,t),e)}))(e,t)})}}initCache(){if(s&&(this.cache=JSON.parse($prefs.valueForKey(this.name)||"{}")),(i||n)&&(this.cache=JSON.parse($persistentStore.read(this.name)||"{}")),o){let e="root.json";this.node.fs.existsSync(e)||this.node.fs.writeFileSync(e,JSON.stringify({}),{flag:"wx"},e=>console.log(e)),this.root={},e=`${this.name}.json`,this.node.fs.existsSync(e)?this.cache=JSON.parse(this.node.fs.readFileSync(`${this.name}.json`)):(this.node.fs.writeFileSync(e,JSON.stringify({}),{flag:"wx"},e=>console.log(e)),this.cache={})}}persistCache(){const e=JSON.stringify(this.cache,null,2);s&&$prefs.setValueForKey(e,this.name),(i||n)&&$persistentStore.write(e,this.name),o&&(this.node.fs.writeFileSync(`${this.name}.json`,e,{flag:"w"},e=>console.log(e)),this.node.fs.writeFileSync("root.json",JSON.stringify(this.root,null,2),{flag:"w"},e=>console.log(e)))}write(e,t){if(this.log(`SET ${t}`),-1!==t.indexOf("#")){if(t=t.substr(1),n||i)return $persistentStore.write(e,t);if(s)return $prefs.setValueForKey(e,t);o&&(this.root[t]=e)}else this.cache[t]=e;this.persistCache()}read(e){return this.log(`READ ${e}`),-1===e.indexOf("#")?this.cache[e]:(e=e.substr(1),n||i?$persistentStore.read(e):s?$prefs.valueForKey(e):o?this.root[e]:void 0)}delete(e){if(this.log(`DELETE ${e}`),-1!==e.indexOf("#")){if(e=e.substr(1),n||i)return $persistentStore.write(null,e);if(s)return $prefs.removeValueForKey(e);o&&delete this.root[e]}else delete this.cache[e];this.persistCache()}notify(e,t="",l="",h={}){const a=h["open-url"],c=h["media-url"];if(s&&$notify(e,t,l,h),n&&$notification.post(e,t,l+`${c?"\n多媒体:"+c:""}`,{url:a}),i){let s={};a&&(s.openUrl=a),c&&(s.mediaUrl=c),"{}"===JSON.stringify(s)?$notification.post(e,t,l):$notification.post(e,t,l,s)}if(o||u){const s=l+(a?`\n点击跳转: ${a}`:"")+(c?`\n多媒体: ${c}`:"");if(r){require("push").schedule({title:e,body:(t?t+"\n":"")+s})}else console.log(`${e}\n${t}\n${s}\n\n`)}}log(e){this.debug&&console.log(`[${this.name}] LOG: ${this.stringify(e)}`)}info(e){console.log(`[${this.name}] INFO: ${this.stringify(e)}`)}error(e){console.log(`[${this.name}] ERROR: ${this.stringify(e)}`)}wait(e){return new Promise(t=>setTimeout(t,e))}done(e={}){s||i||n?$done(e):o&&!r&&"undefined"!=typeof $context&&($context.headers=e.headers,$context.statusCode=e.statusCode,$context.body=e.body)}stringify(e){if("string"==typeof e||e instanceof String)return e;try{return JSON.stringify(e,null,2)}catch(e){return"[object Object]"}}}(e,t)} 79 | /*****************************************************************************/ -------------------------------------------------------------------------------- /Bili/BiliBili.Enhanced.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=BiliBili Enhanced 2 | #!desc=嗶哩嗶哩 UI 自定義及去廣告 3 | #!category=Bilibili 4 | #!arguments=IP:DIRECT,BiliBili.字幕簡轉繁:BiliBili.字幕簡轉繁,BiliBili.Upos.Redirect:BiliBili.Upos.Redirect,[開屏]去除廣告:true,[推薦]去除廣告:true,[推薦]去除“活動大圖”:true,[首頁]去除短影片廣告:true,[番劇電影]去除廣告:true,[用戶投稿]去除影片廣告:true,[搜尋]去除廣告:true,[彈幕]去除互動式彈幕:false,[彈幕]替換大會員彈幕:false,[評論區]去除廣告:true,[直播]去除廣告:true,[搜尋]去除“熱搜”:true,[動態]去除“熱門話題”:true,[動態]去除“最常訪問”:true,[動態]去除廣告動態:true 5 | #!arguments-desc=IP:可能影響 IP 屬地的Ruleset,預設策略為 DIRECT\nBiliBili.字幕簡轉繁:輸入#時關閉\nBiliBili.Upos.Redirect:修改播放 CDN 地址,輸入#時關閉 6 | # Thanks to VirgilClyne,app2smile,RuCu6,ClydeTime 7 | 8 | [General] 9 | # > TCP Force HTTP Hosts 10 | force-http-engine-hosts = %INSERT% upos*:0 11 | 12 | [Rule] 13 | RULE-SET,BiliBili-IP,{{{IP}}} 14 | DOMAIN,data.bilibili.com,REJECT-DROP 15 | DOMAIN,dataflow.biliapi.com,REJECT-DROP 16 | DOMAIN,cm.bilibili.com,REJECT-DROP 17 | # block httpdns 18 | DOMAIN,httpdns.bilivideo.com,REJECT,extended-matching 19 | # IP-CIDR,203.107.1.0/24,REJECT,no-resolve 20 | # 微信定位接口? 開啟 app 後會一直請求這個域名 21 | DOMAIN-SUFFIX,up-hl.3g.qq.com,REJECT-DROP 22 | 23 | [Ruleset BiliBili-IP] 24 | DOMAIN,member.bilibili.com 25 | DOMAIN,passport.bilibili.com 26 | DOMAIN,log1.cmpassport.com 27 | DOMAIN,passport.bigfun.cn 28 | DOMAIN,passport.bigfunapp.cn 29 | DOMAIN,broadcast.chat.bilibili.com 30 | URL-REGEX,host=passport.bilibili.com&query#4 31 | URL-REGEX,host=app.bilibili.com&query=4 32 | URL-REGEX,broadcast.chat.bilibili.com&query=4 33 | URL-REGEX,host=api.bilibili.com&query=4 34 | URL-REGEX,/x/web-interface 35 | URL-REGEX,http://api.bilibili.com/client_info 36 | URL-REGEX,^https:\/\/app\.bilibili\.com\/x\/resource\/ip* 37 | URL-REGEX,https://api.bilibili.com/x/v2/reply/add 38 | 39 | [Map Local] 40 | # IP 與地理位置 41 | ^https:\/\/api\.bilibili\.com\/x\/web-interface\/zone\?jsonp data-type=text data="{}" status-code=200 42 | ^https:\/\/app\.bilibili\.com\/x\/resource\/ip data-type=text data="{}" status-code=200 43 | # 滿意度 44 | ^https:\/\/api\.bilibili\.com\/x\/v2\/dm\/qoe\/show\? data-type=text data="{}" status-code=200 45 | # 開屏廣告 //app.bilibili.com 46 | ^http:\/\/[\d\.]+:8000\/v1\/resource\/\w{32}-1-SPLASH data-type=text data="{}" status-code=200 47 | ^http:\/\/upos-sz-static\.bilivideo\.com\/ssaxcode\/\w{2}\/\w{2}\/\w{32}-1-SPLASH data-type=text data="{}" status-code=200 48 | ^https:\/\/(api\.bilibili\.com\/x\/mengqi\/v1\/resource|app\.bilibili\.com\/x\/resource\/peak\/download) data-type=text data="{}" status-code=200 49 | # 去除搜索中的大家都在搜 50 | ^https?:\/\/api\.vc\.bilibili\.com\/search_svr\/v\d\/Search\/recommend_words data="https://raw.githubusercontent.com/mieqq/mieqq/master/reject-dict.json" 51 | # 去除動態中的話題 52 | ^https?:\/\/api\.vc\.bilibili\.com\/topic_svr\/v1\/topic_svr data="https://raw.githubusercontent.com/mieqq/mieqq/master/reject-dict.json" 53 | # 去除動態中的最常訪問 54 | ^https?:\/\/api\.vc\.bilibili\.com\/dynamic_svr\/v1\/dynamic_svr\/mix_uplist data="https://raw.githubusercontent.com/mieqq/mieqq/master/reject-dict.json" 55 | # 可能的一些推廣(beta) 56 | ^https?:\/\/api\.bili(bili\.com|api\.net)\/pgc\/season\/app\/related\/recommend\? data="https://raw.githubusercontent.com/mieqq/mieqq/master/reject-dict.json" 57 | # BiliBili 漫畫去廣告 58 | ^https?:\/\/manga\.bilibili\.com\/twirp\/comic\.v\d\.Comic\/Flash data="https://raw.githubusercontent.com/mieqq/mieqq/master/reject-dict.json" 59 | ^https?:\/\/manga\.bilibili\.com\/twirp\/comic\.v\d\.Comic\/ListFlash data="https://raw.githubusercontent.com/mieqq/mieqq/master/reject-dict.json" 60 | ^https:\/\/api\.live\.bilibili\.com\/xlive\/e-commerce-interface\/v1\/ecommerce-user\/get_shopping_info\? data="https://raw.githubusercontent.com/mieqq/mieqq/master/reject-dict.json" 61 | 62 | [Script] 63 | BiliBili.Enhanced.x.resource.show.tab.v2 = type=http-response, pattern=^https?:\/\/app\.bili(bili\.com|api\.net)\/x\/resource\/show\/tab\/v2\?, requires-body=1, engine=jsc, script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.Enhanced.response.js, argument= 64 | BiliBili.Enhanced.x.v2.account.mine = type=http-response, pattern=^https?:\/\/app\.bili(bili\.com|api\.net)\/x\/v2\/account\/mine(\/ipad)?\?, requires-body=1, engine=jsc, script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.Enhanced.response.js, argument= 65 | BiliBili.Enhanced.x.v2.region.index = type=http-response, pattern=^https?:\/\/app\.bili(bili\.com|api\.net)\/x\/v2\/region\/index\?, requires-body=1, engine=jsc, script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.Enhanced.response.js, argument= 66 | BiliBili.Enhanced.x.v2.channel.region.list = type=http-response, pattern=^https?:\/\/app\.bili(bili\.com|api\.net)\/x\/v2\/channel\/region\/list\?, requires-body=1, engine=jsc, script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.Enhanced.response.js, argument= 67 | 68 | #去廣告 69 | BiliBili.ADBlock.feed.index.request.json = type=http-request, pattern=^https?:\/\/app\.bili(bili\.com|api\.net)\/x\/v2\/feed\/index\?, engine=webview, debug=1, script-path=https://github.com/BiliUniverse/ADBlock/releases/download/v0.6.7/request.bundle.js, argument=Detail.splash={{{[開屏]去除廣告}}}&Detail.feed={{{[推薦]去除廣告}}}&Detail.activity={{{[推薦]去除“活動大圖”}}}&Detail.story={{{[首頁]去除短影片廣告}}}&Detail.cinema={{{[番劇電影]去除廣告}}}&Detail.view={{{[用戶投稿]去除影片廣告}}}&Detail.search={{{[搜尋]去除廣告}}}&Detail.commandDms={{{[彈幕]去除互動式彈幕}}}&Detail.colorfulDms={{{[彈幕]替換大會員彈幕}}}&Detail.MainList={{{[評論區]去除廣告}}}&Detail.xlive={{{[直播]去除廣告}}}&Detail.Hot_search={{{[搜尋]去除“熱搜”}}}&Detail.Hot_topics={{{[動態]去除“熱門話題”}}}&Detail.Most_visited={{{[動態]去除“最常訪問”}}}&Detail.Dynamic_adcard={{{[動態]去除廣告動態}}} 70 | BiliBili.ADBlock.response.json = type=http-response, pattern=^https?:\/\/app\.bili(bili\.com|api\.net)\/x\/v2\/(splash\/(brand\/list|event\/list2|list|show)|feed\/index(\/story)?|search\/square), requires-body=1, engine=webview, debug=1, script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.ADBlock.response.js, argument=Detail.splash={{{[開屏]去除廣告}}}&Detail.feed={{{[推薦]去除廣告}}}&Detail.activity={{{[推薦]去除“活動大圖”}}}&Detail.story={{{[首頁]去除短影片廣告}}}&Detail.cinema={{{[番劇電影]去除廣告}}}&Detail.view={{{[用戶投稿]去除影片廣告}}}&Detail.search={{{[搜尋]去除廣告}}}&Detail.commandDms={{{[彈幕]去除互動式彈幕}}}&Detail.colorfulDms={{{[彈幕]替換大會員彈幕}}}&Detail.MainList={{{[評論區]去除廣告}}}&Detail.xlive={{{[直播]去除廣告}}}&Detail.Hot_search={{{[搜尋]去除“熱搜”}}}&Detail.Hot_topics={{{[動態]去除“熱門話題”}}}&Detail.Most_visited={{{[動態]去除“最常訪問”}}}&Detail.Dynamic_adcard={{{[動態]去除廣告動態}}} 71 | BiliBili.ADBlock.response.json = type=http-response, pattern=^https?:\/\/api\.bilibili\.com\/x\/web-interface\/wbi\/index\/top\/feed\/rcmd\?, requires-body=1, engine=webview, debug=1, script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.ADBlock.response.js, argument=Detail.splash={{{[開屏]去除廣告}}}&Detail.feed={{{[推薦]去除廣告}}}&Detail.activity={{{[推薦]去除“活動大圖”}}}&Detail.story={{{[首頁]去除短影片廣告}}}&Detail.cinema={{{[番劇電影]去除廣告}}}&Detail.view={{{[用戶投稿]去除影片廣告}}}&Detail.search={{{[搜尋]去除廣告}}}&Detail.commandDms={{{[彈幕]去除互動式彈幕}}}&Detail.colorfulDms={{{[彈幕]替換大會員彈幕}}}&Detail.MainList={{{[評論區]去除廣告}}}&Detail.xlive={{{[直播]去除廣告}}}&Detail.Hot_search={{{[搜尋]去除“熱搜”}}}&Detail.Hot_topics={{{[動態]去除“熱門話題”}}}&Detail.Most_visited={{{[動態]去除“最常訪問”}}}&Detail.Dynamic_adcard={{{[動態]去除廣告動態}}} 72 | BiliBili.ADBlock.pgc.page.response.json = type=http-response, pattern=^https?:\/\/api\.bili(bili\.com|api\.net)\/pgc\/page\/(bangumi|cinema\/tab\?), requires-body=1, engine=webview, debug=1, script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.ADBlock.response.js, argument=Detail.splash={{{[開屏]去除廣告}}}&Detail.feed={{{[推薦]去除廣告}}}&Detail.activity={{{[推薦]去除“活動大圖”}}}&Detail.story={{{[首頁]去除短影片廣告}}}&Detail.cinema={{{[番劇電影]去除廣告}}}&Detail.view={{{[用戶投稿]去除影片廣告}}}&Detail.search={{{[搜尋]去除廣告}}}&Detail.commandDms={{{[彈幕]去除互動式彈幕}}}&Detail.colorfulDms={{{[彈幕]替換大會員彈幕}}}&Detail.MainList={{{[評論區]去除廣告}}}&Detail.xlive={{{[直播]去除廣告}}}&Detail.Hot_search={{{[搜尋]去除“熱搜”}}}&Detail.Hot_topics={{{[動態]去除“熱門話題”}}}&Detail.Most_visited={{{[動態]去除“最常訪問”}}}&Detail.Dynamic_adcard={{{[動態]去除廣告動態}}} 73 | BiliBili.ADBlock.app-room.response.json = type=http-response, pattern=^https?:\/\/api\.live\.bilibili\.com\/xlive\/app-room\/v1\/index\/getInfoByRoom, requires-body=1, engine=webview, debug=1, script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.ADBlock.response.js, argument=Detail.splash={{{[開屏]去除廣告}}}&Detail.feed={{{[推薦]去除廣告}}}&Detail.activity={{{[推薦]去除“活動大圖”}}}&Detail.story={{{[首頁]去除短影片廣告}}}&Detail.cinema={{{[番劇電影]去除廣告}}}&Detail.view={{{[用戶投稿]去除影片廣告}}}&Detail.search={{{[搜尋]去除廣告}}}&Detail.commandDms={{{[彈幕]去除互動式彈幕}}}&Detail.colorfulDms={{{[彈幕]替換大會員彈幕}}}&Detail.MainList={{{[評論區]去除廣告}}}&Detail.xlive={{{[直播]去除廣告}}}&Detail.Hot_search={{{[搜尋]去除“熱搜”}}}&Detail.Hot_topics={{{[動態]去除“熱門話題”}}}&Detail.Most_visited={{{[動態]去除“最常訪問”}}}&Detail.Dynamic_adcard={{{[動態]去除廣告動態}}} 74 | #BiliBili.ADBlock.response.grpc = type=http-response, pattern=^https?:\/\/(grpc\.biliapi\.net|app\.bilibili\.com)\/bilibili\.app\.(view|viewunite)\.v1\.View\/(View|TFInfo|RelatesFeed)$, requires-body=1, binary-body-mode=1, engine=webview, debug=1, script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.ADBlock.response.js, argument=Detail.splash={{{[開屏]去除廣告}}}&Detail.feed={{{[推薦]去除廣告}}}&Detail.activity={{{[推薦]去除“活動大圖”}}}&Detail.story={{{[首頁]去除短影片廣告}}}&Detail.cinema={{{[番劇電影]去除廣告}}}&Detail.view={{{[用戶投稿]去除影片廣告}}}&Detail.search={{{[搜尋]去除廣告}}}&Detail.commandDms={{{[彈幕]去除互動式彈幕}}}&Detail.colorfulDms={{{[彈幕]替換大會員彈幕}}}&Detail.MainList={{{[評論區]去除廣告}}}&Detail.xlive={{{[直播]去除廣告}}}&Detail.Hot_search={{{[搜尋]去除“熱搜”}}}&Detail.Hot_topics={{{[動態]去除“熱門話題”}}}&Detail.Most_visited={{{[動態]去除“最常訪問”}}}&Detail.Dynamic_adcard={{{[動態]去除廣告動態}}} 75 | BiliBili.ADBlock.response.grpc = type=http-response, pattern=^https?:\/\/(grpc\.biliapi\.net|app\.bilibili\.com)\/bilibili\.app\.dynamic\.v2\.Dynamic\/Dyn(All|Video)$, requires-body=1, binary-body-mode=1, engine=webview, debug=1, script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.ADBlock.response.js, argument=Detail.splash={{{[開屏]去除廣告}}}&Detail.feed={{{[推薦]去除廣告}}}&Detail.activity={{{[推薦]去除“活動大圖”}}}&Detail.story={{{[首頁]去除短影片廣告}}}&Detail.cinema={{{[番劇電影]去除廣告}}}&Detail.view={{{[用戶投稿]去除影片廣告}}}&Detail.search={{{[搜尋]去除廣告}}}&Detail.commandDms={{{[彈幕]去除互動式彈幕}}}&Detail.colorfulDms={{{[彈幕]替換大會員彈幕}}}&Detail.MainList={{{[評論區]去除廣告}}}&Detail.xlive={{{[直播]去除廣告}}}&Detail.Hot_search={{{[搜尋]去除“熱搜”}}}&Detail.Hot_topics={{{[動態]去除“熱門話題”}}}&Detail.Most_visited={{{[動態]去除“最常訪問”}}}&Detail.Dynamic_adcard={{{[動態]去除廣告動態}}} 76 | BiliBili.ADBlock.response.grpc = type=http-response, pattern=^https?:\/\/(grpc\.biliapi\.net|app\.bilibili\.com)\/bilibili\.app\.playurl\.v1\.PlayURL\/PlayView$, requires-body=1, binary-body-mode=1, engine=webview, debug=1, script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.ADBlock.response.js, argument=Detail.splash={{{[開屏]去除廣告}}}&Detail.feed={{{[推薦]去除廣告}}}&Detail.activity={{{[推薦]去除“活動大圖”}}}&Detail.story={{{[首頁]去除短影片廣告}}}&Detail.cinema={{{[番劇電影]去除廣告}}}&Detail.view={{{[用戶投稿]去除影片廣告}}}&Detail.search={{{[搜尋]去除廣告}}}&Detail.commandDms={{{[彈幕]去除互動式彈幕}}}&Detail.colorfulDms={{{[彈幕]替換大會員彈幕}}}&Detail.MainList={{{[評論區]去除廣告}}}&Detail.xlive={{{[直播]去除廣告}}}&Detail.Hot_search={{{[搜尋]去除“熱搜”}}}&Detail.Hot_topics={{{[動態]去除“熱門話題”}}}&Detail.Most_visited={{{[動態]去除“最常訪問”}}}&Detail.Dynamic_adcard={{{[動態]去除廣告動態}}} 77 | BiliBili.ADBlock.response.grpc = type=http-response, pattern=^https?:\/\/(grpc\.biliapi\.net|app\.bilibili\.com)\/bilibili\.polymer\.app\.search\.v1\.Search\/SearchAll$, requires-body=1, binary-body-mode=1, engine=webview, debug=1, script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.ADBlock.response.js, argument=Detail.splash={{{[開屏]去除廣告}}}&Detail.feed={{{[推薦]去除廣告}}}&Detail.activity={{{[推薦]去除“活動大圖”}}}&Detail.story={{{[首頁]去除短影片廣告}}}&Detail.cinema={{{[番劇電影]去除廣告}}}&Detail.view={{{[用戶投稿]去除影片廣告}}}&Detail.search={{{[搜尋]去除廣告}}}&Detail.commandDms={{{[彈幕]去除互動式彈幕}}}&Detail.colorfulDms={{{[彈幕]替換大會員彈幕}}}&Detail.MainList={{{[評論區]去除廣告}}}&Detail.xlive={{{[直播]去除廣告}}}&Detail.Hot_search={{{[搜尋]去除“熱搜”}}}&Detail.Hot_topics={{{[動態]去除“熱門話題”}}}&Detail.Most_visited={{{[動態]去除“最常訪問”}}}&Detail.Dynamic_adcard={{{[動態]去除廣告動態}}} 78 | BiliBili.ADBlock.response.grpc = type=http-response, pattern=^https:\/\/(grpc\.biliapi\.net|app\.bilibili\.com)\/bilibili\.community\.service\.dm\.v1\.DM\/(DmView|DmSegMobile), requires-body=1, binary-body-mode=1, engine=webview, debug=1, script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.ADBlock.response.js, argument=Detail.splash={{{[開屏]去除廣告}}}&Detail.feed={{{[推薦]去除廣告}}}&Detail.activity={{{[推薦]去除“活動大圖”}}}&Detail.story={{{[首頁]去除短影片廣告}}}&Detail.cinema={{{[番劇電影]去除廣告}}}&Detail.view={{{[用戶投稿]去除影片廣告}}}&Detail.search={{{[搜尋]去除廣告}}}&Detail.commandDms={{{[彈幕]去除互動式彈幕}}}&Detail.colorfulDms={{{[彈幕]替換大會員彈幕}}}&Detail.MainList={{{[評論區]去除廣告}}}&Detail.xlive={{{[直播]去除廣告}}}&Detail.Hot_search={{{[搜尋]去除“熱搜”}}}&Detail.Hot_topics={{{[動態]去除“熱門話題”}}}&Detail.Most_visited={{{[動態]去除“最常訪問”}}}&Detail.Dynamic_adcard={{{[動態]去除廣告動態}}} 79 | BiliBili.ADBlock.response.grpc = type=http-response, pattern=^https:\/\/(grpc\.biliapi\.net|app\.bilibili\.com)\/bilibili\.app\.interface\.v1\.Teenagers\/ModeStatus, requires-body=1, binary-body-mode=1, engine=webview, debug=1, script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.ADBlock.response.js, argument=Detail.splash={{{[開屏]去除廣告}}}&Detail.feed={{{[推薦]去除廣告}}}&Detail.activity={{{[推薦]去除“活動大圖”}}}&Detail.story={{{[首頁]去除短影片廣告}}}&Detail.cinema={{{[番劇電影]去除廣告}}}&Detail.view={{{[用戶投稿]去除影片廣告}}}&Detail.search={{{[搜尋]去除廣告}}}&Detail.commandDms={{{[彈幕]去除互動式彈幕}}}&Detail.colorfulDms={{{[彈幕]替換大會員彈幕}}}&Detail.MainList={{{[評論區]去除廣告}}}&Detail.xlive={{{[直播]去除廣告}}}&Detail.Hot_search={{{[搜尋]去除“熱搜”}}}&Detail.Hot_topics={{{[動態]去除“熱門話題”}}}&Detail.Most_visited={{{[動態]去除“最常訪問”}}}&Detail.Dynamic_adcard={{{[動態]去除廣告動態}}} 80 | BiliBili.ADBlock.response.grpc = type=http-response, pattern=^https:\/\/(grpc\.biliapi\.net|app\.bilibili\.com)\/bilibili\.main\.community\.reply\.v1\.Reply\/MainList, requires-body=1, binary-body-mode=1, engine=webview, debug=1, script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.ADBlock.response.js, argument=Detail.splash={{{[開屏]去除廣告}}}&Detail.feed={{{[推薦]去除廣告}}}&Detail.activity={{{[推薦]去除“活動大圖”}}}&Detail.story={{{[首頁]去除短影片廣告}}}&Detail.cinema={{{[番劇電影]去除廣告}}}&Detail.view={{{[用戶投稿]去除影片廣告}}}&Detail.search={{{[搜尋]去除廣告}}}&Detail.commandDms={{{[彈幕]去除互動式彈幕}}}&Detail.colorfulDms={{{[彈幕]替換大會員彈幕}}}&Detail.MainList={{{[評論區]去除廣告}}}&Detail.xlive={{{[直播]去除廣告}}}&Detail.Hot_search={{{[搜尋]去除“熱搜”}}}&Detail.Hot_topics={{{[動態]去除“熱門話題”}}}&Detail.Most_visited={{{[動態]去除“最常訪問”}}}&Detail.Dynamic_adcard={{{[動態]去除廣告動態}}} 81 | # DefaultWords 82 | BiliBili.ADBlock.response.grpc = type=http-response, pattern=^https://(app|grpc).(biliapi|bilibili).(net|com)/bilibili.app.interface.v1.Search/DefaultWords$, requires-body=1, binary-body-mode=1, engine=webview, debug=1, script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.ADBlock.response.js, argument=Detail.splash={{{[開屏]去除廣告}}}&Detail.feed={{{[推薦]去除廣告}}}&Detail.activity={{{[推薦]去除“活動大圖”}}}&Detail.story={{{[首頁]去除短影片廣告}}}&Detail.cinema={{{[番劇電影]去除廣告}}}&Detail.view={{{[用戶投稿]去除影片廣告}}}&Detail.search={{{[搜尋]去除廣告}}}&Detail.commandDms={{{[彈幕]去除互動式彈幕}}}&Detail.colorfulDms={{{[彈幕]替換大會員彈幕}}}&Detail.MainList={{{[評論區]去除廣告}}}&Detail.xlive={{{[直播]去除廣告}}}&Detail.Hot_search={{{[搜尋]去除“熱搜”}}}&Detail.Hot_topics={{{[動態]去除“熱門話題”}}}&Detail.Most_visited={{{[動態]去除“最常訪問”}}}&Detail.Dynamic_adcard={{{[動態]去除廣告動態}}} 83 | # RelatesFeed 84 | BiliBili.ADBlock.response.grpc = type=http-response, pattern=^https://(grpc.biliapi.net|app.bilibili.com)/bilibili.app.viewunite.v1.View/RelatesFeed, requires-body=1, binary-body-mode=1, engine=webview, debug=1, script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.ADBlock.response.js, argument=Detail.splash={{{[開屏]去除廣告}}}&Detail.feed={{{[推薦]去除廣告}}}&Detail.activity={{{[推薦]去除“活動大圖”}}}&Detail.story={{{[首頁]去除短影片廣告}}}&Detail.cinema={{{[番劇電影]去除廣告}}}&Detail.view={{{[用戶投稿]去除影片廣告}}}&Detail.search={{{[搜尋]去除廣告}}}&Detail.commandDms={{{[彈幕]去除互動式彈幕}}}&Detail.colorfulDms={{{[彈幕]替換大會員彈幕}}}&Detail.MainList={{{[評論區]去除廣告}}}&Detail.xlive={{{[直播]去除廣告}}}&Detail.Hot_search={{{[搜尋]去除“熱搜”}}}&Detail.Hot_topics={{{[動態]去除“熱門話題”}}}&Detail.Most_visited={{{[動態]去除“最常訪問”}}}&Detail.Dynamic_adcard={{{[動態]去除廣告動態}}} 85 | # kokoryh v1/view 86 | bilibili.v1.view = type=http-response,pattern=^https://(grpc.biliapi.net|app.bilibili.com)/bilibili.app.(view|viewunite).v1.View/(View|ViewProgress|TFInfo)$,requires-body=1,binary-body-mode=1,max-size=-1,engine=webview,script-path=https://raw.githubusercontent.com/kokoryh/Script/master/js/bilibili.protobuf.js 87 | 88 | # others 89 | {{{BiliBili.字幕簡轉繁}}} = type=http-response,pattern=^https:\/\/aisubtitle\.hdslb\.com\/bfs\/subtitle\/.+\.json,requires-body=1,max-size=-1,timeout=99,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.CC.FanHuaJi.js,script-update-interval=0 90 | {{{BiliBili.Upos.Redirect}}} = type=http-request,pattern=^https?:\/\/upos.*(bilivideo.com|akamaized.net),requires-body=0,script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/Bili/BiliBili.Upos.Redirect.js,engine=jsc 91 | 92 | [MITM] 93 | h2 = true 94 | hostname = %APPEND% app.bilibili.com, app.biliapi.net, manga.bilibili.com, api.live.bilibili.com, api.vc.bilibili.com, api.bilibili.com, api.biliapi.net, grpc.biliapi.net, aisubtitle.hdslb.com 95 | -------------------------------------------------------------------------------- /js/listenify.js: -------------------------------------------------------------------------------- 1 | /* 2 | listenify 從網易雲音樂獲取歌詞與藝人封面 3 | 歌詞 api 處填入 https://api.lrc.cx/lyrics 4 | 封面 api 處填入 https://api.lrc.cx/cover 5 | Surge: 6 | [Script] 7 | listenify = type=http-request,pattern=https:\/\/api\.lrc\.cx\/(lyrics|cover),script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/listenify.js,script-update-interval=0 8 | 9 | [MITM] 10 | hostname = api.lrc.cx 11 | 12 | Loon: 13 | [Script] 14 | http-request https:\/\/api\.lrc\.cx\/(lyrics|cover) script-path=https://raw.githubusercontent.com/Yswag/for-own-use/main/js/listenify.js, timeout=10, tag=lyrics 15 | 16 | [Mitm] 17 | hostname = api.lrc.cx 18 | 19 | QX: 20 | [rewrite_local] 21 | https:\/\/api\.lrc\.cx\/(lyrics|cover) url script-echo-response https://raw.githubusercontent.com/Yswag/for-own-use/main/js/listenify.js 22 | 23 | [mitm] 24 | hostname = api.lrc.cx 25 | */ 26 | const $ = new Env('lyrics') 27 | 28 | const host = 'https://music.163.com' 29 | const headers = { 30 | accept: 'application/json', 31 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0', 32 | cookie: 'os=ios; __remember_me=true; NMTID=xxx', 33 | referer: host 34 | } 35 | const url = $request.url 36 | 37 | if (url.includes('lyrics')) { 38 | const params = url.split('?')[1].split('&') 39 | const artist = params[0].split('=')[1] 40 | const title = params[1] ? params[1].split('=')[1] : '' 41 | const s = `${artist}%20${title}` 42 | const search = { 43 | url: `${host}/api/cloudsearch/pc?type=1&limit=1&offset=0&s=${s}`, 44 | headers: headers 45 | } 46 | 47 | ;(async () => { 48 | try { 49 | const id = await getID(search) 50 | const lyrics = await getLyrics(id) 51 | $.isQuanX() 52 | ? $.done({ 53 | status: 'HTTP/1.1 200', 54 | headers: { 'Content-Type': 'text/html' }, 55 | body: lyrics + '\n[99:00.00] 歌詞來源:網易雲' 56 | }) 57 | : $.done({ 58 | response: { 59 | status: 200, 60 | body: lyrics + '\n[99:00.00] 歌詞來源:網易雲' 61 | } 62 | }) 63 | } catch (err) { 64 | $.logErr(err) 65 | if ($.isQuanX()) { 66 | let originalResp = await getOriginal() 67 | $.done({ 68 | status: 'HTTP/1.1 200', 69 | headers: { 'Content-Type': 'text/html' }, 70 | body: originalResp 71 | }) 72 | } else $.done({}) 73 | } 74 | })() 75 | } else if (url.includes('cover')) { 76 | const params = url.split('?')[1].split('&') 77 | if (params.includes('title') || params.includes('album')) $.done({}) 78 | 79 | const artist = params[0].split('=')[1] 80 | const search = { 81 | url: `${host}/api/cloudsearch/pc?type=100&limit=1&offset=0&s=${artist}`, 82 | headers: headers 83 | } 84 | $.get(search, (err, resp) => { 85 | if (err) { 86 | $.logErr(err) 87 | $.done({}) 88 | } else { 89 | const body = JSON.parse(resp.body) 90 | artistPic = body.result.artists ? body.result.artists[0].picUrl : '' 91 | if (artistPic === '') $.done({}) 92 | 93 | $.isQuanX() 94 | ? $.done({ 95 | status: 'HTTP/1.1 302', 96 | headers: { Location: artistPic } 97 | }) 98 | : $.done({ 99 | response: { 100 | status: 302, 101 | headers: { Location: artistPic } 102 | } 103 | }) 104 | } 105 | }) 106 | } 107 | 108 | function getID(req) { 109 | return new Promise((resolve, reject) => { 110 | $.get(req, function (err, resp) { 111 | if (err) { 112 | $.logErr(err) 113 | reject(err) 114 | } else { 115 | const obj = JSON.parse(resp.body) 116 | if (obj.result.songs === undefined) { 117 | reject('No songs found') 118 | } else { 119 | const id = obj.result.songs[0].id 120 | resolve(id) 121 | } 122 | } 123 | }) 124 | }) 125 | } 126 | 127 | function getLyrics(id) { 128 | return new Promise((resolve, reject) => { 129 | let req = { 130 | url: `${host}/api/song/lyric?id=${id}&lv=0&yv=0&tv=0`, 131 | headers: headers 132 | } 133 | 134 | $.get(req, function (err, resp) { 135 | if (err) { 136 | $.logErr(err) 137 | reject(err) 138 | } else { 139 | const obj = JSON.parse(resp.body) 140 | const lyrics = obj.lrc.lyric 141 | // empty lyrics 142 | if (lyrics === '') reject('No lyrics found') 143 | // check if lyrics are plain text, if true return to lrc.cx 144 | if (!/^\[\d{2,3}:\d{2,3}\.\d{2,3}\].+/.test(lyrics)) reject('Plain text lyrics, return to original') 145 | 146 | resolve(lyrics) 147 | } 148 | }) 149 | }) 150 | } 151 | 152 | function getOriginal() { 153 | return new Promise((resolve, reject) => { 154 | let req = { 155 | url: $request.url, 156 | headers: $request.headers 157 | } 158 | 159 | $.get(req, function (err, resp) { 160 | if (err) { 161 | $.logErr(err) 162 | reject(err) 163 | } else { 164 | resolve(resp.body) 165 | } 166 | }) 167 | }) 168 | } 169 | 170 | //prettier-ignore 171 | function Env(t,e){class s{constructor(t){this.env=t}send(t,e="GET"){t="string"==typeof t?{url:t}:t;let s=this.get;"POST"===e&&(s=this.post);const i=new Promise(((e,i)=>{s.call(this,t,((t,s,o)=>{t?i(t):e(s)}))}));return t.timeout?((t,e=1e3)=>Promise.race([t,new Promise(((t,s)=>{setTimeout((()=>{s(new Error("请求超时"))}),e)}))]))(i,t.timeout):i}get(t){return this.send.call(this.env,t)}post(t){return this.send.call(this.env,t,"POST")}}return new class{constructor(t,e){this.logLevels={debug:0,info:1,warn:2,error:3},this.logLevelPrefixs={debug:"[DEBUG] ",info:"[INFO] ",warn:"[WARN] ",error:"[ERROR] "},this.logLevel="info",this.name=t,this.http=new s(this),this.data=null,this.dataFile="box.dat",this.logs=[],this.isMute=!1,this.isNeedRewrite=!1,this.logSeparator="\n",this.encoding="utf-8",this.startTime=(new Date).getTime(),Object.assign(this,e),this.log("",`🔔${this.name}, 开始!`)}getEnv(){return"undefined"!=typeof $environment&&$environment["surge-version"]?"Surge":"undefined"!=typeof $environment&&$environment["stash-version"]?"Stash":"undefined"!=typeof module&&module.exports?"Node.js":"undefined"!=typeof $task?"Quantumult X":"undefined"!=typeof $loon?"Loon":"undefined"!=typeof $rocket?"Shadowrocket":void 0}isNode(){return"Node.js"===this.getEnv()}isQuanX(){return"Quantumult X"===this.getEnv()}isSurge(){return"Surge"===this.getEnv()}isLoon(){return"Loon"===this.getEnv()}isShadowrocket(){return"Shadowrocket"===this.getEnv()}isStash(){return"Stash"===this.getEnv()}toObj(t,e=null){try{return JSON.parse(t)}catch{return e}}toStr(t,e=null,...s){try{return JSON.stringify(t,...s)}catch{return e}}getjson(t,e){let s=e;if(this.getdata(t))try{s=JSON.parse(this.getdata(t))}catch{}return s}setjson(t,e){try{return this.setdata(JSON.stringify(t),e)}catch{return!1}}getScript(t){return new Promise((e=>{this.get({url:t},((t,s,i)=>e(i)))}))}runScript(t,e){return new Promise((s=>{let i=this.getdata("@chavy_boxjs_userCfgs.httpapi");i=i?i.replace(/\n/g,"").trim():i;let o=this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout");o=o?1*o:20,o=e&&e.timeout?e.timeout:o;const[r,a]=i.split("@"),n={url:`http://${a}/v1/scripting/evaluate`,body:{script_text:t,mock_type:"cron",timeout:o},headers:{"X-Key":r,Accept:"*/*"},policy:"DIRECT",timeout:o};this.post(n,((t,e,i)=>s(i)))})).catch((t=>this.logErr(t)))}loaddata(){if(!this.isNode())return{};{this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const t=this.path.resolve(this.dataFile),e=this.path.resolve(process.cwd(),this.dataFile),s=this.fs.existsSync(t),i=!s&&this.fs.existsSync(e);if(!s&&!i)return{};{const i=s?t:e;try{return JSON.parse(this.fs.readFileSync(i))}catch(t){return{}}}}}writedata(){if(this.isNode()){this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const t=this.path.resolve(this.dataFile),e=this.path.resolve(process.cwd(),this.dataFile),s=this.fs.existsSync(t),i=!s&&this.fs.existsSync(e),o=JSON.stringify(this.data);s?this.fs.writeFileSync(t,o):i?this.fs.writeFileSync(e,o):this.fs.writeFileSync(t,o)}}lodash_get(t,e,s){const i=e.replace(/\[(\d+)\]/g,".$1").split(".");let o=t;for(const t of i)if(o=Object(o)[t],void 0===o)return s;return o}lodash_set(t,e,s){return Object(t)!==t||(Array.isArray(e)||(e=e.toString().match(/[^.[\]]+/g)||[]),e.slice(0,-1).reduce(((t,s,i)=>Object(t[s])===t[s]?t[s]:t[s]=Math.abs(e[i+1])>>0==+e[i+1]?[]:{}),t)[e[e.length-1]]=s),t}getdata(t){let e=this.getval(t);if(/^@/.test(t)){const[,s,i]=/^@(.*?)\.(.*?)$/.exec(t),o=s?this.getval(s):"";if(o)try{const t=JSON.parse(o);e=t?this.lodash_get(t,i,""):e}catch(t){e=""}}return e}setdata(t,e){let s=!1;if(/^@/.test(e)){const[,i,o]=/^@(.*?)\.(.*?)$/.exec(e),r=this.getval(i),a=i?"null"===r?null:r||"{}":"{}";try{const e=JSON.parse(a);this.lodash_set(e,o,t),s=this.setval(JSON.stringify(e),i)}catch(e){const r={};this.lodash_set(r,o,t),s=this.setval(JSON.stringify(r),i)}}else s=this.setval(t,e);return s}getval(t){switch(this.getEnv()){case"Surge":case"Loon":case"Stash":case"Shadowrocket":return $persistentStore.read(t);case"Quantumult X":return $prefs.valueForKey(t);case"Node.js":return this.data=this.loaddata(),this.data[t];default:return this.data&&this.data[t]||null}}setval(t,e){switch(this.getEnv()){case"Surge":case"Loon":case"Stash":case"Shadowrocket":return $persistentStore.write(t,e);case"Quantumult X":return $prefs.setValueForKey(t,e);case"Node.js":return this.data=this.loaddata(),this.data[e]=t,this.writedata(),!0;default:return this.data&&this.data[e]||null}}initGotEnv(t){this.got=this.got?this.got:require("got"),this.cktough=this.cktough?this.cktough:require("tough-cookie"),this.ckjar=this.ckjar?this.ckjar:new this.cktough.CookieJar,t&&(t.headers=t.headers?t.headers:{},t&&(t.headers=t.headers?t.headers:{},void 0===t.headers.cookie&&void 0===t.headers.Cookie&&void 0===t.cookieJar&&(t.cookieJar=this.ckjar)))}get(t,e=(()=>{})){switch(t.headers&&(delete t.headers["Content-Type"],delete t.headers["Content-Length"],delete t.headers["content-type"],delete t.headers["content-length"]),t.params&&(t.url+="?"+this.queryStr(t.params)),void 0===t.followRedirect||t.followRedirect||((this.isSurge()||this.isLoon())&&(t["auto-redirect"]=!1),this.isQuanX()&&(t.opts?t.opts.redirection=!1:t.opts={redirection:!1})),this.getEnv()){case"Surge":case"Loon":case"Stash":case"Shadowrocket":default:this.isSurge()&&this.isNeedRewrite&&(t.headers=t.headers||{},Object.assign(t.headers,{"X-Surge-Skip-Scripting":!1})),$httpClient.get(t,((t,s,i)=>{!t&&s&&(s.body=i,s.statusCode=s.status?s.status:s.statusCode,s.status=s.statusCode),e(t,s,i)}));break;case"Quantumult X":this.isNeedRewrite&&(t.opts=t.opts||{},Object.assign(t.opts,{hints:!1})),$task.fetch(t).then((t=>{const{statusCode:s,statusCode:i,headers:o,body:r,bodyBytes:a}=t;e(null,{status:s,statusCode:i,headers:o,body:r,bodyBytes:a},r,a)}),(t=>e(t&&t.error||"UndefinedError")));break;case"Node.js":let s=require("iconv-lite");this.initGotEnv(t),this.got(t).on("redirect",((t,e)=>{try{if(t.headers["set-cookie"]){const s=t.headers["set-cookie"].map(this.cktough.Cookie.parse).toString();s&&this.ckjar.setCookieSync(s,null),e.cookieJar=this.ckjar}}catch(t){this.logErr(t)}})).then((t=>{const{statusCode:i,statusCode:o,headers:r,rawBody:a}=t,n=s.decode(a,this.encoding);e(null,{status:i,statusCode:o,headers:r,rawBody:a,body:n},n)}),(t=>{const{message:i,response:o}=t;e(i,o,o&&s.decode(o.rawBody,this.encoding))}));break}}post(t,e=(()=>{})){const s=t.method?t.method.toLocaleLowerCase():"post";switch(t.body&&t.headers&&!t.headers["Content-Type"]&&!t.headers["content-type"]&&(t.headers["content-type"]="application/x-www-form-urlencoded"),t.headers&&(delete t.headers["Content-Length"],delete t.headers["content-length"]),void 0===t.followRedirect||t.followRedirect||((this.isSurge()||this.isLoon())&&(t["auto-redirect"]=!1),this.isQuanX()&&(t.opts?t.opts.redirection=!1:t.opts={redirection:!1})),this.getEnv()){case"Surge":case"Loon":case"Stash":case"Shadowrocket":default:this.isSurge()&&this.isNeedRewrite&&(t.headers=t.headers||{},Object.assign(t.headers,{"X-Surge-Skip-Scripting":!1})),$httpClient[s](t,((t,s,i)=>{!t&&s&&(s.body=i,s.statusCode=s.status?s.status:s.statusCode,s.status=s.statusCode),e(t,s,i)}));break;case"Quantumult X":t.method=s,this.isNeedRewrite&&(t.opts=t.opts||{},Object.assign(t.opts,{hints:!1})),$task.fetch(t).then((t=>{const{statusCode:s,statusCode:i,headers:o,body:r,bodyBytes:a}=t;e(null,{status:s,statusCode:i,headers:o,body:r,bodyBytes:a},r,a)}),(t=>e(t&&t.error||"UndefinedError")));break;case"Node.js":let i=require("iconv-lite");this.initGotEnv(t);const{url:o,...r}=t;this.got[s](o,r).then((t=>{const{statusCode:s,statusCode:o,headers:r,rawBody:a}=t,n=i.decode(a,this.encoding);e(null,{status:s,statusCode:o,headers:r,rawBody:a,body:n},n)}),(t=>{const{message:s,response:o}=t;e(s,o,o&&i.decode(o.rawBody,this.encoding))}));break}}time(t,e=null){const s=e?new Date(e):new Date;let i={"M+":s.getMonth()+1,"d+":s.getDate(),"H+":s.getHours(),"m+":s.getMinutes(),"s+":s.getSeconds(),"q+":Math.floor((s.getMonth()+3)/3),S:s.getMilliseconds()};/(y+)/.test(t)&&(t=t.replace(RegExp.$1,(s.getFullYear()+"").substr(4-RegExp.$1.length)));for(let e in i)new RegExp("("+e+")").test(t)&&(t=t.replace(RegExp.$1,1==RegExp.$1.length?i[e]:("00"+i[e]).substr((""+i[e]).length)));return t}queryStr(t){let e="";for(const s in t){let i=t[s];null!=i&&""!==i&&("object"==typeof i&&(i=JSON.stringify(i)),e+=`${s}=${i}&`)}return e=e.substring(0,e.length-1),e}msg(e=t,s="",i="",o={}){const r=t=>{const{$open:e,$copy:s,$media:i,$mediaMime:o}=t;switch(typeof t){case void 0:return t;case"string":switch(this.getEnv()){case"Surge":case"Stash":default:return{url:t};case"Loon":case"Shadowrocket":return t;case"Quantumult X":return{"open-url":t};case"Node.js":return}case"object":switch(this.getEnv()){case"Surge":case"Stash":case"Shadowrocket":default:{const r={};let a=t.openUrl||t.url||t["open-url"]||e;a&&Object.assign(r,{action:"open-url",url:a});let n=t["update-pasteboard"]||t.updatePasteboard||s;if(n&&Object.assign(r,{action:"clipboard",text:n}),i){let t,e,s;if(i.startsWith("http"))t=i;else if(i.startsWith("data:")){const[t]=i.split(";"),[,o]=i.split(",");e=o,s=t.replace("data:","")}else{e=i,s=(t=>{const e={JVBERi0:"application/pdf",R0lGODdh:"image/gif",R0lGODlh:"image/gif",iVBORw0KGgo:"image/png","/9j/":"image/jpg"};for(var s in e)if(0===t.indexOf(s))return e[s];return null})(i)}Object.assign(r,{"media-url":t,"media-base64":e,"media-base64-mime":o??s})}return Object.assign(r,{"auto-dismiss":t["auto-dismiss"],sound:t.sound}),r}case"Loon":{const s={};let o=t.openUrl||t.url||t["open-url"]||e;o&&Object.assign(s,{openUrl:o});let r=t.mediaUrl||t["media-url"];return i?.startsWith("http")&&(r=i),r&&Object.assign(s,{mediaUrl:r}),console.log(JSON.stringify(s)),s}case"Quantumult X":{const o={};let r=t["open-url"]||t.url||t.openUrl||e;r&&Object.assign(o,{"open-url":r});let a=t["media-url"]||t.mediaUrl;i?.startsWith("http")&&(a=i),a&&Object.assign(o,{"media-url":a});let n=t["update-pasteboard"]||t.updatePasteboard||s;return n&&Object.assign(o,{"update-pasteboard":n}),console.log(JSON.stringify(o)),o}case"Node.js":return}default:return}};if(!this.isMute)switch(this.getEnv()){case"Surge":case"Loon":case"Stash":case"Shadowrocket":default:$notification.post(e,s,i,r(o));break;case"Quantumult X":$notify(e,s,i,r(o));break;case"Node.js":break}if(!this.isMuteLog){let t=["","==============📣系统通知📣=============="];t.push(e),s&&t.push(s),i&&t.push(i),console.log(t.join("\n")),this.logs=this.logs.concat(t)}}debug(...t){this.logLevels[this.logLevel]<=this.logLevels.debug&&(t.length>0&&(this.logs=[...this.logs,...t]),console.log(`${this.logLevelPrefixs.debug}${t.map((t=>t??String(t))).join(this.logSeparator)}`))}info(...t){this.logLevels[this.logLevel]<=this.logLevels.info&&(t.length>0&&(this.logs=[...this.logs,...t]),console.log(`${this.logLevelPrefixs.info}${t.map((t=>t??String(t))).join(this.logSeparator)}`))}warn(...t){this.logLevels[this.logLevel]<=this.logLevels.warn&&(t.length>0&&(this.logs=[...this.logs,...t]),console.log(`${this.logLevelPrefixs.warn}${t.map((t=>t??String(t))).join(this.logSeparator)}`))}error(...t){this.logLevels[this.logLevel]<=this.logLevels.error&&(t.length>0&&(this.logs=[...this.logs,...t]),console.log(`${this.logLevelPrefixs.error}${t.map((t=>t??String(t))).join(this.logSeparator)}`))}log(...t){t.length>0&&(this.logs=[...this.logs,...t]),console.log(t.map((t=>t??String(t))).join(this.logSeparator))}logErr(t,e){switch(this.getEnv()){case"Surge":case"Loon":case"Stash":case"Shadowrocket":case"Quantumult X":default:this.log("",`❗️${this.name}, 错误!`,e,t);break;case"Node.js":this.log("",`❗️${this.name}, 错误!`,e,void 0!==t.message?t.message:t,t.stack);break}}wait(t){return new Promise((e=>setTimeout(e,t)))}done(t={}){const e=((new Date).getTime()-this.startTime)/1e3;switch(this.log("",`🔔${this.name}, 结束! 🕛 ${e} 秒`),this.log(),this.getEnv()){case"Surge":case"Loon":case"Stash":case"Shadowrocket":case"Quantumult X":default:$done(t);break;case"Node.js":process.exit(1)}}}(t,e)} 172 | -------------------------------------------------------------------------------- /js/cmsAdblock.js: -------------------------------------------------------------------------------- 1 | const $ = new Env('cmsAdblock') 2 | 3 | // heimuer 4 | let hmrvideo = ['tang.hz'] 5 | 6 | // 量子資源 7 | let lzzy = [] 8 | 9 | // 非凡資源 10 | let ffzy = [ 11 | //":6.400000,", 12 | //":3.700000,", 13 | //":2.800000,", 14 | //":1.766667,", 15 | ] 16 | 17 | // 暴風 18 | let bfeng = ['/adjump/'] 19 | 20 | // 快看 21 | let kuaikan = [] 22 | 23 | // ikun 24 | //let ikun = ['9898kb'] 25 | 26 | // 魔都 27 | //let modu = ['11425kb'] 28 | 29 | // 360 30 | //let lyhuicheng = ['11978kb'] 31 | 32 | // U酷資源 33 | //let ukzy = [] 34 | 35 | // 櫻花資源 36 | let yhzy = [] 37 | 38 | // 91麻豆 39 | let t097img = ['y.ts'] 40 | 41 | let temp = [] 42 | 43 | let arg = getArg() 44 | let isMsg = arg.toLowerCase() === 'true' 45 | 46 | //if (arg === '2') { 47 | // $.log('using 2nd method') 48 | // fixAdM3u8Ai($request.url) 49 | //} 50 | 51 | if ($response.body === undefined || !$response.body.includes('#EXTM3U')) 52 | $.done({}) 53 | 54 | const url = $request.url 55 | let lines = $response.body.trim().split('\n') 56 | 57 | let adCount = 0 58 | 59 | ;(async () => { 60 | switch (true) { 61 | case url.includes('hmrvideo'): 62 | await fetchJxResult() 63 | break 64 | case url.includes('wgslsw'): 65 | hostsCount(yhzy, /^https?:\/\/(.*?)\//) 66 | filterAds(yhzy) 67 | break 68 | case url.includes('v.cdnlz'): 69 | case url.includes('lz-cdn'): 70 | case url.includes('lzcdn'): 71 | await fetchJxResult() 72 | length() 73 | filterAds(lzzy) 74 | break 75 | case url.includes('ffzy'): 76 | await fetchJxResult() 77 | length() 78 | filterAds(ffzy) 79 | break 80 | case url.includes('rrcdnbf'): 81 | filterAds(bfeng) 82 | break 83 | case url.includes('kuaikan'): 84 | await fetchJxResult() 85 | hostsCount(kuaikan, /(.+)\/hls\//) 86 | filterAds(kuaikan) 87 | break 88 | case url.includes('97img'): 89 | filterAds(t097img) 90 | break 91 | case url.includes('feidaozy'): 92 | vodId(temp, 10) 93 | filterAds(temp) 94 | break 95 | case url.includes('bfikuncdn'): 96 | case url.includes('modujx'): 97 | case url.includes('lyhuicheng'): 98 | case url.includes('ukzy'): 99 | case url.includes('askzy'): 100 | case url.includes('bfbfhao'): 101 | case url.includes('cl9987'): 102 | case url.includes('ykv3'): 103 | case url.includes('sybf'): 104 | case url.includes('bfnxxcdn'): 105 | case url.includes('huangguam3u'): 106 | case url.includes('leshiyuncdn'): 107 | await fetchJxResult() 108 | vodId(temp, 15) 109 | filterAds(temp) 110 | break 111 | default: 112 | await fetchJxResult() 113 | vodId(temp, 10) 114 | filterAds(temp) 115 | break 116 | } 117 | })() 118 | 119 | function filterAds(valuesToRemove) { 120 | for (let i = lines.length - 1; i >= 0; i--) { 121 | if (valuesToRemove.some((value) => lines[i].includes(value))) { 122 | let value = valuesToRemove.find((value) => lines[i].includes(value)) 123 | $.log('Match:' + value) 124 | if (value.includes('kb')) { 125 | $.log('Remove ad(by bitrate):' + lines[i]) 126 | lines.splice(i - 1, 2) 127 | adCount++ 128 | } else if (!lines[i].startsWith('#')) { 129 | $.log('Remove ad(by url):' + lines[i]) 130 | lines.splice(i - 1, 2) 131 | adCount++ 132 | } else if (i < lines.length - 1 && lines[i + 1].includes('.ts')) { 133 | $.log('Remove ad(by duration):' + lines[i + 1]) 134 | lines.splice(i, 2) 135 | adCount++ 136 | } 137 | } 138 | } 139 | 140 | if (isMsg) $.msg('cmsAdblock', `移除廣告${adCount}行`) 141 | $.log(`移除廣告${adCount}行`) 142 | $.done({ body: lines.join('\n') }) 143 | } 144 | 145 | function filterHmr(valuesToRemove) { 146 | for (let i = lines.length - 1; i >= 0; i--) { 147 | if (valuesToRemove.some((value) => lines[i].includes(value))) { 148 | let value = valuesToRemove.find((value) => lines[i].includes(value)) 149 | $.log('Match:' + value) 150 | if (value.includes('kb')) { 151 | $.log('Remove ad(by bitrate):' + lines[i]) 152 | lines.splice(i - 1, 2) 153 | adCount++ 154 | } else if (!lines[i].startsWith('#')) { 155 | $.log('Remove ad(by url):' + lines[i]) 156 | lines.splice(i - 1, 2) 157 | adCount++ 158 | } else if ( 159 | i < lines.length - 1 && 160 | lines[i + 1].includes('.ts') && 161 | lines[i - 1].includes('DISCONTINUITY') && 162 | lines[i + 2].includes('DISCONTINUITY') 163 | ) { 164 | $.log('Remove ad(by duration):' + lines[i + 1]) 165 | lines.splice(i, 2) 166 | adCount++ 167 | } 168 | } 169 | } 170 | 171 | $.msg('debug', `移除廣告${adCount}行`) 172 | $.log(`移除廣告${adCount}行`) 173 | $.done({ body: lines.join('\n') }) 174 | } 175 | 176 | function hostsCount(name, regex) { 177 | const hostsCount = {} 178 | lines.forEach((line) => { 179 | if (line.includes('.ts')) { 180 | const hostname = line.match(regex)[1] 181 | hostsCount[hostname] = (hostsCount[hostname] || 0) + 1 182 | } 183 | }) 184 | 185 | $.log(hostsCount) 186 | const keys = Object.keys(hostsCount) 187 | if (keys.length > 1) { 188 | keys.sort((a, b) => hostsCount[b] - hostsCount[a]) 189 | let temp = keys.slice(1) 190 | name.push(...temp) 191 | } else return 192 | } 193 | 194 | function vodId(name, length) { 195 | const vodIds = {} 196 | lines.forEach((line) => { 197 | if (!line.startsWith('#')) { 198 | const vodId = line.slice(0, length) 199 | vodIds[vodId] = (vodIds[vodId] || 0) + 1 200 | } 201 | }) 202 | $.log(JSON.stringify(vodIds)) 203 | const keys = Object.keys(vodIds) 204 | 205 | if (keys.length > 1) { 206 | keys.sort((a, b) => vodIds[b] - vodIds[a]) 207 | let temp = keys.slice(1) 208 | name.push(...temp) 209 | } else return 210 | } 211 | 212 | function length() { 213 | const fileLength = {} 214 | let files = lines.filter((i) => !i.startsWith('#')) 215 | 216 | files.forEach((file) => { 217 | fileLength[file.length] = (fileLength[file.length] || 0) + 1 218 | }) 219 | $.log(JSON.stringify(fileLength)) 220 | 221 | const keys = Object.keys(fileLength) 222 | if (keys.length > 1) { 223 | keys.sort((a, b) => fileLength[b] - fileLength[a]) 224 | let ad = keys.slice(1)[0] 225 | $.log(ad) 226 | for (let i = lines.length - 1; i >= 0; i--) { 227 | if (lines[i].length == ad && lines[i].endsWith('.ts')) { 228 | $.log('Remove ad(by file length):' + lines[i]) 229 | lines.splice(i - 1, 2) 230 | adCount++ 231 | } 232 | } 233 | } else return 234 | } 235 | 236 | async function fetchJxResult() { 237 | if (url.includes('hls')) return 238 | 239 | let jx 240 | 241 | if (url.includes('hmrvideo')) { 242 | let newUrl = url.replace('m3u8.', 'mycj-m3u8.') 243 | jx = 'https://speed.tang.hz.cz/noads?token=e50317a7&url=' 244 | const requestUrl = jx + newUrl 245 | $.log('requestIrl:', requestUrl) 246 | const req = { 247 | url: requestUrl, 248 | headers: { 'User-Agent': 'okhttp/5.0.0-alpha.14' }, 249 | timeout: 10000, 250 | } 251 | try { 252 | const resp = await $.http.get(req) 253 | const body = resp.body 254 | if (body.startsWith('{')) { 255 | $.log('error') 256 | $.done({}) 257 | } 258 | lines = body.trim().split('\n') 259 | filterAds(hmrvideo) 260 | //$.done({ body: body }) 261 | } catch (e) { 262 | $.log(e) 263 | $.done({}) 264 | } 265 | } else { 266 | jx = 'https://jscdn.centos.chat/bilfun.php/?url=' 267 | const requestUrl = jx + url 268 | const req = { 269 | url: requestUrl, 270 | headers: { 'User-Agent': 'okhttp/5.0.0-alpha.14' }, 271 | timeout: 5000, 272 | } 273 | try { 274 | const resp = await $.http.get(req) 275 | const body = JSON.parse(resp.body) 276 | $.log(resp.body) 277 | if (body.url !== $request.url) { 278 | const m3u8 = ( 279 | await $.http.get({ 280 | url: body.url, 281 | headers: { 'User-Agent': 'okhttp/3.12' }, 282 | timeout: 8000, 283 | }) 284 | ).body 285 | if (isMsg) $.msg('cmsAdblock', 'Redirect to ' + body.url) 286 | $.isQuanX() 287 | ? $.done({ 288 | status: 'HTTP/1.1 200', 289 | headers: { 'Content-Type': 'application/vnd.apple.mpegURL' }, 290 | body: m3u8, 291 | }) 292 | : $.done({ 293 | status: 200, 294 | headers: { 'Content-Type': 'application/vnd.apple.mpegURL' }, 295 | body: m3u8, 296 | }) 297 | //$.log('Redirect to', body.url) 298 | //$.msg('Redirect to', body.url) 299 | } else return 300 | } catch (e) { 301 | $.log(e) 302 | $.done({}) 303 | } 304 | } 305 | } 306 | 307 | function getArg() { 308 | if ($.isLoon()) return $persistentStore.read('彈窗通知') 309 | if (typeof $argument === 'undefined') { 310 | return false 311 | } 312 | return $argument 313 | } 314 | 315 | //prettier-ignore 316 | function Env(t,e){class s{constructor(t){this.env=t}send(t,e="GET"){t="string"==typeof t?{url:t}:t;let s=this.get;"POST"===e&&(s=this.post);const i=new Promise(((e,i)=>{s.call(this,t,((t,s,o)=>{t?i(t):e(s)}))}));return t.timeout?((t,e=1e3)=>Promise.race([t,new Promise(((t,s)=>{setTimeout((()=>{s(new Error("请求超时"))}),e)}))]))(i,t.timeout):i}get(t){return this.send.call(this.env,t)}post(t){return this.send.call(this.env,t,"POST")}}return new class{constructor(t,e){this.logLevels={debug:0,info:1,warn:2,error:3},this.logLevelPrefixs={debug:"[DEBUG] ",info:"[INFO] ",warn:"[WARN] ",error:"[ERROR] "},this.logLevel="info",this.name=t,this.http=new s(this),this.data=null,this.dataFile="box.dat",this.logs=[],this.isMute=!1,this.isNeedRewrite=!1,this.logSeparator="\n",this.encoding="utf-8",this.startTime=(new Date).getTime(),Object.assign(this,e),this.log("",`🔔${this.name}, 开始!`)}getEnv(){return"undefined"!=typeof $environment&&$environment["surge-version"]?"Surge":"undefined"!=typeof $environment&&$environment["stash-version"]?"Stash":"undefined"!=typeof module&&module.exports?"Node.js":"undefined"!=typeof $task?"Quantumult X":"undefined"!=typeof $loon?"Loon":"undefined"!=typeof $rocket?"Shadowrocket":void 0}isNode(){return"Node.js"===this.getEnv()}isQuanX(){return"Quantumult X"===this.getEnv()}isSurge(){return"Surge"===this.getEnv()}isLoon(){return"Loon"===this.getEnv()}isShadowrocket(){return"Shadowrocket"===this.getEnv()}isStash(){return"Stash"===this.getEnv()}toObj(t,e=null){try{return JSON.parse(t)}catch{return e}}toStr(t,e=null,...s){try{return JSON.stringify(t,...s)}catch{return e}}getjson(t,e){let s=e;if(this.getdata(t))try{s=JSON.parse(this.getdata(t))}catch{}return s}setjson(t,e){try{return this.setdata(JSON.stringify(t),e)}catch{return!1}}getScript(t){return new Promise((e=>{this.get({url:t},((t,s,i)=>e(i)))}))}runScript(t,e){return new Promise((s=>{let i=this.getdata("@chavy_boxjs_userCfgs.httpapi");i=i?i.replace(/\n/g,"").trim():i;let o=this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout");o=o?1*o:20,o=e&&e.timeout?e.timeout:o;const[r,a]=i.split("@"),n={url:`http://${a}/v1/scripting/evaluate`,body:{script_text:t,mock_type:"cron",timeout:o},headers:{"X-Key":r,Accept:"*/*"},policy:"DIRECT",timeout:o};this.post(n,((t,e,i)=>s(i)))})).catch((t=>this.logErr(t)))}loaddata(){if(!this.isNode())return{};{this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const t=this.path.resolve(this.dataFile),e=this.path.resolve(process.cwd(),this.dataFile),s=this.fs.existsSync(t),i=!s&&this.fs.existsSync(e);if(!s&&!i)return{};{const i=s?t:e;try{return JSON.parse(this.fs.readFileSync(i))}catch(t){return{}}}}}writedata(){if(this.isNode()){this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const t=this.path.resolve(this.dataFile),e=this.path.resolve(process.cwd(),this.dataFile),s=this.fs.existsSync(t),i=!s&&this.fs.existsSync(e),o=JSON.stringify(this.data);s?this.fs.writeFileSync(t,o):i?this.fs.writeFileSync(e,o):this.fs.writeFileSync(t,o)}}lodash_get(t,e,s){const i=e.replace(/\[(\d+)\]/g,".$1").split(".");let o=t;for(const t of i)if(o=Object(o)[t],void 0===o)return s;return o}lodash_set(t,e,s){return Object(t)!==t||(Array.isArray(e)||(e=e.toString().match(/[^.[\]]+/g)||[]),e.slice(0,-1).reduce(((t,s,i)=>Object(t[s])===t[s]?t[s]:t[s]=Math.abs(e[i+1])>>0==+e[i+1]?[]:{}),t)[e[e.length-1]]=s),t}getdata(t){let e=this.getval(t);if(/^@/.test(t)){const[,s,i]=/^@(.*?)\.(.*?)$/.exec(t),o=s?this.getval(s):"";if(o)try{const t=JSON.parse(o);e=t?this.lodash_get(t,i,""):e}catch(t){e=""}}return e}setdata(t,e){let s=!1;if(/^@/.test(e)){const[,i,o]=/^@(.*?)\.(.*?)$/.exec(e),r=this.getval(i),a=i?"null"===r?null:r||"{}":"{}";try{const e=JSON.parse(a);this.lodash_set(e,o,t),s=this.setval(JSON.stringify(e),i)}catch(e){const r={};this.lodash_set(r,o,t),s=this.setval(JSON.stringify(r),i)}}else s=this.setval(t,e);return s}getval(t){switch(this.getEnv()){case"Surge":case"Loon":case"Stash":case"Shadowrocket":return $persistentStore.read(t);case"Quantumult X":return $prefs.valueForKey(t);case"Node.js":return this.data=this.loaddata(),this.data[t];default:return this.data&&this.data[t]||null}}setval(t,e){switch(this.getEnv()){case"Surge":case"Loon":case"Stash":case"Shadowrocket":return $persistentStore.write(t,e);case"Quantumult X":return $prefs.setValueForKey(t,e);case"Node.js":return this.data=this.loaddata(),this.data[e]=t,this.writedata(),!0;default:return this.data&&this.data[e]||null}}initGotEnv(t){this.got=this.got?this.got:require("got"),this.cktough=this.cktough?this.cktough:require("tough-cookie"),this.ckjar=this.ckjar?this.ckjar:new this.cktough.CookieJar,t&&(t.headers=t.headers?t.headers:{},t&&(t.headers=t.headers?t.headers:{},void 0===t.headers.cookie&&void 0===t.headers.Cookie&&void 0===t.cookieJar&&(t.cookieJar=this.ckjar)))}get(t,e=(()=>{})){switch(t.headers&&(delete t.headers["Content-Type"],delete t.headers["Content-Length"],delete t.headers["content-type"],delete t.headers["content-length"]),t.params&&(t.url+="?"+this.queryStr(t.params)),void 0===t.followRedirect||t.followRedirect||((this.isSurge()||this.isLoon())&&(t["auto-redirect"]=!1),this.isQuanX()&&(t.opts?t.opts.redirection=!1:t.opts={redirection:!1})),this.getEnv()){case"Surge":case"Loon":case"Stash":case"Shadowrocket":default:this.isSurge()&&this.isNeedRewrite&&(t.headers=t.headers||{},Object.assign(t.headers,{"X-Surge-Skip-Scripting":!1})),$httpClient.get(t,((t,s,i)=>{!t&&s&&(s.body=i,s.statusCode=s.status?s.status:s.statusCode,s.status=s.statusCode),e(t,s,i)}));break;case"Quantumult X":this.isNeedRewrite&&(t.opts=t.opts||{},Object.assign(t.opts,{hints:!1})),$task.fetch(t).then((t=>{const{statusCode:s,statusCode:i,headers:o,body:r,bodyBytes:a}=t;e(null,{status:s,statusCode:i,headers:o,body:r,bodyBytes:a},r,a)}),(t=>e(t&&t.error||"UndefinedError")));break;case"Node.js":let s=require("iconv-lite");this.initGotEnv(t),this.got(t).on("redirect",((t,e)=>{try{if(t.headers["set-cookie"]){const s=t.headers["set-cookie"].map(this.cktough.Cookie.parse).toString();s&&this.ckjar.setCookieSync(s,null),e.cookieJar=this.ckjar}}catch(t){this.logErr(t)}})).then((t=>{const{statusCode:i,statusCode:o,headers:r,rawBody:a}=t,n=s.decode(a,this.encoding);e(null,{status:i,statusCode:o,headers:r,rawBody:a,body:n},n)}),(t=>{const{message:i,response:o}=t;e(i,o,o&&s.decode(o.rawBody,this.encoding))}));break}}post(t,e=(()=>{})){const s=t.method?t.method.toLocaleLowerCase():"post";switch(t.body&&t.headers&&!t.headers["Content-Type"]&&!t.headers["content-type"]&&(t.headers["content-type"]="application/x-www-form-urlencoded"),t.headers&&(delete t.headers["Content-Length"],delete t.headers["content-length"]),void 0===t.followRedirect||t.followRedirect||((this.isSurge()||this.isLoon())&&(t["auto-redirect"]=!1),this.isQuanX()&&(t.opts?t.opts.redirection=!1:t.opts={redirection:!1})),this.getEnv()){case"Surge":case"Loon":case"Stash":case"Shadowrocket":default:this.isSurge()&&this.isNeedRewrite&&(t.headers=t.headers||{},Object.assign(t.headers,{"X-Surge-Skip-Scripting":!1})),$httpClient[s](t,((t,s,i)=>{!t&&s&&(s.body=i,s.statusCode=s.status?s.status:s.statusCode,s.status=s.statusCode),e(t,s,i)}));break;case"Quantumult X":t.method=s,this.isNeedRewrite&&(t.opts=t.opts||{},Object.assign(t.opts,{hints:!1})),$task.fetch(t).then((t=>{const{statusCode:s,statusCode:i,headers:o,body:r,bodyBytes:a}=t;e(null,{status:s,statusCode:i,headers:o,body:r,bodyBytes:a},r,a)}),(t=>e(t&&t.error||"UndefinedError")));break;case"Node.js":let i=require("iconv-lite");this.initGotEnv(t);const{url:o,...r}=t;this.got[s](o,r).then((t=>{const{statusCode:s,statusCode:o,headers:r,rawBody:a}=t,n=i.decode(a,this.encoding);e(null,{status:s,statusCode:o,headers:r,rawBody:a,body:n},n)}),(t=>{const{message:s,response:o}=t;e(s,o,o&&i.decode(o.rawBody,this.encoding))}));break}}time(t,e=null){const s=e?new Date(e):new Date;let i={"M+":s.getMonth()+1,"d+":s.getDate(),"H+":s.getHours(),"m+":s.getMinutes(),"s+":s.getSeconds(),"q+":Math.floor((s.getMonth()+3)/3),S:s.getMilliseconds()};/(y+)/.test(t)&&(t=t.replace(RegExp.$1,(s.getFullYear()+"").substr(4-RegExp.$1.length)));for(let e in i)new RegExp("("+e+")").test(t)&&(t=t.replace(RegExp.$1,1==RegExp.$1.length?i[e]:("00"+i[e]).substr((""+i[e]).length)));return t}queryStr(t){let e="";for(const s in t){let i=t[s];null!=i&&""!==i&&("object"==typeof i&&(i=JSON.stringify(i)),e+=`${s}=${i}&`)}return e=e.substring(0,e.length-1),e}msg(e=t,s="",i="",o={}){const r=t=>{const{$open:e,$copy:s,$media:i,$mediaMime:o}=t;switch(typeof t){case void 0:return t;case"string":switch(this.getEnv()){case"Surge":case"Stash":default:return{url:t};case"Loon":case"Shadowrocket":return t;case"Quantumult X":return{"open-url":t};case"Node.js":return}case"object":switch(this.getEnv()){case"Surge":case"Stash":case"Shadowrocket":default:{const r={};let a=t.openUrl||t.url||t["open-url"]||e;a&&Object.assign(r,{action:"open-url",url:a});let n=t["update-pasteboard"]||t.updatePasteboard||s;if(n&&Object.assign(r,{action:"clipboard",text:n}),i){let t,e,s;if(i.startsWith("http"))t=i;else if(i.startsWith("data:")){const[t]=i.split(";"),[,o]=i.split(",");e=o,s=t.replace("data:","")}else{e=i,s=(t=>{const e={JVBERi0:"application/pdf",R0lGODdh:"image/gif",R0lGODlh:"image/gif",iVBORw0KGgo:"image/png","/9j/":"image/jpg"};for(var s in e)if(0===t.indexOf(s))return e[s];return null})(i)}Object.assign(r,{"media-url":t,"media-base64":e,"media-base64-mime":o??s})}return Object.assign(r,{"auto-dismiss":t["auto-dismiss"],sound:t.sound}),r}case"Loon":{const s={};let o=t.openUrl||t.url||t["open-url"]||e;o&&Object.assign(s,{openUrl:o});let r=t.mediaUrl||t["media-url"];return i?.startsWith("http")&&(r=i),r&&Object.assign(s,{mediaUrl:r}),console.log(JSON.stringify(s)),s}case"Quantumult X":{const o={};let r=t["open-url"]||t.url||t.openUrl||e;r&&Object.assign(o,{"open-url":r});let a=t["media-url"]||t.mediaUrl;i?.startsWith("http")&&(a=i),a&&Object.assign(o,{"media-url":a});let n=t["update-pasteboard"]||t.updatePasteboard||s;return n&&Object.assign(o,{"update-pasteboard":n}),console.log(JSON.stringify(o)),o}case"Node.js":return}default:return}};if(!this.isMute)switch(this.getEnv()){case"Surge":case"Loon":case"Stash":case"Shadowrocket":default:$notification.post(e,s,i,r(o));break;case"Quantumult X":$notify(e,s,i,r(o));break;case"Node.js":break}if(!this.isMuteLog){let t=["","==============📣系统通知📣=============="];t.push(e),s&&t.push(s),i&&t.push(i),console.log(t.join("\n")),this.logs=this.logs.concat(t)}}debug(...t){this.logLevels[this.logLevel]<=this.logLevels.debug&&(t.length>0&&(this.logs=[...this.logs,...t]),console.log(`${this.logLevelPrefixs.debug}${t.map((t=>t??String(t))).join(this.logSeparator)}`))}info(...t){this.logLevels[this.logLevel]<=this.logLevels.info&&(t.length>0&&(this.logs=[...this.logs,...t]),console.log(`${this.logLevelPrefixs.info}${t.map((t=>t??String(t))).join(this.logSeparator)}`))}warn(...t){this.logLevels[this.logLevel]<=this.logLevels.warn&&(t.length>0&&(this.logs=[...this.logs,...t]),console.log(`${this.logLevelPrefixs.warn}${t.map((t=>t??String(t))).join(this.logSeparator)}`))}error(...t){this.logLevels[this.logLevel]<=this.logLevels.error&&(t.length>0&&(this.logs=[...this.logs,...t]),console.log(`${this.logLevelPrefixs.error}${t.map((t=>t??String(t))).join(this.logSeparator)}`))}log(...t){t.length>0&&(this.logs=[...this.logs,...t]),console.log(t.map((t=>t??String(t))).join(this.logSeparator))}logErr(t,e){switch(this.getEnv()){case"Surge":case"Loon":case"Stash":case"Shadowrocket":case"Quantumult X":default:this.log("",`❗️${this.name}, 错误!`,e,t);break;case"Node.js":this.log("",`❗️${this.name}, 错误!`,e,void 0!==t.message?t.message:t,t.stack);break}}wait(t){return new Promise((e=>setTimeout(e,t)))}done(t={}){const e=((new Date).getTime()-this.startTime)/1e3;switch(this.log("",`🔔${this.name}, 结束! 🕛 ${e} 秒`),this.log(),this.getEnv()){case"Surge":case"Loon":case"Stash":case"Shadowrocket":case"Quantumult X":default:$done(t);break;case"Node.js":process.exit(1)}}}(t,e)} 317 | -------------------------------------------------------------------------------- /Bili/BiliBili.boxjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "BiliUniverse.BiliBili.sub", 3 | "name": "🪐 BiliUniverse: 📺 BiliBili", 4 | "author": "@ykusu修改", 5 | "description": "BiliBili功能增強", 6 | "icon": "https://avatars.githubusercontent.com/u/129515498?s=200&v=4", 7 | "repo": "https://github.com/BiliUniverse/Universe", 8 | "apps": [ 9 | { 10 | "id": "BiliBili.Enhanced.Home", 11 | "name": "📺 BiliBili: ⚙ Enhanced (首頁)", 12 | "descs_html": [ 13 | "填寫完成後記得點擊此頁面底端右下角的\"保存\"。" 14 | ], 15 | "keys": [ 16 | "@BiliBili.Enhanced.Settings.Home", 17 | "@BiliBili.Optional.Tab", 18 | "@BiliBili.Enhanced.Caches" 19 | ], 20 | "settings": [ 21 | { 22 | "id": "@BiliBili.Enhanced.Settings.Switch", 23 | "name": "總功能開關", 24 | "val": true, 25 | "type": "boolean", 26 | "desc": "是否啓用此APP修改" 27 | }, 28 | { 29 | "id": "@BiliBili.Enhanced.Settings.Home.Tab", 30 | "name": "首頁: 標籤頁", 31 | "val": [ 32 | "直播tab", 33 | "推荐tab", 34 | "hottopic", 35 | "bangumi", 36 | "anime", 37 | "film", 38 | "koreavtw" 39 | ], 40 | "type": "checkboxes", 41 | "items": [ 42 | { 43 | "key": "直播tab", 44 | "label": "直播" 45 | }, 46 | { 47 | "key": "推荐tab", 48 | "label": "推薦" 49 | }, 50 | { 51 | "key": "hottopic", 52 | "label": "熱門" 53 | }, 54 | { 55 | "key": "bangumi", 56 | "label": "番劇" 57 | }, 58 | { 59 | "key": "anime", 60 | "label": "動畫(港澳台)" 61 | }, 62 | { 63 | "key": "film", 64 | "label": "影視" 65 | }, 66 | { 67 | "key": "koreavtw", 68 | "label": "韓綜(港澳台)" 69 | }, 70 | { 71 | "key": "game", 72 | "label": "遊戲" 73 | }, 74 | { 75 | "key": "mctab", 76 | "label": "minecraft" 77 | }, 78 | { 79 | "key": "dhtr", 80 | "label": "動畫同人" 81 | }, 82 | { 83 | "key": "gaoxiao", 84 | "label": "搞笑" 85 | }, 86 | { 87 | "key": "school", 88 | "label": "校園" 89 | }, 90 | { 91 | "key": "kj", 92 | "label": "數碼" 93 | }, 94 | { 95 | "key": "kpop", 96 | "label": "kpop" 97 | }, 98 | { 99 | "key": "ive", 100 | "label": "IVE" 101 | }, 102 | { 103 | "key": "stayc", 104 | "label": "STAYC" 105 | }, 106 | { 107 | "key": "le", 108 | "label": "LESSERAFIM" 109 | }, 110 | { 111 | "key": "gidle", 112 | "label": "(G)I-DLE" 113 | }, 114 | { 115 | "key": "165", 116 | "label": "新征程" 117 | } 118 | ], 119 | "desc": "選擇啓用的首頁標籤頁,建議不超過7個" 120 | }, 121 | { 122 | "id": "@BiliBili.Optional.Tab", 123 | "name": "手動填入標籤頁", 124 | "val": "", 125 | "type": "textarea", 126 | "placeholder": "", 127 | "autoGrow": true, 128 | "desc": "" 129 | }, 130 | { 131 | "id": "@BiliBili.Enhanced.Settings.Home.Tab_default", 132 | "name": "首頁: 默認標籤頁", 133 | "val": "推荐tab", 134 | "type": "selects", 135 | "items": [ 136 | { 137 | "key": "直播tab", 138 | "label": "直播" 139 | }, 140 | { 141 | "key": "推荐tab", 142 | "label": "推薦" 143 | }, 144 | { 145 | "key": "hottopic", 146 | "label": "熱門" 147 | }, 148 | { 149 | "key": "bangumi", 150 | "label": "番劇" 151 | }, 152 | { 153 | "key": "anime", 154 | "label": "動畫(港澳台)" 155 | }, 156 | { 157 | "key": "film", 158 | "label": "影視" 159 | }, 160 | { 161 | "key": "koreavtw", 162 | "label": "韓綜(港澳台)" 163 | }, 164 | { 165 | "key": "game", 166 | "label": "遊戲" 167 | }, 168 | { 169 | "key": "mctab", 170 | "label": "minecraft" 171 | }, 172 | { 173 | "key": "dhtr", 174 | "label": "動畫同人" 175 | }, 176 | { 177 | "key": "gaoxiao", 178 | "label": "搞笑" 179 | }, 180 | { 181 | "key": "school", 182 | "label": "校園" 183 | }, 184 | { 185 | "key": "kj", 186 | "label": "數碼" 187 | }, 188 | { 189 | "key": "kpop", 190 | "label": "kpop" 191 | }, 192 | { 193 | "key": "ive", 194 | "label": "IVE" 195 | }, 196 | { 197 | "key": "stayc", 198 | "label": "STAYC" 199 | }, 200 | { 201 | "key": "le", 202 | "label": "LESSERAFIM" 203 | }, 204 | { 205 | "key": "gidle", 206 | "label": "(G)I-DLE" 207 | }, 208 | { 209 | "key": "165", 210 | "label": "新征程" 211 | } 212 | ], 213 | "desc": "選擇啓動APP時預設顯示的標籤頁,需選擇已啓用的標籤頁" 214 | }, 215 | { 216 | "id": "@BiliBili.Enhanced.Settings.Home.Top_left", 217 | "name": "首頁: 頂欄(左側)按鈕(用戶頭像)", 218 | "val": "mine", 219 | "type": "selects", 220 | "items": [ 221 | { 222 | "key": "mine", 223 | "label": "用户中心-我的" 224 | }, 225 | { 226 | "key": "videoshortcut", 227 | "label": "短視頻" 228 | } 229 | ], 230 | "desc": "選擇頂欄(左側)按鈕(用戶頭像)的作用(在bilibili粉色版中無法修改)" 231 | }, 232 | { 233 | "id": "@BiliBili.Enhanced.Settings.Home.Top", 234 | "name": "首頁: 頂欄(右側)按鈕", 235 | "val": [ 236 | "消息Top" 237 | ], 238 | "type": "checkboxes", 239 | "items": [ 240 | { 241 | "key": "游戏中心Top", 242 | "label": "遊戲中心" 243 | }, 244 | { 245 | "key": "会员购Top", 246 | "label": "會員購" 247 | }, 248 | { 249 | "key": "消息Top", 250 | "label": "消息" 251 | } 252 | ], 253 | "desc": "選擇啓用的頂欄(右側)按鈕" 254 | }, 255 | { 256 | "id": "@BiliBili.Enhanced.Settings.Bottom", 257 | "name": "底部導航欄", 258 | "val": [ 259 | "home", 260 | "dynamic", 261 | "ogv", 262 | "会员购Bottom", 263 | "我的Bottom" 264 | ], 265 | "type": "checkboxes", 266 | "items": [ 267 | { 268 | "key": "home", 269 | "label": "首頁" 270 | }, 271 | { 272 | "key": "频道Bottom", 273 | "label": "頻道" 274 | }, 275 | { 276 | "key": "dynamic", 277 | "label": "動態" 278 | }, 279 | { 280 | "key": "publish", 281 | "label": "發佈" 282 | }, 283 | { 284 | "key": "ogv", 285 | "label": "節目(港澳台)" 286 | }, 287 | { 288 | "key": "会员购Bottom", 289 | "label": "會員購" 290 | }, 291 | { 292 | "key": "消息Bottom", 293 | "label": "消息" 294 | }, 295 | { 296 | "key": "我的Bottom", 297 | "label": "我的" 298 | } 299 | ], 300 | "desc": "選擇啓用的底部導航欄,最多6個" 301 | } 302 | ], 303 | "author": "@BiliUniverse", 304 | "repo": "https://github.com/BiliUniverse/Enhanced", 305 | "icons": [ 306 | "https://github.com/BiliUniverse/Enhanced/raw/main/database/icon_circled_108x.png", 307 | "https://github.com/BiliUniverse/Enhanced/raw/main/database/icon_circled_108x.png" 308 | ] 309 | }, 310 | { 311 | "id": "BiliBili.Enhanced.Mine", 312 | "name": "📺 BiliBili: ⚙ Enhanced (我的)", 313 | "descs_html": [ 314 | "填寫完成後記得點擊此頁面底端右下角的\"保存\"。" 315 | ], 316 | "keys": [ 317 | "@BiliBili.Enhanced.Settings.Mine", 318 | "@BiliBili.Enhanced.Caches" 319 | ], 320 | "settings": [ 321 | { 322 | "id": "@BiliBili.Enhanced.Settings.Switch", 323 | "name": "總功能開關", 324 | "val": true, 325 | "type": "boolean", 326 | "desc": "是否啓用此APP修改" 327 | }, 328 | { 329 | "id": "@BiliBili.Enhanced.Settings.Mine.CreatorCenter", 330 | "name": "創作中心", 331 | "val": [], 332 | "type": "checkboxes", 333 | "items": [ 334 | { 335 | "key": "171", 336 | "label": "創作首頁" 337 | }, 338 | { 339 | "key": "172", 340 | "label": "稿件管理" 341 | }, 342 | { 343 | "key": "174", 344 | "label": "有獎活動" 345 | }, 346 | { 347 | "key": "533", 348 | "label": "任務中心" 349 | }, 350 | { 351 | "key": "707", 352 | "label": "主播中心" 353 | }, 354 | { 355 | "key": "708", 356 | "label": "主播活動" 357 | }, 358 | { 359 | "key": "709", 360 | "label": "開播福利" 361 | }, 362 | { 363 | "key": "710", 364 | "label": "我的直播" 365 | } 366 | ], 367 | "desc": "白色版本APP不存在此選項" 368 | }, 369 | { 370 | "id": "@BiliBili.Enhanced.Settings.Mine.Recommend", 371 | "name": "推薦服務", 372 | "val": [ 373 | "400", 374 | "402", 375 | "403", 376 | "404" 377 | ], 378 | "type": "checkboxes", 379 | "items": [ 380 | { 381 | "key": "400", 382 | "label": "我的課程" 383 | }, 384 | { 385 | "key": "401", 386 | "label": "看視頻免流量" 387 | }, 388 | { 389 | "key": "402", 390 | "label": "個性裝扮" 391 | }, 392 | { 393 | "key": "403", 394 | "label": "遊戲中心" 395 | }, 396 | { 397 | "key": "404", 398 | "label": "我的錢包" 399 | }, 400 | { 401 | "key": "406", 402 | "label": "直播中心" 403 | }, 404 | { 405 | "key": "423", 406 | "label": "邀好友賺紅包" 407 | }, 408 | { 409 | "key": "514", 410 | "label": "社區中心" 411 | }, 412 | { 413 | "key": "544", 414 | "label": "創作中心" 415 | }, 416 | { 417 | "key": "622", 418 | "label": "會員購中心" 419 | }, 420 | { 421 | "key": "924", 422 | "label": "嗶哩嗶哩公益" 423 | }, 424 | { 425 | "key": "990", 426 | "label": "能量加油站" 427 | } 428 | ], 429 | "desc": "白色版本APP不存在此選項" 430 | }, 431 | { 432 | "id": "@BiliBili.Enhanced.Settings.Mine.More", 433 | "name": "更多服務", 434 | "val": [ 435 | "407", 436 | "410", 437 | "1028" 438 | ], 439 | "type": "checkboxes", 440 | "items": [ 441 | { 442 | "key": "407", 443 | "label": "聯繫客服" 444 | }, 445 | { 446 | "key": "410", 447 | "label": "設置" 448 | }, 449 | { 450 | "key": "741", 451 | "label": "我的錢包(白色版)" 452 | }, 453 | { 454 | "key": "742", 455 | "label": "稿件管理(白色版)" 456 | }, 457 | { 458 | "key": "812", 459 | "label": "聽視頻" 460 | }, 461 | { 462 | "key": "950", 463 | "label": "青少年模式(概念版)" 464 | }, 465 | { 466 | "key": "964", 467 | "label": "青少年守護" 468 | }, 469 | { 470 | "key": "1028", 471 | "label": "我的NFT" 472 | } 473 | ] 474 | } 475 | ], 476 | "author": "@BiliUniverse", 477 | "repo": "https://github.com/BiliUniverse/Enhanced/tree/beta", 478 | "icons": [ 479 | "https://github.com/BiliUniverse/Enhanced/raw/main/database/icon_circled_108x.png", 480 | "https://github.com/BiliUniverse/Enhanced/raw/main/database/icon_circled_108x.png" 481 | ] 482 | }, 483 | { 484 | "id": "BiliBili.Enhanced.Region", 485 | "name": "📺 BiliBili: ⚙ Enhanced (分區)", 486 | "descs_html": [ 487 | "填寫完成後記得點擊此頁面底端右下角的\"保存\"。" 488 | ], 489 | "keys": [ 490 | "@BiliBili.Enhanced.Settings.Region", 491 | "@BiliBili.Enhanced.Caches" 492 | ], 493 | "settings": [ 494 | { 495 | "id": "@BiliBili.Enhanced.Settings.Switch", 496 | "name": "總功能開關", 497 | "val": true, 498 | "type": "boolean", 499 | "desc": "是否啓用此APP修改" 500 | }, 501 | { 502 | "id": "@BiliBili.Enhanced.Settings.Region", 503 | "name": "分區", 504 | "val": [ 505 | "1", 506 | "3", 507 | "4", 508 | "5", 509 | "11", 510 | "13", 511 | "23", 512 | "36", 513 | "119", 514 | "129", 515 | "155", 516 | "160", 517 | "167", 518 | "177", 519 | "181", 520 | "188", 521 | "202", 522 | "211", 523 | "217", 524 | "223", 525 | "234", 526 | "6544", 527 | "65537", 528 | "65539", 529 | "65541", 530 | "65545", 531 | "65549", 532 | "65550", 533 | "65551", 534 | "65552", 535 | "65553", 536 | "65555", 537 | "65556", 538 | "65557", 539 | "65559", 540 | "65560", 541 | "65561", 542 | "65563", 543 | "95636", 544 | "168312" 545 | ], 546 | "type": "checkboxes", 547 | "items": [ 548 | { 549 | "key": "1", 550 | "label": "動畫" 551 | }, 552 | { 553 | "key": "3", 554 | "label": "音樂" 555 | }, 556 | { 557 | "key": "4", 558 | "label": "遊戲" 559 | }, 560 | { 561 | "key": "5", 562 | "label": "娛樂" 563 | }, 564 | { 565 | "key": "11", 566 | "label": "電視劇" 567 | }, 568 | { 569 | "key": "13", 570 | "label": "番劇" 571 | }, 572 | { 573 | "key": "23", 574 | "label": "電影" 575 | }, 576 | { 577 | "key": "36", 578 | "label": "知識" 579 | }, 580 | { 581 | "key": "119", 582 | "label": "鬼畜" 583 | }, 584 | { 585 | "key": "129", 586 | "label": "舞蹈" 587 | }, 588 | { 589 | "key": "155", 590 | "label": "時尚" 591 | }, 592 | { 593 | "key": "160", 594 | "label": "生活" 595 | }, 596 | { 597 | "key": "167", 598 | "label": "國創" 599 | }, 600 | { 601 | "key": "177", 602 | "label": "紀錄片" 603 | }, 604 | { 605 | "key": "181", 606 | "label": "影視" 607 | }, 608 | { 609 | "key": "188", 610 | "label": "科技" 611 | }, 612 | { 613 | "key": "202", 614 | "label": "資訊" 615 | }, 616 | { 617 | "key": "211", 618 | "label": "美食" 619 | }, 620 | { 621 | "key": "217", 622 | "label": "動物圈" 623 | }, 624 | { 625 | "key": "223", 626 | "label": "汽車" 627 | }, 628 | { 629 | "key": "234", 630 | "label": "運動" 631 | }, 632 | { 633 | "key": "6544", 634 | "label": "番劇(港澳台)" 635 | }, 636 | { 637 | "key": "65537", 638 | "label": "直播" 639 | }, 640 | { 641 | "key": "65539", 642 | "label": "遊戲中心" 643 | }, 644 | { 645 | "key": "65541", 646 | "label": "專欄" 647 | }, 648 | { 649 | "key": "65545", 650 | "label": "放映廳" 651 | }, 652 | { 653 | "key": "65549", 654 | "label": "工坊集市" 655 | }, 656 | { 657 | "key": "65550", 658 | "label": "遊戲賽事" 659 | }, 660 | { 661 | "key": "65551", 662 | "label": "小黑屋" 663 | }, 664 | { 665 | "key": "65552", 666 | "label": "全區排行榜" 667 | }, 668 | { 669 | "key": "65553", 670 | "label": "活動中心" 671 | }, 672 | { 673 | "key": "65555", 674 | "label": "漫畫" 675 | }, 676 | { 677 | "key": "65556", 678 | "label": "原創排行榜" 679 | }, 680 | { 681 | "key": "65557", 682 | "label": "公開課" 683 | }, 684 | { 685 | "key": "65559", 686 | "label": "VLOG" 687 | }, 688 | { 689 | "key": "65560", 690 | "label": "課堂" 691 | }, 692 | { 693 | "key": "65561", 694 | "label": "專題中心" 695 | }, 696 | { 697 | "key": "65563", 698 | "label": "新歌熱榜" 699 | }, 700 | { 701 | "key": "95636", 702 | "label": "韓綜(港澳台)" 703 | }, 704 | { 705 | "key": "168312", 706 | "label": "節目(港澳台)" 707 | } 708 | ], 709 | "desc": "選擇要顯示的分區" 710 | } 711 | ], 712 | "author": "@BiliUniverse", 713 | "repo": "https://github.com/BiliUniverse/Enhanced", 714 | "icons": [ 715 | "https://github.com/BiliUniverse/Enhanced/raw/main/database/icon_circled_108x.png", 716 | "https://github.com/BiliUniverse/Enhanced/raw/main/database/icon_circled_108x.png" 717 | ] 718 | }, 719 | { 720 | "id": "BiliBili.Global", 721 | "name": "📺 BiliBili: 🌐 Global", 722 | "descs_html": [ 723 | "填寫完成後記得點擊此頁面底端右下角的\"保存\"。" 724 | ], 725 | "keys": [ 726 | "@BiliBili.Global.Settings", 727 | "@BiliBili.Global.Caches" 728 | ], 729 | "settings": [ 730 | { 731 | "id": "@BiliBili.Global.Settings.Switch", 732 | "name": "總功能開關", 733 | "val": true, 734 | "type": "boolean", 735 | "desc": "是否啓用此APP修改" 736 | }, 737 | { 738 | "id": "@BiliBili.Global.Settings.ForceHost", 739 | "name": "強制CDN主機名類型", 740 | "val": "1", 741 | "type": "selects", 742 | "desc": "強制設置各類返回結果的CDN主機名類型", 743 | "items": [ 744 | { 745 | "key": "0", 746 | "label": "IP: 返回遠端DNS解析地址(強烈不推薦!嚴重影響域名分流規則與CDN重定向)" 747 | }, 748 | { 749 | "key": "1", 750 | "label": "HTTP: 返回HTTP域名(推薦,免去重定向時MitM操作)" 751 | }, 752 | { 753 | "key": "2", 754 | "label": "HTTPS: 返回HTTPS域名(重定向時需對指定域名啓用MitM)" 755 | } 756 | ] 757 | }, 758 | { 759 | "id": "@BiliBili.Global.Settings.Locales", 760 | "name": "啓用自動識別和分類功能的地區", 761 | "val": [ 762 | "CHN", 763 | "HKG", 764 | "TWN" 765 | ], 766 | "type": "checkboxes", 767 | "items": [ 768 | { 769 | "key": "CHN", 770 | "label": "🇨🇳中國" 771 | }, 772 | { 773 | "key": "HKG", 774 | "label": "🇭🇰香港" 775 | }, 776 | { 777 | "key": "MAC", 778 | "label": "🇲🇴澳門" 779 | }, 780 | { 781 | "key": "TWN", 782 | "label": "🇹🇼台灣" 783 | } 784 | ], 785 | "desc": "選擇啓用此功能的地區" 786 | }, 787 | { 788 | "id": "@BiliBili.Global.Settings.Proxies.CHN", 789 | "name": "[🇨🇳中國]代理或策略組選擇", 790 | "val": "DIRECT", 791 | "type": "text", 792 | "desc": "選擇此地區代理或策略組" 793 | }, 794 | { 795 | "id": "@BiliBili.Global.Settings.Proxies.HKG", 796 | "name": "[🇭🇰香港]代理或策略組選擇", 797 | "val": "🇭🇰香港", 798 | "type": "text", 799 | "desc": "選擇此地區代理或策略組" 800 | }, 801 | { 802 | "id": "@BiliBili.Global.Settings.Proxies.MAC", 803 | "name": "[🇲🇴澳門]代理或策略組選擇", 804 | "val": "🇲🇴澳門", 805 | "type": "text", 806 | "desc": "選擇此地區代理或策略組" 807 | }, 808 | { 809 | "id": "@BiliBili.Global.Settings.Proxies.TWN", 810 | "name": "[🇹🇼台灣]代理或策略組選擇", 811 | "val": "🇹🇼台灣", 812 | "type": "text", 813 | "desc": "選擇此地區代理或策略組" 814 | } 815 | ], 816 | "author": "@BiliUniverse", 817 | "repo": "https://github.com/BiliUniverse/Global", 818 | "icons": [ 819 | "https://github.com/BiliUniverse/Global/raw/main/database/icon_circled_108x.png", 820 | "https://github.com/BiliUniverse/Global/raw/main/database/icon_circled_108x.png" 821 | ] 822 | }, 823 | { 824 | "id": "BiliBili.ADBlock.Detail", 825 | "name": "📺 BiliBili: 🛡️ ADBlock", 826 | "descs_html":[ 827 | "填寫完成後記得點擊此頁面底端右下角的\"保存\"。" 828 | ], 829 | "keys":[ 830 | "@BiliBili.ADBlock.Settings.Detail", 831 | "@BiliBili.ADBlock.Caches" 832 | ], 833 | "settings":[ 834 | { 835 | "id": "@BiliBili.ADBlock.Settings.Switch", 836 | "name": "總功能開關", 837 | "val": true, 838 | "type": "boolean", 839 | "desc": "是否啓用此修改" 840 | }, 841 | { 842 | "id": "@BiliBili.ADBlock.Settings.Detail.splash", 843 | "name": "[開屏]去廣告", 844 | "val": true, 845 | "type": "boolean", 846 | "desc": "是否啓用關閉廣告" 847 | }, 848 | { 849 | "id": "@BiliBili.ADBlock.Settings.Detail.feed", 850 | "name": "[推薦]去廣告", 851 | "val": true, 852 | "type": "boolean", 853 | "desc": "是否啓用關閉廣告" 854 | }, 855 | { 856 | "id": "@BiliBili.ADBlock.Settings.Detail.activity", 857 | "name": "[推薦]去除活動大圖", 858 | "val": false, 859 | "type": "boolean", 860 | "desc": "是否啓用關閉廣告" 861 | }, 862 | { 863 | "id": "@BiliBili.ADBlock.Settings.Detail.story", 864 | "name": "[首頁]去除短影片廣告", 865 | "val": true, 866 | "type": "boolean", 867 | "desc": "是否啓用關閉廣告" 868 | }, 869 | { 870 | "id": "@BiliBili.ADBlock.Settings.Detail.cinema", 871 | "name": "[番劇電影]去除廣告", 872 | "val": true, 873 | "type": "boolean", 874 | "desc": "是否啓用關閉廣告" 875 | }, 876 | { 877 | "id": "@BiliBili.ADBlock.Settings.Detail.view", 878 | "name": "[用戶投稿]去除影片廣告", 879 | "val": true, 880 | "type": "boolean", 881 | "desc": "是否啓用關閉廣告" 882 | }, 883 | { 884 | "id": "@BiliBili.ADBlock.Settings.Detail.search", 885 | "name": "[搜尋]去除廣告", 886 | "val": true, 887 | "type": "boolean", 888 | "desc": "是否啓用關閉廣告" 889 | }, 890 | { 891 | "id": "@BiliBili.ADBlock.Settings.Detail.commandDms", 892 | "name": "[彈幕]去除交互式彈幕", 893 | "val": false, 894 | "type": "boolean", 895 | "desc": "是否啓用關閉廣告" 896 | }, 897 | { 898 | "id": "@BiliBili.ADBlock.Settings.Detail.colorfulDms", 899 | "name": "[彈幕]替換大會員彈幕", 900 | "val": false, 901 | "type": "boolean", 902 | "desc": "是否啓用關閉廣告" 903 | }, 904 | { 905 | "id": "@BiliBili.ADBlock.Settings.Detail.MainList", 906 | "name": "[評論區]去除廣告", 907 | "val": true, 908 | "type": "boolean", 909 | "desc": "是否啓用關閉廣告" 910 | }, 911 | { 912 | "id": "@BiliBili.ADBlock.Settings.Detail.xlive", 913 | "name": "[直播]去除廣告", 914 | "val": true, 915 | "type": "boolean", 916 | "desc": "是否啓用關閉廣告" 917 | }, 918 | { 919 | "id": "@BiliBili.ADBlock.Settings.Detail.Hot_search", 920 | "name": "[搜索]去除熱搜", 921 | "val": true, 922 | "type": "boolean", 923 | "desc": "是否啓用關閉廣告" 924 | }, 925 | { 926 | "id": "@BiliBili.ADBlock.Settings.Detail.Hot_topics", 927 | "name": "[動態]去除熱門話題", 928 | "val": true, 929 | "type": "boolean", 930 | "desc": "是否啓用關閉廣告" 931 | }, 932 | { 933 | "id": "@BiliBili.ADBlock.Settings.Detail.Most_visited", 934 | "name": "[動態]去除最常訪問", 935 | "val": true, 936 | "type": "boolean", 937 | "desc": "是否啓用關閉廣告" 938 | }, 939 | { 940 | "id": "@BiliBili.ADBlock.Settings.Detail.Dynamic_adcard", 941 | "name": "[動態]去除廣告動態", 942 | "val": true, 943 | "type": "boolean", 944 | "desc": "是否啓用關閉廣告" 945 | }, 946 | { 947 | "id": "@BiliBili.ADBlock.Settings.Detail.blockUpLiveList", 948 | "name": "[推薦]移除up主直播推廣", 949 | "type": "text", 950 | "desc": "填寫up主uid,以英文逗號隔開" 951 | } 952 | ], 953 | "author": "@BiliUniverse", 954 | "repo": "https://github.com/BiliUniverse/ADBlock", 955 | "icons":[ 956 | "https://github.com/BiliUniverse/ADBlock/raw/main/database/icon_circled_108x.png", 957 | "https://github.com/BiliUniverse/ADBlock/raw/main/database/icon_circled_108x.png" 958 | ] 959 | } 960 | ] 961 | } -------------------------------------------------------------------------------- /js/BahamutDailyBonus.js: -------------------------------------------------------------------------------- 1 | /************************ 2 | 3 | 巴哈姆特簽到腳本 4 | 包含主站簽到、公會簽到、動畫瘋答題等 5 | 6 | 腳本兼容: Surge、QuantumultX、Loon、Shadowrocket、Node.js 7 | 適配過程: https://nobyda.github.io/2021/07/24/Bahamut_daily_bonus_js_example 8 | BoxJs訂閱: https://raw.githubusercontent.com/NobyDa/Script/master/NobyDa_BoxJs.json 9 | 10 | ************************* 11 | 【 簽到腳本注意事項 】: 12 | ************************* 13 | 14 | 1. 該腳本需要進入BoxJs或腳本內填寫賬號密碼後, 方可使用. 15 | 2. 不建議在凌晨執行,因需要獲取動畫瘋題目答案; 默認配置將在每天的早上8:00執行. 16 | 3. 如需使用Node.js運行該腳本, 則需安裝got、tough-cookie模塊 17 | 18 | ************************* 19 | 【 Surge & Loon 腳本配置 】: 20 | ************************* 21 | 22 | [Script] 23 | cron "0 8 * * *" script-path=https://raw.githubusercontent.com/NobyDa/Script/master/Bahamut/BahamutDailyBonus.js, wake-system=1, timeout=300 24 | 25 | ************************* 26 | 【 QX 1.0.10+ 腳本配置 】 : 27 | ************************* 28 | 29 | [task_local] 30 | 0 8 * * * https://raw.githubusercontent.com/NobyDa/Script/master/Bahamut/BahamutDailyBonus.js, tag=巴哈姆特簽到, img-url=https://raw.githubusercontent.com/NobyDa/mini/master/Color/bahamutGame.png 31 | 32 | ************************/ 33 | 34 | // 以下全局變量中的持久化接口為BoxJs預留, 以便修改 35 | // 把兼容函數定義到$中, 以便統一調用 36 | const $ = new Env('巴哈姆特'); 37 | 38 | // 取得模組arguments 39 | let args = getArgs(); 40 | 41 | // 用戶名 42 | $.uid = $.getdata('@ND_BAHA.ID') || args.uid || 'YourUserName'; 43 | 44 | // 用戶密碼 45 | $.pwd = $.getdata('@ND_BAHA.PW') || args.pwd || 'YourUserPassword'; 46 | 47 | // 兩步驗證Token, 16位數, 未設置請保持默認 48 | $.totp = $.getdata('@ND_BAHA.TOTP') || args.totp || ''; 49 | 50 | // 是否開啓廣告簽到,true/false,默認關閉 (該功能耗時過長) 51 | $.needSignAds = $.getdata('@ND_BAHA.ADS') || args.needSignAds || false; 52 | 53 | // 是否自動簽到公會,true/false,默認開啓 54 | $.needSignGuild = $.getdata('@ND_BAHA.GUILD') || args.needSignGuild || true; 55 | 56 | // 是否自動答題動畫瘋,true/false,默認開啓 (不保證100%答題正確) 57 | $.needAnswer = $.getdata('@ND_BAHA.ANSWER') || args.needAnswer || true; 58 | 59 | //Bark APP 通知推送Key 60 | $.barkKey = ''; 61 | 62 | // 為通知準備的空數組 63 | $.notifyMsg = []; 64 | 65 | (async function() { // 立即運行的匿名異步函數 66 | await BahamutLogin(); // 登錄 67 | await BahamutGuildSign(); //簽到巴哈公會 68 | await BahamutSign(); //簽到巴哈 69 | await BahamutAnswer(); //動畫瘋答題 70 | })().catch((e) => $.notifyMsg.push(e.message || e)) //捕獲登錄函數等拋出的異常, 並把原因添加到全局變量(通知) 71 | .finally(async () => { //finally在catch之後無論有無異常都會執行 72 | if ($.barkKey) { //如果已填寫Bark Key 73 | await BarkNotify($, $.barkKey, $.name, $.notifyMsg.join('\n')); //推送Bark通知 74 | }; 75 | $.msg($.name, ``, $.notifyMsg.join('\n'), { 76 | 'open-url': 'crazyanime://', //動畫瘋url scheme 77 | 'media-url': 'https://cdn.jsdelivr.net/gh/NobyDa/mini@master/Color/bahamutClear.png' //通知圖片 78 | }); //帶上總結推送通知 79 | $.done(); //調用Surge、QX內部特有的函數, 用於退出腳本執行 80 | }); 81 | 82 | async function BahamutLogin(retry = 3, interval = 1000) { //登錄函數,拿到Set-Cookie 83 | 84 | //登錄成功: {"success":true,"userid":"DGIE","nickname":"coco","gold":152769,"gp":0,"avatar":"https:\/\/avatar2.bahamut.com.tw\/avataruserpic\/dgie.png","avatar_s":"https:\/\/avatar2.bahamut.com.tw\/avataruserpic\/dgie_s.png","lv":6} 85 | //賬號錯誤: {"code":0,"message":"查無此人:SDFOUGB"} 86 | //密碼錯誤: {"code":0,"message":"帳號、密碼或驗證碼錯誤!"} 87 | //驗證碼錯誤: {"code":0,"message":"驗證碼錯誤"} 88 | 89 | for (let i = 0; i < retry; i++) { //循環登錄(默認三次) 90 | if (i > 0) { 91 | $.log('', `🔶嘗試第 ${i+1} 次登錄...`); 92 | await $.wait(interval); //延遲一秒 93 | }; 94 | const reqUrl = { 95 | url: 'https://api.gamer.com.tw/mobile_app/user/v3/do_login.php', //登錄接口 96 | headers: { //請求頭 97 | 'Cookie': 'ckAPP_VCODE=6666' //Cookie中的ckAPP_VCODE為必須 98 | }, 99 | //請求體放入用戶名和密碼,並把它uri編碼 100 | body: `uid=${encodeURIComponent($.uid)}&passwd=${encodeURIComponent($.pwd)}&vcode=6666${$.totp?`&twoStepAuth=${TOTP($.totp)}`:``}` 101 | }; 102 | const res = await $.http.post(reqUrl) //使用post請求查詢 (兼容函數實際上返回Promise實例對象,以便後續調用時可以實現順序執行異步函數) 103 | .then(async (resp) => { //請求成功的處理 104 | const body = JSON.parse(resp.body); //解析響應體json為對象 105 | if (body.userid) { //如果成功返回用戶信息 106 | $.BAHARUNE = JSON.stringify(resp.headers).split(/(BAHARUNE=[^;]+)/)[1]; 107 | return `✅巴哈姆特登錄成功`; 108 | } else { //否則登錄失敗 (例如密碼錯誤) 109 | const failMsg = body.error ? body.error.message : null; //判斷簽到失敗原因 110 | throw new Error(`${body.message||failMsg||'原因未知'}`); //帶上原因拋出異常 111 | } 112 | }).catch((err) => `❌登錄失敗\n❌${err.message || err}`); 113 | $.log('', res.message || res); 114 | if (res === `✅巴哈姆特登錄成功`) { 115 | break; //登錄成功則跳出循環 116 | } else if (retry == i + 1) { //如果最後一次重試仍登錄失敗 117 | throw new Error(res.message || res); //拋出錯誤, 被調用該函數時的catch捕獲, 腳本結束. 118 | } 119 | } 120 | } 121 | 122 | function BahamutSign() { //查詢巴哈姆特簽到Token 123 | return $.http.get({ //使用get方法 (Promise實例對象) 查詢簽到Token 124 | url: 'https://www.gamer.com.tw/ajax/get_csrf_token.php', // 查詢Token接口 125 | headers: {} //請求頭, 客戶端將自動設置Cookie字段 126 | }).then(async (resp) => { //網絡請求成功的處理, 實例函數帶有async關鍵字, 表示裡面有異步操作 127 | if (resp.body) { //如果簽到Token獲取成功 128 | $.log('', '✅獲取簽到令牌成功'); //打印日誌 129 | const sign = await StartSignBahamut(resp.body); //帶上Token開始簽到 130 | $.notifyMsg.push(`首頁簽到: 成功, 已連續簽到 ${sign} 天`); //添加到全局變量備用 (通知) 131 | await StartAdsBonus(resp.body.slice(0, 16), 'start'); //執行廣告簽到 132 | } else { //否則拋出異常 133 | throw new Error('獲取簽到令牌失敗'); //帶上原因被下面catch捕獲 134 | } 135 | }) 136 | .catch(err => { 137 | $.notifyMsg.push(`首頁簽到: ${err.message||err}`); //添加到全局變量備用 (通知) 138 | $.log('', `❌巴哈姆特簽到失敗`, `❌${err.message||err}`); 139 | }); // 捕獲異常, 打印日誌 140 | } 141 | 142 | function StartSignBahamut(token) { //巴哈姆特簽到 143 | 144 | //簽到成功: {"data":{"days":1,"dialog":"","prjSigninDays":0}} 145 | //已簽過: {"error":{"code":0,"message":"今天您已經簽到過了喔","status":"","details":[]}} 146 | //未登錄: {"error":{"code":401,"message":"尚未登入","status":"NO_LOGIN","details":[]}} 147 | //令牌過期: {"error":{"code":403,"message":"網頁已過期","status":"CSRF_TOKEN_ERROR","details":[]}} 148 | 149 | return $.http.post({ //使用post方法 (Promise實例對象) 進行簽到 150 | url: 'https://www.gamer.com.tw/ajax/signin.php', //巴哈姆特簽到接口 151 | headers: {}, //請求頭, 客戶端將自動設置Cookie字段 152 | body: `action=1&token=${token}` //請求體帶上查詢到的簽到Token 153 | }) 154 | .then(res => { // 網絡請求成功的處理 155 | const body = JSON.parse(res.body); //解析響應體json為對象 156 | if (body.data) { // 如果簽到成功 (判斷預期響應格式) 157 | $.log('', '✅巴哈姆特簽到成功', `✅已連續簽到 ${body.data.days} 天`); //打印日誌 158 | return body.data.days; //返回簽到天數 159 | } else { //否則簽到失敗 160 | const failMsg = body.error ? body.error.message : null; //判斷簽到失敗原因 161 | throw new Error(failMsg || body.message || '未知'); //帶上原因拋出異常 162 | } 163 | }); //未寫catch,如果簽到失敗或其他錯誤,則被調用該函數時的catch捕獲 164 | } 165 | 166 | function StartAdsBonus(token, type) { 167 | if ($.needSignAds === false || $.needSignAds === 'false') { //如果用戶選擇不簽到廣告 168 | return; //退出廣告簽到函數 169 | } 170 | return $.http.post({ //使用post方法 (Promise實例對象) 進行簽到 171 | url: 'https://api.gamer.com.tw/mobile_app/bahamut/v1/sign_in_ad_' + type + '.php?bahamutCsrfToken=' + token, //雙倍巴幣廣告獎勵接口 172 | headers: { 173 | 'Cookie': `ckBahamutCsrfToken=${token};${$.BAHARUNE}` //前16位簽到Token和重新設置的Cookie 174 | } 175 | }) 176 | .then(async (res) => { //網絡請求成功的處理, 實例函數帶有async關鍵字, 表示裡面有異步操作 177 | const body = JSON.parse(res.body); //解析響應體json為對象 178 | if (body.data && body.data.finished == 0 && type == 'start') { //如果成功激活廣告獎勵 179 | $.log('', '🔶正在執行廣告簽到 (30s)'); //打印日誌 180 | await $.wait(30000); //等待30秒 181 | await StartAdsBonus(token, 'finished'); //領取獎勵函數 182 | } else if (body.data && body.data.finished == 1) { //如果廣告獎勵領取成功 183 | $.log('', '✅領取廣告獎勵成功'); //打印日誌 184 | $.notifyMsg.push('廣告簽到: 成功, 已領取雙倍簽到獎勵'); //添加到全局變量備用 (通知) 185 | } else { 186 | const failMsg = body.error ? body.error.message : null; //判斷簽到失敗原因 187 | throw new Error(failMsg || body.message || '未知'); //帶上原因拋出異常 188 | } 189 | }) 190 | .catch(err => { 191 | $.notifyMsg.push(`廣告簽到: ${err.message||err}`); //添加到全局變量備用 (通知) 192 | $.log('', `❌廣告獎勵簽到失敗`, `❌${err.message||err}`); 193 | }); // 捕獲異常, 打印日誌 194 | } 195 | 196 | function BahamutGuildSign() { //巴哈姆特查詢公會列表 197 | if ($.needSignGuild === false || $.needSignGuild === 'false') { //如果用戶選擇不簽到公會 198 | return; //退出公會簽到函數 199 | } 200 | return $.http.get({ //使用get請求查詢公會列表 (Promise實例對象) 201 | url: 'https://api.gamer.com.tw/ajax/common/topBar.php?type=forum', // 查詢公會列表接口 202 | headers: {} //請求頭, 客戶端將自動設置Cookie字段 203 | }) 204 | .then(async (resp) => { //網絡請求成功的處理, 實例函數帶有async關鍵字, 表示裡面有異步操作 205 | const list = (resp.body.replace(/\n/g, '').match(/guild\.php\?g?sn=\d.+?<\/p>/g) || []) //正則過濾公會列表大致內容 206 | .map(n => { //使用map遍歷每個大致內容 207 | return { //返回包含公會ID和公會名稱的對象 208 | sn: n.split(/guild\.php\?g?sn=(\d+)/)[1], //正則進一步提取公會ID 209 | name: n.split(/

(.+?)<\/p>/)[1] //正則進一步提取公會名稱 210 | } 211 | }); 212 | if (list.length) { //過濾後, 如果包含公會列表 213 | $.log('', `✅獲取公會列表成功`); //打印日誌 214 | //按照公會數量進行併發簽到, map結合Promise.all後可以實現併發簽到並且都完成後才進行下一行操作 215 | const sign = await Promise.all(list.map(StartSignGuild)); 216 | const sucs = sign.filter(n => n === 1).length; //過濾後得到成功數量 217 | const fail = sign.filter(n => n === 0).length; //過濾後得到失敗數量 218 | //添加到全局變量備用 (通知) 219 | $.notifyMsg.push(`公會簽到: ${sucs?`成功${sucs}個`:``}${sucs&&fail?`, `:``}${fail?`失敗${fail}個`:``}`); 220 | } else { 221 | throw new Error('公會列表為空'); //無公會列表則拋出異常 222 | } 223 | }) 224 | .catch(err => { //捕獲異常, 打印日誌 225 | $.notifyMsg.push(`公會簽到: ${err.message || err}`); //添加到全局變量備用 (通知) 226 | $.log('', `❌巴哈姆特公會簽到失敗`, `❌${err.message || err}`); //打印日誌 227 | }); 228 | } 229 | 230 | function StartSignGuild(v) { //巴哈姆特公會簽到 231 | 232 | //簽到成功: {"ok":1,"msg":"本日簽到成功!獲得5貢獻度"} 233 | //已簽過: {"error":1,"msg":"您今天已經簽到過了!"} 234 | //公會ID錯誤: {"error":1,"msg":"此公會社團不存在。"} 235 | //未加入公會: {"error":1,"msg":"你還不是成員,歡迎加入!"} 236 | //未登錄: {"error":1,"msg":"請先登入"} 237 | 238 | return $.http.post({ //使用post方法簽到公會 (Promise實例對象) 239 | url: 'https://guild.gamer.com.tw/ajax/guildSign.php', //公會簽到接口 240 | headers: {}, //請求頭, 客戶端將自動設置Cookie字段 241 | body: `sn=${v.sn}` //把查詢到的公會ID放進請求體 242 | }) 243 | .then((res) => { //網絡請求成功後的處理 244 | const body = JSON.parse(res.body); //解析響應體json為對象 245 | $.log('', `🔷<${v.name}>`, `${body.ok?`✅`:`❌`}${body.msg}`); //打印日誌, 包含簽到結果 246 | if (body.ok) { //如果簽到成功 247 | return 1; //返回1表示成功 248 | } else { 249 | return 0; //返回0表示失敗 250 | } 251 | }) 252 | .catch(e => { //捕獲異常, 打印日誌 253 | $.log('', `🔷<${v.name}>`, `❌簽到失敗: ${e.message||e}`); 254 | return 0; //返回0表示失敗 255 | }); 256 | } 257 | 258 | function BahamutAnswer() { //動畫瘋答題 259 | 260 | //未答題: {"game":"灌籃高手","question":"流川楓的號碼是下列何者?","a1":"7","a2":"11","a3":"23","a4":"59","userid":"GN32964174","token":"01092fe463ab36ab47cb298e229c4f8fb298e229cc260fa7baf"} 261 | //已答題: {"error":1,"msg":"今日已經答過題目了,一天僅限一次機會"} 262 | //未登錄: {"error":1,"nologin":1,"msg":"請先登入"} 263 | 264 | if ($.needAnswer === false || $.needAnswer === 'false') { //如果用戶關閉動畫瘋答題 265 | return; //退出答題函數 266 | } 267 | return $.http.get({ //使用get方獲取題目 (Promise實例對象) 268 | url: 'https://ani.gamer.com.tw/ajax/animeGetQuestion.php?t=' + Date.now(), //獲取題目接口 269 | headers: {} //請求頭, 客戶端將自動設置Cookie字段 270 | }) 271 | .then(async (res) => { //網絡請求成功的處理, 實例函數帶有async關鍵字, 表示裡面有異步操作 272 | const r = JSON.parse(res.body); //解析響應體json為對象 273 | if (r.token) { //如果有題目 274 | $.log('', `✅獲取動畫瘋題目成功`, ``, `🔶<${r.game}> ${r.question}`, 275 | `1️⃣${r.a1}`, `2️⃣${r.a2}`, `3️⃣${r.a3}`, `4️⃣${r.a4}`); //打印日誌 276 | const article = await GetAanswerArticles(); //獲取答案文章ID 277 | const getAnswer = await StartSearchAnswers(article); //傳入文章ID, 再從文章內獲取答案 278 | const sendAnswer = await StartBahamutAnswer(getAnswer, r.token); //傳入答案和題目令牌, 開始答題 279 | $.notifyMsg.push(`動畫答題: ${sendAnswer}`); //答題後的結果添加到全局變量備用 (通知) 280 | } else { //未獲取到題目 281 | throw new Error(r.msg || `獲取題目失敗`); //帶上原因拋出異常 282 | } 283 | }) 284 | .catch(e => { //捕獲異常, 打印日誌 285 | $.notifyMsg.push(`動畫答題: ${e.message||e||`失敗`}`); //添加到全局變量備用 (通知) 286 | $.log('', `❌動畫瘋答題失敗`, `❌${e.message||e}`); //打印日誌 287 | }); 288 | } 289 | 290 | function GetAanswerArticles() { // 從blackxblue的小屋查詢含答案的文章ID 291 | $.log('', `🔶開始獲取文章`); //打印日誌 292 | return $.http.get({ //使用get方法獲取文章ID (Promise實例對象) 293 | url: 'https://api.gamer.com.tw/mobile_app/bahamut/v1/home.php?owner=blackXblue&page=1', //獲取文章ID接口 294 | headers: {} 295 | }) 296 | .then((res) => { //網絡請求成功後的處理 297 | const body = JSON.parse(res.body); //解析響應體json為對象 298 | const tDate = $.time('MM/dd'); //返回今日日期 299 | const title = (body.creation || []).filter(t => t.title.includes(tDate)); //過濾後返回今日答案文章 300 | if (title.length && title[0].sn) { //如果有答案文章 301 | $.log('', `✅獲取文章成功 (${title[0].sn})`); //打印日誌 302 | return title[0].sn; //返回文章ID 303 | } else { //否則帶上原因拋出異常, 被調用該函數時的catch捕獲 304 | throw new Error('今日答案未發表'); 305 | } 306 | }) 307 | } 308 | 309 | function StartSearchAnswers(id) { //獲取文章內答案 310 | $.log('', `🔶開始獲取答案`); //打印日誌 311 | return $.http.get({ //使用get方法獲取答案 (Promise實例對象) 312 | url: 'https://api.gamer.com.tw/mobile_app/bahamut/v1/home_creation_detail.php?sn=' + id, //獲取答案接口 313 | headers: {} 314 | }) 315 | .then((res) => { //網絡請求成功後的處理 316 | const body = JSON.parse(res.body); //解析響應體json為對象 317 | const answers = body.content.split(/A[:;:](\d)/)[1]; //正則提取答案 318 | if (answers) { //如果成功提取答案 319 | $.log('', `✅獲取答案成功 (${answers})`); //打印日誌 320 | return answers; //返回答案 321 | } else { //否則帶上原因拋出異常, 被調用該函數時的catch捕獲 322 | throw new Error('提取答案失敗'); 323 | } 324 | }) 325 | } 326 | 327 | function StartBahamutAnswer(answer, token) { //動畫瘋答題 328 | 329 | //答題正確: {"ok":1,"gift":"恭喜您得到:300 巴幣"} 330 | //答題錯誤: {"error":1,"msg":"答題錯誤"} 331 | //令牌過期: {"error":1,"msg":"很抱歉!本題目已超過時效!"} 332 | //已答題: {"error":1,"msg":"今日已經答過題目了,一天僅限一次機會"} 333 | //未登錄: {"error":1,"nologin":1,"msg":"請先登入"} 334 | 335 | $.log('', `🔶開始答題`); //打印日誌 336 | return $.http.post({ //使用post方法提交答案 (Promise實例對象) 337 | url: 'https://ani.gamer.com.tw/ajax/animeAnsQuestion.php', //提交答案接口 338 | headers: {}, //請求頭, 客戶端將自動設置Cookie字段 339 | body: `token=${token}&ans=${answer}&t=${Date.now()}`, //請求體帶上答案和答案令牌 340 | }) 341 | .then((res) => { //網絡請求成功後的處理 342 | const body = JSON.parse(res.body); //解析響應體json為對象 343 | if (body.ok) { //如果答題成功 344 | $.log('', `✅${body.gift}`); //打印獎勵日誌 345 | return body.gift; //返回獎勵內容 346 | } else { //否則答題失敗 347 | const failMsg = body.error ? body.error.message : null; //提取簽到失敗原因 348 | throw new Error(body.msg || failMsg || '未知'); //否則帶上原因拋出異常, 被調用該函數時的catch捕獲 349 | } 350 | }) 351 | } 352 | 353 | function getArgs() { 354 | if (typeof $argument === 'undefined') { 355 | return ''; 356 | } 357 | return Object.fromEntries( 358 | $argument 359 | .split("&") 360 | .map((item) => item.split("=")) 361 | .map(([k, v]) => [k, v]) 362 | ); 363 | } 364 | 365 | //Bark APP notify 366 | async function BarkNotify(c,k,t,b){for(let i=0;i<3;i++){console.log(`🔷Bark notify >> Start push (${i+1})`);const s=await new Promise((n)=>{c.post({url:'https://api.day.app/push',headers:{'Content-Type':'application/json'},body:JSON.stringify({title:t,body:b,device_key:k,ext_params:{group:t}})},(e,r,d)=>r&&r.status==200?n(1):n(d||e))});if(s===1){console.log('✅Push success!');break}else{console.log(`❌Push failed! >> ${s.message||s}`)}}}; 367 | 368 | //修改自 https://github.com/chavyleung/scripts/blob/master/Env.js 的兼容函數 369 | function Env(t,e){class s{constructor(t){this.env=t}send(t,e="GET"){t="string"==typeof t?{url:t}:t;let s=this.get;return"POST"===e&&(s=this.post),new Promise((e,i)=>{s.call(this,t,(t,s,r)=>{t?i(t):e(s)})})}get(t){return this.send.call(this.env,t)}post(t){return this.send.call(this.env,t,"POST")}}return new class{constructor(t,e){this.name=t,this.http=new s(this),this.data=null,this.dataFile="box.dat",this.ua="Anime/2.13.9 (tw.com.gamer.anime;build:437;iOS 14.5.0) Alamofire/5.4.1",this.logs=[],this.isMute=!1,this.isNeedRewrite=!0,this.logSeparator="\n",this.startTime=(new Date).getTime(),Object.assign(this,e),this.log("",`\ud83d\udd14${this.name}`)}isNode(){return"undefined"!=typeof module&&!!module.exports}isQuanX(){return"undefined"!=typeof $task}isSurge(){return"undefined"!=typeof $httpClient&&"undefined"==typeof $loon}isLoon(){return"undefined"!=typeof $loon}isShadowrocket(){return"undefined"!=typeof $rocket}toObj(t,e=null){try{return JSON.parse(t)}catch{return e}}toStr(t,e=null){try{return JSON.stringify(t)}catch{return e}}getjson(t,e){let s=e;const i=this.getdata(t);if(i)try{s=JSON.parse(this.getdata(t))}catch{}return s}setjson(t,e){try{return this.setdata(JSON.stringify(t),e)}catch{return!1}}getScript(t){return new Promise(e=>{this.get({url:t},(t,s,i)=>e(i))})}runScript(t,e){return new Promise(s=>{let i=this.getdata("@chavy_boxjs_userCfgs.httpapi");i=i?i.replace(/\n/g,"").trim():i;let r=this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout");r=r?1*r:20,r=e&&e.timeout?e.timeout:r;const[o,h]=i.split("@"),a={url:`http:\/\/${h}/v1/scripting/evaluate`,body:{script_text:t,mock_type:"cron",timeout:r},headers:{"X-Key":o,Accept:"*/*"}};this.post(a,(t,e,i)=>s(i))}).catch(t=>this.logErr(t))}loaddata(){if(!this.isNode())return{};{this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const t=this.path.resolve(this.dataFile),e=this.path.resolve(process.cwd(),this.dataFile),s=this.fs.existsSync(t),i=!s&&this.fs.existsSync(e);if(!s&&!i)return{};{const i=s?t:e;try{return JSON.parse(this.fs.readFileSync(i))}catch(t){return{}}}}}writedata(){if(this.isNode()){this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const t=this.path.resolve(this.dataFile),e=this.path.resolve(process.cwd(),this.dataFile),s=this.fs.existsSync(t),i=!s&&this.fs.existsSync(e),r=JSON.stringify(this.data);s?this.fs.writeFileSync(t,r):i?this.fs.writeFileSync(e,r):this.fs.writeFileSync(t,r)}}lodash_get(t,e,s){const i=e.replace(/\[(\d+)\]/g,".$1").split(".");let r=t;for(const t of i)if(r=Object(r)[t],void 0===r)return s;return r}lodash_set(t,e,s){return Object(t)!==t?t:(Array.isArray(e)||(e=e.toString().match(/[^.[\]]+/g)||[]),e.slice(0,-1).reduce((t,s,i)=>Object(t[s])===t[s]?t[s]:t[s]=Math.abs(e[i+1])>>0==+e[i+1]?[]:{},t)[e[e.length-1]]=s,t)}getdata(t){let e=this.getval(t);if(/^@/.test(t)){const[,s,i]=/^@(.*?)\.(.*?)$/.exec(t),r=s?this.getval(s):"";if(r)try{const t=JSON.parse(r);e=t?this.lodash_get(t,i,""):e}catch(t){e=""}}return e}setdata(t,e){let s=!1;if(/^@/.test(e)){const[,i,r]=/^@(.*?)\.(.*?)$/.exec(e),o=this.getval(i),h=i?"null"===o?null:o||"{}":"{}";try{const e=JSON.parse(h);this.lodash_set(e,r,t),s=this.setval(JSON.stringify(e),i)}catch(e){const o={};this.lodash_set(o,r,t),s=this.setval(JSON.stringify(o),i)}}else s=this.setval(t,e);return s}getval(t){return this.isSurge()||this.isLoon()?$persistentStore.read(t):this.isQuanX()?$prefs.valueForKey(t):this.isNode()?(this.data=this.loaddata(),this.data[t]):this.data&&this.data[t]||null}setval(t,e){return this.isSurge()||this.isLoon()?$persistentStore.write(t,e):this.isQuanX()?$prefs.setValueForKey(t,e):this.isNode()?(this.data=this.loaddata(),this.data[e]=t,this.writedata(),!0):this.data&&this.data[e]||null}initGotEnv(t){this.got=this.got?this.got:require("got"),this.cktough=this.cktough?this.cktough:require("tough-cookie"),this.ckjar=this.ckjar?this.ckjar:new this.cktough.CookieJar,t&&(t.headers=t.headers?t.headers:{},((void 0===t.headers.Cookie||/ckAPP_VCODE/.test(t.headers.Cookie))&&void 0===t.cookieJar)&&(t.cookieJar=this.ckjar))}get(t,e=(()=>{})){t.headers&&(t.headers["User-Agent"]=this.ua,delete t.headers["Content-Type"],delete t.headers["Content-Length"]),this.isSurge()||this.isLoon()?(this.isSurge()&&this.isNeedRewrite&&(t.headers=t.headers||{},Object.assign(t.headers,{"X-Surge-Skip-Scripting":!1})),$httpClient.get(t,(t,s,i)=>{!t&&s&&(s.body=i,s.statusCode=s.status),e(t,s,i)})):this.isQuanX()?(this.isNeedRewrite&&(t.opts=t.opts||{},Object.assign(t.opts,{hints:!1})),$task.fetch(t).then(t=>{const{statusCode:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>e(t))):this.isNode()&&(this.initGotEnv(t),this.got(t).on("redirect",(t,e)=>{try{if(t.headers["set-cookie"]){const s=t.headers["set-cookie"].map(this.cktough.Cookie.parse).toString();s&&this.ckjar.setCookieSync(s,null),e.cookieJar=this.ckjar}}catch(t){this.logErr(t)}}).then(t=>{const{statusCode:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>{const{message:s,response:i}=t;e(s,i,i&&i.body)}))}post(t,e=(()=>{})){const s=t.method?t.method.toLocaleLowerCase():"post";if(t.body&&t.headers&&!t.headers["Content-Type"]&&(t.headers["Content-Type"]="application/x-www-form-urlencoded"),t.headers&&(t.headers["User-Agent"]=this.ua,delete t.headers["Content-Length"]),this.isSurge()||this.isLoon())this.isSurge()&&this.isNeedRewrite&&(t.headers=t.headers||{},Object.assign(t.headers,{"X-Surge-Skip-Scripting":!1})),$httpClient[s](t,(t,s,i)=>{!t&&s&&(s.body=i,s.statusCode=s.status),e(t,s,i)});else if(this.isQuanX())t.method=s,this.isNeedRewrite&&(t.opts=t.opts||{},Object.assign(t.opts,{hints:!1})),$task.fetch(t).then(t=>{const{statusCode:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>e(t));else if(this.isNode()){this.initGotEnv(t);const{url:i,...r}=t;this.got[s](i,r).then(t=>{const{statusCode:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>{const{message:s,response:i}=t;e(s,i,i&&i.body)})}}time(t,e=null){const s=e?new Date(e):new Date;let i={"M+":s.getMonth()+1,"d+":s.getDate(),"H+":s.getHours(),"m+":s.getMinutes(),"s+":s.getSeconds(),"q+":Math.floor((s.getMonth()+3)/3),S:s.getMilliseconds()};/(y+)/.test(t)&&(t=t.replace(RegExp.$1,(s.getFullYear()+"").substr(4-RegExp.$1.length)));for(let e in i)new RegExp("("+e+")").test(t)&&(t=t.replace(RegExp.$1,1==RegExp.$1.length?i[e]:("00"+i[e]).substr((""+i[e]).length)));return t}msg(e=t,s="",i="",r){const o=t=>{if(!t)return t;if("string"==typeof t)return this.isLoon()?t:this.isQuanX()?{"open-url":t}:this.isSurge()?{url:t}:void 0;if("object"==typeof t){if(this.isLoon()){let e=t.openUrl||t.url||t["open-url"],s=t.mediaUrl||t["media-url"];return{openUrl:e,mediaUrl:s}}if(this.isQuanX()){let e=t["open-url"]||t.url||t.openUrl,s=t["media-url"]||t.mediaUrl;return{"open-url":e,"media-url":s}}if(this.isSurge()){let e=t.url||t.openUrl||t["open-url"];return{url:e}}}};if(this.isMute||(this.isSurge()||this.isLoon()?$notification.post(e,s,i,o(r)):this.isQuanX()&&$notify(e,s,i,o(r))),!this.isMuteLog){let t=["","================================="];t.push(e),s&&t.push(s),i&&t.push(i),console.log(t.join("\n")),this.logs=this.logs.concat(t)}}log(...t){t.length>0&&(this.logs=[...this.logs,...t]),console.log(t.join(this.logSeparator))}logErr(t,e){const s=!this.isSurge()&&!this.isQuanX()&&!this.isLoon();s?this.log("",`\u2757\ufe0f${this.name},\u9519\u8bef!`,t.stack):this.log("",`\u2757\ufe0f${this.name},\u9519\u8bef!`,t)}wait(t){return new Promise(e=>setTimeout(e,t))}done(t={}){const e=(new Date).getTime(),s=(e-this.startTime)/1e3;this.log("",`${s}\u79d2`,`=================================`),(this.isSurge()||this.isQuanX()||this.isLoon())&&$done(t)}}(t,e)}; 370 | 371 | // 從 https://jsfiddle.net/russau/rbyjk774 魔改的TOTP兩部驗證算法, 完全使用原生javascript實現 372 | function TOTP(token){function t(e,a,d){var g=0,c=[],b=0,f,k,l,h,m,w,n,y,p=!1,q=[],t=[],v,u=!1;d=d||{};f=d.encoding||"UTF8";v=d.numRounds||1;l=z(a,f);if(v!==parseInt(v,10)||1>v)throw Error("numRounds must a integer >= 1");if("SHA-1"===e)m=512,w=A,n=H,h=160,y=function(a){return a.slice()};else throw Error("Chosen SHA variant is not supported");k=x(e);this.setHMACKey=function(a,b,c){var d;if(!0===p)throw Error("HMAC key already set");if(!0===u)throw Error("Cannot set HMAC key after calling update");f=(c||{}).encoding||"UTF8";b=z(b,f)(a);a=b.binLen;b=b.value;d=m>>>3;c=d/4-1;if(da/8){for(;b.length<=c;)b.push(0);b[c]&=4294967040}for(a=0;a<=c;a+=1)q[a]=b[a]^909522486,t[a]=b[a]^1549556828;k=w(q,k);g=m;p=!0};this.update=function(a){var d,e,f,h=0,n=m>>>5;d=l(a,c,b);a=d.binLen;e=d.value;d=a>>>5;for(f=0;f>>5);b=a%m;u=!0};this.getHash=function(a,d){var f,l,m,r;if(!0===p)throw Error("Cannot call getHash after setting HMAC key");m=B(d);switch(a){case"HEX":f=function(a){return C(a,h,m)};break;case"B64":f=function(a){return D(a,h,m)};break;case"BYTES":f=function(a){return E(a,h)};break;case"ARRAYBUFFER":try{l=new ArrayBuffer(0)}catch(I){throw Error("ARRAYBUFFER not supported by this environment");}f=function(a){return F(a,h)};break;default:throw Error("format must be HEX, B64, BYTES, or ARRAYBUFFER");}r=n(c.slice(),b,g,y(k),h);for(l=1;l>>3;if(0!==g%2)throw Error("String of HEX type must be in byte increments");for(c=0;c>>1)+l;for(f=k>>>2;a.length<=f;)a.push(0);a[f]|=b<<8*(3-k%4)}return{value:a,binLen:4*g+d}}function K(e,a,d){var g=[],c,b,f,k,g=a||[0];d=d||0;b=d>>>3;for(c=0;c>>2,g.length<=f&&g.push(0),g[f]|=a<<8*(3-k%4);return{value:g,binLen:8*e.length+d}}function L(e,a,d){var g=[],c=0,b,f,k,l,h,m,g=a||[0];d=d||0;a=d>>>3;if(-1===e.search(/^[a-zA-Z0-9=+\/]+$/))throw Error("Invalid character in base-64 string");f=e.indexOf("=");e=e.replace(/\=/g,"");if(-1!==f&&f=str.length){str=Array(len+1-str.length).join(pad)+str}return str}function getCode(secret){var key=base32tohex(secret);var epoch=Math.round(new Date().getTime()/1000.0);var time=leftpad(dec2hex(Math.floor(epoch/30)),16,'0');var shaObj=new t("SHA-1","HEX");shaObj.setHMACKey(key,"HEX");shaObj.update(time);var hmac=shaObj.getHMAC("HEX");var offset=hex2dec(hmac.substring(hmac.length-1));var otp=(hex2dec(hmac.substr(offset*2,8))&hex2dec('7fffffff'))+'';otp=(otp).substr(otp.length-6,6);return otp};const res=getCode(token);return res}; -------------------------------------------------------------------------------- /XPTV/spider/archive.js: -------------------------------------------------------------------------------- 1 | function nkvodClass() { 2 | return new (class { 3 | constructor() { 4 | this.url = 'https://nkvod.com' 5 | this.headers = { 6 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', 7 | Referer: this.url, 8 | } 9 | this.ignoreClassName = ['热搜榜', 'APP', '首页'] 10 | } 11 | 12 | async initParseMap() { 13 | const date = new Date() 14 | const t = '' + date.getFullYear() + (date.getMonth() + 1) + date.getDate() 15 | const url = this.url + '/static/js/playerconfig.js?t=' + t 16 | const js = (await $.http.get({ url: url, headers: this.headers })).body 17 | try { 18 | const jsEval = js + '\nMacPlayerConfig' 19 | const playerList = eval(jsEval).player_list 20 | const players = Object.values(playerList) 21 | let parseMap = {} 22 | players.forEach((item) => { 23 | if (!item.ps || item.ps === '0') return 24 | if (!item.parse) return 25 | parseMap[item.show] = item.parse 26 | }) 27 | $.setdata(JSON.stringify(parseMap), 'xptv-sources-nkvod-parseMap') 28 | } catch (e) { 29 | $.logErr(e) 30 | } 31 | } 32 | 33 | async getClassList() { 34 | await this.initParseMap() 35 | let webUrl = this.url 36 | let backData = {} 37 | try { 38 | // const pro = await $.http.get({ url: webUrl, headers: this.headers }) 39 | 40 | // let proData = await pro.body 41 | // if (proData) { 42 | // let _$ = $.cheerio.load(proData) 43 | // let allClass = _$('.navbar .navbar-items .swiper-slide a') 44 | // let list = [] 45 | // allClass.each((index, element) => { 46 | // let isIgnore = this.isIgnoreClassName(_$(element).text()) 47 | // if (isIgnore) { 48 | // return 49 | // } 50 | // let type_name = _$(element).text() 51 | // let url = _$(element).attr('href') || '' 52 | // // url = url.match(/type\/(.*+)\.html/)[1] 53 | 54 | // if (url.length > 0 && type_name.length > 0) { 55 | // let videoClass = {} 56 | // // videoClass.type_id = url 57 | // videoClass.type_id = index 58 | // videoClass.type_name = type_name.trim() 59 | // list.push(videoClass) 60 | // } 61 | // }) 62 | 63 | // let allVideo = _$('.content .module') 64 | // let videos = [] 65 | // allVideo.each((index, element) => { 66 | // let nodes = _$(element).find('.module-items > a') 67 | // nodes.each((index, element) => { 68 | // let vodUrl = _$(element).attr('href') || '' 69 | // let vodPic = _$(element).find('img').attr('data-original') || '' 70 | // let vodName = _$(element).attr('title') || '' 71 | // let vodDiJiJi = _$(element).find('.module-item-note').text() || '' 72 | 73 | // let videoDet = {} 74 | // videoDet.vod_id = +vodUrl.match(/detail\/(.+)\.html/)[1] 75 | // videoDet.vod_pic = vodPic 76 | // videoDet.vod_name = vodName 77 | // videoDet.vod_remarks = vodDiJiJi.trim() 78 | // videos.push(videoDet) 79 | // }) 80 | // }) 81 | 82 | // backData.code = 1 83 | // backData.msg = '數據列表' 84 | // backData.page = 1 85 | // backData.list = videos 86 | // backData.class = list 87 | // } 88 | let list = [ 89 | { type_id: 1, type_name: '電影' }, 90 | { type_id: 2, type_name: '電視劇' }, 91 | { type_id: 3, type_name: '綜藝' }, 92 | { type_id: 4, type_name: '動漫' }, 93 | { type_id: 13, type_name: '國產劇' }, 94 | { type_id: 14, type_name: '港台劇' }, 95 | { type_id: 15, type_name: '日韓劇' }, 96 | { type_id: 16, type_name: '歐美劇' }, 97 | { type_id: 20, type_name: '其他劇' }, 98 | ] 99 | 100 | backData.code = 1 101 | backData.msg = '數據列表' 102 | backData.page = 1 103 | // backData.list = videos 104 | backData.list = [] 105 | backData.class = list 106 | } catch (e) { 107 | $.logErr(e) 108 | backData.error = e.message 109 | } 110 | 111 | return JSON.stringify(backData) 112 | } 113 | 114 | async getVideoList(queryParams) { 115 | let page = queryParams.pg 116 | let type = queryParams.t 117 | 118 | // if (type === '') return this.getClassList() 119 | 120 | let listUrl = this.removeTrailingSlash(this.url) + `/index.php/ajax/data?mid=1&tid=${type}&page=${page}&limit=20` 121 | let backData = {} 122 | try { 123 | let pro = await $.http.get({ url: listUrl, headers: this.headers }) 124 | let proData = pro.body 125 | if (proData) { 126 | // let _$ = $.cheerio.load(proData) 127 | // let allVideo = _$('.content .module > a') 128 | // let lastPage = _$('.pagenavi_txt a[title="尾页"]').attr('href') 129 | // if (lastPage) { 130 | // lastPage = lastPage.match(/\/show\/(.*)--------(.*)---\.html/)[2] 131 | // // console.log('lastpage = ' + lastPage); 132 | // } else { 133 | // lastPage = '1' 134 | // // console.log('lastpage not found, using default value'); 135 | // } 136 | // let videos = [] 137 | // allVideo.each((index, element) => { 138 | // let vodUrl = _$(element).attr('href') || '' 139 | // let vodPic = _$(element).find('img').attr('data-original') || '' 140 | // let vodName = _$(element).attr('title') || '' 141 | // let vodDiJiJi = _$(element).find('.module-item-note').text() || '' 142 | // let videoDet = {} 143 | // videoDet.vod_id = +vodUrl.match(/detail\/(.+)\.html/)[1] 144 | // videoDet.vod_pic = vodPic 145 | // videoDet.vod_name = vodName 146 | // videoDet.vod_remarks = vodDiJiJi.trim() 147 | // videos.push(videoDet) 148 | // }) 149 | // backData.code = 1 150 | // backData.msg = '數據列表' 151 | // backData.page = page.toString() 152 | // backData.pagecount = +lastPage 153 | // backData.limit = videos.length.toString() 154 | // backData.total = videos.length * lastPage 155 | // backData.list = videos 156 | backData = JSON.parse(proData) 157 | } 158 | } catch (e) { 159 | $.logErr('Error fetching list:', e) 160 | backData.error = e.message 161 | } 162 | return JSON.stringify(backData) 163 | } 164 | 165 | async getVideoDetail(queryParams) { 166 | let ids = queryParams.ids 167 | let backData = {} 168 | try { 169 | let webUrl = this.url + `/detail/${ids}.html` 170 | let pro = await $.http.get({ url: webUrl, headers: this.headers }) 171 | let proData = pro.body 172 | if (proData) { 173 | let _$ = $.cheerio.load(proData) 174 | let vod_name = _$('.detail-info h3').text() 175 | let vod_content = _$('.switch-box .text').text() 176 | let vod_pic = _$('.detail-pic img').attr('data-src') 177 | 178 | let from = [] 179 | _$('.anthology .swiper-wrapper a').each((index, element) => { 180 | let name = _$(element) 181 | .contents() 182 | .filter(function () { 183 | return this.type === 'text' 184 | }) 185 | .text() 186 | .trim() 187 | from.push(name) 188 | }) 189 | 190 | let juJiDocment = _$('.anthology-list-box') 191 | // let vod_play_from = ''; 192 | let vod_play_url = '' 193 | juJiDocment.each((index, element) => { 194 | let line = from[index] 195 | let allvideos = _$(element).find('ul.anthology-list-play li a') 196 | allvideos.each((index, element) => { 197 | let playerUrl = this.combineUrl(_$(element).attr('href')) 198 | vod_play_url += line + '-' + _$(element).text() 199 | vod_play_url += '$' 200 | vod_play_url += 201 | 'https://ykusu.ykusu/nkvod/provide/vod?ac=play&url=' + `${from[index]}@@@` + encodeURIComponent(playerUrl) + '&n=.m3u8' 202 | vod_play_url += '#' 203 | }) 204 | vod_play_url += '$$$' 205 | }) 206 | 207 | let temp = { 208 | code: 1, 209 | msg: '数据列表', 210 | page: 1, 211 | pagecount: 1, 212 | limit: '20', 213 | total: 1, 214 | list: [ 215 | { 216 | vod_id: 1, 217 | vod_name: '', 218 | vod_pic: '', 219 | vod_remarks: '', 220 | type_name: '', 221 | vod_year: '', 222 | vod_area: '', 223 | vod_actor: '', 224 | vod_director: '', 225 | vod_content: '', 226 | vod_play_from: '', 227 | vod_play_url: '', 228 | }, 229 | ], 230 | } 231 | temp.list[0].vod_play_url = vod_play_url 232 | temp.list[0].vod_play_from = from.join('$$$') 233 | temp.list[0].vod_play_note = '$$$' 234 | temp.list[0].vod_id = +ids 235 | temp.list[0].vod_name = vod_name 236 | temp.list[0].vod_pic = vod_pic 237 | temp.list[0].vod_content = vod_content.trim() 238 | backData = temp 239 | } 240 | } catch (e) { 241 | backData.error = e.message 242 | } 243 | 244 | return JSON.stringify(backData) 245 | } 246 | 247 | async getVideoPlayUrl(queryParams) { 248 | let backData = {} 249 | let parseMap = JSON.parse($.getdata('xptv-sources-nkvod-parseMap')) 250 | let parts = decodeURIComponent(queryParams.url).split('@@@') 251 | let from = parts[0] 252 | let url = parts[1] 253 | try { 254 | let html = await $.http.get({ url: url, headers: this.headers }) 255 | 256 | let proData = html.body 257 | if (proData) { 258 | let _$ = $.cheerio.load(proData) 259 | const js = JSON.parse(_$('script:contains(player_aaaa)').html().replace('var player_aaaa=', '')) 260 | let playUrl = js.url 261 | if (js.encrypt == 1) { 262 | playUrl = unescape(playUrl) 263 | } else if (js.encrypt == 2) { 264 | playUrl = unescape(base64Decode(playUrl)) 265 | } 266 | if (/\.m3u8$/.test(playUrl)) { 267 | backData.data = playUrl 268 | } else { 269 | const parseUrl = parseMap[from] 270 | if (parseUrl) { 271 | const reqUrl = parseUrl + playUrl 272 | const parseHtml = ( 273 | await $.http.get({ 274 | url: reqUrl, 275 | headers: this.headers, 276 | }) 277 | ).body 278 | const matches = parseHtml.match(/let ConFig = {([\w\W]*)},box/) 279 | if (matches && matches.length > 1) { 280 | const configJson = '{' + matches[1].trim() + '}' 281 | const config = JSON.parse(configJson) 282 | playUrl = this.decryptUrl(config) 283 | } 284 | } 285 | backData.data = playUrl 286 | } 287 | } 288 | } catch (error) { 289 | backData.error = error.message 290 | } 291 | return JSON.stringify(backData) 292 | } 293 | 294 | async searchVideo(queryParams) { 295 | // https://www.nkvod.com/index.php/ajax/suggest?mid=1&wd={wd}&limit=10 296 | const pg = queryParams.pg 297 | const wd = queryParams.wd 298 | let backData = {} 299 | 300 | try { 301 | let searchUrl = this.url + `/index.php/ajax/suggest?mid=1&wd=${wd}&limit=10` 302 | let searchRes = await $.http.get({ 303 | url: searchUrl, 304 | headers: this.headers, 305 | }) 306 | // let _$ = $.cheerio.load(searchRes.body) 307 | // let videos = [] 308 | // let allVideo = _$('.search_list').find('li') 309 | // allVideo.each((index, element) => { 310 | // let vodUrl = _$(element).find('a').attr('href') || '' 311 | // let vodPic = _$(element).find('img.thumb').attr('data-original') || '' 312 | // let vodName = _$(element).find('img.thumb').attr('alt') || '' 313 | // let vodDiJiJi = _$(element).find('.jidi').text() || '' 314 | 315 | // let videoDet = {} 316 | // videoDet.vod_id = +vodUrl.match(/movie\/(.+)\.html/)[1] 317 | // videoDet.vod_pic = vodPic 318 | // videoDet.vod_name = vodName 319 | // videoDet.vod_remarks = vodDiJiJi.trim() 320 | // videos.push(videoDet) 321 | // }) 322 | 323 | // backData.code = 1 324 | // backData.msg = '數據列表' 325 | // backData.page = pg 326 | // // backData.pagecount = +lastPage 327 | // backData.limit = videos.length.toString() 328 | // // backData.total = videos.length * lastPage 329 | // backData.list = videos 330 | backData = JSON.stringify(searchRes.body) 331 | } catch (e) { 332 | backData.error = e.message 333 | } 334 | 335 | return JSON.stringify(backData) 336 | } 337 | 338 | decryptUrl(jsConfig) { 339 | const key = CryptoJS.enc.Utf8.parse('2890' + jsConfig.config.uid + 'tB959C') 340 | const iv = CryptoJS.enc.Utf8.parse('GZ4JgN2BdSqVWJ1z') 341 | const mode = CryptoJS.mode.CBC 342 | const padding = CryptoJS.pad.Pkcs7 343 | const decrypted = CryptoJS.AES.decrypt(jsConfig.url, key, { 344 | iv: iv, 345 | mode: mode, 346 | padding: padding, 347 | }) 348 | const decryptedUrl = CryptoJS.enc.Utf8.stringify(decrypted) 349 | return decryptedUrl 350 | } 351 | 352 | combineUrl(url) { 353 | if (url === undefined) { 354 | return '' 355 | } 356 | if (url.indexOf(this.url) !== -1) { 357 | return url 358 | } 359 | if (url.startsWith('/')) { 360 | return this.url + url 361 | } 362 | return this.url + '/' + url 363 | } 364 | 365 | isIgnoreClassName(className) { 366 | for (let index = 0; index < this.ignoreClassName.length; index++) { 367 | const element = this.ignoreClassName[index] 368 | if (className.indexOf(element) !== -1) { 369 | return true 370 | } 371 | } 372 | return false 373 | } 374 | 375 | removeTrailingSlash(str) { 376 | if (str.endsWith('/')) { 377 | return str.slice(0, -1) 378 | } 379 | return str 380 | } 381 | })() 382 | } 383 | 384 | function kmeijuClass() { 385 | return new (class { 386 | constructor() { 387 | this.key = '美劇星球' 388 | this.url = 'https://www.kmeiju.cc' 389 | this.headers = { 390 | 'User-Agent': 391 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1', 392 | } 393 | this.ignoreClassName = ['首页', '求片留言', 'APP不迷路', '备用网站'] 394 | } 395 | 396 | async getClassList() { 397 | let webUrl = this.url 398 | let backData = {} 399 | try { 400 | const pro = await $.http.get({ url: webUrl, headers: this.headers }) 401 | 402 | let proData = await pro.body 403 | if (proData) { 404 | let _$ = $.cheerio.load(proData) 405 | let allClass = _$('ul.navlist a') 406 | let list = [] 407 | allClass.each((index, element) => { 408 | let isIgnore = this.isIgnoreClassName(_$(element).text()) 409 | if (isIgnore) { 410 | return 411 | } 412 | let type_name = _$(element).text() 413 | let url = _$(element).attr('href') || '' 414 | 415 | if (url.length > 0 && type_name.length > 0) { 416 | let videoClass = {} 417 | // videoClass.type_id = url 418 | videoClass.type_id = index 419 | videoClass.type_name = type_name.trim() 420 | list.push(videoClass) 421 | } 422 | }) 423 | 424 | let allVideo = _$('.mi_cont .mi_btcon') 425 | let videos = [] 426 | allVideo.each((index, element) => { 427 | let nodes = _$(element).find('.bt_img ul li') 428 | nodes.each((index, element) => { 429 | let vodUrl = _$(element).find('a').attr('href') || '' 430 | let vodPic = _$(element).find('img').attr('data-original') || '' 431 | let vodName = _$(element).find('img').attr('alt') || '' 432 | let vodDiJiJi = _$(element).find('.jidi').text() || '' 433 | 434 | let videoDet = {} 435 | videoDet.vod_id = +vodUrl.match(/movie\/(.+)\.html/)[1] 436 | videoDet.vod_pic = vodPic 437 | videoDet.vod_name = vodName 438 | videoDet.vod_remarks = vodDiJiJi.trim() 439 | videos.push(videoDet) 440 | }) 441 | }) 442 | 443 | backData.code = 1 444 | backData.msg = '數據列表' 445 | backData.page = 1 446 | backData.list = videos 447 | backData.class = list 448 | } 449 | } catch (e) { 450 | $.logErr(e) 451 | backData.e = e.message 452 | } 453 | 454 | return JSON.stringify(backData) 455 | } 456 | 457 | async getVideoList(queryParams) { 458 | const page = queryParams.pg 459 | const type = queryParams.t 460 | 461 | let realTypeName = '' 462 | if (type === '') return this.getClassList() 463 | switch (type) { 464 | case '1': 465 | realTypeName = '/movie_kmeiju' 466 | break 467 | case '2': 468 | realTypeName = '/tv_kmeiju' 469 | break 470 | case '3': 471 | realTypeName = '/fan_kmeiju' 472 | break 473 | case '4': 474 | realTypeName = '/record' 475 | break 476 | case '5': 477 | realTypeName = '/gaofen' 478 | break 479 | } 480 | 481 | let listUrl = this.removeTrailingSlash(this.url) + realTypeName + '/page/' + page 482 | let backData = {} 483 | try { 484 | let pro = await $.http.get({ url: listUrl, headers: this.headers }) 485 | let proData = pro.body 486 | if (proData) { 487 | let _$ = $.cheerio.load(proData) 488 | let allVideo = _$('.bt_img.mi_ne_kd.mrb li') 489 | let lastPage = _$('.pagenavi_txt a[title="跳转到最后一页"]').attr('href') 490 | if (lastPage) { 491 | let parts = lastPage.split('/') 492 | lastPage = parts[parts.length - 1] 493 | } else { 494 | lastPage = '1' 495 | } 496 | let videos = [] 497 | allVideo.each((index, element) => { 498 | let vodUrl = _$(element).find('a').attr('href') || '' 499 | let vodPic = _$(element).find('img.thumb').attr('data-original') || '' 500 | let vodName = _$(element).find('img.thumb').attr('alt') || '' 501 | let vodDiJiJi = _$(element).find('.jidi span').text() ? _$(element).find('.jidi span').text() : _$(element).find('.hdinfo').text() 502 | 503 | let videoDet = {} 504 | videoDet.vod_id = +vodUrl.match(/movie\/(.+)\.html/)[1] 505 | videoDet.vod_pic = vodPic 506 | videoDet.vod_name = vodName 507 | videoDet.vod_remarks = vodDiJiJi.trim() 508 | videos.push(videoDet) 509 | }) 510 | 511 | backData.code = 1 512 | backData.msg = '數據列表' 513 | backData.page = page.toString() 514 | backData.pagecount = +lastPage 515 | backData.limit = videos.length.toString() 516 | backData.total = videos.length * lastPage 517 | backData.list = videos 518 | } 519 | } catch (e) { 520 | $.logErr('Error fetching list:', e) 521 | backData.error = e.message 522 | } 523 | return JSON.stringify(backData) 524 | } 525 | 526 | async getVideoDetail(queryParams) { 527 | let ids = queryParams.ids 528 | let backData = {} 529 | try { 530 | let webUrl = this.url + `/movie/${ids}.html` 531 | let pro = await $.http.get({ url: webUrl, headers: this.headers }) 532 | let proData = pro.body 533 | if (proData) { 534 | let _$ = $.cheerio.load(proData) 535 | let vod_name = _$('.moviedteail_tt h1').text() 536 | let vod_content = _$('.yp_context p').text() 537 | let vod_pic = _$('.dyimg img').attr('src') 538 | 539 | let juJiDocment = _$('.paly_list_btn').find('a') 540 | // let vod_play_from = ''; 541 | let vod_play_url = '' 542 | juJiDocment.each((index, element) => { 543 | vod_play_url += _$(element).text() 544 | vod_play_url += '$' 545 | vod_play_url += 'https://ykusu.ykusu/kmeiju/provide/vod?ac=play&url=' + encodeURIComponent(_$(element).attr('href')) + '&n=.m3u8' 546 | vod_play_url += '#' 547 | }) 548 | 549 | let temp = { 550 | code: 1, 551 | msg: '数据列表', 552 | page: 1, 553 | pagecount: 1, 554 | limit: '20', 555 | total: 1, 556 | list: [ 557 | { 558 | vod_id: 1, 559 | vod_name: '', 560 | vod_pic: '', 561 | vod_remarks: '', 562 | type_name: '', 563 | vod_year: '', 564 | vod_area: '', 565 | vod_actor: '', 566 | vod_director: '', 567 | vod_content: '', 568 | vod_play_from: '', 569 | vod_play_url: '', 570 | }, 571 | ], 572 | } 573 | temp.list[0].vod_play_url = vod_play_url 574 | // temp.list[0].vod_play_from = play_from.join('$$$') 575 | // temp.list[0].vod_play_note = '$$$' 576 | temp.list[0].vod_id = +ids 577 | temp.list[0].vod_name = vod_name 578 | temp.list[0].vod_pic = vod_pic 579 | temp.list[0].vod_content = vod_content.trim() 580 | backData = temp 581 | } 582 | } catch (e) { 583 | $.logErr(e) 584 | backData.error = e.message 585 | } 586 | 587 | return JSON.stringify(backData) 588 | } 589 | 590 | async getVideoPlayUrl(queryParams) { 591 | let backData = {} 592 | let url = decodeURIComponent(queryParams.url) 593 | try { 594 | let html = await $.http.get({ url: url, headers: this.headers }) 595 | 596 | let proData = html.body 597 | if (proData) { 598 | let _$ = $.cheerio.load(proData) 599 | let iframe = _$('.viframe').attr('src') 600 | let iframeRes = await $.http.get({ url: iframe, headers: this.headers }) 601 | let config = JSON.parse('{' + iframeRes.body.match(/ConFig = \{(.*)\}/)[1] + '}') 602 | let playUrl = this.decryptUrl(config) 603 | backData.data = playUrl 604 | } 605 | } catch (e) { 606 | $.logErr(e) 607 | backData.error = e.message 608 | } 609 | return JSON.stringify(backData) 610 | } 611 | 612 | async searchVideo(queryParams) { 613 | const pg = queryParams.pg 614 | const wd = queryParams.wd 615 | let backData = {} 616 | 617 | try { 618 | let searchUrl = this.url + '/?s=' + wd 619 | let searchRes = await $.http.get({ 620 | url: searchUrl, 621 | headers: this.headers, 622 | }) 623 | let _$ = $.cheerio.load(searchRes.body) 624 | let videos = [] 625 | let allVideo = _$('.search_list').find('li') 626 | allVideo.each((index, element) => { 627 | let vodUrl = _$(element).find('a').attr('href') || '' 628 | let vodPic = _$(element).find('img.thumb').attr('data-original') || '' 629 | let vodName = _$(element).find('img.thumb').attr('alt') || '' 630 | let vodDiJiJi = _$(element).find('.jidi').text() || '' 631 | 632 | let videoDet = {} 633 | videoDet.vod_id = +vodUrl.match(/movie\/(.+)\.html/)[1] 634 | videoDet.vod_pic = vodPic 635 | videoDet.vod_name = vodName 636 | videoDet.vod_remarks = vodDiJiJi.trim() 637 | videos.push(videoDet) 638 | }) 639 | 640 | backData.code = 1 641 | backData.msg = '數據列表' 642 | backData.page = pg 643 | backData.limit = videos.length.toString() 644 | backData.list = videos 645 | } catch (e) { 646 | $.logErr(e) 647 | backData.error = e.message 648 | } 649 | 650 | return JSON.stringify(backData) 651 | } 652 | 653 | decryptUrl(jsConfig) { 654 | const key = CryptoJS.enc.Utf8.parse('2890' + jsConfig.config.uid + 'tB959C') 655 | const iv = CryptoJS.enc.Utf8.parse('2F131BE91247866E') 656 | const mode = CryptoJS.mode.CBC 657 | const padding = CryptoJS.pad.Pkcs7 658 | const decrypted = CryptoJS.AES.decrypt(jsConfig.url, key, { 659 | iv: iv, 660 | mode: mode, 661 | padding: padding, 662 | }) 663 | const decryptedUrl = CryptoJS.enc.Utf8.stringify(decrypted) 664 | 665 | return decryptedUrl 666 | } 667 | 668 | combineUrl(url) { 669 | if (url === undefined) { 670 | return '' 671 | } 672 | if (url.indexOf(this.url) !== -1) { 673 | return url 674 | } 675 | if (url.startsWith('/')) { 676 | return this.url + url 677 | } 678 | return this.url + '/' + url 679 | } 680 | 681 | isIgnoreClassName(className) { 682 | return this.ignoreClassName.some((element) => className.includes(element)) 683 | } 684 | 685 | removeTrailingSlash(str) { 686 | if (str.endsWith('/')) { 687 | return str.slice(0, -1) 688 | } 689 | return str 690 | } 691 | })() 692 | } 693 | 694 | function duanjuttClass() { 695 | return new (class { 696 | constructor() { 697 | this.url = 'https://duanjutt.tv' 698 | this.headers = { 699 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', 700 | } 701 | this.ignoreClassName = ['首页', '明星'] 702 | } 703 | 704 | async getClassList() { 705 | let webUrl = this.url 706 | let backData = {} 707 | try { 708 | const pro = await $.http.get({ url: webUrl, headers: this.headers }) 709 | 710 | let proData = await pro.body 711 | if (proData) { 712 | let _$ = $.cheerio.load(proData) 713 | let allClass = _$('.dropdown-box ul li a') 714 | let list = [] 715 | allClass.each((index, element) => { 716 | let isIgnore = this.isIgnoreClassName(_$(element).text()) 717 | if (isIgnore) { 718 | return 719 | } 720 | let type_name = _$(element).text().split('(')[0] 721 | let url = _$(element).attr('href') || '' 722 | url = url.match(/vodtype\/(.*)\.html/)[1] 723 | 724 | if (url.length > 0 && type_name.length > 0) { 725 | let videoClass = {} 726 | videoClass.type_id = parseInt(url) 727 | videoClass.type_name = type_name.trim() 728 | list.push(videoClass) 729 | } 730 | }) 731 | 732 | let allVideo = _$('.myui-vodlist') 733 | let videos = [] 734 | allVideo.each((index, element) => { 735 | let nodes = _$(element).find('li') 736 | nodes.each((index, element) => { 737 | let vodUrl = _$(element).find('a.myui-vodlist__thumb').attr('href') || '' 738 | let vodPic = _$(element).find('a.myui-vodlist__thumb').attr('data-original') || '' 739 | let vodName = _$(element).find('a.myui-vodlist__thumb').attr('title') || '' 740 | let vodDiJiJi = _$(element).find('.pic-text').text() || '' 741 | 742 | if (!vodPic.includes('http')) vodPic = this.url + vodPic 743 | 744 | let videoDet = {} 745 | videoDet.vod_id = +vodUrl.match(/voddetail\/(.+)\.html/)[1] 746 | videoDet.vod_pic = vodPic 747 | videoDet.vod_name = vodName 748 | videoDet.vod_remarks = vodDiJiJi.trim() 749 | videos.push(videoDet) 750 | }) 751 | }) 752 | 753 | backData.code = 1 754 | backData.msg = '數據列表' 755 | backData.page = 1 756 | backData.list = videos 757 | backData.class = list 758 | } 759 | } catch (e) { 760 | $.logErr(e) 761 | backData.e = e.message 762 | } 763 | 764 | return JSON.stringify(backData) 765 | } 766 | 767 | async getVideoList(queryParams) { 768 | const page = queryParams.pg 769 | const type = queryParams.t 770 | 771 | if (type === '') return this.getClassList() 772 | 773 | let listUrl = `${this.url}/vodtype/${type}-${page}.html` 774 | let backData = {} 775 | try { 776 | let pro = await $.http.get({ url: listUrl, headers: this.headers }) 777 | let proData = pro.body 778 | if (proData) { 779 | let _$ = $.cheerio.load(proData) 780 | let allVideo = _$('.myui-vodlist li') 781 | let lastPage = _$('.myui-page').children('li').last().find('a').attr('href') 782 | if (lastPage) { 783 | let regex = new RegExp(`/vodtype\/${type}-(.*)\.html`) 784 | lastPage = lastPage.match(regex)[1] 785 | } else { 786 | lastPage = '1' 787 | } 788 | let videos = [] 789 | allVideo.each((index, element) => { 790 | let vodUrl = _$(element).find('a.myui-vodlist__thumb').attr('href') || '' 791 | let vodPic = _$(element).find('a.myui-vodlist__thumb').attr('data-original') || '' 792 | let vodName = _$(element).find('a.myui-vodlist__thumb').attr('title') || '' 793 | let vodDiJiJi = _$(element).find('.pic-text').text() || '' 794 | 795 | if (!vodPic.includes('http')) vodPic = this.url + vodPic 796 | 797 | let videoDet = {} 798 | videoDet.vod_id = +vodUrl.match(/voddetail\/(.+)\.html/)[1] 799 | videoDet.vod_pic = vodPic 800 | videoDet.vod_name = vodName 801 | videoDet.vod_remarks = vodDiJiJi.trim() 802 | videos.push(videoDet) 803 | }) 804 | 805 | backData.code = 1 806 | backData.msg = '數據列表' 807 | backData.page = page.toString() 808 | backData.pagecount = +lastPage 809 | backData.limit = videos.length.toString() 810 | backData.total = videos.length * lastPage 811 | backData.list = videos 812 | } 813 | } catch (e) { 814 | $.logErr('Error fetching list:', e) 815 | backData.error = e.message 816 | } 817 | return JSON.stringify(backData) 818 | } 819 | 820 | async getVideoDetail(queryParams) { 821 | let ids = queryParams.ids 822 | let backData = {} 823 | try { 824 | let webUrl = this.url + `/voddetail/${ids}.html` 825 | let pro = await $.http.get({ url: webUrl, headers: this.headers }) 826 | let proData = pro.body 827 | if (proData) { 828 | let _$ = $.cheerio.load(proData) 829 | let vod_name = _$('h1.title').text() 830 | let vod_content = _$('.context span.content').text() 831 | let vod_pic = _$('a.myui-vodlist__thumb img').attr('data-original') 832 | 833 | if (!vod_pic.includes('http')) vod_pic = this.url + vod_pic 834 | 835 | let juJiDocment = _$('#playlist1').find('a') 836 | // let vod_play_from = ''; 837 | let vod_play_url = '' 838 | juJiDocment.each((index, element) => { 839 | vod_play_url += _$(element).text() 840 | vod_play_url += '$' 841 | vod_play_url += 842 | 'https://ykusu.ykusu/duanjutt/provide/vod?ac=play&url=' + encodeURIComponent(this.combineUrl(_$(element).attr('href'))) + '&n=.m3u8' 843 | vod_play_url += '#' 844 | }) 845 | 846 | let temp = { 847 | code: 1, 848 | msg: '数据列表', 849 | page: 1, 850 | pagecount: 1, 851 | limit: '20', 852 | total: 1, 853 | list: [ 854 | { 855 | vod_id: 1, 856 | vod_name: '', 857 | vod_pic: '', 858 | vod_remarks: '', 859 | type_name: '', 860 | vod_year: '', 861 | vod_area: '', 862 | vod_actor: '', 863 | vod_director: '', 864 | vod_content: '', 865 | vod_play_from: '', 866 | vod_play_url: '', 867 | }, 868 | ], 869 | } 870 | temp.list[0].vod_play_url = vod_play_url 871 | // temp.list[0].vod_play_from = play_from.join('$$$') 872 | // temp.list[0].vod_play_note = '$$$' 873 | temp.list[0].vod_id = +ids 874 | temp.list[0].vod_name = vod_name 875 | temp.list[0].vod_pic = vod_pic 876 | temp.list[0].vod_content = vod_content.trim() 877 | backData = temp 878 | } 879 | } catch (e) { 880 | $.logErr(e) 881 | backData.error = e.message 882 | } 883 | 884 | return JSON.stringify(backData) 885 | } 886 | 887 | async getVideoPlayUrl(queryParams) { 888 | let backData = {} 889 | let url = decodeURIComponent(queryParams.url) 890 | try { 891 | let html = await $.http.get({ url: url, headers: this.headers }) 892 | 893 | let proData = html.body 894 | if (proData) { 895 | let playUrl 896 | let player = proData.match(/r player.*=\{(.*)\}/)[1] 897 | let config = JSON.parse(`{${player}}`) 898 | if (config.encrypt === 0) { 899 | playUrl = config.url 900 | } else { 901 | $.msg('還沒寫,@ykusu叫他寫') 902 | } 903 | backData.data = playUrl 904 | } 905 | } catch (e) { 906 | $.logErr(e) 907 | backData.error = e.message 908 | } 909 | return JSON.stringify(backData) 910 | } 911 | 912 | async searchVideo(queryParams) { 913 | const pg = queryParams.pg 914 | const wd = queryParams.wd 915 | let backData = {} 916 | 917 | try { 918 | let searchUrl = this.url + '/?s=' + wd 919 | let searchRes = await $.http.get({ 920 | url: searchUrl, 921 | headers: this.headers, 922 | }) 923 | let _$ = $.cheerio.load(searchRes.body) 924 | let videos = [] 925 | let allVideo = _$('.search_list').find('li') 926 | allVideo.each((index, element) => { 927 | let vodUrl = _$(element).find('a').attr('href') || '' 928 | let vodPic = _$(element).find('img.thumb').attr('data-original') || '' 929 | let vodName = _$(element).find('img.thumb').attr('alt') || '' 930 | let vodDiJiJi = _$(element).find('.jidi').text() || '' 931 | 932 | let videoDet = {} 933 | videoDet.vod_id = +vodUrl.match(/movie\/(.+)\.html/)[1] 934 | videoDet.vod_pic = vodPic 935 | videoDet.vod_name = vodName 936 | videoDet.vod_remarks = vodDiJiJi.trim() 937 | videos.push(videoDet) 938 | }) 939 | 940 | backData.code = 1 941 | backData.msg = '數據列表' 942 | backData.page = pg 943 | backData.limit = videos.length.toString() 944 | backData.list = videos 945 | } catch (e) { 946 | $.logErr(e) 947 | backData.error = e.message 948 | } 949 | 950 | return JSON.stringify(backData) 951 | } 952 | 953 | decryptUrl(jsConfig) { 954 | const key = CryptoJS.enc.Utf8.parse('2890' + jsConfig.config.uid + 'tB959C') 955 | const iv = CryptoJS.enc.Utf8.parse('2F131BE91247866E') 956 | const mode = CryptoJS.mode.CBC 957 | const padding = CryptoJS.pad.Pkcs7 958 | const decrypted = CryptoJS.AES.decrypt(jsConfig.url, key, { 959 | iv: iv, 960 | mode: mode, 961 | padding: padding, 962 | }) 963 | const decryptedUrl = CryptoJS.enc.Utf8.stringify(decrypted) 964 | 965 | return decryptedUrl 966 | } 967 | 968 | combineUrl(url) { 969 | if (url === undefined) { 970 | return '' 971 | } 972 | if (url.indexOf(this.url) !== -1) { 973 | return url 974 | } 975 | if (url.startsWith('/')) { 976 | return this.url + url 977 | } 978 | return this.url + '/' + url 979 | } 980 | 981 | isIgnoreClassName(className) { 982 | return this.ignoreClassName.some((element) => className.includes(element)) 983 | } 984 | 985 | removeTrailingSlash(str) { 986 | if (str.endsWith('/')) { 987 | return str.slice(0, -1) 988 | } 989 | return str 990 | } 991 | })() 992 | } 993 | 994 | function sxClass() { 995 | return new (class { 996 | async getVideoPlayUrl(queryParams) { 997 | let backData = {} 998 | let url = decodeURIComponent(queryParams.url) 999 | url = base64Decode('aHR0cHM6Ly9qc29uLnN1eHVuLnNpdGUvcGxheWVyL2FydHBsYXllci5waHA/aWY9MSZmcm9tPXN1eHVuangmdXJsPQ==') + url 1000 | try { 1001 | let html = await $.http.get({ url: url, headers: this.headers }) 1002 | 1003 | let proData = html.body 1004 | if (proData) { 1005 | let playUrl 1006 | const matches = proData.match(/let ConFig = {([\w\W]*)},box/) 1007 | if (proData && proData.length > 1) { 1008 | const configJson = '{' + matches[1].trim() + '}' 1009 | const config = JSON.parse(configJson) 1010 | playUrl = this.decryptUrl(config) 1011 | } 1012 | backData.data = playUrl 1013 | } 1014 | } catch (e) { 1015 | $.logErr(e) 1016 | backData.error = e.message 1017 | } 1018 | return JSON.stringify(backData) 1019 | } 1020 | 1021 | decryptUrl(jsConfig) { 1022 | const key = CryptoJS.enc.Utf8.parse('2890' + jsConfig.config.uid + 'tB959C') 1023 | const iv = CryptoJS.enc.Utf8.parse('2F131BE941093035') 1024 | const mode = CryptoJS.mode.CBC 1025 | const padding = CryptoJS.pad.Pkcs7 1026 | const decrypted = CryptoJS.AES.decrypt(jsConfig.url, key, { 1027 | iv: iv, 1028 | mode: mode, 1029 | padding: padding, 1030 | }) 1031 | const decryptedUrl = CryptoJS.enc.Utf8.stringify(decrypted) 1032 | 1033 | return decryptedUrl 1034 | } 1035 | })() 1036 | } 1037 | 1038 | function dsakf23665Class() { 1039 | return new (class { 1040 | constructor() { 1041 | this.headers = { 1042 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', 1043 | } 1044 | } 1045 | 1046 | async getVideoPlayUrl(queryParams) { 1047 | let backData = {} 1048 | let parts = queryParams.url.split('@@@') 1049 | let id = parts[0] 1050 | let from = parts[1] 1051 | let url = decodeURIComponent(parts[2]) 1052 | let parseUrl = 'http://dsakf23665.com/api.php/v1.home/parseurl' 1053 | let body = { 1054 | vod_id: id, 1055 | from: from, 1056 | url: url, 1057 | index: 0, 1058 | } 1059 | try { 1060 | let resp = await $.http.post({ 1061 | url: parseUrl, 1062 | headers: { 1063 | 'Content-Type': 'application/json', 1064 | 'User-Agent': this.headers['User-Agent'], 1065 | }, 1066 | body: JSON.stringify(body), 1067 | }) 1068 | 1069 | let playUrl = JSON.parse(resp.body).data.url 1070 | if (!playUrl.startsWith('http')) { 1071 | $.msg('解析失敗') 1072 | } 1073 | backData.data = playUrl 1074 | } catch (e) { 1075 | $.logErr(e) 1076 | backData.error = e.message 1077 | } 1078 | return JSON.stringify(backData) 1079 | } 1080 | })() 1081 | } --------------------------------------------------------------------------------