sketchfab

download sketchfab models

  1. // ==UserScript==
  2. // @name sketchfab
  3. // @version 2.1
  4. // @description download sketchfab models
  5. // @author Kvali
  6. // @match https://sketchfab.com/*
  7. // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.5/jszip.min.js
  8. // @require https://cdnjs.cloudflare.com/ajax/libs/jszip-utils/0.0.2/jszip-utils.min.js
  9. // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.js
  10. // @run-at document-start
  11. // @grant unsafeWindow
  12. // @grant GM_download
  13. // @namespace https://gf.qytechs.cn/users/956968
  14. // @license MIT
  15. // ==/UserScript==
  16.  
  17. var zip = new JSZip();
  18. let folder = zip.folder('collection');
  19.  
  20. var button_dw = false;
  21. var func_drawGeometry = /(this\._stateCache\.drawGeometry\(this\._graphicContext,t\))/g;
  22. var fund_drawArrays = /t\.drawArrays\(t\.TRIANGLES,0,6\)/g;
  23. //var func_renderInto1 = /x\.renderInto\(n,S,y/g;
  24. var func_renderInto1 = /A\.renderInto\(n,E,R/g; //20 jun 2023 fix
  25. var func_renderInto2 = /g\.renderInto=function\(e,i,r/g;
  26. var func_getResourceImage = /getResourceImage:function\(e,t\){/g;
  27.  
  28. var func_test = /apply:function\(e\){var t=e instanceof r\.Geometry;/g
  29.  
  30. var addbtnfunc;
  31.  
  32. (function() {
  33. 'use strict';
  34. var window = unsafeWindow;
  35. console.log("[UserScript]init", window);
  36.  
  37.  
  38. window.allmodel = [];
  39. var saveimagecache2 = {};
  40. var objects = {};
  41.  
  42.  
  43. var saveimage_to_list = function(url,file_name)
  44. {
  45. if (!saveimagecache2[url])
  46. {
  47. var mdl = {
  48. name: file_name
  49. }
  50.  
  51. saveimagecache2[url] = mdl;
  52. }
  53. }
  54.  
  55. addbtnfunc = function() {
  56. var p = document.evaluate("//div[@class='titlebar']", document, null, 9, null).singleNodeValue;
  57. if(p && !button_dw) {
  58. console.log("[UserScript]add btn dwnld");
  59. var btn = document.createElement("a");
  60. btn.setAttribute("class", "control");
  61. btn.innerHTML = "DOWNLOAD";
  62. btn.style.backgroundColor = "#1caad9";
  63. btn.style.color = "white";
  64. btn.style.padding = "8px";
  65. btn.style.borderRadius = "4px";
  66. btn.style.cursor = "pointer";
  67. btn.style.transition = "background-color 0.2s ease-in-out";
  68. btn.addEventListener("mouseenter", function() {
  69. btn.style.backgroundColor = "#1c88bb";
  70. });
  71. btn.addEventListener("mouseleave", function() {
  72. btn.style.backgroundColor = "#1caad9";
  73. });
  74. btn.addEventListener("click", dodownload, false);
  75. p.appendChild(btn);
  76. button_dw = true;
  77. } else {
  78. console.log("[UserScript]try add btn later");
  79. setTimeout(addbtnfunc, 3000);
  80. }
  81. }
  82.  
  83. var dodownload = function() {
  84. console.log("[UserScript]download");
  85. var idx = 0;
  86. window.allmodel.forEach(function(obj)
  87. {
  88. var mdl = {
  89. name: "model_"+idx,
  90. obj:parseobj(obj)
  91. }
  92. console.log(mdl);
  93. dosavefile(mdl);
  94. idx++;
  95.  
  96. })
  97. PackAll();
  98. }
  99.  
  100. var PackAll = function ()
  101. {
  102. for (var obj in objects) {
  103. console.log("[UserScript]save file", obj);
  104. folder.file(obj, objects[obj], {binary:true});
  105. }
  106.  
  107. var file_name = document.getElementsByClassName('model-name__label')[0].textContent;
  108. folder.generateAsync({ type: "blob" }).then(content => saveAs(content, file_name + ".zip"));
  109. }
  110.  
  111. var parseobj = function(obj)
  112. {
  113. console.log("[UserScript]: obj", obj);
  114. var list = [];
  115. obj._primitives.forEach(function(p) {
  116. if(p && p.indices) {
  117. list.push({
  118. 'mode' : p.mode,
  119. 'indices' : p.indices._elements
  120. });
  121. }
  122. })
  123.  
  124. var attr = obj._attributes;
  125. return {
  126. vertex: attr.Vertex._elements,
  127. normal: attr.Normal ? attr.Normal._elements : [],
  128. uv: attr.TexCoord0 ? attr.TexCoord0._elements :
  129. attr.TexCoord1 ? attr.TexCoord1._elements :
  130. attr.TexCoord2 ? attr.TexCoord2._elements :
  131. attr.TexCoord2 ? attr.TexCoord2._elements :
  132. attr.TexCoord3 ? attr.TexCoord3._elements :
  133. attr.TexCoord4 ? attr.TexCoord4._elements :
  134. attr.TexCoord5 ? attr.TexCoord5._elements :
  135. attr.TexCoord6 ? attr.TexCoord6._elements :
  136. attr.TexCoord7 ? attr.TexCoord7._elements :
  137. attr.TexCoord8 ? attr.TexCoord8._elements : [],
  138. primitives: list,
  139. };
  140. }
  141.  
  142. var dosavefile = function(mdl)
  143. {
  144. var obj = mdl.obj;
  145.  
  146. var str = '';
  147. str += 'mtllib ' + mdl.name + '.mtl\n';
  148. str += 'o ' + mdl.name + '\n';
  149. for (var i = 0; i < obj.vertex.length; i += 3) {
  150. str += 'v ';
  151. for (var j = 0; j < 3; ++j) {
  152. str += obj.vertex[i + j] + ' ';
  153. }
  154. str += '\n';
  155. }
  156. for (i = 0; i < obj.normal.length; i += 3) {
  157. str += 'vn ';
  158. for (j = 0; j < 3; ++j) {
  159. str += obj.normal[i + j] + ' ';
  160. }
  161. str += '\n';
  162. }
  163.  
  164. for (i = 0; i < obj.uv.length; i += 2) {
  165. str += 'vt ';
  166. for (j = 0; j < 2; ++j) {
  167. str += obj.uv[i + j] + ' ';
  168. }
  169. str += '\n';
  170. }
  171. //str += 'usemtl ' + mdl.name + '\n';
  172. str += 's on \n';
  173.  
  174. var vn = obj.normal.length != 0;
  175. var vt = obj.uv.length != 0;
  176.  
  177. for (i = 0; i < obj.primitives.length; ++i) {
  178. var primitive = obj.primitives[i];
  179. if (primitive.mode == 4 || primitive.mode == 5) {
  180. var strip = (primitive.mode == 5);
  181. for (j = 0; j + 2 < primitive.indices.length; !strip ? j += 3 : j++) {
  182. str += 'f ';
  183. var order = [ 0, 1, 2];
  184. if (strip && (j % 2 == 1)) {
  185. order = [ 0, 2, 1];
  186. }
  187. for (var k = 0; k < 3; ++k)
  188. {
  189. var faceNum = primitive.indices[j + order[k]] + 1;
  190. str += faceNum;
  191. if (vn || vt) {
  192. str += '/';
  193. if (vt) {
  194. str += faceNum;
  195. }
  196. if (vn) {
  197. str += '/' + faceNum;
  198. }
  199. }
  200. str += ' ';
  201. }
  202. str += '\n';
  203. }
  204. }
  205. else {
  206. console.log("[UserScript]dosavefile: unknown primitive mode", primitive);
  207. }
  208. }
  209.  
  210. str += '\n';
  211.  
  212. var objblob = new Blob([str], {type:'text/plain'});
  213.  
  214. objects[mdl.name+".obj"] = objblob;
  215. }
  216.  
  217.  
  218. window.attachbody = function(obj)
  219. {
  220. if(obj._faked != true && ((obj.stateset && obj.stateset._name) || obj._name || (obj._parents && obj._parents[0]._name)) ) {
  221. obj._faked = true;
  222. if(obj._name == "composer layer" || obj._name == "Ground - Geometry") return;
  223. window.allmodel.push(obj)
  224. console.log(obj);
  225. }
  226. //console.log(obj);
  227. }
  228.  
  229.  
  230. window.hook_test = function(e, idx)
  231. {
  232. console.log("hooked index: "+idx);
  233. console.log(e);
  234. }
  235. window.drawhookcanvas = function(e, imagemodel)
  236. {
  237.  
  238. if((e.width == 128 && e.height == 128) || (e.width == 32 && e.height == 32) || (e.width == 64 && e.height == 64))
  239. {
  240. return e;
  241. }
  242. if(imagemodel)
  243. {
  244. var alpha = e.options.format;
  245. var filename_image = imagemodel.attributes.name;
  246. var uid = imagemodel.attributes.uid;
  247. var url_image = e.url;
  248. var max_size = 0;
  249. var obr = e;
  250. imagemodel.attributes.images.forEach(function(img)
  251. {
  252. var alpha_is_check = alpha == "A" ? img.options.format == alpha : true;
  253.  
  254. var d = img.width;
  255. while ( d % 2 == 0 )
  256. {
  257. d = d / 2;
  258. }
  259.  
  260. if(img.size > max_size && alpha_is_check && d == 1)
  261. {
  262. max_size = img.size;
  263. url_image = img.url;
  264. uid = img.uid;
  265. obr = img;
  266. }
  267. });
  268. if(!saveimagecache2[url_image])
  269. {
  270. console.log(e);
  271. saveimage_to_list(url_image, filename_image);
  272. }
  273. else
  274. {
  275. //console.log(e);
  276. }
  277.  
  278. return obr;
  279. }
  280. return e;
  281. }
  282.  
  283. window.drawhookimg = function(gl,t)
  284. {
  285. console.log(JSON.stringify(t));
  286. var url = t[5].currentSrc;
  287. var width = t[5].width;
  288. var height = t[5].height;
  289.  
  290. if(!saveimagecache2[url])
  291. {
  292. //console.log("rejected:"+url);
  293. return;
  294. }
  295. else
  296. {
  297. //console.log("saved texture:"+url);
  298. }
  299.  
  300.  
  301. var data = new Uint8Array(width * height * 4);
  302. gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);
  303.  
  304. var halfHeight = height / 2 | 0; // the | 0 keeps the result an int
  305. var bytesPerRow = width * 4;
  306.  
  307. // make a temp buffer to hold one row
  308. var temp = new Uint8Array(width * 4);
  309. for (var y = 0; y < halfHeight; ++y)
  310. {
  311. var topOffset = y * bytesPerRow;
  312. var bottomOffset = (height - y - 1) * bytesPerRow;
  313.  
  314. // make copy of a row on the top half
  315. temp.set(data.subarray(topOffset, topOffset + bytesPerRow));
  316.  
  317. // copy a row from the bottom half to the top
  318. data.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);
  319.  
  320. // copy the copy of the top half row to the bottom half
  321. data.set(temp, bottomOffset);
  322. }
  323.  
  324. // Create a 2D canvas to store the result
  325. var canvas = document.createElement('canvas');
  326. canvas.width = width;
  327. canvas.height = height;
  328. var context = canvas.getContext('2d');
  329.  
  330. // Copy the pixels to a 2D canvas
  331. var imageData = context.createImageData(width, height);
  332. imageData.data.set(data);
  333. context.putImageData(imageData, 0, 0);
  334.  
  335. var re = /(?:\.([^.]+))?$/;
  336. var ext = re.exec(saveimagecache2[url].name)[1];
  337. var name = saveimagecache2[url].name+".png";
  338.  
  339. if(ext == "png" || ext == "jpg" || ext == "jpeg")
  340. {
  341. var ret = saveimagecache2[url].name.replace('.'+ext,'');
  342. name = ret+".png";
  343. }
  344. console.log("saved texture to blob "+name);
  345. canvas.toBlob(function(blob){objects[name] = blob;},"image/png");
  346. }
  347.  
  348. })();
  349.  
  350. (() => {
  351. "use strict";
  352. const Event = class {
  353. constructor(script, target) {
  354. this.script = script;
  355. this.target = target;
  356.  
  357. this._cancel = false;
  358. this._replace = null;
  359. this._stop = false;
  360. }
  361.  
  362. preventDefault() {
  363. this._cancel = true;
  364. }
  365. stopPropagation() {
  366. this._stop = true;
  367. }
  368. replacePayload(payload) {
  369. this._replace = payload;
  370. }
  371. };
  372.  
  373. let callbacks = [];
  374. window.addBeforeScriptExecuteListener = (f) => {
  375. if (typeof f !== "function") {
  376. throw new Error("Event handler must be a function.");
  377. }
  378. callbacks.push(f);
  379. };
  380. window.removeBeforeScriptExecuteListener = (f) => {
  381. let i = callbacks.length;
  382. while (i--) {
  383. if (callbacks[i] === f) {
  384. callbacks.splice(i, 1);
  385. }
  386. }
  387. };
  388.  
  389. const dispatch = (script, target) => {
  390. if (script.tagName !== "SCRIPT") {
  391. return;
  392. }
  393.  
  394. const e = new Event(script, target);
  395.  
  396. if (typeof window.onbeforescriptexecute === "function") {
  397. try {
  398. window.onbeforescriptexecute(e);
  399. } catch (err) {
  400. console.error(err);
  401. }
  402. }
  403.  
  404. for (const func of callbacks) {
  405. if (e._stop) {
  406. break;
  407. }
  408. try {
  409. func(e);
  410. } catch (err) {
  411. console.error(err);
  412. }
  413. }
  414.  
  415. if (e._cancel) {
  416. script.textContent = "";
  417. script.remove();
  418. } else if (typeof e._replace === "string") {
  419. script.textContent = e._replace;
  420. }
  421. };
  422. const observer = new MutationObserver((mutations) => {
  423. for (const m of mutations) {
  424. for (const n of m.addedNodes) {
  425. dispatch(n, m.target);
  426. }
  427. }
  428. });
  429. observer.observe(document, {
  430. childList: true,
  431. subtree: true,
  432. });
  433. })();
  434.  
  435. (() => {
  436. "use strict";
  437.  
  438. window.onbeforescriptexecute = (e) => {
  439. var links_as_arr = Array.from(e.target.childNodes);
  440.  
  441. links_as_arr.forEach(function(srimgc)
  442. {
  443. if(srimgc instanceof HTMLScriptElement)
  444. {
  445. if (srimgc.src.indexOf("web/dist/") >= 0 || srimgc.src.indexOf("standaloneViewer") >= 0)
  446. {
  447. e.preventDefault();
  448. e.stopPropagation();
  449. var req = new XMLHttpRequest();
  450. req.open('GET', srimgc.src, false);
  451. req.send('');
  452. var jstext = req.responseText;
  453. var ret = func_renderInto1.exec(jstext);
  454.  
  455. if (ret)
  456. {
  457. var index = ret.index + ret[0].length;
  458. var head = jstext.slice(0, index);
  459. var tail = jstext.slice(index);
  460. jstext = head + ",i" + tail;
  461. console.log("[UserScript] Injection: patch_0 injected successful " + srimgc.src);
  462. }
  463.  
  464. ret = func_renderInto2.exec(jstext);
  465.  
  466. if (ret)
  467. {
  468. var index = ret.index + ret[0].length;
  469. var head = jstext.slice(0, index);
  470. var tail = jstext.slice(index);
  471. jstext = head + ",image_data" + tail;
  472. console.log("[UserScript] Injection: patch_1 injected successful " + srimgc.src);
  473. if (!func_renderInto1.exec(jstext))
  474. console.log("[UserScript] But patch_0 failed " + srimgc.src);
  475. }
  476.  
  477. ret = fund_drawArrays.exec(jstext);
  478.  
  479. if (ret)
  480. {
  481. var index = ret.index + ret[0].length;
  482. var head = jstext.slice(0, index);
  483. var tail = jstext.slice(index);
  484. jstext = head + ",window.drawhookimg(t,image_data)" + tail;
  485. console.log("[UserScript] Injection: patch_2 injected successful " + srimgc.src);
  486. }
  487.  
  488. ret = func_getResourceImage.exec(jstext);
  489.  
  490. if (ret)
  491. {
  492. var index = ret.index + ret[0].length;
  493. var head = jstext.slice(0, index);
  494. var tail = jstext.slice(index);
  495. jstext = head + "e = window.drawhookcanvas(e,this._imageModel);" + tail;
  496. console.log("[UserScript] Injection: patch_3 injected successful " + srimgc.src);
  497. }
  498.  
  499. ret = func_drawGeometry.exec(jstext);
  500.  
  501. if (ret)
  502. {
  503. var index1 = ret.index + ret[1].length;
  504. var head1 = jstext.slice(0, index1);
  505. var tail1 = jstext.slice(index1);
  506. jstext = head1 + ";window.attachbody(t);" + tail1;
  507. console.log("[UserScript] Injection: patch_4 injected successful " + srimgc.src);
  508. setTimeout(addbtnfunc, 3000);
  509. }
  510. //ret = func_test.exec(jstext)
  511. var idx = 0;
  512. // while (ret = func_test.exec(jstext))
  513. // {
  514. // var index = ret.index + ret[0].length;
  515. // var head = jstext.slice(0, index);
  516. // var tail = jstext.slice(index);
  517. // jstext = head +"window.attachbody(e);"+ tail;
  518. // //jstext = head + "window.drawhook(e);" + tail;
  519. // func_test.lastIndex = index + 1000;
  520. // console.log("[UserScript] Injection: patch_4 injected successful" + srimgc.src);
  521. // setTimeout(addbtnfunc, 3000);
  522. // }
  523.  
  524. var obj = document.createElement('script');
  525. obj.type = "text/javascript";
  526. obj.text = jstext;
  527. document.getElementsByTagName('head')[0].appendChild(obj);
  528. }
  529. }
  530. });
  531. };
  532. })();

QingJ © 2025

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