mediabunny 1.17.3 → 1.18.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.
Files changed (35) hide show
  1. package/dist/bundles/mediabunny.cjs +179 -30
  2. package/dist/bundles/mediabunny.min.cjs +4 -4
  3. package/dist/bundles/mediabunny.min.mjs +4 -4
  4. package/dist/bundles/mediabunny.mjs +179 -30
  5. package/dist/mediabunny.d.ts +32 -3
  6. package/dist/modules/shared/mp3-misc.js +3 -3
  7. package/dist/modules/src/index.d.ts +1 -1
  8. package/dist/modules/src/index.d.ts.map +1 -1
  9. package/dist/modules/src/index.js +1 -1
  10. package/dist/modules/src/input-format.js +1 -1
  11. package/dist/modules/src/matroska/ebml.d.ts +4 -1
  12. package/dist/modules/src/matroska/ebml.d.ts.map +1 -1
  13. package/dist/modules/src/matroska/ebml.js +49 -0
  14. package/dist/modules/src/matroska/matroska-demuxer.d.ts +1 -0
  15. package/dist/modules/src/matroska/matroska-demuxer.d.ts.map +1 -1
  16. package/dist/modules/src/matroska/matroska-demuxer.js +19 -3
  17. package/dist/modules/src/matroska/matroska-muxer.d.ts.map +1 -1
  18. package/dist/modules/src/matroska/matroska-muxer.js +65 -29
  19. package/dist/modules/src/misc.d.ts +7 -1
  20. package/dist/modules/src/misc.d.ts.map +1 -1
  21. package/dist/modules/src/misc.js +51 -13
  22. package/dist/modules/src/source.js +3 -3
  23. package/dist/modules/src/tags.d.ts +31 -3
  24. package/dist/modules/src/tags.d.ts.map +1 -1
  25. package/dist/modules/src/tags.js +45 -2
  26. package/dist/modules/src/tsconfig.tsbuildinfo +1 -1
  27. package/package.json +1 -1
  28. package/src/index.ts +1 -0
  29. package/src/input-format.ts +1 -1
  30. package/src/matroska/ebml.ts +54 -4
  31. package/src/matroska/matroska-demuxer.ts +25 -3
  32. package/src/matroska/matroska-muxer.ts +54 -11
  33. package/src/misc.ts +65 -18
  34. package/src/source.ts +3 -3
  35. package/src/tags.ts +48 -5
@@ -53,6 +53,7 @@ var Mediabunny = (() => {
53
53
  AUDIO_CODECS: () => AUDIO_CODECS,
54
54
  AdtsInputFormat: () => AdtsInputFormat,
55
55
  AdtsOutputFormat: () => AdtsOutputFormat,
56
+ AttachedFile: () => AttachedFile,
56
57
  AudioBufferSink: () => AudioBufferSink,
57
58
  AudioBufferSource: () => AudioBufferSource,
58
59
  AudioSample: () => AudioSample,
@@ -526,19 +527,41 @@ var Mediabunny = (() => {
526
527
  return ISO_639_2_REGEX.test(x);
527
528
  };
528
529
  var SECOND_TO_MICROSECOND_FACTOR = 1e6 * (1 + Number.EPSILON);
529
- var mergeObjectsDeeply = (a, b) => {
530
- const result = { ...a };
531
- for (const key in b) {
532
- if (typeof a[key] === "object" && a[key] !== null && typeof b[key] === "object" && b[key] !== null) {
533
- result[key] = mergeObjectsDeeply(
534
- a[key],
535
- b[key]
530
+ var mergeRequestInit = (init1, init2) => {
531
+ const merged = { ...init1, ...init2 };
532
+ if (init1.headers || init2.headers) {
533
+ const headers1 = init1.headers ? normalizeHeaders(init1.headers) : {};
534
+ const headers2 = init2.headers ? normalizeHeaders(init2.headers) : {};
535
+ const mergedHeaders = { ...headers1 };
536
+ Object.entries(headers2).forEach(([key2, value2]) => {
537
+ const existingKey = Object.keys(mergedHeaders).find(
538
+ (key1) => key1.toLowerCase() === key2.toLowerCase()
536
539
  );
537
- } else {
538
- result[key] = b[key];
539
- }
540
+ if (existingKey) {
541
+ delete mergedHeaders[existingKey];
542
+ }
543
+ mergedHeaders[key2] = value2;
544
+ });
545
+ merged.headers = mergedHeaders;
540
546
  }
541
- return result;
547
+ return merged;
548
+ };
549
+ var normalizeHeaders = (headers) => {
550
+ if (headers instanceof Headers) {
551
+ const result = {};
552
+ headers.forEach((value, key) => {
553
+ result[key] = value;
554
+ });
555
+ return result;
556
+ }
557
+ if (Array.isArray(headers)) {
558
+ const result = {};
559
+ headers.forEach(([key, value]) => {
560
+ result[key] = value;
561
+ });
562
+ return result;
563
+ }
564
+ return headers;
542
565
  };
543
566
  var retriedFetch = async (url2, requestInit, getRetryDelay) => {
544
567
  let attempts = 0;
@@ -671,6 +694,17 @@ var Mediabunny = (() => {
671
694
  }
672
695
  return btoa(string);
673
696
  };
697
+ var uint8ArraysAreEqual = (a, b) => {
698
+ if (a.length !== b.length) {
699
+ return false;
700
+ }
701
+ for (let i = 0; i < a.length; i++) {
702
+ if (a[i] !== b[i]) {
703
+ return false;
704
+ }
705
+ }
706
+ return true;
707
+ };
674
708
 
675
709
  // src/tags.ts
676
710
  var RichImageData = class {
@@ -678,6 +712,33 @@ var Mediabunny = (() => {
678
712
  constructor(data, mimeType) {
679
713
  this.data = data;
680
714
  this.mimeType = mimeType;
715
+ if (!(data instanceof Uint8Array)) {
716
+ throw new TypeError("data must be a Uint8Array.");
717
+ }
718
+ if (typeof mimeType !== "string") {
719
+ throw new TypeError("mimeType must be a string.");
720
+ }
721
+ }
722
+ };
723
+ var AttachedFile = class {
724
+ /** Creates a new {@link AttachedFile}. */
725
+ constructor(data, mimeType, name, description) {
726
+ this.data = data;
727
+ this.mimeType = mimeType;
728
+ this.name = name;
729
+ this.description = description;
730
+ if (!(data instanceof Uint8Array)) {
731
+ throw new TypeError("data must be a Uint8Array.");
732
+ }
733
+ if (mimeType !== void 0 && typeof mimeType !== "string") {
734
+ throw new TypeError("mimeType, when provided, must be a string.");
735
+ }
736
+ if (name !== void 0 && typeof name !== "string") {
737
+ throw new TypeError("name, when provided, must be a string.");
738
+ }
739
+ if (description !== void 0 && typeof description !== "string") {
740
+ throw new TypeError("description, when provided, must be a string.");
741
+ }
681
742
  }
682
743
  };
683
744
  var validateMetadataTags = (tags) => {
@@ -747,9 +808,9 @@ var Mediabunny = (() => {
747
808
  throw new TypeError("tags.raw, when provided, must be an object.");
748
809
  }
749
810
  for (const value of Object.values(tags.raw)) {
750
- if (value !== null && typeof value !== "string" && !(value instanceof Uint8Array) && !(value instanceof RichImageData)) {
811
+ if (value !== null && typeof value !== "string" && !(value instanceof Uint8Array) && !(value instanceof RichImageData) && !(value instanceof AttachedFile)) {
751
812
  throw new TypeError(
752
- "Each value in tags.raw must be a string, Uint8Array, RichImageData, or null."
813
+ "Each value in tags.raw must be a string, Uint8Array, RichImageData, AttachedFile, or null."
753
814
  );
754
815
  }
755
816
  }
@@ -6668,6 +6729,25 @@ var Mediabunny = (() => {
6668
6729
  return 6;
6669
6730
  }
6670
6731
  };
6732
+ var measureUnsignedBigInt = (value) => {
6733
+ if (value < 1n << 8n) {
6734
+ return 1;
6735
+ } else if (value < 1n << 16n) {
6736
+ return 2;
6737
+ } else if (value < 1n << 24n) {
6738
+ return 3;
6739
+ } else if (value < 1n << 32n) {
6740
+ return 4;
6741
+ } else if (value < 1n << 40n) {
6742
+ return 5;
6743
+ } else if (value < 1n << 48n) {
6744
+ return 6;
6745
+ } else if (value < 1n << 56n) {
6746
+ return 7;
6747
+ } else {
6748
+ return 8;
6749
+ }
6750
+ };
6671
6751
  var measureSignedInt = (value) => {
6672
6752
  if (value >= -(1 << 6) && value < 1 << 6) {
6673
6753
  return 1;
@@ -6751,6 +6831,13 @@ var Mediabunny = (() => {
6751
6831
  }
6752
6832
  this.writer.write(this.helper.subarray(0, pos));
6753
6833
  }
6834
+ writeUnsignedBigInt(value, width = measureUnsignedBigInt(value)) {
6835
+ let pos = 0;
6836
+ for (let i = width - 1; i >= 0; i--) {
6837
+ this.helperView.setUint8(pos++, Number(value >> BigInt(i * 8) & 0xffn));
6838
+ }
6839
+ this.writer.write(this.helper.subarray(0, pos));
6840
+ }
6754
6841
  writeSignedInt(value, width = measureSignedInt(value)) {
6755
6842
  if (value < 0) {
6756
6843
  value += 2 ** (width * 8);
@@ -6834,6 +6921,10 @@ var Mediabunny = (() => {
6834
6921
  const size = data.size ?? measureUnsignedInt(data.data);
6835
6922
  this.writeVarInt(size);
6836
6923
  this.writeUnsignedInt(data.data, size);
6924
+ } else if (typeof data.data === "bigint") {
6925
+ const size = data.size ?? measureUnsignedBigInt(data.data);
6926
+ this.writeVarInt(size);
6927
+ this.writeUnsignedBigInt(data.data, size);
6837
6928
  } else if (typeof data.data === "string") {
6838
6929
  this.writeVarInt(data.data.length);
6839
6930
  this.writeAsciiString(data.data);
@@ -6906,6 +6997,17 @@ var Mediabunny = (() => {
6906
6997
  }
6907
6998
  return value;
6908
6999
  };
7000
+ var readUnsignedBigInt = (slice, width) => {
7001
+ if (width < 1) {
7002
+ throw new Error("Bad unsigned int size " + width);
7003
+ }
7004
+ let value = 0n;
7005
+ for (let i = 0; i < width; i++) {
7006
+ value <<= 8n;
7007
+ value += BigInt(readU8(slice));
7008
+ }
7009
+ return value;
7010
+ };
6909
7011
  var readSignedInt = (slice, width) => {
6910
7012
  let value = readUnsignedInt(slice, width);
6911
7013
  if (value & 1 << width * 8 - 1) {
@@ -7418,11 +7520,10 @@ var Mediabunny = (() => {
7418
7520
  }
7419
7521
  maybeCreateAttachments() {
7420
7522
  const metadataTags = this.output._metadataTags;
7421
- if (!metadataTags.images || metadataTags.images.length === 0) {
7422
- return;
7423
- }
7523
+ const elements = [];
7424
7524
  const existingFileUids = /* @__PURE__ */ new Set();
7425
- this.attachmentsElement = { id: 423732329 /* Attachments */, data: metadataTags.images.map((image) => {
7525
+ const images = metadataTags.images ?? [];
7526
+ for (const image of images) {
7426
7527
  let imageName = image.name;
7427
7528
  if (imageName === void 0) {
7428
7529
  const baseName = image.kind === "coverFront" ? "cover" : image.kind === "coverBack" ? "back" : "image";
@@ -7430,13 +7531,17 @@ var Mediabunny = (() => {
7430
7531
  }
7431
7532
  let fileUid;
7432
7533
  while (true) {
7433
- fileUid = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
7434
- if (fileUid !== 0 && !existingFileUids.has(fileUid)) {
7534
+ fileUid = 0n;
7535
+ for (let i = 0; i < 8; i++) {
7536
+ fileUid <<= 8n;
7537
+ fileUid |= BigInt(Math.floor(Math.random() * 256));
7538
+ }
7539
+ if (fileUid !== 0n && !existingFileUids.has(fileUid)) {
7435
7540
  break;
7436
7541
  }
7437
7542
  }
7438
7543
  existingFileUids.add(fileUid);
7439
- return {
7544
+ elements.push({
7440
7545
  id: 24999 /* AttachedFile */,
7441
7546
  data: [
7442
7547
  image.description !== void 0 ? { id: 18046 /* FileDescription */, data: new EBMLUnicodeString(image.description) } : null,
@@ -7445,8 +7550,34 @@ var Mediabunny = (() => {
7445
7550
  { id: 18012 /* FileData */, data: image.data },
7446
7551
  { id: 18094 /* FileUID */, data: fileUid }
7447
7552
  ]
7448
- };
7449
- }) };
7553
+ });
7554
+ }
7555
+ for (const [key, value] of Object.entries(metadataTags.raw ?? {})) {
7556
+ if (!(value instanceof AttachedFile)) {
7557
+ continue;
7558
+ }
7559
+ const keyIsNumeric = /^\d+$/.test(key);
7560
+ if (!keyIsNumeric) {
7561
+ continue;
7562
+ }
7563
+ if (images.find((x) => x.mimeType === value.mimeType && uint8ArraysAreEqual(x.data, value.data))) {
7564
+ continue;
7565
+ }
7566
+ elements.push({
7567
+ id: 24999 /* AttachedFile */,
7568
+ data: [
7569
+ value.description !== void 0 ? { id: 18046 /* FileDescription */, data: new EBMLUnicodeString(value.description) } : null,
7570
+ { id: 18030 /* FileName */, data: new EBMLUnicodeString(value.name ?? "") },
7571
+ { id: 18016 /* FileMediaType */, data: value.mimeType ?? "" },
7572
+ { id: 18012 /* FileData */, data: value.data },
7573
+ { id: 18094 /* FileUID */, data: BigInt(key) }
7574
+ ]
7575
+ });
7576
+ }
7577
+ if (elements.length === 0) {
7578
+ return;
7579
+ }
7580
+ this.attachmentsElement = { id: 423732329 /* Attachments */, data: elements };
7450
7581
  }
7451
7582
  createSegment() {
7452
7583
  this.createTracks();
@@ -8054,11 +8185,11 @@ ${cue.notes ?? ""}`;
8054
8185
  if (layer === 0) {
8055
8186
  return 0;
8056
8187
  } else if (layer === 1) {
8057
- return Math.round(144 * bitrate / (sampleRate << lowSamplingFrequency)) + padding;
8188
+ return Math.floor(144 * bitrate / (sampleRate << lowSamplingFrequency)) + padding;
8058
8189
  } else if (layer === 2) {
8059
- return Math.round(144 * bitrate / sampleRate) + padding;
8190
+ return Math.floor(144 * bitrate / sampleRate) + padding;
8060
8191
  } else {
8061
- return (Math.round(12 * bitrate / sampleRate) + padding) * 4;
8192
+ return (Math.floor(12 * bitrate / sampleRate) + padding) * 4;
8062
8193
  }
8063
8194
  };
8064
8195
  var getXingOffset = (mpegVersionId, channel) => {
@@ -15591,7 +15722,7 @@ ${cue.notes ?? ""}`;
15591
15722
  const abortController = new AbortController();
15592
15723
  const response = await retriedFetch(
15593
15724
  this._url,
15594
- mergeObjectsDeeply(this._options.requestInit ?? {}, {
15725
+ mergeRequestInit(this._options.requestInit ?? {}, {
15595
15726
  headers: {
15596
15727
  // We could also send a non-range request to request the same bytes (all of them), but doing it like
15597
15728
  // this is an easy way to check if the server supports range requests in the first place
@@ -15642,7 +15773,7 @@ ${cue.notes ?? ""}`;
15642
15773
  abortController = new AbortController();
15643
15774
  response = await retriedFetch(
15644
15775
  this._url,
15645
- mergeObjectsDeeply(this._options.requestInit ?? {}, {
15776
+ mergeRequestInit(this._options.requestInit ?? {}, {
15646
15777
  headers: {
15647
15778
  Range: `bytes=${worker.currentPos}-`
15648
15779
  },
@@ -20073,12 +20204,23 @@ ${cue.notes ?? ""}`;
20073
20204
  {
20074
20205
  if (!this.currentSegment) break;
20075
20206
  this.currentAttachedFile = {
20207
+ fileUid: null,
20076
20208
  fileName: null,
20077
20209
  fileMediaType: null,
20078
20210
  fileData: null,
20079
20211
  fileDescription: null
20080
20212
  };
20081
20213
  this.readContiguousElements(slice.slice(dataStartPos, size));
20214
+ const tags = this.currentSegment.metadataTags;
20215
+ if (this.currentAttachedFile.fileUid && this.currentAttachedFile.fileData) {
20216
+ tags.raw ??= {};
20217
+ tags.raw[this.currentAttachedFile.fileUid.toString()] = new AttachedFile(
20218
+ this.currentAttachedFile.fileData,
20219
+ this.currentAttachedFile.fileMediaType ?? void 0,
20220
+ this.currentAttachedFile.fileName ?? void 0,
20221
+ this.currentAttachedFile.fileDescription ?? void 0
20222
+ );
20223
+ }
20082
20224
  if (this.currentAttachedFile.fileMediaType?.startsWith("image/") && this.currentAttachedFile.fileData) {
20083
20225
  const fileName = this.currentAttachedFile.fileName;
20084
20226
  let kind = "unknown";
@@ -20090,8 +20232,8 @@ ${cue.notes ?? ""}`;
20090
20232
  kind = "coverBack";
20091
20233
  }
20092
20234
  }
20093
- this.currentSegment.metadataTags.images ??= [];
20094
- this.currentSegment.metadataTags.images.push({
20235
+ tags.images ??= [];
20236
+ tags.images.push({
20095
20237
  data: this.currentAttachedFile.fileData,
20096
20238
  mimeType: this.currentAttachedFile.fileMediaType,
20097
20239
  kind,
@@ -20103,6 +20245,13 @@ ${cue.notes ?? ""}`;
20103
20245
  }
20104
20246
  ;
20105
20247
  break;
20248
+ case 18094 /* FileUID */:
20249
+ {
20250
+ if (!this.currentAttachedFile) break;
20251
+ this.currentAttachedFile.fileUid = readUnsignedBigInt(slice, size);
20252
+ }
20253
+ ;
20254
+ break;
20106
20255
  case 18030 /* FileName */:
20107
20256
  {
20108
20257
  if (!this.currentAttachedFile) break;
@@ -22743,7 +22892,7 @@ ${cue.notes ?? ""}`;
22743
22892
  if (id3V2HeaderFound) {
22744
22893
  return true;
22745
22894
  }
22746
- currentPos = firstResult.startPos += firstResult.header.totalSize;
22895
+ currentPos = firstResult.startPos + firstResult.header.totalSize;
22747
22896
  const secondResult = await readNextFrameHeader(input._reader, currentPos, currentPos + FRAME_HEADER_SIZE);
22748
22897
  if (!secondResult) {
22749
22898
  return false;