stormcloud-video-player 0.8.16 → 0.8.18

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.
Files changed (52) hide show
  1. package/dist/stormcloud-vp.min.js +2 -2
  2. package/lib/index.cjs +4449 -4221
  3. package/lib/index.cjs.map +1 -1
  4. package/lib/index.d.cts +18 -195
  5. package/lib/index.d.ts +18 -195
  6. package/lib/index.js +4455 -4219
  7. package/lib/index.js.map +1 -1
  8. package/lib/player/AdBreakOrchestrator.cjs +2275 -0
  9. package/lib/player/AdBreakOrchestrator.cjs.map +1 -0
  10. package/lib/player/AdBreakOrchestrator.d.cts +62 -0
  11. package/lib/player/AdPreloadPool.cjs +673 -0
  12. package/lib/player/AdPreloadPool.cjs.map +1 -0
  13. package/lib/player/AdPreloadPool.d.cts +37 -0
  14. package/lib/player/AdTimingService.cjs +841 -0
  15. package/lib/player/AdTimingService.cjs.map +1 -0
  16. package/lib/player/AdTimingService.d.cts +88 -0
  17. package/lib/player/HlsEngine.cjs +1502 -0
  18. package/lib/player/HlsEngine.cjs.map +1 -0
  19. package/lib/player/HlsEngine.d.cts +38 -0
  20. package/lib/player/PlaceholderLayer.cjs +191 -0
  21. package/lib/player/PlaceholderLayer.cjs.map +1 -0
  22. package/lib/player/PlaceholderLayer.d.cts +12 -0
  23. package/lib/player/PlayerControls.cjs +225 -0
  24. package/lib/player/PlayerControls.cjs.map +1 -0
  25. package/lib/player/PlayerControls.d.cts +13 -0
  26. package/lib/player/Scte35CueManager.cjs +413 -0
  27. package/lib/player/Scte35CueManager.cjs.map +1 -0
  28. package/lib/player/Scte35CueManager.d.cts +43 -0
  29. package/lib/player/Scte35Parser.cjs +945 -0
  30. package/lib/player/Scte35Parser.cjs.map +1 -0
  31. package/lib/player/Scte35Parser.d.cts +23 -0
  32. package/lib/player/StormcloudVideoPlayer.cjs +4411 -4168
  33. package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
  34. package/lib/player/StormcloudVideoPlayer.d.cts +19 -196
  35. package/lib/player/VmapManager.cjs +1706 -0
  36. package/lib/player/VmapManager.cjs.map +1 -0
  37. package/lib/player/VmapManager.d.cts +38 -0
  38. package/lib/player/playerTypes.cjs +49 -0
  39. package/lib/player/playerTypes.cjs.map +1 -0
  40. package/lib/player/playerTypes.d.cts +20 -0
  41. package/lib/players/HlsPlayer.cjs +4411 -4169
  42. package/lib/players/HlsPlayer.cjs.map +1 -1
  43. package/lib/players/HlsPlayer.d.cts +1 -1
  44. package/lib/players/index.cjs +4411 -4169
  45. package/lib/players/index.cjs.map +1 -1
  46. package/lib/sdk/hlsAdPlayer.d.cts +1 -1
  47. package/lib/{types-CWWADkvd.d.cts → types-DNiBmPKK.d.cts} +18 -1
  48. package/lib/ui/StormcloudVideoPlayer.cjs +4449 -4221
  49. package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
  50. package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
  51. package/lib/utils/tracking.d.cts +1 -1
  52. package/package.json +1 -1
@@ -0,0 +1,413 @@
1
+ "use strict";
2
+ function _array_like_to_array(arr, len) {
3
+ if (len == null || len > arr.length) len = arr.length;
4
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
5
+ return arr2;
6
+ }
7
+ function _array_with_holes(arr) {
8
+ if (Array.isArray(arr)) return arr;
9
+ }
10
+ function _class_call_check(instance, Constructor) {
11
+ if (!(instance instanceof Constructor)) {
12
+ throw new TypeError("Cannot call a class as a function");
13
+ }
14
+ }
15
+ function _defineProperties(target, props) {
16
+ for(var i = 0; i < props.length; i++){
17
+ var descriptor = props[i];
18
+ descriptor.enumerable = descriptor.enumerable || false;
19
+ descriptor.configurable = true;
20
+ if ("value" in descriptor) descriptor.writable = true;
21
+ Object.defineProperty(target, descriptor.key, descriptor);
22
+ }
23
+ }
24
+ function _create_class(Constructor, protoProps, staticProps) {
25
+ if (protoProps) _defineProperties(Constructor.prototype, protoProps);
26
+ if (staticProps) _defineProperties(Constructor, staticProps);
27
+ return Constructor;
28
+ }
29
+ function _iterable_to_array_limit(arr, i) {
30
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
31
+ if (_i == null) return;
32
+ var _arr = [];
33
+ var _n = true;
34
+ var _d = false;
35
+ var _s, _e;
36
+ try {
37
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
38
+ _arr.push(_s.value);
39
+ if (i && _arr.length === i) break;
40
+ }
41
+ } catch (err) {
42
+ _d = true;
43
+ _e = err;
44
+ } finally{
45
+ try {
46
+ if (!_n && _i["return"] != null) _i["return"]();
47
+ } finally{
48
+ if (_d) throw _e;
49
+ }
50
+ }
51
+ return _arr;
52
+ }
53
+ function _non_iterable_rest() {
54
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
55
+ }
56
+ function _sliced_to_array(arr, i) {
57
+ return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
58
+ }
59
+ function _type_of(obj) {
60
+ "@swc/helpers - typeof";
61
+ return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
62
+ }
63
+ function _unsupported_iterable_to_array(o, minLen) {
64
+ if (!o) return;
65
+ if (typeof o === "string") return _array_like_to_array(o, minLen);
66
+ var n = Object.prototype.toString.call(o).slice(8, -1);
67
+ if (n === "Object" && o.constructor) n = o.constructor.name;
68
+ if (n === "Map" || n === "Set") return Array.from(n);
69
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
70
+ }
71
+ var __defProp = Object.defineProperty;
72
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
73
+ var __getOwnPropNames = Object.getOwnPropertyNames;
74
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
75
+ var __export = function __export(target, all) {
76
+ for(var name in all)__defProp(target, name, {
77
+ get: all[name],
78
+ enumerable: true
79
+ });
80
+ };
81
+ var __copyProps = function __copyProps(to, from, except, desc) {
82
+ if (from && (typeof from === "undefined" ? "undefined" : _type_of(from)) === "object" || typeof from === "function") {
83
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
84
+ try {
85
+ var _loop = function() {
86
+ var key = _step.value;
87
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
88
+ get: function get() {
89
+ return from[key];
90
+ },
91
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
92
+ });
93
+ };
94
+ for(var _iterator = __getOwnPropNames(from)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true)_loop();
95
+ } catch (err) {
96
+ _didIteratorError = true;
97
+ _iteratorError = err;
98
+ } finally{
99
+ try {
100
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
101
+ _iterator.return();
102
+ }
103
+ } finally{
104
+ if (_didIteratorError) {
105
+ throw _iteratorError;
106
+ }
107
+ }
108
+ }
109
+ }
110
+ return to;
111
+ };
112
+ var __toCommonJS = function __toCommonJS(mod) {
113
+ return __copyProps(__defProp({}, "__esModule", {
114
+ value: true
115
+ }), mod);
116
+ };
117
+ // src/player/Scte35CueManager.ts
118
+ var Scte35CueManager_exports = {};
119
+ __export(Scte35CueManager_exports, {
120
+ Scte35CueManager: function Scte35CueManager1() {
121
+ return Scte35CueManager;
122
+ }
123
+ });
124
+ module.exports = __toCommonJS(Scte35CueManager_exports);
125
+ // src/player/Scte35Parser.ts
126
+ function getFragmentStartSeconds(frag) {
127
+ var candidates = [
128
+ frag === null || frag === void 0 ? void 0 : frag.start,
129
+ frag === null || frag === void 0 ? void 0 : frag.startPTS
130
+ ];
131
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
132
+ try {
133
+ for(var _iterator = candidates[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
134
+ var candidate = _step.value;
135
+ if (typeof candidate === "number" && Number.isFinite(candidate)) {
136
+ return candidate;
137
+ }
138
+ }
139
+ } catch (err) {
140
+ _didIteratorError = true;
141
+ _iteratorError = err;
142
+ } finally{
143
+ try {
144
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
145
+ _iterator.return();
146
+ }
147
+ } finally{
148
+ if (_didIteratorError) {
149
+ throw _iteratorError;
150
+ }
151
+ }
152
+ }
153
+ return void 0;
154
+ }
155
+ // src/player/Scte35CueManager.ts
156
+ var Scte35CueManager = /*#__PURE__*/ function() {
157
+ function Scte35CueManager(debug, callbacks) {
158
+ _class_call_check(this, Scte35CueManager);
159
+ this.pendingScte35Cues = /* @__PURE__ */ new Map();
160
+ this.recentScte35CueKeys = /* @__PURE__ */ new Map();
161
+ this.recentScte35CueLimit = 200;
162
+ this.tsScte35Pids = /* @__PURE__ */ new Set();
163
+ this.pmtPids = /* @__PURE__ */ new Set();
164
+ this.processedTsScte35Sections = /* @__PURE__ */ new Set();
165
+ this.debug = debug;
166
+ this.callbacks = callbacks;
167
+ }
168
+ _create_class(Scte35CueManager, [
169
+ {
170
+ key: "pendingScte35CueKey",
171
+ get: function get() {
172
+ return this.pendingScte35CueKeyValue;
173
+ },
174
+ set: function set(value) {
175
+ this.pendingScte35CueKeyValue = value;
176
+ }
177
+ },
178
+ {
179
+ key: "activeScte35BreakKey",
180
+ get: function get() {
181
+ return this.activeScte35BreakKeyValue;
182
+ },
183
+ set: function set(value) {
184
+ this.activeScte35BreakKeyValue = value;
185
+ }
186
+ },
187
+ {
188
+ key: "scheduledScte35BreakKey",
189
+ get: function get() {
190
+ return this.scheduledScte35BreakKeyValue;
191
+ },
192
+ set: function set(value) {
193
+ this.scheduledScte35BreakKeyValue = value;
194
+ }
195
+ },
196
+ {
197
+ key: "createScte35CueContext",
198
+ value: function createScte35CueContext(source, readiness, frag) {
199
+ var context = {
200
+ source: source,
201
+ readiness: readiness
202
+ };
203
+ if (typeof (frag === null || frag === void 0 ? void 0 : frag.sn) === "number") {
204
+ context.fragmentSn = frag.sn;
205
+ }
206
+ var fragmentStartSec = getFragmentStartSeconds(frag);
207
+ if (fragmentStartSec !== void 0) {
208
+ context.fragmentStartSeconds = fragmentStartSec;
209
+ }
210
+ return context;
211
+ }
212
+ },
213
+ {
214
+ key: "onScte35Cue",
215
+ value: function onScte35Cue(marker, context) {
216
+ var cue = this.createScte35Cue(marker, context);
217
+ var pendingKey = this.getPendingScte35CueKey(cue);
218
+ if (context.readiness === "early") {
219
+ this.pendingScte35Cues.set(pendingKey, cue);
220
+ this.trimRecentScte35CueMaps();
221
+ if (marker.type === "start") {
222
+ this.callbacks.onEarlyStartCue(marker, context.fragmentSn, cue.key);
223
+ }
224
+ return;
225
+ }
226
+ if (marker.type === "end" && !this.shouldAcceptScte35EndCue(cue)) {
227
+ if (this.debug) {
228
+ console.log("[StormcloudVideoPlayer] Ignoring SCTE-35 end cue", {
229
+ key: cue.key,
230
+ activeScte35BreakKey: this.activeScte35BreakKeyValue,
231
+ inAdBreak: this.callbacks.isInAdBreak(),
232
+ source: cue.source
233
+ });
234
+ }
235
+ return;
236
+ }
237
+ if (marker.type === "start" && this.hasRecentlyHandledScte35Cue(cue)) {
238
+ if (this.debug) {
239
+ console.log("[StormcloudVideoPlayer] Ignoring duplicate SCTE-35 start cue", {
240
+ key: cue.key,
241
+ source: cue.source
242
+ });
243
+ }
244
+ return;
245
+ }
246
+ this.pendingScte35Cues.delete(pendingKey);
247
+ this.markScte35CueHandled(cue);
248
+ this.callbacks.onPlaybackCue(marker, cue.key);
249
+ }
250
+ },
251
+ {
252
+ key: "activatePendingScte35CuesForFragment",
253
+ value: function activatePendingScte35CuesForFragment(frag) {
254
+ var _this = this;
255
+ var fragmentSn = typeof (frag === null || frag === void 0 ? void 0 : frag.sn) === "number" ? frag.sn : void 0;
256
+ if (fragmentSn === void 0) {
257
+ return;
258
+ }
259
+ var matchingCues = Array.from(this.pendingScte35Cues.entries()).filter(function(param) {
260
+ var _param = _sliced_to_array(param, 2), cue = _param[1];
261
+ return cue.fragmentSn === fragmentSn;
262
+ });
263
+ matchingCues.forEach(function(param) {
264
+ var _param = _sliced_to_array(param, 2), pendingKey = _param[0], cue = _param[1];
265
+ var _cue_fragmentStartSeconds;
266
+ _this.pendingScte35Cues.delete(pendingKey);
267
+ var context = {
268
+ source: cue.source,
269
+ readiness: "playback"
270
+ };
271
+ context.fragmentSn = fragmentSn;
272
+ var fragmentStartSec = (_cue_fragmentStartSeconds = cue.fragmentStartSeconds) !== null && _cue_fragmentStartSeconds !== void 0 ? _cue_fragmentStartSeconds : getFragmentStartSeconds(frag);
273
+ if (fragmentStartSec !== void 0) {
274
+ context.fragmentStartSeconds = fragmentStartSec;
275
+ }
276
+ _this.onScte35Cue(cue.marker, context);
277
+ });
278
+ }
279
+ },
280
+ {
281
+ key: "createScte35Cue",
282
+ value: function createScte35Cue(marker, context) {
283
+ var cue = {
284
+ marker: marker,
285
+ source: context.source,
286
+ readiness: context.readiness,
287
+ key: this.getScte35CueKey(marker, context)
288
+ };
289
+ if (context.fragmentSn !== void 0) {
290
+ cue.fragmentSn = context.fragmentSn;
291
+ }
292
+ if (context.fragmentStartSeconds !== void 0) {
293
+ cue.fragmentStartSeconds = context.fragmentStartSeconds;
294
+ }
295
+ return cue;
296
+ }
297
+ },
298
+ {
299
+ key: "getScte35CueKey",
300
+ value: function getScte35CueKey(marker, context) {
301
+ var _raw_attrs, _raw_attrs1, _raw_attrs2;
302
+ var raw = this.getScte35MarkerRaw(marker);
303
+ var spliceEventId = this.toIdentityString(raw === null || raw === void 0 ? void 0 : raw.splice_event_id);
304
+ if (spliceEventId) {
305
+ return "splice:".concat(spliceEventId);
306
+ }
307
+ var segmentationEventId = this.toIdentityString(raw === null || raw === void 0 ? void 0 : raw.segmentation_event_id);
308
+ if (segmentationEventId) {
309
+ return "segmentation:".concat(segmentationEventId);
310
+ }
311
+ var daterangeId = this.toIdentityString(raw === null || raw === void 0 ? void 0 : (_raw_attrs = raw.attrs) === null || _raw_attrs === void 0 ? void 0 : _raw_attrs.ID);
312
+ if (daterangeId) {
313
+ return "daterange:".concat(daterangeId);
314
+ }
315
+ var scteValue = this.toIdentityString(raw === null || raw === void 0 ? void 0 : (_raw_attrs1 = raw.attrs) === null || _raw_attrs1 === void 0 ? void 0 : _raw_attrs1["SCTE35-OUT"]) || this.toIdentityString(raw === null || raw === void 0 ? void 0 : (_raw_attrs2 = raw.attrs) === null || _raw_attrs2 === void 0 ? void 0 : _raw_attrs2["SCTE35-IN"]);
316
+ if (scteValue) {
317
+ return "daterange-scte:".concat(scteValue);
318
+ }
319
+ if (typeof marker.ptsSeconds === "number" && Number.isFinite(marker.ptsSeconds)) {
320
+ return "pts:".concat(Math.round(marker.ptsSeconds * 2) / 2);
321
+ }
322
+ if (context.fragmentSn !== void 0) {
323
+ return "fragment:".concat(context.fragmentSn);
324
+ }
325
+ return "".concat(context.source, ":wallclock:").concat(Math.floor(Date.now() / 1e3));
326
+ }
327
+ },
328
+ {
329
+ key: "getPendingScte35CueKey",
330
+ value: function getPendingScte35CueKey(cue) {
331
+ return "".concat(cue.marker.type, ":").concat(cue.key, ":").concat(cue.fragmentSn !== void 0 ? cue.fragmentSn : "unknown");
332
+ }
333
+ },
334
+ {
335
+ key: "getScte35MarkerRaw",
336
+ value: function getScte35MarkerRaw(marker) {
337
+ return _type_of(marker.raw) === "object" && marker.raw !== null ? marker.raw : {};
338
+ }
339
+ },
340
+ {
341
+ key: "toIdentityString",
342
+ value: function toIdentityString(value) {
343
+ if (typeof value === "number" && Number.isFinite(value)) {
344
+ return String(value);
345
+ }
346
+ if (typeof value === "string" && value.trim().length > 0) {
347
+ return value.trim();
348
+ }
349
+ return void 0;
350
+ }
351
+ },
352
+ {
353
+ key: "isStrongScte35CueKey",
354
+ value: function isStrongScte35CueKey(key) {
355
+ return !!(key && (key.startsWith("splice:") || key.startsWith("segmentation:") || key.startsWith("daterange:") || key.startsWith("daterange-scte:")));
356
+ }
357
+ },
358
+ {
359
+ key: "hasRecentlyHandledScte35Cue",
360
+ value: function hasRecentlyHandledScte35Cue(cue) {
361
+ if (this.activeScte35BreakKeyValue === cue.key || this.scheduledScte35BreakKeyValue === cue.key) {
362
+ return true;
363
+ }
364
+ var recentKey = "".concat(cue.marker.type, ":").concat(cue.key);
365
+ return this.recentScte35CueKeys.has(recentKey);
366
+ }
367
+ },
368
+ {
369
+ key: "markScte35CueHandled",
370
+ value: function markScte35CueHandled(cue) {
371
+ this.recentScte35CueKeys.set("".concat(cue.marker.type, ":").concat(cue.key), Date.now());
372
+ this.trimRecentScte35CueMaps();
373
+ }
374
+ },
375
+ {
376
+ key: "trimRecentScte35CueMaps",
377
+ value: function trimRecentScte35CueMaps() {
378
+ while(this.recentScte35CueKeys.size > this.recentScte35CueLimit){
379
+ var firstKey = this.recentScte35CueKeys.keys().next().value;
380
+ if (!firstKey) {
381
+ break;
382
+ }
383
+ this.recentScte35CueKeys.delete(firstKey);
384
+ }
385
+ while(this.pendingScte35Cues.size > this.recentScte35CueLimit){
386
+ var firstKey1 = this.pendingScte35Cues.keys().next().value;
387
+ if (!firstKey1) {
388
+ break;
389
+ }
390
+ this.pendingScte35Cues.delete(firstKey1);
391
+ }
392
+ }
393
+ },
394
+ {
395
+ key: "shouldAcceptScte35EndCue",
396
+ value: function shouldAcceptScte35EndCue(cue) {
397
+ if (!this.callbacks.isInAdBreak()) {
398
+ return false;
399
+ }
400
+ if (this.isStrongScte35CueKey(this.activeScte35BreakKeyValue) && this.isStrongScte35CueKey(cue.key) && this.activeScte35BreakKeyValue !== cue.key) {
401
+ return false;
402
+ }
403
+ return true;
404
+ }
405
+ }
406
+ ]);
407
+ return Scte35CueManager;
408
+ }();
409
+ // Annotate the CommonJS export names for ESM import in node:
410
+ 0 && (module.exports = {
411
+ Scte35CueManager: Scte35CueManager
412
+ });
413
+ //# sourceMappingURL=Scte35CueManager.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/player/Scte35CueManager.cjs","../../src/player/Scte35CueManager.ts","../../src/player/Scte35Parser.ts"],"names":["__defProp","Object","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toCommonJS","mod","value","Scte35CueManager_exports","Scte35CueManager","frag","module","exports","getFragmentStartSeconds","candidates","start","startPTS","candidate","Number","isFinite","debug","callbacks","pendingScte35Cues","Map","recentScte35CueKeys","recentScte35CueLimit","tsScte35Pids","Set","pmtPids","processedTsScte35Sections","pendingScte35CueKey","pendingScte35CueKeyValue","activeScte35BreakKey","activeScte35BreakKeyValue","scheduledScte35BreakKey","scheduledScte35BreakKeyValue","createScte35CueContext","source","readiness","context","sn","fragmentSn","fragmentStartSec","fragmentStartSeconds","onScte35Cue","marker","cue","createScte35Cue","pendingKey","getPendingScte35CueKey","set","trimRecentScte35CueMaps","type","onEarlyStartCue","shouldAcceptScte35EndCue","console","log"],"mappings":"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","sourcesContent":["\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/player/Scte35CueManager.ts\nvar Scte35CueManager_exports = {};\n__export(Scte35CueManager_exports, {\n Scte35CueManager: () => Scte35CueManager\n});\nmodule.exports = __toCommonJS(Scte35CueManager_exports);\n\n// src/player/Scte35Parser.ts\nfunction getFragmentStartSeconds(frag) {\n const candidates = [frag?.start, frag?.startPTS];\n for (const candidate of candidates) {\n if (typeof candidate === \"number\" && Number.isFinite(candidate)) {\n return candidate;\n }\n }\n return void 0;\n}\n\n// src/player/Scte35CueManager.ts\nvar Scte35CueManager = class {\n constructor(debug, callbacks) {\n this.pendingScte35Cues = /* @__PURE__ */ new Map();\n this.recentScte35CueKeys = /* @__PURE__ */ new Map();\n this.recentScte35CueLimit = 200;\n this.tsScte35Pids = /* @__PURE__ */ new Set();\n this.pmtPids = /* @__PURE__ */ new Set();\n this.processedTsScte35Sections = /* @__PURE__ */ new Set();\n this.debug = debug;\n this.callbacks = callbacks;\n }\n get pendingScte35CueKey() {\n return this.pendingScte35CueKeyValue;\n }\n set pendingScte35CueKey(value) {\n this.pendingScte35CueKeyValue = value;\n }\n get activeScte35BreakKey() {\n return this.activeScte35BreakKeyValue;\n }\n set activeScte35BreakKey(value) {\n this.activeScte35BreakKeyValue = value;\n }\n get scheduledScte35BreakKey() {\n return this.scheduledScte35BreakKeyValue;\n }\n set scheduledScte35BreakKey(value) {\n this.scheduledScte35BreakKeyValue = value;\n }\n createScte35CueContext(source, readiness, frag) {\n const context = { source, readiness };\n if (typeof frag?.sn === \"number\") {\n context.fragmentSn = frag.sn;\n }\n const fragmentStartSec = getFragmentStartSeconds(frag);\n if (fragmentStartSec !== void 0) {\n context.fragmentStartSeconds = fragmentStartSec;\n }\n return context;\n }\n onScte35Cue(marker, context) {\n const cue = this.createScte35Cue(marker, context);\n const pendingKey = this.getPendingScte35CueKey(cue);\n if (context.readiness === \"early\") {\n this.pendingScte35Cues.set(pendingKey, cue);\n this.trimRecentScte35CueMaps();\n if (marker.type === \"start\") {\n this.callbacks.onEarlyStartCue(marker, context.fragmentSn, cue.key);\n }\n return;\n }\n if (marker.type === \"end\" && !this.shouldAcceptScte35EndCue(cue)) {\n if (this.debug) {\n console.log(\"[StormcloudVideoPlayer] Ignoring SCTE-35 end cue\", {\n key: cue.key,\n activeScte35BreakKey: this.activeScte35BreakKeyValue,\n inAdBreak: this.callbacks.isInAdBreak(),\n source: cue.source\n });\n }\n return;\n }\n if (marker.type === \"start\" && this.hasRecentlyHandledScte35Cue(cue)) {\n if (this.debug) {\n console.log(\"[StormcloudVideoPlayer] Ignoring duplicate SCTE-35 start cue\", {\n key: cue.key,\n source: cue.source\n });\n }\n return;\n }\n this.pendingScte35Cues.delete(pendingKey);\n this.markScte35CueHandled(cue);\n this.callbacks.onPlaybackCue(marker, cue.key);\n }\n activatePendingScte35CuesForFragment(frag) {\n const fragmentSn = typeof frag?.sn === \"number\" ? frag.sn : void 0;\n if (fragmentSn === void 0) {\n return;\n }\n const matchingCues = Array.from(this.pendingScte35Cues.entries()).filter(\n ([, cue]) => cue.fragmentSn === fragmentSn\n );\n matchingCues.forEach(([pendingKey, cue]) => {\n this.pendingScte35Cues.delete(pendingKey);\n const context = {\n source: cue.source,\n readiness: \"playback\"\n };\n context.fragmentSn = fragmentSn;\n const fragmentStartSec = cue.fragmentStartSeconds ?? getFragmentStartSeconds(frag);\n if (fragmentStartSec !== void 0) {\n context.fragmentStartSeconds = fragmentStartSec;\n }\n this.onScte35Cue(cue.marker, context);\n });\n }\n createScte35Cue(marker, context) {\n const cue = {\n marker,\n source: context.source,\n readiness: context.readiness,\n key: this.getScte35CueKey(marker, context)\n };\n if (context.fragmentSn !== void 0) {\n cue.fragmentSn = context.fragmentSn;\n }\n if (context.fragmentStartSeconds !== void 0) {\n cue.fragmentStartSeconds = context.fragmentStartSeconds;\n }\n return cue;\n }\n getScte35CueKey(marker, context) {\n const raw = this.getScte35MarkerRaw(marker);\n const spliceEventId = this.toIdentityString(raw?.splice_event_id);\n if (spliceEventId) {\n return `splice:${spliceEventId}`;\n }\n const segmentationEventId = this.toIdentityString(raw?.segmentation_event_id);\n if (segmentationEventId) {\n return `segmentation:${segmentationEventId}`;\n }\n const daterangeId = this.toIdentityString(raw?.attrs?.ID);\n if (daterangeId) {\n return `daterange:${daterangeId}`;\n }\n const scteValue = this.toIdentityString(raw?.attrs?.[\"SCTE35-OUT\"]) || this.toIdentityString(raw?.attrs?.[\"SCTE35-IN\"]);\n if (scteValue) {\n return `daterange-scte:${scteValue}`;\n }\n if (typeof marker.ptsSeconds === \"number\" && Number.isFinite(marker.ptsSeconds)) {\n return `pts:${Math.round(marker.ptsSeconds * 2) / 2}`;\n }\n if (context.fragmentSn !== void 0) {\n return `fragment:${context.fragmentSn}`;\n }\n return `${context.source}:wallclock:${Math.floor(Date.now() / 1e3)}`;\n }\n getPendingScte35CueKey(cue) {\n return `${cue.marker.type}:${cue.key}:${cue.fragmentSn !== void 0 ? cue.fragmentSn : \"unknown\"}`;\n }\n getScte35MarkerRaw(marker) {\n return typeof marker.raw === \"object\" && marker.raw !== null ? marker.raw : {};\n }\n toIdentityString(value) {\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return String(value);\n }\n if (typeof value === \"string\" && value.trim().length > 0) {\n return value.trim();\n }\n return void 0;\n }\n isStrongScte35CueKey(key) {\n return !!(key && (key.startsWith(\"splice:\") || key.startsWith(\"segmentation:\") || key.startsWith(\"daterange:\") || key.startsWith(\"daterange-scte:\")));\n }\n hasRecentlyHandledScte35Cue(cue) {\n if (this.activeScte35BreakKeyValue === cue.key || this.scheduledScte35BreakKeyValue === cue.key) {\n return true;\n }\n const recentKey = `${cue.marker.type}:${cue.key}`;\n return this.recentScte35CueKeys.has(recentKey);\n }\n markScte35CueHandled(cue) {\n this.recentScte35CueKeys.set(`${cue.marker.type}:${cue.key}`, Date.now());\n this.trimRecentScte35CueMaps();\n }\n trimRecentScte35CueMaps() {\n while (this.recentScte35CueKeys.size > this.recentScte35CueLimit) {\n const firstKey = this.recentScte35CueKeys.keys().next().value;\n if (!firstKey) {\n break;\n }\n this.recentScte35CueKeys.delete(firstKey);\n }\n while (this.pendingScte35Cues.size > this.recentScte35CueLimit) {\n const firstKey = this.pendingScte35Cues.keys().next().value;\n if (!firstKey) {\n break;\n }\n this.pendingScte35Cues.delete(firstKey);\n }\n }\n shouldAcceptScte35EndCue(cue) {\n if (!this.callbacks.isInAdBreak()) {\n return false;\n }\n if (this.isStrongScte35CueKey(this.activeScte35BreakKeyValue) && this.isStrongScte35CueKey(cue.key) && this.activeScte35BreakKeyValue !== cue.key) {\n return false;\n }\n return true;\n }\n};\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n Scte35CueManager\n});\n","import type { Scte35Marker } from \"../types\";\nimport type {\n NormalizedScte35Cue,\n Scte35CueContext,\n Scte35CueSource,\n Scte35CueReadiness,\n} from \"./playerTypes\";\nimport { getFragmentStartSeconds } from \"./Scte35Parser\";\n\nexport interface Scte35CueManagerCallbacks {\n onEarlyStartCue(marker: Scte35Marker, fragmentSn: number | undefined, cueKey: string): void;\n onPlaybackCue(marker: Scte35Marker, cueKey?: string): void;\n isInAdBreak(): boolean;\n}\n\nexport class Scte35CueManager {\n private readonly pendingScte35Cues: Map<string, NormalizedScte35Cue> = new Map();\n private readonly recentScte35CueKeys: Map<string, number> = new Map();\n private readonly recentScte35CueLimit: number = 200;\n private pendingScte35CueKeyValue: string | undefined;\n private activeScte35BreakKeyValue: string | undefined;\n private scheduledScte35BreakKeyValue: string | undefined;\n\n readonly tsScte35Pids: Set<number> = new Set();\n readonly pmtPids: Set<number> = new Set();\n readonly processedTsScte35Sections: Set<string> = new Set();\n\n private debug: boolean;\n private callbacks: Scte35CueManagerCallbacks;\n\n constructor(debug: boolean, callbacks: Scte35CueManagerCallbacks) {\n this.debug = debug;\n this.callbacks = callbacks;\n }\n\n get pendingScte35CueKey(): string | undefined {\n return this.pendingScte35CueKeyValue;\n }\n set pendingScte35CueKey(value: string | undefined) {\n this.pendingScte35CueKeyValue = value;\n }\n\n get activeScte35BreakKey(): string | undefined {\n return this.activeScte35BreakKeyValue;\n }\n set activeScte35BreakKey(value: string | undefined) {\n this.activeScte35BreakKeyValue = value;\n }\n\n get scheduledScte35BreakKey(): string | undefined {\n return this.scheduledScte35BreakKeyValue;\n }\n set scheduledScte35BreakKey(value: string | undefined) {\n this.scheduledScte35BreakKeyValue = value;\n }\n\n createScte35CueContext(\n source: Scte35CueSource,\n readiness: Scte35CueReadiness,\n frag?: any\n ): Scte35CueContext {\n const context: Scte35CueContext = { source, readiness };\n if (typeof frag?.sn === \"number\") {\n context.fragmentSn = frag.sn;\n }\n const fragmentStartSec = getFragmentStartSeconds(frag);\n if (fragmentStartSec !== undefined) {\n context.fragmentStartSeconds = fragmentStartSec;\n }\n return context;\n }\n\n onScte35Cue(marker: Scte35Marker, context: Scte35CueContext): void {\n const cue = this.createScte35Cue(marker, context);\n const pendingKey = this.getPendingScte35CueKey(cue);\n\n if (context.readiness === \"early\") {\n this.pendingScte35Cues.set(pendingKey, cue);\n this.trimRecentScte35CueMaps();\n\n if (marker.type === \"start\") {\n this.callbacks.onEarlyStartCue(marker, context.fragmentSn, cue.key);\n }\n return;\n }\n\n if (marker.type === \"end\" && !this.shouldAcceptScte35EndCue(cue)) {\n if (this.debug) {\n console.log(\"[StormcloudVideoPlayer] Ignoring SCTE-35 end cue\", {\n key: cue.key,\n activeScte35BreakKey: this.activeScte35BreakKeyValue,\n inAdBreak: this.callbacks.isInAdBreak(),\n source: cue.source,\n });\n }\n return;\n }\n\n if (marker.type === \"start\" && this.hasRecentlyHandledScte35Cue(cue)) {\n if (this.debug) {\n console.log(\"[StormcloudVideoPlayer] Ignoring duplicate SCTE-35 start cue\", {\n key: cue.key,\n source: cue.source,\n });\n }\n return;\n }\n\n this.pendingScte35Cues.delete(pendingKey);\n this.markScte35CueHandled(cue);\n this.callbacks.onPlaybackCue(marker, cue.key);\n }\n\n activatePendingScte35CuesForFragment(frag: any): void {\n const fragmentSn = typeof frag?.sn === \"number\" ? frag.sn : undefined;\n if (fragmentSn === undefined) {\n return;\n }\n\n const matchingCues = Array.from(this.pendingScte35Cues.entries()).filter(\n ([, cue]) => cue.fragmentSn === fragmentSn\n );\n\n matchingCues.forEach(([pendingKey, cue]) => {\n this.pendingScte35Cues.delete(pendingKey);\n const context: Scte35CueContext = {\n source: cue.source,\n readiness: \"playback\",\n };\n context.fragmentSn = fragmentSn;\n const fragmentStartSec =\n cue.fragmentStartSeconds ?? getFragmentStartSeconds(frag);\n if (fragmentStartSec !== undefined) {\n context.fragmentStartSeconds = fragmentStartSec;\n }\n this.onScte35Cue(cue.marker, context);\n });\n }\n\n private createScte35Cue(\n marker: Scte35Marker,\n context: Scte35CueContext\n ): NormalizedScte35Cue {\n const cue: NormalizedScte35Cue = {\n marker,\n source: context.source,\n readiness: context.readiness,\n key: this.getScte35CueKey(marker, context),\n };\n if (context.fragmentSn !== undefined) {\n cue.fragmentSn = context.fragmentSn;\n }\n if (context.fragmentStartSeconds !== undefined) {\n cue.fragmentStartSeconds = context.fragmentStartSeconds;\n }\n return cue;\n }\n\n private getScte35CueKey(\n marker: Scte35Marker,\n context: Scte35CueContext\n ): string {\n const raw = this.getScte35MarkerRaw(marker);\n const spliceEventId = this.toIdentityString(raw?.splice_event_id);\n if (spliceEventId) {\n return `splice:${spliceEventId}`;\n }\n\n const segmentationEventId = this.toIdentityString(raw?.segmentation_event_id);\n if (segmentationEventId) {\n return `segmentation:${segmentationEventId}`;\n }\n\n const daterangeId = this.toIdentityString(raw?.attrs?.ID);\n if (daterangeId) {\n return `daterange:${daterangeId}`;\n }\n\n const scteValue =\n this.toIdentityString(raw?.attrs?.[\"SCTE35-OUT\"]) ||\n this.toIdentityString(raw?.attrs?.[\"SCTE35-IN\"]);\n if (scteValue) {\n return `daterange-scte:${scteValue}`;\n }\n\n if (typeof marker.ptsSeconds === \"number\" && Number.isFinite(marker.ptsSeconds)) {\n return `pts:${Math.round(marker.ptsSeconds * 2) / 2}`;\n }\n\n if (context.fragmentSn !== undefined) {\n return `fragment:${context.fragmentSn}`;\n }\n\n return `${context.source}:wallclock:${Math.floor(Date.now() / 1000)}`;\n }\n\n private getPendingScte35CueKey(cue: NormalizedScte35Cue): string {\n return `${cue.marker.type}:${cue.key}:${\n cue.fragmentSn !== undefined ? cue.fragmentSn : \"unknown\"\n }`;\n }\n\n private getScte35MarkerRaw(marker: Scte35Marker): any {\n return typeof marker.raw === \"object\" && marker.raw !== null\n ? (marker.raw as any)\n : {};\n }\n\n private toIdentityString(value: unknown): string | undefined {\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return String(value);\n }\n if (typeof value === \"string\" && value.trim().length > 0) {\n return value.trim();\n }\n return undefined;\n }\n\n isStrongScte35CueKey(key: string | undefined): boolean {\n return !!(\n key &&\n (key.startsWith(\"splice:\") ||\n key.startsWith(\"segmentation:\") ||\n key.startsWith(\"daterange:\") ||\n key.startsWith(\"daterange-scte:\"))\n );\n }\n\n private hasRecentlyHandledScte35Cue(cue: NormalizedScte35Cue): boolean {\n if (\n this.activeScte35BreakKeyValue === cue.key ||\n this.scheduledScte35BreakKeyValue === cue.key\n ) {\n return true;\n }\n\n const recentKey = `${cue.marker.type}:${cue.key}`;\n return this.recentScte35CueKeys.has(recentKey);\n }\n\n private markScte35CueHandled(cue: NormalizedScte35Cue): void {\n this.recentScte35CueKeys.set(`${cue.marker.type}:${cue.key}`, Date.now());\n this.trimRecentScte35CueMaps();\n }\n\n private trimRecentScte35CueMaps(): void {\n while (this.recentScte35CueKeys.size > this.recentScte35CueLimit) {\n const firstKey = this.recentScte35CueKeys.keys().next().value;\n if (!firstKey) {\n break;\n }\n this.recentScte35CueKeys.delete(firstKey);\n }\n\n while (this.pendingScte35Cues.size > this.recentScte35CueLimit) {\n const firstKey = this.pendingScte35Cues.keys().next().value;\n if (!firstKey) {\n break;\n }\n this.pendingScte35Cues.delete(firstKey);\n }\n }\n\n private shouldAcceptScte35EndCue(cue: NormalizedScte35Cue): boolean {\n if (!this.callbacks.isInAdBreak()) {\n return false;\n }\n\n if (\n this.isStrongScte35CueKey(this.activeScte35BreakKeyValue) &&\n this.isStrongScte35CueKey(cue.key) &&\n this.activeScte35BreakKeyValue !== cue.key\n ) {\n return false;\n }\n\n return true;\n }\n}\n","import type { Scte35Marker, Id3TagInfo } from \"../types\";\n\nexport function parseCueOutDuration(value: string): number | undefined {\n const num = parseFloat(value.trim());\n if (!Number.isNaN(num)) return num;\n const match =\n value.match(/(?:^|[,\\s])DURATION\\s*=\\s*([0-9.]+)/i) ||\n value.match(/Duration\\s*=\\s*([0-9.]+)/i);\n if (match && match[1] != null) {\n const dStr = match[1];\n const d = parseFloat(dStr);\n return Number.isNaN(d) ? undefined : d;\n }\n return undefined;\n}\n\nexport function parseCueOutCont(\n value: string\n): { elapsed?: number; duration?: number } | undefined {\n const res: { elapsed?: number; duration?: number } = {};\n\n const elapsedMatch = value.match(/Elapsed\\s*=\\s*([0-9.]+)/i);\n const durationMatch = value.match(/Duration\\s*=\\s*([0-9.]+)/i);\n if (elapsedMatch && elapsedMatch[1] != null) {\n const e = parseFloat(elapsedMatch[1]);\n if (!Number.isNaN(e)) res.elapsed = e;\n }\n if (durationMatch && durationMatch[1] != null) {\n const d = parseFloat(durationMatch[1]);\n if (!Number.isNaN(d)) res.duration = d;\n }\n\n if (!(\"elapsed\" in res) || !(\"duration\" in res)) {\n const slashMatch = value.match(/([0-9.]+)\\s*\\/\\s*([0-9.]+)/);\n if (slashMatch && slashMatch[1] && slashMatch[2]) {\n const elapsed = parseFloat(slashMatch[1]);\n const duration = parseFloat(slashMatch[2]);\n if (!Number.isNaN(elapsed) && !(\"elapsed\" in res)) res.elapsed = elapsed;\n if (!Number.isNaN(duration) && !(\"duration\" in res)) res.duration = duration;\n }\n }\n\n if (\"elapsed\" in res || \"duration\" in res) return res;\n return undefined;\n}\n\nexport function parseAttributeList(value: string): Record<string, string> {\n const attrs: Record<string, string> = {};\n const regex = /([A-Z0-9-]+)=((\"[^\"]*\")|([^\",]*))(?:,|$)/gi;\n let match: RegExpExecArray | null;\n while ((match = regex.exec(value)) !== null) {\n const key: string = (match[1] ?? \"\") as string;\n let rawVal: string = (match[3] ?? match[4] ?? \"\") as string;\n if (rawVal.startsWith('\"') && rawVal.endsWith('\"')) {\n rawVal = rawVal.slice(1, -1);\n }\n if (key) {\n attrs[key] = rawVal;\n }\n }\n return attrs;\n}\n\nexport function toNumber(val: unknown): number | undefined {\n if (val == null) return undefined;\n const n = typeof val === \"string\" ? parseFloat(val) : Number(val);\n return Number.isNaN(n) ? undefined : n;\n}\n\nexport function isManifestBasedMarker(marker: Scte35Marker): boolean {\n const raw = marker.raw as any;\n if (!raw) return false;\n\n if (raw.tag) {\n const tag = String(raw.tag);\n return (\n tag.includes(\"EXT-X-CUE-OUT\") ||\n tag.includes(\"EXT-X-CUE-IN\") ||\n tag.includes(\"EXT-X-DATERANGE\")\n );\n }\n\n if (raw.id3) return false;\n\n if (raw.splice_command_type) return false;\n\n return false;\n}\n\nexport function decodeId3ValueToText(value: string | Uint8Array): string | undefined {\n try {\n if (typeof value === \"string\") return value;\n const decoder = new TextDecoder(\"utf-8\", { fatal: false });\n const text = decoder.decode(value);\n if (text && /[\\x20-\\x7E]/.test(text)) return text;\n let out = \"\";\n for (let i = 0; i < value.length; i++)\n out += String.fromCharCode(value[i]!);\n return out;\n } catch {\n return undefined;\n }\n}\n\nexport function getFragmentStartSeconds(frag: any): number | undefined {\n const candidates = [frag?.start, frag?.startPTS];\n for (const candidate of candidates) {\n if (typeof candidate === \"number\" && Number.isFinite(candidate)) {\n return candidate;\n }\n }\n return undefined;\n}\n\nexport function parseScte35FromId3(tag: Id3TagInfo): Scte35Marker | undefined {\n const text = decodeId3ValueToText(tag.value);\n if (!text) return undefined;\n\n const cueOutMatch =\n text.match(/EXT-X-CUE-OUT(?::([^\\r\\n]*))?/i) ||\n text.match(/CUE-OUT(?::([^\\r\\n]*))?/i);\n if (cueOutMatch) {\n const arg = (cueOutMatch[1] ?? \"\").trim();\n const dur = parseCueOutDuration(arg);\n const marker: Scte35Marker = {\n type: \"start\",\n ...(tag.ptsSeconds !== undefined ? { ptsSeconds: tag.ptsSeconds } : {}),\n ...(dur !== undefined ? { durationSeconds: dur } : {}),\n raw: { id3: text },\n } as Scte35Marker;\n return marker;\n }\n\n const cueOutContMatch = text.match(/EXT-X-CUE-OUT-CONT:([^\\r\\n]*)/i);\n if (cueOutContMatch) {\n const arg = (cueOutContMatch[1] ?? \"\").trim();\n const cont = parseCueOutCont(arg);\n const marker: Scte35Marker = {\n type: \"progress\",\n ...(tag.ptsSeconds !== undefined ? { ptsSeconds: tag.ptsSeconds } : {}),\n ...(cont?.duration !== undefined\n ? { durationSeconds: cont.duration }\n : {}),\n raw: { id3: text },\n } as Scte35Marker;\n return marker;\n }\n\n const cueInMatch = text.match(/EXT-X-CUE-IN\\b/i) || text.match(/CUE-IN\\b/i);\n if (cueInMatch) {\n const marker: Scte35Marker = {\n type: \"end\",\n ...(tag.ptsSeconds !== undefined ? { ptsSeconds: tag.ptsSeconds } : {}),\n raw: { id3: text },\n } as Scte35Marker;\n return marker;\n }\n\n const daterangeMatch = text.match(/EXT-X-DATERANGE:([^\\r\\n]*)/i);\n if (daterangeMatch) {\n const attrs = parseAttributeList(daterangeMatch[1] ?? \"\");\n const hasScteOut =\n \"SCTE35-OUT\" in attrs || attrs[\"SCTE35-OUT\"] !== undefined;\n const hasScteIn =\n \"SCTE35-IN\" in attrs || attrs[\"SCTE35-IN\"] !== undefined;\n const klass = String(attrs[\"CLASS\"] ?? \"\");\n const duration = toNumber(attrs[\"DURATION\"]);\n if (hasScteOut || /com\\.apple\\.hls\\.cue/i.test(klass)) {\n const marker: Scte35Marker = {\n type: \"start\",\n ...(tag.ptsSeconds !== undefined\n ? { ptsSeconds: tag.ptsSeconds }\n : {}),\n ...(duration !== undefined ? { durationSeconds: duration } : {}),\n raw: { id3: text, attrs },\n } as Scte35Marker;\n return marker;\n }\n if (hasScteIn) {\n const marker: Scte35Marker = {\n type: \"end\",\n ...(tag.ptsSeconds !== undefined\n ? { ptsSeconds: tag.ptsSeconds }\n : {}),\n raw: { id3: text, attrs },\n } as Scte35Marker;\n return marker;\n }\n }\n\n if (/SCTE35-OUT/i.test(text)) {\n const marker: Scte35Marker = {\n type: \"start\",\n ...(tag.ptsSeconds !== undefined ? { ptsSeconds: tag.ptsSeconds } : {}),\n raw: { id3: text },\n } as Scte35Marker;\n return marker;\n }\n if (/SCTE35-IN/i.test(text)) {\n const marker: Scte35Marker = {\n type: \"end\",\n ...(tag.ptsSeconds !== undefined ? { ptsSeconds: tag.ptsSeconds } : {}),\n raw: { id3: text },\n } as Scte35Marker;\n return marker;\n }\n\n if (tag.value instanceof Uint8Array) {\n const bin = parseScte35Binary(tag.value);\n if (bin) return bin;\n }\n\n return undefined;\n}\n\nexport function normalizeFragmentPayload(payload: unknown): Uint8Array | undefined {\n if (payload instanceof Uint8Array) {\n return payload;\n }\n if (payload instanceof ArrayBuffer) {\n return new Uint8Array(payload);\n }\n if (ArrayBuffer.isView(payload)) {\n const view = payload as ArrayBufferView;\n return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);\n }\n return undefined;\n}\n\n// --- Transport Stream SCTE-35 parsing ---\n\nexport function detectTsPacketFormat(\n data: Uint8Array\n): { offset: number; packetSize: number; tsPayloadOffset: number } | undefined {\n const packetSizes = [188, 192, 204];\n for (const packetSize of packetSizes) {\n const tsPayloadOffset = packetSize === 192 ? 4 : 0;\n for (let offset = 0; offset < packetSize; offset++) {\n let matchedPackets = 0;\n for (\n let packetStart = offset;\n packetStart + tsPayloadOffset < data.length &&\n matchedPackets < 5;\n packetStart += packetSize\n ) {\n if (data[packetStart + tsPayloadOffset] !== 0x47) {\n break;\n }\n matchedPackets++;\n }\n if (matchedPackets >= 2) {\n return { offset, packetSize, tsPayloadOffset };\n }\n }\n }\n return undefined;\n}\n\nfunction getPsiSectionStart(\n payload: Uint8Array,\n payloadUnitStart: boolean\n): number | undefined {\n if (!payloadUnitStart) {\n return undefined;\n }\n const pointerField = payload[0];\n if (pointerField == null) {\n return undefined;\n }\n const sectionStart = 1 + pointerField;\n return sectionStart < payload.length ? sectionStart : undefined;\n}\n\nfunction getPsiSectionLength(data: Uint8Array, offset: number): number {\n return (((data[offset + 1] ?? 0) & 0x0f) << 8) | (data[offset + 2] ?? 0);\n}\n\nfunction parsePatSection(\n payload: Uint8Array,\n payloadUnitStart: boolean,\n pmtPids: Set<number>\n): void {\n const sectionStart = getPsiSectionStart(payload, payloadUnitStart);\n if (sectionStart == null || sectionStart + 8 >= payload.length) {\n return;\n }\n if (payload[sectionStart] !== 0x00) {\n return;\n }\n\n const sectionLength = getPsiSectionLength(payload, sectionStart);\n const sectionEnd = Math.min(\n payload.length,\n sectionStart + 3 + sectionLength\n );\n\n for (let offset = sectionStart + 8; offset + 4 <= sectionEnd - 4; offset += 4) {\n const programNumber =\n ((payload[offset] ?? 0) << 8) | (payload[offset + 1] ?? 0);\n const pid =\n (((payload[offset + 2] ?? 0) & 0x1f) << 8) |\n (payload[offset + 3] ?? 0);\n if (programNumber !== 0) {\n pmtPids.add(pid);\n }\n }\n}\n\nfunction descriptorsIdentifyScte35(\n data: Uint8Array,\n start: number,\n end: number\n): boolean {\n let offset = start;\n while (offset + 2 <= end) {\n const tag = data[offset] ?? 0;\n const length = data[offset + 1] ?? 0;\n const descriptorStart = offset + 2;\n const descriptorEnd = descriptorStart + length;\n if (descriptorEnd > end) {\n return false;\n }\n\n const registration =\n length >= 4\n ? String.fromCharCode(\n data[descriptorStart] ?? 0,\n data[descriptorStart + 1] ?? 0,\n data[descriptorStart + 2] ?? 0,\n data[descriptorStart + 3] ?? 0\n )\n : \"\";\n if (tag === 0x8a || registration === \"CUEI\" || registration === \"SCTE\") {\n return true;\n }\n\n offset = descriptorEnd;\n }\n return false;\n}\n\nfunction parsePmtSection(\n payload: Uint8Array,\n payloadUnitStart: boolean,\n tsScte35Pids: Set<number>\n): void {\n const sectionStart = getPsiSectionStart(payload, payloadUnitStart);\n if (sectionStart == null || sectionStart + 12 >= payload.length) {\n return;\n }\n if (payload[sectionStart] !== 0x02) {\n return;\n }\n\n const sectionLength = getPsiSectionLength(payload, sectionStart);\n const sectionEnd = Math.min(\n payload.length,\n sectionStart + 3 + sectionLength\n );\n const programInfoLength =\n (((payload[sectionStart + 10] ?? 0) & 0x0f) << 8) |\n (payload[sectionStart + 11] ?? 0);\n\n let offset = sectionStart + 12 + programInfoLength;\n while (offset + 5 <= sectionEnd - 4) {\n const streamType = payload[offset] ?? 0;\n const elementaryPid =\n (((payload[offset + 1] ?? 0) & 0x1f) << 8) |\n (payload[offset + 2] ?? 0);\n const esInfoLength =\n (((payload[offset + 3] ?? 0) & 0x0f) << 8) |\n (payload[offset + 4] ?? 0);\n const descriptorStart = offset + 5;\n const descriptorEnd = Math.min(descriptorStart + esInfoLength, sectionEnd - 4);\n\n if (\n streamType === 0x86 ||\n (streamType === 0x06 &&\n descriptorsIdentifyScte35(payload, descriptorStart, descriptorEnd))\n ) {\n tsScte35Pids.add(elementaryPid);\n }\n\n offset = descriptorStart + esInfoLength;\n }\n}\n\nfunction hasProcessedTsScte35Section(\n section: Uint8Array,\n processedSections: Set<string>\n): boolean {\n let checksum = 0;\n for (let i = 0; i < section.length; i++) {\n checksum = (checksum * 31 + (section[i] ?? 0)) >>> 0;\n }\n\n const key = `${section.length}:${checksum}`;\n if (processedSections.has(key)) {\n return true;\n }\n\n processedSections.add(key);\n if (processedSections.size > 200) {\n const firstKey = processedSections.values().next().value;\n if (firstKey) {\n processedSections.delete(firstKey);\n }\n }\n return false;\n}\n\nfunction addScte35SectionMarker(\n section: Uint8Array,\n markers: Scte35Marker[],\n processedSections: Set<string>\n): void {\n if (section[0] !== 0xfc || hasProcessedTsScte35Section(section, processedSections)) {\n return;\n }\n\n const marker = parseScte35Binary(section);\n if (!marker) {\n return;\n }\n\n marker.raw = {\n ...(typeof marker.raw === \"object\" && marker.raw ? marker.raw : {}),\n source: \"ts-packet\",\n };\n markers.push(marker);\n}\n\nfunction appendScte35Bytes(\n pid: number,\n bytes: Uint8Array,\n assemblers: Map<number, { bytes: number[]; expectedLength: number }>,\n markers: Scte35Marker[],\n processedSections: Set<string>\n): void {\n const assembler = assemblers.get(pid);\n if (!assembler) {\n return;\n }\n\n for (let i = 0; i < bytes.length && assembler.bytes.length < assembler.expectedLength; i++) {\n assembler.bytes.push(bytes[i] ?? 0);\n }\n\n if (assembler.bytes.length >= assembler.expectedLength) {\n assemblers.delete(pid);\n addScte35SectionMarker(new Uint8Array(assembler.bytes), markers, processedSections);\n }\n}\n\nfunction collectScte35SectionsFromPayload(\n pid: number,\n payload: Uint8Array,\n payloadUnitStart: boolean,\n assemblers: Map<number, { bytes: number[]; expectedLength: number }>,\n markers: Scte35Marker[],\n processedSections: Set<string>\n): void {\n if (payload.length === 0) {\n return;\n }\n\n let offset = 0;\n if (payloadUnitStart) {\n const pointerField = payload[0] ?? 0;\n const previousAssembler = assemblers.get(pid);\n if (previousAssembler && pointerField > 0) {\n appendScte35Bytes(\n pid,\n payload.subarray(1, Math.min(1 + pointerField, payload.length)),\n assemblers,\n markers,\n processedSections\n );\n }\n assemblers.delete(pid);\n offset = 1 + pointerField;\n } else {\n appendScte35Bytes(pid, payload, assemblers, markers, processedSections);\n return;\n }\n\n while (offset + 3 <= payload.length) {\n const tableId = payload[offset];\n if (tableId == null || tableId === 0xff) {\n return;\n }\n\n const sectionLength = getPsiSectionLength(payload, offset);\n const totalLength = 3 + sectionLength;\n if (sectionLength <= 0 || totalLength <= 3) {\n return;\n }\n\n if (offset + totalLength <= payload.length) {\n addScte35SectionMarker(\n payload.subarray(offset, offset + totalLength),\n markers,\n processedSections\n );\n offset += totalLength;\n } else {\n assemblers.set(pid, {\n bytes: Array.from(payload.subarray(offset)),\n expectedLength: totalLength,\n });\n return;\n }\n }\n}\n\nexport function parseScte35FromTsPackets(\n data: Uint8Array,\n tsScte35Pids: Set<number>,\n pmtPids: Set<number>,\n processedSections: Set<string>\n): Scte35Marker[] {\n const packetInfo = detectTsPacketFormat(data);\n if (!packetInfo) {\n return [];\n }\n\n const markers: Scte35Marker[] = [];\n const sectionAssemblers = new Map<\n number,\n { bytes: number[]; expectedLength: number }\n >();\n\n for (\n let packetStart = packetInfo.offset;\n packetStart + packetInfo.packetSize <= data.length;\n packetStart += packetInfo.packetSize\n ) {\n const tsStart = packetStart + packetInfo.tsPayloadOffset;\n if (data[tsStart] !== 0x47) {\n continue;\n }\n\n const secondByte = data[tsStart + 1];\n const thirdByte = data[tsStart + 2];\n const fourthByte = data[tsStart + 3];\n if (\n secondByte == null ||\n thirdByte == null ||\n fourthByte == null ||\n (secondByte & 0x80) !== 0\n ) {\n continue;\n }\n\n const payloadUnitStart = (secondByte & 0x40) !== 0;\n const pid = ((secondByte & 0x1f) << 8) | thirdByte;\n const adaptationFieldControl = (fourthByte >> 4) & 0x03;\n const hasPayload =\n adaptationFieldControl === 1 || adaptationFieldControl === 3;\n if (!hasPayload) {\n continue;\n }\n\n let payloadStart = tsStart + 4;\n if (adaptationFieldControl === 3) {\n const adaptationLength = data[payloadStart];\n if (adaptationLength == null) {\n continue;\n }\n payloadStart += 1 + adaptationLength;\n }\n\n const payloadEnd = Math.min(tsStart + 188, data.length);\n if (payloadStart >= payloadEnd || payloadStart >= data.length) {\n continue;\n }\n\n const payload = data.subarray(payloadStart, payloadEnd);\n if (pid === 0) {\n parsePatSection(payload, payloadUnitStart, pmtPids);\n } else if (pmtPids.has(pid)) {\n parsePmtSection(payload, payloadUnitStart, tsScte35Pids);\n } else if (tsScte35Pids.has(pid)) {\n collectScte35SectionsFromPayload(\n pid,\n payload,\n payloadUnitStart,\n sectionAssemblers,\n markers,\n processedSections\n );\n }\n }\n\n return markers;\n}\n\n// --- Binary SCTE-35 parsing ---\n\nclass BitReader {\n private bytePos = 0;\n private bitPos = 0;\n constructor(private readonly buf: Uint8Array) {}\n readBits(numBits: number): number {\n let result = 0;\n while (numBits > 0) {\n if (this.bytePos >= this.buf.length) return result;\n const remainingInByte = 8 - this.bitPos;\n const toRead = Math.min(numBits, remainingInByte);\n const currentByte = this.buf[this.bytePos]!;\n const shift = remainingInByte - toRead;\n const mask = ((1 << toRead) - 1) & 0xff;\n const bits = (currentByte >> shift) & mask;\n result = (result << toRead) | bits;\n this.bitPos += toRead;\n if (this.bitPos >= 8) {\n this.bitPos = 0;\n this.bytePos += 1;\n }\n numBits -= toRead;\n }\n return result >>> 0;\n }\n skipBits(n: number): void {\n this.readBits(n);\n }\n get bytePosition(): number {\n return this.bitPos === 0 ? this.bytePos : this.bytePos + 1;\n }\n}\n\nfunction readSpliceTime(\n r: BitReader,\n ptsAdjustmentTicks: number\n): number | undefined {\n const timeSpecifiedFlag = r.readBits(1) === 1;\n if (!timeSpecifiedFlag) {\n r.readBits(7);\n return undefined;\n }\n r.readBits(6);\n const high = r.readBits(1);\n const low = r.readBits(32);\n const ptsTicks =\n (high * 0x100000000 + low + ptsAdjustmentTicks) % 0x200000000;\n return ptsTicks / 90000;\n}\n\nfunction markerTypeFromSegmentationTypeId(\n segmentationTypeId: number\n): Scte35Marker[\"type\"] | undefined {\n switch (segmentationTypeId) {\n case 0x10:\n case 0x22:\n case 0x30:\n case 0x32:\n case 0x34:\n case 0x36:\n return \"start\";\n case 0x11:\n case 0x23:\n case 0x31:\n case 0x33:\n case 0x35:\n case 0x37:\n return \"end\";\n default:\n return undefined;\n }\n}\n\nfunction parseScte35SegmentationDescriptor(\n descriptor: Uint8Array\n):\n | {\n type: Scte35Marker[\"type\"];\n durationSeconds?: number;\n segmentationEventId?: number;\n }\n | undefined {\n if (descriptor.length < 11) {\n return undefined;\n }\n\n const identifier = String.fromCharCode(\n descriptor[0] ?? 0,\n descriptor[1] ?? 0,\n descriptor[2] ?? 0,\n descriptor[3] ?? 0\n );\n if (identifier !== \"CUEI\") {\n return undefined;\n }\n\n const segmentationEventId =\n ((descriptor[4] ?? 0) * 0x1000000) +\n ((descriptor[5] ?? 0) << 16) +\n ((descriptor[6] ?? 0) << 8) +\n (descriptor[7] ?? 0);\n const cancelIndicator = ((descriptor[8] ?? 0) & 0x80) !== 0;\n if (cancelIndicator) {\n return undefined;\n }\n\n const flags = descriptor[9] ?? 0;\n const programSegmentationFlag = (flags & 0x80) !== 0;\n const segmentationDurationFlag = (flags & 0x40) !== 0;\n let offset = 10;\n\n if (!programSegmentationFlag) {\n const componentCount = descriptor[offset] ?? 0;\n offset += 1 + componentCount * 6;\n if (offset > descriptor.length) {\n return undefined;\n }\n }\n\n let durationSeconds: number | undefined = undefined;\n if (segmentationDurationFlag) {\n if (offset + 5 > descriptor.length) {\n return undefined;\n }\n const durationTicks =\n ((descriptor[offset] ?? 0) * 0x100000000) +\n ((descriptor[offset + 1] ?? 0) * 0x1000000) +\n ((descriptor[offset + 2] ?? 0) << 16) +\n ((descriptor[offset + 3] ?? 0) << 8) +\n (descriptor[offset + 4] ?? 0);\n durationSeconds = durationTicks / 90000;\n offset += 5;\n }\n\n if (offset + 2 > descriptor.length) {\n return undefined;\n }\n offset += 1;\n const segmentationUpidLength = descriptor[offset] ?? 0;\n offset += 1 + segmentationUpidLength;\n if (offset >= descriptor.length) {\n return undefined;\n }\n\n const segmentationTypeId = descriptor[offset] ?? 0;\n const markerType = markerTypeFromSegmentationTypeId(segmentationTypeId);\n if (!markerType) {\n return undefined;\n }\n\n return {\n type: markerType,\n ...(durationSeconds !== undefined ? { durationSeconds } : {}),\n segmentationEventId,\n };\n}\n\nfunction parseScte35SegmentationDescriptors(\n data: Uint8Array,\n offset: number\n):\n | {\n type: Scte35Marker[\"type\"];\n durationSeconds?: number;\n segmentationEventId?: number;\n }\n | undefined {\n if (offset + 2 > data.length) {\n return undefined;\n }\n\n const descriptorLoopLength =\n ((data[offset] ?? 0) << 8) | (data[offset + 1] ?? 0);\n let descriptorOffset = offset + 2;\n const descriptorEnd = Math.min(\n data.length,\n descriptorOffset + descriptorLoopLength\n );\n\n while (descriptorOffset + 2 <= descriptorEnd) {\n const descriptorTag = data[descriptorOffset] ?? 0;\n const descriptorLength = data[descriptorOffset + 1] ?? 0;\n const descriptorDataStart = descriptorOffset + 2;\n const descriptorDataEnd = descriptorDataStart + descriptorLength;\n if (descriptorDataEnd > descriptorEnd) {\n return undefined;\n }\n\n if (descriptorTag === 0x02) {\n const descriptorMarker = parseScte35SegmentationDescriptor(\n data.subarray(descriptorDataStart, descriptorDataEnd)\n );\n if (descriptorMarker) {\n return descriptorMarker;\n }\n }\n\n descriptorOffset = descriptorDataEnd;\n }\n\n return undefined;\n}\n\nexport function parseScte35Binary(data: Uint8Array): Scte35Marker | undefined {\n const r = new BitReader(data);\n const tableId = r.readBits(8);\n if (tableId !== 0xfc) return undefined;\n r.readBits(1);\n r.readBits(1);\n r.readBits(2);\n const sectionLength = r.readBits(12);\n if (sectionLength <= 0 || data.length < 3 + sectionLength) {\n return undefined;\n }\n r.readBits(8);\n r.readBits(1);\n r.readBits(6);\n const ptsAdjHigh = r.readBits(1);\n const ptsAdjLow = r.readBits(32);\n const ptsAdjustmentTicks = ptsAdjHigh * 0x100000000 + ptsAdjLow;\n r.readBits(8);\n r.readBits(12);\n const spliceCommandLength = r.readBits(12);\n const spliceCommandType = r.readBits(8);\n\n let markerType: Scte35Marker[\"type\"] | undefined;\n let durationSeconds: number | undefined = undefined;\n let ptsSeconds: number | undefined = undefined;\n let spliceEventId: number | undefined = undefined;\n const commandBodyStart = r.bytePosition;\n\n if (spliceCommandType === 5) {\n spliceEventId = r.readBits(32);\n const cancel = r.readBits(1) === 1;\n r.readBits(7);\n if (cancel) return undefined;\n const outOfNetwork = r.readBits(1) === 1;\n const programSpliceFlag = r.readBits(1) === 1;\n const durationFlag = r.readBits(1) === 1;\n const spliceImmediateFlag = r.readBits(1) === 1;\n r.readBits(4);\n markerType = outOfNetwork ? \"start\" : \"end\";\n\n if (programSpliceFlag && !spliceImmediateFlag) {\n ptsSeconds = readSpliceTime(r, ptsAdjustmentTicks);\n } else if (!programSpliceFlag) {\n const componentCount = r.readBits(8);\n for (let i = 0; i < componentCount; i++) {\n r.readBits(8);\n if (!spliceImmediateFlag) {\n const componentPtsSeconds = readSpliceTime(r, ptsAdjustmentTicks);\n if (ptsSeconds === undefined) {\n ptsSeconds = componentPtsSeconds;\n }\n }\n }\n }\n\n if (durationFlag) {\n r.readBits(6);\n r.readBits(1);\n const high = r.readBits(1);\n const low = r.readBits(32);\n const durationTicks = high * 0x100000000 + low;\n durationSeconds = durationTicks / 90000;\n }\n r.readBits(16);\n r.readBits(8);\n r.readBits(8);\n } else if (spliceCommandType === 6) {\n ptsSeconds = readSpliceTime(r, ptsAdjustmentTicks);\n } else {\n return undefined;\n }\n\n const expectedDescriptorOffset =\n spliceCommandLength === 0x0fff\n ? r.bytePosition\n : commandBodyStart + spliceCommandLength;\n const descriptorMarker = parseScte35SegmentationDescriptors(\n data,\n expectedDescriptorOffset\n );\n\n if (descriptorMarker) {\n markerType = descriptorMarker.type;\n if (durationSeconds === undefined) {\n durationSeconds = descriptorMarker.durationSeconds;\n }\n }\n\n if (!markerType) {\n return undefined;\n }\n\n const marker: Scte35Marker = {\n type: markerType,\n ...(ptsSeconds !== undefined ? { ptsSeconds } : {}),\n ...(durationSeconds !== undefined ? { durationSeconds } : {}),\n raw: {\n splice_command_type: spliceCommandType,\n ...(spliceEventId !== undefined ? { splice_event_id: spliceEventId } : {}),\n ...(descriptorMarker?.segmentationEventId !== undefined\n ? { segmentation_event_id: descriptorMarker.segmentationEventId }\n : {}),\n },\n } as Scte35Marker;\n return marker;\n}\n"]}
@@ -0,0 +1,43 @@
1
+ import { S as Scte35Marker } from '../types-DNiBmPKK.cjs';
2
+ import { Scte35CueSource, Scte35CueReadiness, Scte35CueContext } from './playerTypes.cjs';
3
+
4
+ interface Scte35CueManagerCallbacks {
5
+ onEarlyStartCue(marker: Scte35Marker, fragmentSn: number | undefined, cueKey: string): void;
6
+ onPlaybackCue(marker: Scte35Marker, cueKey?: string): void;
7
+ isInAdBreak(): boolean;
8
+ }
9
+ declare class Scte35CueManager {
10
+ private readonly pendingScte35Cues;
11
+ private readonly recentScte35CueKeys;
12
+ private readonly recentScte35CueLimit;
13
+ private pendingScte35CueKeyValue;
14
+ private activeScte35BreakKeyValue;
15
+ private scheduledScte35BreakKeyValue;
16
+ readonly tsScte35Pids: Set<number>;
17
+ readonly pmtPids: Set<number>;
18
+ readonly processedTsScte35Sections: Set<string>;
19
+ private debug;
20
+ private callbacks;
21
+ constructor(debug: boolean, callbacks: Scte35CueManagerCallbacks);
22
+ get pendingScte35CueKey(): string | undefined;
23
+ set pendingScte35CueKey(value: string | undefined);
24
+ get activeScte35BreakKey(): string | undefined;
25
+ set activeScte35BreakKey(value: string | undefined);
26
+ get scheduledScte35BreakKey(): string | undefined;
27
+ set scheduledScte35BreakKey(value: string | undefined);
28
+ createScte35CueContext(source: Scte35CueSource, readiness: Scte35CueReadiness, frag?: any): Scte35CueContext;
29
+ onScte35Cue(marker: Scte35Marker, context: Scte35CueContext): void;
30
+ activatePendingScte35CuesForFragment(frag: any): void;
31
+ private createScte35Cue;
32
+ private getScte35CueKey;
33
+ private getPendingScte35CueKey;
34
+ private getScte35MarkerRaw;
35
+ private toIdentityString;
36
+ isStrongScte35CueKey(key: string | undefined): boolean;
37
+ private hasRecentlyHandledScte35Cue;
38
+ private markScte35CueHandled;
39
+ private trimRecentScte35CueMaps;
40
+ private shouldAcceptScte35EndCue;
41
+ }
42
+
43
+ export { Scte35CueManager, type Scte35CueManagerCallbacks };