Guía de integración del agente Soundsible
Esta guía es para OpenClaw, agentes de Hermes, asistentes locales y habilidades de agentes que necesitan controlar un motor de estación Soundsible en ejecución.
Soundsible no es una API de música en la nube. Es un Station Engine autoalojado, generalmente en:
http://localhost:5005
En LAN o Tailscale, reemplace localhost con la IP o el nombre de host de la estación.
En el modo de motor de escritorio, no se supone que la URL base sea :5005. Lea el JSON de preparación emitido por python3 run.py --desktop-engine/soundsible_engine.py, o consulte /api/health desde la URL de loopback conocida y utilice su base_url informado.
Reglas del agente
- Usa la API de agentes para controlar la reproducción:
/api/agent/*. - No inventes identificadores de dispositivo. Consulta
/api/devicesy elige undevice_idreal o undevice_nameexacto. - Si una respuesta incluye
warning: "Device appears offline (no active socket)", el comando se ha emitido, pero no hay ningún reproductor del navegador conectado actualmente a la sala de ese dispositivo. - Deezer solo aporta metadatos. Soundsible nunca reproduce audio de Deezer.
- Para obtener resultados de búsqueda reproducibles fuera de la biblioteca, usa la búsqueda de YouTube / YouTube Music mediante la ruta de yt-dlp del descargador.
- La cola de reproducción no es la misma que la cola de descargas.
- Usa preferentemente
/api/agent/playpara «reproducir esto ahora». Usa preferentemente/api/playback/queuepara «añadir esto a la cola del reproductor».
Autenticación
Los puntos finales del agente requieren un token de agente Soundsible.
Los tokens de agente ahora se almacenan en registros auth_tokens con alcance. La compatibilidad heredada con agent_tokens todavía existe internamente, pero las nuevas integraciones deberían tratar el modelo de token con alcance como la fuente de verdad.
Crea una ficha:
curl -X POST http://localhost:5005/api/agent/token \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <SOUNDSIBLE_ADMIN_TOKEN>' \
-d '{"name":"openclaw"}'
Si SOUNDSIBLE_ADMIN_TOKEN no está configurado, las rutas de administración solo se permiten desde redes LAN/Tailscale confiables.
La respuesta incluye el token sin formato una vez:
{
"token": "raw-token-value",
"token_id": "uuid",
"name": "openclaw",
"created_at": "2026-05-05 18:00:00"
}
Guarde el token sin procesar en el almacén secreto del agente. Soundsible almacena sólo un hash.
Opcionalmente, puedes solicitar un conjunto de alcance más limitado al crear un token:
curl -X POST http://localhost:5005/api/agent/token \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <SOUNDSIBLE_ADMIN_TOKEN>' \
-d '{"name":"openclaw","scopes":["library:read","playback:control"]}'
Utilice uno de estos encabezados en rutas de agentes protegidas:
Authorization: Bearer <agent-token>
X-Soundsible-Agent-Token: <agent-token>
Verificar un token:
curl http://localhost:5005/api/agent/verify \
-H 'Authorization: Bearer <agent-token>'
Modelo mental
Soundsible tiene tres conceptos independientes:
| Concepto | Propósito | Punto final importante |
|---|---|---|
| Biblioteca | Pistas y listas de reproducción persistentes descargadas/importadas | GET /api/library |
| Cola de reproducción | Cola en memoria consumida por el reproductor web | GET/POST /api/playback/queue |
| Cola de descarga | Trabajos en segundo plano que descargan pistas a la biblioteca | POST /api/downloader/queue |
El reproductor activo normalmente es una pestaña del navegador o PWA en /player/ o /player/desktop/. El reproductor registra un dispositivo y se une a una sala Socket.IO:
playback:{scope}:{device_id}
El alcance actual es default.
Los comandos HTTP del agente emiten eventos Socket.IO en la sala del dispositivo de destino. Si el navegador de destino no está conectado, Soundsible puede aceptar la solicitud pero el dispositivo no reaccionará.
Dispositivos
Listar dispositivos:
curl http://localhost:5005/api/devices
Respuesta de ejemplo:
{
"devices": [
{
"device_id": "dev-1775490811387-hu7wrzro8",
"device_name": "Desktop",
"device_type": "desktop",
"scope": "default",
"active_sid": "abc123",
"socket_connected": true,
"last_seen_ts": 1775490830.0
}
]
}
Cómo los agentes deben elegir un dispositivo:
- Prefiere un dispositivo con el conjunto
active_sid. - Prefiera
device_type != "agent"para la reproducción. - Si el usuario dice “Escritorio”, seleccione el dispositivo cuyo
device_namesea exactamenteDesktop, o utilice sudevice_id. - Si no hay dispositivos con
active_sid, dígale al usuario que abra Soundsible en un navegador o PWA.
Registre un dispositivo de agente, opcional pero útil para el descubrimiento:
curl -X POST http://localhost:5005/api/devices/register \
-H 'Content-Type: application/json' \
-d '{"device_id":"openclaw-agent","device_name":"OpenClaw","device_type":"agent"}'
Los reproductores del navegador también se registran a través de Socket.IO. El registro HTTP por sí solo no hace que un dispositivo sea controlable; active_sid es la señal de que está conectado un reproductor Socket.IO real.
Controlar la reproducción
Pausar un dispositivo de destino:
curl -X POST http://localhost:5005/api/agent/command \
-H 'Authorization: Bearer <agent-token>' \
-H 'Content-Type: application/json' \
-d '{"command":"pause","device_id":"Desktop"}'
Reanudar la pista actual en un dispositivo de destino:
curl -X POST http://localhost:5005/api/agent/command \
-H 'Authorization: Bearer <agent-token>' \
-H 'Content-Type: application/json' \
-d '{"command":"play","device_id":"Desktop"}'
Saltar al siguiente:
curl -X POST http://localhost:5005/api/agent/command \
-H 'Authorization: Bearer <agent-token>' \
-H 'Content-Type: application/json' \
-d '{"command":"next","device_id":"Desktop"}'
Buscar una posición absoluta en la pista actual:
curl -X POST http://localhost:5005/api/agent/command \
-H 'Authorization: Bearer <agent-token>' \
-H 'Content-Type: application/json' \
-d '{"command":"seek","position_sec":75.5,"device_id":"Desktop"}'
La búsqueda utiliza segundos absolutos desde el inicio de la pista actual. Para buscar hasta el 1:15, envíe position_sec: 75. Para saltar hacia adelante o hacia atrás, primero lea /api/playback/state, sume o reste segundos de position_sec y luego envíe el nuevo valor absoluto.
Comandos admitidos:
{"command":"pause"}
{"command":"play"}
{"command":"next"}
{"command":"seek","position_sec":75.5}
Para seek, se requiere position_sec y debe ser un número mayor o igual a 0. position_ms también se acepta para clientes basados en milisegundos. El navegador de destino fija posiciones más allá de la duración de la pista cargada hasta el final de la pista.
Si se omite device_id, Soundsible se dirige al dispositivo que no es agente registrado más recientemente, prefiriendo dispositivos con un socket activo.
Reproducir música ahora
Reproducir una pista de la biblioteca por ID:
curl -X POST http://localhost:5005/api/agent/play \
-H 'Authorization: Bearer <agent-token>' \
-H 'Content-Type: application/json' \
-d '{"track_id":"<library-track-id>","device_id":"Desktop"}'
Reproducir por consulta de búsqueda:
curl -X POST http://localhost:5005/api/agent/play \
-H 'Authorization: Bearer <agent-token>' \
-H 'Content-Type: application/json' \
-d '{"query":"Daft Punk One More Time","device_id":"Desktop"}'
Comportamiento de búsqueda:
- Soundsible busca primero en la biblioteca local.
- Si no se encuentra ninguna coincidencia de biblioteca, busca YouTube Music a través de yt-dlp.
- El reproductor objetivo recibe una carga útil de pista de biblioteca o una carga útil de vista previa.
Respuesta esperada cuando se envía a un dispositivo en vivo:
{
"status": "sent",
"device_id": "dev-...",
"track": {
"id": "track-or-video-id",
"title": "Track title",
"artist": "Artist"
}
}
Si el dispositivo está registrado pero fuera de línea:
{
"status": "sent",
"device_id": "dev-...",
"warning": "Device appears offline (no active socket)"
}
Trátelo como si no se hubiera jugado realmente.
Limitación de reproducción automática del navegador
Después de recargar el navegador, es posible que la página no tenga activación por parte del usuario. En ese estado, Chrome, Safari y Firefox pueden rechazar llamadas audibles de audio.play() que se originan en Socket.IO o comandos de agente activados por HTTP.
Soundsible maneja esto colocando la solicitud remota en el reproductor. El dispositivo de destino muestra el mensaje La reproducción remota está lista con la pista solicitada. Un toque inicia la reproducción por etapas y desbloquea la sesión de audio del navegador para comandos posteriores del agente en esa sesión de página.
Comportamiento del agente:
- Si
/api/agent/playdevuelvestatus: "sent"y el dispositivo tieneactive_sid, el comando llegó al navegador. - Si la reproducción no comienza de manera audible inmediatamente después de una nueva recarga, dígale al usuario que toque el mensaje de reproducción remota en ese dispositivo.
- Una vez que el usuario ha iniciado cualquier audio en esa sesión de página, los comandos posteriores de reproducción, pausa, reanudación y siguiente del agente deberían funcionar sin este mensaje.
Esta es una política de seguridad del navegador, no una falla de la API Soundsible. Para evitar el mensaje por completo, utilice una superficie de reproducción nativa o un motor de reproducción del lado del servidor en lugar del audio del navegador.
Biblioteca
Obtenga la biblioteca completa:
curl http://localhost:5005/api/library
Biblioteca de búsqueda:
curl 'http://localhost:5005/api/library/search?q=radiohead'
La respuesta de la biblioteca incluye pistas persistentes. Una pista normal tiene un Soundsible id estable. Utilice ese id para:
{"track_id":"<id>"}
Rutas útiles de la biblioteca:
| Método | Ruta | Finalidad |
|---|---|---|
GET | /api/library | Metadatos completos de la biblioteca |
GET | /api/library/search?q=... | Buscar en la biblioteca local |
GET | /api/library/albums?sort=&genre=&year= | Álbumes del catálogo normalizado. sort es uno de newest, alphabeticalByName, alphabeticalByArtist, byYear, byGenre, random, frequent, recent, highest; un valor desconocido devuelve un 400 |
GET | /api/library/albums/<album_id> | Un disco y sus identificadores de pista, ordenados por disco y pista |
GET | /api/library/artists | Artistas con alguna pista a su nombre, obtenidos de track_artists |
GET | /api/library/artists/<artist_id> | Un artista: las pistas en las que participa y los discos en cuyos créditos figura |
GET | /api/library/genres | Géneros presentes, con el número de canciones y álbumes |
GET | /api/library/years | Años de publicación presentes, con el número de álbumes y pistas |
POST | /api/library/scan | Iniciar un análisis asíncrono de los directorios raíz de música configurados; el cuerpo opcional {"path":"..."} debe quedar dentro de uno de ellos |
GET | /api/library/scan | Estado y contadores del análisis actual o del último |
GET | /api/library/saved-entities | Álbumes y artistas marcados de esta cuenta, con los más recientes primero: {"entities":[...]} |
PUT | /api/library/saved-entities | Guardar o quitar de forma idempotente, con el cuerpo {"saved":true,"entry":{"kind":"album","name":"Record","artist":"Band","destination":"/album/Record?artist=Band&deezer_id=123","cover":"https://..."}}; requiere library:write |
GET | /api/library/favourites | Identificadores de pistas favoritas (solo aquellas de las que tienes un archivo) |
GET | /api/library/favourites/entries | Todas las canciones guardadas, descargadas o no: {"version":2,"favourites":[{"keys":[...],"title","artist",...}]} |
POST | /api/library/favourites/toggle | Marcar o desmarcar como favorita, con el cuerpo {"track_id":"..."} o {"favourite":{"keys":["yt:<video_id>"],"title":"...","artist":"..."}} |
POST | /api/library/saved/set | Guardar o quitar varias canciones a la vez, con el cuerpo {"saved":true,"entries":[{"keys":[...],"title":"...","artist":"..."}]}, hasta un máximo de 500 entradas. Nunca invierte el estado de una canción: al guardar, omite las que ya están guardadas; al quitar, conserva los favoritos y las canciones que tienen archivo. Devuelve {"changed":N}; requiere library:write |
POST | /api/catalog/album/download | Descargar todas las canciones de un álbum de Deezer que aún no estén en la biblioteca, con el cuerpo {"deezer_id":"302127"}. Devuelve 202 con el trabajo. Si hay un trabajo abierto para el álbum, lo devuelve tal cual; si está detenido, lo reanuda; si ha terminado, crea una nueva pasada; requiere library:write |
GET | /api/catalog/album/download?deezer_id= | La última descarga de ese álbum, canción por canción (state, candidates), o null. Las coincidencias dudosas esperan en needs_review: resuélvelas con POST /api/migration/jobs/<id>/decision y reanuda con POST /api/migration/jobs/<id>/control {"action":"resume"} |
GET | /api/catalog/artist/discography?deezer_id= | Los álbumes de un artista, seguidos de sus sencillos y EP, en una única tracklist con cada grabación una sola vez (un ISRC); se excluyen los recopilatorios. También incluye releases (con sus track_ids), partial_failures (lanzamientos que no se han podido leer) y truncated (si se superan las 2000 canciones) |
POST | /api/catalog/artist/download | Descargar esa discografía: todas las canciones que falten en la biblioteca, cada una archivada en su propio lanzamiento. Se aplican las mismas reglas de trabajo que en la ruta de álbumes; requiere library:write |
GET | /api/catalog/artist/download?deezer_id= | La última descarga de ese artista, canción por canción, o null |
POST | /api/library/playlists | Crear una lista de reproducción, con el cuerpo {"name":"..."} |
POST | /api/library/playlists/<name>/tracks | Añadir una pista a una lista de reproducción, con el cuerpo {"track_id":"..."} |
DELETE | /api/library/playlists/<name>/tracks/<track_id> | Quitar una pista de una lista de reproducción |
Los marcadores de álbumes y artistas son referencias de navegación, independientes de las canciones guardadas,
los favoritos y las descargas. kind es album o artist; name y un
destination interno son obligatorios. Los campos opcionales artist y cover conservan los metadatos de presentación.
El servidor deriva las claves de identidad de album_id/artist_id y deezer_id en el
destino, y asigna id y added_at. Las referencias sin identificadores quedan
sin resolver; los nombres por sí solos nunca se fusionan con ediciones o artistas identificados. Ambos
métodos devuelven la colección completa. Usa saved: false con la misma entrada para
quitarla. El evento por usuario saved_entities_updated invalida las lecturas en caché.
La ruta y los controles de marcadores del reproductor web solo modifican el marcador.
Añadir todas las canciones a Canciones y descargar una colección son comandos separados y explícitos
del menú de opciones de la página del álbum o del artista. El mismo panel se abre desde
la foto o el nombre de la cabecera con un clic izquierdo o derecho, mediante el teclado o con una
pulsación prolongada en el móvil. Quitar un marcador nunca
elimina canciones guardadas ni descargas.
Si falla el guardado persistente, se devuelve un error sin descartar la colección anterior;
las copias de seguridad de la instancia incluyen automáticamente el archivo saved_entities.json de la cuenta.
La raíz de Biblioteca en la web (#/, también #/library) contiene accesos directos a las colecciones y
filas de álbumes y artistas guardados. Las canciones, los álbumes y los artistas se abren en
#/library?view=songs|albums|artists; las colecciones guardadas se abren en
#/?saved=albums|artists. Todas estas subentradas se pueden seleccionar en los ajustes de la barra
inferior. Seleccionar Biblioteca siempre devuelve a su raíz. El explorador de NORMAL/DJ
conserva sus secciones actuales, con una sección adicional de Guardados. En
escritorio, las subentradas de la barra lateral permiten cambiar entre canciones, álbumes y artistas;
el buscador de la biblioteca aparece debajo del título, junto al control de ordenación.
Las rutas de mutación pueden requerir autorización de administrador o acceso LAN/Tailscale confiable.
Los escaneos de carpetas requieren library:write. Un inicio exitoso devuelve HTTP 202; sondee la ruta GET hasta que state sea completed o failed. Repetir el inicio durante queued o scanning devuelve el mismo scan_id. Los análisis son aditivos: utilice la ruta de purga separada cuando los archivos fuente faltantes deban eliminarse del catálogo.
Cola de reproducción
Esta es la cola que consume el reproductor del navegador cuando el usuario o agente pregunta “siguiente”.
Cola de lectura:
curl http://localhost:5005/api/playback/queue
Agregar una pista de biblioteca:
curl -X POST http://localhost:5005/api/playback/queue \
-H 'Content-Type: application/json' \
-d '{"track_id":"<library-track-id>"}'
Agregue un elemento de vista previa YouTube:
curl -X POST http://localhost:5005/api/playback/queue \
-H 'Content-Type: application/json' \
-d '{
"preview": {
"video_id": "dQw4w9WgXcQ",
"title": "Example",
"artist": "Artist",
"duration": 213,
"thumbnail": "https://..."
}
}'
Operaciones de cola:
| Método | Camino | Propósito | |||
|---|---|---|---|---|---|
GET | /api/playback/queue | Contenido de la cola | |||
POST | /api/playback/queue | Agregar pista de biblioteca o vista previa | |||
DELETE | /api/playback/queue/<index> | Eliminar por índice de cola | |||
DELETE | /api/playback/queue/track/<track_id> | Eliminar todas las entradas de la cola con ID | |||
POST | /api/playback/queue/move | Cuerpo {"from_index":0,"to_index":1} | |||
DELETE | /api/playback/queue | Limpiar cola | |||
GET | /api/playback/next | Pop siguiente elemento | |||
POST | /api/playback/repeat | Cuerpo `{“modo”:“apagado" | "todos" | "uno" | "una vez”}` |
POST | /api/playback/shuffle | Mezclar la cola actual |
Importante: /api/playback/next muestra un elemento. No lo llames sólo para inspeccionar la cola.
Cola de descarga
Esta cola descarga audio en la biblioteca persistente. No es lo mismo que la cola de reproducción.
Utilice este punto final solo cuando el usuario quiera descargar/importar música a la biblioteca:
POST /api/downloader/queue
El cuerpo de la solicitud es siempre un objeto con una matriz items:
{
"items": [
{
"song_str": "..."
}
]
}
No envíe un objeto de elemento desnudo. No envíe un video_id desnudo. No utilice aquí objetos preview de la cola de reproducción.
Buscar YouTube / YouTube Music:
curl 'http://localhost:5005/api/downloader/youtube/search?q=Daft%20Punk%20One%20More%20Time&limit=5&source=ytmusic'
Valores válidos de source:
ytmusic
music
youtube
source=ytmusic y source=music buscan YouTube Music. source=youtube busca YouTube normal.
Eche un vistazo a una URL o ID de YouTube, sin hacer cola ni descargar:
curl 'http://localhost:5005/api/downloader/youtube/peek?id=dQw4w9WgXcQ'
Descargar formatos de elementos de cola
Hay tres formas de elementos musicales compatibles.
1. Recomendado: resultado YouTube / YouTube Music resuelto
Utilícelo cuando el agente haya buscado /api/downloader/youtube/search y haya seleccionado un resultado. Envíe una URL YouTube completa en song_str e incluya video_id.
Si el resultado de la búsqueda tiene webpage_url, úselo. Si solo tiene un id de 11 caracteres, compila:
https://www.youtube.com/watch?v=<id>
Ejemplo:
curl -X POST http://localhost:5005/api/downloader/queue \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <SOUNDSIBLE_ADMIN_TOKEN>' \
-d '{
"items": [
{
"source_type": "ytmusic_search",
"song_str": "https://www.youtube.com/watch?v=FGBhQbmPwH8",
"video_id": "FGBhQbmPwH8",
"display_title": "One More Time",
"display_artist": "Daft Punk",
"duration_sec": 320,
"thumbnail_url": "https://img.youtube.com/vi/FGBhQbmPwH8/mqdefault.jpg",
"metadata_evidence": {
"title": "One More Time",
"artist": "Daft Punk",
"album": "Discovery"
}
}
]
}'
Utilice source_type: "ytmusic_search" para obtener un resultado seleccionado de la búsqueda YouTube Music. Utilice source_type: "youtube_search" para obtener un resultado seleccionado de la búsqueda normal de YouTube. Utilice source_type: "youtube_url" para obtener una URL YouTube proporcionada por el usuario.
Importante: los tipos de fuente YouTube explícitos requieren una URL YouTube en song_str. Esto no es válido:
{
"items": [
{
"source_type": "ytmusic_search",
"song_str": "Daft Punk One More Time"
}
]
}
2. URL YouTube proporcionada por el usuario
Úselo cuando el usuario proporcione una URL YouTube directamente. source_type es opcional porque Soundsible puede inferir las URL de YouTube, pero incluirlo es más claro.
curl -X POST http://localhost:5005/api/downloader/queue \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <SOUNDSIBLE_ADMIN_TOKEN>' \
-d '{
"items": [
{
"source_type": "youtube_url",
"song_str": "https://www.youtube.com/watch?v=FGBhQbmPwH8",
"video_id": "FGBhQbmPwH8"
}
]
}'
No envíes solo la identificación del video:
{
"items": [
{
"video_id": "FGBhQbmPwH8"
}
]
}
Esto se rechaza porque el descargador necesita que song_str diga qué descargar.
3. Reserva de texto sin formato
Use esto solo cuando el agente aún no haya resuelto el resultado de YouTube y quiera que Soundsible busque durante el procesamiento. No configure source_type.
curl -X POST http://localhost:5005/api/downloader/queue \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <SOUNDSIBLE_ADMIN_TOKEN>' \
-d '{
"items": [
{
"song_str": "Daft Punk - One More Time",
"metadata_evidence": {
"title": "One More Time",
"artist": "Daft Punk"
}
}
]
}'
Este camino es menos preciso. Para los flujos de trabajo de los agentes, prefiera buscar primero, seleccionar un resultado y luego poner en cola el formato de URL resuelto.
Respuesta aceptada
ID de artículos aceptados para devoluciones exitosas en cola:
{
"status": "queued",
"ids": ["download-job-id"],
"accepted": [
{
"index": 0,
"id": "download-job-id",
"source_type": "ytmusic_search"
}
],
"rejected": []
}
Es posible un fallo parcial. Inspeccione siempre tanto accepted como rejected.
Razones comunes de rechazo:
| Razón | Significado | Arreglar |
|---|---|---|
No JSON data received | El cuerpo estaba vacío o no era JSON. | Enviar Content-Type: application/json y un objeto JSON |
Item must be an object | Una entrada items no era un objeto | Poner objetos dentro de items |
Missing source_type/song_str | No hay entrada utilizable | Incluir song_str |
Invalid or unsupported YouTube URL or missing video_id | El tipo de fuente YouTube explícito no tenía ninguna URL/identificación de video YouTube completa | Enviar URL completa en song_str y video_id |
Playlist-only or invalid YouTube URL (missing v=) | La URL no es la URL de un único vídeo. | Elige un vídeo específico |
This link type is not supported | Enlace Spotify o fuente no compatible | Resolver primero a través de YouTube Music |
Iniciar procesamiento:
curl -X POST http://localhost:5005/api/downloader/start \
-H 'Authorization: Bearer <SOUNDSIBLE_ADMIN_TOKEN>'
Verificar cola de descarga:
curl http://localhost:5005/api/downloader/queue/status
Cuando finalizan las descargas, Soundsible emite library_updated y la pista aparece en /api/library.
Reglas de decisión del agente para descargas
- Si el usuario dice “descargar este enlace YouTube”, ponga en cola
source_type: "youtube_url"consong_strcomo URL completa. - Si el usuario dice “descargar canción por artista”, primero llame a
/api/downloader/youtube/search?source=ytmusic, elija el mejor resultado y luego ponga en cola el formato de URL resuelto. - Si la búsqueda no está disponible o no es concluyente, ponga en cola el texto sin formato solo con
song_strymetadata_evidenceopcional; no agreguesource_type. - Nunca utilice ID de Deezer, URL de Spotify, objetos de vista previa de la cola de reproducción ni cargas útiles sin formato solo de
video_idpara/api/downloader/queue. - Después de hacer cola, llame a
/api/downloader/start; la cola por sí sola no comienza a procesarse.
Descubrimiento Deezer
Las rutas Deezer son solo metadatos.
Rutas de proxy permitidas:
chart
search
playlist/<numeric-id>
track/<numeric-id>
artist/<numeric-id>/top
Ejemplos:
curl 'http://localhost:5005/api/discovery/deezer/search?q=daft%20punk'
curl 'http://localhost:5005/api/discovery/deezer/chart'
curl 'http://localhost:5005/api/discovery/deezer/playlist/3155776842'
Los ID de pista Deezer no son ID de pista Soundsible reproducibles. Una fila Deezer es una sugerencia de metadatos. Para jugarlo:
- Extraiga
titleyartist.namede la respuesta Deezer. - Buscar YouTube Music:
/api/downloader/youtube/search?q=<artist title>&source=ytmusic. - Elija el mejor resultado.
- O:
- juega ahora con
/api/agent/playusando la consulta, o - agregar un elemento de vista previa a
/api/playback/queue, o - agréguelo a
/api/downloader/queuepara descargarlo en la biblioteca.
- juega ahora con
No llame a /api/agent/play con track_id: "deezer_123" o una identificación numérica sin formato Deezer.
Estado de reproducción, reanudación y transferencia
Los reproductores escriben periódicamente el estado:
PUT /api/playback/state
El estado incluye:
{
"track_id": "track-or-video-id",
"position_sec": 42.5,
"is_playing": true,
"device_id": "dev-...",
"device_name": "Desktop",
"device_type": "desktop"
}
Los reproductores también pueden publicar junto a él un objeto session: la cola, las
preferencias de reproducción y el espacio de trabajo de DJ de la canción. Esto
permite que otro dispositivo retome la sesión en lugar de solo la pista. Es una
actualización parcial: si se omite la clave, se conserva lo que haya almacenado para ese dispositivo; si se envía null,
se finaliza. Los agentes no tienen motivos para escribir este objeto, y un agente que inicie una
canción ajena a la sesión en un dispositivo debe enviar "session": null para que el dispositivo no
restaure una cola que no contenga esa pista. Se descartan las sesiones de más de 256 KB.
Estado de lectura:
curl http://localhost:5005/api/playback/state
Leer el estado de otro dispositivo, excluyendo el dispositivo actual:
curl 'http://localhost:5005/api/playback/state?exclude_device=<device-id>'
Mover la reproducción de un dispositivo a otro:
curl -X POST http://localhost:5005/api/playback/handoff \
-H 'Content-Type: application/json' \
-d '{"from_device_id":"dev-phone","to_device_id":"dev-desktop"}'
Comportamiento de transferencia:
- Lee el estado de
from_device_id. - Emite
playback_stop_requestedaplayback:default:<from_device_id>. - Escribe el estado de reproducción de destino con
to_device_id. - Emite
playback_start_requestedaplayback:default:<to_device_id>.
Si to_device_id no tiene ningún socket activo, la respuesta incluye una advertencia fuera de línea.
Comportamiento de búsqueda:
POST /api/agent/commandcon{"command":"seek","position_sec":...}lee el estado de reproducción del dispositivo de destino.- La API escribe el
position_secsolicitado en estado de reproducción. - La API emite
playback_seek_requestedaplayback:default:<device_id>. - El reproductor del navegador establece
audio.currentTimey conserva la nueva posición.
Si el destino no tiene un estado de reproducción actual, la API devuelve 404 con No playback state available for target device.
Pódcasts
El descubrimiento y la reproducción de podcasts son independientes de la búsqueda de música.
Rutas útiles:
| Método | Ruta | Finalidad |
|---|---|---|
GET | /api/discovery/podcasts/search?q=... | Buscar en el directorio de pódcasts |
GET | /api/discovery/podcasts/top?country=US&limit=25 | Pódcasts más populares |
GET | /api/podcasts/subscriptions | Canales suscritos |
POST | /api/podcasts/subscribe | Suscribirse con los datos del canal |
GET | /api/podcasts/feeds/<feed_id>/episodes | Episodios de un canal |
GET | /api/podcasts/episodes-by-url?rss_url=... | Programa de un canal (title, author, image_url) y episodios, sin suscribirse |
POST | /api/podcasts/enclosure/peek | Crear un token de transmisión para un archivo adjunto |
GET | /api/podcasts/stream/<token> | Transmitir audio del pódcast |
Los elementos de la cola de vista previa de podcasts usan enclosure_url, no video_id.
Flujo de habilidades del agente recomendado
Para “reproducir X en mi escritorio”:
- Verifique el token con
GET /api/agent/verify. - Leer dispositivos con
GET /api/devices. - Seleccione
Desktopconactive_sid; si falta, pida al usuario que abra/player/desktop/. - Llame a
POST /api/agent/playcon{"query":"X","device_id":"Desktop"}. - Si la respuesta es
warning, informe que el dispositivo está fuera de línea.
Para “agregar X a la cola”:
- Buscar biblioteca local:
GET /api/library/search?q=X. - Si existe una buena coincidencia de biblioteca,
POST /api/playback/queuecon{"track_id":"..."}. - Si no, busque YouTube Music con
/api/downloader/youtube/search. - Agregue el resultado elegido como
{"preview": {...}}.
Para “buscar a 1:15” o “saltar a 75 segundos”:
- Lea dispositivos con
GET /api/devicesy apunte a un reproductor de navegador activo. - Envíe
POST /api/agent/commandcon{"command":"seek","position_sec":75,"device_id":"Desktop"}. - Para búsqueda relativa, lea
/api/playback/state, calcule la nueva posición absoluta y luego envíeseek.
Para “descargar X”:
- Buscar YouTube Music:
GET /api/downloader/youtube/search?q=X&source=ytmusic&limit=5. - Elija el mejor resultado. Utilice
webpage_urlcomosong_str; si falta, compilahttps://www.youtube.com/watch?v=<id>. - Póngalo en cola con
POST /api/downloader/queuecomo{"items":[{"source_type":"ytmusic_search","song_str":"https://www.youtube.com/watch?v=<id>","video_id":"<id>","display_title":"...","display_artist":"..."}]}. - Comience a procesar con
POST /api/downloader/start. - Encuesta
/api/downloader/queue/status. - Actualizar la biblioteca una vez finalizada.
Para “descargar esta URL YouTube”:
- Póngalo en cola con
POST /api/downloader/queuecomo{"items":[{"source_type":"youtube_url","song_str":"<full-youtube-url>"}]}. - Comience a procesar con
POST /api/downloader/start.
Para “reproducir esta pista Deezer”:
- Trate a Deezer únicamente como metadatos.
- Busque YouTube Music para
artist + title. - Juega el partido YouTube Music o agrégalo como vista previa.
Solución de problemas
Verifique el estado del dispositivo/enchufe:
curl http://localhost:5005/api/agent/debug/socketio \
-H 'Authorization: Bearer <agent-token>'
Problemas comunes:
| Síntoma | Significado | Arreglar |
|---|---|---|
warning: Device appears offline | El dispositivo está registrado pero no tiene una unión activa a la sala Socket.IO | Abrir/recargar el reproductor en ese dispositivo |
Target device not found | El agente utilizó un ID/nombre de dispositivo desconocido | Llame a /api/devices y apunte a un dispositivo real |
El comando devuelve sent pero no se reproduce nada. | Por lo general, no se bloquea ningún socket activo o reproducción automática del navegador. | Marque active_sid; después de recargar, toque el mensaje de reproducción remota una vez |
Buscar retornos seek requires position_sec | El agente envió seek sin posición absoluta | Enviar {"command":"seek","position_sec":75} |
| La búsqueda no devuelve ningún estado de reproducción | El dispositivo de destino no ha informado de un seguimiento actual | Inicie la reproducción primero o apunte al dispositivo activo desde /api/devices |
| La identificación de Deezer falla | Los ID de Deezer son solo metadatos | Resolver por título/artista mediante YouTube Music |
/api/playback/next cambió de cola | Aparece el siguiente elemento | Utilice GET /api/playback/queue para inspeccionar |
La cola de descarga rechaza el video_id sin procesar | /api/downloader/queue requiere items[].song_str | Enviar URL completa de YouTube en song_str más video_id |
La cola de descarga rechaza ytmusic_search con texto sin formato | Los tipos de fuente YouTube explícitos requieren URL de YouTube resueltas | Busque primero, luego ponga en cola la URL del resultado seleccionado; u omita source_type para utilizar texto sin formato |
| Canción descargada no visible | El trabajo de descarga no ha finalizado o la biblioteca no está sincronizada | Marque /api/downloader/queue/status, luego /api/library/sync si es necesario |
Resumen mínimo de estilo OpenAPI
GET /api/health
GET /api/devices
POST /api/devices/register
POST /api/agent/token admin
GET /api/agent/verify agent token
POST /api/agent/play agent token
POST /api/agent/command agent token; pause/play/next/seek
GET /api/agent/debug/socketio agent token
GET /api/library
GET /api/library/search?q=
GET /api/library/albums
GET /api/library/albums/<album_id>
GET /api/library/artists
GET /api/library/artists/<artist_id>
GET /api/library/genres
GET /api/library/years
GET /api/library/favourites
GET /api/library/favourites/entries
POST /api/library/favourites/toggle
POST /api/library/playlists
POST /api/library/playlists/<name>/tracks
GET /api/playback/queue
POST /api/playback/queue
DELETE /api/playback/queue/<index>
DELETE /api/playback/queue/track/<track_id>
POST /api/playback/queue/move
DELETE /api/playback/queue
GET /api/playback/state
PUT /api/playback/state
POST /api/playback/handoff
POST /api/playback/notify-stop
GET /api/downloader/youtube/search?q=&limit=&source=
GET /api/downloader/youtube/peek?id=
POST /api/downloader/queue admin/trusted
GET /api/downloader/queue/status
POST /api/downloader/start admin/trusted
GET /api/discovery/deezer/<allowlisted-path>
GET /api/discovery/podcasts/search?q=
GET /api/discovery/podcasts/top