dasha 4.3.1 → 4.4.1

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/dist/index.d.mts CHANGED
@@ -1,6 +1,4 @@
1
- import * as _$mediabunny from "mediabunny";
2
1
  import { AudioCodec as AudioCodec$1, DurationMetadataRequestOptions, EncodedPacket, FilePathSource, HLS, HLS_FORMATS, Input as Input$1, InputAudioTrack as InputAudioTrack$1, InputFormat, InputOptions, InputTrack as InputTrack$2, InputTrackQuery, InputTrackQuery as InputTrackQuery$1, InputVideoTrack as InputVideoTrack$1, MP3, MP4, MaybePromise, MediaCodec as MediaCodec$1, MetadataTags, PacketRetrievalOptions, PathedSource, Source, SourceRef, SubtitleCodec as SubtitleCodec$1, TrackDisposition, UrlSource, VideoCodec as VideoCodec$1, asc, desc, prefer } from "mediabunny";
3
- import * as _$_xmldom_xmldom0 from "@xmldom/xmldom";
4
2
 
5
3
  //#region src/mediabunny.d.ts
6
4
  type Segment$1 = {
@@ -104,7 +102,7 @@ declare const ROLE_TYPE: {
104
102
  type RoleType = (typeof ROLE_TYPE)[keyof typeof ROLE_TYPE];
105
103
  //#endregion
106
104
  //#region src/dash/dash-misc.d.ts
107
- type Element = _$_xmldom_xmldom0.Element;
105
+ type Element = import('@xmldom/xmldom').Element;
108
106
  type DashTrackType = 'video' | 'audio' | 'subtitle';
109
107
  type DashEncryptionData = {
110
108
  method: string;
@@ -231,7 +229,7 @@ declare class DashSegmentedInput {
231
229
  firstTrackPromise: Promise<TrackWithBacking> | null;
232
230
  packetInfos: WeakMap<EncodedPacket, PacketInfo>;
233
231
  firstSegmentFirstTimestamps: WeakMap<DashSegment, number>;
234
- firstTimestampCache: WeakMap<Input$1<_$mediabunny.Source>, number>;
232
+ firstTimestampCache: WeakMap<Input$1<import("mediabunny").Source>, number>;
235
233
  constructor(internalTrack: DashInternalTrack);
236
234
  runUpdateSegments(): Promise<void>;
237
235
  updateSegments(): Promise<void>;
@@ -563,16 +561,19 @@ type SegmentAccessMethods = {
563
561
  type TrackMetadataOverrideMethods = {
564
562
  setLanguageCode(value: string): void;
565
563
  };
564
+ type TrackSourceMethods = {
565
+ readonly source: Source;
566
+ };
566
567
  type VideoDynamicRangeMethods = {
567
568
  getDynamicRange(): Promise<VideoDynamicRange>;
568
569
  };
569
570
  type MediabunnySubtitleTrackLike = InputTrack$2 & {
570
571
  type: 'subtitle';
571
572
  };
572
- type InputTrack$1 = InputTrack$2 & SegmentAccessMethods & TrackMetadataOverrideMethods;
573
- type InputVideoTrack = InputVideoTrack$1 & SegmentAccessMethods & VideoDynamicRangeMethods & TrackMetadataOverrideMethods;
574
- type InputAudioTrack = InputAudioTrack$1 & SegmentAccessMethods & TrackMetadataOverrideMethods;
575
- type InputSubtitleTrack = MediabunnySubtitleTrackLike & SegmentAccessMethods & TrackMetadataOverrideMethods;
573
+ type InputTrack$1 = InputTrack$2 & SegmentAccessMethods & TrackMetadataOverrideMethods & TrackSourceMethods;
574
+ type InputVideoTrack = InputVideoTrack$1 & SegmentAccessMethods & VideoDynamicRangeMethods & TrackMetadataOverrideMethods & TrackSourceMethods;
575
+ type InputAudioTrack = InputAudioTrack$1 & SegmentAccessMethods & TrackMetadataOverrideMethods & TrackSourceMethods;
576
+ type InputSubtitleTrack = MediabunnySubtitleTrackLike & SegmentAccessMethods & TrackMetadataOverrideMethods & TrackSourceMethods;
576
577
  type InputSubtitleSource = PathedSource | SourceRef<PathedSource>;
577
578
  type InputSubtitleTrackMetadata = {
578
579
  codec?: SubtitleCodec | null;
@@ -582,6 +583,14 @@ type InputSubtitleTrackMetadata = {
582
583
  name?: string | null;
583
584
  pairWith?: InputVideoTrack | Iterable<InputVideoTrack>;
584
585
  };
586
+ type InputAudioSource = Source | SourceRef<Source>;
587
+ type InputAudioTrackPairing = InputVideoTrack | Iterable<InputVideoTrack> | 'all' | 'primary' | false;
588
+ type InputAudioTracksOptions = {
589
+ filter?: InputTrackQuery$1<InputAudioTrack>['filter'];
590
+ formats?: readonly InputFormat[];
591
+ pairWith?: InputAudioTrackPairing;
592
+ sortBy?: InputTrackQuery$1<InputAudioTrack>['sortBy'];
593
+ };
585
594
  type SegmentableBacking = {
586
595
  getId(): number;
587
596
  getNumber(): number;
@@ -601,17 +610,54 @@ type SegmentableBacking = {
601
610
  getDecoderConfig?(): Promise<VideoDecoderConfig | AudioDecoderConfig | null>;
602
611
  getMetadataCodecParameterString?(): string | null | Promise<string | null>;
603
612
  getSegmentedInput?(): HlsSegmentedInput | DashSegmentedInput;
613
+ getSource?(): Source;
604
614
  };
605
615
  type NativeTrackBacking = SegmentableBacking;
606
616
  type TrackBacking = NativeTrackBacking | SegmentableBacking;
607
617
  declare const BACKING_TYPE_SUBTITLE = "subtitle";
608
618
  declare const BACKING_TYPE_AUDIO = "audio";
609
619
  declare const BACKING_TYPE_VIDEO = "video";
620
+ declare class ImportedAudioTrackBacking {
621
+ #private;
622
+ constructor(params: {
623
+ backing: SegmentableBacking;
624
+ id: number;
625
+ number: number;
626
+ source: Source;
627
+ });
628
+ getType(): string;
629
+ getId(): number;
630
+ getNumber(): number;
631
+ getCodec(): MediaCodec$1 | Promise<MediaCodec$1 | null> | null;
632
+ getInternalCodecId(): string | number | Uint8Array<ArrayBufferLike> | Promise<string | number | Uint8Array<ArrayBufferLike> | null> | null;
633
+ getName(): string | Promise<string | null> | null;
634
+ getLanguageCode(): string | Promise<string>;
635
+ getTimeResolution(): number | Promise<number>;
636
+ isRelativeToUnixEpoch(): boolean | Promise<boolean>;
637
+ getDisposition(): {};
638
+ getPairingMask(): bigint;
639
+ getSource(): Source;
640
+ getBitrate(): number | Promise<number | null> | null;
641
+ getAverageBitrate(): number | Promise<number | null> | null;
642
+ getDurationFromMetadata(options: unknown): Promise<number | null>;
643
+ getLiveRefreshInterval(): Promise<number | null>;
644
+ getHasOnlyKeyPackets(): boolean;
645
+ getDecoderConfig(): Promise<VideoDecoderConfig | AudioDecoderConfig | null>;
646
+ getMetadataCodecParameterString(): string | Promise<string | null> | null;
647
+ getNumberOfChannels(): number | Promise<number>;
648
+ getSampleRate(): number | Promise<number>;
649
+ getFirstPacket(options: unknown): Promise<EncodedPacket | null>;
650
+ getPacket(timestamp: number, options: unknown): Promise<EncodedPacket | null>;
651
+ getNextPacket(packet: EncodedPacket, options: unknown): Promise<EncodedPacket | null>;
652
+ getKeyPacket(timestamp: number, options: unknown): Promise<EncodedPacket | null>;
653
+ getNextKeyPacket(packet: EncodedPacket, options: unknown): Promise<EncodedPacket | null>;
654
+ getSegmentedInput(): any;
655
+ }
610
656
  declare const preserveSubtitleBackingsOnInput: (input: Input$1) => Input$1<Source>;
611
657
  declare class SegmentedMediabunnyInput<S extends Source = Source> extends Input$1<S> {
612
658
  #private;
613
659
  _wrapBackingAsTrack(backing: TrackBacking): InputTrack$1;
614
- _getSyntheticTrackBackings(type?: typeof BACKING_TYPE_VIDEO | typeof BACKING_TYPE_AUDIO | typeof BACKING_TYPE_SUBTITLE): Promise<(ExternalSubtitleTrackBacking | HlsSubtitleTrackBacking)[]>;
660
+ _getSyntheticTrackBackings(type?: typeof BACKING_TYPE_VIDEO | typeof BACKING_TYPE_AUDIO | typeof BACKING_TYPE_SUBTITLE): Promise<(ImportedAudioTrackBacking | ExternalSubtitleTrackBacking | HlsSubtitleTrackBacking)[]>;
615
661
  getTracks(query?: InputTrackQuery$1<InputTrack$1>): Promise<InputTrack$1[]>;
616
662
  getVideoTracks(query?: InputTrackQuery$1<InputVideoTrack>): Promise<InputVideoTrack[]>;
617
663
  getAudioTracks(query?: InputTrackQuery$1<InputAudioTrack>): Promise<InputAudioTrack[]>;
@@ -619,6 +665,8 @@ declare class SegmentedMediabunnyInput<S extends Source = Source> extends Input$
619
665
  getPrimaryVideoTrack(query?: InputTrackQuery$1<InputVideoTrack>): Promise<InputVideoTrack | null>;
620
666
  getPrimaryAudioTrack(query?: InputTrackQuery$1<InputAudioTrack>): Promise<InputAudioTrack | null>;
621
667
  addSubtitleTrack(source: InputSubtitleSource, metadata?: InputSubtitleTrackMetadata): InputSubtitleTrack;
668
+ addAudioTracks(source: InputAudioSource, options?: InputAudioTracksOptions): Promise<InputAudioTrack[]>;
669
+ dispose(): void;
622
670
  }
623
671
  //#endregion
624
672
  //#region src/index.d.ts
@@ -633,4 +681,4 @@ declare class Input<S extends Source = Source> extends SegmentedMediabunnyInput<
633
681
  declare const isInput: (value: unknown) => value is Input;
634
682
  declare const ALL_FORMATS: InputFormat[];
635
683
  //#endregion
636
- export { ALL_FORMATS, type AudioCodec, DASH, DASH_FORMATS, type DashSegment, type DashSegmentedInput, FilePathSource, HLS, HLS_FORMATS, type HlsSegment, type HlsSegmentedInput, Input, type InputAudioTrack, InputSegment, InputSegmentedInput, type InputSubtitleSource, type InputSubtitleTrack, type InputSubtitleTrackMetadata, type InputTrack$1 as InputTrack, type InputTrackQuery, type InputTrackWithBacking, type InputVideoTrack, MP3, MP4, type MaybePromise, type MediaCodec, type SubtitleCodec, UrlSource, type VideoCodec, type VideoDynamicRange, asc, desc, isInput, prefer, preserveSubtitleBackingsOnInput };
684
+ export { ALL_FORMATS, type AudioCodec, DASH, DASH_FORMATS, type DashSegment, type DashSegmentedInput, FilePathSource, HLS, HLS_FORMATS, type HlsSegment, type HlsSegmentedInput, Input, type InputAudioSource, type InputAudioTrack, type InputAudioTrackPairing, type InputAudioTracksOptions, InputSegment, InputSegmentedInput, type InputSubtitleSource, type InputSubtitleTrack, type InputSubtitleTrackMetadata, type InputTrack$1 as InputTrack, type InputTrackQuery, type InputTrackWithBacking, type InputVideoTrack, MP3, MP4, type MaybePromise, type MediaCodec, type SubtitleCodec, UrlSource, type VideoCodec, type VideoDynamicRange, asc, desc, isInput, prefer, preserveSubtitleBackingsOnInput };
package/dist/index.mjs CHANGED
@@ -1237,6 +1237,7 @@ const getHlsSubtitleTrackBackings = (input) => {
1237
1237
  //#endregion
1238
1238
  //#region src/mediabunny-input.ts
1239
1239
  const CUSTOM_SUBTITLE_TRACK_ID_OFFSET = 1e9;
1240
+ const CUSTOM_AUDIO_TRACK_ID_OFFSET = 2e9;
1240
1241
  const CUSTOM_PAIRING_BIT_START = 1024n;
1241
1242
  const EXTRA_PAIRING_MASK = Symbol.for("dasha.extra-pairing-mask");
1242
1243
  const ORIGINAL_GET_PAIRING_MASK = Symbol.for("dasha.original-get-pairing-mask");
@@ -1382,6 +1383,136 @@ const BACKING_TYPE_AUDIO = "audio";
1382
1383
  const BACKING_TYPE_VIDEO = "video";
1383
1384
  const BASE_INPUT_PATCHED = Symbol.for("dasha.base-mediabunny-input-patched");
1384
1385
  const PRESERVE_SUBTITLE_BACKINGS = Symbol.for("dasha.preserve-subtitle-backings");
1386
+ const getDefaultAudioTrackFormats = (source) => {
1387
+ return isLikelyDashPath(source instanceof SourceRef ? source.source : source) ? [DASH, ...ALL_FORMATS$1] : [...ALL_FORMATS$1, DASH];
1388
+ };
1389
+ var WholeResourceAudioSegmentedInput = class {
1390
+ segments = [];
1391
+ #source;
1392
+ constructor(source) {
1393
+ this.#source = source;
1394
+ }
1395
+ async runUpdateSegments() {
1396
+ if (this.segments.length > 0) return;
1397
+ const sourceWithRootPath = this.#source;
1398
+ if (typeof sourceWithRootPath.rootPath !== "string") return;
1399
+ const segment = {
1400
+ timestamp: 0,
1401
+ duration: 0,
1402
+ relativeToUnixEpoch: false,
1403
+ firstSegment: null,
1404
+ sequenceNumber: 0,
1405
+ location: {
1406
+ path: sourceWithRootPath.rootPath,
1407
+ offset: 0,
1408
+ length: null
1409
+ },
1410
+ encryption: null,
1411
+ initSegment: null,
1412
+ lastProgramDateTimeSeconds: null
1413
+ };
1414
+ segment.firstSegment = segment;
1415
+ this.segments = [segment];
1416
+ }
1417
+ };
1418
+ var ImportedAudioTrackBacking = class {
1419
+ #backing;
1420
+ #id;
1421
+ #number;
1422
+ #source;
1423
+ #wholeResourceSegmentedInput;
1424
+ constructor(params) {
1425
+ this.#backing = params.backing;
1426
+ this.#id = params.id;
1427
+ this.#number = params.number;
1428
+ this.#source = params.source;
1429
+ this.#wholeResourceSegmentedInput = new WholeResourceAudioSegmentedInput(params.source);
1430
+ }
1431
+ getType() {
1432
+ return BACKING_TYPE_AUDIO;
1433
+ }
1434
+ getId() {
1435
+ return this.#id;
1436
+ }
1437
+ getNumber() {
1438
+ return this.#number;
1439
+ }
1440
+ getCodec() {
1441
+ return this.#backing.getCodec();
1442
+ }
1443
+ getInternalCodecId() {
1444
+ return this.#backing.getInternalCodecId?.() ?? null;
1445
+ }
1446
+ getName() {
1447
+ return this.#backing.getName?.() ?? null;
1448
+ }
1449
+ getLanguageCode() {
1450
+ return this.#backing.getLanguageCode?.() ?? "und";
1451
+ }
1452
+ getTimeResolution() {
1453
+ return this.#backing.getTimeResolution?.() ?? 1e3;
1454
+ }
1455
+ isRelativeToUnixEpoch() {
1456
+ return this.#backing.isRelativeToUnixEpoch?.() ?? false;
1457
+ }
1458
+ getDisposition() {
1459
+ return this.#backing.getDisposition?.() ?? {};
1460
+ }
1461
+ getPairingMask() {
1462
+ return 0n;
1463
+ }
1464
+ getSource() {
1465
+ return this.#source;
1466
+ }
1467
+ getBitrate() {
1468
+ return this.#backing.getBitrate?.() ?? null;
1469
+ }
1470
+ getAverageBitrate() {
1471
+ return this.#backing.getAverageBitrate?.() ?? null;
1472
+ }
1473
+ getDurationFromMetadata(options) {
1474
+ return this.#backing.getDurationFromMetadata?.(options) ?? Promise.resolve(null);
1475
+ }
1476
+ getLiveRefreshInterval() {
1477
+ return this.#backing.getLiveRefreshInterval?.() ?? Promise.resolve(null);
1478
+ }
1479
+ getHasOnlyKeyPackets() {
1480
+ return true;
1481
+ }
1482
+ getDecoderConfig() {
1483
+ return this.#backing.getDecoderConfig?.() ?? Promise.resolve(null);
1484
+ }
1485
+ getMetadataCodecParameterString() {
1486
+ return this.#backing.getMetadataCodecParameterString?.() ?? null;
1487
+ }
1488
+ getNumberOfChannels() {
1489
+ return this.#backing.getNumberOfChannels?.() ?? 0;
1490
+ }
1491
+ getSampleRate() {
1492
+ return this.#backing.getSampleRate?.() ?? 0;
1493
+ }
1494
+ getFirstPacket(options) {
1495
+ return this.#backing.getFirstPacket?.(options) ?? Promise.resolve(null);
1496
+ }
1497
+ getPacket(timestamp, options) {
1498
+ return this.#backing.getPacket?.(timestamp, options) ?? Promise.resolve(null);
1499
+ }
1500
+ getNextPacket(packet, options) {
1501
+ return this.#backing.getNextPacket?.(packet, options) ?? Promise.resolve(null);
1502
+ }
1503
+ getKeyPacket(timestamp, options) {
1504
+ return this.#backing.getKeyPacket?.(timestamp, options) ?? Promise.resolve(null);
1505
+ }
1506
+ getNextKeyPacket(packet, options) {
1507
+ return this.#backing.getNextKeyPacket?.(packet, options) ?? Promise.resolve(null);
1508
+ }
1509
+ getSegmentedInput() {
1510
+ if (this.#backing.getSegmentedInput) return this.#backing.getSegmentedInput();
1511
+ const hlsBacking = this.#backing;
1512
+ if (hlsBacking.internalTrack?.demuxer?.getSegmentedInputForPath) return hlsBacking.internalTrack.demuxer.getSegmentedInputForPath(hlsBacking.internalTrack.fullPath);
1513
+ return this.#wholeResourceSegmentedInput;
1514
+ }
1515
+ };
1385
1516
  const getBackingType = (backing) => backing.getType?.();
1386
1517
  const queryWrappedTracks = (input, backings, query) => {
1387
1518
  return queryTracks(backings.map((backing) => input._wrapBackingAsTrack(backing)), query);
@@ -1414,6 +1545,9 @@ const getSegmentedInputForTrack = (track) => {
1414
1545
  return internalTrack.demuxer.getSegmentedInputForPath(internalTrack.fullPath);
1415
1546
  };
1416
1547
  const getTrackBacking = (track) => track._backing;
1548
+ const getTrackSource = (track) => {
1549
+ return getTrackBacking(track).getSource?.() ?? track.input.source;
1550
+ };
1417
1551
  const getTrackMetadataOverrides = (backing) => backing[TRACK_METADATA_OVERRIDES] ??= {};
1418
1552
  const ensureLanguageCodeOverridePatch = (backing) => {
1419
1553
  const patchedBacking = backing;
@@ -1426,6 +1560,7 @@ const setTrackLanguageCode = (track, value) => {
1426
1560
  getTrackMetadataOverrides(ensureLanguageCodeOverridePatch(getTrackBacking(track))).languageCode = value;
1427
1561
  };
1428
1562
  const addSegmentAccess = (track) => new Proxy(track, { get(target, prop) {
1563
+ if (prop === "source") return getTrackSource(target);
1429
1564
  if (prop === "getDynamicRange" && target instanceof InputVideoTrack) return () => getDynamicRangeForTrack(target);
1430
1565
  if (prop === "setLanguageCode") return (value) => setTrackLanguageCode(target, value);
1431
1566
  if (prop === "getSegmentedInput") return () => getSegmentedInputForTrack(target);
@@ -1479,13 +1614,17 @@ const preserveSubtitleBackingsOnInput = (input) => {
1479
1614
  Object.assign(input, { [PRESERVE_SUBTITLE_BACKINGS]: true });
1480
1615
  return input;
1481
1616
  };
1482
- var SegmentedMediabunnyInput = class extends Input$1 {
1617
+ var SegmentedMediabunnyInput = class SegmentedMediabunnyInput extends Input$1 {
1483
1618
  #trackCache = /* @__PURE__ */ new WeakMap();
1484
1619
  #subtitleTrackCache = /* @__PURE__ */ new WeakMap();
1485
1620
  #hlsSubtitleBackingsPromise = null;
1486
1621
  #customSubtitleBackings = [];
1622
+ #customAudioBackings = [];
1623
+ #audioInputs = [];
1487
1624
  #nextCustomSubtitleTrackId = CUSTOM_SUBTITLE_TRACK_ID_OFFSET;
1488
1625
  #nextCustomSubtitleTrackNumber = CUSTOM_SUBTITLE_TRACK_ID_OFFSET;
1626
+ #nextCustomAudioTrackId = CUSTOM_AUDIO_TRACK_ID_OFFSET;
1627
+ #nextCustomAudioTrackNumber = CUSTOM_AUDIO_TRACK_ID_OFFSET;
1489
1628
  #nextPairingBitIndex = null;
1490
1629
  async #queryTracks(query, type) {
1491
1630
  const internalInput = this;
@@ -1500,9 +1639,11 @@ var SegmentedMediabunnyInput = class extends Input$1 {
1500
1639
  return wrapped;
1501
1640
  }
1502
1641
  async _getSyntheticTrackBackings(type) {
1503
- if (type && type !== BACKING_TYPE_SUBTITLE) return [];
1504
- const backings = [...this.#customSubtitleBackings];
1505
- if (await this.getFormat() !== HLS$1) return backings;
1642
+ if (type === BACKING_TYPE_VIDEO) return [];
1643
+ const audioBackings = type !== BACKING_TYPE_SUBTITLE ? [...this.#customAudioBackings] : [];
1644
+ const backings = type !== BACKING_TYPE_AUDIO ? [...this.#customSubtitleBackings] : [];
1645
+ if (type === BACKING_TYPE_AUDIO) return audioBackings;
1646
+ if (await this.getFormat() !== HLS$1) return [...audioBackings, ...backings];
1506
1647
  if (!this.#hlsSubtitleBackingsPromise) {
1507
1648
  const promise = getHlsSubtitleTrackBackings(this).catch((error) => {
1508
1649
  if (this.#hlsSubtitleBackingsPromise === promise) this.#hlsSubtitleBackingsPromise = null;
@@ -1510,7 +1651,11 @@ var SegmentedMediabunnyInput = class extends Input$1 {
1510
1651
  });
1511
1652
  this.#hlsSubtitleBackingsPromise = promise;
1512
1653
  }
1513
- return [...backings, ...await this.#hlsSubtitleBackingsPromise];
1654
+ return [
1655
+ ...audioBackings,
1656
+ ...backings,
1657
+ ...await this.#hlsSubtitleBackingsPromise
1658
+ ];
1514
1659
  }
1515
1660
  #wrapSubtitleBacking(backing) {
1516
1661
  const existing = this.#subtitleTrackCache.get(backing);
@@ -1556,6 +1701,31 @@ var SegmentedMediabunnyInput = class extends Input$1 {
1556
1701
  this.#customSubtitleBackings.push(backing);
1557
1702
  return this._wrapBackingAsTrack(backing);
1558
1703
  }
1704
+ async addAudioTracks(source, options = {}) {
1705
+ const audioInput = new SegmentedMediabunnyInput({
1706
+ source,
1707
+ formats: [...options.formats ?? getDefaultAudioTrackFormats(source)]
1708
+ });
1709
+ this.#audioInputs.push(audioInput);
1710
+ const audioTracks = await audioInput.getAudioTracks({
1711
+ filter: options.filter,
1712
+ sortBy: options.sortBy
1713
+ });
1714
+ const pairWith = await this.#getAudioPairingVideoTracks(options.pairWith);
1715
+ const importedTracks = [];
1716
+ for (const audioTrack of audioTracks) {
1717
+ const backing = new ImportedAudioTrackBacking({
1718
+ id: this.#nextCustomAudioTrackId++,
1719
+ number: this.#nextCustomAudioTrackNumber++,
1720
+ backing: getTrackBacking(audioTrack),
1721
+ source: audioInput.source
1722
+ });
1723
+ this.#pairAudioBacking(backing, pairWith);
1724
+ this.#customAudioBackings.push(backing);
1725
+ importedTracks.push(this._wrapBackingAsTrack(backing));
1726
+ }
1727
+ return importedTracks;
1728
+ }
1559
1729
  #takeSubtitleSourceRef(source) {
1560
1730
  const rawSource = source instanceof SourceRef ? source.source : source;
1561
1731
  if (!(rawSource instanceof Object) || !("rootPath" in rawSource) || !("ref" in rawSource) || typeof rawSource.ref !== "function") throw new TypeError("source must be a pathed source such as UrlSource or FilePathSource.");
@@ -1572,13 +1742,28 @@ var SegmentedMediabunnyInput = class extends Input$1 {
1572
1742
  }
1573
1743
  return tracks;
1574
1744
  }
1745
+ async #getAudioPairingVideoTracks(pairWith) {
1746
+ if (pairWith === false) return [];
1747
+ if (!pairWith || pairWith === "all") return this.getVideoTracks();
1748
+ if (pairWith === "primary") {
1749
+ const primaryVideoTrack = await this.getPrimaryVideoTrack();
1750
+ return primaryVideoTrack ? [primaryVideoTrack] : [];
1751
+ }
1752
+ return this.#toPairableVideoTracks(pairWith);
1753
+ }
1575
1754
  #isIterable(value) {
1576
1755
  return typeof value === "object" && value !== null && Symbol.iterator in value;
1577
1756
  }
1578
1757
  #pairSubtitleBacking(subtitleBacking, videoTracks) {
1758
+ this.#pairBackingWithVideoTracks(subtitleBacking, videoTracks);
1759
+ }
1760
+ #pairAudioBacking(audioBacking, videoTracks) {
1761
+ this.#pairBackingWithVideoTracks(audioBacking, videoTracks);
1762
+ }
1763
+ #pairBackingWithVideoTracks(backing, videoTracks) {
1579
1764
  for (const track of videoTracks) {
1580
1765
  const bit = this.#allocatePairingBit();
1581
- this.#appendPairingMask(subtitleBacking, bit);
1766
+ this.#appendPairingMask(backing, bit);
1582
1767
  this.#appendPairingMask(track._backing, bit);
1583
1768
  }
1584
1769
  }
@@ -1590,7 +1775,11 @@ var SegmentedMediabunnyInput = class extends Input$1 {
1590
1775
  #getInitialPairingBitIndex() {
1591
1776
  const loadedBackings = [...this._trackBackingsCache ?? []];
1592
1777
  let maxBitIndex = -1n;
1593
- for (const backing of [...loadedBackings, ...this.#customSubtitleBackings]) {
1778
+ for (const backing of [
1779
+ ...loadedBackings,
1780
+ ...this.#customSubtitleBackings,
1781
+ ...this.#customAudioBackings
1782
+ ]) {
1594
1783
  const mask = backing.getPairingMask?.() ?? 0n;
1595
1784
  if (mask === 0n) continue;
1596
1785
  const bitIndex = BigInt(mask.toString(2).length - 1);
@@ -1605,6 +1794,12 @@ var SegmentedMediabunnyInput = class extends Input$1 {
1605
1794
  patchedBacking[ORIGINAL_GET_PAIRING_MASK] = backing.getPairingMask?.bind(backing) ?? (() => 0n);
1606
1795
  Object.assign(backing, { getPairingMask: () => (patchedBacking[ORIGINAL_GET_PAIRING_MASK]?.() ?? 0n) | (patchedBacking[EXTRA_PAIRING_MASK] ?? 0n) });
1607
1796
  }
1797
+ dispose() {
1798
+ if (this.disposed) return;
1799
+ super.dispose();
1800
+ for (const input of this.#audioInputs) input.dispose();
1801
+ this.#audioInputs.length = 0;
1802
+ }
1608
1803
  };
1609
1804
  //#endregion
1610
1805
  //#region src/dash/dash-segmented-input.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dasha",
3
- "version": "4.3.1",
3
+ "version": "4.4.1",
4
4
  "description": "Streaming manifest parser",
5
5
  "files": [
6
6
  "dist"
@@ -45,7 +45,7 @@
45
45
  }
46
46
  ],
47
47
  "engines": {
48
- "node": ">=22.18"
48
+ "node": ">=22.19"
49
49
  },
50
50
  "dependencies": {
51
51
  "@xmldom/xmldom": "^0.9.10",
@@ -55,12 +55,12 @@
55
55
  "mediabunny": "^1.45.2"
56
56
  },
57
57
  "devDependencies": {
58
- "@types/node": "^25.8.0",
59
- "oxfmt": "^0.50.0",
60
- "oxlint": "^1.65.0",
61
- "tsdown": "^0.22.0",
58
+ "@types/node": "^25.9.1",
59
+ "oxfmt": "^0.52.0",
60
+ "oxlint": "^1.67.0",
61
+ "tsdown": "^0.22.1",
62
62
  "typescript": "^6.0.3",
63
- "vitest": "^4.1.6"
63
+ "vitest": "^4.1.7"
64
64
  },
65
65
  "scripts": {
66
66
  "test": "vitest",