Servidor MCP de la capa Emitir: agentes (silicio) y personas (carbono) consultan qué hay en antena en cada pantalla, el proof-of-play de cada circuito y cómo poner una pantalla a emitir. Y desde hoy, federación: admira.tv se coordina con circuitos externos y viceversa. Y el player condicionado: sin presencia válida, playlist; con presencia, contenido según el productor y las reglas. Zapatillas solo detecta tipología.
Para trabajar con el player sin modificar el nuestro: identidad por URL o broadcast, lectura de estado sin sesión, cola de órdenes con acuse, playlists por pantalla y precedencias. Documento completo: player-contract.md · Help.
POST https://api.admira.store/locations/cmd {"id":"<pantalla>","screen":"<pantalla>","cmd":"fit-fill"}
GET https://api.admira.store/locations/cmd/ack?id=<pantalla>&action=<action> → ack.status executed|failed|ignored
GET https://api.admira.store/signage/now?screen=<pantalla> → item, sinc, standby, device.display.fit/rotation
GET https://api.admira.store/screen/cache?screen=<pantalla> → ready[], total, downloading[{id,pct}]
GET https://brain.digitalavatar.ai/control/playlist?screen=<pantalla>-tema → playlist programada del kiosko
Órdenes: next prev first last · pause play · mute unmute vol-up vol-down · standby resume · rotation-0|90|180|270 · fit-fill|fit-editorial · goto-N · seek-S · content-NUM · content-clear · tag-X · tag- · play<num> · info-show|info-hide · hardreload. Al id del circuito llegan a todas sus pantallas.
Lo que hace solo: descarga única con adopción al arrancar; sincro predescargada; #ID nunca online hasta estar en disco; autoactualización al cambiar de pieza (≥ r60); Rellenar sin recortar; goto-N aguanta en sincro; duración editorial única (imagen/audio/interactivo) igual en antena, proof-of-play y mando. Cadencias del worker: now 5 min / 4, caché 10 / 7; throttled:"budget" = presupuesto KV agotado hasta las 00:00 UTC.
Precedencia: emparejamiento con player virtual > sincro/modo remoto del circuito > local. Android: identidad por am broadcast -n tv.admira.signage/.ConfigReceiver (el -n es obligatorio); el standby se conserva al reinstalar. Jardinets (15-09): iPad de Admin ipad-admin-mupi (vertical, principal) + Fold 8 samsung-galaxy-fold-8-mupi (horizontal) + iPhone iphone17-mupi (vertical), ambos vía SITE_EXTRA, + Tab A11, tema música.
Guía completa para agentes: Android por USB, OTA, MCP y Xtore. Incluye comandos, contratos, errores, permisos, pairing y criterios de aceptación. Help paso a paso.
{"name":"player_info","arguments":{"platform":"android","section":"all"}}
Secciones: overview, features, releases, install, config, controls, conditional, diagnostics. Sin platform se conserva macOS. Es documentación: no instala ni controla el teléfono. Las versiones del APK, canal web y configuración se verifican por separado.
Reinstalar conserva standby. App abierta y online no significan contenido reproduciéndose. Comprobar ficha local, estado fresco, avance de vídeo y salida de audio. El perfil Zapatillas detecta categorías, no sexo ni edad; un equipo físico necesita emparejamiento y relé autenticado.
Presencia continua: solo xtore-virtual-zapatillas, confirmada en dos fotogramas fuertes o tres para bicicleta de confianza baja, incluso quieta. Último fotograma y evidencia fuerte menores de 1,5 s; edad desde captura, no final de inferencia. Memoria de asociación hasta 8 s no significa visibilidad. Prioridad bici/moto/coche/persona; presencia/cajas caducan a 1,5 s; persona no tiene cola. Coche/moto/bici conservan solo su contenido 2 s más (3,5 s desde última evidencia fuerte), sin simular cajas ni renovar por observaciones antiguas. Prioridad bici → moto → coche → persona; un vehículo nuevo más prioritario puede ganar. Al caducar pasa a otra categoría vigente o neutral. Pausa/desconexión cancelan de inmediato. El respaldo del hijo revisa su TTL de 6 s cada 2 s. No cambia el bus físico descrito más abajo.
Overlay y conteo: rectángulos sobre ROI con clase/#ID y color estable por trayectoria, no identidad. Texto distribuido sin solapamiento, oculto si no cabe; cajas efímeras, borradas al caducar/pausar/ocultar/desconectar/recalibrar. Estar quieto mantiene música sin sumar pasos. Capturas de 6 s independientes, sin renovación por presencia. Solo categoría al player, sin IDs, geometría ni fotos; patinete manual sin overlay automático ni comando. Misión DCL-ea4f046ffd30919c4c705137: documentación de implementación, no verificación de cámara real ni herramientas nuevas.
H · ocultar personas: botón o tecla H con foco en el analizador, fuera de campos de texto. Transformación local, temporal y reversible del previo ROI; el iPad conserva el vídeo original con cajas según la implementación del 13 de septiembre, conservando calle y rectángulos/etiquetas frescos. Aprende fondo en regiones despejadas durante tres fotogramas estables y dilata las cajas de persona; donde no hay fondo conocido muestra trama y rótulo, no inventa detalle. Memoria volátil, reinicio en pausa/ocultación/reencuadre/desconexión y caducidad de 1,5 s sin frame fresco. El detector cuenta y decide sobre el original; H oculta el original de Capturas, pero recortes/Twins son independientes. No usa IA externa ni garantiza anonimización, identidad o evidencia de seguridad; no es grabación.
H y cola de vehículos: documentación local 0.2.11 / Help .9, misión DCL-b7723cea465ac94cb685d1de; incluidas en v.12.09.2026.r15.15:52; contrato, no estado en vivo.
La ficha se mueve dentro del visor completo del player, no dentro del rectángulo del vídeo: una pieza horizontal no reduce su altura y el giro interno del MUPI no gira la ficha. En Xtore se conserva la proyección exterior del iframe; la ficha no sale de él ni modifica la calibración.
Preset: el analizador guarda automáticamente el último encuadre de cámara, iPad y DS en este navegador/origen (solo coordenadas, dimensiones y fecha). Se recupera tras compartir una fuente con proporción compatible, sin conceder permisos; «Arrancar cámara y análisis» inicia con preset compatible, mientras «Compartir sin analizar» conserva el inicio manual. Admite superficies parciales. El operador revisa giros/zoom y remarca cuando cambie la escena.
Ficha y mini mando: doble toque o Ctrl+I abre «Contenido en antena». X o Escape con foco en la ficha la cierran; la cabecera se arrastra o se mueve con flechas al enfocarla con Tab (Mayús mueve más), con botones de foco visible y toolbar fijo al desplazar el cuerpo. Cierre, arrastre y ecualizador son comunes; Anterior/Siguiente, Sonido/Mute y Repetir pieza quedan restringidos al perfil musical exacto de Xtore, en sus presentaciones previa e integrada. Repetir reinicia audio/vídeo al terminar naturalmente y no se habilita para imágenes, sincro, directo o standby. Navegar, cambiar de pieza/contexto base-condicional, un error o neutral por TTL desarman el loop; repetir no renueva presencia ni capturas. Cerrar la ficha no lo cancela. «1 de X» describe la playlist efectiva (condicional de una pieza: «1 de 1»), no emisión confirmada; AUDIO/LIVE/MUTE ocupa su propia fila bajo el ecualizador. Solo controla la instancia y su lista actual, sin editar Flota ni hardware ni evitar el permiso de sonido. Son funciones UI documentadas en el manifest y el MCP local 0.2.11 / Help 2026-09-12.9, no herramientas MCP nuevas ni estado en vivo.
El virtual de zapatillas en Xtore tiene base musical sin necesitar vídeo en directo de la cámara. Su iframe usa xtoreMusic=1 y muted=0, con permiso de sonido sujeto al navegador. Una playlist Por defecto asociada tiene prioridad y conserva su orden; sin asociación elige los últimos cinco contenidos Pixeria con etiqueta exacta #musica, normalizada en mayúsculas, acentos y prefijo #. Tipos automáticos: audio, music, video y animation, por createdAt descendente; no imágenes, interactivos ni locuciones. Audios y vídeos musicales terminan con ended, no con un temporizador editorial. El autoplay bloqueado espera gesto, sin saltar de pieza ni atribuirlo a un códec. El motor condicional queda neutro hasta activar el analizador y confirmar presencia fresca con regla/creatividad, aunque el sujeto no se mueva. Renovar la misma pieza no la reinicia. Pausa/desconexión inmediatas o ausencia sin otra categoría vigente (persona 1,5 s; vehículos 3,5 s incluyendo cola musical de 2 s, no presencia visual) = vuelta a base desde principio; neutral sin interrupción no reinicia la pieza. La asociación sigue en Parrilla con sesión del portal; no hay una escritura MCP anónima. Contrato y límites.
La asociación se relee cada 30 s. Un fallo inicial comunica playlist-unavailable y reintenta sin asumir ausencia; una playlist asociada no reproducible no se reemplaza. playlist-empty indica esa lista no reproducible o falta de coincidencias musicales sin asociación. media-state.musicSource distingue playlist de pixeria-musica; no acredita reproducción. La selección sin filtro existe solo como helper interno y no cambia otros players.
Player que corre en un navegador, sin hardware dedicado asociado. Ficha persistente, playlist compartida; abrir, cerrar o reiniciar su instancia no crea otro equipo. Help · Flota.
screen (prefijo virtual- o xtore-virtual-), name, circuit, mode? (local por defecto o conditional). Exige clave de flota. Alta idempotente: mismos datos devuelven la ficha; un ID ocupado con datos distintos se rechaza sin sobrescribir. No abre navegador, no marca online, no modifica playlists ni reglas. Devuelve channel_url, playlist_url y control_url.device, screen (o null), revision?. Empareja un player real con un player virtual registrado: adopta su playlist, parrilla, modo y reglas, y si el virtual es condicional sigue su bus de audiencia (el analizador de Xtore publica ahí solo la categoría). null desvincula. Idempotente. Clave de flota.screen? o cursor?. Registro público, 100 fichas por página; seguir cursor hasta null. Alias GET /virtual-players. Estar registrado no acredita presencia ni emisión.{"name":"player_virtual_register","arguments":{"screen":"xtore-virtual-zapatillas","name":"Xtore · zapatillas — Player virtual","circuit":"admiranext","mode":"conditional"}}
Contrato local musical: persona → Top Gun / Take My Breath Away (1786533143983-n2y09e); coche/moto → Regreso al Futuro / The Power of Love (1786532932584-a1412h); bici → Pixeria #998, AZUL Y NEGRO - Me estoy volviendo loco (1789214248874-j6qqjo). Con persona y bici confirmadas se mantienen ambos conteos y gana bici para música. Su vídeo comienza al 50 % de la duración real en cada nueva activación/repetición, sin reseek por renovaciones. Solo este condicional del screen exacto: no cambia la base, otras pantallas ni reglas globales, ni infiere sexo/edad/identidad. Medio ausente o no reproducible: base. Presencia automática: frescura de 1,5 s y renovación cada 1000 ms. Los 6 s son respaldo del hijo desde la última orden y plazo manual desde clic, no desde playing; la carga consume parte del plazo. Botones «Reglas musicales · probar» solo sin intención de análisis ni calibración, sin pasos/capturas/histórico; se neutralizan al solicitar análisis. Son UI, no herramientas MCP. Revisión documental 2026-09-12.8, misión DCL-0c79557c38bde9f05e2ec381: nueva regla de bici incluida en v.12.09.2026.r14.15:30; no es estado en vivo.
El inicio de bici espera duración real para posicionar antes de reproducir. Si no llega en 10 s o falla el seek, se permite iniciar desde cero con aviso en consola, sin prometer el 50 % ni bloquear indefinidamente. El permiso de sonido sigue siendo necesario.
Recuperación del analizador: conserva intención ante ocultación, mute temporal, más de 3 s sin fotogramas o resize proporcional. Cada 500 ms verifica vista visible, misma fuente sin mute, nuevo fotograma, marcas válidas y ninguna inferencia pendiente. Pausa manual, desconexión, recalibración, formato incompatible o error del detector cancelan esa intención. Con gemelo XpaceOS enlazado y latidos frescos puede continuar oculto; sin enlace se suspende. No concede nuevos permisos de captura. Contrato estructurado.
La vista Xtore usa un iframe opaco, sin cámara ni captura ni credenciales. Lee catálogo y su playlist pública fija mediante un puente con lista cerrada; sus cuatro reglas musicales son locales, no las reglas globales. El catálogo es el índice público completo, con límite de 2 MiB/10 s: se filtra antes de elegir cinco y se conserva el último catálogo válido ante fallos. No aceptar URLs o IDs de hardware enviados por el iframe. No se migra ni elimina el histórico de IDs de sesión antiguos.
https://mcp-tv.admira.store/mcp
mode local|sync|conditional, audience=remote, muted).metahuman del gemelo, URL del canal (mode=conditional&audience=remote), endpoints del bus (get, post, events, stream), carriles por tipología con su asset resuelto y origen (stock|arg|placeholder|null), con_anuncio/sin_anuncio, stock (escaneados/total/truncado), briefs para el creador, contrato (ttl 2 s · neutro 6 s · fade 350 ms · debug off) y estado vivo (bus + modo + reglas). Lectura abierta.target? (circuito de segmentación, «xtanco») · screen? («xtanco-totem») · auto_assets? (bool, por defecto true: busca en el Stock el ítem cuyo externalRef empiece por admiranext:xtore:<lane>; gana sobre los args) · matrix_asset? (id del Stock para person+m; fallback placeholder Matrix) · topgun_asset? (person+f; fallback placeholder Top Gun) · persona_asset? (person+u) · coche_asset? (car) · moto_asset? (motorcycle) · bici_asset? (bicycle) — estos cuatro sin fallback: sin asset la regla se omite · neutral_assets? (array de ids; [] = carrusel del canal = playlist) · with_age? (bool: añade nino|joven|adulto|senior antes de las reglas por sexo) · mode? (local|sync|conditional, por defecto conditional). Escribe en este orden r-hombre · r-mujer · r-persona · r-coche · r-moto · r-bici · r-neutro (catch-all, kind any, minCount 0, assets []) en omnipublicity /segmentation?target=, cada una con campo kind y tag kind:<kind> (omnipublicity no conserva kind: conserva el tag y el id, y el canal lee rule.kind || tag || id), y fija el modo en /locations/mode (404 tolerado). SUSTITUYE el conjunto de reglas del target. Devuelve lo escrito, lo releído, sin_anuncio y briefs. Clave de flota.screen? · kind? (person|car|motorcycle|bicycle; omitido = person) · sex? (m|f|u; obligatorio si kind = person, para el resto se normaliza a u) · age_band? (child|youth|adult|senior|unknown) · confidence? (0..1, 0.8) · source? (≤80) · ts? (ISO; stale si es anterior al último aceptado o fuera de la ventana de 2 s ±500 ms) · idempotency_key?. Es lo mismo que el POST del bus; devuelve la decisión de carril + poll_url + stream_url. Clave de flota.screen? · target?. Devuelve bus {fresh, ttl_ms, label, decision, server_ts}, mode, rules (con el kind decodificado de rule.kind || tag), los últimos 10 events, tipologias, lanes con asset resuelto, con_anuncio/sin_anuncio, briefs, contract, get_url y stream_url. Lectura abierta. Alias HTTP: GET /player/<screen>/health (reglas solo como counts/ids/kinds).screen?. Siete pasos midiendo latencia: m → Matrix, f → Top Gun, car → Coche, motorcycle → Moto, bicycle → Bici, person+u → Persona y, tras esperar el TTL (2 s), sin label → creative playlist. Devuelve {ok, male_to_matrix, female_to_topgun, car_to_coche, motorcycle_to_moto, bicycle_to_bici, unknown_to_persona, neutral_playlist_ok, latency_ms:{…, budget:2000}, steps, get_url, stream_url}. No toca reglas ni modo; deja el bus en neutro. Clave de flota.Protocolo federacion/1, simétrico: las mismas herramientas a los dos lados. Nosotros pedimos antena en sus pantallas; ellos nos la ofrecen en las nuestras y, al aceptar, la pieza entra de verdad en la playlist de cada pantalla del canal. Lecturas abiertas; escrituras con clave de flota; el circuito externo usa su clave cx_….
Para federar tu circuito: expón offer_airtime y airtime_status por MCP, dinos endpoint y clave, y te registramos. Referencia: circuito-espejo · protocolo: federacion.md · directorio: /federacion.
Qué es. Un tótem sin cámara que cambia de anuncio según quién pasa por delante. Sin nadie → emite su playlist normal. Alguien delante → el anuncio de su sexo (hombre → «Matrix», mujer → «Top Gun», persona sin sexo → «Persona») o de su tipología (coche → «Coche», moto → «Moto», bici → «Bici»). En cuanto se va (2 s sin label) vuelve a la playlist. Caso real: tótem xtanco-totem del circuito xtanco (Xtore Valencia), misión FLT-100243 / FLT-100280.
Flujo (de la puerta a la pantalla, < 2 s):
Puerta Cam (IEU, pestaña compartida)
│ vídeo, nunca sale del navegador
▼
Analizador local admira.tv/videoanalytics/xtore/ (COCO-SSD: person · car · motorcycle · bicycle)
│ POST /audience/<screen> {kind, sex, age_band, confidence, ts, source} (clave de flota)
▼
Bus de audiencia mcp-tv.admira.store (Durable Object AudienceBus, un estado por pantalla, TTL 2 s)
│ GET /audience/<screen> cada 500 ms → {fresh, label, decision:{lane, creative}}
▼
Canal admira.tv/canal.html?audience=remote → carril Matrix | TopGun | Persona | Coche | Moto | Bici | neutral
Carriles (lo decide el bus; el canal casa la regla de la matriz con el mismo nombre):
Matrix · regla r-hombreTopGun · regla r-mujerPersona · regla r-personaCoche · regla r-cocheMoto · regla r-motoBici · regla r-bicineutral · creative playlist · regla catch-all r-neutro (minCount 0, assets []) = playlist normal; hold neutro 6 s, fade 350 msUn kind que no es person nunca casa las reglas de sexo (su sex se normaliza a u). age_band omitido = unknown (nunca se rellena adult); solo kind y sex deciden el carril. Servidor: mcp-tv v2.2.0 (retrocompatible con labels v2.1 sin kind).
Contrato del label (v3) — lo que publica el analizador (o cualquier sidecar) en el bus:
{
"kind": "person" | "car" | "motorcycle" | "bicycle", // omitido = person
"sex": "m" | "f" | "u", // obligatorio si kind = person; el resto → u
"age_band": "child" | "youth" | "adult" | "senior" | "unknown",// opcional; omitido = unknown
"confidence": 0..1, // opcional (por defecto 0.8)
"ts": "2026-09-11T18:40:12.345Z", // ISO; opcional = ahora
"source": "puerta-cam" // ≤ 80 caracteres
}
Publicar un label (clave de flota en Authorization: Bearer o X-MCP-Key; Idempotency-Key opcional, misma clave → mismo event_id, se recuerdan las últimas 50):
curl -s -X POST https://mcp-tv.admira.store/audience/xtanco-totem \
-H "Authorization: Bearer $ADMIRA_FLEET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: puerta-cam-$(date +%s%3N)" \
-d '{"kind":"person","sex":"m","age_band":"adult","confidence":0.91,"ts":"'"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)"'","source":"puerta-cam"}'
→ 200 {"ok":true,"accepted":true,"screen":"xtanco-totem","event_id":"ev_…","decision":{"lane":"Matrix","creative":"Matrix","kind":"person","sex":"m"}}
(con {"kind":"car"} → decision {"lane":"Coche","creative":"Coche","kind":"car","sex":"u"})
→ 400 {"ok":false,"accepted":false,"error":"stale"} # ts anterior al último aceptado o fuera de la ventana de 2 s (±500 ms; futuro > 500 ms también)
→ 401 {"ok":false,"accepted":false,"error":"clave de flota requerida","ayuda":"Authorization: Bearer · https://www.admiranext.com/mcp/#identidad"}
Rutas HTTP del bus y del player (CORS abierto, Cache-Control: no-store; lecturas sin clave):
{ok, screen, fresh, ttl_ms:2000, label|null, decision:{lane, creative, kind, sex, age_ms, hold_ms, since}, server_ts} (decision.kind/sex = null sin label fresco). Es lo que el canal sondea cada 500 ms.publish con el label; lane_change con from/to/creative/cause publish|expiry), más recientes primero; retención 60 s / 100.text/event-stream, CORS abierto): event: hello al conectar, event: audience con {screen, fresh, lane, creative, kind, sex, age_ms, hold_ms, label, server_ts} en cada cambio de carril o publish, keepalive cada 15 s y event: bye a los 5 min (reconectar).?circuit= opcional) ficha de embebido = tool player_embed: {screen, circuit, url_player, url_gemelo, iframe_html, aspect, postmessage:{in, out, origin_allowed}, bus:{get, post, events, stream, health}, lanes, sin_anuncio, briefs, contract}.player_conditional_status con las reglas resumidas: {ok, screen, target, release, bus:{fresh, ttl_ms, label, decision, server_ts}, mode:{id, mode}, rules:{count, ids, kinds, assigned, updatedAt, default}, events[10], contract, get_url, stream_url}.curl -s https://mcp-tv.admira.store/audience/xtanco-totem # respuesta real del 11-09-2026
{"ok":true,"screen":"xtanco-totem","fresh":false,"ttl_ms":2000,
"label":{"kind":"car","sex":"u","age_band":"adult","confidence":0.9,"ts":"2026-09-11T16:51:12.926Z","ts_ms":1789145472926,"source":"smoke-neo"},
"decision":{"lane":"neutral","creative":"playlist","kind":null,"sex":null,"age_ms":5072,"hold_ms":3152,"since":"2026-09-11T16:51:15.150Z"},
"server_ts":"2026-09-11T16:51:17.998Z"}
curl -s "https://mcp-tv.admira.store/audience/xtanco-totem/events?limit=2"
{"ok":true,"screen":"xtanco-totem","events":[
{"id":"ev_…","type":"lane_change","ts":"…","from":"Coche","to":"neutral","creative":"playlist","cause":"expiry"},
{"id":"ev_…","type":"publish","ts":"…","kind":"car","sex":"u","age_band":"adult","confidence":0.9,"source":"smoke-neo","label_ts":"…"}],"total":100}
# SSE en el navegador (sin clave)
const es = new EventSource('https://mcp-tv.admira.store/audience/xtanco-totem/stream');
es.addEventListener('audience', ev => { const d = JSON.parse(ev.data); pintar(d.lane, d.creative, d.kind, d.sex, d.fresh, d.hold_ms); });
es.addEventListener('bye', () => { es.close(); /* reabrir: cierra a los 5 min */ });
Herramientas MCP del player condicionado (argumentos tal cual el inputSchema; por defecto pantalla xtanco-totem y target xtanco):
metahuman del gemelo, URL del canal (mode=conditional&audience=remote), endpoints del bus (get, post, events, stream), carriles por tipología con su asset resuelto y origen (stock|arg|placeholder|null), con_anuncio/sin_anuncio, stock (escaneados/total/truncado), briefs para el creador, contrato (ttl 2 s · neutro 6 s · fade 350 ms · debug off) y estado vivo (bus + modo + reglas). Lectura abierta.target? (circuito de segmentación, «xtanco») · screen? («xtanco-totem») · auto_assets? (bool, por defecto true: busca en el Stock el ítem cuyo externalRef empiece por admiranext:xtore:<lane>; gana sobre los args) · matrix_asset? (id del Stock para person+m; fallback placeholder Matrix) · topgun_asset? (person+f; fallback placeholder Top Gun) · persona_asset? (person+u) · coche_asset? (car) · moto_asset? (motorcycle) · bici_asset? (bicycle) — estos cuatro sin fallback: sin asset la regla se omite · neutral_assets? (array de ids; [] = carrusel del canal = playlist) · with_age? (bool: añade nino|joven|adulto|senior antes de las reglas por sexo) · mode? (local|sync|conditional, por defecto conditional). Escribe en este orden r-hombre · r-mujer · r-persona · r-coche · r-moto · r-bici · r-neutro (catch-all, kind any, minCount 0, assets []) en omnipublicity /segmentation?target=, cada una con campo kind y tag kind:<kind> (omnipublicity no conserva kind: conserva el tag y el id, y el canal lee rule.kind || tag || id), y fija el modo en /locations/mode (404 tolerado). SUSTITUYE el conjunto de reglas del target. Devuelve lo escrito, lo releído, sin_anuncio y briefs. Clave de flota.screen? · kind? (person|car|motorcycle|bicycle; omitido = person) · sex? (m|f|u; obligatorio si kind = person, para el resto se normaliza a u) · age_band? (child|youth|adult|senior|unknown) · confidence? (0..1, 0.8) · source? (≤80) · ts? (ISO; stale si es anterior al último aceptado o fuera de la ventana de 2 s ±500 ms) · idempotency_key?. Es lo mismo que el POST del bus; devuelve la decisión de carril + poll_url + stream_url. Clave de flota.screen? · target?. Devuelve bus {fresh, ttl_ms, label, decision, server_ts}, mode, rules (con el kind decodificado de rule.kind || tag), los últimos 10 events, tipologias, lanes con asset resuelto, con_anuncio/sin_anuncio, briefs, contract, get_url y stream_url. Lectura abierta. Alias HTTP: GET /player/<screen>/health (reglas solo como counts/ids/kinds).screen?. Siete pasos midiendo latencia: m → Matrix, f → Top Gun, car → Coche, motorcycle → Moto, bicycle → Bici, person+u → Persona y, tras esperar el TTL (2 s), sin label → creative playlist. Devuelve {ok, male_to_matrix, female_to_topgun, car_to_coche, motorcycle_to_moto, bicycle_to_bici, unknown_to_persona, neutral_playlist_ok, latency_ms:{…, budget:2000}, steps, get_url, stream_url}. No toca reglas ni modo; deja el bus en neutro. Clave de flota.screen? («xtanco-totem») · circuit? («xtanco»). Devuelve url_player, url_gemelo (xpaceos.com/admira-xp?totem=condicional), iframe_html 9:16, postmessage {in, out, origin_allowed}, bus {get, post, events, stream, health}, lanes con asset resuelto o null, sin_anuncio, briefs y contract. Lectura abierta. Alias HTTP: GET /player/<screen>[?circuit=].screen · circuit · format? · cam? · clean? · mode? (local|sync|conditional) · audience? (remote = el canal lee el bus y arranca en condicional blindado) · muted?.Embeber el tótem en un panel externo (analítica, gemelo, dashboard). Un solo iframe; el canal se gobierna con la URL:
<!-- tal cual lo devuelve GET /player/xtanco-totem (iframe_html) --> <iframe id="totem" src="https://admira.tv/canal.html?clean=1&screen=xtanco-totem&circuit=xtanco&max=24&format=9%3A16&mode=conditional&audience=remote&muted=1" title="Admira.tv · xtanco-totem" style="width:100%;aspect-ratio:9/16;border:0;background:#000" allow="autoplay; fullscreen" sandbox="allow-scripts allow-same-origin" referrerpolicy="no-referrer" loading="eager"></iframe>
postMessage que SALE del canal (en cada cambio de carril, igual que media-state):
const ORIGENES = ['https://admira.tv', 'https://www.admira.tv'];
window.addEventListener('message', e => {
if (!ORIGENES.includes(e.origin) || !e.data || e.data.source !== 'admira-tv-canal') return;
if (e.data.event === 'audience') pintar(e.data.lane, e.data.kind, e.data.sex, e.data.fresh, e.data.ts);
if (e.data.event === 'media-state') console.log('media', e.data.state, e.data.asset);
// audience = {source:'admira-tv-canal', event:'audience', lane:'Matrix|TopGun|Persona|Coche|Moto|Bici|neutral',
// sex:'m|f|u', kind:'person|car|motorcycle|bicycle|none', fresh:bool, ts}
// media-state = {source:'admira-tv-canal', event:'media-state', state:'playing|paused|ended|error', asset}
});
postMessage que ENTRA al canal (simula público sin cámara ni bus). El canal solo lo acepta como objeto {source:'xpaceos-robot-cli', command} y desde los orígenes del puente: https://www.xpaceos.com, https://xpaceos.com o http://localhost:* / 127.0.0.1:* (un panel en otro dominio debe usar el bus: player_consume_label / POST). Responde {source:'admira-tv-canal', requestId, ok, message}.
const totem = document.getElementById('totem').contentWindow;
totem.postMessage({source:'xpaceos-robot-cli', requestId:'x1', command:'admiratv audiencia coche'}, 'https://admira.tv');
// admiratv audiencia m|f|u|coche|moto|bici|persona (u/off = vuelve al público real)
// admiratv sincro|condicional|local (modo de playout)
// playnext · playprevious · mute · unmute
Diagnóstico dentro del canal: window.__adtvAudienceRemote (polls, ok, fails, lane, sex, kind, last) y el CLI local /forcecam m|f|u|coche|moto|bici|persona. Con ?audience_api= y ?seg_api= el canal lee un bus o una matriz de prueba sin tocar producción.
Anuncios por tipología: del creador al carril en 3 pasos
1. Crear las piezas en el creador TikTok de AdmiraNeXT. Cada tool de lectura (player_xtore_map, player_conditional_status, player_embed, GET /player/<screen>) devuelve briefs: un deep-link por carril sin anuncio, https://www.admiranext.com/tiktok/?brief=<encodeURIComponent(JSON)> con {"campana":"Xtore · Puerta Cam","lane":"coche","tipologia":"coche","titulo":"…","mensaje":"…","externalId":"admiranext:xtore:coche","formato":"9:16","duracion":15} (uno por lane persona, coche, moto, bici). La página admiranext.com/tiktok/xtore.html los trae ya cargados (4 briefs). La pieza generada sube al Stock con la identidad externalRef = admiranext:xtore:<lane> (el creador la publica desde brief.externalId); el Stock no expone ese campo: deriva el id fijo del asset como auto- + 20 hex de sha256(externalId) → persona auto-5b9834e34116f1260f2f · coche auto-9a75882d2e3a36bce8e6 · moto auto-50312e3b227b47324a9b · bici auto-f8e5daa9669ca4adff65. Clave estable: dos clics no hacen dos piezas. Los productos del folleto se crean desde /contentcatalogue/ (deep-link ?producto=).
2. Cablear: player_conditional_set {} (con auto_assets por defecto) busca en el Stock (/stock/list?limit=200&type=video|image; no pagina: 200 más recientes por tipo) el ítem cuyo externalRef empiece por admiranext:xtore:<lane>, escribe las 7 reglas en /segmentation?target=xtanco y fija conditional. (v2.2.1, en despliegue: comprueba además la existencia de los ids fijos con GET api.admira.store/stock/asset/<id> y engancha la regla). Los carriles sin anuncio se omiten y vuelven en sin_anuncio + briefs: mientras tanto ese público ve la playlist normal (rótulo CONDICIONAL · playlist normal), nunca negro.
3. Comprobar: player_smoke (7 pasos) o player_consume_label {kind:"car"} → decision.lane = "Coche"; GET /player/xtanco-totem/health enseña rules.kinds (p. ej. r-coche:car) y el último carril. En el tótem el rótulo pasa a CONDICIONAL · Coche en < 2 s; sin label 2 s → playlist.
Placeholders de hoy (Stock, hasta los de Lucas): Matrix 1786643541589-rb1gz3 · Top Gun 1786533143983-n2y09e; el neutro ya no tiene creative fijo (es la playlist). Estado real el 11-09-2026 18:51: reglas vivas r-hombre · r-mujer · r-neutro; Persona/Coche/Moto/Bici sin anuncio aún. Guía para personas: /help/#xtore. Analizador: /videoanalytics/xtore/. Contrato del canal: CANAL.md § «Audiencia remota» y «Tipología» del repo admira-tv.
{ "mcpServers": { "admiratv": { "url": "https://mcp-tv.admira.store/mcp" } } }