Responder bajo demanda
Esta página es una traducción. El texto en inglés es el que prevalece: si ambos difieren, sigue el inglés. La traducción se ofrece por comodidad. Leer esta página en inglés
Todo lo que hay en Enviar un catálogo da por hecho un catálogo que existe. Algunas fuentes no lo tienen: lo que tienen es la capacidad de encontrar algo y luego ir a buscarlo. Un proveedor así no puede reflejarnos su catálogo, porque su catálogo es internet.
Así que responde a preguntas. Tres:
- Búsqueda (search). «¿Tienes algo para estas palabras?»
- Vista previa (preview). «Alguien quiere oír un momento de esa antes de comprometerse.»
- Descarga (fetch). «Alguien pidió esa. Ve y hazla real.»
Seguimos sin llamarte nunca. Esa es toda la premisa del envío, y nada de lo que hay aquí la cambia: las tres son trabajos que tú vienes a recoger. Si tu máquina está dormida, la fiesta simplemente no recibe respuestas, que es el mismo modo de fallo que un bridge apagado.
Solo se pregunta a los proveedores que informan canSearchRemotely, y la vista previa es una adhesión aparte encima de esa. Un bridge que no implemente nada de esto nunca recibe un trabajo y no necesita cambios.
Recoger trabajo
GET https://api.lets.dj/providers/jobs?max=4
Authorization: Bearer <sync token>
{
"jobs": [
{ "id": "j97…", "kind": "search", "term": "diem xua" },
{ "id": "j57…", "kind": "fetch", "externalId": "abc123", "position": 3 },
{ "id": "j21…", "kind": "preview", "externalId": "def456" }
],
"cancelled": ["j91…"]
}
Consúltalo más o menos una vez por segundo. Que se te devuelva un trabajo significa que es tuyo. max es cuántos vas a tomar, de 1 a 20, y cuatro si lo omites; un número mayor se recorta a 20. Un job.id es opaco: úsalo solo para informar.
Los trabajos vienen en un orden fijo, y conviene que lo respetes. Primero las descargas, la de menor position primero. Luego las búsquedas, la más antigua primero. Luego las vistas previas, la más antigua primero.
position es el lugar que ocupa esa canción en la cola para la que se pidió. Toma primero la más baja. De otro modo, tres canciones pedidas de golpe llegan en el orden en que cayeron los toques, y la sala espera la equivocada.
Una vista previa no lleva posición, y conviene tomarla la última. La siguiente canción de alguien tiene siempre prioridad sobre el curioseo de otro.
cancelled enumera los trabajos que tienes y que ya nadie espera: la canción se quitó, o la persona que la pidió se fue. Deja de trabajar en ellos. Se informan una sola vez, así que actúa cuando los veas.
Un trabajo reclamado que se queda en silencio un minuto se vuelve a repartir, dando por hecho que reiniciaste. Cada informe que envías cuenta como no estar en silencio. Termina o haz fallar tu trabajo en lugar de abandonarlo.
Informar
POST https://api.lets.dj/providers/jobs/<id>
Authorization: Bearer <sync token>
Content-Type: application/json
Mientras trabajas, con la frecuencia que sea útil y no más de una vez cada un par de segundos:
{ "state": "working", "phase": "downloading", "percent": 42 }
phase existe porque los porcentajes mienten: descargar dos flujos y luego unirlos produce una barra que retrocede a menos que puedas decir qué cambió. percent es opcional. Omítelo en lugar de inventarlo. Es un número de 0 a 100, y lo que se muestra se limita a ese intervalo.
Las fases son un conjunto ordenado, y nunca puedes retroceder en él:
starting → downloading → converting → verifying → finalizing
Salta las que no hagas. No envíes nada si prefieres no decirlo. Pero una sala a la que se ha dicho que una canción está en finalizing y luego ve downloading aprende que las palabras no significan nada, lo cual es peor que no haberlas tenido nunca, porque además ya no puede fiarse de la barra que las acompaña.
Nada de nuestro lado comprueba esto. phase se guarda como el texto que enviaste, y el orden te toca mantenerlo a ti. Lo que ve una persona no es tu palabra sino la nuestra: mientras alguien espera una vista previa, starting se convierte en «Finding…», downloading en «Loading…», retrying en «Retrying…», y converting, verifying y finalizing pasan a ser «Almost…». Una palabra fuera del vocabulario se muestra como el «Loading…» general y no como ella misma.
La trampa es el trabajo accesorio. Descargar portadas o metadatos suele ocurrir antes que el contenido y puede informar de su propio progreso; si eso se etiqueta con una fase que suena tardía, o su porcentaje se confunde con el del contenido, la secuencia retrocede exactamente de la manera que esta regla prohíbe. O guarda silencio durante eso, o llámalo starting.
Hay una palabra fuera de la secuencia: retrying, para un intento que falló y se está repitiendo. Puede aparecer en cualquier momento, y downloading puede seguirla desde cero. Es el único caso en que una barra reinicia honestamente, y nombrarlo es mejor que un porcentaje que parece caer solo.
Terminar una búsqueda significa devolver candidatos, como máximo 50 (el resto se descarta):
{
"state": "done",
"results": [
{
"externalId": "abc123",
"title": "Diễm Xưa",
"artist": "Trịnh Công Sơn",
"durationSeconds": 254,
"thumbnailUrl": "https://…"
}
]
}
Estos no son entradas del catálogo y no se guarda nada como pista hasta que alguien elija una. Un candidato sin externalId o sin title no se puede elegir ni descargar, y se descarta. Un candidato que ya está en la biblioteca como pista normal se oculta a la persona que preguntó, porque el objetivo de preguntar dos veces es encontrar lo que la biblioteca aún no tiene. externalId debe ser el mismo id que informarías para esa pista en una sincronización, o la descarga que sigue te pedirá algo que no sabrás emparejar.
Terminar una descarga son dos pasos, en este orden: sincroniza primero la pista nueva y después informa de que el trabajo está hecho. La sincronización es lo que hace real la canción; informar antes deja una entrada de la cola apuntando a una pista que aún no existe. Envía esa sincronización con final: false. Una sincronización que omite final es un reemplazo, y reemplazar con una sola pista elimina el resto de tu biblioteca.
{ "state": "done" }
Terminar una vista previa es esa misma línea, y deliberadamente no va seguida de una sincronización: una vista previa no es una entrada del catálogo y nunca debe aparecer como tal. Informar de que está hecha solo significa que ya se pueden servir los bytes descritos en Vista previa.
Y cuando no va a funcionar:
{ "state": "failed", "error": "video is unavailable in this region" }
Di lo que ha pasado de verdad. A la persona que la pidió se le explica por qué desapareció su canción, y «algo ha ido mal» no le dice nada que no hubiera deducido ya. Una descarga fallida saca la canción de la cola en todos los sitios donde esperaba.
La respuesta a cualquiera de estos es { "ok": true, "cancelled": false }. Un cancelled: true significa para: el trabajo se abandonó mientras trabajabas en él. Un trabajo que no es tuyo, o que no existe, es un 404. Un state distinto de working, done o failed es un 400.
Esperar, desde el lado de la fiesta
Una canción en la cola cuyo archivo aún no existe se mantiene como pendiente. Está en la cola, muestra su progreso y nunca se le entrega a la pantalla como una URL, así que una canción a medio descargar nunca es un rectángulo negro delante de una sala. La sincronización que acaba informando de su externalId es lo que la hace normal.
Por defecto una fiesta reproduce la primera canción que está lista y no la primera de la cola, para que una descarga nunca deje a una sala parada; quien dirige puede desactivarlo y esperar en orden estricto. Ninguna de las dos cosas es asunto tuyo más allá de responder con prontitud, pero es por lo que una descarga lenta se puede sobrellevar.
Vista previa
Seis versiones de karaoke de la misma canción son indistinguibles por el título, y una apuesta equivocada le cuesta tres minutos a una sala. Una vista previa permite a alguien oír un momento de una antes de comprometer a la fiesta con ella.
Apúntate con canPreview junto a canSearchRemotely, y sirve los bytes de la vista previa en la ruta de contenido que ya tienes:
GET <baseUrl>/media/<externalId>?t=<mediaToken>&preview=1
El mismo token, los mismos requisitos de rango y CORS, el mismo 403 con un token incorrecto. Una rama más en el manejador que ya has escrito, en lugar de un segundo servidor.
Una vista previa puede ser una versión más barata que la real. Menor resolución, un fragmento más corto, lo que sea rápido: nadie va a cantar sobre ella. Nunca la servimos a la pantalla. Llega al móvil de una persona, a petición suya, y la reproducción de la propia fiesta siempre espera a una descarga real.
Lo que hagas con ella después es asunto tuyo. Si alguien pide una canción que ya has previsualizado y tu vista previa resulta lo bastante buena como para conservarla, una descarga posterior puede terminar casi al instante. Es una optimización dentro de tu caché, no una regla: un proveedor que tira las vistas previas y vuelve a descargar como es debido es igual de correcto, y nada de aquí necesita saber cuál elegiste.
Las vistas previas son especulativas, porque son contenido con el que nadie se ha comprometido a cantar, así que merecen su propio tope en tu disco y su propia expulsión, separados de la biblioteca de la que una fiesta está tirando de verdad.
Decir lo que vas a tolerar
Una descarga gasta el disco, el ancho de banda y la CPU de alguien, y ese alguien eres tú. Envía limits junto a capabilities en cualquier sincronización, y los haremos cumplir:
"limits": {
"pendingFetchesPerRoom": 5,
"pendingFetchesPerPerson": 2,
"pendingPreviewsPerRoom": 3,
"guestRequests": true,
"fetchTimeoutSeconds": 600,
"previewTimeoutSeconds": 120
}
Todos son opcionales. guestRequests: false limita el pedir a los anfitriones, que es la respuesta cuando se presta un portátil a una sala de cuarenta personas. fetchTimeoutSeconds es cuándo renunciamos a ti y se lo decimos a quien canta. Los rechazos nombran el límite, para que la persona que lo alcanza sepa cuál es, en lugar de saber solo que no puede.
Las vistas previas tienen su propio presupuesto porque son baratas de pedir y fáciles de pedir repetidamente: una sala de curiosos pasando por los resultados de búsqueda no debería poder gastar una noche del ancho de banda de alguien. Renuncia también antes a una vista previa que a una descarga. Nadie espera dos minutos para saber si una canción es la versión correcta.
Todo lo que no se envíe recibe un valor por defecto. No hay tope de descargas ni de vistas previas pendientes, los invitados pueden pedir, una descarga se abandona a los 600 segundos y una vista previa a los 120. Un recuento o un número de segundos debe ser un número positivo, y se redondea hacia abajo; cualquier otra cosa se toma como no enviada. guestRequests solo se desactiva con un false literal.
Los límites los fijas tú porque la máquina es tuya; lo que hace una fiesta con una canción lenta lo decide quien la dirige, y eso no se negocia aquí.
Una consola propia
Si tienes una interfaz, para precargar antes de una fiesta o para lo que sea, indícala como consoleUrl en el cuerpo de la sincronización, en las mismas condiciones que baseUrl: HTTPS, y un certificado en el que un navegador confíe. Al dueño de la biblioteca, y a nadie más, se le ofrece un enlace a ella.
Protégela tú, y no con nada que tengamos nosotros. Mostramos una dirección; no queremos tu secreto, y un enlace que lleva uno es un enlace que se filtra.