eb-player 2.2.0 → 2.3.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.
@@ -4,7 +4,7 @@
4
4
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.EBPlayer = {}));
5
5
  })(this, (function (exports) { 'use strict';
6
6
 
7
- var __EB_PLAYER_VERSION__ = "2.2.0";
7
+ var __EB_PLAYER_VERSION__ = "2.3.0";
8
8
 
9
9
  /**
10
10
  * Finite State Machine for player playback state transitions.
@@ -4388,6 +4388,170 @@
4388
4388
  };
4389
4389
  }
4390
4390
 
4391
+ var subtitlesParserVtt = {exports: {}};
4392
+
4393
+ var hasRequiredSubtitlesParserVtt;
4394
+
4395
+ function requireSubtitlesParserVtt () {
4396
+ if (hasRequiredSubtitlesParserVtt) return subtitlesParserVtt.exports;
4397
+ hasRequiredSubtitlesParserVtt = 1;
4398
+ (function (module, exports) {
4399
+ var parser = (function () {
4400
+ var pItems = {};
4401
+
4402
+ /**
4403
+ * Converts SubRip subtitles into array of objects
4404
+ * [{
4405
+ * id: `Number of subtitle`
4406
+ * startTime: `Start time of subtitle`
4407
+ * endTime: `End time of subtitle
4408
+ * text: `Text of subtitle`
4409
+ * }]
4410
+ *
4411
+ * @param {String} data SubRip suntitles string
4412
+ * @param {Boolean} ms Optional: use milliseconds for startTime and endTime
4413
+ * @return {Array}
4414
+ */
4415
+ pItems.fromVtt = function (data, timeFormat, isYoutubeAutoTranscript) {
4416
+ var useYoutubeAutoTranscript = isYoutubeAutoTranscript ? true : false;
4417
+ data = data.replace(/\r/g, "");
4418
+ var regex = /(\d+)?\n?(\d{2}:\d{2}:\d{2}[,.]\d{3}) --> (\d{2}:\d{2}:\d{2}[,.]\d{3}).*\n/g;
4419
+ data = data.split(regex);
4420
+ data.shift();
4421
+ var items = [];
4422
+ for (var i = 0; i < data.length; i += 4) {
4423
+ var text = data[i + 3];
4424
+ if (useYoutubeAutoTranscript) {
4425
+ text = text.split("\n");
4426
+ text.shift();
4427
+ text = text.join("\n");
4428
+ }
4429
+ if (text.trim().length === 0) continue;
4430
+ items.push({
4431
+ id: data[i] ? +data[i].trim() : items.length + 1,
4432
+ startTime: changeTimeFormat(data[i + 1].trim(), timeFormat),
4433
+ endTime: changeTimeFormat(data[i + 2].trim(), timeFormat),
4434
+ text: text.trim(),
4435
+ });
4436
+ }
4437
+ return items;
4438
+ };
4439
+
4440
+ /**
4441
+ * Converts Array of objects created by this module to SubRip subtitles
4442
+ * @param {Array} data
4443
+ * @return {String} SubRip subtitles string
4444
+ */
4445
+ pItems.toVtt = function (data) {
4446
+ if (!data instanceof Array) return "";
4447
+ var res = "";
4448
+
4449
+ for (var i = 0; i < data.length; i++) {
4450
+ var s = data[i];
4451
+
4452
+ if (!isNaN(s.startTime) && !isNaN(s.endTime)) {
4453
+ s.startTime = msTime(parseInt(s.startTime, 10));
4454
+ s.endTime = msTime(parseInt(s.endTime, 10));
4455
+ }
4456
+
4457
+ res += s.id + "\r\n";
4458
+ res += s.startTime + " --> " + s.endTime + "\r\n";
4459
+ res += s.text.replace("\n", "\r\n") + "\r\n\r\n";
4460
+ }
4461
+
4462
+ return res;
4463
+ };
4464
+
4465
+ var changeTimeFormat = function (time, format) {
4466
+ if (format === "ms") {
4467
+ return timeMs(time);
4468
+ } else if (format === "s") {
4469
+ return timeMs(time) / 1000;
4470
+ } else {
4471
+ return time;
4472
+ }
4473
+ };
4474
+
4475
+ var timeMs = function (val) {
4476
+ var regex = /(\d+):(\d{2}):(\d{2})[,.](\d{3})/;
4477
+ var parts = regex.exec(val);
4478
+
4479
+ if (parts === null) {
4480
+ return 0;
4481
+ }
4482
+
4483
+ for (var i = 1; i < 5; i++) {
4484
+ parts[i] = parseInt(parts[i], 10);
4485
+ if (isNaN(parts[i])) parts[i] = 0;
4486
+ }
4487
+
4488
+ // hours + minutes + seconds + ms
4489
+ return parts[1] * 3600000 + parts[2] * 60000 + parts[3] * 1000 + parts[4];
4490
+ };
4491
+
4492
+ var msTime = function (val) {
4493
+ var measures = [3600000, 60000, 1000];
4494
+ var time = [];
4495
+
4496
+ for (var i in measures) {
4497
+ var res = ((val / measures[i]) >> 0).toString();
4498
+
4499
+ if (res.length < 2) res = "0" + res;
4500
+ val %= measures[i];
4501
+ time.push(res);
4502
+ }
4503
+
4504
+ var ms = val.toString();
4505
+ if (ms.length < 3) {
4506
+ for (i = 0; i <= 3 - ms.length; i++) ms = "0" + ms;
4507
+ }
4508
+
4509
+ return time.join(":") + "," + ms;
4510
+ };
4511
+
4512
+ return pItems;
4513
+ })();
4514
+
4515
+ // ignore exports for browser
4516
+ {
4517
+ module.exports = parser;
4518
+ }
4519
+ } (subtitlesParserVtt));
4520
+ return subtitlesParserVtt.exports;
4521
+ }
4522
+
4523
+ var subtitlesParserVttExports = requireSubtitlesParserVtt();
4524
+
4525
+ /**
4526
+ * Parses a WebVTT string into an array of ChapterMark objects.
4527
+ * Returns an empty array if the content is empty or contains no parseable cues.
4528
+ */
4529
+ function parseChapters(vttContent) {
4530
+ if (!vttContent || !vttContent.trim()) {
4531
+ return [];
4532
+ }
4533
+ const cues = subtitlesParserVttExports.fromVtt(vttContent, 's');
4534
+ return cues.map((cue) => ({
4535
+ title: cue.text,
4536
+ startTime: cue.startTime,
4537
+ endTime: cue.endTime
4538
+ }));
4539
+ }
4540
+ /**
4541
+ * Fetches a WebVTT chapters file from a URL and parses it.
4542
+ * Returns an empty array if the fetch or parse fails.
4543
+ */
4544
+ async function fetchChapters(url) {
4545
+ try {
4546
+ const response = await fetch(url);
4547
+ const text = await response.text();
4548
+ return parseChapters(text);
4549
+ }
4550
+ catch {
4551
+ return [];
4552
+ }
4553
+ }
4554
+
4391
4555
  /**
4392
4556
  * PlayerController manages the complete lifecycle of a player instance.
4393
4557
  *
@@ -4588,6 +4752,25 @@
4588
4752
  // CRITICAL: AbortController is one-shot. Create a fresh one for re-mount.
4589
4753
  this.abortController = new AbortController();
4590
4754
  }
4755
+ /**
4756
+ * Populate state.chapters from config.chapters.
4757
+ *
4758
+ * The value is either an inline WebVTT string or an http(s) URL pointing at a
4759
+ * .vtt file. A remote fetch is fire-and-forget and non-critical: fetchChapters
4760
+ * resolves to an empty array on failure, and a result arriving after dispose()
4761
+ * is discarded rather than written into a torn-down state.
4762
+ */
4763
+ loadChapters(source, state, signal) {
4764
+ if (/^https?:\/\//i.test(source.trim())) {
4765
+ void fetchChapters(source).then((chapters) => {
4766
+ if (!signal.aborted) {
4767
+ state.chapters = chapters;
4768
+ }
4769
+ });
4770
+ return;
4771
+ }
4772
+ state.chapters = parseChapters(source);
4773
+ }
4591
4774
  /**
4592
4775
  * Initialize integration managers based on config flags.
4593
4776
  * All integrations are async and non-critical — errors are caught and logged.
@@ -4630,6 +4813,10 @@
4630
4813
  });
4631
4814
  }
4632
4815
  }
4816
+ // Chapters: skip when config.chapters is not set
4817
+ if (config.chapters) {
4818
+ this.loadChapters(config.chapters, state, signal);
4819
+ }
4633
4820
  // IntroStreamManager: skip when config.preroll is false (D-02)
4634
4821
  if (config.preroll && this.skinRoot !== null) {
4635
4822
  const introManager = new IntroStreamManager();