您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Advanced video editor for Drawaria.online Audio, effects and loaded images
// ==UserScript== // @name Drawaria Video Editor // @namespace http://tampermonkey.net/ // @version 1.0 // @description Advanced video editor for Drawaria.online Audio, effects and loaded images // @author YouTubeDrawaria // @match https://drawaria.online/* // @grant none // @license MIT // @icon https://www.google.com/s2/favicons?sz=64&domain=drawaria.online // ==/UserScript== (function() { 'use strict'; // Función para crear e inyectar el editor de video function createVideoEditor() { // Crear contenedor principal del editor const editorContainer = document.createElement('div'); editorContainer.id = 'drawaria-video-editor'; editorContainer.style.cssText = ` position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 999999; background: #1a1a1a; display: none; `; // Inyectar el CSS const style = document.createElement('style'); style.textContent = ` /* Estilos generales del editor de video */ #drawaria-video-editor { font-family: 'Arial', sans-serif; color: white; overflow: hidden; } #drawaria-video-editor * { box-sizing: border-box; } #drawaria-video-editor #main-container { display: flex; height: 100vh; width: 100vw; background: #222; color: white; } /* Panel izquierdo para controles */ #drawaria-video-editor #left-panel { flex: 1; display: flex; flex-direction: column; background: #111; padding: 10px; overflow-y: auto; transition: transform 0.3s ease; border-right: 3px solid #444; } #drawaria-video-editor #controls { width: 100%; box-sizing: border-box; } #drawaria-video-editor .control-group { display: flex; flex-direction: column; align-items: center; gap: 10px; margin-bottom: 20px; background: #1c1c1c; padding: 10px; border-radius: 8px; } #drawaria-video-editor .control-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; justify-content: center; width: 100%; } #drawaria-video-editor .url-container { display: flex; gap: 10px; align-items: center; width: 100%; max-width: 500px; } /* Panel derecho para el canvas */ #drawaria-video-editor #right-panel { flex: 2; display: flex; flex-direction: column; align-items: center; justify-content: center; background: #222; position: relative; transition: transform 0.3s ease; } /* Estilos para el canvas */ #drawaria-video-editor #canvas { display: block; max-width: 100%; max-height: 80vh; border: 1px solid #444; cursor: crosshair; background-color: black; border-radius: 4px; box-shadow: 0 0 15px rgba(0, 100, 255, 0.3); } /* Estilos de botones */ #drawaria-video-editor .button { padding: 10px 20px; font-weight: bold; border: none; cursor: pointer; border-radius: 8px; transition: all 0.2s ease; font-size: 14px; min-width: 120px; } #drawaria-video-editor .button:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.3); } #drawaria-video-editor .start-btn { background: #0f0; color: #000; } #drawaria-video-editor .stop-btn { background: #ff0; color: #000; } #drawaria-video-editor .emergency-btn { background: #f44336; color: #fff; } #drawaria-video-editor .download-btn { background: #0ff; color: #000; } #drawaria-video-editor .single-image-btn { background: #666; color: #fff; } #drawaria-video-editor .multi-image-btn { background: #007bff; color: #fff; } #drawaria-video-editor .size-btn { background: #555; color: #fff; } #drawaria-video-editor .playback-btn { background: #337ab7; color: #fff; } #drawaria-video-editor .diagonal-btn { background: #2a5a8a; color: #fff; } #drawaria-video-editor .effect-btn { background: #d9534f; color: #fff; } #drawaria-video-editor .zoom-btn { background: #5cb85c; color: #fff; } #drawaria-video-editor .preview-btn { background: #f0ad4e; color: #000; } #drawaria-video-editor .file-nav-btn { background: #6c757d; color: #fff; } #drawaria-video-editor .audio-btn { background: #666; color: #fff; } #drawaria-video-editor .remove-effects-btn { background: #a52a2a; color: #fff; } #drawaria-video-editor .load-url-btn { background: #17a2b8; color: #fff; } #drawaria-video-editor .soundcloud-btn { background: #ff5500; color: #fff; } #drawaria-video-editor .screen-rec-btn { background: #9c27b0; color: #fff; } #drawaria-video-editor .close-editor-btn { background: #dc3545; color: #fff; } #drawaria-video-editor .widget-btn { background: #ff5500; color: #fff; font-size: 12px; padding: 5px 10px; min-width: auto; width: 35px; height: 35px; display: flex; align-items: center; justify-content: center; } /* Animaciones */ @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } #drawaria-video-editor .recording-btn { animation: pulse 1.5s infinite; background: #f44336 !important; } /* Controles de grabación de pantalla */ #drawaria-video-editor #screen-recording-controls { position: fixed; bottom: 20px; left: 20px; z-index: 10001; background: rgba(0,0,0,0.7); padding: 10px; border-radius: 8px; box-shadow: 0 0 15px rgba(0, 100, 255, 0.5); backdrop-filter: blur(5px); border: 1px solid #333; } #drawaria-video-editor #recording-timer { color: #00ffff; font-weight: bold; font-size: 18px; margin-top: 5px; text-align: center; } /* Toast notifications */ #drawaria-video-editor #toast-container { position: fixed; top: 20px; right: 20px; z-index: 10001; display: flex; flex-direction: column; gap: 10px; width: 300px; } #drawaria-video-editor .toast { background: rgba(30, 30, 30, 0.9); color: white; padding: 15px; border-radius: 8px; border-left: 4px solid #007bff; box-shadow: 0 5px 15px rgba(0,0,0,0.3); transition: transform 0.3s ease, opacity 0.3s ease; } #drawaria-video-editor .toast.success { border-left-color: #28a745; } #drawaria-video-editor .toast.error { border-left-color: #dc3545; } #drawaria-video-editor .toast.warning { border-left-color: #ffc107; } #drawaria-video-editor .toast.info { border-left-color: #17a2b8; } #drawaria-video-editor .soundcloud-badge { background: linear-gradient(135deg, #f55125, #ff8800); padding: 4px 10px; border-radius: 12px; font-weight: bold; font-size: 12px; display: inline-block; margin: 5px 0; } #drawaria-video-editor .newgrounds-badge { background: linear-gradient(135deg, #00b4ff, #004799); padding: 4px 10px; border-radius: 12px; font-weight: bold; font-size: 12px; display: inline-block; margin: 5px 0; } #drawaria-video-editor input[type="text"], #drawaria-video-editor input[type="range"] { background: #2a2a2a; border: 1px solid #444; border-radius: 4px; color: white; padding: 8px; } #drawaria-video-editor input[type="range"] { padding: 0; height: 30px; } /* Widget de SoundCloud */ #drawaria-video-editor #soundcloud-widget-container { position: fixed; bottom: 10px; left: 10px; background: rgba(0,0,0,0.8); padding: 10px; border-radius: 8px; border: 1px solid #333; z-index: 10000; transition: opacity 0.3s ease; backdrop-filter: blur(3px); } #drawaria-video-editor #soundcloud-widget-container.hidden { display: none; } #drawaria-video-editor #soundcloud-widget { width: 300px; height: 166px; border: none; border-radius: 4px; } #drawaria-video-editor .widget-controls { display: flex; gap: 10px; margin-top: 5px; align-items: center; } #drawaria-video-editor .widget-info { color: #fff; font-size: 12px; margin-top: 5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 280px; } /* Responsive */ @media (max-width: 768px) { #drawaria-video-editor #main-container { flex-direction: column; height: auto; } #drawaria-video-editor #left-panel, #drawaria-video-editor #right-panel { width: 100%; flex: none; } #drawaria-video-editor #canvas { margin: 20px auto; max-width: 90%; max-height: 50vh; } } .hidden { display: none !important; } `; document.head.appendChild(style); // HTML del editor editorContainer.innerHTML = ` <div id="toast-container"></div> <div id="main-container"> <div id="left-panel"> <div id="controls"> <div class="control-group"> <div style="display: flex; justify-content: space-between; align-items: center; width: 100%;"> <p style="font-size: 19px; margin: 0; font-weight: bold;">Drawaria.online Video Editor</p> <button class="button close-editor-btn" onclick="window.drawariaVideoEditor.close()">✖ Cerrar</button> </div> <p style="font-size: 14px; margin: 0; color: #aaa;">Imagen URL:</p> <div class="url-container"> <input type="text" id="imageUrl" placeholder="https://i.ibb.co/QFnyrtyD/drawaria-video-editor.png" value="https://i.ibb.co/WWhBWyMG/editor-video-drawaria.png" style="flex: 1;"> <button class="button load-url-btn" onclick="window.drawariaVideoEditor.loadImageFromUrl()">🔗 Cargar URL</button> </div> </div> <div class="control-group"> <p style="font-size: 14px; margin: 0; color: #aaa;">🎶 Cargar Audio:</p> <div class="url-container"> <input type="text" id="audioUrl" placeholder="SoundCloud, Newgrounds o URL directa de audio..." value="https://audio.ngfiles.com/581000/581799_Remix--Super-Mario-Bros.mp3?f1405275853" style="flex: 1;"> <button class="button soundcloud-btn" onclick="window.drawariaVideoEditor.loadAudioFromUrl()">🎵 Cargar Audio</button> </div> <div class="control-row"> <button class="button load-url-btn" onclick="window.drawariaVideoEditor.testAudioUrl()">🔍 Comprobar URL</button> <button class="button load-url-btn" onclick="window.drawariaVideoEditor.testAudioCapture()">🔊 Probar Captura</button> </div> <div style="display: flex; align-items: center; gap: 8px; margin-top: 5px;"> <span class="soundcloud-badge">SoundCloud</span> <span class="newgrounds-badge">Newgrounds</span> </div> </div> <div class="control-group"> <p style="font-size: 14px; margin: 0; color: #aaa;">Ajustar Tamaño:</p> <div class="control-row"> <button class="button size-btn" onclick="window.drawariaVideoEditor.setCanvasSize('small')">Pequeño</button> <button class="button size-btn" onclick="window.drawariaVideoEditor.setCanvasSize('medium')">Mediano</button> <button class="button size-btn" onclick="window.drawariaVideoEditor.setCanvasSize('large')">Grande</button> <button class="button size-btn" onclick="window.drawariaVideoEditor.setCanvasSize('drawaria')">Drawaria</button> </div> </div> <div class="control-group"> <div class="control-row"> <label for="speedSlider">Velocidad: <span id="speedValue">2.0x</span></label> <input type="range" id="speedSlider" min="0.1" max="10" step="0.1" value="2" oninput="window.drawariaVideoEditor.updateSpeed()" style="flex: 1; margin: 0 10px;"> </div> <div class="control-row"> <button class="button zoom-btn" onclick="window.drawariaVideoEditor.zoomImage(0.1)">🔍 Zoom In</button> <button class="button zoom-btn" onclick="window.drawariaVideoEditor.zoomImage(-0.1)">🔎 Zoom Out</button> </div> </div> <div class="control-group"> <p style="font-size: 14px; margin: 0; color: #aaa;">Dirección de Scroll:</p> <div class="control-row"> <button class="button playback-btn" onclick="window.drawariaVideoEditor.setScrollMode('right')">Derecha</button> <button class="button playback-btn" onclick="window.drawariaVideoEditor.setScrollMode('left')">Izquierda</button> <button class="button playback-btn" onclick="window.drawariaVideoEditor.setScrollMode('up')">Arriba</button> <button class="button playback-btn" onclick="window.drawariaVideoEditor.setScrollMode('down')">Abajo</button> <button class="button playback-btn" onclick="window.drawariaVideoEditor.setScrollMode('static')">Estático</button> </div> </div> <div class="control-group"> <button class="button preview-btn" id="previewBtn" onclick="window.drawariaVideoEditor.togglePreviewMode()">🖼️ Previsualizar</button> <button class="button start-btn" id="startBtn" onclick="window.drawariaVideoEditor.startAnimation()">▷ Iniciar Grabación</button> <button class="button screen-rec-btn" onclick="window.drawariaVideoEditor.startScreenRecording()">🎥 Compartir Pantalla</button> </div> <div class="control-group"> <button class="button playback-btn" id="pauseBtn" onclick="window.drawariaVideoEditor.togglePause()">⏸ Pausar/Reanudar</button> <button class="button stop-btn" id="stopBtn" style="display:none;" onclick="window.drawariaVideoEditor.stopRecording()">⏹ Parar y Descargar</button> <button class="button emergency-btn" id="emergencyBtn" style="display:none;" onclick="window.drawariaVideoEditor.emergencyStop()">🛑 PARAR AHORA</button> </div> <div class="control-group"> <p style="font-size: 14px; margin: 0; color: #aaa;">Efectos Visuales:</p> <div class="control-row"> <button class="button effect-btn" onclick="window.drawariaVideoEditor.toggleEffect('snow')">❄️ Nieve</button> <button class="button effect-btn" onclick="window.drawariaVideoEditor.toggleEffect('sparkles')">✨ Destellos</button> <button class="button effect-btn" onclick="window.drawariaVideoEditor.toggleEffect('fire')">🔥 Fuego</button> </div> <div class="control-row"> <button class="button remove-effects-btn" onclick="window.drawariaVideoEditor.removeAllEffects()">❌ Remover Efectos</button> </div> </div> <div id="status" style="margin-top: 10px; font-size: 14px; color: #888; text-align: center; min-height: 20px;"></div> </div> </div> <div id="right-panel"> <div style="position: relative; width: 100%;"> <canvas id="canvas"></canvas> <button class="button download-btn" id="downloadBtn" style="display:none; width: 90%; margin-top: 15px;" onclick="window.drawariaVideoEditor.downloadVideo()"> ↓ Descargar como WebM ↓ </button> <div style="position: absolute; top: 5px; right: 5px; color: #aaa; font-size: 12px; background: rgba(0,0,0,0.5); padding: 3px 10px; border-radius: 5px;"> Resolución: <span id="resolutionDisplay">862x718</span> </div> </div> </div> </div> <div id="screen-recording-controls" style="display:none;"> <button class="button recording-btn" id="screenStopBtn" onclick="window.drawariaVideoEditor.stopScreenRecording()"> ■ Detener Grabación </button> <div id="recording-timer">00:00</div> </div> <div id="soundcloud-widget-container" class="hidden"> <iframe id="soundcloud-widget" scrolling="no" frameborder="no" allow="autoplay; encrypted-media; fullscreen" sandbox="allow-scripts allow-presentation"> </iframe> <div class="widget-controls"> <button class="button widget-btn" onclick="window.drawariaVideoEditor.soundCloudManager.play()">▶</button> <button class="button widget-btn" onclick="window.drawariaVideoEditor.soundCloudManager.pause()">⏸</button> <button class="button widget-btn" onclick="window.drawariaVideoEditor.toggleWidgetVisibility()">✖</button> </div> <div class="widget-info" id="widget-info">Widget SoundCloud</div> </div> <audio id="backgroundMusic" loop crossOrigin="anonymous"></audio> <div class="control-group"> <button class="button multi-image-btn" onclick="document.getElementById('multiFileInput').click()">🖼️ Múltiples Imágenes</button> <input type="file" id="multiFileInput" accept="image/*" multiple style="display: none;"> <button class="button single-image-btn" onclick="document.getElementById('singleFileInput').click()">Importar imagen</button> <input type="file" id="singleFileInput" accept="image/*" style="display: none;"> <button class="button audio-btn" onclick="document.getElementById('audioInput').click()">Archivo Local</button> <input type="file" id="audioInput" accept="audio/*" style="display: none;"> </div> `; document.body.appendChild(editorContainer); return editorContainer; } // ========================================== // MANAGER DE SOUNDCLOUD WIDGET API (MEJORADO) // ========================================== class SoundCloudWidgetManager { constructor() { this.widget = null; this.isLoaded = false; this.currentTrack = null; this.isPlaying = false; this.hasUserInteracted = false; this.detectUserInteraction(); } isSoundCloudUrl(url) { const soundcloudPatterns = [ /^https?:\/\/(www\.)?soundcloud\.com\//, /^https?:\/\/on\.soundcloud\.com\// ]; return soundcloudPatterns.some(pattern => pattern.test(url)); } async loadSoundCloudTrack(soundcloudUrl) { try { window.drawariaVideoEditor.updateStatus('🎵 Cargando SoundCloud (API oficial)...'); const iframe = window.drawariaVideoEditor.container.querySelector('#soundcloud-widget'); const widgetUrl = `https://w.soundcloud.com/player/?url=${encodeURIComponent(soundcloudUrl)}&auto_play=false&hide_related=true&show_comments=false&show_user=true&visual=true`; iframe.setAttribute('allow', 'autoplay; encrypted-media; fullscreen'); iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-presentation'); iframe.src = widgetUrl; window.drawariaVideoEditor.container.querySelector('#soundcloud-widget-container').classList.remove('hidden'); return new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error('Timeout cargando SoundCloud')); }, 15000); iframe.onload = () => { clearTimeout(timeout); resolve({ title: 'SoundCloud Track', user: { username: 'Usuario' }}); window.drawariaVideoEditor.container.querySelector('#widget-info').textContent = 'SoundCloud Track cargado'; window.drawariaVideoEditor.updateStatus('✅ SoundCloud cargado', 'success'); this.isLoaded = true; }; }); } catch (error) { console.error('Error con SoundCloud:', error); throw new Error(`SoundCloud: ${error.message}`); } } play() { if (!this.hasUserInteracted) { window.drawariaVideoEditor.updateStatus('👆 Haz clic en cualquier botón primero', 'warning'); return; } if (this.widget && this.isLoaded) { this.widget.play(); this.isPlaying = true; window.drawariaVideoEditor.updateStatus('▶️ Reproduciendo SoundCloud', 'info'); } } pause() { if (this.widget && this.isLoaded) { this.widget.pause(); this.isPlaying = false; window.drawariaVideoEditor.updateStatus('⏸ SoundCloud pausado', 'info'); } } detectUserInteraction() { const detectInteraction = () => { this.hasUserInteracted = true; document.removeEventListener('click', detectInteraction); document.removeEventListener('keydown', detectInteraction); }; document.addEventListener('click', detectInteraction); document.addEventListener('keydown', detectInteraction); } } // ========================================== // SISTEMA DE MÚLTIPLES ARCHIVOS DE AUDIO // ========================================== class MultiAudioManager { constructor() { this.audioContext = null; this.audioSources = new Map(); this.masterGain = null; this.outputDestination = null; } async init() { try { this.audioContext = new (window.AudioContext || window.webkitAudioContext)(); this.masterGain = this.audioContext.createGain(); this.outputDestination = this.audioContext.createMediaStreamDestination(); this.masterGain.connect(this.outputDestination); window.drawariaVideoEditor.updateStatus('🎵 Sistema de múltiple audio inicializado', 'success'); return true; } catch (error) { console.error('Error inicializando Web Audio API:', error); window.drawariaVideoEditor.updateStatus('❌ Error inicializando sistema de audio', 'error'); return false; } } async loadAudioFile(file, id = null) { if (!this.audioContext) { await this.init(); } const audioId = id || `audio_${Date.now()}`; try { const arrayBuffer = await file.arrayBuffer(); const audioBuffer = await this.audioContext.decodeAudioData(arrayBuffer); const source = this.audioContext.createBufferSource(); const gainNode = this.audioContext.createGain(); source.buffer = audioBuffer; source.loop = true; source.connect(gainNode); gainNode.connect(this.masterGain); this.audioSources.set(audioId, { source: source, gain: gainNode, buffer: audioBuffer, isPlaying: false, file: file }); window.drawariaVideoEditor.updateStatus(`✅ Audio ${file.name} cargado (ID: ${audioId})`, 'success'); return audioId; } catch (error) { console.error('Error cargando audio:', error); window.drawariaVideoEditor.updateStatus(`❌ Error cargando ${file.name}`, 'error'); throw error; } } getAudioStream() { return this.outputDestination ? this.outputDestination.stream : null; } listLoadedAudio() { const list = []; this.audioSources.forEach((audio, id) => { list.push({ id: id, name: audio.file.name, isPlaying: audio.isPlaying, volume: audio.gain.gain.value }); }); return list; } } // Clase principal del editor class DrawariaVideoEditor { constructor(container) { this.container = container; this.initializeVariables(); this.setupEventListeners(); this.initializeCanvas(); this.showNotification('Editor de Video cargado - ¡Listo para usar!', 'success'); } initializeVariables() { // Variables globales del editor this.mediaRecorder = null; this.recordedChunks = []; this.videoBlob = null; this.isRecording = false; this.animationRunning = false; this.animationFrameId = null; this.scrollSpeed = 2; this.scrollMode = 'right'; this.isPaused = false; this.zoomLevel = 1.0; this.isPreviewMode = false; this.imageLoaded = false; this.previewOffset = { x: 0, y: 0 }; this.isDragging = false; this.dragStartX = 0; this.dragStartY = 0; this.scrollPos = { x: 0, y: 0, directionX: 1, directionY: 1 }; this.activeEffects = new Set(); this.snowFlakes = []; this.sparkles = []; this.fireParticles = []; this.isScreenRecording = false; this.recordingStartTime = null; this.recordingTimerInterval = null; this.sizePresets = { 'small': { width: 640, height: 480 }, 'medium': { width: 960, height: 720 }, 'large': { width: 1280, height: 960 }, 'drawaria': { width: 862, height: 718 } }; this.canvasSize = 'drawaria'; this.img = new Image(); this.img.crossOrigin = "anonymous"; this.img.oncontextmenu = () => false; // Inicializar managers de audio this.soundCloudManager = new SoundCloudWidgetManager(); this.multiAudioManager = new MultiAudioManager(); } setupEventListeners() { // Configurar manejo de archivos const singleFileInput = this.container.querySelector('#singleFileInput'); const multiFileInput = this.container.querySelector('#multiFileInput'); const audioInput = this.container.querySelector('#audioInput'); singleFileInput.addEventListener('change', (e) => { if (e.target.files && e.target.files.length > 0) { this.handleSingleFile(e.target.files); } e.target.value = ''; }); multiFileInput.addEventListener('change', (e) => { if (e.target.files && e.target.files.length > 0) { this.handleMultiFiles(Array.from(e.target.files)); } e.target.value = ''; }); audioInput.addEventListener('change', (e) => { if (e.target.files && e.target.files.length > 0) { this.handleAudioFile(e.target.files); } e.target.value = ''; }); // Configurar canvas para arrastrar const canvas = this.container.querySelector('#canvas'); canvas.addEventListener('mousedown', (e) => this.startDrag(e)); canvas.addEventListener('mousemove', (e) => this.drag(e)); canvas.addEventListener('mouseup', (e) => this.stopDrag(e)); // Atajos de teclado document.addEventListener('keydown', (e) => this.handleKeyDown(e)); } initializeCanvas() { this.setCanvasSize('drawaria'); this.updateSpeed(); // Cargar imagen por defecto const defaultUrl = this.container.querySelector('#imageUrl').value; if (defaultUrl) { this.loadImageFromUrl(); } } // ========================================== // FUNCIONES DE AUDIO MEJORADAS (DESDE TU PÁGINA) // ========================================== checkBrowserCompatibility() { const issues = []; if (!window.MediaRecorder || !MediaRecorder.isTypeSupported('video/webm;codecs=vp9')) { issues.push('MediaRecorder API no soportado'); } if (!window.navigator.mediaDevices || !window.navigator.mediaDevices.getDisplayMedia) { issues.push('getDisplayMedia API no soportado'); } if (issues.length > 0) { const message = `⚠️ Problemas de compatibilidad: ${issues.join(', ')}`; this.showNotification(message, 'warning'); return false; } return true; } isNewgroundsUrl(url) { return url.includes('audio.ngfiles.com') || url.includes('newgrounds.com/audio'); } async loadNewgroundsAudio(newgroundsUrl) { try { this.updateStatus('🎵 Cargando Newgrounds (audio.ngfiles.com)...', 'info'); const proxyHosts = [ 'https://corsproxy.io/?', 'https://thingproxy.freeboard.io/fetch/', 'https://api.codetabs.com/v1/proxy?quest=' ]; for (const proxy of proxyHosts) { try { const proxyUrl = `${proxy}${encodeURIComponent(newgroundsUrl)}`; const response = await fetch(proxyUrl, { mode: 'cors' }); if (!response.ok) continue; const blob = await response.blob(); if (blob.size === 0) continue; const audioPlayer = this.container.querySelector('#backgroundMusic'); const audioUrl = URL.createObjectURL(blob); audioPlayer.src = audioUrl; return new Promise((resolve) => { audioPlayer.addEventListener('canplaythrough', () => { const fileName = newgroundsUrl.split('/').pop(); this.updateStatus(`✅ Audio Newgrounds cargado: ${fileName}`, 'success'); this.showNotification(`Audio Newgrounds cargado: ${fileName}`, 'success'); resolve(); }, { once: true }); }); } catch (error) { console.warn(`Proxy ${proxy} falló:`, error); continue; } } throw new Error('No se pudo cargar el audio de Newgrounds'); } catch (error) { console.error('Error con Newgrounds:', error); this.updateStatus(`❌ Error Newgrounds: ${error.message}`, 'error'); throw error; } } async loadAudioFromUrl() { const audioUrl = this.container.querySelector('#audioUrl').value.trim(); if (!audioUrl) { this.showNotification('❌ URL de audio requerida', 'error'); return; } try { if (this.soundCloudManager.isSoundCloudUrl(audioUrl)) { await this.soundCloudManager.loadSoundCloudTrack(audioUrl); return; } if (this.isNewgroundsUrl(audioUrl)) { await this.loadNewgroundsAudio(audioUrl); return; } this.updateStatus('🎶 Cargando audio desde URL...', 'info'); const audioPlayer = this.container.querySelector('#backgroundMusic'); audioPlayer.crossOrigin = "anonymous"; const testAudio = new Audio(); testAudio.src = audioUrl; return new Promise((resolve, reject) => { testAudio.onloadeddata = function() { audioPlayer.src = audioUrl; audioPlayer.load(); audioPlayer.addEventListener('canplaythrough', function() { const fileName = audioUrl.split('/').pop().split('?') || 'Audio cargado'; window.drawariaVideoEditor.updateStatus(`✅ Audio cargado: ${fileName}`, 'success'); window.drawariaVideoEditor.showNotification(`Audio cargado: ${fileName}`, 'success'); resolve(); }, { once: true }); }; testAudio.onerror = function() { reject(new Error('No se pudo cargar el audio')); }; testAudio.load(); }); } catch (error) { console.error('Error cargando audio:', error); this.updateStatus(`❌ Error de audio: ${error.message}`, 'error'); this.showNotification(`Error de audio: ${error.message}`, 'error'); } } async testAudioUrl() { const audioUrl = this.container.querySelector('#audioUrl').value.trim(); if (!audioUrl) { this.showNotification('❌ Ingresa una URL para probar', 'error'); return; } this.updateStatus('🔍 Analizando URL de audio...', 'info'); try { const testAudio = new Audio(); testAudio.src = audioUrl; return new Promise((resolve) => { testAudio.onloadeddata = () => { if (this.soundCloudManager.isSoundCloudUrl(audioUrl)) { this.updateStatus('✅ SoundCloud detectado - Compatible con API', 'success'); } else if (this.isNewgroundsUrl(audioUrl)) { this.updateStatus('✅ Newgrounds detectado - Compatible', 'success'); } else if (audioUrl.match(/\.(mp3|wav|ogg|m4a|aac|flac)(\?.*)?$/i)) { this.updateStatus('✅ Formato de audio directo detectado', 'success'); } else { this.updateStatus('⚠️ Tipo de URL desconocido - Podría funcionar', 'warning'); } resolve(); }; testAudio.onerror = () => { this.updateStatus('❌ Error al probar la URL', 'error'); }; testAudio.load(); }); } catch (error) { this.updateStatus(`❌ Error probando URL: ${error.message}`, 'error'); } } testAudioCapture() { const audioPlayer = this.container.querySelector('#backgroundMusic'); if (!audioPlayer || !audioPlayer.src) { this.showNotification('❌ No hay audio cargado para probar', 'error'); return; } try { const audioStream = audioPlayer.captureStream(); if (audioStream.getAudioTracks().length > 0) { this.showNotification('✅ Audio compatible con captura', 'success'); console.log('✅ Pistas de audio encontradas:', audioStream.getAudioTracks().length); audioPlayer.play().then(() => { console.log('✅ Audio reproduciéndose correctamente'); this.updateStatus('🎵 Audio funcionando - Listo para grabación', 'success'); }).catch(e => { console.warn('⚠️ Error auto-play:', e); this.updateStatus('⚠️ Audio cargado - Requiere interacción del usuario', 'warning'); }); } else { this.showNotification('⚠️ Audio cargado pero no tiene pistas capturables', 'warning'); } } catch (error) { this.showNotification('❌ Error en captura de audio: ' + error.message, 'error'); console.error('❌ Error probando captura:', error); } } async prepareAudioForRecording() { const audioPlayer = this.container.querySelector('#backgroundMusic'); if (!audioPlayer || !audioPlayer.src) { console.log('ℹ️ No hay audio cargado'); return false; } try { audioPlayer.load(); audioPlayer.currentTime = 0; return new Promise((resolve) => { const timeoutId = setTimeout(() => { console.warn('⚠️ Timeout preparando audio'); resolve(false); }, 3000); audioPlayer.addEventListener('canplaythrough', () => { clearTimeout(timeoutId); console.log('✅ Audio listo para grabación'); resolve(true); }, { once: true }); audioPlayer.addEventListener('error', (e) => { clearTimeout(timeoutId); console.error('❌ Error preparando audio:', e); resolve(false); }, { once: true }); }); } catch (error) { console.error('❌ Error en preparación de audio:', error); return false; } } // Métodos principales async startAnimation() { if (this.isRecording) { this.showNotification('🛑 Ya hay una grabación en curso', 'warning'); return; } if (!this.imageLoaded) { this.showNotification('⚠️ Debes cargar una imagen primero', 'warning'); return; } // Salir del modo preview if (this.isPreviewMode) { this.togglePreviewMode(false); } // Preparar audio ANTES de iniciar grabación await this.prepareAudioForRecording(); const canvas = this.container.querySelector('#canvas'); const currentSize = this.sizePresets[this.canvasSize]; canvas.width = Math.min(currentSize.width, 1920); canvas.height = Math.min(currentSize.height, 1080); try { const stream = canvas.captureStream(30); // Intentar agregar audio let hasAudio = false; const audioPlayer = this.container.querySelector('#backgroundMusic'); if (audioPlayer && audioPlayer.src) { try { const audioStream = audioPlayer.captureStream(); if (audioStream.getAudioTracks().length > 0) { audioStream.getAudioTracks().forEach(track => { stream.addTrack(track); }); audioPlayer.currentTime = 0; audioPlayer.play().catch(e => console.warn('Error auto-play:', e)); hasAudio = true; console.log('✅ Audio tradicional agregado al stream'); } } catch (e) { console.warn('Error capturando audio tradicional:', e); } } this.recordedChunks = []; const selectedMimeType = 'video/webm;codecs=vp9'; this.mediaRecorder = new MediaRecorder(stream, { mimeType: selectedMimeType, videoBitsPerSecond: 2000000 }); this.mediaRecorder.ondataavailable = (event) => { if (event.data && event.data.size > 0) { this.recordedChunks.push(event.data); } }; this.mediaRecorder.onstop = () => { if (this.recordedChunks.length === 0) { this.showNotification('❌ No se grabaron datos', 'error'); this.restoreInterface(); return; } this.videoBlob = new Blob(this.recordedChunks, { type: selectedMimeType }); this.container.querySelector('#downloadBtn').style.display = 'inline-block'; this.showNotification('✅ Grabación completada', 'success'); this.updateStatus('✅ Grabación completada. Haz clic en descargar'); this.restoreInterface(); }; this.mediaRecorder.start(100); this.isRecording = true; this.animationRunning = true; this.container.querySelector('#stopBtn').style.display = 'inline-block'; this.container.querySelector('#emergencyBtn').style.display = 'inline-block'; this.container.querySelector('#downloadBtn').style.display = 'none'; setTimeout(() => { this.animate(); }, 100); if (hasAudio) { this.showNotification('🎵 Grabando video con audio', 'success'); } else { this.showNotification('ℹ️ Grabando solo video (sin audio)', 'info'); } this.updateStatus('🔴 Grabando scroll de imagen...'); } catch (error) { console.error('Error iniciando grabación:', error); this.showNotification(`❌ Error al iniciar: ${error.message}`, 'error'); this.restoreInterface(); } } animate() { if (!this.animationRunning) { cancelAnimationFrame(this.animationFrameId); return; } const canvas = this.container.querySelector('#canvas'); const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); const viewWidth = canvas.width / this.zoomLevel; const viewHeight = canvas.height / this.zoomLevel; const maxScrollX = this.img.width - viewWidth; const maxScrollY = this.img.height - viewHeight; if (!this.isPaused) { switch (this.scrollMode) { case 'right': this.scrollPos.x = Math.min(this.scrollPos.x + this.scrollSpeed, maxScrollX); break; case 'left': this.scrollPos.x = Math.max(this.scrollPos.x - this.scrollSpeed, 0); break; case 'up': this.scrollPos.y = Math.max(this.scrollPos.y - this.scrollSpeed, 0); break; case 'down': this.scrollPos.y = Math.min(this.scrollPos.y + this.scrollSpeed, maxScrollY); break; case 'static': break; } } // Dibujar la imagen ctx.drawImage(this.img, this.scrollPos.x, this.scrollPos.y, viewWidth, viewHeight, 0, 0, canvas.width, canvas.height ); // Efectos visuales if (this.activeEffects.has('snow')) this.drawSnow(ctx, canvas); if (this.activeEffects.has('sparkles')) this.drawSparkles(ctx, canvas); if (this.activeEffects.has('fire')) this.drawFire(ctx, canvas); this.animationFrameId = requestAnimationFrame(() => this.animate()); } stopRecording() { if (!this.isRecording) return; this.animationRunning = false; cancelAnimationFrame(this.animationFrameId); if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') { this.mediaRecorder.stop(); } this.isRecording = false; this.updateStatus('⏸️ Grabación detenida. Preparando video...', 'info'); } emergencyStop() { this.stopRecording(); this.showNotification('🛑 Parada de emergencia activada', 'warning'); } downloadVideo() { if (!this.videoBlob) { this.showNotification('❌ No hay video para descargar', 'error'); return; } const url = URL.createObjectURL(this.videoBlob); const a = document.createElement('a'); const timestamp = new Date().toISOString().slice(0, 16).replace(/:/g, '-'); document.body.appendChild(a); a.style.display = 'none'; a.href = url; a.download = `drawaria-video-${timestamp}.webm`; a.click(); setTimeout(() => { document.body.removeChild(a); URL.revokeObjectURL(url); }, 1000); this.showNotification('✅ Video descargado con éxito', 'success'); this.videoBlob = null; this.recordedChunks = []; this.container.querySelector('#downloadBtn').style.display = 'none'; } restoreInterface() { this.container.querySelector('#stopBtn').style.display = 'none'; this.container.querySelector('#emergencyBtn').style.display = 'none'; const audioPlayer = this.container.querySelector('#backgroundMusic'); if (audioPlayer && audioPlayer.src) { audioPlayer.pause(); audioPlayer.currentTime = 0; } } // Métodos de control loadImageFromUrl() { const imageUrl = this.container.querySelector('#imageUrl').value.trim(); if (!imageUrl) { this.showNotification('❌ URL de imagen requerida', 'error'); return; } this.updateStatus('🔄 Cargando imagen desde URL...', 'info'); this.img.onload = () => { this.imageLoaded = true; this.updateStatus('✅ Imagen cargada con éxito', 'success'); this.togglePreviewMode(true); this.showNotification('Imagen cargada correctamente', 'success'); }; this.img.onerror = () => { this.updateStatus('❌ Error al cargar imagen', 'error'); this.showNotification('Error al cargar imagen', 'error'); this.imageLoaded = false; }; this.img.src = imageUrl; } setCanvasSize(size) { const canvas = this.container.querySelector('#canvas'); if (!this.sizePresets[size]) return; canvas.width = this.sizePresets[size].width; canvas.height = this.sizePresets[size].height; this.canvasSize = size; this.container.querySelector('#resolutionDisplay').textContent = `${this.sizePresets[size].width}x${this.sizePresets[size].height}`; this.updateStatus(`📏 Tamaño: ${size.toUpperCase()}`, 'info'); this.showNotification(`Tamaño cambiado a ${size.toUpperCase()}`, 'info'); } updateSpeed() { const slider = this.container.querySelector('#speedSlider'); this.scrollSpeed = parseFloat(slider.value); this.container.querySelector('#speedValue').textContent = `${this.scrollSpeed.toFixed(1)}x`; this.updateStatus(`⚡ Velocidad: ${this.scrollSpeed.toFixed(1)}x`, 'info'); } zoomImage(amount) { const canvas = this.container.querySelector('#canvas'); this.zoomLevel = Math.max(0.1, Math.min(5.0, this.zoomLevel + amount)); this.updateStatus(`🔍 Zoom: ${(this.zoomLevel * 100).toFixed(0)}%`, 'info'); if (this.imageLoaded && this.isPreviewMode) { this.drawPreviewOverlay(); } } setScrollMode(mode) { this.scrollMode = mode; this.updateStatus(`🎬 Dirección: ${mode.toUpperCase()}`, 'info'); } togglePause() { this.isPaused = !this.isPaused; const button = this.container.querySelector('#pauseBtn'); button.textContent = this.isPaused ? '▶ Reanudar' : '⏸ Pausar'; this.updateStatus(this.isPaused ? '⏸ Animación pausada' : '▶ Animación reanudada', 'info'); } togglePreviewMode(forceState) { this.isPreviewMode = (typeof forceState !== 'undefined') ? forceState : !this.isPreviewMode; const canvas = this.container.querySelector('#canvas'); const previewBtn = this.container.querySelector('#previewBtn'); if (!this.imageLoaded) { this.updateStatus('❌ Carga una imagen para usar previsualización', 'error'); this.isPreviewMode = false; return; } if (this.isPreviewMode) { previewBtn.textContent = '🔄 Salir de Previ'; canvas.style.cursor = 'move'; this.drawPreviewOverlay(); this.updateStatus('🔄 Modo Previsualización activado', 'info'); } else { previewBtn.textContent = '🖼 Previsualizar'; canvas.style.cursor = 'crosshair'; const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); this.updateStatus('🎮 Editor listo para comenzar', 'success'); } } drawPreviewOverlay() { if (!this.imageLoaded) return; const canvas = this.container.querySelector('#canvas'); const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); const viewWidth = canvas.width / this.zoomLevel; const viewHeight = canvas.height / this.zoomLevel; ctx.drawImage(this.img, this.previewOffset.x, this.previewOffset.y, viewWidth, viewHeight, 0, 0, canvas.width, canvas.height ); ctx.strokeStyle = "#4da6ff"; ctx.lineWidth = 2; ctx.strokeRect(0, 0, canvas.width, canvas.height); } // Efectos visuales toggleEffect(effect) { if (this.activeEffects.has(effect)) { this.activeEffects.delete(effect); this.updateStatus(`❌ Efecto '${effect}' desactivado`, 'info'); if (effect === 'fire') this.fireParticles = []; else if (effect === 'sparkles') this.sparkles = []; else if (effect === 'snow') this.snowFlakes = []; } else { this.activeEffects.add(effect); this.updateStatus(`🎨 Efecto '${effect}' activado`, 'success'); } } removeAllEffects() { this.activeEffects.clear(); this.snowFlakes = []; this.sparkles = []; this.fireParticles = []; this.updateStatus('💥 Todos los efectos removidos', 'info'); } drawSnow(ctx, canvas) { if (Math.random() < 0.3) { this.snowFlakes.push({ x: Math.random() * canvas.width, y: -10, size: 2 + Math.random() * 3, speed: 0.5 + Math.random() * 2 }); } ctx.fillStyle = 'rgba(255, 255, 255, 0.8)'; this.snowFlakes.forEach((flake, index) => { ctx.beginPath(); ctx.arc(flake.x, flake.y, flake.size, 0, 2 * Math.PI); ctx.fill(); flake.y += flake.speed; if (flake.y > canvas.height) this.snowFlakes.splice(index, 1); }); } drawSparkles(ctx, canvas) { if (Math.random() < 0.5) { this.sparkles.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, size: 1 + Math.random() * 2, opacity: 1, fadeSpeed: 0.02 + Math.random() * 0.03 }); } this.sparkles.forEach((sparkle, index) => { ctx.fillStyle = `rgba(255, 255, 255, ${sparkle.opacity})`; ctx.beginPath(); ctx.arc(sparkle.x, sparkle.y, sparkle.size, 0, Math.PI * 2); ctx.fill(); sparkle.opacity -= sparkle.fadeSpeed; if (sparkle.opacity <= 0) this.sparkles.splice(index, 1); }); } drawFire(ctx, canvas) { if (Math.random() < 0.5) { const size = 5 + Math.random() * 10; this.fireParticles.push({ x: Math.random() * canvas.width, y: canvas.height, size: size, vx: (Math.random() - 0.5) * 2, vy: -1 - Math.random() * 3, opacity: 1, color: `hsl(50, 100%, ${70 + Math.random() * 30}%)` }); } this.fireParticles.forEach((p, index) => { ctx.save(); ctx.globalAlpha = p.opacity; ctx.fillStyle = p.color; ctx.beginPath(); ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); ctx.fill(); ctx.restore(); p.x += p.vx; p.y += p.vy; p.opacity -= 0.02; p.size *= 0.98; if (p.opacity <= 0 || p.size < 0.5) { this.fireParticles.splice(index, 1); } }); } // Manejo de archivos handleSingleFile(file) { if (!file || !file.type.startsWith('image/')) { this.showNotification('❌ Solo se permiten archivos de imagen', 'error'); return; } const fileURL = URL.createObjectURL(file); this.container.querySelector('#imageUrl').value = fileURL; this.img.onload = () => { this.imageLoaded = true; this.updateStatus(`✅ ${file.name} cargado`, 'success'); this.showNotification(`Imagen ${file.name} cargada`, 'success'); this.togglePreviewMode(true); }; this.img.src = fileURL; } handleMultiFiles(files) { const imageFiles = files.filter(file => file.type && file.type.startsWith('image/')); if (imageFiles.length === 0) { this.showNotification('❌ No se encontraron imágenes válidas', 'error'); return; } this.updateStatus(`✅ ${imageFiles.length} imágenes cargadas`, 'success'); this.showNotification(`${imageFiles.length} imágenes cargadas`, 'success'); } handleAudioFile(file) { if (!file || !file.type || !file.type.startsWith('audio/')) { this.showNotification('❌ Solo se permiten archivos de audio válidos', 'error'); return; } // Método tradicional const audioPlayer = this.container.querySelector('#backgroundMusic'); const fileURL = URL.createObjectURL(file); audioPlayer.src = fileURL; audioPlayer.addEventListener('canplaythrough', () => { this.updateStatus(`✅ ${file.name} cargado`, 'success'); this.showNotification(`Audio ${file.name} cargado`, 'success'); }, { once: true }); // Método múltiple (Web Audio API) this.multiAudioManager.loadAudioFile(file).then(audioId => { this.showNotification(`🎵 ${file.name} agregado al mixer multiaudio`, 'info'); }).catch(error => { console.warn('Fallback al método tradicional:', error); }); } // Controles de arrastre startDrag(e) { if (!this.isPreviewMode && this.scrollMode !== 'static') return; this.isDragging = true; this.dragStartX = e.offsetX; this.dragStartY = e.offsetY; } drag(e) { if (!this.isDragging || (!this.isPreviewMode && this.scrollMode !== 'static')) return; const canvas = this.container.querySelector('#canvas'); const dx = (e.offsetX - this.dragStartX) / this.zoomLevel; const dy = (e.offsetY - this.dragStartY) / this.zoomLevel; if (this.isPreviewMode) { this.previewOffset.x -= dx; this.previewOffset.y -= dy; this.drawPreviewOverlay(); } this.dragStartX = e.offsetX; this.dragStartY = e.offsetY; } stopDrag() { this.isDragging = false; } // Grabación de pantalla async startScreenRecording() { if (this.isRecording) { this.showNotification('🛑 Ya hay una grabación en curso', 'warning'); return; } if (!this.checkBrowserCompatibility()) { this.showNotification('❌ Navegador no compatible', 'error'); return; } try { const displayStream = await navigator.mediaDevices.getDisplayMedia({ video: { cursor: "always" }, audio: true }); this.recordedChunks = []; this.mediaRecorder = new MediaRecorder(displayStream, { mimeType: 'video/webm;codecs=vp9', videoBitsPerSecond: 5000000 }); this.mediaRecorder.ondataavailable = e => { if (e.data.size > 0) { this.recordedChunks.push(e.data); } }; this.mediaRecorder.onstop = () => { this.videoBlob = new Blob(this.recordedChunks, { type: 'video/webm' }); this.container.querySelector('#downloadBtn').style.display = 'inline-block'; this.container.querySelector('#screen-recording-controls').style.display = 'none'; this.isRecording = false; this.isScreenRecording = false; displayStream.getTracks().forEach(track => track.stop()); this.showNotification('✅ Grabación de pantalla completada', 'success'); }; this.mediaRecorder.start(100); this.isRecording = true; this.isScreenRecording = true; this.container.querySelector('#screen-recording-controls').style.display = 'block'; this.recordingStartTime = Date.now(); this.recordingTimerInterval = setInterval(() => this.updateRecordingTimer(), 1000); this.showNotification('🔴 Grabando pantalla...', 'info'); } catch (error) { this.showNotification(`❌ Error en grabación: ${error.message}`, 'error'); } } stopScreenRecording() { if (this.mediaRecorder && this.isScreenRecording) { this.mediaRecorder.stop(); clearInterval(this.recordingTimerInterval); this.showNotification('⏹ Grabación detenida', 'info'); } } updateRecordingTimer() { if (!this.isScreenRecording) return; const elapsed = Math.floor((Date.now() - this.recordingStartTime) / 1000); const minutes = Math.floor(elapsed / 60).toString().padStart(2, '0'); const seconds = (elapsed % 60).toString().padStart(2, '0'); this.container.querySelector('#recording-timer').textContent = `${minutes}:${seconds}`; } // Manejo de teclado handleKeyDown(e) { if (e.target.tagName === 'INPUT' || e.target.isContentEditable) return; switch (e.key.toLowerCase()) { case ' ': e.preventDefault(); if (this.animationRunning) this.togglePause(); break; case 'r': e.preventDefault(); if (!this.isRecording) this.startAnimation(); break; case 's': e.preventDefault(); if (this.isRecording) this.stopRecording(); break; case 'p': e.preventDefault(); this.togglePreviewMode(); break; case 'escape': e.preventDefault(); if (this.isRecording) this.emergencyStop(); break; } } // Utilidades showNotification(message, type = 'info') { const container = this.container.querySelector('#toast-container'); const toast = document.createElement('div'); toast.className = `toast ${type}`; toast.textContent = message; container.appendChild(toast); setTimeout(() => { toast.style.transform = 'translateX(0)'; toast.style.opacity = '1'; }, 10); setTimeout(() => { toast.style.transform = 'translateX(100%)'; toast.style.opacity = '0'; setTimeout(() => { if (toast.parentNode) { toast.parentNode.removeChild(toast); } }, 300); }, 5000); } updateStatus(message, type = 'info') { const statusElement = this.container.querySelector('#status'); statusElement.textContent = message; let color = '#888'; switch(type) { case 'success': color = '#4CAF50'; break; case 'error': color = '#F44336'; break; case 'warning': color = '#FFC107'; break; case 'info': color = '#2196F3'; break; } statusElement.style.color = color; } toggleWidgetVisibility() { const container = this.container.querySelector('#soundcloud-widget-container'); container.classList.toggle('hidden'); } // Método para cerrar el editor close() { if (this.isRecording) { this.emergencyStop(); } this.container.style.display = 'none'; this.showNotification('Editor cerrado', 'info'); } // Método para mostrar el editor show() { this.container.style.display = 'block'; this.showNotification('Editor de Video abierto', 'success'); } } // Función para crear botón de acceso al editor function createEditorButton() { const button = document.createElement('button'); button.innerHTML = '🎥 Video Editor'; button.style.cssText = ` position: fixed; top: 20px; right: 20px; z-index: 999998; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; padding: 12px 20px; border-radius: 25px; font-weight: bold; font-size: 14px; cursor: pointer; box-shadow: 0 4px 15px rgba(0,0,0,0.3); transition: all 0.3s ease; backdrop-filter: blur(10px); `; button.addEventListener('mouseenter', () => { button.style.transform = 'translateY(-2px) scale(1.05)'; button.style.boxShadow = '0 6px 20px rgba(0,0,0,0.4)'; }); button.addEventListener('mouseleave', () => { button.style.transform = 'translateY(0) scale(1)'; button.style.boxShadow = '0 4px 15px rgba(0,0,0,0.3)'; }); button.addEventListener('click', () => { if (window.drawariaVideoEditor) { window.drawariaVideoEditor.show(); } }); document.body.appendChild(button); return button; } // Inicialización function initializeEditor() { console.log('🎬 Inicializando Drawaria Video Editor...'); try { // Crear el editor const editorContainer = createVideoEditor(); const editor = new DrawariaVideoEditor(editorContainer); // Crear botón de acceso createEditorButton(); // Hacer el editor accesible globalmente window.drawariaVideoEditor = editor; console.log('✅ Drawaria Video Editor inicializado correctamente'); setTimeout(() => { console.log('🎉 Editor completamente funcional'); console.log('📌 Usa el botón flotante en la esquina superior derecha'); console.log('⌨️ Atajos: Espacio=Pausa, R=Grabar, S=Parar, P=Preview, ESC=Emergencia'); console.log('🎵 Sistema de audio mejorado con SoundCloud y Newgrounds'); }, 1000); } catch (error) { console.error('❌ Error inicializando editor:', error); const errorNotification = document.createElement('div'); errorNotification.style.cssText = ` position: fixed; top: 20px; right: 20px; z-index: 999999; background: #f44336; color: white; padding: 15px 20px; border-radius: 8px; font-weight: bold; box-shadow: 0 4px 15px rgba(0,0,0,0.3); `; errorNotification.textContent = '❌ Error cargando Video Editor'; document.body.appendChild(errorNotification); setTimeout(() => { if (errorNotification.parentNode) { errorNotification.parentNode.removeChild(errorNotification); } }, 5000); } } // Función para verificar si estamos en drawaria.online function isDrawariaPage() { return window.location.hostname.includes('drawaria.online'); } // Función para esperar a que la página cargue function waitForPageLoad() { return new Promise((resolve) => { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', resolve); } else { resolve(); } }); } // Función principal de inicialización async function main() { try { if (!isDrawariaPage()) { console.log('🔄 Script cargado pero no está en drawaria.online'); return; } await waitForPageLoad(); setTimeout(() => { initializeEditor(); }, 2000); console.log('✅ Script de Drawaria Video Editor cargado exitosamente'); } catch (error) { console.error('❌ Error en inicialización principal:', error); } } // Función para limpiar recursos al cerrar la página function cleanup() { if (window.drawariaVideoEditor) { try { if (window.drawariaVideoEditor.isRecording) { window.drawariaVideoEditor.emergencyStop(); } console.log('🧹 Recursos del editor limpiados'); } catch (error) { console.error('Error durante limpieza:', error); } } } window.addEventListener('beforeunload', cleanup); window.addEventListener('unload', cleanup); // Agregar comando de menú de Tampermonkey if (typeof GM_registerMenuCommand !== 'undefined') { GM_registerMenuCommand('🎥 Abrir Video Editor', function() { if (window.drawariaVideoEditor) { window.drawariaVideoEditor.show(); } else { alert('El editor no está disponible. Recarga la página.'); } }); GM_registerMenuCommand('🛑 Cerrar Video Editor', function() { if (window.drawariaVideoEditor) { window.drawariaVideoEditor.close(); } }); } // Detectar cambios de página let currentUrl = window.location.href; const urlChangeObserver = new MutationObserver(() => { if (window.location.href !== currentUrl) { currentUrl = window.location.href; console.log('🔄 URL cambió, verificando si reinicializar editor...'); if (isDrawariaPage() && !window.drawariaVideoEditor) { setTimeout(() => { initializeEditor(); }, 1000); } } }); urlChangeObserver.observe(document.body, { childList: true, subtree: true }); // Agregar estilos CSS globales const globalStyles = document.createElement('style'); globalStyles.textContent = ` body.drawaria-video-editor-active { overflow: hidden !important; } @keyframes drawaria-editor-float { 0%, 100% { transform: translateY(0px); } 50% { transform: translateY(-5px); } } #drawaria-video-editor button:focus { outline: 2px solid #4da6ff !important; outline-offset: 2px; } @media (prefers-color-scheme: dark) { #drawaria-video-editor { filter: brightness(1.1); } } `; document.head.appendChild(globalStyles); // Inicializar cuando la página esté lista if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', main); } else { main(); } // Mensaje de bienvenida en consola console.log(` 🎬 ================================ 📹 DRAWARIA VIDEO EDITOR v1.0 ================================ ✅ Script cargado correctamente 🌐 Sitio: ${window.location.hostname} 📅 Fecha: ${new Date().toLocaleDateString()} 📋 CARACTERÍSTICAS MEJORADAS: • 🎥 Grabación de canvas con audio • 🖼️ Soporte para múltiples imágenes • 🎵 SoundCloud Widget API mejorado • 🎶 Newgrounds con proxies CORS • ✨ Efectos visuales avanzados • 📱 Grabación de pantalla • 🔊 Test de captura de audio • ⌨️ Controles de teclado 🎯 USO: 1. Busca el botón flotante "🎥 Video Editor" 2. Carga una imagen desde URL o archivo 3. Configura velocidad y dirección 4. Carga audio desde SoundCloud/Newgrounds 5. ¡Graba tu video con audio! 🎵 AUDIO MEJORADO: • SoundCloud: URLs completas soportadas • Newgrounds: Múltiples proxies CORS • Test de captura: Verifica compatibilidad • Multi-audio: Web Audio API integrado 📞 SOPORTE: • GitHub: Reporta issues y sugerencias • Consola: Logs detallados disponibles • Hotkeys: Espacio, R, S, P, ESC • Audio test: Botón "🔊 Probar Captura" ================================ `); // Función para mostrar información de depuración window.drawariaDebugInfo = function() { console.log('🐛 INFORMACIÓN DE DEPURACIÓN:'); console.log('📊 Estado del Editor:', window.drawariaVideoEditor ? 'Iniciado' : 'No iniciado'); console.log('🌐 URL actual:', window.location.href); console.log('📱 User Agent:', navigator.userAgent); console.log('🎥 MediaRecorder:', typeof MediaRecorder !== 'undefined' ? 'Soportado' : 'No soportado'); console.log('🖥️ Screen Capture:', navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia ? 'Soportado' : 'No soportado'); console.log('🔊 Audio Context:', typeof AudioContext !== 'undefined' || typeof webkitAudioContext !== 'undefined' ? 'Soportado' : 'No soportado'); if (window.drawariaVideoEditor) { console.log('🎬 Estado de grabación:', window.drawariaVideoEditor.isRecording); console.log('🖼️ Imagen cargada:', window.drawariaVideoEditor.imageLoaded); console.log('▶️ Modo de scroll:', window.drawariaVideoEditor.scrollMode); console.log('⚡ Velocidad:', window.drawariaVideoEditor.scrollSpeed); console.log('🎵 SoundCloud Manager:', window.drawariaVideoEditor.soundCloudManager ? 'Disponible' : 'No disponible'); console.log('🎚️ Multi Audio Manager:', window.drawariaVideoEditor.multiAudioManager ? 'Disponible' : 'No disponible'); } }; // Función para mostrar información de compatibilidad de audio window.drawariaAudioInfo = function() { console.log('🎵 INFORMACIÓN DE AUDIO:'); // Test MediaRecorder const supportedTypes = [ 'video/webm;codecs=vp9,opus', 'video/webm;codecs=vp8,opus', 'video/webm;codecs=vp9', 'video/webm;codecs=vp8' ]; console.log('📊 Codecs soportados:'); supportedTypes.forEach(type => { const supported = MediaRecorder.isTypeSupported ? MediaRecorder.isTypeSupported(type) : false; console.log(` ${supported ? '✅' : '❌'} ${type}`); }); // Test Audio Context try { const AudioContext = window.AudioContext || window.webkitAudioContext; if (AudioContext) { const testContext = new AudioContext(); console.log('✅ AudioContext disponible'); console.log('📊 Sample Rate:', testContext.sampleRate); console.log('📊 State:', testContext.state); testContext.close(); } } catch (e) { console.log('❌ AudioContext error:', e.message); } // Test HTMLMediaElement.captureStream() try { const testAudio = document.createElement('audio'); if (typeof testAudio.captureStream === 'function' || typeof testAudio.mozCaptureStream === 'function') { console.log('✅ HTMLMediaElement.captureStream() disponible'); } else { console.log('❌ HTMLMediaElement.captureStream() no disponible'); } } catch (e) { console.log('❌ Error testing captureStream:', e.message); } }; // Función para test rápido de SoundCloud window.testSoundCloud = function(url) { if (!url) { console.log('❌ Proporciona una URL de SoundCloud'); console.log('Ejemplo: testSoundCloud("https://soundcloud.com/artist/track")'); return; } if (window.drawariaVideoEditor && window.drawariaVideoEditor.soundCloudManager) { const isValid = window.drawariaVideoEditor.soundCloudManager.isSoundCloudUrl(url); console.log(isValid ? '✅ URL de SoundCloud válida' : '❌ URL de SoundCloud inválida'); if (isValid) { console.log('🎵 Puedes usar esta URL en el editor'); console.log('📋 Copia y pega en el campo "🎶 Cargar Audio"'); } } else { console.log('❌ Editor no inicializado. Abre el editor primero.'); } }; // Función para mostrar ayuda window.drawariaHelp = function() { console.log(` 🆘 AYUDA - DRAWARIA VIDEO EDITOR ================================ 📋 COMANDOS DISPONIBLES EN CONSOLA: • drawariaDebugInfo() - Información de depuración • drawariaAudioInfo() - Compatibilidad de audio • testSoundCloud("url") - Test URL SoundCloud • drawariaHelp() - Mostrar esta ayuda ⌨️ ATAJOS DE TECLADO: • ESPACIO - Pausar/Reanudar grabación • R - Iniciar grabación • S - Parar grabación • P - Toggle previsualización • ESC - Parada de emergencia 🎵 FORMATOS DE AUDIO SOPORTADOS: • SoundCloud: URLs completas de tracks • Newgrounds: audio.ngfiles.com/* • Archivos: MP3, WAV, OGG, M4A, AAC • URLs directas con headers CORS 🎥 FORMATOS DE GRABACIÓN: • WebM VP9 con Opus (preferido) • WebM VP8 con Opus (fallback) • WebM VP9 solo video • WebM VP8 solo video 🛠️ SOLUCIÓN DE PROBLEMAS: 1. Audio no se graba: Usar "🔊 Probar Captura" 2. Imagen no carga: Verificar dominio CORS 3. Grabación falla: Revisar compatibilidad navegador 4. SoundCloud no funciona: Verificar URL completa 📞 SOPORTE: • Consola del navegador (F12) para logs • GitHub issues para reportes • Documentación en código fuente ================================ `); }; // Event listener para detectar errores de audio específicamente window.addEventListener('error', (event) => { if (event.error && event.error.message) { const message = event.error.message.toLowerCase(); if (message.includes('audio') || message.includes('sound') || message.includes('media')) { console.warn('🎵 Error relacionado con audio detectado:', event.error.message); console.log('💡 Sugerencia: Ejecuta drawariaAudioInfo() para verificar compatibilidad'); } if (message.includes('cors') || message.includes('cross-origin')) { console.warn('🌐 Error CORS detectado:', event.error.message); console.log('💡 Sugerencia: Usa dominios compatibles como i.ibb.co o imgur.com para imágenes'); } } }); // Agregar script de SoundCloud Widget API si no existe function loadSoundCloudWidgetAPI() { if (typeof SC !== 'undefined' && SC.Widget) { console.log('✅ SoundCloud Widget API ya disponible'); return Promise.resolve(); } return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = 'https://w.soundcloud.com/player/api.js'; script.async = true; script.onload = () => { console.log('✅ SoundCloud Widget API cargado'); resolve(); }; script.onerror = () => { console.warn('⚠️ No se pudo cargar SoundCloud Widget API'); reject(new Error('SoundCloud API load failed')); }; document.head.appendChild(script); }); } // Función para verificar y preparar el entorno async function prepareEnvironment() { console.log('🔧 Preparando entorno...'); try { // Cargar SoundCloud Widget API await loadSoundCloudWidgetAPI(); } catch (error) { console.warn('⚠️ SoundCloud Widget API no disponible, continuando sin él'); } // Verificar permisos necesarios if (navigator.permissions) { try { const micPermission = await navigator.permissions.query({ name: 'microphone' }); console.log('🎤 Permisos de micrófono:', micPermission.state); if ('display-capture' in navigator.permissions) { const displayPermission = await navigator.permissions.query({ name: 'display-capture' }); console.log('🖥️ Permisos de pantalla:', displayPermission.state); } } catch (error) { console.log('ℹ️ No se pudieron verificar permisos, continuando...'); } } console.log('✅ Entorno preparado'); } // Función de inicialización principal mejorada async function initializeEditorWithEnvironment() { try { console.log('🚀 Inicializando Drawaria Video Editor con entorno completo...'); // Preparar entorno await prepareEnvironment(); // Inicializar editor initializeEditor(); // Mostrar comandos disponibles setTimeout(() => { console.log('🎉 ¡Editor listo!'); console.log('💡 Tip: Ejecuta drawariaHelp() para ver todos los comandos disponibles'); console.log('🔊 Tip: Usa drawariaAudioInfo() para verificar compatibilidad de audio'); }, 2000); } catch (error) { console.error('❌ Error en inicialización completa:', error); // Intentar inicialización básica como fallback initializeEditor(); } } // Detectar si el usuario tiene SoundCloud abierto en otras pestañas function detectExistingSoundCloud() { try { // Intentar detectar si hay SoundCloud en localStorage const soundCloudData = localStorage.getItem('sc_anonymous_id'); if (soundCloudData) { console.log('🎵 SoundCloud detectado en el navegador - Compatibilidad mejorada disponible'); } } catch (error) { // Ignorar errores de localStorage } } // Modificar la inicialización principal para usar la nueva función async function main() { try { if (!isDrawariaPage()) { console.log('🔄 Script cargado pero no está en drawaria.online'); return; } await waitForPageLoad(); // Detectar entorno SoundCloud existente detectExistingSoundCloud(); setTimeout(() => { // Usar la función de inicialización mejorada initializeEditorWithEnvironment(); }, 2000); console.log('✅ Script de Drawaria Video Editor cargado exitosamente'); } catch (error) { console.error('❌ Error en inicialización principal:', error); } } // Llamada principal if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', main); } else { main(); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址