Explorar documentación
Documentación de v0.33.0Ver fuente original ↗

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

  1. Usa la API de agentes para controlar la reproducción: /api/agent/*.
  2. No inventes identificadores de dispositivo. Consulta /api/devices y elige un device_id real o un device_name exacto.
  3. 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.
  4. Deezer solo aporta metadatos. Soundsible nunca reproduce audio de Deezer.
  5. 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.
  6. La cola de reproducción no es la misma que la cola de descargas.
  7. Usa preferentemente /api/agent/play para «reproducir esto ahora». Usa preferentemente /api/playback/queue para «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:

ConceptoPropósitoPunto final importante
BibliotecaPistas y listas de reproducción persistentes descargadas/importadasGET /api/library
Cola de reproducciónCola en memoria consumida por el reproductor webGET/POST /api/playback/queue
Cola de descargaTrabajos en segundo plano que descargan pistas a la bibliotecaPOST /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:

  1. Prefiere un dispositivo con el conjunto active_sid.
  2. Prefiera device_type != "agent" para la reproducción.
  3. Si el usuario dice “Escritorio”, seleccione el dispositivo cuyo device_name sea exactamente Desktop, o utilice su device_id.
  4. 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:

  1. Soundsible busca primero en la biblioteca local.
  2. Si no se encuentra ninguna coincidencia de biblioteca, busca YouTube Music a través de yt-dlp.
  3. 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:

  1. Si /api/agent/play devuelve status: "sent" y el dispositivo tiene active_sid, el comando llegó al navegador.
  2. 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.
  3. 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étodoRutaFinalidad
GET/api/libraryMetadatos 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/artistsArtistas 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/genresGéneros presentes, con el número de canciones y álbumes
GET/api/library/yearsAños de publicación presentes, con el número de álbumes y pistas
POST/api/library/scanIniciar 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/scanEstado 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-entitiesGuardar 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/favouritesIdentificadores de pistas favoritas (solo aquellas de las que tienes un archivo)
GET/api/library/favourites/entriesTodas las canciones guardadas, descargadas o no: {"version":2,"favourites":[{"keys":[...],"title","artist",...}]}
POST/api/library/favourites/toggleMarcar o desmarcar como favorita, con el cuerpo {"track_id":"..."} o {"favourite":{"keys":["yt:<video_id>"],"title":"...","artist":"..."}}
POST/api/library/saved/setGuardar 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/downloadDescargar 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/downloadDescargar 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/playlistsCrear una lista de reproducción, con el cuerpo {"name":"..."}
POST/api/library/playlists/<name>/tracksAñ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étodoCaminoPropósito
GET/api/playback/queueContenido de la cola
POST/api/playback/queueAgregar 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/moveCuerpo {"from_index":0,"to_index":1}
DELETE/api/playback/queueLimpiar cola
GET/api/playback/nextPop siguiente elemento
POST/api/playback/repeatCuerpo `{“modo”:“apagado""todos""uno""una vez”}`
POST/api/playback/shuffleMezclar 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.

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ónSignificadoArreglar
No JSON data receivedEl cuerpo estaba vacío o no era JSON.Enviar Content-Type: application/json y un objeto JSON
Item must be an objectUna entrada items no era un objetoPoner objetos dentro de items
Missing source_type/song_strNo hay entrada utilizableIncluir song_str
Invalid or unsupported YouTube URL or missing video_idEl tipo de fuente YouTube explícito no tenía ninguna URL/identificación de video YouTube completaEnviar 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 supportedEnlace Spotify o fuente no compatibleResolver 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

  1. Si el usuario dice “descargar este enlace YouTube”, ponga en cola source_type: "youtube_url" con song_str como URL completa.
  2. 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.
  3. Si la búsqueda no está disponible o no es concluyente, ponga en cola el texto sin formato solo con song_str y metadata_evidence opcional; no agregue source_type.
  4. 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_id para /api/downloader/queue.
  5. 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:

  1. Extraiga title y artist.name de la respuesta Deezer.
  2. Buscar YouTube Music: /api/downloader/youtube/search?q=<artist title>&source=ytmusic.
  3. Elija el mejor resultado.
  4. O:
    • juega ahora con /api/agent/play usando la consulta, o
    • agregar un elemento de vista previa a /api/playback/queue, o
    • agréguelo a /api/downloader/queue para descargarlo en la biblioteca.

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:

  1. Lee el estado de from_device_id.
  2. Emite playback_stop_requested a playback:default:<from_device_id>.
  3. Escribe el estado de reproducción de destino con to_device_id.
  4. Emite playback_start_requested a playback: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:

  1. POST /api/agent/command con {"command":"seek","position_sec":...} lee el estado de reproducción del dispositivo de destino.
  2. La API escribe el position_sec solicitado en estado de reproducción.
  3. La API emite playback_seek_requested a playback:default:<device_id>.
  4. El reproductor del navegador establece audio.currentTime y 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étodoRutaFinalidad
GET/api/discovery/podcasts/search?q=...Buscar en el directorio de pódcasts
GET/api/discovery/podcasts/top?country=US&limit=25Pódcasts más populares
GET/api/podcasts/subscriptionsCanales suscritos
POST/api/podcasts/subscribeSuscribirse con los datos del canal
GET/api/podcasts/feeds/<feed_id>/episodesEpisodios 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/peekCrear 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.

Para “reproducir X en mi escritorio”:

  1. Verifique el token con GET /api/agent/verify.
  2. Leer dispositivos con GET /api/devices.
  3. Seleccione Desktop con active_sid; si falta, pida al usuario que abra /player/desktop/.
  4. Llame a POST /api/agent/play con {"query":"X","device_id":"Desktop"}.
  5. Si la respuesta es warning, informe que el dispositivo está fuera de línea.

Para “agregar X a la cola”:

  1. Buscar biblioteca local: GET /api/library/search?q=X.
  2. Si existe una buena coincidencia de biblioteca, POST /api/playback/queue con {"track_id":"..."}.
  3. Si no, busque YouTube Music con /api/downloader/youtube/search.
  4. Agregue el resultado elegido como {"preview": {...}}.

Para “buscar a 1:15” o “saltar a 75 segundos”:

  1. Lea dispositivos con GET /api/devices y apunte a un reproductor de navegador activo.
  2. Envíe POST /api/agent/command con {"command":"seek","position_sec":75,"device_id":"Desktop"}.
  3. Para búsqueda relativa, lea /api/playback/state, calcule la nueva posición absoluta y luego envíe seek.

Para “descargar X”:

  1. Buscar YouTube Music: GET /api/downloader/youtube/search?q=X&source=ytmusic&limit=5.
  2. Elija el mejor resultado. Utilice webpage_url como song_str; si falta, compila https://www.youtube.com/watch?v=<id>.
  3. Póngalo en cola con POST /api/downloader/queue como {"items":[{"source_type":"ytmusic_search","song_str":"https://www.youtube.com/watch?v=<id>","video_id":"<id>","display_title":"...","display_artist":"..."}]}.
  4. Comience a procesar con POST /api/downloader/start.
  5. Encuesta /api/downloader/queue/status.
  6. Actualizar la biblioteca una vez finalizada.

Para “descargar esta URL YouTube”:

  1. Póngalo en cola con POST /api/downloader/queue como {"items":[{"source_type":"youtube_url","song_str":"<full-youtube-url>"}]}.
  2. Comience a procesar con POST /api/downloader/start.

Para “reproducir esta pista Deezer”:

  1. Trate a Deezer únicamente como metadatos.
  2. Busque YouTube Music para artist + title.
  3. 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íntomaSignificadoArreglar
warning: Device appears offlineEl dispositivo está registrado pero no tiene una unión activa a la sala Socket.IOAbrir/recargar el reproductor en ese dispositivo
Target device not foundEl agente utilizó un ID/nombre de dispositivo desconocidoLlame 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_secEl agente envió seek sin posición absolutaEnviar {"command":"seek","position_sec":75}
La búsqueda no devuelve ningún estado de reproducciónEl dispositivo de destino no ha informado de un seguimiento actualInicie la reproducción primero o apunte al dispositivo activo desde /api/devices
La identificación de Deezer fallaLos ID de Deezer son solo metadatosResolver por título/artista mediante YouTube Music
/api/playback/next cambió de colaAparece el siguiente elementoUtilice GET /api/playback/queue para inspeccionar
La cola de descarga rechaza el video_id sin procesar/api/downloader/queue requiere items[].song_strEnviar URL completa de YouTube en song_str más video_id
La cola de descarga rechaza ytmusic_search con texto sin formatoLos tipos de fuente YouTube explícitos requieren URL de YouTube resueltasBusque primero, luego ponga en cola la URL del resultado seleccionado; u omita source_type para utilizar texto sin formato
Canción descargada no visibleEl trabajo de descarga no ha finalizado o la biblioteca no está sincronizadaMarque /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

Buscar documentación