obs-scheduler 0.0.3 → 0.1.2

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/README.md CHANGED
@@ -40,7 +40,8 @@ Options:
40
40
  {
41
41
  "name": "Example Event 2",
42
42
  "schedule_type": "recurring",
43
- "schedule": "* * * * * *",
43
+ // At 14:15 (2:15pm) on each first day of the month.
44
+ "schedule": "15 14 1 * *",
44
45
  "action": "start_recording"
45
46
  }
46
47
  ]
package/cli.js CHANGED
@@ -17,7 +17,7 @@ program
17
17
  undefined,
18
18
  )
19
19
  .option('-c, --config <file-path>', 'config file path')
20
- .version('0.0.3')
20
+ .version('0.1.2')
21
21
  .action(run);
22
22
 
23
23
  await program.parseAsync(process.argv);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
- "title": "OBS Scheduler Config File Schema",
3
+ "title": "OBS Scheduler Configuration",
4
4
  "type": "object",
5
5
  "required": [
6
6
  "events"
@@ -13,6 +13,7 @@
13
13
  "format": "tabs",
14
14
  "items": {
15
15
  "title": "Event",
16
+ "headerTemplate": "{{ self.name }}",
16
17
  "description": "A scheduled event for triggering a set action in OBS.",
17
18
  "type": "object",
18
19
  "required": [
@@ -40,11 +41,23 @@
40
41
  "$ref": "#/definitions/ScheduleType"
41
42
  },
42
43
  "date": {
43
- "title": "Date",
44
- "description": "A date/time for when the action should trigger.",
44
+ "title": "Date(s)",
45
+ "description": "A date/time (or a list of dates/times) for when the action should trigger.",
45
46
  "propertyOrder": 4,
46
- "type": "string",
47
- "format": "datetime-local",
47
+ "anyOf": [
48
+ {
49
+ "title": "Single Date/Time",
50
+ "$ref": "#/definitions/DateString"
51
+ },
52
+ {
53
+ "title": "Multiple Dates/Times",
54
+ "type": "array",
55
+ "uniqueItems": true,
56
+ "items": {
57
+ "$ref": "#/definitions/DateString"
58
+ }
59
+ }
60
+ ],
48
61
  "options": {
49
62
  "dependencies": {
50
63
  "schedule_type": "date"
@@ -52,10 +65,23 @@
52
65
  }
53
66
  },
54
67
  "schedule": {
55
- "title": "Schedule",
56
- "description": "[WIP] A schedule for a recurring event.",
68
+ "title": "Schedule(s)",
69
+ "description": "A schedule (or a list of schedules) for a recurring event in crontab format.",
57
70
  "propertyOrder": 4,
58
- "type": "string",
71
+ "anyOf": [
72
+ {
73
+ "title": "Single Schedule",
74
+ "$ref": "#/definitions/CronTabString"
75
+ },
76
+ {
77
+ "title": "Multiple Schedules",
78
+ "type": "array",
79
+ "uniqueItems": true,
80
+ "items": {
81
+ "$ref": "#/definitions/CronTabString"
82
+ }
83
+ }
84
+ ],
59
85
  "options": {
60
86
  "dependencies": {
61
87
  "schedule_type": "recurring"
@@ -79,69 +105,70 @@
79
105
  },
80
106
  "ws_request": {
81
107
  "title": "OBS Websocket Message",
82
- "$ref": "#/definitions/OBSWSRequest"
108
+ "$ref": "#/definitions/OBSWSRequest",
109
+ "options": {
110
+ "dependencies": {
111
+ "action": "obs_ws"
112
+ }
113
+ }
83
114
  }
84
115
  },
85
116
  "allOf": [
86
117
  {
87
- "oneOf": [
88
- {
89
- "required": [
90
- "date"
91
- ],
92
- "properties": {
93
- "schedule_type": {
94
- "const": "date"
95
- }
96
- }
97
- },
98
- {
99
- "required": [
100
- "schedule"
101
- ],
102
- "properties": {
103
- "schedule_type": {
104
- "const": "recurring"
105
- }
118
+ "if": {
119
+ "properties": {
120
+ "schedule_type": {
121
+ "const": "date"
106
122
  }
107
123
  }
108
- ]
124
+ },
125
+ "then": {
126
+ "required": [
127
+ "date"
128
+ ]
129
+ }
109
130
  },
110
131
  {
111
- "oneOf": [
112
- {
113
- "not": {
114
- "properties": {
115
- "action": {
116
- "enum": [
117
- "advanced_scene_switcher",
118
- "obs_ws"
119
- ]
120
- }
121
- }
132
+ "if": {
133
+ "properties": {
134
+ "schedule_type": {
135
+ "const": "recurring"
122
136
  }
123
- },
124
- {
125
- "required": [
126
- "switcher_message"
127
- ],
128
- "properties": {
129
- "action": {
130
- "const": "advanced_scene_switcher"
131
- }
137
+ }
138
+ },
139
+ "then": {
140
+ "required": [
141
+ "schedule"
142
+ ]
143
+ }
144
+ },
145
+ {
146
+ "if": {
147
+ "properties": {
148
+ "action": {
149
+ "const": "advanced_scene_switcher"
132
150
  }
133
- },
134
- {
135
- "required": [
136
- "ws_request"
137
- ],
138
- "properties": {
139
- "action": {
140
- "const": "obs_ws"
141
- }
151
+ }
152
+ },
153
+ "then": {
154
+ "required": [
155
+ "switcher_message"
156
+ ]
157
+ }
158
+ },
159
+ {
160
+ "if": {
161
+ "properties": {
162
+ "action": {
163
+ "const": "obs_ws"
142
164
  }
143
165
  }
144
- ]
166
+ },
167
+ "then": {
168
+ "required": [
169
+ "ws_request"
170
+ ]
171
+ }
145
172
  }
146
173
  ],
147
174
  "options": {
@@ -166,6 +193,15 @@
166
193
  ]
167
194
  }
168
195
  },
196
+ "DateString": {
197
+ "type": "string",
198
+ "format": "datetime-local"
199
+ },
200
+ "CronTabString": {
201
+ "type": "string",
202
+ "format": "crontab",
203
+ "pattern": "^((((\\d+,)+\\d+|(\\d+(\\/|-|#)\\d+)|\\d+L?|\\*(\\/\\d+)?|L(-\\d+)?|\\?|[A-Z]{3}(-[A-Z]{3})?) ?){5,7})|(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\\d+(ns|us|µs|ms|s|m|h))+)$"
204
+ },
169
205
  "EventAction": {
170
206
  "description": "An action to trigger in OBS.",
171
207
  "type": "string",
@@ -199,17 +235,15 @@
199
235
  ],
200
236
  "additionalProperties": false,
201
237
  "properties": {
238
+ "id": {
239
+ "type": "string"
240
+ },
202
241
  "type": {
203
242
  "type": "string"
204
243
  },
205
244
  "data": {
206
245
  "type": "object"
207
246
  }
208
- },
209
- "options": {
210
- "dependencies": {
211
- "action": "obs_ws"
212
- }
213
247
  }
214
248
  }
215
249
  }
package/dist/config.js CHANGED
@@ -1,3 +1,4 @@
1
+ import CronExpressionParser from "cron-parser";
1
2
  import { DateTime } from "luxon";
2
3
 
3
4
  /** @typedef {'date'|'recurring'} ConfigEventType */
@@ -8,6 +9,7 @@ import { DateTime } from "luxon";
8
9
 
9
10
  /**
10
11
  * @typedef {Object} OBSWSRequest
12
+ * @property {string} [id]
11
13
  * @property {string} type
12
14
  * @property {Object} data
13
15
  */
@@ -17,8 +19,8 @@ import { DateTime } from "luxon";
17
19
  * @property {string} [name]
18
20
  * @property {boolean} [enabled=true]
19
21
  * @property {ConfigEventType} schedule_type
20
- * @property {string} [date] Only present if `schedule_type` is `'date'`.
21
- * @property {string} [schedule] Only present if `schedule_type` is `'recurring'`.
22
+ * @property {string|string[]} [date] Only present if `schedule_type` is `'date'`.
23
+ * @property {string|string[]} [schedule] Only present if `schedule_type` is `'recurring'`.
22
24
  * @property {ConfigEventAction} action
23
25
  * @property {string} [switcher_message] Only present if `action` is `'advanced_scene_switcher'`.
24
26
  * @property {OBSWSRequest} [ws_request] Only present if `action` is `'obs_ws'`.
@@ -36,17 +38,47 @@ import { DateTime } from "luxon";
36
38
  * @returns {boolean}
37
39
  */
38
40
  export function checkEventSchedule(date, event) {
41
+ // Ignore a disabled event
39
42
  if (event.enabled == false) {
40
43
  return false;
41
44
  }
42
45
 
43
- if (event.date) {
44
- const eventDate = DateTime.fromISO(event.date);
45
- // console.log(eventDate.getTime(), date.getTime())
46
- return eventDate.startOf('second').toMillis() == date.toMillis();
47
- } else if (event.schedule) {
48
- // TODO: match `schedule` field
49
- return false;
46
+ if (event.date) { // If event has the `date` field
47
+ /** @type {string[]} */
48
+ let datesToCompare = new Array();
49
+ if (typeof event.date == 'string') {
50
+ datesToCompare.push(event.date);
51
+ } else {
52
+ datesToCompare.push(...event.date);
53
+ }
54
+
55
+ return datesToCompare.some((eventDate) => {
56
+ // Parse event's date string to `DateTime`
57
+ const eventDateTime = DateTime.fromISO(eventDate);
58
+ // console.log(eventDate.getTime(), date.getTime())
59
+ // Compare parsed date (rounded to the previous second) to the current `date`
60
+ return eventDateTime.startOf('second').toMillis() == date.toMillis();
61
+ });
62
+ } else if (event.schedule) { // If event has the `schedule` field
63
+ /** @type {string[]} */
64
+ let schedulesToCompare = new Array();
65
+ if (typeof event.schedule == 'string') {
66
+ schedulesToCompare.push(event.schedule);
67
+ } else {
68
+ schedulesToCompare.push(...event.schedule);
69
+ }
70
+
71
+ return schedulesToCompare.some((eventSchedule) => {
72
+ try {
73
+ // Parse crontab string
74
+ const schedule = CronExpressionParser.parse(eventSchedule);
75
+ // Return if the parsed schedule includes the current `date`
76
+ return schedule.includesDate(date.toJSDate());
77
+ } catch (error) {
78
+ // If parse fails, return `false`
79
+ return false;
80
+ }
81
+ });
50
82
  } else {
51
83
  return false;
52
84
  }
package/dist/index.js CHANGED
@@ -33,12 +33,14 @@ export async function run() {
33
33
  }
34
34
 
35
35
  /** @type {Config} */
36
- let config = {};
36
+ let config;
37
37
 
38
38
  function updateConfigFromFile() {
39
39
  const fileContents = fs.readFileSync(configPath, "utf8")
40
40
  .replace(/^[ \t]*\/\/.+$\n/gm, '');
41
+ const firstRun = config == undefined;
41
42
  config = JSON.parse(fileContents);
43
+ console.log((firstRun ? 'Read' : 'Updated') + ' configuration file.');
42
44
  }
43
45
 
44
46
  // Read config file for initial value
@@ -54,6 +56,9 @@ export async function run() {
54
56
  const matchingEvents = config.events
55
57
  .filter(e => checkEventSchedule(date, e));
56
58
 
59
+ // console.log(config.events);
60
+ console.log(matchingEvents);
61
+
57
62
  const wsRequests = matchingEvents
58
63
  .flatMap(convertEventToWebsocketRequest);
59
64
 
package/dist/obs.js CHANGED
@@ -6,7 +6,9 @@
6
6
  * @returns {import("obs-websocket-js").RequestBatchRequest[]}
7
7
  */
8
8
  export function convertEventToWebsocketRequest(event) {
9
- const requestID = `${event.name ? event.name + '.' : ''}${event.action}`;
9
+ const requestID = event.action == 'obs_ws' && event.ws_request?.id
10
+ ? event.ws_request.id
11
+ : `${event.name ? event.name + '.' : ''}${event.action}`;
10
12
 
11
13
  switch (event.action) {
12
14
  case "start_stream":
@@ -1,17 +1,17 @@
1
1
  {
2
- "$schema": "../config-schema.json",
2
+ "$schema": "/Users/edon/Documents/Developer Stuff/NPM Packages/obs-scheduler/config-schema.json",
3
3
  "events": [
4
4
  {
5
5
  "name": "Example Event 1",
6
6
  "schedule_type": "date",
7
- "date": "2026-01-06T10:59",
8
- "action": "start_recording"
9
- },
10
- {
11
- "name": "Example Event 2",
12
- "schedule_type": "recurring",
13
- "schedule": "",
14
- "action": "start_recording"
7
+ // "date": "2026-01-09T14:49:30",
8
+ "date": [
9
+ "2026-01-09T15:06:15",
10
+ "2026-01-09T15:06:45",
11
+ "2026-01-09T15:07"
12
+ ],
13
+ "action": "advanced_scene_switcher",
14
+ "switcher_message": "test"
15
15
  }
16
16
  ]
17
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "obs-scheduler",
3
- "version": "0.0.3",
3
+ "version": "0.1.2",
4
4
  "description": "A CLI tool for scheduling OBS functionality.",
5
5
  "author": "edonv",
6
6
  "type": "module",
@@ -19,6 +19,7 @@
19
19
  "homepage": "https://github.com/edonv/obs-scheduler#readme",
20
20
  "dependencies": {
21
21
  "commander": "^14.0.2",
22
+ "cron-parser": "^5.4.0",
22
23
  "luxon": "^3.7.2",
23
24
  "obs-websocket-js": "^5.0.7"
24
25
  },