Bypass Loading, Exportación Secuencial en Vivo y Sobrescritura de Librería JSON
Script này sẽ không được không được cài đặt trực tiếp. Nó là một thư viện cho các script khác để bao gồm các chỉ thị meta
// @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);
};
})();