Spaces:
Sleeping
Sleeping
from smolagents import CodeAgent, HfApiModel, tool | |
import datetime | |
import requests | |
import pytz | |
import yaml | |
import os | |
import pickle | |
import time | |
import gradio as gr | |
from tools.final_answer import FinalAnswerTool | |
# Herramienta para obtener la hora actual en una zona horaria | |
def get_current_time_in_timezone(timezone: str) -> str: | |
"""A tool that fetches the current local time in a specified timezone. | |
Args: | |
timezone: A string representing a valid timezone (e.g., 'America/New_York'). | |
""" | |
try: | |
# Create timezone object | |
tz = pytz.timezone(timezone) | |
# Get current time in that timezone | |
local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S") | |
return f"The current local time in {timezone} is: {local_time}" | |
except Exception as e: | |
return f"Error fetching time for timezone '{timezone}': {str(e)}" | |
# Herramienta para reconocer canciones usando AudD | |
def recognize_song(audio_path: str) -> dict: | |
"""Reconoce una canción a partir de un archivo de audio | |
Args: | |
audio_path: ruta al archivo de audio a reconocer | |
""" | |
AUDD_API_TOKEN = os.getenv("AUDD_API_TOKEN") | |
if not os.path.exists(audio_path): | |
return {"error": "El archivo de audio no existe"} | |
try: | |
with open(audio_path, 'rb') as file: | |
data = { | |
'api_token': AUDD_API_TOKEN, | |
'return': 'spotify,apple_music' | |
} | |
files = { | |
'file': file | |
} | |
response = requests.post('https://api.audd.io/', data=data, files=files) | |
if response.status_code != 200: | |
return {"error": f"Error en la API: {response.status_code}"} | |
result = response.json() | |
if result['status'] == 'error': | |
return {"error": result['error']['error_message']} | |
if not result.get('result'): | |
return {"error": "No se pudo reconocer la canción"} | |
song_info = result['result'] | |
return { | |
"🎶 Canción": song_info.get('title', 'Desconocido'), | |
"🎤 Artista": song_info.get('artist', 'Desconocido'), | |
"📀 Álbum": song_info.get('album', 'Desconocido'), | |
"🎧 Spotify": song_info.get('spotify', {}).get('external_urls', {}).get('spotify', 'No disponible'), | |
"🍏 Apple Music": song_info.get('apple_music', {}).get('url', 'No disponible') | |
} | |
except Exception as e: | |
return {"error": f"Error al procesar el audio: {str(e)}"} | |
# Herramienta para iniciar sesión en Soundeo | |
def @tool | |
def login_soundeo() -> str: | |
"""Inicia sesión en Soundeo usando las credenciales almacenadas en secrets""" | |
from selenium import webdriver | |
from selenium.webdriver.chrome.options import Options | |
from selenium.webdriver.chrome.service import Service | |
from webdriver_manager.chrome import ChromeDriverManager | |
from selenium.webdriver.common.by import By | |
from selenium.webdriver.support.ui import WebDriverWait | |
from selenium.webdriver.support import expected_conditions as EC | |
# Obtener credenciales de los secrets | |
username = os.getenv("SOUNDEO_USERNAME") | |
password = os.getenv("SOUNDEO_PASSWORD") | |
if not username or not password: | |
return "❌ No se encontraron las credenciales en los secrets" | |
# Configurar opciones de Chrome | |
chrome_options = Options() | |
chrome_options.add_argument("--headless") | |
chrome_options.add_argument("--no-sandbox") | |
chrome_options.add_argument("--disable-dev-shm-usage") | |
chrome_options.add_argument("--disable-gpu") | |
try: | |
# Inicializar el driver con WebDriverManager | |
service = Service(ChromeDriverManager().install()) | |
driver = webdriver.Chrome(service=service, options=chrome_options) | |
# Resto del código igual... | |
# Navegar a Soundeo | |
driver.get("https://soundeo.com/login") | |
time.sleep(3) # Esperar a que cargue la página | |
# Capturar screenshot para debug | |
driver.save_screenshot("pre_login.png") | |
# Completar formulario de login | |
username_field = WebDriverWait(driver, 10).until( | |
EC.presence_of_element_located((By.ID, "username")) | |
) | |
username_field.send_keys(username) | |
password_field = driver.find_element(By.ID, "password") | |
password_field.send_keys(password) | |
# Hacer clic en el botón de login | |
login_button = driver.find_element(By.XPATH, "//button[@type='submit']") | |
login_button.click() | |
# Esperar a que se complete el login | |
time.sleep(5) | |
# Capturar screenshot para verificar | |
driver.save_screenshot("post_login.png") | |
# Guardar cookies para uso futuro | |
pickle.dump(driver.get_cookies(), open("soundeo_cookies.pkl", "wb")) | |
# Verificar si el login fue exitoso | |
if "dashboard" in driver.current_url or "account" in driver.current_url: | |
result = "✅ Login exitoso en Soundeo" | |
else: | |
result = "❌ Error al iniciar sesión. Verifica tus credenciales." | |
driver.quit() | |
return result | |
except Exception as e: | |
return f"❌ Error con Selenium: {str(e)}" | |
# Herramienta para buscar y añadir una canción a la lista de descarga | |
def add_song_to_download_list(song_title: str, artist: str) -> str: | |
"""Busca una canción en Soundeo y la añade a la lista de descarga | |
Args: | |
song_title: título de la canción | |
artist: nombre del artista | |
""" | |
from selenium import webdriver | |
from selenium.webdriver.chrome.options import Options | |
from selenium.webdriver.chrome.service import Service | |
from webdriver_manager.chrome import ChromeDriverManager | |
from selenium.webdriver.common.by import By | |
from selenium.webdriver.support.ui import WebDriverWait | |
from selenium.webdriver.support import expected_conditions as EC | |
# Obtener credenciales de los secrets | |
username = os.getenv("SOUNDEO_USERNAME") | |
password = os.getenv("SOUNDEO_PASSWORD") | |
if not username or not password: | |
return "❌ No se encontraron las credenciales en los secrets" | |
# Configurar opciones de Chrome | |
chrome_options = Options() | |
chrome_options.add_argument("--headless") | |
chrome_options.add_argument("--no-sandbox") | |
chrome_options.add_argument("--disable-dev-shm-usage") | |
chrome_options.add_argument("--disable-gpu") | |
try: | |
# Inicializar el driver con WebDriverManager | |
service = Service(ChromeDriverManager().install()) | |
driver = webdriver.Chrome(service=service, options=chrome_options) | |
# Verificar si hay sesión activa | |
cookies_file = "soundeo_cookies.pkl" | |
if not os.path.exists(cookies_file): | |
driver.quit() | |
return "❌ No hay sesión activa. Primero debes iniciar sesión en Soundeo." | |
# Cargar cookies para mantener la sesión | |
driver.get("https://soundeo.com") | |
cookies = pickle.load(open(cookies_file, "rb")) | |
for cookie in cookies: | |
try: | |
driver.add_cookie(cookie) | |
except Exception: | |
# Ignorar cookies problemáticas | |
pass | |
# Refrescar para aplicar cookies | |
driver.refresh() | |
time.sleep(2) | |
# Verificar si la sesión sigue activa después de cargar cookies | |
if "login" in driver.current_url: | |
driver.save_screenshot("session_expired.png") | |
driver.quit() | |
return "❌ La sesión ha expirado. Por favor, inicia sesión nuevamente." | |
# Construir una consulta de búsqueda más precisa | |
# Usar comillas para búsqueda exacta de título y artista juntos | |
search_query = f'"{song_title}" "{artist}"' | |
search_query = search_query.replace(" ", "+") | |
# Navegar a la búsqueda con la consulta | |
search_url = f"https://soundeo.com/search?q={search_query}" | |
driver.get(search_url) | |
time.sleep(5) # Esperar a que carguen los resultados | |
# Capturar screenshot de resultados para debugging | |
driver.save_screenshot("search_results.png") | |
# Buscar si hay resultados | |
try: | |
# Verificar primero si hay mensaje de "no se encontraron resultados" | |
no_results = driver.find_elements(By.XPATH, "//div[contains(text(), 'No results found')]") | |
if no_results: | |
# Intentar con una búsqueda menos restrictiva | |
simple_query = f"{song_title} {artist}".replace(" ", "+") | |
driver.get(f"https://soundeo.com/search?q={simple_query}") | |
time.sleep(5) | |
driver.save_screenshot("broader_search_results.png") | |
# Verificar nuevamente si hay resultados | |
no_results = driver.find_elements(By.XPATH, "//div[contains(text(), 'No results found')]") | |
if no_results: | |
driver.quit() | |
return f"❌ No se encontraron resultados para '{song_title}' por '{artist}'" | |
# Encontrar todos los resultados para elegir la mejor coincidencia | |
track_items = WebDriverWait(driver, 10).until( | |
EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".track-item")) | |
) | |
if not track_items: | |
driver.quit() | |
return f"❌ No se encontraron resultados para '{song_title}' por '{artist}'" | |
# Buscar la mejor coincidencia entre los resultados | |
best_match = None | |
exact_match = False | |
for item in track_items[:5]: # Limitar a los primeros 5 resultados | |
try: | |
title_elem = item.find_element(By.CSS_SELECTOR, ".track-title") | |
artist_elem = item.find_element(By.CSS_SELECTOR, ".track-artist") | |
item_title = title_elem.text.lower() | |
item_artist = artist_elem.text.lower() | |
# Verificar si es una coincidencia exacta | |
if song_title.lower() in item_title and artist.lower() in item_artist: | |
best_match = item | |
exact_match = True | |
break | |
# Si no hay coincidencia exacta, tomar el primer resultado | |
if best_match is None: | |
best_match = item | |
except Exception: | |
continue | |
if best_match is None: | |
driver.quit() | |
return f"❌ No se pudieron procesar los resultados para '{song_title}' por '{artist}'" | |
# Hacer clic en "Add to Download List" | |
try: | |
download_button = best_match.find_element(By.CSS_SELECTOR, ".download-button") | |
download_button.click() | |
time.sleep(3) | |
driver.save_screenshot("added_to_list.png") | |
# Obtener información del track añadido | |
track_info = best_match.find_element(By.CSS_SELECTOR, ".track-title").text | |
artist_info = best_match.find_element(By.CSS_SELECTOR, ".track-artist").text | |
match_type = "exacta" if exact_match else "aproximada" | |
driver.quit() | |
return f"✅ Añadida a la lista de descarga (coincidencia {match_type}): {track_info} - {artist_info}" | |
except Exception as e: | |
driver.save_screenshot("download_button_error.png") | |
driver.quit() | |
return f"❌ Error al añadir a la lista de descarga: {str(e)}" | |
except Exception as e: | |
driver.save_screenshot("search_error.png") | |
driver.quit() | |
return f"❌ Error al buscar resultados: {str(e)}" | |
except Exception as e: | |
return f"❌ Error con Selenium: {str(e)}" | |
def view_download_list() -> str: | |
"""Muestra la lista actual de canciones en la lista de descargas de Soundeo""" | |
from selenium import webdriver | |
from selenium.webdriver.chrome.options import Options | |
from selenium.webdriver.chrome.service import Service | |
from webdriver_manager.chrome import ChromeDriverManager | |
from selenium.webdriver.common.by import By | |
from selenium.webdriver.support.ui import WebDriverWait | |
from selenium.webdriver.support import expected_conditions as EC | |
# Obtener credenciales de los secrets | |
username = os.getenv("SOUNDEO_USERNAME") | |
password = os.getenv("SOUNDEO_PASSWORD") | |
if not username or not password: | |
return "❌ No se encontraron las credenciales en los secrets" | |
# Configurar opciones de Chrome | |
chrome_options = Options() | |
chrome_options.add_argument("--headless") | |
chrome_options.add_argument("--no-sandbox") | |
chrome_options.add_argument("--disable-dev-shm-usage") | |
chrome_options.add_argument("--disable-gpu") | |
try: | |
# Inicializar el driver con WebDriverManager | |
service = Service(ChromeDriverManager().install()) | |
driver = webdriver.Chrome(service=service, options=chrome_options) | |
# Verificar si hay sesión activa | |
cookies_file = "soundeo_cookies.pkl" | |
if not os.path.exists(cookies_file): | |
driver.quit() | |
return "❌ No hay sesión activa. Primero debes iniciar sesión en Soundeo." | |
# Cargar cookies para mantener la sesión | |
driver.get("https://soundeo.com") | |
cookies = pickle.load(open(cookies_file, "rb")) | |
for cookie in cookies: | |
try: | |
driver.add_cookie(cookie) | |
except Exception: | |
# Ignorar cookies problemáticas | |
pass | |
# Refrescar para aplicar cookies | |
driver.refresh() | |
time.sleep(2) | |
# Verificar si la sesión sigue activa después de cargar cookies | |
if "login" in driver.current_url: | |
driver.save_screenshot("session_expired.png") | |
driver.quit() | |
return "❌ La sesión ha expirado. Por favor, inicia sesión nuevamente." | |
# Navegar a la página de la lista de descargas | |
driver.get("https://soundeo.com/download-list") | |
time.sleep(5) | |
# Capturar screenshot para debugging | |
driver.save_screenshot("download_list.png") | |
# Buscar las canciones en la lista | |
try: | |
track_items = driver.find_elements(By.CSS_SELECTOR, ".track-item") | |
if not track_items: | |
driver.quit() | |
return "📋 Tu lista de descargas está vacía." | |
# Crear una lista de las canciones | |
download_list = [] | |
for i, item in enumerate(track_items, 1): | |
try: | |
title = item.find_element(By.CSS_SELECTOR, ".track-title").text | |
artist = item.find_element(By.CSS_SELECTOR, ".track-artist").text | |
download_list.append(f"{i}. **{title}** - *{artist}*") | |
except Exception: | |
download_list.append(f"{i}. *[Error al obtener detalles]*") | |
driver.quit() | |
# Formatear la salida | |
result = "📋 **Lista de descargas actual:**\n\n" | |
result += "\n".join(download_list) | |
result += f"\n\n**Total: {len(download_list)} canciones**" | |
return result | |
except Exception as e: | |
driver.save_screenshot("list_error.png") | |
driver.quit() | |
return f"❌ Error al obtener la lista de descargas: {str(e)}" | |
except Exception as e: | |
return f"❌ Error con Selenium: {str(e)}" | |
def recognize_and_download(audio_path: str) -> str: | |
"""Reconoce una canción desde un archivo de audio y la añade a la lista de descargas | |
Args: | |
audio_path: ruta al archivo de audio a reconocer | |
""" | |
# Primero reconocer la canción | |
recognition_result = recognize_song(audio_path) | |
if "error" in recognition_result: | |
return f"❌ Error en el reconocimiento: {recognition_result['error']}" | |
# Obtener título y artista | |
song_title = recognition_result["🎶 Canción"] | |
artist = recognition_result["🎤 Artista"] | |
# Añadir a la lista de descargas | |
download_result = add_song_to_download_list(song_title, artist) | |
# Formatear la respuesta | |
response = f"🎵 **Canción reconocida:** {song_title}\n" | |
response += f"🎤 **Artista:** {artist}\n" | |
response += f"📀 **Álbum:** {recognition_result['📀 Álbum']}\n\n" | |
if "✅" in download_result: | |
response += f"✅ **{download_result}**\n\n" | |
else: | |
response += f"⚠️ **{download_result}**\n\n" | |
if recognition_result['🎧 Spotify'] != "No disponible": | |
response += f"🎧 [Escuchar en Spotify]({recognition_result['🎧 Spotify']})\n" | |
if recognition_result['🍏 Apple Music'] != "No disponible": | |
response += f"🍏 [Escuchar en Apple Music]({recognition_result['🍏 Apple Music']})\n" | |
return response | |
def process_audio_query(query: str) -> str: | |
"""Procesa una consulta relacionada con reconocimiento de audio y busca el archivo más reciente | |
Args: | |
query: la consulta del usuario sobre reconocimiento de audio | |
""" | |
# Buscar el archivo de audio más reciente en la carpeta de uploads | |
import os | |
import glob | |
# Normalmente Gradio guarda los archivos en una carpeta como esta | |
audio_files = glob.glob('/tmp/gradio/*') | |
if not audio_files: | |
return "No se encontraron archivos de audio recientes." | |
# Ordenar por fecha de modificación (el más reciente primero) | |
latest_audio = max(audio_files, key=os.path.getmtime) | |
# Usar la herramienta de reconocimiento con el archivo encontrado | |
result = recognize_song(latest_audio) | |
if "error" in result: | |
return f"Error al reconocer la canción: {result['error']}" | |
# Formatear la respuesta | |
response = f"🎵 **Canción reconocida:** {result['🎶 Canción']}\n" | |
response += f"🎤 **Artista:** {result['🎤 Artista']}\n" | |
response += f"📀 **Álbum:** {result['📀 Álbum']}\n" | |
if result['🎧 Spotify'] != "No disponible": | |
response += f"🎧 [Escuchar en Spotify]({result['🎧 Spotify']})\n" | |
if result['🍏 Apple Music'] != "No disponible": | |
response += f"🍏 [Escuchar en Apple Music]({result['🍏 Apple Music']})\n" | |
return response | |
def test_recognition_with_samples() -> str: | |
"""Genera un archivo de audio de prueba y lo reconoce""" | |
import numpy as np | |
import tempfile | |
import os | |
from scipy.io import wavfile | |
try: | |
# Generar un tono simple (440 Hz, nota La) | |
sample_rate = 44100 # 44.1 kHz | |
duration = 5 # 5 segundos | |
t = np.linspace(0, duration, int(sample_rate * duration), False) | |
# Crear una melodía simple con algunas frecuencias | |
note_a = np.sin(2 * np.pi * 440 * t) # La (A4) | |
note_c = np.sin(2 * np.pi * 523.25 * t) # Do (C5) | |
note_e = np.sin(2 * np.pi * 659.25 * t) # Mi (E5) | |
# Combinar en una melodía simple | |
melody = np.concatenate([ | |
note_a[:sample_rate], | |
note_c[:sample_rate], | |
note_e[:sample_rate], | |
note_c[:sample_rate], | |
note_a[:sample_rate] | |
]) | |
# Normalizar para evitar clipping | |
melody = melody * 0.3 | |
# Guardar como archivo WAV | |
fd, temp_path = tempfile.mkstemp(suffix=".wav") | |
os.close(fd) | |
wavfile.write(temp_path, sample_rate, melody.astype(np.float32)) | |
# Verificar que el archivo existe y tiene contenido | |
file_size = os.path.getsize(temp_path) | |
# Informar el resultado (en un caso real intentaríamos reconocer, | |
# pero como es un audio generado, probablemente no lo reconocería) | |
return f"✅ Archivo de audio de prueba generado correctamente, tamaño: {file_size} bytes.\n\nNota: Este es un audio sintético generado para pruebas, no una canción real." | |
except Exception as e: | |
return f"❌ Error: {str(e)}" | |
# Configuración del agente | |
final_answer = FinalAnswerTool() | |
model = HfApiModel( | |
max_tokens=2096, | |
temperature=0.5, | |
model_id='Qwen/Qwen2.5-Coder-32B-Instruct', | |
custom_role_conversions=None, | |
) | |
with open("prompts.yaml", 'r') as stream: | |
prompt_templates = yaml.safe_load(stream) | |
# REEMPLAZA ESTE BLOQUE COMPLETO | |
agent = CodeAgent( | |
model=model, | |
tools=[ | |
final_answer, | |
recognize_song, | |
login_soundeo, | |
add_song_to_download_list, | |
get_current_time_in_timezone, | |
test_recognition_with_samples, | |
view_download_list, | |
recognize_and_download | |
], | |
max_steps=8, | |
verbosity_level=1, | |
grammar=None, | |
planning_interval=None, | |
name=None, | |
description=None, | |
prompt_templates=prompt_templates | |
) | |
# Después de este bloque suele venir la configuración de la interfaz Gradio | |
# Interfaz de usuario con Gradio | |
with gr.Blocks() as demo: | |
gr.Markdown("# 🎵 Asistente Musical - Reconocimiento y Descarga") | |
with gr.Tab("Reconocimiento de Música"): | |
with gr.Row(): | |
audio_input = gr.Audio(type="filepath", label="Sube o graba un fragmento de audio") | |
with gr.Row(): | |
recognize_btn = gr.Button("🔍 Reconocer Canción") | |
recognize_download_btn = gr.Button("🔍+📥 Reconocer y Añadir a Descargas") | |
output = gr.Markdown() | |
# Función para procesar el reconocimiento | |
def process_audio(audio_path): | |
if not audio_path: | |
return "❌ Por favor sube o graba un fragmento de audio." | |
try: | |
result = recognize_song(audio_path) | |
if "error" in result: | |
return f"Error al reconocer la canción: {result['error']}" | |
# Formatear la respuesta | |
response = f"🎵 **Canción reconocida:** {result['🎶 Canción']}\n" | |
response += f"🎤 **Artista:** {result['🎤 Artista']}\n" | |
response += f"📀 **Álbum:** {result['📀 Álbum']}\n" | |
if result['🎧 Spotify'] != "No disponible": | |
response += f"🎧 [Escuchar en Spotify]({result['🎧 Spotify']})\n" | |
if result['🍏 Apple Music'] != "No disponible": | |
response += f"🍏 [Escuchar en Apple Music]({result['🍏 Apple Music']})\n" | |
return response | |
except Exception as e: | |
return f"❌ Error al procesar el audio: {str(e)}" | |
# Función para reconocer y añadir a descargas | |
def process_audio_and_download(audio_path): | |
if not audio_path: | |
return "❌ Por favor sube o graba un fragmento de audio." | |
try: | |
return recognize_and_download(audio_path) | |
except Exception as e: | |
return f"❌ Error al procesar el audio: {str(e)}" | |
recognize_btn.click( | |
fn=process_audio, | |
inputs=[audio_input], | |
outputs=output | |
) | |
recognize_download_btn.click( | |
fn=process_audio_and_download, | |
inputs=[audio_input], | |
outputs=output | |
) | |
with gr.Tab("Gestión de Soundeo"): | |
gr.Markdown("### Inicia sesión y gestiona tu lista de descargas") | |
with gr.Row(): | |
login_btn = gr.Button("🔑 Iniciar Sesión") | |
view_list_btn = gr.Button("📋 Ver Lista de Descargas") | |
with gr.Row(): | |
song_title = gr.Textbox(label="Título de la canción") | |
artist = gr.Textbox(label="Artista") | |
add_btn = gr.Button("➕ Añadir a Descargas") | |
soundeo_output = gr.Markdown() | |
def login_process(): | |
return login_soundeo() | |
def view_list_process(): | |
return view_download_list() | |
def add_to_list_process(title, artist): | |
if not title or not artist: | |
return "❌ Por favor ingresa tanto el título como el artista." | |
return add_song_to_download_list(title, artist) | |
login_btn.click( | |
fn=login_process, | |
inputs=[], | |
outputs=soundeo_output | |
) | |
view_list_btn.click( | |
fn=view_list_process, | |
inputs=[], | |
outputs=soundeo_output | |
) | |
add_btn.click( | |
fn=add_to_list_process, | |
inputs=[song_title, artist], | |
outputs=soundeo_output | |
) | |
with gr.Tab("Pruebas con Muestras"): | |
gr.Markdown("### Probar con muestras de música") | |
test_btn = gr.Button("Descargar Muestra de Prueba") | |
test_results = gr.Markdown() | |
def direct_test(): | |
# Ejecuta la función directamente en lugar de usar el agente | |
return test_recognition_with_samples() | |
test_btn.click(fn=direct_test, inputs=[], outputs=test_results) | |
demo.launch() |