|
1 | 1 | { |
2 | | - "$comment": "Generated by screenrig-protocol-gen from api/openapi.yaml; sha256=11f6d9a8a78c28f001596d14354c0da67b5b767b53118361e989027301b13427. Shape validation for the adslot page union. Run canonical playlist-write-semantics.ts next. At most 16 adslot pages and one ordinary page with no visibility remain server checks.", |
| 2 | + "$comment": "Generated by screenrig-protocol-gen from api/openapi.yaml; sha256=1b411f9213b31c6238575febb48f9818fd8b962f848cefe47aed6d6aee1f54a7. Shape validation for the adslot page union. Run canonical playlist-write-semantics.ts next. At most 16 adslot pages and one ordinary page with no visibility remain server checks.", |
3 | 3 | "$defs": { |
4 | 4 | "AdslotPageWrite": { |
5 | 5 | "additionalProperties": false, |
|
316 | 316 | "default": false, |
317 | 317 | "type": "boolean" |
318 | 318 | }, |
| 319 | + "displays": { |
| 320 | + "$ref": "#/$defs/PlaylistDisplays" |
| 321 | + }, |
319 | 322 | "enter": { |
320 | 323 | "$ref": "#/$defs/PrimitiveEnter" |
321 | 324 | }, |
|
338 | 341 | "type": "string" |
339 | 342 | }, |
340 | 343 | "rect": { |
341 | | - "$ref": "#/$defs/PlaylistRect" |
| 344 | + "$ref": "#/$defs/PlaylistRect", |
| 345 | + "description": "Required unless displays is set. With displays, omit it or send the rect derived from them." |
342 | 346 | }, |
343 | 347 | "release_id": { |
344 | 348 | "type": "string" |
|
348 | 352 | "id", |
349 | 353 | "primitive", |
350 | 354 | "release_id", |
351 | | - "rect", |
352 | 355 | "layer", |
353 | 356 | "content_fit" |
354 | 357 | ], |
|
485 | 488 | }, |
486 | 489 | "PlaylistCardPrimitiveWrite": { |
487 | 490 | "additionalProperties": false, |
488 | | - "description": "A built-in card the Player draws natively (clock, weather, menu board, ticker, KPI tiles and the rest of the vendored cards catalog, screenrig.cards.contract/v1). layout places it: full (the whole canvas), free (your own rect and layer), center (900 x 420), wide (the canvas inset 30 from the left and right and 80 from the top and bottom), header (the whole top edge, 80 high), footer (the whole bottom edge, 80 high), corner (270 square), chip (240 x 70) or strip (the whole edge, 120 high), in 1080p design units scaled by min(canvas width, canvas height) / 1080. corner and chip take an anchor corner (top-left, top-right, bottom-left, bottom-right) and strip an anchor edge (top, bottom, left, right); the other layouts take none. Except for free, the server derives rect and layer from the canvas: omit them, or send exactly what it derives. A page holds at most one full, center or wide card, one corner or chip card per corner, and one strip per edge (a card with presence may share an edge with a normal strip); a header takes the top edge's normal strip slot and a footer the bottom edge's, so a page holds at most one of each, no normal strip on that edge, and a card with presence may still share it. A corner or chip on an edge that holds a strip moves in past it by 120, and past a header or footer by 80. A card's tier decides its layouts (GET /api/card-types): core takes all nine, fullscreen takes full. params holds the placement's static properties (theme, location, titles, column definitions) and changes only with a republish. data names a card data document (GET/PUT /api/card-data/{key}) that holds the card's dynamic properties (menu sections, ticker items, KPI tiles); agents change it without a republish. With data, params may not carry dynamic properties; without it, params carry the whole configuration. Every string in params that begins with media: is a media reference, media:\u003cid\u003e, naming a ready image in this project that the card draws (spotlight people[].photo, product-grid products[].photo); a card never takes an image URL. Publish refuses an unknown card or major and invalid params (card_invalid), a layout, anchor, rect or layer the card may not carry (card_layout_invalid), cards that claim one place on the page (card_layout_conflict), a media reference that names no ready image of the project (card_media_invalid), and a bound key whose stored document the card rejects (card_data_invalid). The screens showing the placement are granted its media, and a live write approves pending media it names, as with a by:id selector. A card with a third-party feed (weather) reads it from the key the server derives from its static params.", |
| 491 | + "description": "A built-in card the Player draws natively (clock, weather, menu board, ticker, KPI tiles and the rest of the vendored cards catalog, screenrig.cards.contract/v1). layout places it: full (the whole canvas), free (your own rect and layer), center (900 x 420), wide (the canvas inset 30 from the left and right and 80 from the top and bottom), header (the whole top edge, 80 high), footer (the whole bottom edge, 80 high), corner (270 square), chip (240 x 70) or strip (the whole edge, 120 high), in 1080p design units scaled by min(canvas width, canvas height) / 1080. corner and chip take an anchor corner (top-left, top-right, bottom-left, bottom-right) and strip an anchor edge (top, bottom, left, right); the other layouts take none. Except for free, the server derives rect and layer from the canvas, or from the frame displays cover: omit them, or send exactly what it derives. A page holds at most one full, center or wide card, one corner or chip card per corner, and one strip per edge (a card with presence may share an edge with a normal strip); a header takes the top edge's normal strip slot and a footer the bottom edge's, so a page holds at most one of each, no normal strip on that edge, and a card with presence may still share it. A corner or chip on an edge that holds a strip moves in past it by 120, and past a header or footer by 80. On a playlist with a format, displays is the frame the layout derives in (full is the frame, corner the frame's corner; free refuses displays), the card scale is the tile's min(width, height) / 1080 = 1 rather than derived from the frame, and two cards compete for a slot, or move in past each other's strips, only when their frames share a display (no displays is every display). A card's tier decides its layouts (GET /api/card-types): core takes all nine, fullscreen takes full. params holds the placement's static properties (theme, location, titles, column definitions) and changes only with a republish. data names a card data document (GET/PUT /api/card-data/{key}) that holds the card's dynamic properties (menu sections, ticker items, KPI tiles); agents change it without a republish. With data, params may not carry dynamic properties; without it, params carry the whole configuration. Every string in params that begins with media: is a media reference, media:\u003cid\u003e, naming a ready image in this project that the card draws (spotlight people[].photo, product-grid products[].photo); a card never takes an image URL. Publish refuses an unknown card or major and invalid params (card_invalid), a layout, anchor, rect or layer the card may not carry (card_layout_invalid), cards that claim one place on the page (card_layout_conflict), a media reference that names no ready image of the project (card_media_invalid), and a bound key whose stored document the card rejects (card_data_invalid). The screens showing the placement are granted its media, and a live write approves pending media it names, as with a by:id selector. A card with a third-party feed (weather) reads it from the key the server derives from its static params.", |
489 | 492 | "properties": { |
490 | 493 | "anchor": { |
491 | 494 | "description": "Required for corner and chip (a corner) and strip (an edge); not allowed for full, free, center, wide, header and footer.", |
|
516 | 519 | "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$", |
517 | 520 | "type": "string" |
518 | 521 | }, |
| 522 | + "displays": { |
| 523 | + "$ref": "#/$defs/PlaylistDisplays", |
| 524 | + "description": "Playlists with a format only. The displays whose frame the layout derives in: full is the frame, corner the frame's corner, and so on. Ascending contiguous 1-based display numbers within 1..format.displays, such as [2] or [1, 2]. Absent is every display. Not allowed for layout free." |
| 525 | + }, |
519 | 526 | "enter": { |
520 | 527 | "$ref": "#/$defs/PrimitiveEnter" |
521 | 528 | }, |
|
574 | 581 | ], |
575 | 582 | "type": "object" |
576 | 583 | }, |
| 584 | + "PlaylistDisplays": { |
| 585 | + "description": "Playlists with a format only. The displays a primitive covers: ascending contiguous 1-based display numbers within 1..format.displays, such as [2], [1, 2] or [1, 2, 3]. The server derives rect as the union of those tiles; rect is omitted or exactly the derived value. Stored and echoed on read.", |
| 586 | + "items": { |
| 587 | + "maximum": 3, |
| 588 | + "minimum": 1, |
| 589 | + "type": "integer" |
| 590 | + }, |
| 591 | + "maxItems": 3, |
| 592 | + "minItems": 1, |
| 593 | + "type": "array", |
| 594 | + "uniqueItems": true |
| 595 | + }, |
577 | 596 | "PlaylistDurationAdvance": { |
578 | 597 | "additionalProperties": false, |
579 | 598 | "properties": { |
|
595 | 614 | ], |
596 | 615 | "type": "object" |
597 | 616 | }, |
| 617 | + "PlaylistFormat": { |
| 618 | + "additionalProperties": false, |
| 619 | + "description": "The video wall a playlist is authored for: displays tiles, each 1920 x 1080 (landscape) or 1080 x 1920 (portrait), laid out in a row or a column. The canvas of n tiles of width w and height h is (n * w, h) for a row and (w, n * h) for a column, for example 5760 x 1080 for 3 landscape displays in a row and 1080 x 3840 for 2 portrait displays in a column. Every ordinary page's canvas width and height must equal it exactly, else 400 invalid_request at pages[i].canvas. Card scale on such a playlist is the tile's min(width, height) / 1080 = 1. A playlist with a format may be bound only to a screen whose wall has the same shape (displays = number of outputs, arrangement, portrait if and only if rotation is 90 or 270), else 409 playlist_format_mismatch: assignment, fleet assignment, schedule entries and takeover. A playlist without a format binds to any screen.", |
| 620 | + "properties": { |
| 621 | + "arrangement": { |
| 622 | + "enum": [ |
| 623 | + "row", |
| 624 | + "column" |
| 625 | + ], |
| 626 | + "type": "string" |
| 627 | + }, |
| 628 | + "displays": { |
| 629 | + "enum": [ |
| 630 | + 2, |
| 631 | + 3 |
| 632 | + ], |
| 633 | + "type": "integer" |
| 634 | + }, |
| 635 | + "orientation": { |
| 636 | + "enum": [ |
| 637 | + "landscape", |
| 638 | + "portrait" |
| 639 | + ], |
| 640 | + "type": "string" |
| 641 | + } |
| 642 | + }, |
| 643 | + "required": [ |
| 644 | + "displays", |
| 645 | + "arrangement", |
| 646 | + "orientation" |
| 647 | + ], |
| 648 | + "type": "object" |
| 649 | + }, |
598 | 650 | "PlaylistIframePrimitiveWrite": { |
599 | 651 | "additionalProperties": false, |
600 | 652 | "properties": { |
|
604 | 656 | ], |
605 | 657 | "type": "string" |
606 | 658 | }, |
| 659 | + "displays": { |
| 660 | + "$ref": "#/$defs/PlaylistDisplays" |
| 661 | + }, |
607 | 662 | "enter": { |
608 | 663 | "$ref": "#/$defs/PrimitiveEnter" |
609 | 664 | }, |
|
626 | 681 | "type": "string" |
627 | 682 | }, |
628 | 683 | "rect": { |
629 | | - "$ref": "#/$defs/PlaylistRect" |
| 684 | + "$ref": "#/$defs/PlaylistRect", |
| 685 | + "description": "Required unless displays is set. With displays, omit it or send the rect derived from them." |
630 | 686 | }, |
631 | 687 | "src": { |
632 | 688 | "format": "uri", |
|
644 | 700 | "primitive", |
645 | 701 | "src", |
646 | 702 | "title", |
647 | | - "rect", |
648 | 703 | "layer", |
649 | 704 | "content_fit" |
650 | 705 | ], |
|
665 | 720 | ], |
666 | 721 | "type": "string" |
667 | 722 | }, |
| 723 | + "displays": { |
| 724 | + "$ref": "#/$defs/PlaylistDisplays" |
| 725 | + }, |
668 | 726 | "dwell_ms": { |
669 | 727 | "description": "Required on media_end image primitives. Forbidden on duration and application pages.", |
670 | 728 | "maximum": 86400000, |
|
693 | 751 | "type": "string" |
694 | 752 | }, |
695 | 753 | "rect": { |
696 | | - "$ref": "#/$defs/PlaylistRect" |
| 754 | + "$ref": "#/$defs/PlaylistRect", |
| 755 | + "description": "Required unless displays is set. With displays, omit it or send the rect derived from them." |
697 | 756 | }, |
698 | 757 | "selector": { |
699 | 758 | "$ref": "#/$defs/PlaylistMediaSelector" |
|
703 | 762 | "id", |
704 | 763 | "primitive", |
705 | 764 | "selector", |
706 | | - "rect", |
707 | 765 | "layer", |
708 | 766 | "content_fit" |
709 | 767 | ], |
|
1097 | 1155 | ], |
1098 | 1156 | "type": "string" |
1099 | 1157 | }, |
| 1158 | + "displays": { |
| 1159 | + "$ref": "#/$defs/PlaylistDisplays" |
| 1160 | + }, |
1100 | 1161 | "enter": { |
1101 | 1162 | "$ref": "#/$defs/PrimitiveEnter" |
1102 | 1163 | }, |
|
1127 | 1188 | "type": "string" |
1128 | 1189 | }, |
1129 | 1190 | "rect": { |
1130 | | - "$ref": "#/$defs/PlaylistRect" |
| 1191 | + "$ref": "#/$defs/PlaylistRect", |
| 1192 | + "description": "Required unless displays is set. With displays, omit it or send the rect derived from them." |
1131 | 1193 | }, |
1132 | 1194 | "sources": { |
1133 | 1195 | "items": { |
|
1143 | 1205 | "primitive", |
1144 | 1206 | "sources", |
1145 | 1207 | "fallback_media_id", |
1146 | | - "rect", |
1147 | 1208 | "layer", |
1148 | 1209 | "content_fit" |
1149 | 1210 | ], |
|
1266 | 1327 | ], |
1267 | 1328 | "type": "string" |
1268 | 1329 | }, |
| 1330 | + "displays": { |
| 1331 | + "$ref": "#/$defs/PlaylistDisplays" |
| 1332 | + }, |
1269 | 1333 | "enter": { |
1270 | 1334 | "$ref": "#/$defs/PrimitiveEnter" |
1271 | 1335 | }, |
|
1299 | 1363 | "type": "string" |
1300 | 1364 | }, |
1301 | 1365 | "rect": { |
1302 | | - "$ref": "#/$defs/PlaylistRect" |
| 1366 | + "$ref": "#/$defs/PlaylistRect", |
| 1367 | + "description": "Required unless displays is set. With displays, omit it or send the rect derived from them." |
1303 | 1368 | }, |
1304 | 1369 | "selector": { |
1305 | 1370 | "$ref": "#/$defs/PlaylistMediaSelector" |
|
1309 | 1374 | "id", |
1310 | 1375 | "primitive", |
1311 | 1376 | "selector", |
1312 | | - "rect", |
1313 | 1377 | "layer", |
1314 | 1378 | "content_fit" |
1315 | 1379 | ], |
|
1321 | 1385 | "audio": { |
1322 | 1386 | "$ref": "#/$defs/PlaylistAudioWrite" |
1323 | 1387 | }, |
| 1388 | + "format": { |
| 1389 | + "$ref": "#/$defs/PlaylistFormat", |
| 1390 | + "description": "Omitted means none: an ordinary playlist." |
| 1391 | + }, |
1324 | 1392 | "name": { |
1325 | 1393 | "maxLength": 120, |
1326 | 1394 | "minLength": 1, |
|
0 commit comments