Bypass Loading, Exportación Secuencial en Vivo y Sobrescritura de Librería JSON
Questo script non dovrebbe essere installato direttamente. È una libreria per altri script da includere con la chiave // @require https://update.greatest.deepsurf.us/scripts/569955/1787222/Drawaria%20Avatar%20Builder%20Loading%20Fix%20Suite.js
// ==UserScript==
// @name Drawaria Avatar Builder Loading Fix Suite
// @namespace http://tampermonkey.net/
// @version 3.0
// @description Bypass Loading, Exportación Secuencial en Vivo y Sobrescritura de Librería JSON
// @author youtubedrawaria
// @icon https://www.google.com/s2/favicons?sz=64&domain=drawaria.online
// @match https://drawaria.online/avatar/builder/*
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// --- ESTILOS DEL MENÚ ---
const style = document.createElement('style');
style.innerHTML = `
#drawaria-suite {
position: fixed; top: 20px; right: 20px; width: 280px;
background: #1a1a1a; color: white; border: 2px solid #007cff;
border-radius: 8px; z-index: 99999; font-family: sans-serif;
box-shadow: 0 4px 15px rgba(0,0,0,0.5); overflow: hidden;
}
#suite-header {
background: #007cff; padding: 10px; cursor: move;
font-weight: bold; text-align: center; font-size: 14px;
}
.suite-content { padding: 15px; display: flex; flex-direction: column; gap: 10px; }
.suite-btn {
background: #333; color: white; border: 1px solid #444;
padding: 8px; cursor: pointer; border-radius: 4px; font-size: 12px;
transition: 0.2s;
}
.suite-btn:hover { background: #444; border-color: #007cff; }
.suite-btn.primary { background: #007cff; border: none; font-weight: bold; }
.suite-btn.primary:hover { background: #0056b3; }
.status-msg { font-size: 11px; color: #00ff00; text-align: center; margin-top: 5px; font-weight: bold; }
`;
document.head.appendChild(style);
// --- ESTRUCTURA DEL MENÚ ---
const menu = document.createElement('div');
menu.id = 'drawaria-suite';
menu.innerHTML = `
<div id="suite-header">Drawaria Avatar Builder Suite Pro Max</div>
<div class="suite-content">
<button id="btn-bypass" class="suite-btn primary">🚀 FORZAR BYPASS (Entrar)</button>
<hr style="border: 0; border-top: 1px solid #444; width: 100%;">
<button id="btn-export" class="suite-btn">📤 Exportar Librería Lenta (JSON)</button>
<button id="btn-import" class="suite-btn">📥 Sobrescribir Imágenes (JSON)</button>
<input type="file" id="file-input" style="display:none" accept=".json">
<div id="suite-status" class="status-msg" style="color:#aaa;">Esperando acción...</div>
<div id="suite-progress" style="width: 100%; background: #333; height: 5px; border-radius: 3px; display: none;">
<div id="progress-bar" style="width: 0%; background: #007cff; height: 100%; transition: width 0.1s;"></div>
</div>
</div>
`;
document.body.appendChild(menu);
// --- LÓGICA DRAGGABLE ---
function makeDraggable(el) {
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
const header = document.getElementById('suite-header');
header.onmousedown = (e) => {
e.preventDefault();
pos3 = e.clientX; pos4 = e.clientY;
document.onmouseup = () => { document.onmouseup = null; document.onmousemove = null; };
document.onmousemove = (e) => {
pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY;
pos3 = e.clientX; pos4 = e.clientY;
el.style.top = (el.offsetTop - pos2) + "px";
el.style.left = (el.offsetLeft - pos1) + "px";
};
};
}
makeDraggable(menu);
// --- UTILIDADES ---
// --- UTILIDADES CON ESCUDO PROTECTOR PARA CUSTOM EMOJIS ---
const getReactApp = () => {
const root = document.querySelector('.App') || document.querySelector('#root');
if (!root) return null;
const key = Object.keys(root).find(k => k.startsWith('__react'));
let fiber = root[key];
let app = null;
while (fiber) {
if (fiber.stateNode && typeof fiber.stateNode.setState === 'function' && fiber.stateNode.randomize) {
app = fiber.stateNode;
break;
}
fiber = fiber.return;
}
// EL ESCUDO: Si encontramos la App y no la hemos protegido aún
if (app && !app._isShielded) {
const originalSetState = app.setState.bind(app);
// Interceptamos cualquier intento de actualizar la pantalla
app.setState = function(newState, callback) {
// 1. Proteger los avatares personalizados de tu script (evita la amnesia)
if (newState && newState.assets && this.state && this.state.assets) {
const customAssets = this.state.assets.filter(a => a.category === 'custom_hack' || a.category === 'animations');
if (customAssets.length > 0) {
// Si la nueva actualización no tiene tus custom, se los volvemos a inyectar a la fuerza
const hasCustom = newState.assets.some(a => a.category === 'custom_hack');
if (!hasCustom) {
newState.assets = [...customAssets, ...newState.assets];
}
}
}
// 2. Proteger la interfaz HTML (Buscador y botón de añadir)
const suiteControls = document.getElementById('suite-controls');
// Llamamos a la actualización real de React
originalSetState(newState, () => {
// 3. Restaurar la UI inmediatamente después de que React la borre
if (suiteControls && !document.getElementById('suite-controls')) {
const library = document.querySelector('.Library');
if (library) library.prepend(suiteControls);
}
if (callback) callback();
});
};
app._isShielded = true; // Marcamos como protegida
}
return app;
};
const setStatus = (msg, color = "#aaa") => {
const statusEl = document.getElementById('suite-status');
statusEl.innerText = msg;
statusEl.style.color = color;
};
const updateProgress = (percent) => {
const bar = document.getElementById('progress-bar');
const container = document.getElementById('suite-progress');
container.style.display = 'block';
bar.style.width = `${percent}%`;
if (percent >= 100) setTimeout(() => container.style.display = 'none', 1000);
};
// --- ACCIÓN 1: BYPASS ---
document.getElementById('btn-bypass').onclick = () => {
const app = getReactApp();
if (app) {
app.setState({
allAssetsLoaded: true,
assetsLoaded: app.state.assets.length,
assets: [...app.state.assets] // <-- Importante: Clonamos la memoria
});
setStatus(`✅ ${inyectados} imágenes fijadas en React`, "#00ff00");
} else {
setStatus("❌ React no encontrado", "#ff4444");
}
};
// --- ACCIÓN 2: EXPORTAR (Proceso Secuencial con DOM Update) ---
document.getElementById('btn-export').onclick = async () => {
const container = document.querySelector('.List.Library');
if (!container) return setStatus("❌ Abre la librería primero", "#ff4444");
const imagenes = container.querySelectorAll('li:not(.category) img');
if (imagenes.length === 0) return setStatus("❌ No hay imágenes para exportar", "#ff4444");
console.log("🚀 Iniciando conversión secuencial de la librería...");
let convertidas = 0;
let omitidas = 0;
const libraryData = [];
const getBase64 = async (blobUrl) => {
const res = await fetch(blobUrl);
const blob = await res.blob();
return new Promise((resolve) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(blob);
});
};
for (let i = 0; i < imagenes.length; i++) {
const img = imagenes[i];
const currentSrc = img.src;
const itemName = img.alt || img.parentElement.title;
let finalData = currentSrc;
if (currentSrc.startsWith('data:image')) {
omitidas++;
} else if (currentSrc.startsWith('blob:')) {
try {
finalData = await getBase64(currentSrc);
img.setAttribute('src', finalData); // Sobrescribe en el DOM visualmente
convertidas++;
await new Promise(r => setTimeout(r, 50)); // Pausa para no bloquear navegador
} catch (e) {
console.error(`Error en imagen ${itemName}:`, e);
}
}
// Guardamos en el Array que luego será nuestro JSON
libraryData.push({
name: itemName,
data: finalData
});
// Actualizar Interfaz
const percent = Math.round(((i + 1) / imagenes.length) * 100);
setStatus(`⏳ Procesando ${i + 1}/${imagenes.length}...`, "#007cff");
updateProgress(percent);
console.log(`Procesando ${i + 1}/${imagenes.length} (${itemName})... OK`);
}
setStatus(`✅ Fin. Convertidas: ${convertidas}, Omitidas: ${omitidas}`, "#00ff00");
console.log(`✅ Proceso terminado. Convertidas: ${convertidas}, Ya existentes: ${omitidas}.`);
// Generar y descargar JSON
const dataStr = JSON.stringify(libraryData);
const blobFinal = new Blob([dataStr], { type: "application/json" });
const urlDescarga = URL.createObjectURL(blobFinal);
const link = document.createElement('a');
link.href = urlDescarga;
link.download = `Drawaria_Library_Exportada.json`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
// --- ACCIÓN 3: IMPORTAR (Secuencial con Sobrescritura total: Library + Layers) ---
document.getElementById('btn-import').onclick = () => document.getElementById('file-input').click();
document.getElementById('file-input').onchange = (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = async (event) => {
try {
const data = JSON.parse(event.target.result);
const app = getReactApp();
if (!app) throw new Error("React no detectado");
// Buscamos las imágenes tanto en la Librería como en las Capas activas
const imagenesDOM = document.querySelectorAll('.List.Library img, .List.Layers img');
if (imagenesDOM.length === 0) return setStatus("❌ No hay imágenes para reemplazar", "#ff4444");
setStatus(`⌛ Inyectando ${data.length} imágenes...`, "#007cff");
let inyectados = 0;
for (let i = 0; i < data.length; i++) {
const item = data[i];
// 1. Encontrar el asset en la memoria de la aplicación
const targetAsset = app.state.assets.find(a => a.prettyName === item.name || a.name === item.name);
if (targetAsset) {
// 2. Actualizar las fuentes base
targetAsset.src = item.data;
if (targetAsset.img) targetAsset.img.src = item.data;
// 3. Hack del Virtual DOM para evitar que React lo borre
if (targetAsset.imgEl && targetAsset.imgEl.props) {
targetAsset.imgEl = Object.assign({}, targetAsset.imgEl, {
props: Object.assign({}, targetAsset.imgEl.props, {
src: item.data
})
});
}
}
// 4. Forzar visualización en el HTML (Library Y Layers al mismo tiempo)
// Usamos filter en lugar de find para atrapar TODAS las coincidencias (ej. si hay dos ojos iguales en Layers)
const elementosAActualizar = Array.from(imagenesDOM).filter(img =>
img.alt === item.name ||
(img.parentElement && img.parentElement.title === item.name)
);
if (elementosAActualizar.length > 0) {
elementosAActualizar.forEach(imgElement => {
imgElement.setAttribute('src', item.data);
inyectados++;
});
// Progreso visual
const percent = Math.round(((i + 1) / data.length) * 100);
setStatus(`⏳ Restaurando ${i + 1}/${data.length}...`, "#007cff");
updateProgress(percent);
await new Promise(r => setTimeout(r, 5));
}
}
// 5. Al actualizar React, clonamos la memoria para proteger tus Custom Emojis
app.setState({
allAssetsLoaded: true,
assetsLoaded: app.state.assets.length,
assets: [...app.state.assets]
});
setStatus(`✅ ${inyectados} iconos restaurados (Library y Layers)`, "#00ff00");
} catch (err) {
setStatus("❌ Error en JSON o React", "#ff4444");
console.error(err);
}
};
reader.readAsText(file);
};
})();