Подписывайтесь на наш Telegram и не пропускайте важные новости! Перейти

script Web Ripper Sketchfab

Подскажите с артстейшен с помощью чего можно рипнуть?
 
:frog-wow: Ребят, у кого не появляется надпись скачать, хотя все настроено правильно, нужно включить режим разработчика в расширениях вашего браузера, это новое условие для Tampermonkey. :frog-wow:
:seemsgood: У меня такая же проблема была, решилась таким путем.
Так что попробуйте, кто не пробовал:da:
 
Доброго всем дня !
Кнопка появляется, но скачиваются пустые архивы. Что можно сделать или как починить?
 
Подскажите пожалуйста, актуально ли сейчас?
Да, работает
Снимок экрана (154).png
 
Скрытое содержимое
Нет не устарел, тоже работает
 
Внёс некоторые изменения, работает для меня.

Код:
Expand Collapse Copy
// ==UserScript==
// @name         sketchfab
// @version      0.0.4
// @include      /^https?://(www\.)?sketchfab\.com/.*
// @include      /^https?://(www\.)?fab\.com/.*
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.5/jszip.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip-utils/0.0.2/jszip-utils.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.js
// @run-at       document-start
// @grant        unsafeWindow
// @grant        GM_download
// ==/UserScript==

var zip = new JSZip();
let folder = zip.folder('collection');

var button_dw = false;
var func_drawGeometry = /(this\._stateCache\.drawGeometry\(this\._graphicContext,t\))/g;
var fund_drawArrays = /t\.drawArrays\(t\.TRIANGLES,0,6\)/g;
//var func_renderInto1 = /x\.renderInto\(n,S,y/g;
var func_renderInto1 = /A\.renderInto\(n,E,R/g; //20 jun 2023 fix
var func_renderInto2 = /g\.renderInto=function\(e,i,r/g;
var func_getResourceImage = /getResourceImage:function\(e,t\){/g;

var func_test = /apply:function\(e\){var t=e instanceof r\.Geometry;/g

var addbtnfunc;

(function() {
    'use strict';
    var window = unsafeWindow;
    console.log("[UserScript]init", window);


    window.allmodel = [];
    var saveimagecache2 = {};
    var objects = {};


    var saveimage_to_list = function(url,file_name)
    {
        if (!saveimagecache2[url])
        {
            var mdl = {
                name: file_name
            }

            saveimagecache2[url] = mdl;
        }
    }

    addbtnfunc = function() {
    if (!button_dw) {
        console.log("[UserScript] add btn dwnld");
        var btn = document.createElement("a");
        btn.setAttribute("class", "control");
        btn.style.position = "fixed";
        btn.style.right = "20px";
        btn.style.top = "50%";
        btn.style.transform = "translateY(-50%)";
        btn.style.zIndex = "1000";
        btn.style.backgroundColor = "#ff0000";
        btn.style.color = "#fff";
        btn.style.padding = "10px 15px";
        btn.style.borderRadius = "5px";
        btn.style.textAlign = "center";
        btn.style.cursor = "pointer";
        btn.style.boxShadow = "0 2px 5px rgba(0,0,0,0.3)";
        btn.style.fontFamily = "Arial, sans-serif";
        btn.style.fontSize = "14px";
        btn.innerHTML = "DOWNLOAD";

        btn.addEventListener("click", dodownload, false);
        document.body.appendChild(btn);
        button_dw = true;
    } else {
        console.log("[UserScript] try add btn later");
        setTimeout(addbtnfunc, 3000);
    }
};



    var dodownload = function() {
        console.log("[UserScript]download");
        var idx = 0;
        window.allmodel.forEach(function(obj)
        {
            var mdl = {
                name: "model_"+idx,
                obj:parseobj(obj)
            }
            console.log(mdl);
            dosavefile(mdl);
            idx++;

        })
        PackAll();
    }

    var PackAll = function ()
        {
            for (var obj in objects) {
                console.log("[UserScript]save file", obj);
                folder.file(obj, objects[obj], {binary:true});
              }

            var file_name = document.getElementsByClassName('model-name__label')[0].textContent;
            folder.generateAsync({ type: "blob" }).then(content => saveAs(content, file_name + ".zip"));
        }

        var parseobj = function(obj)
        {
            console.log("[UserScript]: obj", obj);
            var list = [];
            obj._primitives.forEach(function(p) {
                if(p && p.indices) {
                    list.push({
                        'mode' : p.mode,
                        'indices' : p.indices._elements
                    });
                }
            })

            var attr = obj._attributes;
            return {
                vertex: attr.Vertex._elements,
                normal: attr.Normal ? attr.Normal._elements : [],
                uv: attr.TexCoord0 ? attr.TexCoord0._elements :
                attr.TexCoord1 ? attr.TexCoord1._elements :
                attr.TexCoord2 ? attr.TexCoord2._elements :
                attr.TexCoord2 ? attr.TexCoord2._elements :
                attr.TexCoord3 ? attr.TexCoord3._elements :
                attr.TexCoord4 ? attr.TexCoord4._elements :
                attr.TexCoord5 ? attr.TexCoord5._elements :
                attr.TexCoord6 ? attr.TexCoord6._elements :
                attr.TexCoord7 ? attr.TexCoord7._elements :
                attr.TexCoord8 ? attr.TexCoord8._elements :  [],
                primitives: list,
            };
        }

        var dosavefile = function(mdl)
        {
            var obj = mdl.obj;

            //console.log("TEST");
            //console.log(obj);

            var str = '';
            str += 'mtllib ' + mdl.name + '.mtl\n';
            str += 'o ' + mdl.name + '\n';
            for (var i = 0; i < obj.vertex.length; i += 3) {
                str += 'v ';
                for (var j = 0; j < 3; ++j) {
                    str += obj.vertex[i + j] + ' ';
                }
                str += '\n';
            }
            for (i = 0; i < obj.normal.length; i += 3) {
                str += 'vn ';
                for (j = 0; j < 3; ++j) {
                    str += obj.normal[i + j] + ' ';
                }
                str += '\n';
            }

            for (i = 0; i < obj.uv.length; i += 2) {
                str += 'vt ';
                for (j = 0; j < 2; ++j) {
                    str += obj.uv[i + j] + ' ';
                }
                str += '\n';
            }
            //str += 'usemtl ' + mdl.name + '\n';
            str += 's on \n';

            var vn = obj.normal.length != 0;
            var vt = obj.uv.length != 0;

            for (i = 0; i < obj.primitives.length; ++i) {
                var primitive = obj.primitives[i];
                if (primitive.mode == 4 || primitive.mode == 5) {
                    var strip = (primitive.mode == 5);
                    for (j = 0; j + 2 < primitive.indices.length; !strip ? j += 3 : j++) {
                        str += 'f ';
                        var order = [ 0, 1, 2];
                        if (strip && (j % 2 == 1)) {
                            order = [ 0, 2, 1];
                        }
                        for (var k = 0; k < 3; ++k)
                        {
                            var faceNum = primitive.indices[j + order[k]] + 1;
                            str += faceNum;
                            if (vn || vt) {
                                str += '/';
                                if (vt) {
                                    str += faceNum;
                                }
                                if (vn) {
                                    str += '/' + faceNum;
                                }
                            }
                            str += ' ';
                        }
                        str += '\n';
                    }
                }
                else {
                    console.log("[UserScript]dosavefile: unknown primitive mode", primitive);
                }
            }

            str += '\n';

            var objblob = new Blob([str], {type:'text/plain'});

            objects[mdl.name+".obj"] = objblob;
        }


        window.attachbody = function(obj)
        {
             if(obj._faked != true && ((obj.stateset && obj.stateset._name) || obj._name || (obj._parents && obj._parents[0]._name)) ) {
                 obj._faked = true;
                 if(obj._name == "composer layer" || obj._name == "Ground - Geometry") return;
                 window.allmodel.push(obj)
                 console.log(obj);
             }
            //console.log(obj);
        }


        window.hook_test = function(e, idx)
        {
            console.log("hooked index: "+idx);
            console.log(e);
        }
        window.drawhookcanvas = function(e, imagemodel)
        {

            if((e.width == 128 && e.height == 128) || (e.width == 32 && e.height == 32) || (e.width == 64 && e.height == 64))
            {
                return e;
            }
            if(imagemodel)
            {
                var alpha = e.options.format;
                var filename_image =  imagemodel.attributes.name;
                var uid = imagemodel.attributes.uid;
                var url_image = e.url;
                var max_size = 0;
                var obr = e;
                imagemodel.attributes.images.forEach(function(img)
                {
                    var alpha_is_check = alpha == "A" ? img.options.format == alpha : true;

                    var d = img.width;
                    while ( d % 2 == 0 )
                    {
                        d = d / 2;
                    }

                    if(img.size > max_size && alpha_is_check && d == 1)
                    {
                        max_size = img.size;
                        url_image = img.url;
                        uid = img.uid;
                        obr = img;
                    }
                });
                if(!saveimagecache2[url_image])
                {
                    console.log(e);
                    saveimage_to_list(url_image, filename_image);
                }
                else
                {
                    //console.log(e);
                }

                return obr;
            }
            return e;
        }

        window.drawhookimg = function(gl,t)
        {
            console.log(JSON.stringify(t));
            var url = t[5].currentSrc;
            var width = t[5].width;
            var height = t[5].height;

            if(!saveimagecache2[url])
            {
                //console.log("rejected:"+url);
                return;
            }
            else
            {
                //console.log("saved texture:"+url);
            }


            var data = new Uint8Array(width * height * 4);
            gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);

            var halfHeight = height / 2 | 0;  // the | 0 keeps the result an int
            var bytesPerRow = width * 4;

            // make a temp buffer to hold one row
            var temp = new Uint8Array(width * 4);
            for (var y = 0; y < halfHeight; ++y)
            {
            var topOffset = y * bytesPerRow;
            var bottomOffset = (height - y - 1) * bytesPerRow;

            // make copy of a row on the top half
            temp.set(data.subarray(topOffset, topOffset + bytesPerRow));

            // copy a row from the bottom half to the top
            data.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);

            // copy the copy of the top half row to the bottom half
            data.set(temp, bottomOffset);
            }

             // Create a 2D canvas to store the result
            var canvas = document.createElement('canvas');
            canvas.width = width;
            canvas.height = height;
            var context = canvas.getContext('2d');

            // Copy the pixels to a 2D canvas
            var imageData = context.createImageData(width, height);
            imageData.data.set(data);
            context.putImageData(imageData, 0, 0);

            var re = /(?:\.([^.]+))?$/;
            var ext = re.exec(saveimagecache2[url].name)[1];
            var name = saveimagecache2[url].name+".png";

            if(ext == "png" || ext == "jpg" || ext == "jpeg")
            {
                var ret = saveimagecache2[url].name.replace('.'+ext,'');
                name = ret+".png";
            }
            console.log("saved texture to blob "+name);
            canvas.toBlob(function(blob){objects[name] = blob;},"image/png");
        }

})();

(() => {
            "use strict";
            const Event = class {
                constructor(script, target) {
                    this.script = script;
                    this.target = target;

                    this._cancel = false;
                    this._replace = null;
                    this._stop = false;
                }

                preventDefault() {
                    this._cancel = true;
                }
                stopPropagation() {
                    this._stop = true;
                }
                replacePayload(payload) {
                    this._replace = payload;
                }
            };

            let callbacks = [];
            window.addBeforeScriptExecuteListener = (f) => {
                if (typeof f !== "function") {
                    throw new Error("Event handler must be a function.");
                }
                callbacks.push(f);
            };
            window.removeBeforeScriptExecuteListener = (f) => {
                let i = callbacks.length;
                while (i--) {
                    if (callbacks[i] === f) {
                        callbacks.splice(i, 1);
                    }
                }
            };

            const dispatch = (script, target) => {
                if (script.tagName !== "SCRIPT") {
                    return;
                }

                const e = new Event(script, target);

                if (typeof window.onbeforescriptexecute === "function") {
                    try {
                        window.onbeforescriptexecute(e);
                    } catch (err) {
                        console.error(err);
                    }
                }

                for (const func of callbacks) {
                    if (e._stop) {
                        break;
                    }
                    try {
                        func(e);
                    } catch (err) {
                        console.error(err);
                    }
                }

                if (e._cancel) {
                    script.textContent = "";
                    script.remove();
                } else if (typeof e._replace === "string") {
                    script.textContent = e._replace;
                }
            };
            const observer = new MutationObserver((mutations) => {
                for (const m of mutations) {
                    for (const n of m.addedNodes) {
                        dispatch(n, m.target);
                    }
                }
            });
            observer.observe(document, {
                childList: true,
                subtree: true,
            });
        })();

(() => {
            "use strict";

            window.onbeforescriptexecute = (e) => {
                var links_as_arr = Array.from(e.target.childNodes);

                links_as_arr.forEach(function(srimgc)
                {
                    if(srimgc instanceof HTMLScriptElement)
                    {
                        if (srimgc.src.indexOf("web/dist/") >= 0 || srimgc.src.indexOf("standaloneViewer") >= 0)
                        {
                            e.preventDefault();
                            e.stopPropagation();
                            var req = new XMLHttpRequest();
                            req.open('GET', srimgc.src, false);
                            req.send('');
                            var jstext = req.responseText;
                            var ret = func_renderInto1.exec(jstext);

                            if (ret)
                            {
                                var index = ret.index + ret[0].length;
                                var head = jstext.slice(0, index);
                                var tail = jstext.slice(index);
                                jstext = head + ",i" + tail;
                                console.log("[UserScript] Injection: patch_0 injected successful " + srimgc.src);
                            }

                            ret = func_renderInto2.exec(jstext);

                            if (ret)
                            {
                                var index = ret.index + ret[0].length;
                                var head = jstext.slice(0, index);
                                var tail = jstext.slice(index);
                                jstext = head + ",image_data" + tail;
                                console.log("[UserScript] Injection: patch_1 injected successful " + srimgc.src);
                                if (!func_renderInto1.exec(jstext))
                                     console.log("[UserScript] But patch_0 failed " + srimgc.src);
                            }

                            ret = fund_drawArrays.exec(jstext);

                            if (ret)
                            {
                                var index = ret.index + ret[0].length;
                                var head = jstext.slice(0, index);
                                var tail = jstext.slice(index);
                                jstext = head + ",window.drawhookimg(t,image_data)" + tail;
                                console.log("[UserScript] Injection: patch_2 injected successful " + srimgc.src);
                            }

                            ret = func_getResourceImage.exec(jstext);

                            if (ret)
                            {
                                var index = ret.index + ret[0].length;
                                var head = jstext.slice(0, index);
                                var tail = jstext.slice(index);
                                jstext = head + "e = window.drawhookcanvas(e,this._imageModel);" + tail;
                                console.log("[UserScript] Injection: patch_3 injected successful " + srimgc.src);
                            }

                            ret = func_drawGeometry.exec(jstext);

                            if (ret)
                            {
                                var index1 = ret.index + ret[1].length;
                                var head1 = jstext.slice(0, index1);
                                var tail1 = jstext.slice(index1);
                                jstext = head1 + ";window.attachbody(t);" + tail1;
                                console.log("[UserScript] Injection: patch_4 injected successful " + srimgc.src);
                                setTimeout(addbtnfunc, 3000);
                            }
                            //ret = func_test.exec(jstext)
                            var idx = 0;
                            // while (ret = func_test.exec(jstext))
                            // {
                            //     var index = ret.index + ret[0].length;
                            //     var head = jstext.slice(0, index);
                            //     var tail = jstext.slice(index);
                            //     jstext = head +"window.attachbody(e);"+ tail;
                            //     //jstext = head + "window.drawhook(e);" + tail;
                            //     func_test.lastIndex = index + 1000;
                            //     console.log("[UserScript] Injection: patch_4 injected successful" + srimgc.src);
                            //     setTimeout(addbtnfunc, 3000);
                            // }

                            var obj = document.createElement('script');
                            obj.type = "text/javascript";
                            obj.text = jstext;
                            document.getElementsByTagName('head')[0].appendChild(obj);
                        }
                    }
                });
            };
        })();
 
:frog-wow: Ребят, у кого не появляется надпись скачать, хотя все настроено правильно, нужно включить режим разработчика в расширениях вашего браузера, это новое условие для Tampermonkey. :frog-wow:
:seemsgood: У меня такая же проблема была, решилась таким путем.
Так что попробуйте, кто не пробовал:da:
Увы нет. Не появляется кнопка.
 
Привет, ребята. У меня скрипт работает. Подскажите, пожалуйста, а можно отключить триангуляцию мешей при скачивании? Некоторые модели на хабе с квадратной сеткой, а скачивание происходит с триангуляцией.
 
Привет, ребята. У меня скрипт работает. Подскажите, пожалуйста, а можно отключить триангуляцию мешей при скачивании? Некоторые модели на хабе с квадратной сеткой, а скачивание происходит с триангуляцией.
В блендере можно потом подчистить автоматом
 
Внёс некоторые изменения, работает для меня.

Код:
Expand Collapse Copy
// ==UserScript==
// @name         sketchfab
// @version      0.0.4
// @include      /^https?://(www\.)?sketchfab\.com/.*
// @include      /^https?://(www\.)?fab\.com/.*
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.5/jszip.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip-utils/0.0.2/jszip-utils.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.js
// @run-at       document-start
// @grant        unsafeWindow
// @grant        GM_download
// ==/UserScript==

var zip = new JSZip();
let folder = zip.folder('collection');

var button_dw = false;
var func_drawGeometry = /(this\._stateCache\.drawGeometry\(this\._graphicContext,t\))/g;
var fund_drawArrays = /t\.drawArrays\(t\.TRIANGLES,0,6\)/g;
//var func_renderInto1 = /x\.renderInto\(n,S,y/g;
var func_renderInto1 = /A\.renderInto\(n,E,R/g; //20 jun 2023 fix
var func_renderInto2 = /g\.renderInto=function\(e,i,r/g;
var func_getResourceImage = /getResourceImage:function\(e,t\){/g;

var func_test = /apply:function\(e\){var t=e instanceof r\.Geometry;/g

var addbtnfunc;

(function() {
    'use strict';
    var window = unsafeWindow;
    console.log("[UserScript]init", window);


    window.allmodel = [];
    var saveimagecache2 = {};
    var objects = {};


    var saveimage_to_list = function(url,file_name)
    {
        if (!saveimagecache2[url])
        {
            var mdl = {
                name: file_name
            }

            saveimagecache2[url] = mdl;
        }
    }

    addbtnfunc = function() {
    if (!button_dw) {
        console.log("[UserScript] add btn dwnld");
        var btn = document.createElement("a");
        btn.setAttribute("class", "control");
        btn.style.position = "fixed";
        btn.style.right = "20px";
        btn.style.top = "50%";
        btn.style.transform = "translateY(-50%)";
        btn.style.zIndex = "1000";
        btn.style.backgroundColor = "#ff0000";
        btn.style.color = "#fff";
        btn.style.padding = "10px 15px";
        btn.style.borderRadius = "5px";
        btn.style.textAlign = "center";
        btn.style.cursor = "pointer";
        btn.style.boxShadow = "0 2px 5px rgba(0,0,0,0.3)";
        btn.style.fontFamily = "Arial, sans-serif";
        btn.style.fontSize = "14px";
        btn.innerHTML = "DOWNLOAD";

        btn.addEventListener("click", dodownload, false);
        document.body.appendChild(btn);
        button_dw = true;
    } else {
        console.log("[UserScript] try add btn later");
        setTimeout(addbtnfunc, 3000);
    }
};



    var dodownload = function() {
        console.log("[UserScript]download");
        var idx = 0;
        window.allmodel.forEach(function(obj)
        {
            var mdl = {
                name: "model_"+idx,
                obj:parseobj(obj)
            }
            console.log(mdl);
            dosavefile(mdl);
            idx++;

        })
        PackAll();
    }

    var PackAll = function ()
        {
            for (var obj in objects) {
                console.log("[UserScript]save file", obj);
                folder.file(obj, objects[obj], {binary:true});
              }

            var file_name = document.getElementsByClassName('model-name__label')[0].textContent;
            folder.generateAsync({ type: "blob" }).then(content => saveAs(content, file_name + ".zip"));
        }

        var parseobj = function(obj)
        {
            console.log("[UserScript]: obj", obj);
            var list = [];
            obj._primitives.forEach(function(p) {
                if(p && p.indices) {
                    list.push({
                        'mode' : p.mode,
                        'indices' : p.indices._elements
                    });
                }
            })

            var attr = obj._attributes;
            return {
                vertex: attr.Vertex._elements,
                normal: attr.Normal ? attr.Normal._elements : [],
                uv: attr.TexCoord0 ? attr.TexCoord0._elements :
                attr.TexCoord1 ? attr.TexCoord1._elements :
                attr.TexCoord2 ? attr.TexCoord2._elements :
                attr.TexCoord2 ? attr.TexCoord2._elements :
                attr.TexCoord3 ? attr.TexCoord3._elements :
                attr.TexCoord4 ? attr.TexCoord4._elements :
                attr.TexCoord5 ? attr.TexCoord5._elements :
                attr.TexCoord6 ? attr.TexCoord6._elements :
                attr.TexCoord7 ? attr.TexCoord7._elements :
                attr.TexCoord8 ? attr.TexCoord8._elements :  [],
                primitives: list,
            };
        }

        var dosavefile = function(mdl)
        {
            var obj = mdl.obj;

            //console.log("TEST");
            //console.log(obj);

            var str = '';
            str += 'mtllib ' + mdl.name + '.mtl\n';
            str += 'o ' + mdl.name + '\n';
            for (var i = 0; i < obj.vertex.length; i += 3) {
                str += 'v ';
                for (var j = 0; j < 3; ++j) {
                    str += obj.vertex[i + j] + ' ';
                }
                str += '\n';
            }
            for (i = 0; i < obj.normal.length; i += 3) {
                str += 'vn ';
                for (j = 0; j < 3; ++j) {
                    str += obj.normal[i + j] + ' ';
                }
                str += '\n';
            }

            for (i = 0; i < obj.uv.length; i += 2) {
                str += 'vt ';
                for (j = 0; j < 2; ++j) {
                    str += obj.uv[i + j] + ' ';
                }
                str += '\n';
            }
            //str += 'usemtl ' + mdl.name + '\n';
            str += 's on \n';

            var vn = obj.normal.length != 0;
            var vt = obj.uv.length != 0;

            for (i = 0; i < obj.primitives.length; ++i) {
                var primitive = obj.primitives[i];
                if (primitive.mode == 4 || primitive.mode == 5) {
                    var strip = (primitive.mode == 5);
                    for (j = 0; j + 2 < primitive.indices.length; !strip ? j += 3 : j++) {
                        str += 'f ';
                        var order = [ 0, 1, 2];
                        if (strip && (j % 2 == 1)) {
                            order = [ 0, 2, 1];
                        }
                        for (var k = 0; k < 3; ++k)
                        {
                            var faceNum = primitive.indices[j + order[k]] + 1;
                            str += faceNum;
                            if (vn || vt) {
                                str += '/';
                                if (vt) {
                                    str += faceNum;
                                }
                                if (vn) {
                                    str += '/' + faceNum;
                                }
                            }
                            str += ' ';
                        }
                        str += '\n';
                    }
                }
                else {
                    console.log("[UserScript]dosavefile: unknown primitive mode", primitive);
                }
            }

            str += '\n';

            var objblob = new Blob([str], {type:'text/plain'});

            objects[mdl.name+".obj"] = objblob;
        }


        window.attachbody = function(obj)
        {
             if(obj._faked != true && ((obj.stateset && obj.stateset._name) || obj._name || (obj._parents && obj._parents[0]._name)) ) {
                 obj._faked = true;
                 if(obj._name == "composer layer" || obj._name == "Ground - Geometry") return;
                 window.allmodel.push(obj)
                 console.log(obj);
             }
            //console.log(obj);
        }


        window.hook_test = function(e, idx)
        {
            console.log("hooked index: "+idx);
            console.log(e);
        }
        window.drawhookcanvas = function(e, imagemodel)
        {

            if((e.width == 128 && e.height == 128) || (e.width == 32 && e.height == 32) || (e.width == 64 && e.height == 64))
            {
                return e;
            }
            if(imagemodel)
            {
                var alpha = e.options.format;
                var filename_image =  imagemodel.attributes.name;
                var uid = imagemodel.attributes.uid;
                var url_image = e.url;
                var max_size = 0;
                var obr = e;
                imagemodel.attributes.images.forEach(function(img)
                {
                    var alpha_is_check = alpha == "A" ? img.options.format == alpha : true;

                    var d = img.width;
                    while ( d % 2 == 0 )
                    {
                        d = d / 2;
                    }

                    if(img.size > max_size && alpha_is_check && d == 1)
                    {
                        max_size = img.size;
                        url_image = img.url;
                        uid = img.uid;
                        obr = img;
                    }
                });
                if(!saveimagecache2[url_image])
                {
                    console.log(e);
                    saveimage_to_list(url_image, filename_image);
                }
                else
                {
                    //console.log(e);
                }

                return obr;
            }
            return e;
        }

        window.drawhookimg = function(gl,t)
        {
            console.log(JSON.stringify(t));
            var url = t[5].currentSrc;
            var width = t[5].width;
            var height = t[5].height;

            if(!saveimagecache2[url])
            {
                //console.log("rejected:"+url);
                return;
            }
            else
            {
                //console.log("saved texture:"+url);
            }


            var data = new Uint8Array(width * height * 4);
            gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);

            var halfHeight = height / 2 | 0;  // the | 0 keeps the result an int
            var bytesPerRow = width * 4;

            // make a temp buffer to hold one row
            var temp = new Uint8Array(width * 4);
            for (var y = 0; y < halfHeight; ++y)
            {
            var topOffset = y * bytesPerRow;
            var bottomOffset = (height - y - 1) * bytesPerRow;

            // make copy of a row on the top half
            temp.set(data.subarray(topOffset, topOffset + bytesPerRow));

            // copy a row from the bottom half to the top
            data.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);

            // copy the copy of the top half row to the bottom half
            data.set(temp, bottomOffset);
            }

             // Create a 2D canvas to store the result
            var canvas = document.createElement('canvas');
            canvas.width = width;
            canvas.height = height;
            var context = canvas.getContext('2d');

            // Copy the pixels to a 2D canvas
            var imageData = context.createImageData(width, height);
            imageData.data.set(data);
            context.putImageData(imageData, 0, 0);

            var re = /(?:\.([^.]+))?$/;
            var ext = re.exec(saveimagecache2[url].name)[1];
            var name = saveimagecache2[url].name+".png";

            if(ext == "png" || ext == "jpg" || ext == "jpeg")
            {
                var ret = saveimagecache2[url].name.replace('.'+ext,'');
                name = ret+".png";
            }
            console.log("saved texture to blob "+name);
            canvas.toBlob(function(blob){objects[name] = blob;},"image/png");
        }

})();

(() => {
            "use strict";
            const Event = class {
                constructor(script, target) {
                    this.script = script;
                    this.target = target;

                    this._cancel = false;
                    this._replace = null;
                    this._stop = false;
                }

                preventDefault() {
                    this._cancel = true;
                }
                stopPropagation() {
                    this._stop = true;
                }
                replacePayload(payload) {
                    this._replace = payload;
                }
            };

            let callbacks = [];
            window.addBeforeScriptExecuteListener = (f) => {
                if (typeof f !== "function") {
                    throw new Error("Event handler must be a function.");
                }
                callbacks.push(f);
            };
            window.removeBeforeScriptExecuteListener = (f) => {
                let i = callbacks.length;
                while (i--) {
                    if (callbacks[i] === f) {
                        callbacks.splice(i, 1);
                    }
                }
            };

            const dispatch = (script, target) => {
                if (script.tagName !== "SCRIPT") {
                    return;
                }

                const e = new Event(script, target);

                if (typeof window.onbeforescriptexecute === "function") {
                    try {
                        window.onbeforescriptexecute(e);
                    } catch (err) {
                        console.error(err);
                    }
                }

                for (const func of callbacks) {
                    if (e._stop) {
                        break;
                    }
                    try {
                        func(e);
                    } catch (err) {
                        console.error(err);
                    }
                }

                if (e._cancel) {
                    script.textContent = "";
                    script.remove();
                } else if (typeof e._replace === "string") {
                    script.textContent = e._replace;
                }
            };
            const observer = new MutationObserver((mutations) => {
                for (const m of mutations) {
                    for (const n of m.addedNodes) {
                        dispatch(n, m.target);
                    }
                }
            });
            observer.observe(document, {
                childList: true,
                subtree: true,
            });
        })();

(() => {
            "use strict";

            window.onbeforescriptexecute = (e) => {
                var links_as_arr = Array.from(e.target.childNodes);

                links_as_arr.forEach(function(srimgc)
                {
                    if(srimgc instanceof HTMLScriptElement)
                    {
                        if (srimgc.src.indexOf("web/dist/") >= 0 || srimgc.src.indexOf("standaloneViewer") >= 0)
                        {
                            e.preventDefault();
                            e.stopPropagation();
                            var req = new XMLHttpRequest();
                            req.open('GET', srimgc.src, false);
                            req.send('');
                            var jstext = req.responseText;
                            var ret = func_renderInto1.exec(jstext);

                            if (ret)
                            {
                                var index = ret.index + ret[0].length;
                                var head = jstext.slice(0, index);
                                var tail = jstext.slice(index);
                                jstext = head + ",i" + tail;
                                console.log("[UserScript] Injection: patch_0 injected successful " + srimgc.src);
                            }

                            ret = func_renderInto2.exec(jstext);

                            if (ret)
                            {
                                var index = ret.index + ret[0].length;
                                var head = jstext.slice(0, index);
                                var tail = jstext.slice(index);
                                jstext = head + ",image_data" + tail;
                                console.log("[UserScript] Injection: patch_1 injected successful " + srimgc.src);
                                if (!func_renderInto1.exec(jstext))
                                     console.log("[UserScript] But patch_0 failed " + srimgc.src);
                            }

                            ret = fund_drawArrays.exec(jstext);

                            if (ret)
                            {
                                var index = ret.index + ret[0].length;
                                var head = jstext.slice(0, index);
                                var tail = jstext.slice(index);
                                jstext = head + ",window.drawhookimg(t,image_data)" + tail;
                                console.log("[UserScript] Injection: patch_2 injected successful " + srimgc.src);
                            }

                            ret = func_getResourceImage.exec(jstext);

                            if (ret)
                            {
                                var index = ret.index + ret[0].length;
                                var head = jstext.slice(0, index);
                                var tail = jstext.slice(index);
                                jstext = head + "e = window.drawhookcanvas(e,this._imageModel);" + tail;
                                console.log("[UserScript] Injection: patch_3 injected successful " + srimgc.src);
                            }

                            ret = func_drawGeometry.exec(jstext);

                            if (ret)
                            {
                                var index1 = ret.index + ret[1].length;
                                var head1 = jstext.slice(0, index1);
                                var tail1 = jstext.slice(index1);
                                jstext = head1 + ";window.attachbody(t);" + tail1;
                                console.log("[UserScript] Injection: patch_4 injected successful " + srimgc.src);
                                setTimeout(addbtnfunc, 3000);
                            }
                            //ret = func_test.exec(jstext)
                            var idx = 0;
                            // while (ret = func_test.exec(jstext))
                            // {
                            //     var index = ret.index + ret[0].length;
                            //     var head = jstext.slice(0, index);
                            //     var tail = jstext.slice(index);
                            //     jstext = head +"window.attachbody(e);"+ tail;
                            //     //jstext = head + "window.drawhook(e);" + tail;
                            //     func_test.lastIndex = index + 1000;
                            //     console.log("[UserScript] Injection: patch_4 injected successful" + srimgc.src);
                            //     setTimeout(addbtnfunc, 3000);
                            // }

                            var obj = document.createElement('script');
                            obj.type = "text/javascript";
                            obj.text = jstext;
                            document.getElementsByTagName('head')[0].appendChild(obj);
                        }
                    }
                });
            };
        })();
Я не знаю что там сделал ТЬІ!. Но единственная версия что работает. Гений
 
Я не знаю что там сделал ТЬІ!. Но единственная версия что работает. Гений
Вот доработанная версия с помощью Claude

JavaScript:
Expand Collapse Copy
// ==UserScript==
// @name         sketchfab
// @version      0.0.6
// @include      /^https?://(www\.)?sketchfab\.com/.*
// @include      /^https?://(www\.)?fab\.com/.*
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.5/jszip.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip-utils/0.0.2/jszip-utils.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.js
// @run-at       document-start
// @grant        unsafeWindow
// @grant        GM_download
// ==/UserScript==

(function () {
    'use strict';

    // unsafeWindow — страница, window — sandbox TM
    var win = unsafeWindow;

    // Общее хранилище
    var zip    = new JSZip();
    var folder = zip.folder('collection');
    var objects        = {};   // имя → Blob
    var saveimagecache = {};   // url  → { name }
    var buttonAdded    = false;

    win.allmodel = [];

    // ════════════════════════════════════════════════════════════════
    //  КНОПКА
    // ════════════════════════════════════════════════════════════════
    function addbtnfunc() {
        if (buttonAdded) return;
        if (!document.body) { setTimeout(addbtnfunc, 500); return; }

        console.log('[SF] adding DOWNLOAD button');
        var btn = document.createElement('div');
        btn.textContent = 'DOWNLOAD';
        btn.style.cssText = [
            'position:fixed',
            'right:20px',
            'top:50%',
            'transform:translateY(-50%)',
            'z-index:2147483647',
            'background:#c0392b',
            'color:#fff',
            'padding:12px 20px',
            'border-radius:6px',
            'cursor:pointer',
            'font-family:Arial,sans-serif',
            'font-size:14px',
            'font-weight:bold',
            'box-shadow:0 3px 8px rgba(0,0,0,.5)',
            'user-select:none',
        ].join(';');
        btn.addEventListener('click', doDownload);
        document.body.appendChild(btn);
        buttonAdded = true;
        console.log('[SF] button added OK');
    }

    // ════════════════════════════════════════════════════════════════
    //  СКАЧИВАНИЕ
    // ════════════════════════════════════════════════════════════════
    function doDownload() {
        console.log('[SF] download, models:', win.allmodel.length);
        win.allmodel.forEach(function (obj, idx) {
            saveObjFile({ name: 'model_' + idx, obj: parseObj(obj) });
        });
        packAll();
    }

    function packAll() {
        for (var name in objects) {
            if (objects.hasOwnProperty(name)) {
                console.log('[SF] packing', name);
                folder.file(name, objects[name], { binary: true });
            }
        }
        var labelEl  = document.querySelector('.model-name__label');
        var fileName = labelEl ? labelEl.textContent.trim() : 'model';
        folder.generateAsync({ type: 'blob' })
              .then(function (blob) { saveAs(blob, fileName + '.zip'); });
    }

    // ════════════════════════════════════════════════════════════════
    //  ПАРСИНГ ГЕОМЕТРИИ
    // ════════════════════════════════════════════════════════════════
    function parseObj(obj) {
        var primitives = [];
        (obj._primitives || []).forEach(function (p) {
            if (p && p.indices)
                primitives.push({ mode: p.mode, indices: p.indices._elements });
        });

        var attr = obj._attributes || {};
        var uv = (
            attr.TexCoord0 || attr.TexCoord1 || attr.TexCoord2 || attr.TexCoord3 ||
            attr.TexCoord4 || attr.TexCoord5 || attr.TexCoord6 || attr.TexCoord7 ||
            attr.TexCoord8 || {}
        )._elements || [];

        return {
            vertex:     (attr.Vertex || {})._elements || [],
            normal:     (attr.Normal || {})._elements || [],
            uv:         uv,
            primitives: primitives,
        };
    }

    function saveObjFile(mdl) {
        var obj   = mdl.obj;
        var lines = ['mtllib ' + mdl.name + '.mtl', 'o ' + mdl.name];
        var i, j;

        for (i = 0; i < obj.vertex.length; i += 3)
            lines.push('v ' + obj.vertex[i] + ' ' + obj.vertex[i+1] + ' ' + obj.vertex[i+2]);
        for (i = 0; i < obj.normal.length; i += 3)
            lines.push('vn ' + obj.normal[i] + ' ' + obj.normal[i+1] + ' ' + obj.normal[i+2]);
        for (i = 0; i < obj.uv.length; i += 2)
            lines.push('vt ' + obj.uv[i] + ' ' + obj.uv[i+1]);

        lines.push('s on');

        var hasN = obj.normal.length > 0;
        var hasT = obj.uv.length     > 0;

        obj.primitives.forEach(function (prim) {
            if (prim.mode !== 4 && prim.mode !== 5) return;
            var strip = (prim.mode === 5);
            var idx   = prim.indices;
            for (j = 0; j + 2 < idx.length; strip ? j++ : j += 3) {
                var order = (strip && j % 2 === 1) ? [0,2,1] : [0,1,2];
                var face  = 'f ';
                for (var k = 0; k < 3; k++) {
                    var n = idx[j + order[k]] + 1;
                    face += n;
                    if (hasN || hasT) {
                        face += '/';
                        if (hasT) face += n;
                        if (hasN) face += '/' + n;
                    }
                    face += ' ';
                }
                lines.push(face);
            }
        });

        objects[mdl.name + '.obj'] = new Blob([lines.join('\n')], { type: 'text/plain' });
    }

    // ════════════════════════════════════════════════════════════════
    //  ХУКИ — вызываются из пропатченного движка
    // ════════════════════════════════════════════════════════════════
    win.attachbody = function (obj) {
        if (obj._faked) return;
        var named = (obj.stateset && obj.stateset._name)
                 || obj._name
                 || (obj._parents && obj._parents[0] && obj._parents[0]._name);
        if (!named) return;
        if (obj._name === 'composer layer' || obj._name === 'Ground - Geometry') return;
        obj._faked = true;
        win.allmodel.push(obj);
    };

    win.drawhookcanvas = function (e, imageModel) {
        var w = e.width, h = e.height;
        if ((w === 128 || w === 64 || w === 32) &&
            (h === 128 || h === 64 || h === 32)) return e;
        if (!imageModel) return e;

        var alpha   = e.options && e.options.format;
        var url     = e.url;
        var maxSize = 0;
        var best    = e;

        (imageModel.attributes.images || []).forEach(function (img) {
            var fmtOk = (alpha === 'A') ? (img.options && img.options.format === 'A') : true;
            var d = img.width;
            while (d % 2 === 0) d /= 2;
            if (img.size > maxSize && fmtOk && d === 1) {
                maxSize = img.size;
                url     = img.url;
                best    = img;
            }
        });

        if (!saveimagecache[url])
            saveimagecache[url] = { name: imageModel.attributes.name };
        return best;
    };

    win.drawhookimg = function (gl, t) {
        if (!t || !t[5]) return;
        var src = t[5].currentSrc;
        if (!src || !saveimagecache[src]) return;

        var width  = t[5].width;
        var height = t[5].height;
        var data   = new Uint8Array(width * height * 4);
        gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);

        var row  = width * 4;
        var temp = new Uint8Array(row);
        for (var y = 0; y < (height / 2 | 0); y++) {
            var top = y * row, bot = (height - y - 1) * row;
            temp.set(data.subarray(top, top + row));
            data.copyWithin(top, bot, bot + row);
            data.set(temp, bot);
        }

        var canvas = document.createElement('canvas');
        canvas.width = width; canvas.height = height;
        var ctx = canvas.getContext('2d');
        var imgData = ctx.createImageData(width, height);
        imgData.data.set(data);
        ctx.putImageData(imgData, 0, 0);

        var name = saveimagecache[src].name.replace(/\.(png|jpe?g)$/i, '') + '.png';
        canvas.toBlob(function (blob) { objects[name] = blob; }, 'image/png');
    };

    // ════════════════════════════════════════════════════════════════
    //  beforescriptexecute SHIM
    // ════════════════════════════════════════════════════════════════
    var bseCallbacks = [];
    window.addBeforeScriptExecuteListener    = function (f) { bseCallbacks.push(f); };
    window.removeBeforeScriptExecuteListener = function (f) {
        var i = bseCallbacks.indexOf(f);
        if (i !== -1) bseCallbacks.splice(i, 1);
    };

    function ScriptEvent(script) {
        this.target   = script;
        this._cancel  = false;
        this._replace = null;
        this._stop    = false;
    }
    ScriptEvent.prototype.preventDefault  = function () { this._cancel = true; };
    ScriptEvent.prototype.stopPropagation = function () { this._stop   = true; };
    ScriptEvent.prototype.replacePayload  = function (p) { this._replace = p; };

    function dispatchBSE(node) {
        if (!node || node.tagName !== 'SCRIPT') return;
        var e = new ScriptEvent(node);
        if (typeof window.onbeforescriptexecute === 'function') {
            try { window.onbeforescriptexecute(e); } catch (err) { console.error(err); }
        }
        for (var i = 0; i < bseCallbacks.length; i++) {
            if (e._stop) break;
            try { bseCallbacks[i](e); } catch (err) { console.error(err); }
        }
        if (e._cancel) {
            node.textContent = '';
            node.remove();
        } else if (typeof e._replace === 'string') {
            node.textContent = e._replace;
        }
    }

    new MutationObserver(function (mutations) {
        for (var i = 0; i < mutations.length; i++) {
            var added = mutations[i].addedNodes;
            for (var j = 0; j < added.length; j++) dispatchBSE(added[j]);
        }
    }).observe(document, { childList: true, subtree: true });

    // ════════════════════════════════════════════════════════════════
    //  ПЕРЕХВАТ И ПАТЧ JS ДВИЖКА
    // ════════════════════════════════════════════════════════════════

    // inject: вставляет suffix сразу после первого совпадения с patStr
    function inject(js, patStr, suffix, label, src) {
        var re = new RegExp(patStr);
        var m  = re.exec(js);
        if (!m) {
            console.warn('[SF] ' + label + ' — NOT FOUND in ' + src);
            return js;
        }
        var pos = m.index + m[0].length;
        console.log('[SF] ' + label + ' — injected OK');
        return js.slice(0, pos) + suffix + js.slice(pos);
    }

    function applyPatches(js, src) {
        // patch_0: renderInto получает доп. аргумент i
        js = inject(js,
            'A\\.renderInto\\(n,E,R',
            ',i',
            'patch_0 renderInto1', src);

        // patch_1: renderInto-определение получает доп. параметр image_data
        js = inject(js,
            'g\\.renderInto=function\\(e,i,r',
            ',image_data',
            'patch_1 renderInto2', src);

        // patch_2: хук для захвата текстуры
        js = inject(js,
            't\\.drawArrays\\(t\\.TRIANGLES,0,6\\)',
            ',window.drawhookimg(t,image_data)',
            'patch_2 drawArrays', src);

        // patch_3: хук для маппинга URL текстуры
        js = inject(js,
            'getResourceImage:function\\(e,t\\)\\{',
            'e=window.drawhookcanvas(e,this._imageModel);',
            'patch_3 getResourceImage', src);

        // patch_4: хук захвата геометрии → кнопка появляется после
        var before = js;
        js = inject(js,
            'this\\._stateCache\\.drawGeometry\\(this\\._graphicContext,t\\)',
            ';window.attachbody(t);',
            'patch_4 drawGeometry', src);
        if (js !== before) setTimeout(addbtnfunc, 3000);

        return js;
    }

    window.onbeforescriptexecute = function (e) {
        var script = e.target;
        var src    = script.src || '';

        if (!src.includes('web/dist/') && !src.includes('standaloneViewer')) return;

        console.log('[SF] intercepted script:', src);
        e.preventDefault();
        e.stopPropagation();

        fetch(src)
            .then(function (r) { return r.text(); })
            .then(function (jstext) {
                jstext = applyPatches(jstext, src);
                var tag  = document.createElement('script');
                tag.type = 'text/javascript';
                tag.text = jstext;
                (document.head || document.documentElement).appendChild(tag);
            })
            .catch(function (err) {
                console.error('[SF] fetch failed:', src, err);
            });
    };

    console.log('[SF] v0.0.6 installed');

})();
 
Назад
Сверху Снизу