猫抓 - 深度搜索

猫抓扩展提取出来的深度搜索脚本。

目前为 2023-07-29 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name 猫抓 - 深度搜索
  3. // @namespace https://bmmmd.com
  4. // @version 2.4.4.0
  5. // @description 猫抓扩展提取出来的深度搜索脚本。
  6. // @author bmm
  7. // @match http://*/*
  8. // @match https://*/*
  9. // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
  10. // @grant none
  11. // @run-at document-start
  12. // @license GPL v3
  13. // ==/UserScript==
  14.  
  15. (function () {
  16. const _log = console.log;
  17. _log("start search.js");
  18. const CATCH_SEARCH_DEBUG = false;
  19. const filter = new Set();
  20.  
  21. // 拦截JSON.parse 分析内容
  22. const _JSONparse = JSON.parse;
  23. JSON.parse = function () {
  24. let data = _JSONparse.apply(this, arguments);
  25. findMedia(data);
  26. return data;
  27. }
  28. JSON.parse.toString = function () {
  29. return _JSONparse.toString();
  30. }
  31.  
  32. async function findMedia(data, depth = 0) {
  33. CATCH_SEARCH_DEBUG && _log(data);
  34. let index = 0;
  35. if (!data) { return; }
  36. if (data instanceof Array && data.length == 16) {
  37. const isKey = data.every(function (value) {
  38. return typeof value == 'number' && value <= 256
  39. });
  40. if (isKey) {
  41. postData({ action: "catCatchAddKey", key: data, href: location.href, ext: "key" });
  42. return;
  43. }
  44. }
  45. for (let key in data) {
  46. if (index != 0) { depth = 0; } index++;
  47. if (typeof data[key] == "object") {
  48. // 查找疑似key
  49. if (data[key] instanceof Array && data[key].length == 16) {
  50. const isKey = data[key].every(function (value) {
  51. return typeof value == 'number' && value <= 256
  52. });
  53. isKey && postData({ action: "catCatchAddKey", key: data[key], href: location.href, ext: "key" });
  54. continue;
  55. }
  56. if (depth > 10) { continue; } // 防止死循环 最大深度
  57. findMedia(data[key], ++depth);
  58. continue;
  59. }
  60. if (typeof data[key] == "string") {
  61. if (isUrl(data[key])) {
  62. let ext = getExtension(data[key]);
  63. ext && postData({ action: "catCatchAddMedia", url: data[key], href: location.href, ext: ext });
  64. continue;
  65. }
  66. if (data[key].substring(0, 7).toUpperCase() == "#EXTM3U") {
  67. isFullM3u8(data[key]) && toUrl(data[key]);
  68. continue;
  69. }
  70. if (data[key].substring(0, 17).toLowerCase() == "data:application/") {
  71. const text = getDataM3U8(data[key].substring(17));
  72. text && toUrl(text);
  73. continue;
  74. }
  75. if (data[key].toLowerCase().includes("urn:mpeg:dash:schema:mpd")) {
  76. toUrl(data[key], "mpd");
  77. continue;
  78. }
  79. if (CATCH_SEARCH_DEBUG && data[key].includes("manifest")) {
  80. _log(data);
  81. }
  82. }
  83. }
  84. }
  85.  
  86. // 拦截 XHR 分析内容
  87. const _xhrOpen = XMLHttpRequest.prototype.open;
  88. XMLHttpRequest.prototype.open = function (method) {
  89. method = method.toUpperCase();
  90. CATCH_SEARCH_DEBUG && _log(this);
  91. this.addEventListener("readystatechange", function (event) {
  92. CATCH_SEARCH_DEBUG && _log(this);
  93. if (this.status != 200) { return; }
  94. // 查找疑似key
  95. if (this.responseType == "arraybuffer" && this.response?.byteLength && this.response.byteLength == 16) {
  96. postData({ action: "catCatchAddKey", key: this.response, href: location.href, ext: "key" });
  97. }
  98. if (this.response == "" || typeof this.response != "string") { return; }
  99. if (this.response.substring(0, 17).toLowerCase() == "data:application/") {
  100. const text = this.response.substring(17);
  101. toUrl(getDataM3U8(text));
  102. return;
  103. }
  104. if (this.responseURL.substring(0, 17).toLowerCase() == "data:application/") {
  105. const text = getDataM3U8(this.responseURL.substring(17));
  106. text && toUrl(text);
  107. return;
  108. }
  109. if (isUrl(this.response)) {
  110. const ext = getExtension(this.response);
  111. ext && postData({ action: "catCatchAddMedia", url: this.response, href: location.href, ext: ext });
  112. return;
  113. }
  114. if (this.response.toUpperCase().includes("#EXTM3U")) {
  115. if (this.response.substring(0, 7) == "#EXTM3U") {
  116. if (method == "GET") {
  117. postData({ action: "catCatchAddMedia", url: this.responseURL, href: location.href, ext: "m3u8" });
  118. return;
  119. }
  120. isFullM3u8(this.response) && toUrl(this.response);
  121. return;
  122. }
  123. if (isJSON(this.response)) {
  124. if (method == "GET") {
  125. postData({ action: "catCatchAddMedia", url: this.responseURL, href: location.href, ext: "json" });
  126. return;
  127. }
  128. toUrl(this.response, "json");
  129. return;
  130. }
  131. }
  132. const isJson = isJSON(this.response);
  133. if (isJson) {
  134. findMedia(isJson);
  135. return;
  136. }
  137. });
  138. _xhrOpen.apply(this, arguments);
  139. }
  140. XMLHttpRequest.prototype.open.toString = function () {
  141. return _xhrOpen.toString();
  142. }
  143.  
  144. // 拦截 fetch 分析内容
  145. const _fetch = window.fetch;
  146. window.fetch = async function (input, init) {
  147. const response = await _fetch.apply(this, arguments);
  148. const clone = response.clone();
  149. CATCH_SEARCH_DEBUG && _log(response);
  150. response.arrayBuffer()
  151. .then(arrayBuffer => {
  152. CATCH_SEARCH_DEBUG && _log({ arrayBuffer, input });
  153. if (arrayBuffer.byteLength == 16) {
  154. postData({ action: "catCatchAddKey", key: arrayBuffer, href: location.href, ext: "key" });
  155. return;
  156. }
  157. let text = new TextDecoder().decode(arrayBuffer);
  158. if (text == "") { return; }
  159. if (typeof input == "object") { input = input.url; }
  160. let isJson = isJSON(text);
  161. if (isJson) {
  162. findMedia(isJson);
  163. return;
  164. }
  165. if (text.substring(0, 7).toUpperCase() == "#EXTM3U") {
  166. if (init?.method == undefined || (init.method && init.method.toUpperCase() == "GET")) {
  167. postData({ action: "catCatchAddMedia", url: input, href: location.href, ext: "m3u8" });
  168. return;
  169. }
  170. isFullM3u8(text) && toUrl(text);
  171. return;
  172. }
  173. if (text.substring(0, 17).toLowerCase() == "data:application/") {
  174. const text = getDataM3U8(text.substring(0, 17));
  175. text && toUrl(text);
  176. return;
  177. }
  178. });
  179. return clone;
  180. }
  181. window.fetch.toString = function () {
  182. return _fetch.toString();
  183. }
  184.  
  185. // 拦截 Array.prototype.slice
  186. const _slice = Array.prototype.slice;
  187. Array.prototype.slice = function (start, end) {
  188. let data = _slice.apply(this, arguments);
  189. if (end == 16 && this.length == 32) {
  190. for (let item of data) {
  191. if (typeof item != "number" || item > 255) { return data; }
  192. }
  193. postData({ action: "catCatchAddKey", key: data, href: location.href, ext: "key" });
  194. }
  195. return data;
  196. }
  197. Array.prototype.slice.toString = function () {
  198. return _slice.toString();
  199. }
  200.  
  201. // 拦截 window.btoa / window.atob
  202. const _btoa = window.btoa;
  203. window.btoa = function (data) {
  204. const base64 = _btoa.apply(this, arguments);
  205. CATCH_SEARCH_DEBUG && _log(base64, data, base64.length);
  206. if (base64.length == 24 && base64.substring(22, 24) == "==") {
  207. postData({ action: "catCatchAddKey", key: base64, href: location.href, ext: "base64Key" });
  208. }
  209. if (data.substring(0, 7).toUpperCase() == "#EXTM3U" && isFullM3u8(data)) {
  210. toUrl(data);
  211. }
  212. return base64;
  213. }
  214. // 反检测
  215. window.btoa.toString = function () {
  216. return _btoa.toString();
  217. }
  218. const _atob = window.atob;
  219. window.atob = function (base64) {
  220. const data = _atob.apply(this, arguments);
  221. CATCH_SEARCH_DEBUG && _log(base64, data, base64.length);
  222. if (base64.length == 24 && base64.substring(22, 24) == "==") {
  223. postData({ action: "catCatchAddKey", key: base64, href: location.href, ext: "base64Key" });
  224. }
  225. if (data.substring(0, 7).toUpperCase() == "#EXTM3U" && isFullM3u8(data)) {
  226. toUrl(data);
  227. }
  228. return data;
  229. }
  230. window.atob.toString = function () {
  231. return _atob.toString();
  232. }
  233.  
  234. // 拦截fromCharCode
  235. const _fromCharCode = String.fromCharCode;
  236. let m3u8Text = '';
  237. String.fromCharCode = function () {
  238. const data = _fromCharCode.apply(this, arguments);
  239. if (data.length < 7) { return data; }
  240. if (data.substring(0, 7) == "#EXTM3U" || data.includes("#EXTINF:")) {
  241. m3u8Text += data;
  242. if (m3u8Text.includes("#EXT-X-ENDLIST")) {
  243. toUrl(m3u8Text.split("#EXT-X-ENDLIST")[0] + "#EXT-X-ENDLIST");
  244. m3u8Text = '';
  245. }
  246. return data;
  247. }
  248. const key = data.replaceAll("\u0010", "");
  249. if (key.length == 32) {
  250. postData({ action: "catCatchAddKey", key: key, href: location.href, ext: "key" });
  251. }
  252. return data;
  253. }
  254. String.fromCharCodetoString = function () {
  255. return _fromCharCode.toString();
  256. }
  257.  
  258. function isUrl(str) {
  259. return /^http[s]*:\/\/.+/i.test(str);
  260. }
  261. function isFullM3u8(text) {
  262. let tsLists = text.split("\n");
  263. for (let ts of tsLists) {
  264. if (ts[0] == "#") { continue; }
  265. if (isUrl(ts)) { return true; }
  266. return false;
  267. }
  268. return false;
  269. }
  270. function isJSON(str) {
  271. if (typeof str == "object") {
  272. return str;
  273. }
  274. if (typeof str == "string") {
  275. try {
  276. return _JSONparse(str);
  277. } catch (e) { return false; }
  278. }
  279. return false;
  280. }
  281. function getExtension(str) {
  282. let ext;
  283. try { ext = new URL(str); } catch (e) { return undefined; }
  284. ext = ext.pathname.split(".");
  285. if (ext.length == 1) { return undefined; }
  286. ext = ext[ext.length - 1].toLowerCase();
  287. if (ext == "m3u8" ||
  288. ext == "m3u" ||
  289. ext == "mpd" ||
  290. ext == "mp4" ||
  291. ext == "mp3" ||
  292. ext == "key"
  293. ) { return ext; }
  294. return false;
  295. }
  296. function toUrl(text, ext = "m3u8") {
  297. let url = URL.createObjectURL(new Blob([new TextEncoder("utf-8").encode(text)]));
  298. postData({ action: "catCatchAddMedia", url: url, href: location.href, ext: ext });
  299. }
  300. function getDataM3U8(text) {
  301. const type = ["vnd.apple.mpegurl", "x-mpegurl", "mpegurl"];
  302. let isM3U8 = false;
  303. for (let item of type) {
  304. if (text.substring(0, item.length).toLowerCase() == item) {
  305. text = text.substring(item.length + 1);
  306. isM3U8 = true;
  307. break;
  308. }
  309. }
  310. if (!isM3U8) { return false; }
  311. if (text.substring(0, 7).toLowerCase() == "base64,") {
  312. return window.atob(text.substring(7));
  313. }
  314. return text;
  315. }
  316. function postData(data) {
  317. const key = data.url ? data.url : data.key;
  318. if (filter.has(key)) { return false; }
  319. filter.add(key);
  320. data.requestId = Date.now().toString() + filter.size;
  321. window.postMessage(data);
  322. }
  323. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址