Agente · dev-signage
Propósito
Desarrollo y mantenimiento del módulo Digital Signage SaaS de CreaRack-Pro.
Cubre la app signage/ (CMS), los servicios de contenido, playlists, schedules, deployment y el portal de clientes.
Estructura de archivos
signage/
├── models.py # 7 modelos core
├── models_portal.py # ClientShareLink + PortalChangeLog
├── tasks.py # 5 Huey tasks
├── views.py # Client portal (public, token-based)
├── urls.py # /client/<token> route
├── api/
│ ├── __init__.py # Router assembly (10 sub-routers)
│ ├── schemas.py # Pydantic schemas
│ ├── content.py # 11 endpoints media CRUD
│ ├── players.py # 6 endpoints player CRUD
│ ├── playlists.py # 7 endpoints playlist CRUD
│ ├── schedules.py # 6 endpoints schedule CRUD
│ ├── deployments.py # 6 endpoints deployment
│ ├── display_control.py # 6 endpoints display control
│ ├── proof_of_play.py # 8 endpoints proof-of-play
│ ├── share_links.py # 6 endpoints link management
│ ├── portal.py # 12 endpoints client portal (public)
│ └── approvals.py # 4 endpoints approval workflow
├── services/
│ ├── file_storage.py # Upload, validación, paths
│ ├── thumbnail.py # Pillow + ffmpeg thumbnails
│ ├── transcode.py # ffmpeg H.264/H.265 pipeline
│ ├── deployment.py # Push content via VAL
│ ├── svg_composer.py # Genera project.svg dinámico
│ ├── adapters/
│ │ ├── base.py # Abstract SignageDeviceAdapter
│ │ ├── registry.py # Factory: resolve by slug
│ │ ├── spinetix.py # JSON-RPC + WebDAV
│ │ ├── brightsign.py # BSN.cloud REST
│ │ ├── samsung.py # MagicINFO API
│ │ ├── lg.py # SuperSign API
│ │ └── generic_snmp.py # Fallback SNMP-only
│ ├── scheduling/
│ │ ├── engine.py # Evaluate rules → active playlist
│ │ └── smil_generator.py # JSON → SMIL XML
│ ├── display_control/
│ │ ├── samsung_mdc.py # TCP:1515 binary
│ │ ├── lg_protocol.py # TCP:9761 ASCII
│ │ ├── pjlink.py # TCP:4352 standard
│ │ └── controller.py # Unified dispatcher
│ └── proof_of_play/
│ ├── collector.py # Fetch logs via VAL
│ ├── screenshot.py # Capture + cleanup
│ └── reporter.py # JSON/HTML/CSV reports
└── migrations/
├── 0001_initial.py
├── 0002_seed_adapters.py # 12 vendor adapters seeded
├── 0003_portal_share_links.py
├── 0004_portal_approval_fields.py
├── 0006_signage_operations.py # SignageOperation (rollback)
└── 0008_client_projects.py # ClientProject (multi-client)
Modelos
| Modelo | Scope | Descripción |
|---|---|---|
SignageVendorAdapter | Global | Config por vendor (12 seeded) |
SignagePlayer | Org | Player físico vinculado a DeviceProfile |
MediaAsset | Org | Archivo multimedia (imagen/vídeo/HTML5) |
Playlist | Org | Colección ordenada de assets (is_auto para auto-generadas) |
Schedule | Org | Reglas temporales → playlists |
PlaybackLog | Org | Registro de reproducción (alto volumen) |
ContentDeployment | Org | Tracking de despliegues a players |
ClientShareLink | Org | Link compartible UUID para clientes externos |
PortalChangeLog | Org | Auditoría + cola de aprobación del portal |
SignageOperation | Org | Historial para rollback (últimas 50 por org) |
ClientProject | Org | Proyecto multi-cliente con scope aislado |
Huey Tasks
| Task | Frecuencia | Función |
|---|---|---|
process_media_asset | On upload | Thumbnail + metadata + transcode |
run_deployment | On demand | Push content via VAL |
collect_playback_logs | Cada 15 min | Recolectar logs de players |
capture_screenshots | Cada 30 min | Capturar screenshots |
cleanup_old_data | Diario 3AM | Screenshots >30d + logs >90d + auto-playlists >24h |
Flujo de publicación (Publish)
1. Usuario: Publish desde Content/Playlist tab
2. Frontend: SignageModal.selectDevices() → selección con groups + checkboxes
3. Frontend: resolveSignageCredentials() → sessionStorage → Credential Store API → modal prompt
4. SaaS: POST /signage/publish-setup → crea SignagePlayer + bind playlist/schedule
→ devuelve publish_url (SIN passwords)
5. Agent: POST /signage/configure
a. GET / → sesión + CSRF
b. POST /user/login (header X-Requested-With: XMLHttpRequest — obligatorio)
c. POST /controlcenter/main/content → guarda Scheduled Download URL
d. POST /controlcenter/operation/httpPublish → descarga inmediata
6. SpinetiX: GET spx-listing.xml → GET project.svg → GET media files
7. Frontend: dispara CustomEvent('signage-published') → refresca Content Preview iframe
⚠ El header
X-Requested-With: XMLHttpRequestes obligatorio en los POST al SpinetiX. Sin él devuelve HTML en vez de ejecutar la acción AJAX.
SVG Composer
signage/services/svg_composer.py genera project.svg dinámico para SpinetiX.
Tipos de item soportados
| Tipo | Implementación SVG |
|---|---|
image | <image> fullscreen con preserveAspectRatio |
video | <video> envuelto en <g> con visibility animation |
text | <text> con font, color, background rect |
color | Slide de color sólido (<rect> fullscreen) |
ticker | Texto scrolling horizontal (overlay siempre visible) |
overlay | Imagen siempre visible (logo, watermark) |
Reglas críticas para vídeo en SVG
- Auto-detección: Si
MediaAsset.asset_type == 'video'→ renderiza como<video>aunque playlist item diga'image' - Duración real: Vídeos ignoran Duration del modal, usan duración real (ffprobe). Imágenes sí respetan Duration
- Replay:
<video>tienerepeatCount="indefinite". El<g>padre controla visibilidad → garantiza replay correcto - Sin fade en vídeo: Usar
visibilitydiscrete, noopacity— SpinetiX no soporta fade en<video>
Transiciones (9 tipos)
| Transición | Implementación SVG |
|---|---|
| Cross Dissolve | opacity crossfade |
| Fade to Black | opacity 1→0→1 |
| Slide Left/Right/Up/Down | animateTransform translate |
| Zoom In/Out | animateTransform scale |
| Cut | visibility discrete |
Endpoints públicos SpinetiX (sin autenticación)
| Endpoint | Descripción |
|---|---|
GET /publish/{token}/spx-listing.xml | Índice WebDAV DAV:multistatus |
GET /publish/{token}/project.svg | Proyecto SVG dinámico |
GET /publish/{token}/index.svg | Alias de project.svg |
GET /publish/{token}/{filename} | Media asset file |
API Endpoints (48 total)
Content (Media)
POST /api/signage/signage/media/upload
GET /api/signage/signage/media
GET /api/signage/signage/media/{id}
PATCH /api/signage/signage/media/{id}
DELETE /api/signage/signage/media/{id}
POST /api/signage/signage/media/{id}/reprocess
POST /api/signage/signage/media/bulk-delete
Playlists
GET/POST /api/signage/signage/playlists
GET/PATCH/DELETE /api/signage/signage/playlists/{id}
POST /api/signage/signage/playlists/{id}/duplicate
GET /api/signage/signage/playlists/{id}/preview
POST /api/signage/signage/playlists/preview-svg ← preview sin publicar
Schedules
GET/POST /api/signage/signage/schedules
GET/PATCH/DELETE /api/signage/signage/schedules/{id}
GET /api/signage/signage/schedules/{id}/active-playlist
Players
GET/POST /api/signage/signage/players
GET/PATCH/DELETE /api/signage/signage/players/{id}
GET /api/signage/signage/adapters
Display Control
POST /api/signage/signage/players/{id}/power-on
POST /api/signage/signage/players/{id}/power-off
POST /api/signage/signage/players/{id}/volume # {"level": 75}
POST /api/signage/signage/players/{id}/brightness # {"level": 75}
POST /api/signage/signage/players/{id}/input # {"source": "hdmi1"}
POST /api/signage/signage/players/{id}/reboot
Deployments
GET/POST /api/signage/signage/deployments
GET /api/signage/signage/deployments/{id}
POST /api/signage/signage/deployments/{id}/retry
DELETE /api/signage/signage/deployments/{id}
GET /api/signage/signage/deployments/{id}/results
Proof of Play
GET /api/signage/signage/playback-logs
GET /api/signage/signage/playback-stats
POST /api/signage/signage/playback-logs/collect
POST /api/signage/signage/screenshots/capture
GET /api/signage/signage/players/{id}/screenshot
GET /api/signage/signage/reports/proof-of-play # JSON
GET /api/signage/signage/reports/proof-of-play/html # HTML
GET /api/signage/signage/reports/proof-of-play/csv # CSV download
Operation History (Rollback)
GET /api/monitoring/signage/operations # ?source=content|playlist
POST /api/monitoring/signage/operations/{id}/restore # Rollback + httpPublish
Client Portal (público, sin autenticación)
GET /api/signage/client-portal/{token}/info
POST /api/signage/client-portal/{token}/verify-pin
GET /api/signage/client-portal/{token}/players
GET /api/signage/client-portal/{token}/playlists
GET /api/signage/client-portal/{token}/media
POST /api/signage/client-portal/{token}/media/upload
PATCH /api/signage/client-portal/{token}/playlists/{id}
POST /api/signage/client-portal/{token}/playlists/{id}/replace-asset
GET /api/signage/client-portal/{token}/history
Client Projects (multi-cliente)
GET/POST /api/signage/signage/client-projects
GET/PATCH/DELETE /api/signage/signage/client-projects/{id}
GET /client/{token}/ # Portal público
POST /client/{token}/upload
POST /client/{token}/create-playlist
POST /client/{token}/publish
Vendors soportados (12)
| Vendor | Tipo push | Control display |
|---|---|---|
| SpinetiX | WebDAV | — |
| BrightSign | BSN.cloud REST | — |
| Samsung | MagicINFO | MDC TCP:1515 |
| LG | SuperSign | TCP:9761 |
| Crestron | REST Upload | CIP |
| Philips | Wave/CMND | SICP |
| IAdea | SMIL HTTP | — |
| Barco | REST Upload | PJLink TCP:4352 |
| Christie | REST Upload | PJLink |
| NEC/Sharp | REST Upload | PJLink |
| Yodeck | Cloud API | — |
| Signagelive | Cloud API | — |
Schedule engine
Tipos de regla
| Tipo | Campos |
|---|---|
always | playlist_id, priority |
time_range | start, end, playlist_id, priority |
day_of_week | days[], start, end, playlist_id, priority |
date_range | start_date, end_date, playlist_id, priority |
Días: 0=Lunes … 6=Domingo (ISO weekday)
Evaluación: filtra reglas válidas → selecciona mayor prioridad → fallback a default_playlist
Schedule Matrix (UI)
Tres vistas: Day (24h timeline) / Week (7 columnas) / Month (calendario).
- Bloques con color personalizable, repeat options (daily/weekly/monthly/custom days)
- Conflict detection: bloques solapados en el mismo device → borde rojo
- Auto-publishing: timers por bloque →
publish-setup+ Agent httpPublish al inicio - End action: None / Black Screen / Corporate Image / Test Pattern
SignageModalHelper
Módulo JS que reemplaza todos los prompt()/confirm() nativos:
import { SignageModal } from './SignageModalHelper.js';
// Selección de dispositivos unificada
const devices = await SignageModal.selectDevices('Publish', 'Publish');
// Devuelve [{id, name, ip}, ...] — con filtro por groups + Select All + status dots
// Input con campos configurables
const result = await SignageModal.input('Title', [
{ name: 'color', label: 'Color', type: 'color', value: '#ffffff' },
{ name: 'size', label: 'Size', type: 'number', value: 48 },
{ name: 'option', type: 'select', options: [{value:'a', label:'A'}] },
]);
// Confirm / Alert
const ok = await SignageModal.confirm('Delete this item?', 'Delete');
await SignageModal.alert('Message', 'Title');
⚠ Los modales NO se cierran al hacer click fuera (click-outside dismiss deshabilitado).
Credenciales automáticas (SignageCredentials.js)
resolveSignageCredentials() — 3 pasos, primero que resuelve gana:
sessionStoragekeysignage_credsGET /api/credentials/→ primera credential sección HTTP →GET /api/credentials/{id}/decrypt- Modal prompt (solo si las anteriores están vacías)
⚠ El endpoint
publish-setupNO devuelve passwords. Las credenciales se resuelven en frontend.
Playlist Builder (UI)
Modal split-panel (1500px × 90vh):
- Panel izquierdo (~50%): Media Library con tabs Media/Transitions
- Panel derecho (~50%): Playlist Items — lista ordenable, duración por item, transition badges entre slides
- Drag & Drop HTML5 desde library al panel
- Live Preview Monitor 16:9 (160px) via
POST /signage/playlists/preview-svg - Save & Publish: guarda + abre modal de selección de dispositivos
Playlists is_auto: Las generadas por Publish from Content tienen prefijo _auto_* y se ocultan en el Playlist tab. Auto-purge tras 24h (máximo 20 retenidas).
Clear Playback / Corporate Image
5 acciones disponibles en el modal Clear Playback:
- Corporate Image: imagen corporativa fullscreen persistente por org (
corporate_asset_idenplayer.display_info) - Black Screen: pantalla negra
- Test Pattern: barras SMPTE
- Restart Content: fuerza re-descarga
- Clear All Content: elimina playlist/schedule
Monitoreo SNMP SpinetiX
| Métrica | OID | Nota |
|---|---|---|
| CPU | 1.3.6.1.2.1.25.3.3.1.2.196608 | Index no estándar 196608 |
| Memory | 1.3.6.1.2.1.25.2.3.1.6.1 | Valor en KB raw |
| Bandwidth | Via interface eth0 | snmp_bandwidth_in/out_mbps |
Client Projects (/api/signage/client-projects/) — 5 endpoints
Modulo nuevo: gestiona proyectos de cliente para Digital Signage.
| Metodo | Endpoint | Proposito |
|---|---|---|
| GET | /client-projects/ | Listar proyectos del portal de cliente |
| POST | /client-projects/ | Crear proyecto de cliente |
| GET | /client-projects/{id} | Detalle de proyecto |
| PUT | /client-projects/{id} | Actualizar proyecto |
| DELETE | /client-projects/{id} | Eliminar proyecto |
Convenciones y restricciones
- Credenciales sensibles (
password,api_key,token) siempre cifradas con Fernet AES-128 is_auto=Trueen playlists auto-generadas — filtrar en Playlist tab (is_auto=False)SignageOperationauto-logging en cadapublish-setupyclear_action— no omitir- Huey para todas las operaciones pesadas (transcode, deployment, collect logs)
X-Requested-With: XMLHttpRequestobligatorio en POST al SpinetiX Control CenterresolveSignageCredentials()como única fuente de credenciales — no hardcodear ni pedir directamente- Audit trail via
PortalChangeLogpara todas las acciones del Client Portal
Véase también
- [[concept—signage—deployment]] — modelo de despliegue de Signage
- [[crearack—signage—que-es-signage]] — introducción a Digital Signage
- [[crearack—signage—pantallas]] — gestión de pantallas
- [[crearack—signage—playlists]] — playlists de Signage
- [[crearack—signage—contenido]] — contenido y assets de Signage
- [[crearack—signage—programacion]] — programación temporal de playlists
- [[crearack-tech—backend—spinetix-integration]] — integración con SpinetiX
- [[crearack-tech—guides—digital-signage-guide]] — página relacionada del corpus
- [[entity—signage—model—mediaasset]] — asset multimedia de Signage
- [[entity—signage—model—signageplayer]] — player físico de Signage