Skip to main content
Glama
player.js10.5 kB
import { exec } from 'child_process'; import { promisify } from 'util'; import { z } from 'zod'; import { ejecutarPeticionPlayer } from '../core/spotify.js'; const execAsync = promisify(exec); const spotifyPlayer = { nombre: 'spotifyPlayer', descripcion: `Controla reproduccion de Spotify. MANEJO DE ERRORES: - Si error "No hay dispositivo activo": 1) Usa spotifyInfo(accion="devices") para obtener lista. 2) Si hay dispositivos, usa spotifyPlayer(accion="transfer", dispositivo="ID") para activarlo y luego reintenta play. 3) Si no hay dispositivos, usa spotifyPlayer(accion="openApp"), espera que cargue, repite desde paso 1. - Si transfer falla o no hay dispositivos despues de abrir: Pide al usuario que reproduzca algo manualmente en Spotify para activar la sesion. - Si error "Premium requerido": Informa que necesita Spotify Premium. - Para reproducir: Primero busca con spotifyInfo(accion="search"), luego usa el ID con play. FLUJO RECOMENDADO cuando no reproduce: devices -> transfer(dispositivo=ID) -> play. Si falla, openApp -> esperar -> devices -> transfer -> play.`, esquema: { accion: z.enum(['play', 'pause', 'resume', 'next', 'prev', 'volume', 'shuffle', 'repeat', 'seek', 'queue', 'transfer', 'playLiked', 'openApp']) .describe('play=reproducir(uri o tipo+id), pause/resume, next/prev, volume(valor:0-100), shuffle(valor:bool), repeat(valor:track/context/off), seek(valor:ms), queue=agregar a cola, transfer=cambiar dispositivo, playLiked=reproducir Me gusta, openApp=abrir Spotify(valor:true=web)'), uri: z.string().optional().describe('URI completo de Spotify ej: spotify:track:ID (alternativa a tipo+id)'), tipo: z.enum(['track', 'album', 'artist', 'playlist']).optional().describe('Tipo de contenido para play/queue'), id: z.string().optional().describe('ID del contenido (obtener de spotifyInfo search)'), valor: z.union([z.number(), z.boolean(), z.string()]).optional().describe('Valor según acción: volume(0-100), shuffle(bool), repeat(track/context/off), seek(ms), openApp(true=forzar web)'), dispositivo: z.string().optional().describe('ID del dispositivo (obtener de spotifyInfo devices)'), enContexto: z.boolean().optional().describe('Para tracks: reproducir en contexto del álbum (default: true, false=solo la canción)'), }, ejecutar: async (args, _extra) => { const { accion, uri, tipo, id, valor, dispositivo, enContexto = true } = args; const device = dispositivo || ''; switch (accion) { case 'play': { if (!(uri || (tipo && id))) { return { content: [{ type: 'text', text: 'Error: Proporciona uri o tipo+id' }] }; } const spotifyUri = uri || `spotify:${tipo}:${id}`; let mensaje = `▶️ Reproduciendo ${tipo || 'música'}`; const res = await ejecutarPeticionPlayer(async (api) => { if (tipo === 'track' && id && enContexto) { const track = await api.tracks.get(id); if (track.album?.uri) { await api.player.startResumePlayback(device, track.album.uri, undefined, { uri: spotifyUri }); mensaje = `▶️ "${track.name}" en álbum "${track.album.name}"`; return; } } if (tipo === 'track') { await api.player.startResumePlayback(device, undefined, [spotifyUri]); } else { await api.player.startResumePlayback(device, spotifyUri); } }, 'play'); return { content: [{ type: 'text', text: res.ok ? mensaje : res.error }] }; } case 'pause': { const res = await ejecutarPeticionPlayer(async (api) => { await api.player.pausePlayback(device); }, 'pause'); return { content: [{ type: 'text', text: res.ok ? '⏸️ Pausado' : res.error }] }; } case 'resume': { const res = await ejecutarPeticionPlayer(async (api) => { await api.player.startResumePlayback(device); }, 'resume'); return { content: [{ type: 'text', text: res.ok ? '▶️ Reanudado' : res.error }] }; } case 'next': { const res = await ejecutarPeticionPlayer(async (api) => { await api.player.skipToNext(device); }, 'next'); return { content: [{ type: 'text', text: res.ok ? '⏭️ Siguiente' : res.error }] }; } case 'prev': { const res = await ejecutarPeticionPlayer(async (api) => { await api.player.skipToPrevious(device); }, 'prev'); return { content: [{ type: 'text', text: res.ok ? '⏮️ Anterior' : res.error }] }; } case 'volume': { const vol = typeof valor === 'number' ? valor : 50; const res = await ejecutarPeticionPlayer(async (api) => { await api.player.setPlaybackVolume(Math.min(100, Math.max(0, vol)), device); }, 'volume'); return { content: [{ type: 'text', text: res.ok ? `🔊 Volumen: ${vol}%` : res.error }] }; } case 'shuffle': { const activar = typeof valor === 'boolean' ? valor : true; const res = await ejecutarPeticionPlayer(async (api) => { await api.player.togglePlaybackShuffle(activar, device); }, 'shuffle'); return { content: [{ type: 'text', text: res.ok ? `🔀 Aleatorio ${activar ? 'activado' : 'desactivado'}` : res.error }] }; } case 'repeat': { const modo = typeof valor === 'string' && ['track', 'context', 'off'].includes(valor) ? valor : 'off'; const res = await ejecutarPeticionPlayer(async (api) => { await api.player.setRepeatMode(modo, device); }, 'repeat'); const modos = { track: 'canción', context: 'álbum/playlist', off: 'desactivado' }; return { content: [{ type: 'text', text: res.ok ? `🔁 Repetición: ${modos[modo]}` : res.error }] }; } case 'seek': { const posMs = typeof valor === 'number' ? valor : 0; const res = await ejecutarPeticionPlayer(async (api) => { await api.player.seekToPosition(posMs, device); }, 'seek'); const min = Math.floor(posMs / 60000); const seg = Math.floor((posMs % 60000) / 1000); return { content: [{ type: 'text', text: res.ok ? `⏩ Posición: ${min}:${seg.toString().padStart(2, '0')}` : res.error }] }; } case 'queue': { const queueUri = uri || (tipo && id ? `spotify:${tipo}:${id}` : undefined); if (!queueUri) return { content: [{ type: 'text', text: 'Error: Proporciona uri o tipo+id' }] }; const res = await ejecutarPeticionPlayer(async (api) => { await api.player.addItemToPlaybackQueue(queueUri, device); }, 'queue'); return { content: [{ type: 'text', text: res.ok ? '➕ Agregado a la cola' : res.error }] }; } case 'transfer': { if (!dispositivo) return { content: [{ type: 'text', text: 'Error: Proporciona dispositivo' }] }; const res = await ejecutarPeticionPlayer(async (api) => { await api.player.transferPlayback([dispositivo], true); }, 'transfer'); return { content: [{ type: 'text', text: res.ok ? '📱 Reproducción transferida' : res.error }] }; } case 'playLiked': { const shuffle = typeof valor === 'boolean' ? valor : false; const res = await ejecutarPeticionPlayer(async (api) => { const perfil = await api.currentUser.profile(); if (shuffle) await api.player.togglePlaybackShuffle(true, device); await api.player.startResumePlayback(device, `spotify:user:${perfil.id}:collection`); }, 'playLiked'); return { content: [{ type: 'text', text: res.ok ? `💚 Reproduciendo Me gusta${shuffle ? ' (aleatorio)' : ''}` : res.error }] }; } case 'openApp': { const plataforma = process.platform; const forzarWeb = typeof valor === 'boolean' ? valor : false; const urlWeb = 'https://open.spotify.com'; if (forzarWeb) { await abrirEnNavegador(urlWeb, plataforma); return { content: [{ type: 'text', text: '🌐 Abriendo Spotify Web... IMPORTANTE: Pide al usuario que espere a que cargue completamente y te avise. Luego usa spotifyInfo(accion="devices") para verificar que el dispositivo esté disponible antes de reproducir.' }] }; } try { if (plataforma === 'win32') await execAsync('start spotify:'); else if (plataforma === 'darwin') await execAsync('open -a Spotify'); else await execAsync('spotify &'); return { content: [{ type: 'text', text: '🎵 Abriendo Spotify Desktop... IMPORTANTE: Pide al usuario que espere a que cargue completamente y te avise. Luego usa spotifyInfo(accion="devices") para verificar que el dispositivo esté disponible antes de reproducir.' }] }; } catch { await abrirEnNavegador(urlWeb, plataforma); return { content: [{ type: 'text', text: '🌐 No se encontró Spotify Desktop, abriendo Spotify Web... IMPORTANTE: Pide al usuario que espere a que cargue completamente y te avise. Luego usa spotifyInfo(accion="devices") para verificar.' }] }; } } default: return { content: [{ type: 'text', text: '❌ Acción no válida' }] }; } }, }; async function abrirEnNavegador(url, plataforma) { if (plataforma === 'win32') await execAsync(`start "" "${url}"`); else if (plataforma === 'darwin') await execAsync(`open "${url}"`); else await execAsync(`xdg-open "${url}"`); } export const herramientasPlayer = [spotifyPlayer];

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Yonsn76/spotify-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server