@scarlett-player/hls 1.12.0 → 1.13.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.
@@ -246,7 +246,7 @@ function setupHlsEventHandlers(hls, api, callbacks) {
246
246
  api.emit("quality:levels", {
247
247
  levels: levels.map((l) => ({ id: l.id, label: l.label }))
248
248
  });
249
- callbacks.onManifestParsed?.(data.levels);
249
+ callbacks.onManifestParsed?.(data.levels, data);
250
250
  });
251
251
  addHandler("hlsLevelSwitched", (_event, data) => {
252
252
  const level = hls.levels[data.level];
@@ -455,12 +455,6 @@ function setupVideoEventHandlers(video, api, getLiveMetrics) {
455
455
  });
456
456
  addHandler("loadedmetadata", () => {
457
457
  api.setState("duration", video.duration);
458
- api.setState("mediaType", video.videoWidth > 0 ? "video" : "audio");
459
- });
460
- addHandler("loadeddata", () => {
461
- if (video.videoWidth > 0) {
462
- api.setState("mediaType", "video");
463
- }
464
458
  });
465
459
  addHandler("error", () => {
466
460
  const error = video.error;
@@ -502,6 +496,119 @@ function setupVideoEventHandlers(video, api, getLiveMetrics) {
502
496
  };
503
497
  }
504
498
 
499
+ // src/media-type.ts
500
+ var ELEMENT_EVENTS = ["loadedmetadata", "loadeddata", "resize", "playing"];
501
+ var TRACK_LIST_EVENTS = ["addtrack", "removetrack", "change"];
502
+ var HAVE_METADATA = 1;
503
+ function isTrackList(list) {
504
+ return typeof list === "object" && list !== null && typeof list.length === "number";
505
+ }
506
+ function createMediaTypeClassifier(api) {
507
+ let source = null;
508
+ let videoConfirmed = false;
509
+ let audioConfirmed = false;
510
+ let elementReported = false;
511
+ let element = null;
512
+ let elementDisposers = [];
513
+ let published = null;
514
+ let destroyed = false;
515
+ const decide = () => {
516
+ if (videoConfirmed) return "video";
517
+ if (audioConfirmed) return "audio";
518
+ return "unknown";
519
+ };
520
+ const publish = () => {
521
+ const next = decide();
522
+ if (next === published) return;
523
+ published = next;
524
+ api.setState("mediaType", next);
525
+ };
526
+ const readElement = () => {
527
+ const video = element;
528
+ if (!video) return;
529
+ if (elementReported && video.videoWidth > 0) {
530
+ videoConfirmed = true;
531
+ return;
532
+ }
533
+ if (video.readyState < HAVE_METADATA) return;
534
+ const videoTracks = video.videoTracks;
535
+ const audioTracks = video.audioTracks;
536
+ if (!isTrackList(videoTracks)) return;
537
+ if (videoTracks.length > 0) {
538
+ videoConfirmed = true;
539
+ return;
540
+ }
541
+ if (isTrackList(audioTracks) && audioTracks.length > 0) {
542
+ audioConfirmed = true;
543
+ }
544
+ };
545
+ const evaluate = () => {
546
+ if (destroyed) return;
547
+ readElement();
548
+ publish();
549
+ };
550
+ const detachElement = () => {
551
+ for (const off of elementDisposers) off();
552
+ elementDisposers = [];
553
+ element = null;
554
+ };
555
+ return {
556
+ beginSource(src) {
557
+ if (destroyed) return;
558
+ if (source === src) return;
559
+ source = src;
560
+ videoConfirmed = false;
561
+ audioConfirmed = false;
562
+ elementReported = false;
563
+ published = null;
564
+ publish();
565
+ },
566
+ attach(video) {
567
+ if (destroyed) return;
568
+ detachElement();
569
+ element = video;
570
+ elementReported = false;
571
+ for (const event of ELEMENT_EVENTS) {
572
+ const handler = () => {
573
+ elementReported = true;
574
+ evaluate();
575
+ };
576
+ video.addEventListener(event, handler);
577
+ elementDisposers.push(() => video.removeEventListener(event, handler));
578
+ }
579
+ for (const list of [element.videoTracks, element.audioTracks]) {
580
+ if (!isTrackList(list) || typeof list.addEventListener !== "function") continue;
581
+ for (const event of TRACK_LIST_EVENTS) {
582
+ const handler = () => evaluate();
583
+ list.addEventListener(event, handler);
584
+ elementDisposers.push(() => list.removeEventListener?.(event, handler));
585
+ }
586
+ }
587
+ evaluate();
588
+ },
589
+ noteManifestParsed(data) {
590
+ if (destroyed || !data) return;
591
+ const hasVideo = typeof data.video === "boolean" ? data.video : null;
592
+ const hasAudio = typeof data.audio === "boolean" ? data.audio : null;
593
+ if (hasVideo === true) {
594
+ videoConfirmed = true;
595
+ } else if (hasVideo === false && hasAudio === true) {
596
+ audioConfirmed = true;
597
+ }
598
+ publish();
599
+ },
600
+ evaluate,
601
+ current() {
602
+ return decide();
603
+ },
604
+ destroy() {
605
+ if (destroyed) return;
606
+ destroyed = true;
607
+ detachElement();
608
+ }
609
+ };
610
+ }
611
+
505
612
  // src/playlist-validation.ts
506
613
  var PLAYLIST_INVALID_TEXT = "Invalid playlist document";
507
614
  var MEDIA_PLAYLIST_CONTEXTS = ["level", "audioTrack", "subtitleTrack"];
@@ -591,6 +698,7 @@ function createHLSPluginWith(loader, variant, config) {
591
698
  let cleanupHlsEvents = null;
592
699
  let cleanupVideoEvents = null;
593
700
  let isAutoQuality = true;
701
+ let mediaTypeClassifier = null;
594
702
  let lastLevelDetails = null;
595
703
  let lastLiveMetrics = null;
596
704
  let lastKnownTargetLatency = null;
@@ -948,6 +1056,8 @@ function createHLSPluginWith(loader, variant, config) {
948
1056
  if (api) {
949
1057
  cleanupVideoEvents = setupVideoEventHandlers(videoEl, api, readLiveMetrics);
950
1058
  }
1059
+ mediaTypeClassifier?.beginSource(src);
1060
+ mediaTypeClassifier?.attach(videoEl);
951
1061
  return new Promise((resolve, reject) => {
952
1062
  let watchdog = null;
953
1063
  let settled = false;
@@ -1041,6 +1151,8 @@ function createHLSPluginWith(loader, variant, config) {
1041
1151
  if (api) {
1042
1152
  cleanupVideoEvents = setupVideoEventHandlers(videoEl, api, readLiveMetrics);
1043
1153
  }
1154
+ mediaTypeClassifier?.beginSource(src);
1155
+ mediaTypeClassifier?.attach(videoEl);
1044
1156
  return new Promise((resolve, reject) => {
1045
1157
  if (!hls || !api) {
1046
1158
  reject(new Error("HLS not initialized"));
@@ -1067,8 +1179,9 @@ function createHLSPluginWith(loader, variant, config) {
1067
1179
  }
1068
1180
  };
1069
1181
  cleanupHlsEvents = setupHlsEventHandlers(hls, api, {
1070
- onManifestParsed: () => {
1182
+ onManifestParsed: (_levels, manifestData) => {
1071
1183
  if (session !== loadSession) return;
1184
+ mediaTypeClassifier?.noteManifestParsed(manifestData);
1072
1185
  if (!resolved) {
1073
1186
  resolved = true;
1074
1187
  releaseAbort();
@@ -1327,6 +1440,7 @@ function createHLSPluginWith(loader, variant, config) {
1327
1440
  async init(pluginApi) {
1328
1441
  api = pluginApi;
1329
1442
  api.logger.info(`HLS plugin${variant.logSuffix} initialized`);
1443
+ mediaTypeClassifier = createMediaTypeClassifier(api);
1330
1444
  const unsubPlay = api.on("playback:play", async () => {
1331
1445
  if (!video) return;
1332
1446
  try {
@@ -1462,6 +1576,8 @@ function createHLSPluginWith(loader, variant, config) {
1462
1576
  onlineListener = null;
1463
1577
  }
1464
1578
  cleanup(new Error("HLS load cancelled: player destroyed"));
1579
+ mediaTypeClassifier?.destroy();
1580
+ mediaTypeClassifier = null;
1465
1581
  if (video?.parentNode) {
1466
1582
  video.parentNode.removeChild(video);
1467
1583
  }
package/dist/index.cjs CHANGED
@@ -346,7 +346,7 @@ function setupHlsEventHandlers(hls, api, callbacks) {
346
346
  api.emit("quality:levels", {
347
347
  levels: levels.map((l) => ({ id: l.id, label: l.label }))
348
348
  });
349
- callbacks.onManifestParsed?.(data.levels);
349
+ callbacks.onManifestParsed?.(data.levels, data);
350
350
  });
351
351
  addHandler("hlsLevelSwitched", (_event, data) => {
352
352
  const level = hls.levels[data.level];
@@ -555,12 +555,6 @@ function setupVideoEventHandlers(video, api, getLiveMetrics) {
555
555
  });
556
556
  addHandler("loadedmetadata", () => {
557
557
  api.setState("duration", video.duration);
558
- api.setState("mediaType", video.videoWidth > 0 ? "video" : "audio");
559
- });
560
- addHandler("loadeddata", () => {
561
- if (video.videoWidth > 0) {
562
- api.setState("mediaType", "video");
563
- }
564
558
  });
565
559
  addHandler("error", () => {
566
560
  const error = video.error;
@@ -602,6 +596,119 @@ function setupVideoEventHandlers(video, api, getLiveMetrics) {
602
596
  };
603
597
  }
604
598
 
599
+ // src/media-type.ts
600
+ var ELEMENT_EVENTS = ["loadedmetadata", "loadeddata", "resize", "playing"];
601
+ var TRACK_LIST_EVENTS = ["addtrack", "removetrack", "change"];
602
+ var HAVE_METADATA = 1;
603
+ function isTrackList(list) {
604
+ return typeof list === "object" && list !== null && typeof list.length === "number";
605
+ }
606
+ function createMediaTypeClassifier(api) {
607
+ let source = null;
608
+ let videoConfirmed = false;
609
+ let audioConfirmed = false;
610
+ let elementReported = false;
611
+ let element = null;
612
+ let elementDisposers = [];
613
+ let published = null;
614
+ let destroyed = false;
615
+ const decide = () => {
616
+ if (videoConfirmed) return "video";
617
+ if (audioConfirmed) return "audio";
618
+ return "unknown";
619
+ };
620
+ const publish = () => {
621
+ const next = decide();
622
+ if (next === published) return;
623
+ published = next;
624
+ api.setState("mediaType", next);
625
+ };
626
+ const readElement = () => {
627
+ const video = element;
628
+ if (!video) return;
629
+ if (elementReported && video.videoWidth > 0) {
630
+ videoConfirmed = true;
631
+ return;
632
+ }
633
+ if (video.readyState < HAVE_METADATA) return;
634
+ const videoTracks = video.videoTracks;
635
+ const audioTracks = video.audioTracks;
636
+ if (!isTrackList(videoTracks)) return;
637
+ if (videoTracks.length > 0) {
638
+ videoConfirmed = true;
639
+ return;
640
+ }
641
+ if (isTrackList(audioTracks) && audioTracks.length > 0) {
642
+ audioConfirmed = true;
643
+ }
644
+ };
645
+ const evaluate = () => {
646
+ if (destroyed) return;
647
+ readElement();
648
+ publish();
649
+ };
650
+ const detachElement = () => {
651
+ for (const off of elementDisposers) off();
652
+ elementDisposers = [];
653
+ element = null;
654
+ };
655
+ return {
656
+ beginSource(src) {
657
+ if (destroyed) return;
658
+ if (source === src) return;
659
+ source = src;
660
+ videoConfirmed = false;
661
+ audioConfirmed = false;
662
+ elementReported = false;
663
+ published = null;
664
+ publish();
665
+ },
666
+ attach(video) {
667
+ if (destroyed) return;
668
+ detachElement();
669
+ element = video;
670
+ elementReported = false;
671
+ for (const event of ELEMENT_EVENTS) {
672
+ const handler = () => {
673
+ elementReported = true;
674
+ evaluate();
675
+ };
676
+ video.addEventListener(event, handler);
677
+ elementDisposers.push(() => video.removeEventListener(event, handler));
678
+ }
679
+ for (const list of [element.videoTracks, element.audioTracks]) {
680
+ if (!isTrackList(list) || typeof list.addEventListener !== "function") continue;
681
+ for (const event of TRACK_LIST_EVENTS) {
682
+ const handler = () => evaluate();
683
+ list.addEventListener(event, handler);
684
+ elementDisposers.push(() => list.removeEventListener?.(event, handler));
685
+ }
686
+ }
687
+ evaluate();
688
+ },
689
+ noteManifestParsed(data) {
690
+ if (destroyed || !data) return;
691
+ const hasVideo = typeof data.video === "boolean" ? data.video : null;
692
+ const hasAudio = typeof data.audio === "boolean" ? data.audio : null;
693
+ if (hasVideo === true) {
694
+ videoConfirmed = true;
695
+ } else if (hasVideo === false && hasAudio === true) {
696
+ audioConfirmed = true;
697
+ }
698
+ publish();
699
+ },
700
+ evaluate,
701
+ current() {
702
+ return decide();
703
+ },
704
+ destroy() {
705
+ if (destroyed) return;
706
+ destroyed = true;
707
+ detachElement();
708
+ }
709
+ };
710
+ }
711
+
605
712
  // src/playlist-validation.ts
606
713
  var PLAYLIST_INVALID_TEXT = "Invalid playlist document";
607
714
  var MEDIA_PLAYLIST_CONTEXTS = ["level", "audioTrack", "subtitleTrack"];
@@ -691,6 +798,7 @@ function createHLSPluginWith(loader, variant, config) {
691
798
  let cleanupHlsEvents = null;
692
799
  let cleanupVideoEvents = null;
693
800
  let isAutoQuality = true;
801
+ let mediaTypeClassifier = null;
694
802
  let lastLevelDetails = null;
695
803
  let lastLiveMetrics = null;
696
804
  let lastKnownTargetLatency = null;
@@ -1048,6 +1156,8 @@ function createHLSPluginWith(loader, variant, config) {
1048
1156
  if (api) {
1049
1157
  cleanupVideoEvents = setupVideoEventHandlers(videoEl, api, readLiveMetrics);
1050
1158
  }
1159
+ mediaTypeClassifier?.beginSource(src);
1160
+ mediaTypeClassifier?.attach(videoEl);
1051
1161
  return new Promise((resolve, reject) => {
1052
1162
  let watchdog = null;
1053
1163
  let settled = false;
@@ -1141,6 +1251,8 @@ function createHLSPluginWith(loader, variant, config) {
1141
1251
  if (api) {
1142
1252
  cleanupVideoEvents = setupVideoEventHandlers(videoEl, api, readLiveMetrics);
1143
1253
  }
1254
+ mediaTypeClassifier?.beginSource(src);
1255
+ mediaTypeClassifier?.attach(videoEl);
1144
1256
  return new Promise((resolve, reject) => {
1145
1257
  if (!hls || !api) {
1146
1258
  reject(new Error("HLS not initialized"));
@@ -1167,8 +1279,9 @@ function createHLSPluginWith(loader, variant, config) {
1167
1279
  }
1168
1280
  };
1169
1281
  cleanupHlsEvents = setupHlsEventHandlers(hls, api, {
1170
- onManifestParsed: () => {
1282
+ onManifestParsed: (_levels, manifestData) => {
1171
1283
  if (session !== loadSession) return;
1284
+ mediaTypeClassifier?.noteManifestParsed(manifestData);
1172
1285
  if (!resolved) {
1173
1286
  resolved = true;
1174
1287
  releaseAbort();
@@ -1427,6 +1540,7 @@ function createHLSPluginWith(loader, variant, config) {
1427
1540
  async init(pluginApi) {
1428
1541
  api = pluginApi;
1429
1542
  api.logger.info(`HLS plugin${variant.logSuffix} initialized`);
1543
+ mediaTypeClassifier = createMediaTypeClassifier(api);
1430
1544
  const unsubPlay = api.on("playback:play", async () => {
1431
1545
  if (!video) return;
1432
1546
  try {
@@ -1562,6 +1676,8 @@ function createHLSPluginWith(loader, variant, config) {
1562
1676
  onlineListener = null;
1563
1677
  }
1564
1678
  cleanup(new Error("HLS load cancelled: player destroyed"));
1679
+ mediaTypeClassifier?.destroy();
1680
+ mediaTypeClassifier = null;
1565
1681
  if (video?.parentNode) {
1566
1682
  video.parentNode.removeChild(video);
1567
1683
  }
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  computeLiveMetrics,
5
5
  createHLSPluginWith,
6
6
  sanitizeUrl
7
- } from "./chunk-47MU7IVX.js";
7
+ } from "./chunk-3KFYKCR2.js";
8
8
 
9
9
  // src/hls-loader.ts
10
10
  var hls_loader_exports = {};
package/dist/light.cjs CHANGED
@@ -346,7 +346,7 @@ function setupHlsEventHandlers(hls, api, callbacks) {
346
346
  api.emit("quality:levels", {
347
347
  levels: levels.map((l) => ({ id: l.id, label: l.label }))
348
348
  });
349
- callbacks.onManifestParsed?.(data.levels);
349
+ callbacks.onManifestParsed?.(data.levels, data);
350
350
  });
351
351
  addHandler("hlsLevelSwitched", (_event, data) => {
352
352
  const level = hls.levels[data.level];
@@ -555,12 +555,6 @@ function setupVideoEventHandlers(video, api, getLiveMetrics) {
555
555
  });
556
556
  addHandler("loadedmetadata", () => {
557
557
  api.setState("duration", video.duration);
558
- api.setState("mediaType", video.videoWidth > 0 ? "video" : "audio");
559
- });
560
- addHandler("loadeddata", () => {
561
- if (video.videoWidth > 0) {
562
- api.setState("mediaType", "video");
563
- }
564
558
  });
565
559
  addHandler("error", () => {
566
560
  const error = video.error;
@@ -602,6 +596,119 @@ function setupVideoEventHandlers(video, api, getLiveMetrics) {
602
596
  };
603
597
  }
604
598
 
599
+ // src/media-type.ts
600
+ var ELEMENT_EVENTS = ["loadedmetadata", "loadeddata", "resize", "playing"];
601
+ var TRACK_LIST_EVENTS = ["addtrack", "removetrack", "change"];
602
+ var HAVE_METADATA = 1;
603
+ function isTrackList(list) {
604
+ return typeof list === "object" && list !== null && typeof list.length === "number";
605
+ }
606
+ function createMediaTypeClassifier(api) {
607
+ let source = null;
608
+ let videoConfirmed = false;
609
+ let audioConfirmed = false;
610
+ let elementReported = false;
611
+ let element = null;
612
+ let elementDisposers = [];
613
+ let published = null;
614
+ let destroyed = false;
615
+ const decide = () => {
616
+ if (videoConfirmed) return "video";
617
+ if (audioConfirmed) return "audio";
618
+ return "unknown";
619
+ };
620
+ const publish = () => {
621
+ const next = decide();
622
+ if (next === published) return;
623
+ published = next;
624
+ api.setState("mediaType", next);
625
+ };
626
+ const readElement = () => {
627
+ const video = element;
628
+ if (!video) return;
629
+ if (elementReported && video.videoWidth > 0) {
630
+ videoConfirmed = true;
631
+ return;
632
+ }
633
+ if (video.readyState < HAVE_METADATA) return;
634
+ const videoTracks = video.videoTracks;
635
+ const audioTracks = video.audioTracks;
636
+ if (!isTrackList(videoTracks)) return;
637
+ if (videoTracks.length > 0) {
638
+ videoConfirmed = true;
639
+ return;
640
+ }
641
+ if (isTrackList(audioTracks) && audioTracks.length > 0) {
642
+ audioConfirmed = true;
643
+ }
644
+ };
645
+ const evaluate = () => {
646
+ if (destroyed) return;
647
+ readElement();
648
+ publish();
649
+ };
650
+ const detachElement = () => {
651
+ for (const off of elementDisposers) off();
652
+ elementDisposers = [];
653
+ element = null;
654
+ };
655
+ return {
656
+ beginSource(src) {
657
+ if (destroyed) return;
658
+ if (source === src) return;
659
+ source = src;
660
+ videoConfirmed = false;
661
+ audioConfirmed = false;
662
+ elementReported = false;
663
+ published = null;
664
+ publish();
665
+ },
666
+ attach(video) {
667
+ if (destroyed) return;
668
+ detachElement();
669
+ element = video;
670
+ elementReported = false;
671
+ for (const event of ELEMENT_EVENTS) {
672
+ const handler = () => {
673
+ elementReported = true;
674
+ evaluate();
675
+ };
676
+ video.addEventListener(event, handler);
677
+ elementDisposers.push(() => video.removeEventListener(event, handler));
678
+ }
679
+ for (const list of [element.videoTracks, element.audioTracks]) {
680
+ if (!isTrackList(list) || typeof list.addEventListener !== "function") continue;
681
+ for (const event of TRACK_LIST_EVENTS) {
682
+ const handler = () => evaluate();
683
+ list.addEventListener(event, handler);
684
+ elementDisposers.push(() => list.removeEventListener?.(event, handler));
685
+ }
686
+ }
687
+ evaluate();
688
+ },
689
+ noteManifestParsed(data) {
690
+ if (destroyed || !data) return;
691
+ const hasVideo = typeof data.video === "boolean" ? data.video : null;
692
+ const hasAudio = typeof data.audio === "boolean" ? data.audio : null;
693
+ if (hasVideo === true) {
694
+ videoConfirmed = true;
695
+ } else if (hasVideo === false && hasAudio === true) {
696
+ audioConfirmed = true;
697
+ }
698
+ publish();
699
+ },
700
+ evaluate,
701
+ current() {
702
+ return decide();
703
+ },
704
+ destroy() {
705
+ if (destroyed) return;
706
+ destroyed = true;
707
+ detachElement();
708
+ }
709
+ };
710
+ }
711
+
605
712
  // src/playlist-validation.ts
606
713
  var PLAYLIST_INVALID_TEXT = "Invalid playlist document";
607
714
  var MEDIA_PLAYLIST_CONTEXTS = ["level", "audioTrack", "subtitleTrack"];
@@ -691,6 +798,7 @@ function createHLSPluginWith(loader, variant, config) {
691
798
  let cleanupHlsEvents = null;
692
799
  let cleanupVideoEvents = null;
693
800
  let isAutoQuality = true;
801
+ let mediaTypeClassifier = null;
694
802
  let lastLevelDetails = null;
695
803
  let lastLiveMetrics = null;
696
804
  let lastKnownTargetLatency = null;
@@ -1048,6 +1156,8 @@ function createHLSPluginWith(loader, variant, config) {
1048
1156
  if (api) {
1049
1157
  cleanupVideoEvents = setupVideoEventHandlers(videoEl, api, readLiveMetrics);
1050
1158
  }
1159
+ mediaTypeClassifier?.beginSource(src);
1160
+ mediaTypeClassifier?.attach(videoEl);
1051
1161
  return new Promise((resolve, reject) => {
1052
1162
  let watchdog = null;
1053
1163
  let settled = false;
@@ -1141,6 +1251,8 @@ function createHLSPluginWith(loader, variant, config) {
1141
1251
  if (api) {
1142
1252
  cleanupVideoEvents = setupVideoEventHandlers(videoEl, api, readLiveMetrics);
1143
1253
  }
1254
+ mediaTypeClassifier?.beginSource(src);
1255
+ mediaTypeClassifier?.attach(videoEl);
1144
1256
  return new Promise((resolve, reject) => {
1145
1257
  if (!hls || !api) {
1146
1258
  reject(new Error("HLS not initialized"));
@@ -1167,8 +1279,9 @@ function createHLSPluginWith(loader, variant, config) {
1167
1279
  }
1168
1280
  };
1169
1281
  cleanupHlsEvents = setupHlsEventHandlers(hls, api, {
1170
- onManifestParsed: () => {
1282
+ onManifestParsed: (_levels, manifestData) => {
1171
1283
  if (session !== loadSession) return;
1284
+ mediaTypeClassifier?.noteManifestParsed(manifestData);
1172
1285
  if (!resolved) {
1173
1286
  resolved = true;
1174
1287
  releaseAbort();
@@ -1427,6 +1540,7 @@ function createHLSPluginWith(loader, variant, config) {
1427
1540
  async init(pluginApi) {
1428
1541
  api = pluginApi;
1429
1542
  api.logger.info(`HLS plugin${variant.logSuffix} initialized`);
1543
+ mediaTypeClassifier = createMediaTypeClassifier(api);
1430
1544
  const unsubPlay = api.on("playback:play", async () => {
1431
1545
  if (!video) return;
1432
1546
  try {
@@ -1562,6 +1676,8 @@ function createHLSPluginWith(loader, variant, config) {
1562
1676
  onlineListener = null;
1563
1677
  }
1564
1678
  cleanup(new Error("HLS load cancelled: player destroyed"));
1679
+ mediaTypeClassifier?.destroy();
1680
+ mediaTypeClassifier = null;
1565
1681
  if (video?.parentNode) {
1566
1682
  video.parentNode.removeChild(video);
1567
1683
  }
package/dist/light.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  computeLiveMetrics,
5
5
  createHLSPluginWith,
6
6
  sanitizeUrl
7
- } from "./chunk-47MU7IVX.js";
7
+ } from "./chunk-3KFYKCR2.js";
8
8
 
9
9
  // src/hls-loader-light.ts
10
10
  var hls_loader_light_exports = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scarlett-player/hls",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "description": "HLS Provider Plugin for Scarlett Player",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -37,7 +37,7 @@
37
37
  "vitest": "^1.6.0",
38
38
  "@vitest/coverage-v8": "^1.6.0",
39
39
  "jsdom": "^24.0.0",
40
- "@scarlett-player/core": "1.12.0"
40
+ "@scarlett-player/core": "1.13.0"
41
41
  },
42
42
  "keywords": [
43
43
  "video",