├── README.md ├── groupPanel.sgmudole ├── NetflixSelect ├── NetflixSelect.sgmudule ├── NetflixController.sgmodule ├── README.md ├── nf_autocheck.js ├── netflixchecker.js ├── netflixshortcut.js ├── nf_autoselect.js └── netflixcontroller.js ├── DisneySelect ├── DisneyPlusSelect.sgmodule ├── DisneyPlusController.sgmodule ├── README.md ├── disney_checker.js ├── disney_selecter.js ├── disneychecker.js ├── disneyshortcut.js └── disneycontroller.js ├── YouTubeSelect ├── YouTubeController.sgmodule ├── readme.md ├── youtubechecker.js ├── youtubeshortcut.js └── youtubecontroller.js ├── PanelScripts ├── net_info.js ├── surgepro_flushdns.js ├── surgepro_reloadprofile.js ├── grouppanel.js └── trafficstatistics.js ├── groupPanel.js └── Panels.sgmodule /README.md: -------------------------------------------------------------------------------- 1 | # 个人用Surge Panel脚本 2 | 3 | 由于大部分panel脚本需要根据配置进行参数调整,因此没有提供单独模组,而是整理为一个集合,你可以在其中选择自己感兴趣的部分进行添加 4 | 5 | https://raw.githubusercontent.com/fishingworld/something/main/Panels.sgmodule 6 | 7 | 效果展示: 8 | 9 | ![d9hQHjY](https://i.imgur.com/d9hQHjY.jpg) 10 | 11 | ![cdHZn09](https://i.imgur.com/cdHZn09.jpg) 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /groupPanel.sgmudole: -------------------------------------------------------------------------------- 1 | #!name=groupPanel 2 | #!desc=通过panel显示及控制策略组 只有手动更新情况下会切换策略组,因此你可以配置自动更新以防止数据与实际不一致 3 | 4 | [Script] 5 | groupPanel = type=generic,timeout=10,script-path=https://raw.githubusercontent.com/fishingworld/something/main/groupPanel.js,argument=icon=network&color=#86abee&group=Master 6 | #对应参数: 7 | #icon:图标 8 | #color:图标颜色 9 | #group:策略组名称 10 | [Panel] 11 | groupPanel = script-name=groupPanel,update-interval=5 12 | -------------------------------------------------------------------------------- /NetflixSelect/NetflixSelect.sgmudule: -------------------------------------------------------------------------------- 1 | #!name=NetflixSelect 2 | #!desc=通过panel显示及控制Netflix策略组 3 | 4 | 5 | [Panel] 6 | NetflixSelect = script-name=NetflixSelect, update-interval=3600 7 | 8 | 9 | [Script] 10 | NetflixSelect = type=generic, script-path=https://raw.githubusercontent.com/fishingworld/something/main/NetflixSelect/nf_autoselect.js, timeout=3600, argument=icon1=checkmark.circle&color1=#55ba94&icon2=checkmark.circle.trianglebadge.exclamationmark&color2=#9a9ced&icon3=hand.raised.circle&color3=#ea5532&netflixGroup=Netflix 11 | NetflixChecker = type=cron,cronexp=5 4 * * *,wake-system=1,timeout=3600,script-path=https://raw.githubusercontent.com/fishingworld/something/main/NetflixSelect/nf_autocheck.js,script-update-interval=0,control-api=1 12 | -------------------------------------------------------------------------------- /DisneySelect/DisneyPlusSelect.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=DisneyPlusSelect 2 | #!desc=通过panel显示及控制Disney策略组 3 | 4 | [Panel] 5 | DisneySelect = script-name=DisneySelecter, update-interval=3600 6 | 7 | [Script] 8 | #应当修改的字段 disneyGroup Disney+的策略组名称 9 | DisneySelecter = type=generic, script-path=https://raw.githubusercontent.com/fishingworld/something/main/DisneySelect/disney_selecter.js, timeout=3600, argument=icon1=checkmark.circle&color1=#55ba94&icon2=cursorarrow.click.badge.clock&color2=#ed6c84&icon3=xmark.shield&color3=#AF52DE&disneyGroup=Disney+ 10 | DisneyChecker = type=cron,cronexp=35 4 * * *,wake-system=1,timeout=3600,script-path=https://raw.githubusercontent.com/fishingworld/something/main/DisneySelect/disney_checker.js,script-update-interval=86400,control-api=1 11 | -------------------------------------------------------------------------------- /YouTubeSelect/YouTubeController.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=YouTubeController 2 | #!desc=YouTube策略控制器 3 | 4 | 5 | [Panel] 6 | YouTubeController = script-name=YouTubeController, update-interval=600 7 | 8 | [Script] 9 | #应该修改的字段 YouTubeGroup 10 | YouTubeController = type=generic, script-path=https://raw.githubusercontent.com/fishingworld/something/main/YouTubeSelect/youtubecontroller.js, argument=icon1=play.rectangle.on.rectangle.circle&color1=#55ba94&icon2=xmark.shield&color2=#AF52DE&YouTubeGroup=YouTube,timeout=3600 11 | YouTubeChecker = type=cron,cronexp=10 0-23/4 * * *,wake-system=0,timeout=3600,script-path=https://raw.githubusercontent.com/fishingworld/something/main/YouTubeSelect/youtubechecker.js,script-update-interval=86400,control-api=1 12 | #捷径执行 13 | YouTubeShortcut = type=cron,cronexp=5 0 31 1 2000,wake-system=0,timeout=3600,script-path=https://raw.githubusercontent.com/fishingworld/something/main/YouTubeSelect/youtubeshortcut.js,script-update-interval=86400,control-api=1 14 | -------------------------------------------------------------------------------- /YouTubeSelect/readme.md: -------------------------------------------------------------------------------- 1 | # YouTube策略組控制器 2 | 3 | 使用者反馈:Telegram https://t.me/okmytg 4 | 5 | 脚本修改自: @Helge_0x00 6 | 7 | 效果展示: 8 | 9 | ![mYjYIik](https://i.imgur.com/mYjYIik.jpg) 10 | 11 | ⭐使用规范 12 | 13 | https://raw.githubusercontent.com/fishingworld/something/main/YouTubeSelect/YouTubeController.sgmodule 14 | 15 | 1:本脚本实用至上 以效率为优先 你可以更快速的控制你的策略组 16 | 17 | 2:首次执行或切换至新的子策略时会花费较长时间 这是在建立索引 18 | 19 | 3:策略组应当为完全的子策略组嵌套且模式应当为select 你不应放置任何单独节点 否则将失去效果 你可以隐藏你的子策略组 20 | 21 | 4:你可以调整cron代码以控制节点刷新的频率 但不宜过于频繁 22 | 23 | 5:可用的自定义参数: 24 | 25 | icon1 color1:节点正常时的图标及颜色 26 | 27 | icon2 color2:节点异常时的图标及颜色 28 | 29 | YouTubeGroup:YouTube策略组名称(应当修改) 30 | 31 | 你可以配置捷径自动化实现打开YouTube自动检测 32 | 33 | 配置自动化后可以根据需要减少cron执行频率甚至你可以删除cron脚本 34 | 35 | YouTubeShortcut相对于YouTubeChecker仅增加一项通知弹窗,如果你不想看到通知 捷径执行的脚本名称应当为YouTubeChecker 36 | 37 | ![STaRsyw](https://i.imgur.com/STaRsyw.png) 38 | 39 | ![H7E741q](https://i.imgur.com/H7E741q.png) 40 | 41 | ![xL6Qk5V](https://i.imgur.com/xL6Qk5V.jpg) 42 | -------------------------------------------------------------------------------- /DisneySelect/DisneyPlusController.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=DisneyPlusController 2 | #!desc=Disney+策略控制器 3 | 4 | 5 | [Panel] 6 | DisneyController = script-name=DisneyController, update-interval=600 7 | 8 | [Script] 9 | #应该修改的字段 disneyGroup 10 | DisneyController = type=generic, script-path=https://raw.githubusercontent.com/fishingworld/something/main/DisneySelect/disneycontroller.js, timeout=3600,argument=icon1=checkmark.circle&color1=#55ba94&icon2=cursorarrow.click.badge.clock&color2=#ed6c84&icon3=xmark.shield&color3=#AF52DE&disneyGroup=Disney+ 11 | DisneyChecker = type=cron,cronexp=13 0-23/4 * * *,wake-system=0,timeout=3600,script-path=https://raw.githubusercontent.com/fishingworld/something/main/DisneySelect/disneychecker.js,script-update-interval=86400,control-api=1 12 | #捷径执行 13 | DisneyShortcut = type=cron,cronexp=5 0 31 1 2000,wake-system=0,timeout=3600,script-path=https://raw.githubusercontent.com/fishingworld/something/main/DisneySelect/disneyshortcut.js,script-update-interval=86400,control-api=1 14 | -------------------------------------------------------------------------------- /NetflixSelect/NetflixController.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=NetflixController 2 | #!desc=Netflix策略控制器 3 | 4 | 5 | [Panel] 6 | NetflixController = script-name=NetflixController, update-interval=600 7 | 8 | [Script] 9 | #应该修改的字段 netflixGroup 10 | NetflixController = type=generic, script-path=https://raw.githubusercontent.com/fishingworld/something/main/NetflixSelect/netflixcontroller.js, timeout=3600, argument=icon1=checkmark.circle&color1=55ba94&icon2=checkmark.circle.trianglebadge.exclamationmark&color2=#9a9ced&icon3=hand.raised.circle&color3=#ea5532&netflixGroup=Netflix 11 | NetflixChecker = type=cron,cronexp=10 * * * *,wake-system=0,timeout=3600,script-path=https://raw.githubusercontent.com/fishingworld/something/main/NetflixSelect/netflixchecker.js,script-update-interval=86400,control-api=1 12 | #捷径执行 13 | NetflixShortcut = type=cron,cronexp=5 * 31 1 2000,wake-system=0,timeout=3600,script-path=https://raw.githubusercontent.com/fishingworld/something/main/NetflixSelect/netflixshortcut.js,script-update-interval=86400,control-api=1 14 | -------------------------------------------------------------------------------- /NetflixSelect/README.md: -------------------------------------------------------------------------------- 1 | 通过Surge Panel 显示及控制你的Netflix策略组 2 | 3 | 使用者反馈:Telegram https://t.me/okmytg 4 | 5 | 脚本修改自: @Helge_0x00 6 | 7 | 效果展示: 8 | 9 | ![MwnEmy7sJFHiBaT](https://i.loli.net/2021/10/05/MwnEmy7sJFHiBaT.jpg) 10 | 11 | 12 | ⭐优化版(使用新版请删除旧版本) 13 | 14 | https://raw.githubusercontent.com/fishingworld/something/main/NetflixSelect/NetflixController.sgmodule 15 | 16 | 1:优化版实用至上 大幅提高了使用效率 你可以更快速的控制你的策略组 17 | 18 | 2:首次执行或切换至新的子策略时会花费较长时间 这是在建立索引 19 | 20 | 3:策略组应当为完全的子策略组嵌套且模式应当为select 你不应放置任何单独节点 否则将失去效果 你可以隐藏你的子策略组 21 | 22 | 4:你可以调整cron代码以控制节点刷新的频率 但不宜过于频繁 23 | 24 | 5:参数部分与老版本相同 25 | 26 | 你可以配置捷径自动化实现打开Netflix自动检测 27 | 28 | 配置自动化后可以根据需要减少cron执行频率甚至你可以删除cron脚本 29 | 30 | NetflixShortcut相对于NetflixChecker仅增加一项通知弹窗,如果你不想看到通知 捷径执行的脚本名称应当为NetflixChecker 31 | 32 | ![s1Lhxg0](https://i.imgur.com/s1Lhxg0.png) 33 | 34 | ![ZMWplwo](https://i.imgur.com/ZMWplwo.png) 35 | 36 | ![kCGURnZ](https://i.imgur.com/kCGURnZ.jpg) 37 | 38 | 39 | 40 | ❄老版本说明:(老版本可能仍有bug,但已经不再维护) 41 | 42 | 1:panel脚本依赖cron脚本传送数据,你应当手动运行一次cron脚本以获取节点列表 43 | 44 | 2:点击panel时切换至下一个可解锁节点,节点列表为空时仅执行状态检测 45 | 46 | 3:panel脚本允许自动更新,自动更新将刷新策略组信息,并可以自动选择更优选项 47 | 48 | 4: cron脚本用于遍历Netflix策略组,以获取节点列表,你可以通过配置cron表达式以修改执行频率 49 | 50 | 5:可用的自定义参数: 51 | 52 | icon1 color1:全解锁时的图标及颜色 53 | 54 | icon2 color2:仅自制时的图标及颜色 55 | 56 | icon3 color3:无可用节点的图标及颜色 57 | 58 | netflixGroup:网飞策略组名称 59 | 60 | https://github.com/fishingworld/something/blob/main/NetflixSelect/NetflixSelect.sgmudule 61 | -------------------------------------------------------------------------------- /PanelScripts/net_info.js: -------------------------------------------------------------------------------- 1 | ;(async () => { 2 | 3 | 4 | 5 | let params = getParams($argument) 6 | //获取根节点名 7 | let proxy = await httpAPI("/v1/policy_groups"); 8 | let allGroup = []; 9 | for (var key in proxy){ 10 | allGroup.push(key) 11 | } 12 | let group = params.group 13 | let rootName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(group)+"")).policy; 14 | while(allGroup.includes(rootName)==true){ 15 | rootName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(rootName)+"")).policy; 16 | } 17 | 18 | $httpClient.get('http://ip-api.com/json/?lang=en', function (error, response, data) { 19 | const jsonData = JSON.parse(data); 20 | $done({ 21 | title:rootName, 22 | content: 23 | `國家地區: ${jsonData.country} - ${jsonData.city}\n`+ 24 | `運營商 : ${jsonData.isp}\n` + 25 | `數據中心: ${jsonData.org}`, 26 | icon: params.icon, 27 | "icon-color":params.color 28 | }); 29 | }); 30 | 31 | })(); 32 | 33 | 34 | function httpAPI(path = "", method = "GET", body = null) { 35 | return new Promise((resolve) => { 36 | $httpAPI(method, path, body, (result) => { 37 | resolve(result); 38 | }); 39 | }); 40 | }; 41 | 42 | function getParams(param) { 43 | return Object.fromEntries( 44 | $argument 45 | .split("&") 46 | .map((item) => item.split("=")) 47 | .map(([k, v]) => [k, decodeURIComponent(v)]) 48 | ); 49 | } 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /DisneySelect/README.md: -------------------------------------------------------------------------------- 1 | # Disney+策略組控制器 2 | 3 | 使用者反馈:Telegram https://t.me/okmytg 4 | 5 | 脚本修改自: @Helge_0x00 6 | 7 | 效果展示: 8 | 9 | ![APc3kCsf2YDw7K1](https://i.loli.net/2021/10/07/APc3kCsf2YDw7K1.jpg) 10 | 11 | ![OP4NofYgsv5piSI](https://i.loli.net/2021/10/07/OP4NofYgsv5piSI.jpg) 12 | 13 | ⭐优化版(使用新版请删除旧版本) 14 | 15 | https://raw.githubusercontent.com/fishingworld/something/main/DisneySelect/DisneyPlusController.sgmodule 16 | 17 | 1:优化版实用至上 大幅提高了使用效率 你可以更快速的控制你的策略组 18 | 19 | 2:首次执行或切换至新的子策略时会花费较长时间 这是在建立索引 20 | 21 | 3:策略组应当为完全的子策略组嵌套且模式应当为select 你不应放置任何单独节点 否则将失去效果 你可以隐藏你的子策略组 22 | 23 | 4:你可以调整cron代码以控制节点刷新的频率 但不宜过于频繁 24 | 25 | 5:参数部分与老版本相同 26 | 27 | 你可以配置捷径自动化实现打开Disney自动检测 28 | 29 | 配置自动化后可以根据需要减少cron执行频率甚至你可以删除cron脚本 30 | 31 | DisneyShortcut相对于DisneyChecker仅增加一项通知弹窗,如果你不想看到通知 捷径执行的脚本名称应当为DisneyChecker 32 | 33 | ![m4E7twi](https://i.imgur.com/m4E7twi.png) 34 | 35 | ![c6wUWif](https://i.imgur.com/c6wUWif.png) 36 | 37 | ![fEqMzsP](https://i.imgur.com/fEqMzsP.jpg) 38 | 39 | ❄老版本说明:(老版本可能仍有bug,但已经不再维护) 40 | 41 | 1:panel脚本依赖cron脚本传送数据,你应当手动运行一次cron脚本以获取节点列表 42 | 43 | 2:点击panel时切换至下一个可解锁节点,节点列表为空时仅执行状态检测 44 | 45 | 3:panel脚本允许自动更新,自动更新将刷新策略组信息,并可以自动选择更优选项 46 | 47 | 4: cron脚本用于遍历Disney策略组,以获取节点列表,你可以通过配置cron表达式以修改执行频率 48 | 49 | 5:可用的自定义参数: 50 | 51 | icon1 color1:全解锁时的图标及颜色 52 | 53 | icon2 color2:即将上线的图标及颜色 54 | 55 | icon3 color3:无可用节点的图标及颜色 56 | 57 | disneyGroup:迪士尼策略组名称 58 | 59 | https://raw.githubusercontent.com/fishingworld/something/main/DisneySelect/DisneyPlusSelect.sgmodule 60 | -------------------------------------------------------------------------------- /PanelScripts/surgepro_flushdns.js: -------------------------------------------------------------------------------- 1 | let params = getParams($argument) 2 | 3 | !(async () => { 4 | /* 时间获取 */ 5 | let traffic = (await httpAPI("/v1/traffic","GET")) 6 | let dateNow = new Date() 7 | let dateTime = Math.floor(traffic.startTime*1000) 8 | let startTime = timeTransform(dateNow,dateTime) 9 | 10 | if ($trigger == "button") await httpAPI("/v1/dns/flush"); 11 | 12 | $done({ 13 | title:"Surge Pro", 14 | content:`启动时长: ${startTime}`, 15 | icon: params.icon, 16 | "icon-color":params.color 17 | }); 18 | 19 | })(); 20 | 21 | function timeTransform(dateNow,dateTime) { 22 | let dateDiff = dateNow - dateTime; 23 | let days = Math.floor(dateDiff / (24 * 3600 * 1000));//计算出相差天数 24 | let leave1=dateDiff%(24*3600*1000) //计算天数后剩余的毫秒数 25 | let hours=Math.floor(leave1/(3600*1000))//计算出小时数 26 | //计算相差分钟数 27 | let leave2=leave1%(3600*1000) //计算小时数后剩余的毫秒数 28 | let minutes=Math.floor(leave2/(60*1000))//计算相差分钟数 29 | //计算相差秒数 30 | let leave3=leave2%(60*1000) //计算分钟数后剩余的毫秒数 31 | let seconds=Math.round(leave3/1000) 32 | 33 | if(days==0){ 34 | 35 | if(hours==0){ 36 | if(minutes==0)return(`${seconds}秒`); 37 | return(`${minutes}分${seconds}秒`) 38 | } 39 | return(`${hours}时${minutes}分${seconds}秒`) 40 | }else { 41 | return(`${days}天${hours}时${minutes}分`) 42 | } 43 | 44 | } 45 | 46 | 47 | function httpAPI(path = "", method = "POST", body = null) { 48 | return new Promise((resolve) => { 49 | $httpAPI(method, path, body, (result) => { 50 | resolve(result); 51 | }); 52 | }); 53 | } 54 | 55 | function getParams(param) { 56 | return Object.fromEntries( 57 | $argument 58 | .split("&") 59 | .map((item) => item.split("=")) 60 | .map(([k, v]) => [k, decodeURIComponent(v)]) 61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /PanelScripts/surgepro_reloadprofile.js: -------------------------------------------------------------------------------- 1 | let params = getParams($argument) 2 | 3 | !(async () => { 4 | /* 时间获取 */ 5 | let traffic = (await httpAPI("/v1/traffic","GET")) 6 | let dateNow = new Date() 7 | let dateTime = Math.floor(traffic.startTime*1000) 8 | let startTime = timeTransform(dateNow,dateTime) 9 | 10 | if ($trigger == "button") await httpAPI("/v1/profiles/reload"); 11 | 12 | $done({ 13 | title:"Surge Pro", 14 | content:`启动时长: ${startTime}`, 15 | icon: params.icon, 16 | "icon-color":params.color 17 | }); 18 | 19 | })(); 20 | 21 | function timeTransform(dateNow,dateTime) { 22 | let dateDiff = dateNow - dateTime; 23 | let days = Math.floor(dateDiff / (24 * 3600 * 1000));//计算出相差天数 24 | let leave1=dateDiff%(24*3600*1000) //计算天数后剩余的毫秒数 25 | let hours=Math.floor(leave1/(3600*1000))//计算出小时数 26 | //计算相差分钟数 27 | let leave2=leave1%(3600*1000) //计算小时数后剩余的毫秒数 28 | let minutes=Math.floor(leave2/(60*1000))//计算相差分钟数 29 | //计算相差秒数 30 | let leave3=leave2%(60*1000) //计算分钟数后剩余的毫秒数 31 | let seconds=Math.round(leave3/1000) 32 | 33 | if(days==0){ 34 | 35 | if(hours==0){ 36 | if(minutes==0)return(`${seconds}秒`); 37 | return(`${minutes}分${seconds}秒`) 38 | } 39 | return(`${hours}时${minutes}分${seconds}秒`) 40 | }else { 41 | return(`${days}天${hours}时${minutes}分`) 42 | } 43 | 44 | } 45 | 46 | 47 | function httpAPI(path = "", method = "POST", body = null) { 48 | return new Promise((resolve) => { 49 | $httpAPI(method, path, body, (result) => { 50 | resolve(result); 51 | }); 52 | }); 53 | } 54 | 55 | function getParams(param) { 56 | return Object.fromEntries( 57 | $argument 58 | .split("&") 59 | .map((item) => item.split("=")) 60 | .map(([k, v]) => [k, decodeURIComponent(v)]) 61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /groupPanel.js: -------------------------------------------------------------------------------- 1 | /* 2 | [Script] 3 | groupPanel = type=generic,timeout=10,script-path=https://raw.githubusercontent.com/fishingworld/something/main/groupPanel.js,argument=icon=network&color=#86abee&group=Master 4 | 对应参数: 5 | icon:图标 6 | color:图标颜色 7 | group:策略组名称 8 | [Panel] 9 | groupPanel = script-name=groupPanel,update-interval=5 10 | */ 11 | 12 | 13 | 14 | ;(async () => { 15 | 16 | let params = getParams($argument); 17 | let group=params.group; 18 | let proxy = await httpAPI("/v1/policy_groups"); 19 | let groupName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(group)+"")).policy; 20 | var proxyName= []; 21 | let arr = proxy[""+group+""]; 22 | let allGroup = []; 23 | 24 | for (var key in proxy){ 25 | allGroup.push(key) 26 | } 27 | 28 | 29 | for (let i = 0; i < arr.length; ++i) { 30 | proxyName.push(arr[i].name); 31 | } 32 | 33 | let index; 34 | 35 | for(let i = 0;i < proxyName.length; ++i) { 36 | if(groupName==proxyName[i]){ 37 | index=i 38 | } 39 | }; 40 | 41 | if($trigger == "button"){ 42 | index += 1; 43 | 44 | if(index>arr.length-1){ 45 | index = 0; 46 | } 47 | $surge.setSelectGroupPolicy(group, proxyName[index]); 48 | 49 | }; 50 | 51 | let name =proxyName[index]; 52 | let secondName; 53 | let rootName = name; 54 | if(allGroup.includes(rootName)==true){ 55 | secondName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(rootName)+"")).policy; 56 | name = name + ' ➟ ' + secondName 57 | } 58 | 59 | while(allGroup.includes(rootName)==true){ 60 | rootName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(rootName)+"")).policy; 61 | } 62 | 63 | if(arr[index].isGroup==true && secondName!= rootName){ 64 | name=name + ' ➟ ' + rootName; 65 | } 66 | 67 | $done({ 68 | title:group, 69 | content:name, 70 | icon: params.icon, 71 | "icon-color":params.color 72 | }); 73 | })(); 74 | 75 | 76 | function httpAPI(path = "", method = "GET", body = null) { 77 | return new Promise((resolve) => { 78 | $httpAPI(method, path, body, (result) => { 79 | resolve(result); 80 | }); 81 | }); 82 | }; 83 | 84 | function getParams(param) { 85 | return Object.fromEntries( 86 | $argument 87 | .split("&") 88 | .map((item) => item.split("=")) 89 | .map(([k, v]) => [k, decodeURIComponent(v)]) 90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /PanelScripts/grouppanel.js: -------------------------------------------------------------------------------- 1 | /* 2 | [Script] 3 | groupPanel = type=generic,timeout=10,script-path= https://raw.githubusercontent.com/fishingworld/something/main/groupPanel.js,argument=icon=network&color=#86abee&group=Master 4 | 对应参数: 5 | icon:图标 6 | color:图标颜色 7 | group:策略组名称 8 | [Panel] 9 | groupPanel = script-name=groupPanel,update-interval=5 10 | */ 11 | 12 | 13 | 14 | ;(async () => { 15 | 16 | let params = getParams($argument); 17 | let group=params.group; 18 | let proxy = await httpAPI("/v1/policy_groups"); 19 | let groupName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(group)+"")).policy; 20 | var proxyName= []; 21 | let arr = proxy[""+group+""]; 22 | let allGroup = []; 23 | 24 | for (var key in proxy){ 25 | allGroup.push(key) 26 | } 27 | 28 | 29 | for (let i = 0; i < arr.length; ++i) { 30 | proxyName.push(arr[i].name); 31 | } 32 | 33 | let index; 34 | 35 | for(let i = 0;i < proxyName.length; ++i) { 36 | if(groupName==proxyName[i]){ 37 | index=i 38 | } 39 | }; 40 | 41 | if($trigger == "button"){ 42 | index += 1; 43 | 44 | if(index>arr.length-1){ 45 | index = 0; 46 | } 47 | $surge.setSelectGroupPolicy(group, proxyName[index]); 48 | 49 | }; 50 | 51 | let name =proxyName[index]; 52 | let secondName; 53 | let rootName = name; 54 | if(allGroup.includes(rootName)==true){ 55 | secondName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(rootName)+"")).policy; 56 | name = name + ' ➟ ' + secondName 57 | } 58 | 59 | while(allGroup.includes(rootName)==true){ 60 | rootName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(rootName)+"")).policy; 61 | } 62 | 63 | if(arr[index].isGroup==true && secondName!= rootName){ 64 | name=name + ' ➟ ' + rootName; 65 | } 66 | 67 | $done({ 68 | title:group, 69 | content:name, 70 | icon: params.icon, 71 | "icon-color":params.color 72 | }); 73 | })(); 74 | 75 | 76 | function httpAPI(path = "", method = "GET", body = null) { 77 | return new Promise((resolve) => { 78 | $httpAPI(method, path, body, (result) => { 79 | resolve(result); 80 | }); 81 | }); 82 | }; 83 | 84 | function getParams(param) { 85 | return Object.fromEntries( 86 | $argument 87 | .split("&") 88 | .map((item) => item.split("=")) 89 | .map(([k, v]) => [k, decodeURIComponent(v)]) 90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /PanelScripts/trafficstatistics.js: -------------------------------------------------------------------------------- 1 | let params = getParams($argument) 2 | 3 | ;(async () => { 4 | 5 | let traffic = (await httpAPI("/v1/traffic")) 6 | let interface = traffic.interface 7 | 8 | /* 获取所有网络界面 */ 9 | let allNet = []; 10 | for (var key in interface){ 11 | allNet.push(key) 12 | } 13 | 14 | if(allNet.includes("lo0")==true){ 15 | del(allNet,"lo0") 16 | } 17 | 18 | let net; 19 | let index; 20 | if( $persistentStore.read("NETWORK")==null||allNet.includes($persistentStore.read("NETWORK"))==false){ 21 | index = 0 22 | }else{ 23 | net = $persistentStore.read("NETWORK") 24 | for(let i = 0;i < allNet.length; ++i) { 25 | if(net==allNet[i]){ 26 | index=i 27 | } 28 | } 29 | } 30 | 31 | /* 手动执行时切换网络界面 */ 32 | if($trigger == "button"){ 33 | if(allNet.length>1) index += 1 34 | if(index>=allNet.length) index = 0; 35 | $persistentStore.write(allNet[index],"NETWORK") 36 | }; 37 | 38 | net = allNet[index] 39 | let network = interface[net] 40 | 41 | let outCurrentSpeed = speedTransform(network.outCurrentSpeed) //上传速度 42 | let outMaxSpeed = speedTransform(network.outMaxSpeed) //最大上传速度 43 | let download = bytesToSize(network.in) //下载流量 44 | let upload = bytesToSize(network.out) //上传流量 45 | let inMaxSpeed = speedTransform(network.inMaxSpeed) //最大下载速度 46 | let inCurrentSpeed = speedTransform(network.inCurrentSpeed) //下载速度 47 | 48 | /* 判断网络类型 */ 49 | let netType; 50 | if(net=="en0") { 51 | netType = "WiFi" 52 | }else{ 53 | netType = "Cellular" 54 | } 55 | 56 | 57 | $done({ 58 | title:"流量统计 | "+netType, 59 | content:`流量 ➟ ${upload} | ${download}\n`+ 60 | `速度 ➟ ${outCurrentSpeed} | ${inCurrentSpeed}\n` + 61 | `峰值 ➟ ${outMaxSpeed} | ${inMaxSpeed}`, 62 | icon: params.icon, 63 | "icon-color":params.color 64 | }); 65 | 66 | })() 67 | 68 | function bytesToSize(bytes) { 69 | if (bytes === 0) return "0B"; 70 | let k = 1024; 71 | sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; 72 | let i = Math.floor(Math.log(bytes) / Math.log(k)); 73 | return (bytes / Math.pow(k, i)).toFixed(2) + " " + sizes[i]; 74 | } 75 | 76 | function speedTransform(bytes) { 77 | if (bytes === 0) return "0B/s"; 78 | let k = 1024; 79 | sizes = ["B/s", "KB/s", "MB/s", "GB/s", "TB/s", "PB/s", "EB/s", "ZB/s", "YB/s"]; 80 | let i = Math.floor(Math.log(bytes) / Math.log(k)); 81 | return (bytes / Math.pow(k, i)).toFixed(2) + " " + sizes[i]; 82 | } 83 | 84 | 85 | function httpAPI(path = "", method = "GET", body = null) { 86 | return new Promise((resolve) => { 87 | $httpAPI(method, path, body, (result) => { 88 | 89 | resolve(result); 90 | }); 91 | }); 92 | }; 93 | 94 | 95 | function getParams(param) { 96 | return Object.fromEntries( 97 | $argument 98 | .split("&") 99 | .map((item) => item.split("=")) 100 | .map(([k, v]) => [k, decodeURIComponent(v)]) 101 | ); 102 | } 103 | 104 | function del(arr,num) { 105 | var l=arr.length; 106 | for (var i = 0; i < l; i++) { 107 | if (arr[0]!==num) { 108 | arr.push(arr[0]); 109 | } 110 | arr.shift(arr[0]); 111 | } 112 | return arr; 113 | } 114 | -------------------------------------------------------------------------------- /Panels.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=Panels 2 | #!desc=信息面板 3 | 4 | [Panel] 5 | #Surge Pro标题,可显示启动时间,点击刷新为重载配置 6 | SurgePro_ReloadProfile = script-name=SurgePro_ReloadProfile,update-interval=1 7 | 8 | #流量统计 9 | TrafficStatistics = script-name=TrafficStatistics,update-interval=1 10 | 11 | #网络详情 12 | NET_info = script-name=NET_info,update-interval=1 13 | 14 | #机场信息 15 | Sub_info = script-name=Sub_info,update-interval = 43200 16 | 17 | #Disney+策略组自动控制 18 | DisneySelect = script-name=DisneySelecter, update-interval=3600 19 | 20 | #Netflix策略组自动控制 21 | NetflixSelect = script-name=NetflixSelect, update-interval=3600 22 | 23 | #策略组面板 可重复配置 注意修改相应字段 24 | groupPanel= script-name=groupPanel,update-interval=5 25 | 26 | 27 | [Script] 28 | #附带重载配置,Surge Pro标题,可显示启动时间,点击刷新为重载配置 29 | SurgePro_ReloadProfile = type=generic,timeout=10,script-path= https://raw.githubusercontent.com/fishingworld/something/main/PanelScripts/surgepro_reloadprofile.js ,argument=icon=crown.fill&color=#f6c970 30 | 31 | #流量统计 点击以切换网络界面 32 | TrafficStatistics = type=generic,timeout=10,script-path= https://raw.githubusercontent.com/fishingworld/something/main/PanelScripts/trafficstatistics.js ,argument=icon=arrow.up.arrow.down.circle&color=#5d84f8 33 | 34 | #網路詳情 标题显示为根节点名 35 | #应当修改的字段 group 代理策略组名称 36 | NET_info = type=generic,timeout=10,script-path=https://raw.githubusercontent.com/fishingworld/something/main/PanelScripts/net_info.js,argument=icon=externaldrive.connected.to.line.below&color=#9a7ff7&group=Master 37 | 38 | #机场信息 39 | #必须修改的字段:你encode后的机场订阅链接 40 | Sub_info = type=generic,timeout=10,script-path=https://raw.githubusercontent.com/mieqq/mieqq/master/sub_info_panel.js ,script-update-interval=0,argument=url=[URL encode 后的机场节点链接]&reset_day=1&title=ExFlux&icon=opticaldisc&color=#5AC8FA 41 | 42 | #disney+策略组控制 43 | #应当修改的字段 disneyGroup Disney+的策略组名称 44 | #详情请阅读:https://github.com/fishingworld/something/tree/main/DisneySelect 45 | DisneySelecter = type=generic, script-path=https://raw.githubusercontent.com/fishingworld/something/main/DisneySelect/disney_selecter.js, argument=icon1=checkmark.circle&color1=#55ba94&icon2=cursorarrow.click.badge.clock&color2=#ed6c84&icon3=xmark.shield&color3=#AF52DE&disneyGroup=Disney+ 46 | DisneyChecker = type=cron,cronexp=35 4 * * *,wake-system=1,timeout=3600,script-path=https://raw.githubusercontent.com/fishingworld/something/main/DisneySelect/disney_checker.js,script-update-interval=86400,control-api=1 47 | 48 | #netflix策略组控制 49 | #必须更改的字段 netflixGroup 填写你Netflix策略组名称 50 | #详情请阅读:https://github.com/fishingworld/something/blob/main/NetflixSelect/README.md 51 | NetflixSelect = type=generic, script-path=https://raw.githubusercontent.com/fishingworld/something/main/NetflixSelect/nf_autoselect.js, argument=icon1=checkmark.circle&color1=#55ba94&icon2=checkmark.circle.trianglebadge.exclamationmark&color2=#9a9ced&icon3=hand.raised.circle&color3=#ea5532&netflixGroup=Netflix 52 | NetflixChecker = type=cron,cronexp=5 4 * * *,wake-system=1,timeout=3600,script-path=https://raw.githubusercontent.com/fishingworld/something/main/NetflixSelect/nf_autocheck.js ,script-update-interval=0,control-api=1 53 | 54 | #策略组面板 可重复配置 注意修改相应字段 55 | #必须更改的字段:group 填写需要显示的策略组名称 56 | groupPanel = type=generic,timeout=10,script-path=https://raw.githubusercontent.com/fishingworld/something/main/groupPanel.js ,argument=icon=network&color=#86abee&group=Master 57 | -------------------------------------------------------------------------------- /NetflixSelect/nf_autocheck.js: -------------------------------------------------------------------------------- 1 | /* 脚本经本人测试已经可以正常运行,但仍可能存在bug,使用过程中遇到障碍请联系Telegram:https://t.me/okmytg 2 | 脚本说明: 3 | 1:本脚本修改自 @Helge_0x00 4 | 2:本脚本用于遍历Netflix策略组,以获取节点列表 5 | 3:本脚本与姊妹脚本nf_autoselect相互依赖,你应当优先执行一次panel脚本,且必须手动运行一次本脚本以获取节点列表 6 | */ 7 | 8 | const FILM_ID = 81215567 9 | const AREA_TEST_FILM_ID = 80018499 10 | 11 | 12 | ;(async () => { 13 | let netflixGroup = $persistentStore.read("NFGroupName") 14 | let proxy = await httpAPI("/v1/policy_groups"); 15 | let groupName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(netflixGroup)+"")).policy; 16 | let first = groupName; 17 | var proxyName= [];//netflix节点组名称 18 | let arr = proxy[""+netflixGroup+""]; 19 | for (let i = 0; i < arr.length; ++i) { 20 | proxyName.push(arr[i].name); 21 | } 22 | 23 | 24 | /** 25 | * 遍历测试节点组 26 | */ 27 | 28 | var fullUnlock=[]; 29 | var onlyOriginal=[]; 30 | 31 | for (let i = 0; i < proxyName.length; ++i) { 32 | //切换节点 33 | $surge.setSelectGroupPolicy(netflixGroup, proxyName[i]); 34 | //等待 35 | await timeout(1000).catch(() => {}) 36 | //执行测试 37 | 38 | let { status, regionCode, policyName } = await testPolicy(proxyName[i]); 39 | let newStatus=status 40 | /* 检测超时 再测一次 */ 41 | if(status <0) { 42 | console.log(proxyName[i]+": 连接超时了,再测一次") 43 | await timeout(1000).catch(() => {}) 44 | let { status, regionCode, policyName } = await testPolicy(proxyName[i]); 45 | newStatus=status 46 | } 47 | //填充数据 48 | status = newStatus 49 | console.log("检测结果:"+proxyName[i]+" | "+statusName(status)) 50 | 51 | if(status===2){ 52 | if(fullUnlock.includes(proxyName[i])==false){ 53 | fullUnlock.push(proxyName[i]) 54 | } 55 | }else if(status===1){ 56 | if(onlyOriginal.includes(proxyName[i])==false){ 57 | onlyOriginal.push(proxyName[i]) 58 | } 59 | } 60 | } 61 | 62 | //去除杂项 63 | for (let i = 0; i < fullUnlock.length; ++i){ 64 | if(onlyOriginal.includes(fullUnlock[i])==true){ 65 | fullUnlock.splice(fullUnlock.indexOf(fullUnlock[i]), 1) 66 | } 67 | } 68 | 69 | for (let i = 0; i < onlyOriginal.length; ++i){ 70 | if(fullUnlock.includes(onlyOriginal[i])==true){ 71 | onlyOriginal.splice(onlyOriginal.indexOf(onlyOriginal[i]), 1) 72 | } 73 | } 74 | 75 | // 创建持久化数据 76 | $persistentStore.write(fullUnlock.toString(),"fullUnlockNetflix"); 77 | $persistentStore.write(onlyOriginal.toString(),"onlyOriginalNetflix") 78 | 79 | 80 | //打印测试结果 81 | console.log("全解锁:"+fullUnlock.sort()) 82 | console.log("仅自制:"+onlyOriginal.sort()) 83 | 84 | //设定策略选项为初始值 85 | $surge.setSelectGroupPolicy(netflixGroup, first); 86 | 87 | $done() 88 | 89 | })(); 90 | 91 | 92 | 93 | 94 | 95 | function httpAPI(path = "", method = "GET", body = null) { 96 | return new Promise((resolve) => { 97 | $httpAPI(method, path, body, (result) => { 98 | resolve(result); 99 | }); 100 | }); 101 | }; 102 | 103 | async function testPolicy(policyName) { 104 | try { 105 | const regionCode = await Promise.race([testFilm(FILM_ID), timeout(3000)]) 106 | return { status: 2, regionCode, policyName } 107 | } catch (error) { 108 | if (error === 'Not Found') { 109 | return { status: 1, policyName } 110 | } 111 | if (error === 'Not Available') { 112 | return { status: 0, policyName } 113 | } 114 | console.log(error) 115 | return { status: -1, policyName } 116 | } 117 | } 118 | 119 | /** 120 | * 测试是否解锁 121 | */ 122 | function testFilm(filmId) { 123 | return new Promise((resolve, reject) => { 124 | let option = { 125 | url: `https://www.netflix.com/title/${filmId}`, 126 | headers: { 127 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36', 128 | }, 129 | } 130 | $httpClient.get(option, function (error, response, data) { 131 | if (error != null) { 132 | reject(error) 133 | return 134 | } 135 | 136 | if (response.status === 403) { 137 | reject('Not Available') 138 | return 139 | } 140 | 141 | if (response.status === 404) { 142 | reject('Not Found') 143 | return 144 | } 145 | 146 | if (response.status === 200) { 147 | let url = response.headers['x-originating-url'] 148 | let region = url.split('/')[3] 149 | region = region.split('-')[0] 150 | if (region == 'title') { 151 | region = 'us' 152 | } 153 | resolve(region.toUpperCase()) 154 | return 155 | } 156 | 157 | reject('Error') 158 | }) 159 | }) 160 | } 161 | 162 | function timeout(delay = 5000) { 163 | return new Promise((resolve, reject) => { 164 | setTimeout(() => { 165 | reject('Timeout') 166 | }, delay) 167 | }) 168 | } 169 | 170 | function statusName(status) { 171 | return status==2 ? "全解锁" 172 | : status==1 ? "仅自制" 173 | : status==0 ? "不解锁" 174 | : status==-1 ? "检测超时" 175 | : "检测失败"; 176 | } 177 | 178 | -------------------------------------------------------------------------------- /YouTubeSelect/youtubechecker.js: -------------------------------------------------------------------------------- 1 | /* 脚本经本人测试已经可以正常运行,但仍可能存在bug,使用过程中遇到障碍请联系Telegram:https://t.me/okmytg 2 | 脚本说明:https://github.com/fishingworld/something/tree/main/YouTubeSelect 3 | */ 4 | 5 | const BASE_URL = 'https://www.youtube.com/premium' 6 | 7 | ; (async () => { 8 | //测试当前状态 9 | let { status, region } = await testGoogle() 10 | let newStatus = status 11 | let reg = region 12 | if (newStatus < 1) { 13 | console.log("连接超时了,再测一次") 14 | await timeout(1000).catch(() => { }) 15 | let { status, region } = await testGoogle() 16 | newStatus = status 17 | reg = region 18 | } 19 | if (newStatus === 2) { 20 | console.log("当前节点仍可用 退出检测") 21 | } else { 22 | 23 | let youtubeGroup = $persistentStore.read("YOUTUBEGROUP") 24 | let proxy = await httpAPI("/v1/policy_groups"); 25 | let groupName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(youtubeGroup) + "")).policy; 26 | 27 | let allGroup = []; 28 | for (var key in proxy) { 29 | allGroup.push(key) 30 | } 31 | var regData 32 | if ($persistentStore.read("YOUTUBEREG") == null) { 33 | regData = {} 34 | } else { 35 | regData = JSON.parse($persistentStore.read("YOUTUBEREG")) 36 | } 37 | 38 | let dataname; 39 | var isOK = [] 40 | var selectOK = [] 41 | 42 | if ($persistentStore.read("YOUTUBEISOK") == null) { 43 | } else { 44 | //读取持久化数据 45 | isOK = $persistentStore.read("YOUTUBEISOK").split(","); 46 | //清除空值 47 | del(isOK, "") 48 | } 49 | 50 | /* 测试当选策略组节点状态并记录数据 */ 51 | 52 | var selectName = [] 53 | let select = proxy["" + groupName + ""]; 54 | for (let i = 0; i < select.length; ++i) { 55 | selectName.push(select[i].name); 56 | } 57 | //去除历史数据 58 | for (let i = 0; i < selectName.length; ++i) { 59 | if (isOK.includes(selectName[i]) == true) { 60 | del(isOK, selectName[i]) 61 | } 62 | } 63 | 64 | //遍历检测当选策略 65 | console.log("当前检测:" + groupName) 66 | for (let i = 0; i < selectName.length; ++i) { 67 | //切换节点 68 | $surge.setSelectGroupPolicy(groupName, selectName[i]); 69 | //等待 70 | await timeout(1000).catch(() => { }) 71 | //执行测试 72 | let { status, region } = await testGoogle() 73 | newStatus = status 74 | reg = region 75 | /* 检测超时 再测一次 */ 76 | if (newStatus < 1) { 77 | console.log(selectName[i] + ": 连接超时了,再测一次") 78 | await timeout(1000).catch(() => { }) 79 | let { status, region } = await testGoogle() 80 | newStatus = status 81 | reg = region 82 | } 83 | console.log("检测结果:" + selectName[i] + " | " + statusName(newStatus)) 84 | 85 | //填充数据 86 | dataname = selectName[i] 87 | regData[dataname] = reg 88 | if (newStatus === 2) { 89 | if (isOK.includes(selectName[i]) == false) { 90 | isOK.push(selectName[i]) 91 | selectOK.push(selectName[i]) 92 | } 93 | } 94 | 95 | //找到全解锁节点 退出检测 96 | if (newStatus === 2) { 97 | console.log("找到可用节点 退出检测") 98 | break; 99 | } 100 | } 101 | 102 | //设定节点 103 | if (selectOK.length > 0) { 104 | $surge.setSelectGroupPolicy(groupName, selectOK[0]); 105 | } else { 106 | $surge.setSelectGroupPolicy(groupName, selectName[0]); 107 | } 108 | 109 | // 创建持久化数据 110 | 111 | $persistentStore.write(isOK.toString(), "YOUTUBEISOK"); 112 | $persistentStore.write(JSON.stringify(regData), "YOUTUBEREG") 113 | 114 | //打印测试结果 115 | console.log("支持的:" + isOK.sort()) 116 | } 117 | 118 | $done() 119 | 120 | })() 121 | 122 | async function testGoogle() { 123 | try { 124 | const region = await Promise.race([test(), timeout(3000)]) 125 | return { status: 2, region } 126 | } catch (error) { 127 | if (error === 'Not Available') { 128 | return { status: 1 } 129 | } 130 | console.log(error) 131 | return { status: 0 } 132 | } 133 | } 134 | 135 | function test() { 136 | return new Promise((resolve, reject) => { 137 | let option = { 138 | url: BASE_URL, 139 | headers: { 140 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36', 141 | 'Accept-Language': 'en', 142 | }, 143 | } 144 | $httpClient.get(option, function (error, response, data) { 145 | if (error != null || response.status !== 200) { 146 | reject('Error') 147 | return 148 | } 149 | 150 | if (data.indexOf('Premium is not available in your country') !== -1) { 151 | reject('Not Available') 152 | return 153 | } 154 | 155 | let region = '' 156 | let re = new RegExp('"countryCode":"(.*?)"', 'gm') 157 | let result = re.exec(data) 158 | if (result != null && result.length === 2) { 159 | region = result[1] 160 | } else if (data.indexOf('www.google.cn') !== -1) { 161 | region = 'CN' 162 | } else { 163 | region = 'US' 164 | } 165 | resolve(region.toUpperCase()) 166 | }) 167 | }) 168 | } 169 | 170 | function timeout(delay = 5000) { 171 | return new Promise((resolve, reject) => { 172 | setTimeout(() => { 173 | reject('Timeout') 174 | }, delay) 175 | }) 176 | } 177 | 178 | function httpAPI(path = "", method = "GET", body = null) { 179 | return new Promise((resolve) => { 180 | $httpAPI(method, path, body, (result) => { 181 | resolve(result); 182 | }); 183 | }); 184 | }; 185 | 186 | function statusName(status) { 187 | return status == 2 ? "支持的" 188 | : status == 1 ? "被封禁" 189 | : status == 0 ? "检测超时" 190 | : "检测异常"; 191 | } 192 | 193 | function del(arr, num) { 194 | var l = arr.length; 195 | for (var i = 0; i < l; i++) { 196 | if (arr[0] !== num) { 197 | arr.push(arr[0]); 198 | } 199 | arr.shift(arr[0]); 200 | } 201 | return arr; 202 | } 203 | -------------------------------------------------------------------------------- /YouTubeSelect/youtubeshortcut.js: -------------------------------------------------------------------------------- 1 | /* 脚本经本人测试已经可以正常运行,但仍可能存在bug,使用过程中遇到障碍请联系Telegram:https://t.me/okmytg 2 | 脚本说明:https://github.com/fishingworld/something/tree/main/YouTubeSelect 3 | */ 4 | 5 | const BASE_URL = 'https://www.youtube.com/premium' 6 | 7 | ; (async () => { 8 | 9 | let youtubeGroup = $persistentStore.read("YOUTUBEGROUP") 10 | let proxy = await httpAPI("/v1/policy_groups"); 11 | let groupName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(youtubeGroup) + "")).policy; 12 | 13 | let allGroup = []; 14 | for (var key in proxy) { 15 | allGroup.push(key) 16 | } 17 | 18 | //测试当前状态 19 | let { status, region } = await testGoogle() 20 | let newStatus = status 21 | let reg = region 22 | if (newStatus < 1) { 23 | console.log("连接超时了,再测一次") 24 | await timeout(1000).catch(() => { }) 25 | let { status, region } = await testGoogle() 26 | newStatus = status 27 | reg = region 28 | } 29 | if (newStatus === 2) { 30 | console.log("当前节点仍可用 退出检测") 31 | } else { 32 | 33 | 34 | var regData 35 | if ($persistentStore.read("YOUTUBEREG") == null) { 36 | regData = {} 37 | } else { 38 | regData = JSON.parse($persistentStore.read("YOUTUBEREG")) 39 | } 40 | 41 | let dataname; 42 | var isOK = [] 43 | var selectOK = [] 44 | 45 | if ($persistentStore.read("YOUTUBEISOK") == null) { 46 | } else { 47 | //读取持久化数据 48 | isOK = $persistentStore.read("YOUTUBEISOK").split(","); 49 | //清除空值 50 | del(isOK, "") 51 | } 52 | 53 | /* 测试当选策略组节点状态并记录数据 */ 54 | 55 | var selectName = [] 56 | let select = proxy["" + groupName + ""]; 57 | for (let i = 0; i < select.length; ++i) { 58 | selectName.push(select[i].name); 59 | } 60 | //去除历史数据 61 | for (let i = 0; i < selectName.length; ++i) { 62 | if (isOK.includes(selectName[i]) == true) { 63 | del(isOK, selectName[i]) 64 | } 65 | } 66 | 67 | //遍历检测当选策略 68 | console.log("当前检测:" + groupName) 69 | for (let i = 0; i < selectName.length; ++i) { 70 | //切换节点 71 | $surge.setSelectGroupPolicy(groupName, selectName[i]); 72 | //等待 73 | await timeout(1000).catch(() => { }) 74 | //执行测试 75 | let { status, region } = await testGoogle() 76 | newStatus = status 77 | reg = region 78 | /* 检测超时 再测一次 */ 79 | if (newStatus < 1) { 80 | console.log(selectName[i] + ": 连接超时了,再测一次") 81 | await timeout(1000).catch(() => { }) 82 | let { status, region } = await testGoogle() 83 | newStatus = status 84 | reg = region 85 | } 86 | console.log("检测结果:" + selectName[i] + " | " + statusName(newStatus)) 87 | 88 | //填充数据 89 | dataname = selectName[i] 90 | regData[dataname] = reg 91 | if (newStatus === 2) { 92 | if (isOK.includes(selectName[i]) == false) { 93 | isOK.push(selectName[i]) 94 | selectOK.push(selectName[i]) 95 | } 96 | } 97 | 98 | //找到全解锁节点 退出检测 99 | if (newStatus === 2) { 100 | console.log("找到可用节点 退出检测") 101 | break; 102 | } 103 | } 104 | 105 | //设定节点 106 | if (selectOK.length > 0) { 107 | $surge.setSelectGroupPolicy(groupName, selectOK[0]); 108 | } else { 109 | $surge.setSelectGroupPolicy(groupName, selectName[0]); 110 | } 111 | 112 | // 创建持久化数据 113 | 114 | $persistentStore.write(isOK.toString(), "YOUTUBEISOK"); 115 | $persistentStore.write(JSON.stringify(regData), "YOUTUBEREG") 116 | 117 | //打印测试结果 118 | console.log("支持的:" + isOK.sort()) 119 | } 120 | 121 | //获取根节点名 122 | let rootName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(youtubeGroup) + "")).policy; 123 | while (allGroup.includes(rootName) == true) { 124 | rootName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(rootName) + "")).policy; 125 | } 126 | 127 | let info 128 | if (newStatus === 2) { 129 | info = `已选定节点: ${rootName} | ${statusName(newStatus)} | ${reg}` 130 | } else if (statusData[rootName] == 1) { 131 | info = `节点异常 你可能无法使用会员权益` 132 | } 133 | 134 | $notification.post("YouTube檢測", info, "") 135 | 136 | 137 | $done() 138 | 139 | })() 140 | 141 | async function testGoogle() { 142 | try { 143 | const region = await Promise.race([test(), timeout(3000)]) 144 | return { status: 2, region } 145 | } catch (error) { 146 | if (error === 'Not Available') { 147 | return { status: 1 } 148 | } 149 | console.log(error) 150 | return { status: 0 } 151 | } 152 | } 153 | 154 | function test() { 155 | return new Promise((resolve, reject) => { 156 | let option = { 157 | url: BASE_URL, 158 | headers: { 159 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36', 160 | 'Accept-Language': 'en', 161 | }, 162 | } 163 | $httpClient.get(option, function (error, response, data) { 164 | if (error != null || response.status !== 200) { 165 | reject('Error') 166 | return 167 | } 168 | 169 | if (data.indexOf('Premium is not available in your country') !== -1) { 170 | reject('Not Available') 171 | return 172 | } 173 | 174 | let region = '' 175 | let re = new RegExp('"countryCode":"(.*?)"', 'gm') 176 | let result = re.exec(data) 177 | if (result != null && result.length === 2) { 178 | region = result[1] 179 | } else if (data.indexOf('www.google.cn') !== -1) { 180 | region = 'CN' 181 | } else { 182 | region = 'US' 183 | } 184 | resolve(region.toUpperCase()) 185 | }) 186 | }) 187 | } 188 | 189 | function timeout(delay = 5000) { 190 | return new Promise((resolve, reject) => { 191 | setTimeout(() => { 192 | reject('Timeout') 193 | }, delay) 194 | }) 195 | } 196 | 197 | function httpAPI(path = "", method = "GET", body = null) { 198 | return new Promise((resolve) => { 199 | $httpAPI(method, path, body, (result) => { 200 | resolve(result); 201 | }); 202 | }); 203 | }; 204 | 205 | function statusName(status) { 206 | return status == 2 ? "支持的" 207 | : status == 1 ? "被封禁" 208 | : status == 0 ? "检测超时" 209 | : "检测异常"; 210 | } 211 | 212 | function del(arr, num) { 213 | var l = arr.length; 214 | for (var i = 0; i < l; i++) { 215 | if (arr[0] !== num) { 216 | arr.push(arr[0]); 217 | } 218 | arr.shift(arr[0]); 219 | } 220 | return arr; 221 | } 222 | -------------------------------------------------------------------------------- /DisneySelect/disney_checker.js: -------------------------------------------------------------------------------- 1 | /* 脚本经本人测试已经可以正常运行,但仍可能存在bug,使用过程中遇到障碍请联系Telegram:https://t.me/okmytg 2 | 脚本说明: 3 | 1:本脚本修改自 @Helge_0x00 4 | 2:本脚本用于遍历Disney策略组,以获取节点列表 5 | 3:本脚本与姊妹脚本disney_selecter相互依赖,你必须手动运行一次本脚本以获取节点列表 6 | */ 7 | 8 | const AUTHORIZATION = 'Bearer ZGlzbmV5JmJyb3dzZXImMS4wLjA.Cu56AgSfBTDag5NiRA81oLHkDZfu5L3CKadnefEAY84' 9 | const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36' 10 | 11 | // 即将登陆 12 | const STATUS_COMING = 2 13 | // 支持解锁 14 | const STATUS_AVAILABLE = 1 15 | // 不支持解锁 16 | const STATUS_NOT_AVAILABLE = 0 17 | // 检测超时 18 | const STATUS_TIMEOUT = -1 19 | // 检测异常 20 | const STATUS_ERROR = -2 21 | 22 | ;(async () => { 23 | let disneyGroup = $persistentStore.read("DISNEYGROUP") 24 | let proxy = await httpAPI("/v1/policy_groups"); 25 | let groupName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(disneyGroup)+"")).policy; 26 | let first = groupName; 27 | var proxyName= [];//Disney节点组名称 28 | let arr = proxy[""+disneyGroup+""]; 29 | for (let i = 0; i < arr.length; ++i) { 30 | proxyName.push(arr[i].name); 31 | } 32 | 33 | /** 34 | * 遍历测试节点组 35 | */ 36 | var unlocked = []; 37 | for (let i = 0; i < proxyName.length; ++i) { 38 | /* 切换节点 */ 39 | $surge.setSelectGroupPolicy(disneyGroup, proxyName[i]); 40 | //等待 41 | await timeout(1000).catch(() => {}) 42 | //执行测试 43 | let { region, status } = await testDisneyPlus() 44 | let newStatus=status 45 | /* 检测超时 再测一次 */ 46 | if(status <0) { 47 | console.log(proxyName[i]+": 连接超时了,再测一次") 48 | await timeout(1000).catch(() => {}) 49 | let { region, status } = await testDisneyPlus() 50 | newStatus=status 51 | } 52 | 53 | /* 填充数据 */ 54 | status = newStatus 55 | if(status==1){ 56 | if(unlocked.includes(proxyName[i])==false){ 57 | unlocked.push(proxyName[i]) 58 | console.log("可解锁: "+ proxyName[i]+" | "+status) 59 | } 60 | }else if(status==2){ 61 | console.log("即将登陆: "+ proxyName[i]+" | "+status) 62 | } 63 | } 64 | 65 | 66 | 67 | /* 创建持久化数据 */ 68 | $persistentStore.write(unlocked.toString(),"unlockedDisney"); 69 | console.log("可解锁"+unlocked.sort()) 70 | //设定策略选项为初始值 71 | $surge.setSelectGroupPolicy(disneyGroup, first); 72 | 73 | $done() 74 | 75 | 76 | })() 77 | 78 | async function testDisneyPlus() { 79 | try { 80 | let { region, cnbl } = await Promise.race([testHomePage(), timeout(3000)]) 81 | 82 | // 即将登陆 83 | if (cnbl == 2) { 84 | return { region, status: STATUS_COMING } 85 | } 86 | 87 | let { countryCode, inSupportedLocation } = await Promise.race([getLocationInfo(), timeout(1000)]) 88 | 89 | 90 | region = countryCode ?? region 91 | // 即将登陆 92 | if (inSupportedLocation === false || inSupportedLocation === 'false') { 93 | return { region, status: STATUS_COMING } 94 | } 95 | 96 | // 支持解锁 97 | return { region, status: STATUS_AVAILABLE } 98 | } catch (error) { 99 | console.log(error) 100 | 101 | // 不支持解锁 102 | if (error === 'Not Available') { 103 | return { status: STATUS_NOT_AVAILABLE } 104 | } 105 | 106 | // 检测超时 107 | if (error === 'Timeout') { 108 | return { status: STATUS_TIMEOUT } 109 | } 110 | 111 | return { status: STATUS_ERROR } 112 | } 113 | } 114 | 115 | 116 | 117 | function getLocationInfo() { 118 | return new Promise((resolve, reject) => { 119 | let opts = { 120 | url: 'https://disney.api.edge.bamgrid.com/graph/v1/device/graphql', 121 | headers: { 122 | 'Accept-Language': 'en', 123 | Authorization: 'ZGlzbmV5JmJyb3dzZXImMS4wLjA.Cu56AgSfBTDag5NiRA81oLHkDZfu5L3CKadnefEAY84', 124 | 'Content-Type': 'application/json', 125 | 'User-Agent': UA, 126 | }, 127 | body: JSON.stringify({ 128 | query: 'mutation registerDevice($input: RegisterDeviceInput!) { registerDevice(registerDevice: $input) { grant { grantType assertion } } }', 129 | variables: { 130 | input: { 131 | applicationRuntime: 'chrome', 132 | attributes: { 133 | browserName: 'chrome', 134 | browserVersion: '94.0.4606', 135 | manufacturer: 'microsoft', 136 | model: null, 137 | operatingSystem: 'windows', 138 | operatingSystemVersion: '10.0', 139 | osDeviceIds: [], 140 | }, 141 | deviceFamily: 'browser', 142 | deviceLanguage: 'en', 143 | deviceProfile: 'windows', 144 | }, 145 | }, 146 | }), 147 | } 148 | 149 | $httpClient.post(opts, function (error, response, data) { 150 | if (error) { 151 | reject('Error') 152 | return 153 | } 154 | 155 | if (response.status !== 200) { 156 | reject('Not Available') 157 | return 158 | } 159 | 160 | let { 161 | inSupportedLocation, 162 | location: { countryCode }, 163 | } = JSON.parse(data)?.extensions?.sdk?.session 164 | resolve({ inSupportedLocation, countryCode }) 165 | }) 166 | }) 167 | } 168 | 169 | function testHomePage() { 170 | return new Promise((resolve, reject) => { 171 | let opts = { 172 | url: 'https://www.disneyplus.com/', 173 | headers: { 174 | 'Accept-Language': 'en', 175 | 'User-Agent': UA, 176 | }, 177 | } 178 | 179 | $httpClient.get(opts, function (error, response, data) { 180 | if (error) { 181 | reject('Error') 182 | return 183 | } 184 | if (response.status !== 200 || data.indexOf('unavailable') !== -1) { 185 | reject('Not Available') 186 | return 187 | } 188 | 189 | let match = data.match(/Region: ([A-Za-z]{2})[\s\S]*?CNBL: ([12])/) 190 | if (!match) { 191 | resolve({ region: '', cnbl: '' }) 192 | return 193 | } 194 | 195 | let region = match[1] 196 | let cnbl = match[2] 197 | resolve({ region, cnbl }) 198 | }) 199 | }) 200 | } 201 | 202 | function timeout(delay = 5000) { 203 | return new Promise((resolve, reject) => { 204 | setTimeout(() => { 205 | reject('Timeout') 206 | }, delay) 207 | }) 208 | } 209 | 210 | 211 | function replaceRegionPlaceholder(content, region) { 212 | let result = content 213 | 214 | if (result.indexOf('#REGION_CODE#') !== -1) { 215 | result = result.replaceAll('#REGION_CODE#', region.toUpperCase()) 216 | } 217 | if (result.indexOf('#REGION_FLAG#') !== -1) { 218 | result = result.replaceAll('#REGION_FLAG#', getCountryFlagEmoji(region.toUpperCase())) 219 | } 220 | 221 | if (result.indexOf('#REGION_NAME#') !== -1) { 222 | result = result.replaceAll('#REGION_NAME#', RESION_NAMES?.[region.toUpperCase()]?.chinese ?? '') 223 | } 224 | 225 | if (result.indexOf('#REGION_NAME_EN#') !== -1) { 226 | result = result.replaceAll('#REGION_NAME_EN#', RESION_NAMES?.[region.toUpperCase()]?.english ?? '') 227 | } 228 | 229 | return result 230 | } 231 | 232 | function httpAPI(path = "", method = "GET", body = null) { 233 | return new Promise((resolve) => { 234 | $httpAPI(method, path, body, (result) => { 235 | resolve(result); 236 | }); 237 | }); 238 | }; 239 | 240 | -------------------------------------------------------------------------------- /NetflixSelect/netflixchecker.js: -------------------------------------------------------------------------------- 1 | /* 脚本经本人测试已经可以正常运行,但仍可能存在bug,使用过程中遇到障碍请联系Telegram:https://t.me/okmytg 2 | 脚本说明:https://github.com/fishingworld/something/blob/main/NetflixSelect/README.md 3 | */ 4 | 5 | const FILM_ID = 81215567 6 | const AREA_TEST_FILM_ID = 80018499 7 | 8 | 9 | ; (async () => { 10 | 11 | //测试当前状态 12 | let { status, regionCode } = await testPolicy(); 13 | let newStatus = status 14 | let reg = regionCode 15 | if (status < 0) { 16 | console.log("连接超时了,再测一次") 17 | await timeout(1000).catch(() => { }) 18 | let { status, regionCode } = await testPolicy(); 19 | newStatus = status 20 | reg = regionCode 21 | } 22 | if (newStatus === 2) { 23 | console.log("当前节点仍可用 退出检测") 24 | } else { 25 | 26 | 27 | let netflixGroup = $persistentStore.read("NFGroupName") 28 | let proxy = await httpAPI("/v1/policy_groups"); 29 | let groupName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(netflixGroup) + "")).policy; 30 | 31 | let allGroup = []; 32 | for (var key in proxy) { 33 | allGroup.push(key) 34 | } 35 | var data 36 | if ($persistentStore.read("NFREGIONCODE") == null) { 37 | data = {} 38 | } else { 39 | data = JSON.parse($persistentStore.read("NFREGIONCODE")) 40 | } 41 | 42 | let dataname; 43 | var fullUnlock = []; 44 | var onlyOriginal = []; 45 | var selectFU = [] 46 | var selectOG = [] 47 | 48 | 49 | if ($persistentStore.read("FULLUNLOCK") == null || $persistentStore.read("ONLYORIGINAL") == null) { 50 | } else { 51 | //读取持久化数据 52 | fullUnlock = $persistentStore.read("FULLUNLOCK").split(","); 53 | onlyOriginal = $persistentStore.read("ONLYORIGINAL").split(","); 54 | //清除空值 55 | del(fullUnlock, "") 56 | del(onlyOriginal, "") 57 | } 58 | 59 | /* 测试当选策略组节点状态并记录数据 */ 60 | 61 | var selectName = [] 62 | let select = proxy["" + groupName + ""]; 63 | for (let i = 0; i < select.length; ++i) { 64 | selectName.push(select[i].name); 65 | } 66 | //去除历史数据 67 | for (let i = 0; i < selectName.length; ++i) { 68 | if (fullUnlock.includes(selectName[i]) == true) { 69 | del(fullUnlock, selectName[i]) 70 | } else if (onlyOriginal.includes(selectName[i]) == true) { 71 | del(onlyOriginal, selectName[i]) 72 | } 73 | } 74 | 75 | 76 | //遍历检测当选策略 77 | console.log("当前检测:" + groupName) 78 | for (let i = 0; i < selectName.length; ++i) { 79 | //切换节点 80 | $surge.setSelectGroupPolicy(groupName, selectName[i]); 81 | //等待 82 | await timeout(1000).catch(() => { }) 83 | //执行测试 84 | 85 | let { status, regionCode } = await testPolicy(); 86 | newStatus = status 87 | reg = regionCode 88 | /* 检测超时 再测一次 */ 89 | if (status < 0) { 90 | console.log(selectName[i] + ": 连接超时了,再测一次") 91 | await timeout(1000).catch(() => { }) 92 | let { status, regionCode } = await testPolicy(); 93 | newStatus = status 94 | reg = regionCode 95 | } 96 | console.log("检测结果:" + selectName[i] + " | " + statusName(newStatus)) 97 | //填充数据 98 | dataname = selectName[i] 99 | data[dataname] = reg 100 | if (newStatus === 2) { 101 | if (fullUnlock.includes(selectName[i]) == false) { 102 | fullUnlock.push(selectName[i]) 103 | selectFU.push(selectName[i]) 104 | } 105 | } else if (newStatus === 1) { 106 | if (onlyOriginal.includes(selectName[i]) == false) { 107 | onlyOriginal.push(selectName[i]) 108 | selectOG.push(selectName[i]) 109 | } 110 | } 111 | //找到全解锁节点 退出检测 112 | if (newStatus == 2) { 113 | console.log("找到可用节点 退出检测") 114 | break; 115 | } 116 | } 117 | 118 | /* 择优选择节点 */ 119 | //设定选择列表 120 | var selectList = [] 121 | 122 | console.log(selectFU.length + " | " + selectOG.length) 123 | 124 | if (selectFU.length > 0) { 125 | selectList = selectFU 126 | } else if (selectFU.length == 0 && selectOG.length > 0) { 127 | selectList = selectOG 128 | } 129 | 130 | console.log("选择列表:" + selectList.sort()) 131 | //切换节点 132 | if (selectList.length > 0) $surge.setSelectGroupPolicy(groupName, selectList[0]); 133 | 134 | 135 | 136 | 137 | // 创建持久化数据 138 | 139 | $persistentStore.write(fullUnlock.toString(), "FULLUNLOCK"); 140 | $persistentStore.write(onlyOriginal.toString(), "ONLYORIGINAL") 141 | $persistentStore.write(JSON.stringify(data), "NFREGIONCODE") 142 | 143 | //打印测试结果 144 | console.log("全解锁:" + fullUnlock.sort()) 145 | console.log("仅自制:" + onlyOriginal.sort()) 146 | } 147 | 148 | $done() 149 | 150 | })(); 151 | 152 | 153 | 154 | 155 | 156 | function httpAPI(path = "", method = "GET", body = null) { 157 | return new Promise((resolve) => { 158 | $httpAPI(method, path, body, (result) => { 159 | resolve(result); 160 | }); 161 | }); 162 | }; 163 | 164 | async function testPolicy() { 165 | try { 166 | const regionCode = await Promise.race([testFilm(FILM_ID), timeout(3000)]) 167 | return { status: 2, regionCode } 168 | } catch (error) { 169 | if (error === 'Not Found') { 170 | 171 | return { status: 1 } 172 | } 173 | if (error === 'Not Available') { 174 | return { status: 0 } 175 | } 176 | console.log(error) 177 | return { status: -1 } 178 | } 179 | } 180 | 181 | /** 182 | * 测试是否解锁 183 | */ 184 | function testFilm(filmId) { 185 | return new Promise((resolve, reject) => { 186 | let option = { 187 | url: `https://www.netflix.com/title/${filmId}`, 188 | headers: { 189 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36', 190 | }, 191 | } 192 | $httpClient.get(option, function (error, response, data) { 193 | if (error != null) { 194 | reject(error) 195 | return 196 | } 197 | 198 | if (response.status === 403) { 199 | reject('Not Available') 200 | return 201 | } 202 | 203 | if (response.status === 404) { 204 | reject('Not Found') 205 | return 206 | } 207 | 208 | if (response.status === 200) { 209 | let url = response.headers['x-originating-url'] 210 | let region = url.split('/')[3] 211 | region = region.split('-')[0] 212 | if (region == 'title') { 213 | region = 'us' 214 | } 215 | resolve(region.toUpperCase()) 216 | return 217 | } 218 | 219 | reject('Error') 220 | }) 221 | }) 222 | } 223 | 224 | function timeout(delay = 5000) { 225 | return new Promise((resolve, reject) => { 226 | setTimeout(() => { 227 | reject('Timeout') 228 | }, delay) 229 | }) 230 | } 231 | 232 | function statusName(status) { 233 | return status == 2 ? "全解锁" 234 | : status == 1 ? "仅自制" 235 | : status == 0 ? "不解锁" 236 | : status == -1 ? "检测超时" 237 | : "检测失败"; 238 | } 239 | 240 | function del(arr, num) { 241 | var l = arr.length; 242 | for (var i = 0; i < l; i++) { 243 | if (arr[0] !== num) { 244 | arr.push(arr[0]); 245 | } 246 | arr.shift(arr[0]); 247 | } 248 | return arr; 249 | } 250 | -------------------------------------------------------------------------------- /YouTubeSelect/youtubecontroller.js: -------------------------------------------------------------------------------- 1 | /* 脚本经本人测试已经可以正常运行,但仍可能存在bug,使用过程中遇到障碍请联系Telegram:https://t.me/okmytg 2 | 脚本说明:https://github.com/fishingworld/something/upload/main/YouTubeSelect 3 | */ 4 | 5 | const BASE_URL = 'https://www.youtube.com/premium' 6 | 7 | ; (async () => { 8 | 9 | let params = getParams($argument) 10 | let youtubeGroup = params.YouTubeGroup 11 | //将策略组名称创建为持久化数据 12 | $persistentStore.write(youtubeGroup, "YOUTUBEGROUP"); 13 | 14 | let proxy = await httpAPI("/v1/policy_groups"); 15 | let groupName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(youtubeGroup) + "")).policy; 16 | var proxyName = [];//youtube节点组名称 17 | let arr = proxy["" + youtubeGroup + ""]; 18 | for (let i = 0; i < arr.length; ++i) { 19 | proxyName.push(arr[i].name); 20 | } 21 | let allGroup = []; 22 | for (var key in proxy) { 23 | allGroup.push(key) 24 | } 25 | 26 | /* 手动切换策略 */ 27 | let index; 28 | for (let i = 0; i < proxyName.length; ++i) { 29 | if (groupName == proxyName[i]) { 30 | index = i 31 | } 32 | }; 33 | if ($trigger == "button") { 34 | index += 1; 35 | if (index > arr.length - 1) { 36 | index = 0; 37 | } 38 | $surge.setSelectGroupPolicy(youtubeGroup, proxyName[index]); 39 | }; 40 | 41 | groupName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(youtubeGroup) + "")).policy; 42 | 43 | /* 判断节点列表是否为空 */ 44 | var regData 45 | if ($persistentStore.read("YOUTUBEREG") == null) { 46 | regData = {} 47 | } else { 48 | regData = JSON.parse($persistentStore.read("YOUTUBEREG")) 49 | } 50 | 51 | let dataname; 52 | var isOK = [] 53 | var selectOK = [] 54 | 55 | if ($persistentStore.read("YOUTUBEISOK") == null) { 56 | } else { 57 | //读取持久化数据 58 | isOK = $persistentStore.read("YOUTUBEISOK").split(","); 59 | //清除空值 60 | del(isOK, "") 61 | } 62 | 63 | var selectName = [] 64 | let select = proxy["" + groupName + ""]; 65 | for (let i = 0; i < select.length; ++i) { 66 | selectName.push(select[i].name); 67 | } 68 | 69 | for (let i = 0; i < selectName.length; ++i) { 70 | if (isOK.includes(selectName[i]) == true) { 71 | selectOK.push(selectName[i]) 72 | } 73 | } 74 | 75 | // 为空时执行检测 76 | if (selectOK.length == 0) { 77 | //遍历检测当选策略 78 | console.log("当前检测:" + groupName) 79 | let newStatus; 80 | let reg; 81 | for (let i = 0; i < selectName.length; ++i) { 82 | //切换节点 83 | $surge.setSelectGroupPolicy(groupName, selectName[i]); 84 | //等待 85 | await timeout(1000).catch(() => { }) 86 | //执行测试 87 | let { status, region } = await testGoogle() 88 | newStatus = status 89 | reg = region 90 | /* 检测超时 再测一次 */ 91 | if (newStatus < 1) { 92 | console.log(selectName[i] + ": 连接超时了,再测一次") 93 | await timeout(1000).catch(() => { }) 94 | let { status, region } = await testGoogle() 95 | newStatus = status 96 | reg = region 97 | } 98 | console.log("检测结果:" + selectName[i] + " | " + statusName(newStatus)) 99 | 100 | //填充数据 101 | dataname = selectName[i] 102 | regData[dataname] = reg 103 | if (newStatus === 2) { 104 | if (isOK.includes(selectName[i]) == false) { 105 | isOK.push(selectName[i]) 106 | selectOK.push(selectName[i]) 107 | } 108 | } 109 | 110 | //找到全解锁节点 退出检测 111 | if (newStatus === 2) { 112 | console.log("找到可用节点 退出检测") 113 | break; 114 | } 115 | } 116 | 117 | // 更新持久化数据 118 | $persistentStore.write(isOK.toString(), "YOUTUBEISOK"); 119 | $persistentStore.write(JSON.stringify(regData), "YOUTUBEREG") 120 | } 121 | 122 | //设定节点 123 | if (selectOK.length > 0) { 124 | $surge.setSelectGroupPolicy(groupName, selectOK[0]); 125 | } else { 126 | $surge.setSelectGroupPolicy(groupName, selectName[0]); 127 | } 128 | 129 | /* 刷新信息 */ 130 | //获取根节点名 131 | let rootName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(youtubeGroup) + "")).policy; 132 | while (allGroup.includes(rootName) == true) { 133 | rootName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(rootName) + "")).policy; 134 | } 135 | 136 | /** 137 | * 面板显示 138 | */ 139 | 140 | let title = "YouTube ➟ " + rootName; 141 | 142 | let panel = { 143 | title: `${title}`, 144 | } 145 | 146 | if (isOK.includes(rootName) == true) { 147 | panel['content'] = `YouTube节点正常 地区:${regData[rootName]}` 148 | panel['icon'] = params.icon1 149 | panel['icon-color'] = params.color1 150 | } else { 151 | panel['content'] = `节点异常 你可能无法使用会员权益` 152 | panel['icon'] = params.icon2 153 | panel['icon-color'] = params.color2 154 | } 155 | 156 | $done(panel) 157 | 158 | 159 | })() 160 | 161 | async function testGoogle() { 162 | try { 163 | const region = await Promise.race([test(), timeout(3000)]) 164 | return { status: 2, region } 165 | } catch (error) { 166 | if (error === 'Not Available') { 167 | return { status: 1 } 168 | } 169 | console.log(error) 170 | return { status: 0 } 171 | } 172 | } 173 | 174 | function test() { 175 | return new Promise((resolve, reject) => { 176 | let option = { 177 | url: BASE_URL, 178 | headers: { 179 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36', 180 | 'Accept-Language': 'en', 181 | }, 182 | } 183 | $httpClient.get(option, function (error, response, data) { 184 | if (error != null || response.status !== 200) { 185 | reject('Error') 186 | return 187 | } 188 | 189 | if (data.indexOf('Premium is not available in your country') !== -1) { 190 | reject('Not Available') 191 | return 192 | } 193 | 194 | let region = '' 195 | let re = new RegExp('"countryCode":"(.*?)"', 'gm') 196 | let result = re.exec(data) 197 | if (result != null && result.length === 2) { 198 | region = result[1] 199 | } else if (data.indexOf('www.google.cn') !== -1) { 200 | region = 'CN' 201 | } else { 202 | region = 'US' 203 | } 204 | resolve(region.toUpperCase()) 205 | }) 206 | }) 207 | } 208 | 209 | function timeout(delay = 5000) { 210 | return new Promise((resolve, reject) => { 211 | setTimeout(() => { 212 | reject('Timeout') 213 | }, delay) 214 | }) 215 | } 216 | 217 | function httpAPI(path = "", method = "GET", body = null) { 218 | return new Promise((resolve) => { 219 | $httpAPI(method, path, body, (result) => { 220 | resolve(result); 221 | }); 222 | }); 223 | }; 224 | 225 | function getParams(param) { 226 | return Object.fromEntries( 227 | $argument 228 | .split("&") 229 | .map((item) => item.split("=")) 230 | .map(([k, v]) => [k, decodeURIComponent(v)]) 231 | ); 232 | } 233 | 234 | function statusName(status) { 235 | return status == 2 ? "支持的" 236 | : status == 1 ? "被封禁" 237 | : status == 0 ? "检测超时" 238 | : "检测异常"; 239 | } 240 | 241 | function del(arr, num) { 242 | var l = arr.length; 243 | for (var i = 0; i < l; i++) { 244 | if (arr[0] !== num) { 245 | arr.push(arr[0]); 246 | } 247 | arr.shift(arr[0]); 248 | } 249 | return arr; 250 | } 251 | -------------------------------------------------------------------------------- /NetflixSelect/netflixshortcut.js: -------------------------------------------------------------------------------- 1 | /* 脚本经本人测试已经可以正常运行,但仍可能存在bug,使用过程中遇到障碍请联系Telegram:https://t.me/okmytg 2 | 脚本说明:https://github.com/fishingworld/something/blob/main/NetflixSelect/README.md 3 | */ 4 | 5 | const FILM_ID = 81215567 6 | const AREA_TEST_FILM_ID = 80018499 7 | 8 | 9 | ; (async () => { 10 | 11 | let netflixGroup = $persistentStore.read("NFGroupName") 12 | let proxy = await httpAPI("/v1/policy_groups"); 13 | let groupName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(netflixGroup) + "")).policy; 14 | 15 | let allGroup = []; 16 | for (var key in proxy) { 17 | allGroup.push(key) 18 | } 19 | 20 | //测试当前状态 21 | let { status, regionCode } = await testPolicy(); 22 | let newStatus = status 23 | let reg = regionCode 24 | if (status < 0) { 25 | console.log("连接超时了,再测一次") 26 | await timeout(1000).catch(() => { }) 27 | let { status, regionCode } = await testPolicy(); 28 | newStatus = status 29 | reg = regionCode 30 | } 31 | if (newStatus === 2) { 32 | console.log("当前节点仍可用 退出检测") 33 | } else { 34 | 35 | var data 36 | if ($persistentStore.read("NFREGIONCODE") == null) { 37 | data = {} 38 | } else { 39 | data = JSON.parse($persistentStore.read("NFREGIONCODE")) 40 | } 41 | 42 | let dataname; 43 | var fullUnlock = []; 44 | var onlyOriginal = []; 45 | var selectFU = [] 46 | var selectOG = [] 47 | 48 | 49 | if ($persistentStore.read("FULLUNLOCK") == null || $persistentStore.read("ONLYORIGINAL") == null) { 50 | } else { 51 | //读取持久化数据 52 | fullUnlock = $persistentStore.read("FULLUNLOCK").split(","); 53 | onlyOriginal = $persistentStore.read("ONLYORIGINAL").split(","); 54 | //清除空值 55 | del(fullUnlock, "") 56 | del(onlyOriginal, "") 57 | } 58 | 59 | /* 测试当选策略组节点状态并记录数据 */ 60 | 61 | var selectName = [] 62 | let select = proxy["" + groupName + ""]; 63 | for (let i = 0; i < select.length; ++i) { 64 | selectName.push(select[i].name); 65 | } 66 | //去除历史数据 67 | for (let i = 0; i < selectName.length; ++i) { 68 | if (fullUnlock.includes(selectName[i]) == true) { 69 | del(fullUnlock, selectName[i]) 70 | } else if (onlyOriginal.includes(selectName[i]) == true) { 71 | del(onlyOriginal, selectName[i]) 72 | } 73 | } 74 | 75 | 76 | //遍历检测当选策略 77 | console.log("当前检测:" + groupName) 78 | for (let i = 0; i < selectName.length; ++i) { 79 | //切换节点 80 | $surge.setSelectGroupPolicy(groupName, selectName[i]); 81 | //等待 82 | await timeout(1000).catch(() => { }) 83 | //执行测试 84 | 85 | let { status, regionCode } = await testPolicy(); 86 | newStatus = status 87 | reg = regionCode 88 | /* 检测超时 再测一次 */ 89 | if (newStatus < 0) { 90 | console.log(selectName[i] + ": 连接超时了,再测一次") 91 | await timeout(1000).catch(() => { }) 92 | let { status, regionCode } = await testPolicy(); 93 | newStatus = status 94 | reg = regionCode 95 | } 96 | console.log("检测结果:" + selectName[i] + " | " + statusName(newStatus)) 97 | //填充数据 98 | dataname = selectName[i] 99 | data[dataname] = reg 100 | if (newStatus === 2) { 101 | if (fullUnlock.includes(selectName[i]) == false) { 102 | fullUnlock.push(selectName[i]) 103 | selectFU.push(selectName[i]) 104 | } 105 | } else if (newStatus === 1) { 106 | if (onlyOriginal.includes(selectName[i]) == false) { 107 | onlyOriginal.push(selectName[i]) 108 | selectOG.push(selectName[i]) 109 | } 110 | } 111 | //找到全解锁节点 退出检测 112 | if (newStatus == 2) { 113 | console.log("找到可用节点 退出检测") 114 | break; 115 | } 116 | } 117 | 118 | /* 择优选择节点 */ 119 | //设定选择列表 120 | var selectList = [] 121 | 122 | console.log(selectFU.length + " | " + selectOG.length) 123 | 124 | if (selectFU.length > 0) { 125 | selectList = selectFU 126 | } else if (selectFU.length == 0 && selectOG.length > 0) { 127 | selectList = selectOG 128 | } 129 | 130 | console.log("选择列表:" + selectList.sort()) 131 | //切换节点 132 | if (selectList.length > 0) $surge.setSelectGroupPolicy(groupName, selectList[0]); 133 | 134 | 135 | 136 | 137 | // 创建持久化数据 138 | 139 | $persistentStore.write(fullUnlock.toString(), "FULLUNLOCK"); 140 | $persistentStore.write(onlyOriginal.toString(), "ONLYORIGINAL") 141 | $persistentStore.write(JSON.stringify(data), "NFREGIONCODE") 142 | 143 | //打印测试结果 144 | console.log("全解锁:" + fullUnlock.sort()) 145 | console.log("仅自制:" + onlyOriginal.sort()) 146 | } 147 | 148 | //获取根节点名 149 | let rootName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(netflixGroup) + "")).policy; 150 | while (allGroup.includes(rootName) == true) { 151 | rootName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(rootName) + "")).policy; 152 | } 153 | 154 | 155 | let info 156 | if (newStatus === 2) { 157 | info = `已选定节点: ${rootName} | ${statusName(newStatus)} | ${reg}` 158 | } else if (newStatus === 1) { 159 | info = `已选定节点: ${rootName} | ${statusName(newStatus)}` 160 | } else { 161 | info = "该策略组暂无可供支援的节点" 162 | } 163 | 164 | $notification.post("Netflix檢測", info, "") 165 | 166 | $done() 167 | 168 | })(); 169 | 170 | 171 | 172 | 173 | 174 | function httpAPI(path = "", method = "GET", body = null) { 175 | return new Promise((resolve) => { 176 | $httpAPI(method, path, body, (result) => { 177 | resolve(result); 178 | }); 179 | }); 180 | }; 181 | 182 | async function testPolicy() { 183 | try { 184 | const regionCode = await Promise.race([testFilm(FILM_ID), timeout(3000)]) 185 | return { status: 2, regionCode } 186 | } catch (error) { 187 | if (error === 'Not Found') { 188 | 189 | return { status: 1 } 190 | } 191 | if (error === 'Not Available') { 192 | return { status: 0 } 193 | } 194 | console.log(error) 195 | return { status: -1 } 196 | } 197 | } 198 | 199 | /** 200 | * 测试是否解锁 201 | */ 202 | function testFilm(filmId) { 203 | return new Promise((resolve, reject) => { 204 | let option = { 205 | url: `https://www.netflix.com/title/${filmId}`, 206 | headers: { 207 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36', 208 | }, 209 | } 210 | $httpClient.get(option, function (error, response, data) { 211 | if (error != null) { 212 | reject(error) 213 | return 214 | } 215 | 216 | if (response.status === 403) { 217 | reject('Not Available') 218 | return 219 | } 220 | 221 | if (response.status === 404) { 222 | reject('Not Found') 223 | return 224 | } 225 | 226 | if (response.status === 200) { 227 | let url = response.headers['x-originating-url'] 228 | let region = url.split('/')[3] 229 | region = region.split('-')[0] 230 | if (region == 'title') { 231 | region = 'us' 232 | } 233 | resolve(region.toUpperCase()) 234 | return 235 | } 236 | 237 | reject('Error') 238 | }) 239 | }) 240 | } 241 | 242 | function timeout(delay = 5000) { 243 | return new Promise((resolve, reject) => { 244 | setTimeout(() => { 245 | reject('Timeout') 246 | }, delay) 247 | }) 248 | } 249 | 250 | function statusName(status) { 251 | return status == 2 ? "全解锁" 252 | : status == 1 ? "仅自制" 253 | : status == 0 ? "不解锁" 254 | : status == -1 ? "检测超时" 255 | : "检测失败"; 256 | } 257 | 258 | function del(arr, num) { 259 | var l = arr.length; 260 | for (var i = 0; i < l; i++) { 261 | if (arr[0] !== num) { 262 | arr.push(arr[0]); 263 | } 264 | arr.shift(arr[0]); 265 | } 266 | return arr; 267 | } 268 | -------------------------------------------------------------------------------- /NetflixSelect/nf_autoselect.js: -------------------------------------------------------------------------------- 1 | /* 脚本经本人测试已经可以正常运行,但仍可能存在bug,使用过程中遇到障碍请联系Telegram:https://t.me/okmytg 2 | 脚本说明: 3 | 1:本脚本修改自 @Helge_0x00 4 | 2:panel脚本依赖cron脚本传送数据,你应当手动运行一次cron脚本以获取节点列表 5 | 3:为了节省效能,请尽量精简策略组 6 | 4:点击panel时切换至下一个可解锁节点,节点列表为空时,仅执行状态检测 7 | 5:panel脚本允许自动更新,自动更新将刷新策略组信息,并可以自动选择更优选项 8 | 6:检测数据有一定概率会出错,且网飞数据会有所变动,因此你可能遇到切换至非全解锁节点,此时切换至下一个即可,毕竟这是概率较小的事件,大部分检测都是正确的,亦可手动执行一次cron脚本,节点列表将得到更新与修正 9 | 6:可用的自定义参数: 10 | icon1 color1:全解锁时的图标及颜色 11 | icon2 color2:仅自制时的图标及颜色 12 | icon3 color3:无可用节点的图标及颜色 13 | netflixGroup:网飞策略组名称 14 | */ 15 | 16 | 17 | const FILM_ID = 81215567 18 | const AREA_TEST_FILM_ID = 80018499 19 | let params = getParams($argument) 20 | 21 | ;(async () => { 22 | let netflixGroup = params.netflixGroup 23 | //将策略组名称创建为持久化数据 24 | $persistentStore.write(netflixGroup,"NFGroupName"); 25 | 26 | let proxy = await httpAPI("/v1/policy_groups"); 27 | let groupName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(netflixGroup)+"")).policy; 28 | let first = groupName; 29 | var proxyName= [];//netflix节点组名称 30 | let arr = proxy[""+netflixGroup+""]; 31 | for (let i = 0; i < arr.length; ++i) { 32 | proxyName.push(arr[i].name); 33 | } 34 | let allGroup = []; 35 | for (var key in proxy){ 36 | allGroup.push(key) 37 | } 38 | 39 | var fullUnlock=[]; 40 | var onlyOriginal=[]; 41 | 42 | //读取持久化数据 43 | fullUnlock = $persistentStore.read("fullUnlockNetflix").split(","); 44 | onlyOriginal= $persistentStore.read("onlyOriginalNetflix").split(","); 45 | 46 | //打印测试结果 47 | console.log("全解锁:"+fullUnlock.sort()) 48 | console.log("仅自制:"+onlyOriginal.sort()) 49 | 50 | /** 51 | * 过滤选择列表 52 | */ 53 | 54 | 55 | //删除策略组外节点并更新持久化数据 56 | var select=[]; 57 | //清除空值 58 | if(fullUnlock.toString().length==0){ 59 | fullUnlock.splice(fullUnlock.indexOf(fullUnlock[0]), 1) 60 | } 61 | if(onlyOriginal.toString().length==0){ 62 | onlyOriginal.splice(onlyOriginal.indexOf(onlyOriginal[0]), 1) 63 | } 64 | 65 | console.log(fullUnlock.length+" | "+ onlyOriginal.length) 66 | 67 | if(fullUnlock.length>0){ 68 | for (let i = 0; i < fullUnlock.length; ++i) { 69 | if(proxyName.includes(fullUnlock[i])==false){ 70 | fullUnlock.splice(fullUnlock.indexOf(fullUnlock[i]), 1) 71 | } 72 | } 73 | select = fullUnlock 74 | $persistentStore.write(select.sort().toString(),"fullUnlockNetflix"); 75 | }else if(fullUnlock.length==0&&onlyOriginal.length>0){ 76 | for (let i = 0; i < onlyOriginal.length; ++i) { 77 | if(proxyName.includes(onlyOriginal[i])==false){ 78 | onlyOriginal.splice(onlyOriginal.indexOf(onlyOriginal[i]), 1) 79 | } 80 | } 81 | select = onlyOriginal 82 | $persistentStore.write(select.sort().toString(),"onlyOriginalNetflix") 83 | } 84 | 85 | console.log("选择列表:"+select.sort()) 86 | 87 | //手动切换 88 | 89 | if($trigger == "button"){ 90 | 91 | //当前节点 92 | groupName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(netflixGroup)+"")).policy; 93 | console.log("当前节点:"+groupName) 94 | 95 | let index = select.indexOf(groupName)+1; 96 | 97 | if(index>=select.length){ 98 | index=0 99 | } 100 | console.log("目标节点:"+ select[index]) 101 | 102 | $surge.setSelectGroupPolicy(netflixGroup, select[index]); 103 | 104 | await timeout(1000).catch(() => {}) 105 | 106 | } 107 | 108 | 109 | /** 110 | * 自动刷新 111 | */ 112 | 113 | /* 检查选择列表 */ 114 | console.log(select.length) 115 | if(select.length==0){ 116 | $notification.post("节点列表获取失败", "未获取到节点列表,请手动运行一次NetflixChecker脚本", "") 117 | } 118 | //测试当前选择 119 | 120 | //当前节点 121 | groupName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(netflixGroup)+"")).policy; 122 | console.log("当前节点:"+groupName) 123 | 124 | let { status, regionCode, policyName } = await testPolicy(groupName); 125 | let newStatus=status 126 | let reg = regionCode 127 | 128 | console.log("节点状态:"+status) 129 | 130 | //当前节点不可全解锁时,执行自动切换,若列表为空,仅执行测试 131 | //连接超时再测一次 132 | if(status == -1) { 133 | let { status, regionCode, policyName } = await testPolicy(groupName); 134 | console.log("连接超时了,又测了一次") 135 | console.log("当前节点:"+groupName) 136 | console.log("节点状态:"+status) 137 | } 138 | if(status!= 2){ 139 | if(select.length>0){ 140 | //遍历选择列表,找到第一个更优节点 141 | for (let i = 0; i < select.length; ++i) { 142 | $surge.setSelectGroupPolicy(netflixGroup, select[i]); 143 | await timeout(1000).catch(() => {}) 144 | groupName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(netflixGroup)+"")).policy; 145 | console.log("当前节点:"+groupName) 146 | let { status, regionCode, policyName } = await testPolicy(groupName); 147 | console.log("节点状态:"+status) 148 | if(status>newStatus){ 149 | newStatus=status 150 | reg = regionCode 151 | break; 152 | } 153 | } 154 | }else { 155 | groupName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(netflixGroup)+"")).policy; 156 | console.log("当前节点:"+groupName) 157 | let { status, regionCode, policyName } = await testPolicy(groupName); 158 | console.log("节点状态:"+status) 159 | newStatus=status 160 | reg = regionCode 161 | } 162 | } 163 | 164 | status=newStatus 165 | regionCode=reg 166 | 167 | //获取根节点名 168 | let rootName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(netflixGroup)+"")).policy; 169 | while(allGroup.includes(rootName)==true){ 170 | rootName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(rootName)+"")).policy; 171 | } 172 | 173 | /** 174 | * 面板显示 175 | */ 176 | 177 | let title = "Netflix ➟ " + rootName; 178 | 179 | let panel = { 180 | title: `${title}`, 181 | } 182 | 183 | 184 | if (status==2) { 185 | panel['content'] = `完整支援Netflix,区域:${regionCode}` 186 | panel['icon'] = params.icon1 187 | panel['icon-color'] = params.color1 188 | } else if (status==1) { 189 | panel['content'] = `解锁自制内容` 190 | panel['icon'] = params.icon2 191 | panel['icon-color'] = params.color2 192 | }else { 193 | $surge.setSelectGroupPolicy(netflixGroup, first); 194 | panel['content'] = `您的节点连自制内容都不支持呢~` 195 | panel['icon'] = params.icon3 196 | panel['icon-color'] = params.color3 197 | return 198 | } 199 | 200 | 201 | 202 | console.log(panel) 203 | 204 | $done(panel) 205 | 206 | 207 | })(); 208 | 209 | 210 | 211 | 212 | 213 | function httpAPI(path = "", method = "GET", body = null) { 214 | return new Promise((resolve) => { 215 | $httpAPI(method, path, body, (result) => { 216 | resolve(result); 217 | }); 218 | }); 219 | }; 220 | 221 | async function testPolicy(policyName) { 222 | try { 223 | const regionCode = await Promise.race([testFilm(FILM_ID), timeout(3000)]) 224 | return { status: 2, regionCode, policyName } 225 | } catch (error) { 226 | if (error === 'Not Found') { 227 | return { status: 1, policyName } 228 | } 229 | if (error === 'Not Available') { 230 | return { status: 0, policyName } 231 | } 232 | console.log(error) 233 | return { status: -1, policyName } 234 | } 235 | } 236 | 237 | /** 238 | * 测试是否解锁 239 | */ 240 | function testFilm(filmId) { 241 | return new Promise((resolve, reject) => { 242 | let option = { 243 | url: `https://www.netflix.com/title/${filmId}`, 244 | headers: { 245 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36', 246 | }, 247 | } 248 | $httpClient.get(option, function (error, response, data) { 249 | if (error != null) { 250 | reject(error) 251 | return 252 | } 253 | 254 | if (response.status === 403) { 255 | reject('Not Available') 256 | return 257 | } 258 | 259 | if (response.status === 404) { 260 | reject('Not Found') 261 | return 262 | } 263 | 264 | if (response.status === 200) { 265 | let url = response.headers['x-originating-url'] 266 | let region = url.split('/')[3] 267 | region = region.split('-')[0] 268 | if (region == 'title') { 269 | region = 'us' 270 | } 271 | resolve(region.toUpperCase()) 272 | return 273 | } 274 | 275 | reject('Error') 276 | }) 277 | }) 278 | } 279 | 280 | function timeout(delay = 5000) { 281 | return new Promise((resolve, reject) => { 282 | setTimeout(() => { 283 | reject('Timeout') 284 | }, delay) 285 | }) 286 | } 287 | 288 | function getParams(param) { 289 | return Object.fromEntries( 290 | $argument 291 | .split("&") 292 | .map((item) => item.split("=")) 293 | .map(([k, v]) => [k, decodeURIComponent(v)]) 294 | ); 295 | } 296 | -------------------------------------------------------------------------------- /NetflixSelect/netflixcontroller.js: -------------------------------------------------------------------------------- 1 | /* 脚本经本人测试已经可以正常运行,但仍可能存在bug,使用过程中遇到障碍请联系Telegram:https://t.me/okmytg 2 | 脚本说明:https://github.com/fishingworld/something/blob/main/NetflixSelect/README.md 3 | */ 4 | 5 | 6 | const FILM_ID = 81215567 7 | const AREA_TEST_FILM_ID = 80018499 8 | let params = getParams($argument) 9 | 10 | ; 11 | (async () => { 12 | let netflixGroup = params.netflixGroup 13 | //将策略组名称创建为持久化数据 14 | $persistentStore.write(netflixGroup, "NFGroupName"); 15 | 16 | let proxy = await httpAPI("/v1/policy_groups"); 17 | let groupName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(netflixGroup) + "")).policy; 18 | var proxyName = [];//获取子策略名称 19 | let arr = proxy["" + netflixGroup + ""]; 20 | for (let i = 0; i < arr.length; ++i) { 21 | proxyName.push(arr[i].name); 22 | } 23 | let allGroup = []; 24 | for (var key in proxy) { 25 | allGroup.push(key) 26 | } 27 | 28 | /* 手动切换策略 */ 29 | let index; 30 | for (let i = 0; i < proxyName.length; ++i) { 31 | if (groupName == proxyName[i]) { 32 | index = i 33 | } 34 | }; 35 | if ($trigger == "button") { 36 | index += 1; 37 | 38 | if (index > arr.length - 1) { 39 | index = 0; 40 | } 41 | $surge.setSelectGroupPolicy(netflixGroup, proxyName[index]); 42 | 43 | }; 44 | 45 | groupName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(netflixGroup) + "")).policy; 46 | 47 | 48 | /* 判断节点列表是否为空 */ 49 | var data 50 | if ($persistentStore.read("NFREGIONCODE") == null) { 51 | data = {} 52 | } else { 53 | data = JSON.parse($persistentStore.read("NFREGIONCODE")) 54 | } 55 | 56 | let dataname; 57 | var fullUnlock = []; 58 | var onlyOriginal = []; 59 | var selectFU = [] 60 | var selectOG = [] 61 | 62 | if ($persistentStore.read("FULLUNLOCK") == null || $persistentStore.read("ONLYORIGINAL") == null) { } else { 63 | //读取持久化数据 64 | fullUnlock = $persistentStore.read("FULLUNLOCK").split(","); 65 | onlyOriginal = $persistentStore.read("ONLYORIGINAL").split(","); 66 | //清除空值 67 | del(fullUnlock, "") 68 | del(onlyOriginal, "") 69 | } 70 | var selectName = [] 71 | let select = proxy["" + groupName + ""]; 72 | for (let i = 0; i < select.length; ++i) { 73 | selectName.push(select[i].name); 74 | } 75 | 76 | for (let i = 0; i < selectName.length; ++i) { 77 | if (fullUnlock.includes(selectName[i]) == true) { 78 | selectFU.push(selectName[i]) 79 | } else if (onlyOriginal.includes(selectName[i]) == true) { 80 | selectOG.push(selectName[i]) 81 | } 82 | } 83 | 84 | var selectList = [] 85 | if (selectFU.length > 0) { 86 | selectList = selectFU 87 | } else if (selectFU.length == 0 && selectOG.length > 0) { 88 | selectList = selectOG 89 | } 90 | 91 | // 为空时执行检测 92 | if (selectFU.length == 0) { 93 | //去除历史数据 94 | for (let i = 0; i < selectName.length; ++i) { 95 | if (fullUnlock.includes(selectName[i]) == true) { 96 | del(fullUnlock, selectName[i]) 97 | } else if (onlyOriginal.includes(selectName[i]) == true) { 98 | del(onlyOriginal, selectName[i]) 99 | } 100 | } 101 | //遍历检测当选策略 102 | console.log("当前检测:" + groupName) 103 | let newStatus; 104 | let reg; 105 | for (let i = 0; i < selectName.length; ++i) { 106 | //切换节点 107 | $surge.setSelectGroupPolicy(groupName, selectName[i]); 108 | //等待 109 | await timeout(1000).catch(() => { }) 110 | //执行测试 111 | let { status, regionCode, policyName } = await testPolicy(selectName[i]); 112 | newStatus = status 113 | reg = regionCode 114 | 115 | /* 检测超时 再测一次 */ 116 | if (newStatus < 0) { 117 | console.log(selectName[i] + ": 连接超时了,再测一次") 118 | await timeout(1000).catch(() => { }) 119 | let { status, regionCode, policyName } = await testPolicy(selectName[i]); 120 | newStatus = status 121 | reg = regionCode 122 | } 123 | console.log("检测结果:" + selectName[i] + " | " + statusName(newStatus)) 124 | //填充数据 125 | dataname = selectName[i] 126 | data[dataname] = reg 127 | if (newStatus === 2) { 128 | if (fullUnlock.includes(selectName[i]) == false) { 129 | fullUnlock.push(selectName[i]) 130 | selectFU.push(selectName[i]) 131 | } 132 | } else if (newStatus === 1) { 133 | if (onlyOriginal.includes(selectName[i]) == false) { 134 | onlyOriginal.push(selectName[i]) 135 | selectOG.push(selectName[i]) 136 | } 137 | } 138 | //找到全解锁节点 退出检测 139 | if (newStatus == 2) { 140 | console.log("找到可用节点 退出检测") 141 | break; 142 | } 143 | } 144 | 145 | if (selectFU.length > 0) { 146 | selectList = selectFU 147 | } else if (selectFU.length == 0 && selectOG.length > 0) { 148 | selectList = selectOG 149 | } 150 | // 更新持久化数据 151 | $persistentStore.write(fullUnlock.toString(), "FULLUNLOCK"); 152 | $persistentStore.write(onlyOriginal.toString(), "ONLYORIGINAL") 153 | $persistentStore.write(JSON.stringify(data), "NFREGIONCODE") 154 | 155 | } 156 | 157 | //设定节点 158 | if (selectList.length > 0) { 159 | $surge.setSelectGroupPolicy(groupName, selectList[0]); 160 | } else { 161 | $surge.setSelectGroupPolicy(groupName, selectName[0]); 162 | } 163 | 164 | /* 刷新信息 */ 165 | //获取根节点名 166 | let rootName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(netflixGroup) + "")).policy; 167 | while (allGroup.includes(rootName) == true) { 168 | rootName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(rootName) + "")).policy; 169 | } 170 | 171 | /** 172 | * 面板显示 173 | */ 174 | 175 | let title = "Netflix ➟ " + rootName; 176 | 177 | let panel = { 178 | title: `${title}`, 179 | } 180 | 181 | if (fullUnlock.includes(rootName)) { 182 | panel['content'] = `完整支援Netflix 地区:${data[rootName]}` 183 | panel['icon'] = params.icon1 184 | panel['icon-color'] = params.color1 185 | } else if (onlyOriginal.includes(rootName)) { 186 | panel['content'] = `仅支援自制内容~ ` 187 | panel['icon'] = params.icon2 188 | panel['icon-color'] = params.color2 189 | } else { 190 | console.log("test") 191 | panel['content'] = `没有可供支援的节点呢~` 192 | panel['icon'] = params.icon3 193 | panel['icon-color'] = params.color3 194 | } 195 | 196 | 197 | $done(panel) 198 | 199 | 200 | })(); 201 | 202 | function httpAPI(path = "", method = "GET", body = null) { 203 | return new Promise((resolve) => { 204 | $httpAPI(method, path, body, (result) => { 205 | resolve(result); 206 | }); 207 | }); 208 | }; 209 | 210 | async function testPolicy(policyName) { 211 | try { 212 | const regionCode = await Promise.race([testFilm(FILM_ID), timeout(3000)]) 213 | return { 214 | status: 2, 215 | regionCode, 216 | policyName 217 | } 218 | } catch (error) { 219 | if (error === 'Not Found') { 220 | return { 221 | status: 1, 222 | policyName 223 | } 224 | } 225 | if (error === 'Not Available') { 226 | return { 227 | status: 0, 228 | policyName 229 | } 230 | } 231 | console.log(error) 232 | return { 233 | status: -1, 234 | policyName 235 | } 236 | } 237 | } 238 | 239 | /** 240 | * 测试是否解锁 241 | */ 242 | function testFilm(filmId) { 243 | return new Promise((resolve, reject) => { 244 | let option = { 245 | url: `https://www.netflix.com/title/${filmId}`, 246 | headers: { 247 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36', 248 | }, 249 | } 250 | $httpClient.get(option, function (error, response, data) { 251 | if (error != null) { 252 | reject(error) 253 | return 254 | } 255 | 256 | if (response.status === 403) { 257 | reject('Not Available') 258 | return 259 | } 260 | 261 | if (response.status === 404) { 262 | reject('Not Found') 263 | return 264 | } 265 | 266 | if (response.status === 200) { 267 | let url = response.headers['x-originating-url'] 268 | let region = url.split('/')[3] 269 | region = region.split('-')[0] 270 | if (region == 'title') { 271 | region = 'us' 272 | } 273 | resolve(region.toUpperCase()) 274 | return 275 | } 276 | 277 | reject('Error') 278 | }) 279 | }) 280 | } 281 | 282 | function timeout(delay = 5000) { 283 | return new Promise((resolve, reject) => { 284 | setTimeout(() => { 285 | reject('Timeout') 286 | }, delay) 287 | }) 288 | } 289 | 290 | function getParams(param) { 291 | return Object.fromEntries( 292 | $argument 293 | .split("&") 294 | .map((item) => item.split("=")) 295 | .map(([k, v]) => [k, decodeURIComponent(v)]) 296 | ); 297 | } 298 | 299 | function del(arr, num) { 300 | var l = arr.length; 301 | for (var i = 0; i < l; i++) { 302 | if (arr[0] !== num) { 303 | arr.push(arr[0]); 304 | } 305 | arr.shift(arr[0]); 306 | } 307 | return arr; 308 | } 309 | 310 | function statusName(status) { 311 | return status == 2 ? "全解锁" 312 | : status == 1 ? "仅自制" 313 | : status == 0 ? "不解锁" 314 | : status == -1 ? "检测超时" 315 | : "检测失败"; 316 | } -------------------------------------------------------------------------------- /DisneySelect/disney_selecter.js: -------------------------------------------------------------------------------- 1 | /* 脚本经本人测试已经可以正常运行,但仍可能存在bug,使用过程中遇到障碍请联系Telegram:https://t.me/okmytg 2 | 脚本说明: 3 | 1:本 4 | 2:本脚本与姊妹脚本disney_checker相互依赖,你必须手动运行一次cron脚本以获取节点列表 5 | 3:为了节省效能,请尽量精简策略组 6 | 4:点击panel时切换至下一个可解锁节点 7 | 5:panel脚本允许自动更新,自动更新将刷新策略组信息,并可以自动选择更优选项 8 | 6:可用的自定义参数: 9 | icon1 color1:全解锁时的图标及颜色 10 | icon2 color2:即将上线的图标及颜色 11 | icon3 color3:无可用节点的图标及颜色 12 | disneyGroup:迪士尼策略组名称 13 | */ 14 | 15 | const AUTHORIZATION = 'Bearer ZGlzbmV5JmJyb3dzZXImMS4wLjA.Cu56AgSfBTDag5NiRA81oLHkDZfu5L3CKadnefEAY84' 16 | const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36' 17 | 18 | // 即将登陆 19 | const STATUS_COMING = 2 20 | // 支持解锁 21 | const STATUS_AVAILABLE = 1 22 | // 不支持解锁 23 | const STATUS_NOT_AVAILABLE = 0 24 | // 检测超时 25 | const STATUS_TIMEOUT = -1 26 | // 检测异常 27 | const STATUS_ERROR = -2 28 | 29 | ;(async () => { 30 | let params = getParams($argument) 31 | let disneyGroup = params.disneyGroup 32 | //将策略组名称创建为持久化数据 33 | $persistentStore.write(disneyGroup,"DISNEYGROUP"); 34 | 35 | let proxy = await httpAPI("/v1/policy_groups"); 36 | let groupName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(disneyGroup)+"")).policy; 37 | let first = groupName; 38 | var proxyName= [];//Disney节点组名称 39 | let arr = proxy[""+disneyGroup+""]; 40 | for (let i = 0; i < arr.length; ++i) { 41 | proxyName.push(arr[i].name); 42 | } 43 | let allGroup = []; 44 | for (var key in proxy){ 45 | allGroup.push(key) 46 | } 47 | 48 | var unlocked = []; 49 | /* 读取持久化数据 */ 50 | unlocked = $persistentStore.read("unlockedDisney").split(","); 51 | 52 | //打印数据结果 53 | console.log("可解锁: " + unlocked.sort()) 54 | 55 | /** 56 | * 过滤选择列表 57 | */ 58 | 59 | var select=[]; 60 | //清除空值 61 | if(unlocked.toString().length==0){ 62 | unlocked.splice(unlocked.indexOf(unlocked[0]), 1) 63 | } 64 | //删除策略组外节点并更新持久化数据 65 | if(unlocked.length>0){ 66 | for (let i = 0; i < unlocked.length; ++i) { 67 | if(proxyName.includes(unlocked[i])==true){ 68 | select.push(unlocked[i]) 69 | } 70 | } 71 | 72 | $persistentStore.write(select.sort().toString(),"unlockedDisney"); 73 | } 74 | 75 | console.log("选择列表:"+select.sort()) 76 | 77 | /* 手动切换 */ 78 | 79 | if($trigger == "button"){ 80 | 81 | //当前节点 82 | groupName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(disneyGroup)+"")).policy; 83 | console.log("当前节点:"+groupName) 84 | 85 | let index; 86 | if(select.includes(groupName)==true){ 87 | index = select.indexOf(groupName)+1; 88 | }else{ 89 | index = 0 90 | } 91 | 92 | if(index>=select.length){ 93 | index=0 94 | } 95 | console.log("目标节点:"+ select[index]) 96 | if(select.length>0){ 97 | $surge.setSelectGroupPolicy(disneyGroup, select[index]); 98 | } 99 | await timeout(1000).catch(() => {}) 100 | } 101 | 102 | /** 103 | * 自动刷新 104 | */ 105 | 106 | /* 检查选择列表 */ 107 | console.log(select.length) 108 | if(select.length==0){ 109 | $notification.post("节点列表获取失败", "请手动运行一次DisneyChecker脚本", "") 110 | } 111 | //测试当前选择 112 | 113 | //当前节点 114 | groupName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(disneyGroup)+"")).policy; 115 | console.log("当前节点:"+groupName) 116 | 117 | let { region, status } = await testDisneyPlus() 118 | let newStatus=status 119 | let reg = region 120 | console.log("节点状态:"+status) 121 | /* 检测超时 再测一次 */ 122 | if(status <0) { 123 | console.log(groupName+": 连接超时了,再测一次") 124 | await timeout(1000).catch(() => {}) 125 | let { region, status } = await testDisneyPlus() 126 | newStatus=status 127 | reg = region 128 | console.log("当前节点:"+groupName) 129 | console.log("节点状态:"+newStatus) 130 | } 131 | 132 | status = newStatus 133 | region = reg 134 | 135 | 136 | /* 当前节点不可解锁时,执行自动切换,若列表为空,仅执行测试 */ 137 | if(status!= 1){ 138 | if(select.length>0){ 139 | //遍历选择列表,找到第一个更优节点 140 | for (let i = 0; i < select.length; ++i) { 141 | console.log("在找新节点了,稍等一下") 142 | $surge.setSelectGroupPolicy(disneyGroup, select[i]); 143 | await timeout(1000).catch(() => {}) 144 | groupName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(disneyGroup)+"")).policy; 145 | console.log("当前节点:"+groupName) 146 | let { region, status } = await testDisneyPlus() 147 | console.log("节点状态:"+status) 148 | if(status==1){ 149 | newStatus=status 150 | reg = region 151 | break; 152 | } 153 | } 154 | }else { 155 | groupName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(disneyGroup)+"")).policy; 156 | console.log("当前节点:"+groupName) 157 | let { region, status } = await testDisneyPlus() 158 | console.log("节点状态:"+status) 159 | newStatus = status 160 | reg = region 161 | } 162 | } 163 | 164 | status= newStatus 165 | region =reg 166 | 167 | 168 | //获取根节点名 169 | let rootName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(disneyGroup)+"")).policy; 170 | while(allGroup.includes(rootName)==true){ 171 | rootName = (await httpAPI("/v1/policy_groups/select?group_name="+encodeURIComponent(rootName)+"")).policy; 172 | } 173 | 174 | /** 175 | * 面板显示 176 | */ 177 | 178 | 179 | 180 | let title = "Disney+ ➟ " + rootName; 181 | 182 | let panel = { 183 | title: `${title}`, 184 | } 185 | 186 | if (status==1) { 187 | panel['content'] = `支援Disney+,区域:${region}` 188 | panel['icon'] = params.icon1 189 | panel['icon-color'] = params.color1 190 | } else if (status==2) { 191 | panel['content'] = `即将登陆,敬请期待,区域:${region}` 192 | panel['icon'] = params.icon2 193 | panel['icon-color'] = params.color2 194 | }else { 195 | $surge.setSelectGroupPolicy(disneyGroup, first); 196 | panel['content'] = `您的节点不支持Disney+呢~` 197 | panel['icon'] = params.icon3 198 | panel['icon-color'] = params.color3 199 | return 200 | } 201 | 202 | console.log(panel) 203 | 204 | $done(panel) 205 | 206 | 207 | })() 208 | 209 | async function testDisneyPlus() { 210 | try { 211 | let { region, cnbl } = await Promise.race([testHomePage(), timeout(3000)]) 212 | 213 | // 即将登陆 214 | if (cnbl == 2) { 215 | return { region, status: STATUS_COMING } 216 | } 217 | 218 | let { countryCode, inSupportedLocation } = await Promise.race([getLocationInfo(), timeout(1000)]) 219 | 220 | 221 | region = countryCode?? region 222 | // 即将登陆 223 | if (inSupportedLocation === false || inSupportedLocation === 'false') { 224 | return { region, status: STATUS_COMING } 225 | } 226 | 227 | // 支持解锁 228 | return { region, status: STATUS_AVAILABLE } 229 | } catch (error) { 230 | console.log(error) 231 | 232 | // 不支持解锁 233 | if (error === 'Not Available') { 234 | return { status: STATUS_NOT_AVAILABLE } 235 | } 236 | 237 | // 检测超时 238 | if (error === 'Timeout') { 239 | return { status: STATUS_TIMEOUT } 240 | } 241 | 242 | return { status: STATUS_ERROR } 243 | } 244 | } 245 | 246 | 247 | 248 | function getLocationInfo() { 249 | return new Promise((resolve, reject) => { 250 | let opts = { 251 | url: 'https://disney.api.edge.bamgrid.com/graph/v1/device/graphql', 252 | headers: { 253 | 'Accept-Language': 'en', 254 | Authorization: 'ZGlzbmV5JmJyb3dzZXImMS4wLjA.Cu56AgSfBTDag5NiRA81oLHkDZfu5L3CKadnefEAY84', 255 | 'Content-Type': 'application/json', 256 | 'User-Agent': UA, 257 | }, 258 | body: JSON.stringify({ 259 | query: 'mutation registerDevice($input: RegisterDeviceInput!) { registerDevice(registerDevice: $input) { grant { grantType assertion } } }', 260 | variables: { 261 | input: { 262 | applicationRuntime: 'chrome', 263 | attributes: { 264 | browserName: 'chrome', 265 | browserVersion: '94.0.4606', 266 | manufacturer: 'microsoft', 267 | model: null, 268 | operatingSystem: 'windows', 269 | operatingSystemVersion: '10.0', 270 | osDeviceIds: [], 271 | }, 272 | deviceFamily: 'browser', 273 | deviceLanguage: 'en', 274 | deviceProfile: 'windows', 275 | }, 276 | }, 277 | }), 278 | } 279 | 280 | $httpClient.post(opts, function (error, response, data) { 281 | if (error) { 282 | reject('Error') 283 | return 284 | } 285 | 286 | if (response.status !== 200) { 287 | reject('Not Available') 288 | return 289 | } 290 | 291 | let { 292 | inSupportedLocation, 293 | location: { countryCode }, 294 | } = JSON.parse(data)?.extensions?.sdk?.session 295 | resolve({ inSupportedLocation, countryCode }) 296 | }) 297 | }) 298 | } 299 | 300 | function testHomePage() { 301 | return new Promise((resolve, reject) => { 302 | let opts = { 303 | url: 'https://www.disneyplus.com/', 304 | headers: { 305 | 'Accept-Language': 'en', 306 | 'User-Agent': UA, 307 | }, 308 | } 309 | 310 | $httpClient.get(opts, function (error, response, data) { 311 | if (error) { 312 | reject('Error') 313 | return 314 | } 315 | if (response.status !== 200 || data.indexOf('unavailable') !== -1) { 316 | reject('Not Available') 317 | return 318 | } 319 | 320 | let match = data.match(/Region: ([A-Za-z]{2})[\s\S]*?CNBL: ([12])/) 321 | if (!match) { 322 | resolve({ region: '', cnbl: '' }) 323 | return 324 | } 325 | 326 | let region = match[1] 327 | let cnbl = match[2] 328 | resolve({ region, cnbl }) 329 | }) 330 | }) 331 | } 332 | 333 | function timeout(delay = 5000) { 334 | return new Promise((resolve, reject) => { 335 | setTimeout(() => { 336 | reject('Timeout') 337 | }, delay) 338 | }) 339 | } 340 | 341 | 342 | function replaceRegionPlaceholder(content, region) { 343 | let result = content 344 | 345 | if (result.indexOf('#REGION_CODE#') !== -1) { 346 | result = result.replaceAll('#REGION_CODE#', region.toUpperCase()) 347 | } 348 | if (result.indexOf('#REGION_FLAG#') !== -1) { 349 | result = result.replaceAll('#REGION_FLAG#', getCountryFlagEmoji(region.toUpperCase())) 350 | } 351 | 352 | if (result.indexOf('#REGION_NAME#') !== -1) { 353 | result = result.replaceAll('#REGION_NAME#', RESION_NAMES?.[region.toUpperCase()]?.chinese ?? '') 354 | } 355 | 356 | if (result.indexOf('#REGION_NAME_EN#') !== -1) { 357 | result = result.replaceAll('#REGION_NAME_EN#', RESION_NAMES?.[region.toUpperCase()]?.english ?? '') 358 | } 359 | 360 | return result 361 | } 362 | 363 | function httpAPI(path = "", method = "GET", body = null) { 364 | return new Promise((resolve) => { 365 | $httpAPI(method, path, body, (result) => { 366 | resolve(result); 367 | }); 368 | }); 369 | }; 370 | 371 | function getParams(param) { 372 | return Object.fromEntries( 373 | $argument 374 | .split("&") 375 | .map((item) => item.split("=")) 376 | .map(([k, v]) => [k, decodeURIComponent(v)]) 377 | ); 378 | } -------------------------------------------------------------------------------- /DisneySelect/disneychecker.js: -------------------------------------------------------------------------------- 1 | /* 脚本经本人测试已经可以正常运行,但仍可能存在bug,使用过程中遇到障碍请联系Telegram:https://t.me/okmytg 2 | 脚本说明:https://github.com/fishingworld/something/blob/main/DisneySelect/README.md 3 | */ 4 | 5 | const AUTHORIZATION = 'Bearer ZGlzbmV5JmJyb3dzZXImMS4wLjA.Cu56AgSfBTDag5NiRA81oLHkDZfu5L3CKadnefEAY84' 6 | const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36' 7 | 8 | // 即将登陆 9 | const STATUS_COMING = 2 10 | // 支持解锁 11 | const STATUS_AVAILABLE = 1 12 | // 不支持解锁 13 | const STATUS_NOT_AVAILABLE = 0 14 | // 检测超时 15 | const STATUS_TIMEOUT = -1 16 | // 检测异常 17 | const STATUS_ERROR = -2 18 | 19 | ; (async () => { 20 | //测试当前状态 21 | let { region, status } = await testDisneyPlus() 22 | let newStatus = status 23 | let reg = region 24 | if (newStatus < 0) { 25 | console.log("连接超时了,再测一次") 26 | await timeout(1000).catch(() => { }) 27 | let { region, status } = await testDisneyPlus() 28 | newStatus = status 29 | reg = region 30 | } 31 | if (newStatus === 1) { 32 | console.log("当前节点仍可用 退出检测") 33 | } else { 34 | 35 | 36 | let disneyGroup = $persistentStore.read("DISNEYGROUP") 37 | let proxy = await httpAPI("/v1/policy_groups"); 38 | let groupName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(disneyGroup) + "")).policy; 39 | 40 | let allGroup = []; 41 | for (var key in proxy) { 42 | allGroup.push(key) 43 | } 44 | var regData 45 | if ($persistentStore.read("DISNETYREG") == null) { 46 | regData = {} 47 | } else { 48 | regData = JSON.parse($persistentStore.read("DISNETYREG")) 49 | } 50 | var statusData 51 | if ($persistentStore.read("DISNETYSTATUS") == null) { 52 | statusData = {} 53 | } else { 54 | statusData = JSON.parse($persistentStore.read("DISNETYSTATUS")) 55 | } 56 | 57 | let dataname; 58 | var unlocked = [] 59 | var selectFU = [] 60 | 61 | if ($persistentStore.read("DISNEYUNLOCKED") == null) { 62 | } else { 63 | //读取持久化数据 64 | unlocked = $persistentStore.read("DISNEYUNLOCKED").split(","); 65 | //清除空值 66 | del(unlocked, "") 67 | } 68 | 69 | /* 测试当选策略组节点状态并记录数据 */ 70 | 71 | var selectName = [] 72 | let select = proxy["" + groupName + ""]; 73 | for (let i = 0; i < select.length; ++i) { 74 | selectName.push(select[i].name); 75 | } 76 | //去除历史数据 77 | for (let i = 0; i < selectName.length; ++i) { 78 | if (unlocked.includes(selectName[i]) == true) { 79 | del(unlocked, selectName[i]) 80 | } 81 | } 82 | 83 | //遍历检测当选策略 84 | console.log("当前检测:" + groupName) 85 | for (let i = 0; i < selectName.length; ++i) { 86 | //切换节点 87 | $surge.setSelectGroupPolicy(groupName, selectName[i]); 88 | //等待 89 | await timeout(1000).catch(() => { }) 90 | //执行测试 91 | let { region, status } = await testDisneyPlus() 92 | newStatus = status 93 | reg = region 94 | /* 检测超时 再测一次 */ 95 | if (newStatus < 0) { 96 | console.log(selectName[i] + ": 连接超时了,再测一次") 97 | await timeout(1000).catch(() => { }) 98 | let { region, status } = await testDisneyPlus() 99 | newStatus = status 100 | reg = region 101 | } 102 | console.log("检测结果:" + selectName[i] + " | " + statusName(newStatus)) 103 | 104 | //填充数据 105 | dataname = selectName[i] 106 | regData[dataname] = reg 107 | statusData[dataname] = newStatus 108 | if (newStatus === 1) { 109 | if (unlocked.includes(selectName[i]) == false) { 110 | unlocked.push(selectName[i]) 111 | selectFU.push(selectName[i]) 112 | } 113 | } 114 | 115 | //找到全解锁节点 退出检测 116 | if (newStatus === 1) { 117 | console.log("找到可用节点 退出检测") 118 | break; 119 | } 120 | } 121 | 122 | //设定节点 123 | if (selectFU.length > 0) { 124 | $surge.setSelectGroupPolicy(groupName, selectFU[0]); 125 | } else { 126 | $surge.setSelectGroupPolicy(groupName, selectName[0]); 127 | } 128 | 129 | // 创建持久化数据 130 | 131 | $persistentStore.write(unlocked.toString(), "DISNEYUNLOCKED"); 132 | $persistentStore.write(JSON.stringify(regData), "DISNETYREG") 133 | $persistentStore.write(JSON.stringify(statusData), "DISNETYSTATUS") 134 | 135 | //打印测试结果 136 | console.log("可解锁:" + unlocked.sort()) 137 | } 138 | 139 | $done() 140 | 141 | })() 142 | 143 | async function testDisneyPlus() { 144 | try { 145 | let { region, cnbl } = await Promise.race([testHomePage(), timeout(3000)]) 146 | 147 | let { countryCode, inSupportedLocation, accessToken } = await Promise.race([getLocationInfo(), timeout(3000)]) 148 | 149 | region = countryCode ?? region 150 | // 即将登陆 151 | if (inSupportedLocation === false || inSupportedLocation === 'false') { 152 | return { region, status: STATUS_COMING } 153 | } 154 | 155 | let support = await Promise.race([testPublicGraphqlAPI(accessToken), timeout(3000)]) 156 | if (!support) { 157 | return { status: STATUS_NOT_AVAILABLE } 158 | } 159 | // 支持解锁 160 | return { region, status: STATUS_AVAILABLE } 161 | } catch (error) { 162 | console.log(error) 163 | 164 | // 不支持解锁 165 | if (error === 'Not Available') { 166 | return { status: STATUS_NOT_AVAILABLE } 167 | } 168 | 169 | // 检测超时 170 | if (error === 'Timeout') { 171 | return { status: STATUS_TIMEOUT } 172 | } 173 | 174 | return { status: STATUS_ERROR } 175 | } 176 | } 177 | 178 | function testPublicGraphqlAPI(accessToken) { 179 | return new Promise((resolve, reject) => { 180 | let opts = { 181 | url: 'https://disney.api.edge.bamgrid.com/v1/public/graphql', 182 | headers: { 183 | 'Accept-Language': 'en', 184 | Authorization: accessToken, 185 | 'Content-Type': 'application/json', 186 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36', 187 | }, 188 | body: JSON.stringify({ 189 | query: 190 | 'query($preferredLanguages: [String!]!, $version: String) {globalization(version: $version) { uiLanguage(preferredLanguages: $preferredLanguages) }}', 191 | variables: { version: '1.5.0', preferredLanguages: ['en'] }, 192 | }), 193 | } 194 | 195 | $httpClient.post(opts, function (error, response, data) { 196 | if (error) { 197 | reject('Error') 198 | return 199 | } 200 | resolve(response.status === 200) 201 | }) 202 | }) 203 | } 204 | 205 | function getLocationInfo() { 206 | return new Promise((resolve, reject) => { 207 | let opts = { 208 | url: 'https://disney.api.edge.bamgrid.com/graph/v1/device/graphql', 209 | headers: { 210 | 'Accept-Language': 'en', 211 | Authorization: 'ZGlzbmV5JmJyb3dzZXImMS4wLjA.Cu56AgSfBTDag5NiRA81oLHkDZfu5L3CKadnefEAY84', 212 | 'Content-Type': 'application/json', 213 | 'User-Agent': UA, 214 | }, 215 | body: JSON.stringify({ 216 | query: 'mutation registerDevice($input: RegisterDeviceInput!) { registerDevice(registerDevice: $input) { grant { grantType assertion } } }', 217 | variables: { 218 | input: { 219 | applicationRuntime: 'chrome', 220 | attributes: { 221 | browserName: 'chrome', 222 | browserVersion: '94.0.4606', 223 | manufacturer: 'apple', 224 | model: null, 225 | operatingSystem: 'macintosh', 226 | operatingSystemVersion: '10.15.7', 227 | osDeviceIds: [], 228 | }, 229 | deviceFamily: 'browser', 230 | deviceLanguage: 'en', 231 | deviceProfile: 'macosx', 232 | }, 233 | }, 234 | }), 235 | } 236 | 237 | $httpClient.post(opts, function (error, response, data) { 238 | if (error) { 239 | reject('Error') 240 | return 241 | } 242 | 243 | if (response.status !== 200) { 244 | console.log('getLocationInfo: ' + data) 245 | reject('Not Available') 246 | return 247 | } 248 | 249 | data = JSON.parse(data) 250 | if(data?.errors){ 251 | console.log('getLocationInfo: ' + data) 252 | reject('Not Available') 253 | return 254 | } 255 | 256 | let { 257 | token: { accessToken }, 258 | session: { 259 | inSupportedLocation, 260 | location: { countryCode }, 261 | }, 262 | } = data?.extensions?.sdk 263 | resolve({ inSupportedLocation, countryCode, accessToken }) 264 | }) 265 | }) 266 | } 267 | 268 | function testHomePage() { 269 | return new Promise((resolve, reject) => { 270 | let opts = { 271 | url: 'https://www.disneyplus.com/', 272 | headers: { 273 | 'Accept-Language': 'en', 274 | 'User-Agent': UA, 275 | }, 276 | } 277 | 278 | $httpClient.get(opts, function (error, response, data) { 279 | if (error) { 280 | reject('Error') 281 | return 282 | } 283 | if (response.status !== 200 || data.indexOf('Sorry, Disney+ is not available in your region.') !== -1) { 284 | reject('Not Available') 285 | return 286 | } 287 | 288 | let match = data.match(/Region: ([A-Za-z]{2})[\s\S]*?CNBL: ([12])/) 289 | if (!match) { 290 | resolve({ region: '', cnbl: '' }) 291 | return 292 | } 293 | 294 | let region = match[1] 295 | let cnbl = match[2] 296 | resolve({ region, cnbl }) 297 | }) 298 | }) 299 | } 300 | 301 | function timeout(delay = 5000) { 302 | return new Promise((resolve, reject) => { 303 | setTimeout(() => { 304 | reject('Timeout') 305 | }, delay) 306 | }) 307 | } 308 | 309 | 310 | function replaceRegionPlaceholder(content, region) { 311 | let result = content 312 | 313 | if (result.indexOf('#REGION_CODE#') !== -1) { 314 | result = result.replaceAll('#REGION_CODE#', region.toUpperCase()) 315 | } 316 | if (result.indexOf('#REGION_FLAG#') !== -1) { 317 | result = result.replaceAll('#REGION_FLAG#', getCountryFlagEmoji(region.toUpperCase())) 318 | } 319 | 320 | if (result.indexOf('#REGION_NAME#') !== -1) { 321 | result = result.replaceAll('#REGION_NAME#', RESION_NAMES?.[region.toUpperCase()]?.chinese ?? '') 322 | } 323 | 324 | if (result.indexOf('#REGION_NAME_EN#') !== -1) { 325 | result = result.replaceAll('#REGION_NAME_EN#', RESION_NAMES?.[region.toUpperCase()]?.english ?? '') 326 | } 327 | 328 | return result 329 | } 330 | 331 | function httpAPI(path = "", method = "GET", body = null) { 332 | return new Promise((resolve) => { 333 | $httpAPI(method, path, body, (result) => { 334 | resolve(result); 335 | }); 336 | }); 337 | }; 338 | 339 | function statusName(status) { 340 | return status == 2 ? "即将登陆" 341 | : status == 1 ? "已解锁" 342 | : status == 0 ? "不解锁" 343 | : status == -1 ? "检测超时" 344 | : "检测异常"; 345 | } 346 | 347 | function del(arr, num) { 348 | var l = arr.length; 349 | for (var i = 0; i < l; i++) { 350 | if (arr[0] !== num) { 351 | arr.push(arr[0]); 352 | } 353 | arr.shift(arr[0]); 354 | } 355 | return arr; 356 | } 357 | -------------------------------------------------------------------------------- /DisneySelect/disneyshortcut.js: -------------------------------------------------------------------------------- 1 | /* 脚本经本人测试已经可以正常运行,但仍可能存在bug,使用过程中遇到障碍请联系Telegram:https://t.me/okmytg 2 | 脚本说明:https://github.com/fishingworld/something/blob/main/DisneySelect/README.md 3 | */ 4 | 5 | const AUTHORIZATION = 'Bearer ZGlzbmV5JmJyb3dzZXImMS4wLjA.Cu56AgSfBTDag5NiRA81oLHkDZfu5L3CKadnefEAY84' 6 | const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36' 7 | 8 | // 即将登陆 9 | const STATUS_COMING = 2 10 | // 支持解锁 11 | const STATUS_AVAILABLE = 1 12 | // 不支持解锁 13 | const STATUS_NOT_AVAILABLE = 0 14 | // 检测超时 15 | const STATUS_TIMEOUT = -1 16 | // 检测异常 17 | const STATUS_ERROR = -2 18 | 19 | ; (async () => { 20 | 21 | let disneyGroup = $persistentStore.read("DISNEYGROUP") 22 | let proxy = await httpAPI("/v1/policy_groups"); 23 | let groupName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(disneyGroup) + "")).policy; 24 | 25 | let allGroup = []; 26 | for (var key in proxy) { 27 | allGroup.push(key) 28 | } 29 | 30 | //测试当前状态 31 | let { region, status } = await testDisneyPlus() 32 | let newStatus = status 33 | let reg = region 34 | if (newStatus < 0) { 35 | console.log("连接超时了,再测一次") 36 | await timeout(1000).catch(() => { }) 37 | let { region, status } = await testDisneyPlus() 38 | newStatus = status 39 | reg = region 40 | } 41 | if (newStatus === 1) { 42 | console.log("当前节点仍可用 退出检测") 43 | } else { 44 | 45 | var regData 46 | if ($persistentStore.read("DISNETYREG") == null) { 47 | regData = {} 48 | } else { 49 | regData = JSON.parse($persistentStore.read("DISNETYREG")) 50 | } 51 | var statusData 52 | if ($persistentStore.read("DISNETYSTATUS") == null) { 53 | statusData = {} 54 | } else { 55 | statusData = JSON.parse($persistentStore.read("DISNETYSTATUS")) 56 | } 57 | 58 | 59 | let dataname; 60 | var unlocked = [] 61 | var selectFU = [] 62 | 63 | if ($persistentStore.read("DISNEYUNLOCKED") == null) { 64 | } else { 65 | //读取持久化数据 66 | unlocked = $persistentStore.read("DISNEYUNLOCKED").split(","); 67 | //清除空值 68 | del(unlocked, "") 69 | } 70 | 71 | /* 测试当选策略组节点状态并记录数据 */ 72 | 73 | var selectName = [] 74 | let select = proxy["" + groupName + ""]; 75 | for (let i = 0; i < select.length; ++i) { 76 | selectName.push(select[i].name); 77 | } 78 | //去除历史数据 79 | for (let i = 0; i < selectName.length; ++i) { 80 | if (unlocked.includes(selectName[i]) == true) { 81 | del(unlocked, selectName[i]) 82 | } 83 | } 84 | 85 | //遍历检测当选策略 86 | console.log("当前检测:" + groupName) 87 | for (let i = 0; i < selectName.length; ++i) { 88 | //切换节点 89 | $surge.setSelectGroupPolicy(groupName, selectName[i]); 90 | //等待 91 | await timeout(1000).catch(() => { }) 92 | //执行测试 93 | let { region, status } = await testDisneyPlus() 94 | newStatus = status 95 | reg = region 96 | /* 检测超时 再测一次 */ 97 | if (newStatus < 0) { 98 | console.log(selectName[i] + ": 连接超时了,再测一次") 99 | await timeout(1000).catch(() => { }) 100 | let { region, status } = await testDisneyPlus() 101 | newStatus = status 102 | reg = region 103 | } 104 | console.log("检测结果:" + selectName[i] + " | " + statusName(newStatus)) 105 | 106 | //填充数据 107 | dataname = selectName[i] 108 | regData[dataname] = reg 109 | statusData[dataname] = newStatus 110 | if (newStatus === 1) { 111 | if (unlocked.includes(selectName[i]) == false) { 112 | unlocked.push(selectName[i]) 113 | selectFU.push(selectName[i]) 114 | } 115 | } 116 | 117 | //找到全解锁节点 退出检测 118 | if (newStatus === 1) { 119 | console.log("找到可用节点 退出检测") 120 | break; 121 | } 122 | } 123 | 124 | //设定节点 125 | if (selectFU.length > 0) { 126 | $surge.setSelectGroupPolicy(groupName, selectFU[0]); 127 | } else { 128 | $surge.setSelectGroupPolicy(groupName, selectName[0]); 129 | } 130 | 131 | // 创建持久化数据 132 | 133 | $persistentStore.write(unlocked.toString(), "DISNEYUNLOCKED"); 134 | $persistentStore.write(JSON.stringify(regData), "DISNETYREG") 135 | $persistentStore.write(JSON.stringify(statusData), "DISNETYSTATUS") 136 | 137 | 138 | //打印测试结果 139 | console.log("可解锁:" + unlocked.sort()) 140 | } 141 | 142 | //获取根节点名 143 | let rootName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(disneyGroup) + "")).policy; 144 | while (allGroup.includes(rootName) == true) { 145 | rootName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(rootName) + "")).policy; 146 | } 147 | 148 | let info 149 | if (newStatus === 1) { 150 | info = `已选定节点: ${rootName} | ${statusName(newStatus)} | ${reg}` 151 | } else if (statusData[rootName] == 2) { 152 | info = `该地区即将上线 敬请期待哦~` 153 | } else { 154 | info = "该策略组暂无可供支援的节点" 155 | } 156 | 157 | $notification.post("Disney檢測", info, "") 158 | 159 | 160 | $done() 161 | 162 | })() 163 | 164 | async function testDisneyPlus() { 165 | try { 166 | let { region, cnbl } = await Promise.race([testHomePage(), timeout(3000)]) 167 | 168 | let { countryCode, inSupportedLocation, accessToken } = await Promise.race([getLocationInfo(), timeout(3000)]) 169 | 170 | region = countryCode ?? region 171 | // 即将登陆 172 | if (inSupportedLocation === false || inSupportedLocation === 'false') { 173 | return { region, status: STATUS_COMING } 174 | } 175 | 176 | let support = await Promise.race([testPublicGraphqlAPI(accessToken), timeout(3000)]) 177 | if (!support) { 178 | return { status: STATUS_NOT_AVAILABLE } 179 | } 180 | // 支持解锁 181 | return { region, status: STATUS_AVAILABLE } 182 | } catch (error) { 183 | console.log(error) 184 | 185 | // 不支持解锁 186 | if (error === 'Not Available') { 187 | return { status: STATUS_NOT_AVAILABLE } 188 | } 189 | 190 | // 检测超时 191 | if (error === 'Timeout') { 192 | return { status: STATUS_TIMEOUT } 193 | } 194 | 195 | return { status: STATUS_ERROR } 196 | } 197 | } 198 | 199 | function testPublicGraphqlAPI(accessToken) { 200 | return new Promise((resolve, reject) => { 201 | let opts = { 202 | url: 'https://disney.api.edge.bamgrid.com/v1/public/graphql', 203 | headers: { 204 | 'Accept-Language': 'en', 205 | Authorization: accessToken, 206 | 'Content-Type': 'application/json', 207 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36', 208 | }, 209 | body: JSON.stringify({ 210 | query: 211 | 'query($preferredLanguages: [String!]!, $version: String) {globalization(version: $version) { uiLanguage(preferredLanguages: $preferredLanguages) }}', 212 | variables: { version: '1.5.0', preferredLanguages: ['en'] }, 213 | }), 214 | } 215 | 216 | $httpClient.post(opts, function (error, response, data) { 217 | if (error) { 218 | reject('Error') 219 | return 220 | } 221 | resolve(response.status === 200) 222 | }) 223 | }) 224 | } 225 | 226 | function getLocationInfo() { 227 | return new Promise((resolve, reject) => { 228 | let opts = { 229 | url: 'https://disney.api.edge.bamgrid.com/graph/v1/device/graphql', 230 | headers: { 231 | 'Accept-Language': 'en', 232 | Authorization: 'ZGlzbmV5JmJyb3dzZXImMS4wLjA.Cu56AgSfBTDag5NiRA81oLHkDZfu5L3CKadnefEAY84', 233 | 'Content-Type': 'application/json', 234 | 'User-Agent': UA, 235 | }, 236 | body: JSON.stringify({ 237 | query: 'mutation registerDevice($input: RegisterDeviceInput!) { registerDevice(registerDevice: $input) { grant { grantType assertion } } }', 238 | variables: { 239 | input: { 240 | applicationRuntime: 'chrome', 241 | attributes: { 242 | browserName: 'chrome', 243 | browserVersion: '94.0.4606', 244 | manufacturer: 'apple', 245 | model: null, 246 | operatingSystem: 'macintosh', 247 | operatingSystemVersion: '10.15.7', 248 | osDeviceIds: [], 249 | }, 250 | deviceFamily: 'browser', 251 | deviceLanguage: 'en', 252 | deviceProfile: 'macosx', 253 | }, 254 | }, 255 | }), 256 | } 257 | 258 | $httpClient.post(opts, function (error, response, data) { 259 | if (error) { 260 | reject('Error') 261 | return 262 | } 263 | 264 | if (response.status !== 200) { 265 | console.log('getLocationInfo: ' + data) 266 | reject('Not Available') 267 | return 268 | } 269 | 270 | data = JSON.parse(data) 271 | if(data?.errors){ 272 | console.log('getLocationInfo: ' + data) 273 | reject('Not Available') 274 | return 275 | } 276 | 277 | let { 278 | token: { accessToken }, 279 | session: { 280 | inSupportedLocation, 281 | location: { countryCode }, 282 | }, 283 | } = data?.extensions?.sdk 284 | resolve({ inSupportedLocation, countryCode, accessToken }) 285 | }) 286 | }) 287 | } 288 | 289 | function testHomePage() { 290 | return new Promise((resolve, reject) => { 291 | let opts = { 292 | url: 'https://www.disneyplus.com/', 293 | headers: { 294 | 'Accept-Language': 'en', 295 | 'User-Agent': UA, 296 | }, 297 | } 298 | 299 | $httpClient.get(opts, function (error, response, data) { 300 | if (error) { 301 | reject('Error') 302 | return 303 | } 304 | if (response.status !== 200 || data.indexOf('Sorry, Disney+ is not available in your region.') !== -1) { 305 | reject('Not Available') 306 | return 307 | } 308 | 309 | let match = data.match(/Region: ([A-Za-z]{2})[\s\S]*?CNBL: ([12])/) 310 | if (!match) { 311 | resolve({ region: '', cnbl: '' }) 312 | return 313 | } 314 | 315 | let region = match[1] 316 | let cnbl = match[2] 317 | resolve({ region, cnbl }) 318 | }) 319 | }) 320 | } 321 | 322 | function timeout(delay = 5000) { 323 | return new Promise((resolve, reject) => { 324 | setTimeout(() => { 325 | reject('Timeout') 326 | }, delay) 327 | }) 328 | } 329 | 330 | 331 | function replaceRegionPlaceholder(content, region) { 332 | let result = content 333 | 334 | if (result.indexOf('#REGION_CODE#') !== -1) { 335 | result = result.replaceAll('#REGION_CODE#', region.toUpperCase()) 336 | } 337 | if (result.indexOf('#REGION_FLAG#') !== -1) { 338 | result = result.replaceAll('#REGION_FLAG#', getCountryFlagEmoji(region.toUpperCase())) 339 | } 340 | 341 | if (result.indexOf('#REGION_NAME#') !== -1) { 342 | result = result.replaceAll('#REGION_NAME#', RESION_NAMES?.[region.toUpperCase()]?.chinese ?? '') 343 | } 344 | 345 | if (result.indexOf('#REGION_NAME_EN#') !== -1) { 346 | result = result.replaceAll('#REGION_NAME_EN#', RESION_NAMES?.[region.toUpperCase()]?.english ?? '') 347 | } 348 | 349 | return result 350 | } 351 | 352 | function httpAPI(path = "", method = "GET", body = null) { 353 | return new Promise((resolve) => { 354 | $httpAPI(method, path, body, (result) => { 355 | resolve(result); 356 | }); 357 | }); 358 | }; 359 | 360 | function statusName(status) { 361 | return status == 2 ? "即将登陆" 362 | : status == 1 ? "已解锁" 363 | : status == 0 ? "不解锁" 364 | : status == -1 ? "检测超时" 365 | : "检测异常"; 366 | } 367 | 368 | function del(arr, num) { 369 | var l = arr.length; 370 | for (var i = 0; i < l; i++) { 371 | if (arr[0] !== num) { 372 | arr.push(arr[0]); 373 | } 374 | arr.shift(arr[0]); 375 | } 376 | return arr; 377 | } 378 | -------------------------------------------------------------------------------- /DisneySelect/disneycontroller.js: -------------------------------------------------------------------------------- 1 | /* 脚本经本人测试已经可以正常运行,但仍可能存在bug,使用过程中遇到障碍请联系Telegram:https://t.me/okmytg 2 | 脚本说明:https://github.com/fishingworld/something/blob/main/DisneySelect/README.md 3 | */ 4 | 5 | const AUTHORIZATION = 'Bearer ZGlzbmV5JmJyb3dzZXImMS4wLjA.Cu56AgSfBTDag5NiRA81oLHkDZfu5L3CKadnefEAY84' 6 | const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36' 7 | 8 | // 即将登陆 9 | const STATUS_COMING = 2 10 | // 支持解锁 11 | const STATUS_AVAILABLE = 1 12 | // 不支持解锁 13 | const STATUS_NOT_AVAILABLE = 0 14 | // 检测超时 15 | const STATUS_TIMEOUT = -1 16 | // 检测异常 17 | const STATUS_ERROR = -2 18 | 19 | ; (async () => { 20 | 21 | let params = getParams($argument) 22 | let disneyGroup = params.disneyGroup 23 | //将策略组名称创建为持久化数据 24 | $persistentStore.write(disneyGroup, "DISNEYGROUP"); 25 | 26 | let proxy = await httpAPI("/v1/policy_groups"); 27 | let groupName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(disneyGroup) + "")).policy; 28 | var proxyName = [];//Disney节点组名称 29 | let arr = proxy["" + disneyGroup + ""]; 30 | for (let i = 0; i < arr.length; ++i) { 31 | proxyName.push(arr[i].name); 32 | } 33 | let allGroup = []; 34 | for (var key in proxy) { 35 | allGroup.push(key) 36 | } 37 | 38 | /* 手动切换策略 */ 39 | let index; 40 | for (let i = 0; i < proxyName.length; ++i) { 41 | if (groupName == proxyName[i]) { 42 | index = i 43 | } 44 | }; 45 | if ($trigger == "button") { 46 | index += 1; 47 | if (index > arr.length - 1) { 48 | index = 0; 49 | } 50 | $surge.setSelectGroupPolicy(disneyGroup, proxyName[index]); 51 | }; 52 | 53 | groupName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(disneyGroup) + "")).policy; 54 | 55 | /* 判断节点列表是否为空 */ 56 | var regData 57 | if ($persistentStore.read("DISNETYREG") == null) { 58 | regData = {} 59 | } else { 60 | regData = JSON.parse($persistentStore.read("DISNETYREG")) 61 | } 62 | var statusData 63 | if ($persistentStore.read("DISNETYSTATUS") == null) { 64 | statusData = {} 65 | } else { 66 | statusData = JSON.parse($persistentStore.read("DISNETYSTATUS")) 67 | } 68 | 69 | let dataname; 70 | var unlocked = [] 71 | var selectFU = [] 72 | 73 | if ($persistentStore.read("DISNEYUNLOCKED") == null) { 74 | } else { 75 | //读取持久化数据 76 | unlocked = $persistentStore.read("DISNEYUNLOCKED").split(","); 77 | //清除空值 78 | del(unlocked, "") 79 | } 80 | 81 | var selectName = [] 82 | let select = proxy["" + groupName + ""]; 83 | for (let i = 0; i < select.length; ++i) { 84 | selectName.push(select[i].name); 85 | } 86 | 87 | for (let i = 0; i < selectName.length; ++i) { 88 | if (unlocked.includes(selectName[i]) == true) { 89 | selectFU.push(selectName[i]) 90 | } 91 | } 92 | 93 | // 为空时执行检测 94 | if (selectFU.length == 0) { 95 | //遍历检测当选策略 96 | console.log("当前检测:" + groupName) 97 | let newStatus; 98 | let reg; 99 | for (let i = 0; i < selectName.length; ++i) { 100 | //切换节点 101 | $surge.setSelectGroupPolicy(groupName, selectName[i]); 102 | //等待 103 | await timeout(1000).catch(() => { }) 104 | //执行测试 105 | let { region, status } = await testDisneyPlus() 106 | newStatus = status 107 | reg = region 108 | /* 检测超时 再测一次 */ 109 | if (newStatus < 0) { 110 | console.log(selectName[i] + ": 连接超时了,再测一次") 111 | await timeout(1000).catch(() => { }) 112 | let { region, status } = await testDisneyPlus() 113 | newStatus = status 114 | reg = region 115 | } 116 | console.log("检测结果:" + selectName[i] + " | " + statusName(newStatus)) 117 | 118 | //填充数据 119 | dataname = selectName[i] 120 | regData[dataname] = reg 121 | statusData[dataname] = newStatus 122 | if (newStatus === 1) { 123 | if (unlocked.includes(selectName[i]) == false) { 124 | unlocked.push(selectName[i]) 125 | selectFU.push(selectName[i]) 126 | } 127 | } 128 | 129 | //找到全解锁节点 退出检测 130 | if (newStatus === 1) { 131 | console.log("找到可用节点 退出检测") 132 | break; 133 | } 134 | } 135 | 136 | // 更新持久化数据 137 | $persistentStore.write(unlocked.toString(), "DISNEYUNLOCKED"); 138 | $persistentStore.write(JSON.stringify(regData), "DISNETYREG") 139 | $persistentStore.write(JSON.stringify(statusData), "DISNETYSTATUS") 140 | 141 | 142 | } 143 | 144 | //设定节点 145 | if (selectFU.length > 0) { 146 | $surge.setSelectGroupPolicy(groupName, selectFU[0]); 147 | } else { 148 | $surge.setSelectGroupPolicy(groupName, selectName[0]); 149 | } 150 | 151 | /* 刷新信息 */ 152 | //获取根节点名 153 | let rootName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(disneyGroup) + "")).policy; 154 | while (allGroup.includes(rootName) == true) { 155 | rootName = (await httpAPI("/v1/policy_groups/select?group_name=" + encodeURIComponent(rootName) + "")).policy; 156 | } 157 | 158 | /** 159 | * 面板显示 160 | */ 161 | 162 | let title = "Disney+ ➟ " + rootName; 163 | 164 | let panel = { 165 | title: `${title}`, 166 | } 167 | 168 | if (statusData[rootName] == 1) { 169 | panel['content'] = `支援Disney+ 地区:${regData[rootName]}` 170 | panel['icon'] = params.icon1 171 | panel['icon-color'] = params.color1 172 | } else if (statusData[rootName] == 2) { 173 | panel['content'] = `即将登陆 敬请期待~` 174 | panel['icon'] = params.icon2 175 | panel['icon-color'] = params.color2 176 | } else { 177 | panel['content'] = `暂无可供支援的节点呢~` 178 | panel['icon'] = params.icon3 179 | panel['icon-color'] = params.color3 180 | } 181 | 182 | $done(panel) 183 | 184 | 185 | })() 186 | 187 | async function testDisneyPlus() { 188 | try { 189 | let { region, cnbl } = await Promise.race([testHomePage(), timeout(3000)]) 190 | 191 | let { countryCode, inSupportedLocation, accessToken } = await Promise.race([getLocationInfo(), timeout(3000)]) 192 | 193 | region = countryCode ?? region 194 | // 即将登陆 195 | if (inSupportedLocation === false || inSupportedLocation === 'false') { 196 | return { region, status: STATUS_COMING } 197 | } 198 | 199 | let support = await Promise.race([testPublicGraphqlAPI(accessToken), timeout(3000)]) 200 | if (!support) { 201 | return { status: STATUS_NOT_AVAILABLE } 202 | } 203 | // 支持解锁 204 | return { region, status: STATUS_AVAILABLE } 205 | } catch (error) { 206 | console.log(error) 207 | 208 | // 不支持解锁 209 | if (error === 'Not Available') { 210 | return { status: STATUS_NOT_AVAILABLE } 211 | } 212 | 213 | // 检测超时 214 | if (error === 'Timeout') { 215 | return { status: STATUS_TIMEOUT } 216 | } 217 | 218 | return { status: STATUS_ERROR } 219 | } 220 | } 221 | 222 | function testPublicGraphqlAPI(accessToken) { 223 | return new Promise((resolve, reject) => { 224 | let opts = { 225 | url: 'https://disney.api.edge.bamgrid.com/v1/public/graphql', 226 | headers: { 227 | 'Accept-Language': 'en', 228 | Authorization: accessToken, 229 | 'Content-Type': 'application/json', 230 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36', 231 | }, 232 | body: JSON.stringify({ 233 | query: 234 | 'query($preferredLanguages: [String!]!, $version: String) {globalization(version: $version) { uiLanguage(preferredLanguages: $preferredLanguages) }}', 235 | variables: { version: '1.5.0', preferredLanguages: ['en'] }, 236 | }), 237 | } 238 | 239 | $httpClient.post(opts, function (error, response, data) { 240 | if (error) { 241 | reject('Error') 242 | return 243 | } 244 | resolve(response.status === 200) 245 | }) 246 | }) 247 | } 248 | 249 | function getLocationInfo() { 250 | return new Promise((resolve, reject) => { 251 | let opts = { 252 | url: 'https://disney.api.edge.bamgrid.com/graph/v1/device/graphql', 253 | headers: { 254 | 'Accept-Language': 'en', 255 | Authorization: 'ZGlzbmV5JmJyb3dzZXImMS4wLjA.Cu56AgSfBTDag5NiRA81oLHkDZfu5L3CKadnefEAY84', 256 | 'Content-Type': 'application/json', 257 | 'User-Agent': UA, 258 | }, 259 | body: JSON.stringify({ 260 | query: 'mutation registerDevice($input: RegisterDeviceInput!) { registerDevice(registerDevice: $input) { grant { grantType assertion } } }', 261 | variables: { 262 | input: { 263 | applicationRuntime: 'chrome', 264 | attributes: { 265 | browserName: 'chrome', 266 | browserVersion: '94.0.4606', 267 | manufacturer: 'apple', 268 | model: null, 269 | operatingSystem: 'macintosh', 270 | operatingSystemVersion: '10.15.7', 271 | osDeviceIds: [], 272 | }, 273 | deviceFamily: 'browser', 274 | deviceLanguage: 'en', 275 | deviceProfile: 'macosx', 276 | }, 277 | }, 278 | }), 279 | } 280 | 281 | $httpClient.post(opts, function (error, response, data) { 282 | if (error) { 283 | reject('Error') 284 | return 285 | } 286 | 287 | if (response.status !== 200) { 288 | console.log('getLocationInfo: ' + data) 289 | reject('Not Available') 290 | return 291 | } 292 | 293 | data = JSON.parse(data) 294 | if(data?.errors){ 295 | console.log('getLocationInfo: ' + data) 296 | reject('Not Available') 297 | return 298 | } 299 | 300 | let { 301 | token: { accessToken }, 302 | session: { 303 | inSupportedLocation, 304 | location: { countryCode }, 305 | }, 306 | } = data?.extensions?.sdk 307 | resolve({ inSupportedLocation, countryCode, accessToken }) 308 | }) 309 | }) 310 | } 311 | 312 | function testHomePage() { 313 | return new Promise((resolve, reject) => { 314 | let opts = { 315 | url: 'https://www.disneyplus.com/', 316 | headers: { 317 | 'Accept-Language': 'en', 318 | 'User-Agent': UA, 319 | }, 320 | } 321 | 322 | $httpClient.get(opts, function (error, response, data) { 323 | if (error) { 324 | reject('Error') 325 | return 326 | } 327 | if (response.status !== 200 || data.indexOf('Sorry, Disney+ is not available in your region.') !== -1) { 328 | reject('Not Available') 329 | return 330 | } 331 | 332 | let match = data.match(/Region: ([A-Za-z]{2})[\s\S]*?CNBL: ([12])/) 333 | if (!match) { 334 | resolve({ region: '', cnbl: '' }) 335 | return 336 | } 337 | 338 | let region = match[1] 339 | let cnbl = match[2] 340 | resolve({ region, cnbl }) 341 | }) 342 | }) 343 | } 344 | 345 | function timeout(delay = 5000) { 346 | return new Promise((resolve, reject) => { 347 | setTimeout(() => { 348 | reject('Timeout') 349 | }, delay) 350 | }) 351 | } 352 | 353 | 354 | function replaceRegionPlaceholder(content, region) { 355 | let result = content 356 | 357 | if (result.indexOf('#REGION_CODE#') !== -1) { 358 | result = result.replaceAll('#REGION_CODE#', region.toUpperCase()) 359 | } 360 | if (result.indexOf('#REGION_FLAG#') !== -1) { 361 | result = result.replaceAll('#REGION_FLAG#', getCountryFlagEmoji(region.toUpperCase())) 362 | } 363 | 364 | if (result.indexOf('#REGION_NAME#') !== -1) { 365 | result = result.replaceAll('#REGION_NAME#', RESION_NAMES?.[region.toUpperCase()]?.chinese ?? '') 366 | } 367 | 368 | if (result.indexOf('#REGION_NAME_EN#') !== -1) { 369 | result = result.replaceAll('#REGION_NAME_EN#', RESION_NAMES?.[region.toUpperCase()]?.english ?? '') 370 | } 371 | 372 | return result 373 | } 374 | 375 | 376 | function httpAPI(path = "", method = "GET", body = null) { 377 | return new Promise((resolve) => { 378 | $httpAPI(method, path, body, (result) => { 379 | resolve(result); 380 | }); 381 | }); 382 | }; 383 | 384 | function getParams(param) { 385 | return Object.fromEntries( 386 | $argument 387 | .split("&") 388 | .map((item) => item.split("=")) 389 | .map(([k, v]) => [k, decodeURIComponent(v)]) 390 | ); 391 | } 392 | 393 | function statusName(status) { 394 | return status == 2 ? "即将登陆" 395 | : status == 1 ? "已解锁" 396 | : status == 0 ? "不解锁" 397 | : status == -1 ? "检测超时" 398 | : "检测异常"; 399 | } 400 | 401 | function del(arr, num) { 402 | var l = arr.length; 403 | for (var i = 0; i < l; i++) { 404 | if (arr[0] !== num) { 405 | arr.push(arr[0]); 406 | } 407 | arr.shift(arr[0]); 408 | } 409 | return arr; 410 | } 411 | --------------------------------------------------------------------------------