@xiboplayer/schedule 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/README.md +102 -0
- package/docs/XIBO_CAMPAIGNS_AND_PRIORITY.md +600 -0
- package/docs/advanced-features.md +425 -0
- package/docs/integration.md +284 -0
- package/docs/interrupts-implementation.md +357 -0
- package/package.json +41 -0
- package/src/criteria.js +135 -0
- package/src/criteria.test.js +376 -0
- package/src/index.js +20 -0
- package/src/integration.test.js +351 -0
- package/src/interrupts.js +298 -0
- package/src/interrupts.test.js +482 -0
- package/src/overlays.js +174 -0
- package/src/schedule.dayparting.test.js +390 -0
- package/src/schedule.js +509 -0
- package/src/schedule.test.js +505 -0
- package/vitest.config.js +8 -0
package/docs/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# @xiboplayer/schedule Documentation
|
|
2
|
+
|
|
3
|
+
**Campaign scheduling, dayparting, and priority logic.**
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `@xiboplayer/schedule` package provides:
|
|
8
|
+
|
|
9
|
+
- **Campaign scheduler** - Multi-campaign priority handling
|
|
10
|
+
- **Dayparting** - Time-based scheduling
|
|
11
|
+
- **Geo-scheduling** - Location-based campaigns
|
|
12
|
+
- **Interrupt campaigns** - High-priority content
|
|
13
|
+
- **Default fallback** - Graceful degradation
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @xiboplayer/schedule
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
import { Scheduler } from '@xiboplayer/schedule';
|
|
25
|
+
|
|
26
|
+
const scheduler = new Scheduler({
|
|
27
|
+
campaigns: campaignData,
|
|
28
|
+
timezone: 'America/New_York'
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Get current layout
|
|
32
|
+
const layout = scheduler.getCurrentLayout();
|
|
33
|
+
|
|
34
|
+
// Check next scheduled event
|
|
35
|
+
const nextEvent = scheduler.getNextEvent();
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
### Campaign Priority
|
|
41
|
+
|
|
42
|
+
Campaigns ordered by priority (1 = highest):
|
|
43
|
+
1. Interrupt campaigns (override all)
|
|
44
|
+
2. Normal campaigns (scheduled)
|
|
45
|
+
3. Default layout (fallback)
|
|
46
|
+
|
|
47
|
+
### Dayparting
|
|
48
|
+
|
|
49
|
+
Time-based scheduling:
|
|
50
|
+
```javascript
|
|
51
|
+
{
|
|
52
|
+
dayOfWeek: [1, 2, 3, 4, 5], // Mon-Fri
|
|
53
|
+
startTime: '08:00',
|
|
54
|
+
endTime: '18:00'
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Geo-Scheduling
|
|
59
|
+
|
|
60
|
+
Location-based content:
|
|
61
|
+
```javascript
|
|
62
|
+
{
|
|
63
|
+
geofence: {
|
|
64
|
+
latitude: 40.7128,
|
|
65
|
+
longitude: -74.0060,
|
|
66
|
+
radius: 1000 // meters
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## API Reference
|
|
72
|
+
|
|
73
|
+
### Scheduler
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
class Scheduler {
|
|
77
|
+
constructor(options)
|
|
78
|
+
getCurrentLayout()
|
|
79
|
+
getNextEvent()
|
|
80
|
+
setLocation(lat, lon)
|
|
81
|
+
on(event, callback)
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Events
|
|
86
|
+
|
|
87
|
+
- `schedule:change` - Active schedule changed
|
|
88
|
+
- `campaign:start` - Campaign started
|
|
89
|
+
- `campaign:end` - Campaign ended
|
|
90
|
+
|
|
91
|
+
## Dependencies
|
|
92
|
+
|
|
93
|
+
- `@xiboplayer/utils` - Logger, EventEmitter
|
|
94
|
+
|
|
95
|
+
## Related Packages
|
|
96
|
+
|
|
97
|
+
- [@xiboplayer/core](../../core/docs/) - Player orchestration
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
**Package Version**: 1.0.0
|
|
102
|
+
**Last Updated**: 2026-02-10
|
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
# Xibo Campaigns and Priority System
|
|
2
|
+
|
|
3
|
+
**Last Updated:** 2026-01-30
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This document explains how Xibo's campaign and priority system works across all three player implementations (Electron, PWA, and Arexibo). Understanding this system is crucial for proper schedule configuration and layout management.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What Are Campaigns?
|
|
12
|
+
|
|
13
|
+
**Campaigns** are a Xibo scheduling feature that groups related layouts together as a single schedulable unit.
|
|
14
|
+
|
|
15
|
+
### Key Concepts
|
|
16
|
+
|
|
17
|
+
- **Campaign** = A collection of layouts with shared scheduling attributes
|
|
18
|
+
- **Priority Level** = Applied at the campaign level (not individual layouts)
|
|
19
|
+
- **Layout Cycling** = All layouts within a campaign cycle in sequence
|
|
20
|
+
- **Competition** = Campaigns compete with each other and standalone layouts based on priority
|
|
21
|
+
|
|
22
|
+
### Why Use Campaigns?
|
|
23
|
+
|
|
24
|
+
Campaigns solve the problem of managing related content that should play together:
|
|
25
|
+
|
|
26
|
+
**Without Campaigns:**
|
|
27
|
+
- Schedule 10 individual breakfast menu layouts
|
|
28
|
+
- Each needs identical time windows and priorities
|
|
29
|
+
- Changes require updating 10 separate schedule entries
|
|
30
|
+
|
|
31
|
+
**With Campaigns:**
|
|
32
|
+
- Group 10 breakfast layouts into a "Breakfast Menu" campaign
|
|
33
|
+
- Schedule the campaign once with shared priority and time window
|
|
34
|
+
- Changes affect all layouts at once
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Priority System Explained
|
|
39
|
+
|
|
40
|
+
### How Priority Works
|
|
41
|
+
|
|
42
|
+
Priority determines which layouts (or campaigns) play when multiple schedules overlap.
|
|
43
|
+
|
|
44
|
+
#### Priority Rules
|
|
45
|
+
|
|
46
|
+
1. **Higher Number Wins**: Priority 10 beats priority 5
|
|
47
|
+
2. **Campaign-Level Priority**: All layouts in a campaign inherit the campaign's priority
|
|
48
|
+
3. **Same Priority = All Play**: Multiple items with the same priority all display (cycling)
|
|
49
|
+
4. **No Priority = Priority 0**: Default priority if not specified
|
|
50
|
+
|
|
51
|
+
#### Priority Range
|
|
52
|
+
|
|
53
|
+
- **Type**: Integer (positive or negative)
|
|
54
|
+
- **Common Range**: 0-100
|
|
55
|
+
- **Special Values**:
|
|
56
|
+
- `0`: Default/lowest priority (normal content)
|
|
57
|
+
- `10`: Standard scheduled content
|
|
58
|
+
- `100`: Urgent/interrupt content (alerts, emergencies)
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## XML Schedule Format
|
|
63
|
+
|
|
64
|
+
### Campaign Structure
|
|
65
|
+
|
|
66
|
+
```xml
|
|
67
|
+
<schedule>
|
|
68
|
+
<!-- Default layout (fallback when nothing scheduled) -->
|
|
69
|
+
<default file="1" />
|
|
70
|
+
|
|
71
|
+
<!-- Campaign: Group of layouts with shared attributes -->
|
|
72
|
+
<campaign id="10" priority="10" fromdt="2026-01-30 08:00:00" todt="2026-01-30 17:00:00">
|
|
73
|
+
<layout file="100" />
|
|
74
|
+
<layout file="101" />
|
|
75
|
+
<layout file="102" />
|
|
76
|
+
</campaign>
|
|
77
|
+
|
|
78
|
+
<!-- Standalone layout (not in a campaign) -->
|
|
79
|
+
<layout file="200" priority="5" fromdt="2026-01-30 00:00:00" todt="2026-01-30 23:59:59" />
|
|
80
|
+
</schedule>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Campaign Attributes
|
|
84
|
+
|
|
85
|
+
| Attribute | Description | Required | Example |
|
|
86
|
+
|-----------|-------------|----------|---------|
|
|
87
|
+
| `id` | Unique campaign identifier | Yes | `id="10"` |
|
|
88
|
+
| `priority` | Priority level (integer) | No (default: 0) | `priority="10"` |
|
|
89
|
+
| `fromdt` | Start date/time | Yes | `fromdt="2026-01-30 08:00:00"` |
|
|
90
|
+
| `todt` | End date/time | Yes | `todt="2026-01-30 17:00:00"` |
|
|
91
|
+
| `scheduleid` | CMS schedule ID | No | `scheduleid="123"` |
|
|
92
|
+
|
|
93
|
+
### Standalone Layout Attributes
|
|
94
|
+
|
|
95
|
+
| Attribute | Description | Required | Example |
|
|
96
|
+
|-----------|-------------|----------|---------|
|
|
97
|
+
| `file` | Layout ID | Yes | `file="200"` |
|
|
98
|
+
| `priority` | Priority level (integer) | No (default: 0) | `priority="5"` |
|
|
99
|
+
| `fromdt` | Start date/time | Yes | `fromdt="2026-01-30 00:00:00"` |
|
|
100
|
+
| `todt` | End date/time | Yes | `todt="2026-01-30 23:59:59"` |
|
|
101
|
+
| `scheduleid` | CMS schedule ID | No | `scheduleid="456"` |
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Priority Resolution Examples
|
|
106
|
+
|
|
107
|
+
### Example 1: Campaign Beats Standalone
|
|
108
|
+
|
|
109
|
+
**Schedule:**
|
|
110
|
+
```xml
|
|
111
|
+
<campaign id="1" priority="10" fromdt="2026-01-30 08:00:00" todt="2026-01-30 17:00:00">
|
|
112
|
+
<layout file="100" />
|
|
113
|
+
<layout file="101" />
|
|
114
|
+
</campaign>
|
|
115
|
+
<layout file="200" priority="5" fromdt="2026-01-30 00:00:00" todt="2026-01-30 23:59:59" />
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**At 12:00 PM (both active):**
|
|
119
|
+
- Campaign priority: 10
|
|
120
|
+
- Standalone priority: 5
|
|
121
|
+
- **Result**: Displays layouts 100, 101 (campaign wins)
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
### Example 2: Multiple Same-Priority Campaigns
|
|
126
|
+
|
|
127
|
+
**Schedule:**
|
|
128
|
+
```xml
|
|
129
|
+
<campaign id="1" priority="10" fromdt="2026-01-30 08:00:00" todt="2026-01-30 17:00:00">
|
|
130
|
+
<layout file="100" />
|
|
131
|
+
<layout file="101" />
|
|
132
|
+
</campaign>
|
|
133
|
+
<campaign id="2" priority="10" fromdt="2026-01-30 08:00:00" todt="2026-01-30 17:00:00">
|
|
134
|
+
<layout file="200" />
|
|
135
|
+
<layout file="201" />
|
|
136
|
+
</campaign>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**At 12:00 PM (both active):**
|
|
140
|
+
- Both campaigns at priority 10
|
|
141
|
+
- **Result**: Displays layouts 100, 101, 200, 201 (all layouts cycle)
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
### Example 3: Mixed Campaigns and Standalone
|
|
146
|
+
|
|
147
|
+
**Schedule:**
|
|
148
|
+
```xml
|
|
149
|
+
<campaign id="1" priority="10" fromdt="2026-01-30 08:00:00" todt="2026-01-30 17:00:00">
|
|
150
|
+
<layout file="100" />
|
|
151
|
+
<layout file="101" />
|
|
152
|
+
</campaign>
|
|
153
|
+
<layout file="200" priority="10" fromdt="2026-01-30 08:00:00" todt="2026-01-30 17:00:00" />
|
|
154
|
+
<layout file="300" priority="5" fromdt="2026-01-30 00:00:00" todt="2026-01-30 23:59:59" />
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**At 12:00 PM:**
|
|
158
|
+
- Campaign 1: priority 10 (layouts 100, 101)
|
|
159
|
+
- Standalone 200: priority 10
|
|
160
|
+
- Standalone 300: priority 5
|
|
161
|
+
- **Result**: Displays layouts 100, 101, 200 (priority 10 wins, all shown)
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
### Example 4: Time Window Filtering
|
|
166
|
+
|
|
167
|
+
**Schedule:**
|
|
168
|
+
```xml
|
|
169
|
+
<campaign id="1" priority="10" fromdt="2026-01-30 08:00:00" todt="2026-01-30 12:00:00">
|
|
170
|
+
<layout file="100" /> <!-- Morning campaign -->
|
|
171
|
+
</campaign>
|
|
172
|
+
<campaign id="2" priority="10" fromdt="2026-01-30 12:00:01" todt="2026-01-30 17:00:00">
|
|
173
|
+
<layout file="200" /> <!-- Afternoon campaign -->
|
|
174
|
+
</campaign>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**At 10:00 AM:**
|
|
178
|
+
- Campaign 1 active (08:00-12:00)
|
|
179
|
+
- **Result**: Displays layout 100
|
|
180
|
+
|
|
181
|
+
**At 2:00 PM:**
|
|
182
|
+
- Campaign 2 active (12:00-17:00)
|
|
183
|
+
- **Result**: Displays layout 200
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
### Example 5: Interrupt Content
|
|
188
|
+
|
|
189
|
+
**Schedule:**
|
|
190
|
+
```xml
|
|
191
|
+
<campaign id="1" priority="10" fromdt="2026-01-30 00:00:00" todt="2026-01-30 23:59:59">
|
|
192
|
+
<layout file="100" /> <!-- Regular content -->
|
|
193
|
+
</campaign>
|
|
194
|
+
<layout file="999" priority="100" fromdt="2026-01-30 14:00:00" todt="2026-01-30 14:15:00" />
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**At 1:00 PM:**
|
|
198
|
+
- Campaign 1 active (priority 10)
|
|
199
|
+
- **Result**: Displays layout 100
|
|
200
|
+
|
|
201
|
+
**At 2:05 PM:**
|
|
202
|
+
- Campaign 1 still active (priority 10)
|
|
203
|
+
- Emergency layout 999 active (priority 100)
|
|
204
|
+
- **Result**: Displays layout 999 (interrupt content overrides)
|
|
205
|
+
|
|
206
|
+
**At 2:20 PM:**
|
|
207
|
+
- Campaign 1 active (priority 10)
|
|
208
|
+
- Emergency layout 999 expired
|
|
209
|
+
- **Result**: Displays layout 100 (resumes normal content)
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Real-World Use Cases
|
|
214
|
+
|
|
215
|
+
### Restaurant Menu Board
|
|
216
|
+
|
|
217
|
+
**Scenario:** Different menus for breakfast, lunch, dinner
|
|
218
|
+
|
|
219
|
+
```xml
|
|
220
|
+
<schedule>
|
|
221
|
+
<default file="1" /> <!-- "We're Closed" layout -->
|
|
222
|
+
|
|
223
|
+
<!-- Breakfast: Mon-Fri 6am-11am -->
|
|
224
|
+
<campaign id="10" priority="10" fromdt="2026-01-30 06:00:00" todt="2026-01-30 11:00:00">
|
|
225
|
+
<layout file="100" /> <!-- Breakfast Page 1 -->
|
|
226
|
+
<layout file="101" /> <!-- Breakfast Page 2 -->
|
|
227
|
+
<layout file="102" /> <!-- Breakfast Specials -->
|
|
228
|
+
</campaign>
|
|
229
|
+
|
|
230
|
+
<!-- Lunch: Mon-Fri 11am-3pm -->
|
|
231
|
+
<campaign id="11" priority="10" fromdt="2026-01-30 11:00:00" todt="2026-01-30 15:00:00">
|
|
232
|
+
<layout file="200" /> <!-- Lunch Menu -->
|
|
233
|
+
<layout file="201" /> <!-- Daily Specials -->
|
|
234
|
+
</campaign>
|
|
235
|
+
|
|
236
|
+
<!-- Dinner: Mon-Fri 3pm-10pm -->
|
|
237
|
+
<campaign id="12" priority="10" fromdt="2026-01-30 15:00:00" todt="2026-01-30 22:00:00">
|
|
238
|
+
<layout file="300" /> <!-- Dinner Menu -->
|
|
239
|
+
<layout file="301" /> <!-- Wine List -->
|
|
240
|
+
</campaign>
|
|
241
|
+
|
|
242
|
+
<!-- Special Promotion: Overrides all (emergency interrupt) -->
|
|
243
|
+
<layout file="999" priority="100" fromdt="2026-01-30 17:00:00" todt="2026-01-30 18:00:00" />
|
|
244
|
+
</schedule>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Timeline:**
|
|
248
|
+
- **6:00 AM - 11:00 AM**: Breakfast campaign (layouts 100, 101, 102 cycle)
|
|
249
|
+
- **11:00 AM - 3:00 PM**: Lunch campaign (layouts 200, 201 cycle)
|
|
250
|
+
- **3:00 PM - 10:00 PM**: Dinner campaign (layouts 300, 301 cycle)
|
|
251
|
+
- **5:00 PM - 6:00 PM**: Happy hour promotion (layout 999 interrupts dinner campaign)
|
|
252
|
+
- **All other times**: Default "We're Closed" layout
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
### Retail Store Displays
|
|
257
|
+
|
|
258
|
+
**Scenario:** Regular content + promotional overrides
|
|
259
|
+
|
|
260
|
+
```xml
|
|
261
|
+
<schedule>
|
|
262
|
+
<default file="1" /> <!-- Store logo/hours -->
|
|
263
|
+
|
|
264
|
+
<!-- Regular store content (low priority) -->
|
|
265
|
+
<campaign id="1" priority="5" fromdt="2026-01-30 00:00:00" todt="2026-01-31 00:00:00">
|
|
266
|
+
<layout file="100" /> <!-- Product showcase 1 -->
|
|
267
|
+
<layout file="101" /> <!-- Product showcase 2 -->
|
|
268
|
+
<layout file="102" /> <!-- Brand story -->
|
|
269
|
+
</campaign>
|
|
270
|
+
|
|
271
|
+
<!-- Tuesday sale (higher priority, overrides regular) -->
|
|
272
|
+
<campaign id="2" priority="10" fromdt="2026-01-30 09:00:00" todt="2026-01-30 21:00:00">
|
|
273
|
+
<layout file="200" /> <!-- Sale announcement -->
|
|
274
|
+
<layout file="201" /> <!-- Featured deals -->
|
|
275
|
+
</campaign>
|
|
276
|
+
|
|
277
|
+
<!-- Flash sale announcement (highest priority, interrupts everything) -->
|
|
278
|
+
<layout file="999" priority="100" fromdt="2026-01-30 12:00:00" todt="2026-01-30 13:00:00" />
|
|
279
|
+
</schedule>
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
**Timeline:**
|
|
283
|
+
- **12:00 AM - 9:00 AM**: Regular content (layouts 100, 101, 102)
|
|
284
|
+
- **9:00 AM - 12:00 PM**: Tuesday sale (layouts 200, 201 override regular)
|
|
285
|
+
- **12:00 PM - 1:00 PM**: Flash sale (layout 999 interrupts Tuesday sale)
|
|
286
|
+
- **1:00 PM - 9:00 PM**: Tuesday sale resumes (layouts 200, 201)
|
|
287
|
+
- **9:00 PM - 12:00 AM**: Regular content resumes (layouts 100, 101, 102)
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
### Corporate Lobby
|
|
292
|
+
|
|
293
|
+
**Scenario:** News + scheduled events
|
|
294
|
+
|
|
295
|
+
```xml
|
|
296
|
+
<schedule>
|
|
297
|
+
<default file="1" /> <!-- Company logo -->
|
|
298
|
+
|
|
299
|
+
<!-- Always-on company news (low priority) -->
|
|
300
|
+
<campaign id="1" priority="5" fromdt="2026-01-30 08:00:00" todt="2026-01-30 18:00:00">
|
|
301
|
+
<layout file="100" /> <!-- News feed -->
|
|
302
|
+
<layout file="101" /> <!-- Employee spotlights -->
|
|
303
|
+
<layout file="102" /> <!-- Company metrics -->
|
|
304
|
+
</campaign>
|
|
305
|
+
|
|
306
|
+
<!-- Weekly all-hands meeting (interrupts news) -->
|
|
307
|
+
<layout file="200" priority="50" fromdt="2026-01-30 14:00:00" todt="2026-01-30 15:00:00" />
|
|
308
|
+
|
|
309
|
+
<!-- Emergency alert (highest priority) -->
|
|
310
|
+
<layout file="999" priority="100" fromdt="2026-01-30 10:00:00" todt="2026-01-30 10:15:00" />
|
|
311
|
+
</schedule>
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
**Timeline:**
|
|
315
|
+
- **8:00 AM - 2:00 PM**: Company news (layouts 100, 101, 102 cycle)
|
|
316
|
+
- **10:00 AM - 10:15 AM**: Emergency alert (layout 999 interrupts news)
|
|
317
|
+
- **2:00 PM - 3:00 PM**: All-hands meeting (layout 200 overrides news)
|
|
318
|
+
- **3:00 PM - 6:00 PM**: Company news resumes
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Implementation Status
|
|
323
|
+
|
|
324
|
+
### Feature Parity Matrix
|
|
325
|
+
|
|
326
|
+
| Feature | Electron | PWA | Arexibo | Status |
|
|
327
|
+
|---------|----------|-----|---------|--------|
|
|
328
|
+
| Campaign XML parsing | ✅ | ✅ | ✅ | Complete |
|
|
329
|
+
| Campaign-level priority | ✅ | ✅ | ✅ | Complete |
|
|
330
|
+
| Layout cycling within campaign | ✅ | ✅ | ✅ | Complete |
|
|
331
|
+
| Mixed campaigns + standalone | ✅ | ✅ | ✅ | Complete |
|
|
332
|
+
| Time window filtering | ✅ | ✅ | ✅ | Complete |
|
|
333
|
+
| Multiple same-priority campaigns | ✅ | ✅ | ✅ | Complete |
|
|
334
|
+
| Backward compatibility | ✅ | ✅ | ✅ | Complete |
|
|
335
|
+
|
|
336
|
+
### Implementation Details
|
|
337
|
+
|
|
338
|
+
#### PWA Core
|
|
339
|
+
- **Branch**: `feature/pwa-campaigns`
|
|
340
|
+
- **Files**: `packages/core/src/xmds.js`, `packages/core/src/schedule.js`
|
|
341
|
+
- **Tests**: 6 comprehensive test cases
|
|
342
|
+
- **Status**: Ready for merge
|
|
343
|
+
|
|
344
|
+
#### Arexibo
|
|
345
|
+
- **Branch**: `feature/arx-dayparting` (includes campaigns)
|
|
346
|
+
- **Files**: `src/schedule.rs`
|
|
347
|
+
- **Tests**: 9 campaign-specific tests
|
|
348
|
+
- **Status**: Ready for merge
|
|
349
|
+
|
|
350
|
+
#### Electron
|
|
351
|
+
- **Status**: Already implemented (baseline reference)
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## Technical Details
|
|
356
|
+
|
|
357
|
+
### Scheduling Algorithm
|
|
358
|
+
|
|
359
|
+
All three players use the same priority resolution algorithm:
|
|
360
|
+
|
|
361
|
+
```
|
|
362
|
+
function getCurrentLayouts(currentDateTime):
|
|
363
|
+
1. Filter all schedule items (campaigns + standalone) by time window
|
|
364
|
+
→ Keep only items where: fromdt <= now <= todt
|
|
365
|
+
|
|
366
|
+
2. Find maximum priority among active items
|
|
367
|
+
→ maxPriority = max(item.priority for all active items)
|
|
368
|
+
|
|
369
|
+
3. Select all items matching maximum priority
|
|
370
|
+
→ winners = items where priority == maxPriority
|
|
371
|
+
|
|
372
|
+
4. Collect layouts from winning items
|
|
373
|
+
→ For campaigns: add all layouts in campaign
|
|
374
|
+
→ For standalone: add the layout
|
|
375
|
+
|
|
376
|
+
5. Return collected layouts
|
|
377
|
+
→ If no layouts: return default layout
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Layout Cycling
|
|
381
|
+
|
|
382
|
+
When multiple layouts are selected, they cycle with:
|
|
383
|
+
|
|
384
|
+
1. **Duration**: Each layout's configured duration (from layout definition)
|
|
385
|
+
2. **Order**: Maintains XML order within campaigns
|
|
386
|
+
3. **Seamless**: Transitions between layouts based on player settings
|
|
387
|
+
4. **Repeat**: Cycles indefinitely until schedule changes
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## Configuration Best Practices
|
|
392
|
+
|
|
393
|
+
### Priority Guidelines
|
|
394
|
+
|
|
395
|
+
**Recommended Priority Tiers:**
|
|
396
|
+
|
|
397
|
+
| Priority Range | Use Case | Example |
|
|
398
|
+
|----------------|----------|---------|
|
|
399
|
+
| 0 | Default/fallback content | Idle screens, logos |
|
|
400
|
+
| 1-5 | Low-priority background content | Generic ads, filler content |
|
|
401
|
+
| 10 | Standard scheduled content | Normal operations, menus |
|
|
402
|
+
| 20-50 | Important scheduled content | Special events, meetings |
|
|
403
|
+
| 75-90 | High-priority overrides | Alerts, announcements |
|
|
404
|
+
| 100+ | Emergency interrupts | Fire alarms, emergencies |
|
|
405
|
+
|
|
406
|
+
### Time Window Guidelines
|
|
407
|
+
|
|
408
|
+
1. **No Gaps**: Ensure continuous coverage with default layout fallback
|
|
409
|
+
2. **No Overlaps**: Use priority to handle intentional overlaps
|
|
410
|
+
3. **Timezone Aware**: All times in player's local timezone
|
|
411
|
+
4. **Boundary Handling**: End time is inclusive (todt="17:00:00" includes 17:00)
|
|
412
|
+
|
|
413
|
+
### Campaign Organization
|
|
414
|
+
|
|
415
|
+
**Good Campaign Structure:**
|
|
416
|
+
```xml
|
|
417
|
+
<!-- Logical grouping: Related content together -->
|
|
418
|
+
<campaign id="morning" priority="10" fromdt="..." todt="...">
|
|
419
|
+
<layout file="100" /> <!-- Morning welcome -->
|
|
420
|
+
<layout file="101" /> <!-- Morning news -->
|
|
421
|
+
<layout file="102" /> <!-- Morning schedule -->
|
|
422
|
+
</campaign>
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
**Poor Campaign Structure:**
|
|
426
|
+
```xml
|
|
427
|
+
<!-- Unrelated content mixed together -->
|
|
428
|
+
<campaign id="random" priority="10" fromdt="..." todt="...">
|
|
429
|
+
<layout file="100" /> <!-- Welcome screen -->
|
|
430
|
+
<layout file="999" /> <!-- Emergency alert -->
|
|
431
|
+
<layout file="200" /> <!-- Lunch menu -->
|
|
432
|
+
</campaign>
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
## Troubleshooting
|
|
438
|
+
|
|
439
|
+
### Common Issues
|
|
440
|
+
|
|
441
|
+
#### Issue: "Lower priority content showing instead of higher"
|
|
442
|
+
|
|
443
|
+
**Cause**: Time window mismatch
|
|
444
|
+
|
|
445
|
+
**Check:**
|
|
446
|
+
1. Verify both items are active at current time
|
|
447
|
+
2. Check system clock on player
|
|
448
|
+
3. Confirm timezone matches schedule
|
|
449
|
+
|
|
450
|
+
#### Issue: "Campaign layouts not cycling"
|
|
451
|
+
|
|
452
|
+
**Cause**: Empty campaign or single layout
|
|
453
|
+
|
|
454
|
+
**Check:**
|
|
455
|
+
1. Verify campaign has multiple layouts in XML
|
|
456
|
+
2. Check layout durations are set correctly
|
|
457
|
+
3. Ensure layouts are valid and downloaded
|
|
458
|
+
|
|
459
|
+
#### Issue: "All layouts showing at once instead of cycling"
|
|
460
|
+
|
|
461
|
+
**Cause**: Player misconfiguration (not a campaign issue)
|
|
462
|
+
|
|
463
|
+
**Check:**
|
|
464
|
+
1. Player cycling settings
|
|
465
|
+
2. Region configuration
|
|
466
|
+
3. Xibo player version compatibility
|
|
467
|
+
|
|
468
|
+
#### Issue: "Standalone layout never shows"
|
|
469
|
+
|
|
470
|
+
**Cause**: Another item always has higher priority
|
|
471
|
+
|
|
472
|
+
**Check:**
|
|
473
|
+
1. Compare priorities of all schedule items
|
|
474
|
+
2. Verify time windows don't overlap with higher priority items
|
|
475
|
+
3. Add default layout as fallback
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
## Migration from Non-Campaign Schedules
|
|
480
|
+
|
|
481
|
+
### Converting Standalone Layouts to Campaigns
|
|
482
|
+
|
|
483
|
+
**Before (Standalone):**
|
|
484
|
+
```xml
|
|
485
|
+
<layout file="100" priority="10" fromdt="2026-01-30 08:00:00" todt="2026-01-30 17:00:00" />
|
|
486
|
+
<layout file="101" priority="10" fromdt="2026-01-30 08:00:00" todt="2026-01-30 17:00:00" />
|
|
487
|
+
<layout file="102" priority="10" fromdt="2026-01-30 08:00:00" todt="2026-01-30 17:00:00" />
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
**After (Campaign):**
|
|
491
|
+
```xml
|
|
492
|
+
<campaign id="1" priority="10" fromdt="2026-01-30 08:00:00" todt="2026-01-30 17:00:00">
|
|
493
|
+
<layout file="100" />
|
|
494
|
+
<layout file="101" />
|
|
495
|
+
<layout file="102" />
|
|
496
|
+
</campaign>
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
**Benefits:**
|
|
500
|
+
- ✅ Single schedule entry instead of three
|
|
501
|
+
- ✅ Guaranteed same priority and time window
|
|
502
|
+
- ✅ Easier to update all layouts at once
|
|
503
|
+
|
|
504
|
+
### Backward Compatibility
|
|
505
|
+
|
|
506
|
+
All players maintain full backward compatibility:
|
|
507
|
+
|
|
508
|
+
- ✅ Old schedules without campaigns continue working
|
|
509
|
+
- ✅ Mixed old + new schedules supported
|
|
510
|
+
- ✅ No breaking changes to existing functionality
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
## References
|
|
515
|
+
|
|
516
|
+
### Xibo CMS Documentation
|
|
517
|
+
- [Xibo Manual - Scheduling](https://xibosignage.com/manual/en/scheduling.html)
|
|
518
|
+
- [Xibo Manual - Campaigns](https://xibosignage.com/manual/en/media_module_campaigns.html)
|
|
519
|
+
|
|
520
|
+
### Implementation Branches
|
|
521
|
+
- **PWA**: `feature/pwa-campaigns` in [xibo_players repo](https://github.com/linuxnow/xibo_players)
|
|
522
|
+
- **Arexibo**: `feature/arx-dayparting` in [arexibo repo](https://github.com/linuxnow/arexibo)
|
|
523
|
+
|
|
524
|
+
### Related Documentation
|
|
525
|
+
- `TRANSITIONS.md` - Layout transition effects
|
|
526
|
+
- `DAYPARTING.md` - Recurring schedule patterns
|
|
527
|
+
- `XIBO_FEATURE_COMPARISON.md` - Cross-player feature matrix
|
|
528
|
+
|
|
529
|
+
---
|
|
530
|
+
|
|
531
|
+
## Appendix: Complete Example
|
|
532
|
+
|
|
533
|
+
### Multi-Day Restaurant Schedule
|
|
534
|
+
|
|
535
|
+
```xml
|
|
536
|
+
<schedule>
|
|
537
|
+
<!-- Fallback: Closed -->
|
|
538
|
+
<default file="1" />
|
|
539
|
+
|
|
540
|
+
<!-- Monday-Friday Breakfast (6am-11am) -->
|
|
541
|
+
<campaign id="weekday_breakfast" priority="10"
|
|
542
|
+
fromdt="2026-01-27 06:00:00" todt="2026-01-27 11:00:00">
|
|
543
|
+
<layout file="100" />
|
|
544
|
+
<layout file="101" />
|
|
545
|
+
<layout file="102" />
|
|
546
|
+
</campaign>
|
|
547
|
+
|
|
548
|
+
<!-- Monday-Friday Lunch (11am-3pm) -->
|
|
549
|
+
<campaign id="weekday_lunch" priority="10"
|
|
550
|
+
fromdt="2026-01-27 11:00:00" todt="2026-01-27 15:00:00">
|
|
551
|
+
<layout file="200" />
|
|
552
|
+
<layout file="201" />
|
|
553
|
+
</campaign>
|
|
554
|
+
|
|
555
|
+
<!-- Monday-Friday Dinner (3pm-10pm) -->
|
|
556
|
+
<campaign id="weekday_dinner" priority="10"
|
|
557
|
+
fromdt="2026-01-27 15:00:00" todt="2026-01-27 22:00:00">
|
|
558
|
+
<layout file="300" />
|
|
559
|
+
<layout file="301" />
|
|
560
|
+
</campaign>
|
|
561
|
+
|
|
562
|
+
<!-- Weekend Brunch (9am-3pm) -->
|
|
563
|
+
<campaign id="weekend_brunch" priority="10"
|
|
564
|
+
fromdt="2026-02-01 09:00:00" todt="2026-02-01 15:00:00">
|
|
565
|
+
<layout file="400" />
|
|
566
|
+
<layout file="401" />
|
|
567
|
+
<layout file="402" />
|
|
568
|
+
</campaign>
|
|
569
|
+
|
|
570
|
+
<!-- Weekend Dinner (3pm-10pm) -->
|
|
571
|
+
<campaign id="weekend_dinner" priority="10"
|
|
572
|
+
fromdt="2026-02-01 15:00:00" todt="2026-02-01 22:00:00">
|
|
573
|
+
<layout file="500" />
|
|
574
|
+
<layout file="501" />
|
|
575
|
+
</campaign>
|
|
576
|
+
|
|
577
|
+
<!-- Special: Valentine's Day Override -->
|
|
578
|
+
<campaign id="valentines" priority="50"
|
|
579
|
+
fromdt="2026-02-14 15:00:00" todt="2026-02-14 22:00:00">
|
|
580
|
+
<layout file="600" /> <!-- Valentine's Special Menu -->
|
|
581
|
+
<layout file="601" /> <!-- Valentine's Desserts -->
|
|
582
|
+
</campaign>
|
|
583
|
+
|
|
584
|
+
<!-- Emergency: Power Outage Notice -->
|
|
585
|
+
<layout file="999" priority="100"
|
|
586
|
+
fromdt="2026-01-30 18:00:00" todt="2026-01-30 19:00:00" />
|
|
587
|
+
</schedule>
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
This schedule demonstrates:
|
|
591
|
+
- ✅ Multiple campaigns for different meal times
|
|
592
|
+
- ✅ Weekday vs weekend content separation
|
|
593
|
+
- ✅ Special event overrides (Valentine's Day)
|
|
594
|
+
- ✅ Emergency interrupt capability
|
|
595
|
+
- ✅ Proper priority tier usage
|
|
596
|
+
- ✅ Comprehensive time coverage with fallback
|
|
597
|
+
|
|
598
|
+
---
|
|
599
|
+
|
|
600
|
+
**End of Document**
|