您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Asegura que los botones de subida estén siempre visibles y funcionales. Implementa un cooldown interno para evitar errores del servidor.
// ==UserScript== // @name Drawaria Buttons Fixed Improved // @namespace http://tampermonkey.net/ // @version 1.01 // @description Asegura que los botones de subida estén siempre visibles y funcionales. Implementa un cooldown interno para evitar errores del servidor. // @author YouTubeDrawaria // @match https://drawaria.online/* // @grant GM_addStyle // @grant GM_xmlhttpRequest // @connect picsum.photos // @license MIT // @icon https://www.google.com/s2/favicons?sz=64&domain=drawaria.online // ==/UserScript== (function() { 'use-strict'; const COOLDOWN_DURATION = 60 * 1000; // 60 segundos en milisegundos let lastUploadTime = 0; // Marca de tiempo de la última subida let cooldownDisplayTimeout = null; // Para limpiar el mensaje de cooldown // --- Inyectamos estilos para que los botones se vean bien --- GM_addStyle(` #superSendButton, #downloadCanvasButton, #uploadTenTimesButton, #uploadTwentyTimesButton, #uploadRandomSketchButton { margin-bottom: 0.5em; /* Espacio debajo de los botones */ } #superSendButton .spinner-border, #downloadCanvasButton .spinner-border, #uploadTenTimesButton .spinner-border, #uploadTwentyTimesButton .spinner-border, #uploadRandomSketchButton .spinner-border { width: 1em; height: 1em; margin-left: 0.5em; vertical-align: text-bottom; } #uploadStatusSection { font-size: 0.85em; color: #555; text-align: center; margin-top: -0.2em; margin-bottom: 0.5em; min-height: 1.2em; /* Para mantener el espacio */ } .cooldown-active-msg { color: orange; font-weight: bold; } .upload-error-msg { color: red; font-weight: bold; } .upload-success-msg { color: green; font-weight: bold; } `); /** * Espera a que un elemento DOM esté disponible. * @param {string} selector - Selector CSS del elemento. * @param {function} callback - Función a ejecutar cuando el elemento se encuentre. * @param {number} [intervalTime=100] - Intervalo de chequeo en ms. */ function waitForElement(selector, callback, intervalTime = 100) { const intervalId = setInterval(() => { const element = document.querySelector(selector); if (element) { clearInterval(intervalId); callback(element); } }, intervalTime); } /** * Obtiene el valor de una flag de modo de juego de forma segura. * Si las variables de Drawaria no están disponibles, devuelve `false`. * @param {string} flagConstName - Nombre de la constante global (ej. 'PGMODE_STENCILS'). * @returns {boolean} - True si el modo está activo, false en caso contrario. */ const getGameModeFlag = (flagConstName) => { // Accede directamente a window.Fn y window[flagConstName] si existen. if (typeof window.Fn !== 'undefined' && typeof window[flagConstName] !== 'undefined') { const flagValue = window.Fn[window[flagConstName]]; return !!flagValue; // Asegura que siempre sea un booleano. } return false; // Valor por defecto si las variables del juego no están disponibles. }; /** * Inyecta y ejecuta un fragmento de script en el contexto de la página. * Esto permite acceder a variables y funciones que no son accesibles desde el entorno aislado de Tampermonkey. * @param {string} codeToInject - El código JavaScript a inyectar y ejecutar. */ function injectScriptInPageContext(codeToInject) { const script = document.createElement('script'); script.textContent = `(function() { ${codeToInject} })();`; // Envuelve el código en un IIFE. (document.head || document.documentElement).appendChild(script); script.remove(); // Limpia el elemento script del DOM inmediatamente después de la ejecución. } /** * Actualiza el mensaje de estado de subida (cooldown, errores, etc.). * @param {string} message - El mensaje a mostrar. * @param {string} [type=''] - Clase CSS para el mensaje (ej. 'cooldown-active-msg', 'upload-error-msg'). * @param {number} [duration=3000] - Duración en ms antes de limpiar el mensaje (0 para no limpiar). */ function setUploadStatusMessage(message, type = '', duration = 3000) { const statusSection = document.getElementById('uploadStatusSection'); if (statusSection) { statusSection.innerHTML = message; statusSection.className = `upload-status-section ${type}`; if (cooldownDisplayTimeout) { clearTimeout(cooldownDisplayTimeout); } if (duration > 0) { cooldownDisplayTimeout = setTimeout(() => { statusSection.innerHTML = ''; statusSection.className = 'upload-status-section'; }, duration); } } } /** * Inicia el temporizador de cooldown global después de una subida. */ function startGlobalCooldown() { lastUploadTime = Date.now(); localStorage.setItem('drawariaLastUploadTime', lastUploadTime); // Guardar en localStorage setUploadStatusMessage(`Cooldown: ${Math.ceil(COOLDOWN_DURATION / 1000)}s restantes.`, 'cooldown-active-msg', COOLDOWN_DURATION + 1000); } /** * Función para descargar el dibujo actual del canvas como PNG. */ function downloadDrawing() { const downloadButton = document.getElementById('downloadCanvasButton'); const spinner = downloadButton.querySelector('.spinner-border'); spinner.style.display = 'inline-block'; downloadButton.disabled = true; // Deshabilitar solo este botón durante la descarga try { const mainCanvas = document.getElementById('canvas'); if (!mainCanvas) { throw new Error("No se encontró el canvas principal para descargar."); } let sourceCanvas = mainCanvas; const targetWordElement = document.getElementById('targetword'); const isWordGuessMode = (targetWordElement && targetWordElement.style.display !== 'none'); if (!isWordGuessMode && typeof window.ze !== 'undefined' && window.ze instanceof HTMLCanvasElement && window.ze.width > 0 && window.ze.height > 0) { sourceCanvas = window.ze; console.log("Usando el canvas auto-guardado (window.ze) para la descarga."); } else { console.log("Usando el canvas visible (mainCanvas) para la descarga."); } // Usar canvas.toBlob() para obtener el Blob del dibujo. sourceCanvas.toBlob((blob) => { if (blob) { const filename = `Drawaria-online-${new Date().toISOString().slice(0,10)}.png`; // Utilizar la función saveAs de FileSaver.js, que Drawaria ya carga. if (typeof window.saveAs === 'function') { window.saveAs(blob, filename); console.log(`Dibujo descargado como ${filename}`); setUploadStatusMessage('Dibujo descargado con éxito.', 'upload-success-msg'); } else { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); console.warn("window.saveAs no disponible, usando fallback para descargar."); setUploadStatusMessage('Dibujo descargado (fallback).', 'upload-success-msg'); } } else { throw new Error("No se pudo crear el Blob del canvas."); } }, 'image/png'); // Siempre descargar como PNG. } catch (error) { console.error("Error al descargar el dibujo:", error); setUploadStatusMessage(`Error: ${error.message}`, 'upload-error-msg'); alert("Ocurrió un error al descargar el dibujo: " + error.message); } finally { spinner.style.display = 'none'; downloadButton.disabled = false; } } /** * Procesa una imagen (ya sea del canvas del juego o externa) y la convierte a DataURLs para la subida. * @param {HTMLCanvasElement|HTMLImageElement} sourceImage - El canvas del juego o un objeto Image ya cargado. * @param {boolean} isSourceCanvas - True si sourceImage es un HTMLCanvasElement, false si es un HTMLImageElement. * @returns {Promise<{imageData: string, thumbData: string}>} DataURLs procesados. */ async function processImageForUpload(sourceImage, isSourceCanvas) { let imgToDraw = sourceImage; // Si la fuente es un canvas, convertimos primero a DataURL (PNG) y luego a Image. // Esto sirve como "purificador" para canvas complejos. if (isSourceCanvas) { const sourceDataURL = sourceImage.toDataURL('image/png'); imgToDraw = await new Promise((resolve, reject) => { const tempImg = new Image(); tempImg.onload = () => resolve(tempImg); tempImg.onerror = (e) => reject(new Error("Error al cargar la imagen del canvas fuente para purificación.")); tempImg.src = sourceDataURL; }); } // --- Procesamiento de la imagen principal para subir (JPEG 1200x1000) --- const uploadCanvas = document.createElement("canvas"); const uploadCtx = uploadCanvas.getContext("2d"); uploadCanvas.width = 1200; uploadCanvas.height = 1000; uploadCtx.fillStyle = "#ffffff"; uploadCtx.fillRect(0, 0, uploadCanvas.width, uploadCanvas.height); uploadCtx.drawImage(imgToDraw, 0, 0, uploadCanvas.width, uploadCanvas.height); // Dibujar Image object. if (getGameModeFlag('PGMODE_PIXELART')) { uploadCtx.imageSmoothingEnabled = false; } else { uploadCtx.imageSmoothingEnabled = true; } // --- Procesamiento de la miniatura para subir (JPEG 300x[proporción]) --- const thumbCanvas = document.createElement("canvas"); const thumbCtx = thumbCanvas.getContext("2d"); thumbCanvas.width = 300; thumbCanvas.height = 300 / 1.2; thumbCtx.fillStyle = "#ffffff"; thumbCtx.fillRect(0, 0, thumbCanvas.width, thumbCanvas.height); thumbCtx.drawImage(imgToDraw, 0, 0, thumbCanvas.width, thumbCanvas.height); // Dibujar Image object. if (getGameModeFlag('PGMODE_PIXELART')) { thumbCtx.imageSmoothingEnabled = false; } else { thumbCtx.imageSmoothingEnabled = true; } // Convertir los canvases a DataURL (JPEG con compresión). const imageData = uploadCanvas.toDataURL("image/jpeg", 0.8); const thumbData = thumbCanvas.toDataURL("image/jpeg", 0.8); // --- IMPORTANTE: Extraer solo la parte base64 si el servidor lo espera así. --- const base64ImageData = imageData.split(',')[1]; const base64ThumbData = thumbData.split(',')[1]; return { imageData: base64ImageData, thumbData: base64ThumbData }; } /** * Función base para subir un dibujo a la galería. * @param {HTMLCanvasElement|HTMLImageElement} sourceContent - El canvas del juego o un objeto Image ya cargado. * @param {boolean} isSourceCanvas - True si sourceContent es un HTMLCanvasElement, false si es un HTMLImageElement. * @param {boolean} shouldRedirect - Si la página debe redirigir después de la subida. * @returns {Promise<boolean>} Resuelve a true si la subida fue exitosa, false si hubo un error. */ async function uploadDrawingToGallery(sourceContent, isSourceCanvas, shouldRedirect = true) { // Mostrar spinner para el botón principal si es una subida individual const superSendButton = document.getElementById('superSendButton'); const spinner = superSendButton ? superSendButton.querySelector('.spinner-border') : null; if (spinner && shouldRedirect) { spinner.style.display = 'inline-block'; } // --- Verificar Cooldown --- const now = Date.now(); const timeLeft = lastUploadTime + COOLDOWN_DURATION - now; if (timeLeft > 0) { const secondsLeft = Math.ceil(timeLeft / 1000); const msg = `Cooldown: ${secondsLeft}s. Por favor, espera.`; setUploadStatusMessage(msg, 'cooldown-active-msg', 0); // No limpiar automáticamente console.warn(msg); if (spinner && shouldRedirect) spinner.style.display = 'none'; // Quitar spinner return false; // Fallo debido a cooldown } try { const processedData = await processImageForUpload(sourceContent, isSourceCanvas); const base64ImageData = processedData.imageData; const base64ThumbData = processedData.thumbData; // --- Preparación y envío de la petición HTTP --- let roomId = (window.location.pathname.match(/room\/([^/]+)/) || [])[1] || ''; const galleryHost = "//" + (window.qn || "gallery.drawaria.online"); const sessionId = (document.cookie.match(/sid1=([^;]+)/) || [])[1] || ""; const postUrl = `${galleryHost}/gallery/uploadimage/?sessionid=${sessionId}`; const formData = new URLSearchParams(); formData.append('imagedata', base64ImageData); formData.append('imagedata1', base64ThumbData); formData.append('room', roomId); formData.append('targetword', ''); formData.append('guestplayernames', null); formData.append('playeruids', null); formData.append('stencils', JSON.stringify(getGameModeFlag('PGMODE_STENCILS'))); formData.append('pixelart', JSON.stringify(getGameModeFlag('PGMODE_PIXELART'))); formData.append('advtools', JSON.stringify(getGameModeFlag('PGMODE_ADVTOOLS'))); const response = await fetch(postUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: formData }); if (!response.ok) { let errorDetails = await response.text(); try { const errorJson = JSON.parse(errorDetails); errorDetails = errorJson.error || errorDetails; } catch (e) { /* no es JSON */ } throw new Error(`Error de red o servidor (${response.status}): ${errorDetails}`); } const result = await response.json(); if (result.error) { throw new Error(result.error); } // Éxito: iniciar cooldown startGlobalCooldown(); // --- Notificación interna del juego (inyección de script, mejor esfuerzo) --- if (shouldRedirect) { // Solo inyecta si es una subida individual que lleva a redirigir const notificationScriptCode = ` (function() { if (typeof window.Je !== 'undefined') window.Je = true; if (typeof window.Ze !== 'undefined') window.Ze = Date.now(); let notificationSent = false; const imageId = '${result.imageid}'; if (typeof window.Hr === 'function') { try { window.Hr('clientnotify', null, 11, [imageId]); notificationSent = true; } catch (e) { console.error("Injected: Error calling Hr:", e); } } if (!notificationSent && typeof window.Nt === 'object' && window.Nt !== null && typeof window.Nt.emit === 'function') { try { window.Nt.emit('clientnotify', null, 11, [imageId]); notificationSent = true; } catch (e) { console.error("Injected: Error calling Nt.emit:", e); } } if (!notificationSent) { console.warn("Injected: No se pudo enviar la notificación interna del juego."); } })(); `; injectScriptInPageContext(notificationScriptCode); setUploadStatusMessage('Dibujo subido con éxito. Redirigiendo...', 'upload-success-msg'); console.log("¡Dibujo subido con éxito! Notificación interna intentada via inyección de script."); } else { console.log(`¡Dibujo subido con éxito! ID: ${result.imageid}`); } // Redirigir si se solicitó. if (shouldRedirect && result.imageid) { const galleryHost = "//" + (window.qn || "gallery.drawaria.online"); window.location.href = `${galleryHost}/gallery/img/${result.imageid}`; } else if (shouldRedirect) { const galleryHost = "//" + (window.qn || "gallery.drawaria.online"); window.location.href = `${galleryHost}/gallery/new`; } return true; } catch (error) { console.error("Error al subir el dibujo:", error); setUploadStatusMessage(`Error: ${error.message}`, 'upload-error-msg'); if (shouldRedirect) { alert("Ocurrió un error al subir el dibujo: " + error.message + ". Por favor, inténtalo de nuevo."); } return false; } finally { if (spinner && shouldRedirect) { spinner.style.display = 'none'; } } } /** * Sube el dibujo actual del canvas del juego. */ async function uploadGameCanvasDrawing() { const mainCanvas = document.getElementById('canvas'); if (!mainCanvas) { alert("No se encontró el canvas principal del juego."); return; } let sourceCanvas = mainCanvas; const targetWordElement = document.getElementById('targetword'); const isWordGuessMode = (targetWordElement && targetWordElement.style.display !== 'none'); // Seleccionar el canvas fuente. if (!isWordGuessMode && typeof window.ze !== 'undefined' && window.ze instanceof HTMLCanvasElement && window.ze.width > 0 && window.ze.height > 0) { sourceCanvas = window.ze; } await uploadDrawingToGallery(sourceCanvas, true, true); // Redirige } /** * Sube un boceto aleatorio desde una API externa (una vez). */ async function uploadRandomSketch() { const uploadRandomBtn = document.getElementById('uploadRandomSketchButton'); const superSendButton = document.getElementById('superSendButton'); // Main button to show spinner const spinner = superSendButton ? superSendButton.querySelector('.spinner-border') : null; // Mostrar spinner para el botón que se clickeó if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'inline-block'; if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'inline-block'; // También en el principal // No deshabilitamos los botones para que el usuario pueda intentar de nuevo si quiere // Esto se gestiona por el mensaje de cooldown // --- Verificar Cooldown --- const now = Date.now(); const timeLeft = lastUploadTime + COOLDOWN_DURATION - now; if (timeLeft > 0) { const secondsLeft = Math.ceil(timeLeft / 1000); const msg = `Cooldown: ${secondsLeft}s. Por favor, espera.`; setUploadStatusMessage(msg, 'cooldown-active-msg', 0); // No limpiar automáticamente console.warn(msg); // Quitar spinners si no se procede if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'none'; if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'none'; return; } try { // Fetch la imagen de una API externa const imageUrl = 'https://picsum.photos/1200/1000'; // Imagen aleatoria para testing const blob = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: imageUrl, responseType: "blob", onload: function(response) { if (response.status === 200) { resolve(response.response); } else { reject(new Error(`Fallo al obtener la imagen: ${response.status} ${response.statusText}`)); } }, onerror: function(response) { reject(new Error(`Error de red al obtener la imagen: ${response.statusText || response.error}`)); } }); }); const imgElement = await new Promise((resolve, reject) => { const img = new Image(); img.onload = () => resolve(img); img.onerror = (e) => reject(new Error("Fallo al cargar la imagen obtenida en el objeto Image.")); img.src = URL.createObjectURL(blob); }); URL.revokeObjectURL(imgElement.src); console.log("Imagen de API externa cargada. Procediendo con la subida."); await uploadDrawingToGallery(imgElement, false, true); // Usa Image object, redirige } catch (error) { console.error("Error al subir boceto de API:", error); setUploadStatusMessage(`Error API: ${error.message}`, 'upload-error-msg'); alert("Ocurrió un error al subir el boceto de API: " + error.message); } finally { // Quitar spinners al finalizar if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'none'; if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'none'; } } /** * Función para subir N dibujos del canvas del juego. * @param {number} count - Número de veces a subir. */ async function uploadGameCanvasNTimes(count) { const uploadBtn = document.getElementById(count === 10 ? 'uploadTenTimesButton' : 'uploadTwentyTimesButton'); const superSendButton = document.getElementById('superSendButton'); const spinner = superSendButton ? superSendButton.querySelector('.spinner-border') : null; const uploadStatusSection = document.getElementById('uploadStatusSection'); // Mostrar spinners para todos los botones involucrados if (uploadBtn) uploadBtn.querySelector('.spinner-border').style.display = 'inline-block'; if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'inline-block'; const uploadRandomBtn = document.getElementById('uploadRandomSketchButton'); if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'inline-block'; // --- Verificar Cooldown --- const now = Date.now(); const timeLeft = lastUploadTime + COOLDOWN_DURATION - now; if (timeLeft > 0) { const secondsLeft = Math.ceil(timeLeft / 1000); const msg = `Cooldown: ${secondsLeft}s. Espera para iniciar la subida múltiple.`; setUploadStatusMessage(msg, 'cooldown-active-msg', 0); // No limpiar automáticamente console.warn(msg); // Quitar spinners si no se procede if (uploadBtn) uploadBtn.querySelector('.spinner-border').style.display = 'none'; if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'none'; if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'inline-block'; return; } setUploadStatusMessage(`Iniciando ${count} subidas...`, ''); // Mensaje sin clase especial let successfulUploads = 0; for (let i = 0; i < count; i++) { setUploadStatusMessage(`Subiendo ${i + 1}/${count}... (éxitos: ${successfulUploads})`, ''); const mainCanvas = document.getElementById('canvas'); if (!mainCanvas) { console.error("No se encontró el canvas principal del juego para subida múltiple. Deteniendo."); setUploadStatusMessage('Error: Canvas no encontrado.', 'upload-error-msg'); break; } let sourceCanvas = mainCanvas; const targetWordElement = document.getElementById('targetword'); const isWordGuessMode = (targetWordElement && targetWordElement.style.display !== 'none'); if (!isWordGuessMode && typeof window.ze !== 'undefined' && window.ze instanceof HTMLCanvasElement && window.ze.width > 0 && window.ze.height > 0) { sourceCanvas = window.ze; } // Importante: shouldRedirect = false para que no redirija por cada subida const success = await uploadDrawingToGallery(sourceCanvas, true, false); if (success) { successfulUploads++; } else { // Si falla una subida, aún esperamos el cooldown para el siguiente intento. console.warn(`Subida ${i + 1} falló.`); } // Pausa obligatoria entre subidas para respetar el cooldown del servidor if (i < count - 1) { // No esperar después de la última subida setUploadStatusMessage(`Subida ${i + 1} completada. Esperando cooldown...`, 'cooldown-active-msg'); await new Promise(resolve => setTimeout(resolve, COOLDOWN_DURATION)); } } // Quitar spinners y mostrar resultado final if (uploadBtn) uploadBtn.querySelector('.spinner-border').style.display = 'none'; if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'none'; if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'none'; setUploadStatusMessage(`Completado: ${successfulUploads} de ${count} subidas exitosas.`, successfulUploads === count ? 'upload-success-msg' : 'upload-error-msg'); alert(`¡Finalizado! ${successfulUploads} de ${count} dibujos subidos exitosamente.`); } /** * Función para subir N bocetos de API. * @param {number} count - Número de veces a subir. */ async function uploadRandomSketchNTimes(count) { const uploadBtn = document.getElementById(count === 10 ? 'uploadTenTimesButton' : 'uploadTwentyTimesButton'); const superSendButton = document.getElementById('superSendButton'); const spinner = superSendButton ? superSendButton.querySelector('.spinner-border') : null; const uploadStatusSection = document.getElementById('uploadStatusSection'); const uploadRandomBtn = document.getElementById('uploadRandomSketchButton'); // Mostrar spinners if (uploadBtn) uploadBtn.querySelector('.spinner-border').style.display = 'inline-block'; if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'inline-block'; if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'inline-block'; // --- Verificar Cooldown --- const now = Date.now(); const timeLeft = lastUploadTime + COOLDOWN_DURATION - now; if (timeLeft > 0) { const secondsLeft = Math.ceil(timeLeft / 1000); const msg = `Cooldown: ${secondsLeft}s. Espera para iniciar la subida múltiple.`; setUploadStatusMessage(msg, 'cooldown-active-msg', 0); console.warn(msg); // Quitar spinners si no se procede if (uploadBtn) uploadBtn.querySelector('.spinner-border').style.display = 'none'; if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'none'; if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'none'; return; } setUploadStatusMessage(`Iniciando ${count} subidas de bocetos...`, ''); let successfulUploads = 0; for (let i = 0; i < count; i++) { setUploadStatusMessage(`Subiendo boceto ${i + 1}/${count}... (éxitos: ${successfulUploads})`, ''); try { const imageUrl = 'https://picsum.photos/1200/1000'; const blob = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: imageUrl, responseType: "blob", onload: (response) => { if (response.status === 200) resolve(response.response); else reject(new Error(`Failed to fetch: ${response.status}`)); }, onerror: (response) => reject(new Error(`Network error: ${response.statusText || response.error}`)), }); }); const imgElement = await new Promise((resolve, reject) => { const img = new Image(); img.onload = () => resolve(img); img.onerror = () => reject(new Error("Failed to load fetched image.")); img.src = URL.createObjectURL(blob); }); URL.revokeObjectURL(imgElement.src); const success = await uploadDrawingToGallery(imgElement, false, false); // No redirigir if (success) { successfulUploads++; } else { console.warn(`Subida de boceto ${i + 1} falló.`); } } catch (error) { console.error(`Error en la subida de boceto ${i + 1}:`, error); setUploadStatusMessage(`Error: Subida de boceto ${i + 1} falló.`, 'upload-error-msg'); } // Pausa obligatoria entre subidas para respetar el cooldown del servidor if (i < count - 1) { setUploadStatusMessage(`Boceto ${i + 1} completado. Esperando cooldown...`, 'cooldown-active-msg'); await new Promise(resolve => setTimeout(resolve, COOLDOWN_DURATION)); } } // Quitar spinners y mostrar resultado final if (uploadBtn) uploadBtn.querySelector('.spinner-border').style.display = 'none'; if (superSendButton) superSendButton.querySelector('.spinner-border').style.display = 'none'; if (uploadRandomBtn) uploadRandomBtn.querySelector('.spinner-border').style.display = 'none'; setUploadStatusMessage(`Completado: ${successfulUploads} de ${count} bocetos subidos exitosamente.`, successfulUploads === count ? 'upload-success-msg' : 'upload-error-msg'); alert(`¡Finalizado! ${successfulUploads} de ${count} bocetos subidos exitosamente.`); } /** * Configura la interfaz del script: crea los botones personalizados y oculta los originales. */ function setupInterface() { const rightbar = document.querySelector('#rightbar'); if (!rightbar || document.getElementById('superSendButton')) return; // Ya existe o no hay padre. // Ocultar botones originales del juego const originalSendToGalleryButton = document.getElementById('sendtogallery'); if (originalSendToGalleryButton) originalSendToGalleryButton.style.display = 'none'; const originalDownloadCanvasButton = document.getElementById('downloadcanvas'); if (originalDownloadCanvasButton) originalDownloadCanvasButton.style.display = 'none'; const originalDropdownButton = document.getElementById('roomcontrols-menu'); if (originalDropdownButton) originalDropdownButton.style.display = 'none'; const originalDropdownMenu = document.querySelector('#roomcontrols .dropdown-menu'); if (originalDropdownMenu) originalDropdownMenu.style.display = 'none'; // Ocultar el mensaje "Inicie sesión en su cuenta..." si está presente. const loginMessage = document.querySelector('#roomcontrols .dropdown-header h6'); if (loginMessage && loginMessage.textContent.includes('Inicie sesión en su cuenta')) { loginMessage.style.display = 'none'; } // --- Crear y añadir el botón "Subir Dibujo Falso" --- const superSendButton = document.createElement('button'); superSendButton.id = 'superSendButton'; superSendButton.type = 'button'; superSendButton.className = 'btn btn-primary btn-block'; superSendButton.innerHTML = ` <i class="fas fa-cloud-upload-alt"></i> <span>Subir Dibujo Falso</span> <div class="spinner-border spinner-border-sm" role="status" style="display: none;"></div> `; superSendButton.addEventListener('click', uploadGameCanvasDrawing); rightbar.prepend(superSendButton); // --- Crear y añadir el botón "Descargar Dibujo" --- const downloadCanvasButton = document.createElement('button'); downloadCanvasButton.id = 'downloadCanvasButton'; downloadCanvasButton.type = 'button'; downloadCanvasButton.className = 'btn btn-secondary btn-block'; downloadCanvasButton.innerHTML = ` <i class="fas fa-download"></i> <span>Descargar Dibujo</span> <div class="spinner-border spinner-border-sm" role="status" style="display: none;"></div> `; downloadCanvasButton.addEventListener('click', downloadDrawing); superSendButton.after(downloadCanvasButton); // --- Crear y añadir el botón "Subir 20 Dibujos Falsos" --- const uploadTwentyTimesButton = document.createElement('button'); uploadTwentyTimesButton.id = 'uploadTwentyTimesButton'; uploadTwentyTimesButton.type = 'button'; uploadTwentyTimesButton.className = 'btn btn-success btn-block'; uploadTwentyTimesButton.innerHTML = ` <i class="fas fa-image"></i> <span>Subir 20 Dibujos Falsos</span> <div class="spinner-border spinner-border-sm" role="status" style="display: none;"></div> `; uploadTwentyTimesButton.addEventListener('click', () => uploadRandomSketchNTimes(20)); downloadCanvasButton.after(uploadTwentyTimesButton); // --- Crear y añadir el botón "Subir 10 Dibujos Falsos" --- const uploadTenTimesButton = document.createElement('button'); uploadTenTimesButton.id = 'uploadTenTimesButton'; uploadTenTimesButton.type = 'button'; uploadTenTimesButton.className = 'btn btn-info btn-block'; uploadTenTimesButton.innerHTML = ` <i class="fas fa-cloud-upload-alt"></i> <span>Subir 10 Dibujos Falsos</span> <div class="spinner-border spinner-border-sm" role="status" style="display: none;"></div> `; uploadTenTimesButton.addEventListener('click', () => uploadGameCanvasNTimes(10)); uploadTwentyTimesButton.after(uploadTenTimesButton); // --- Mensaje de estado de subidas y cooldown --- const uploadStatusSection = document.createElement('div'); uploadStatusSection.id = 'uploadStatusSection'; uploadStatusSection.textContent = ''; // Vacío inicialmente uploadTenTimesButton.after(uploadStatusSection); // Cargar el último tiempo de subida al iniciar el script } // --- Inicio del Script --- // Paso 1: Esperar por la barra lateral (#rightbar) para poder añadir los botones. waitForElement('#rightbar', (rightbarElement) => { if (rightbarElement) { setupInterface(); } }); // Paso 2: Configurar un observador de mutaciones para mantener la interfaz. waitForElement('body', (bodyElement) => { const observer = new MutationObserver(() => { // Si nuestros botones no están presentes, o un botón original reaparece, re-ejecutamos setupInterface(). if (!document.getElementById('superSendButton') || !document.getElementById('downloadCanvasButton') || !document.getElementById('uploadTwentyTimesButton') || !document.getElementById('uploadTenTimesButton') || (document.getElementById('sendtogallery') && document.getElementById('sendtogallery').style.display !== 'none')) { setupInterface(); } }); observer.observe(bodyElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'disabled'] }); }); })();