mediabunny 1.17.2 → 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 (42) hide show
  1. package/dist/bundles/mediabunny.cjs +191 -39
  2. package/dist/bundles/mediabunny.min.cjs +4 -4
  3. package/dist/bundles/mediabunny.min.mjs +4 -4
  4. package/dist/bundles/mediabunny.mjs +191 -39
  5. package/dist/mediabunny.d.ts +32 -3
  6. package/dist/modules/shared/mp3-misc.js +3 -3
  7. package/dist/modules/src/flac/flac-demuxer.d.ts.map +1 -1
  8. package/dist/modules/src/flac/flac-demuxer.js +16 -6
  9. package/dist/modules/src/index.d.ts +1 -1
  10. package/dist/modules/src/index.d.ts.map +1 -1
  11. package/dist/modules/src/index.js +1 -1
  12. package/dist/modules/src/input-format.js +1 -1
  13. package/dist/modules/src/matroska/ebml.d.ts +4 -1
  14. package/dist/modules/src/matroska/ebml.d.ts.map +1 -1
  15. package/dist/modules/src/matroska/ebml.js +49 -0
  16. package/dist/modules/src/matroska/matroska-demuxer.d.ts +1 -0
  17. package/dist/modules/src/matroska/matroska-demuxer.d.ts.map +1 -1
  18. package/dist/modules/src/matroska/matroska-demuxer.js +19 -3
  19. package/dist/modules/src/matroska/matroska-muxer.d.ts.map +1 -1
  20. package/dist/modules/src/matroska/matroska-muxer.js +65 -29
  21. package/dist/modules/src/misc.d.ts +7 -1
  22. package/dist/modules/src/misc.d.ts.map +1 -1
  23. package/dist/modules/src/misc.js +51 -13
  24. package/dist/modules/src/reader.d.ts +18 -1
  25. package/dist/modules/src/reader.d.ts.map +1 -1
  26. package/dist/modules/src/reader.js +12 -1
  27. package/dist/modules/src/source.js +3 -3
  28. package/dist/modules/src/tags.d.ts +31 -3
  29. package/dist/modules/src/tags.d.ts.map +1 -1
  30. package/dist/modules/src/tags.js +45 -2
  31. package/dist/modules/src/tsconfig.tsbuildinfo +1 -1
  32. package/package.json +1 -1
  33. package/src/flac/flac-demuxer.ts +13 -9
  34. package/src/index.ts +1 -0
  35. package/src/input-format.ts +1 -1
  36. package/src/matroska/ebml.ts +54 -4
  37. package/src/matroska/matroska-demuxer.ts +25 -3
  38. package/src/matroska/matroska-muxer.ts +54 -11
  39. package/src/misc.ts +65 -18
  40. package/src/reader.ts +7 -0
  41. package/src/source.ts +3 -3
  42. 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
  }
@@ -3207,6 +3268,7 @@ var Mediabunny = (() => {
3207
3268
  skip(byteCount) {
3208
3269
  this.bufferPos += byteCount;
3209
3270
  }
3271
+ /** Creates a new subslice of this slice whose byte range must be contained within this slice. */
3210
3272
  slice(filePos, length = this.end - filePos) {
3211
3273
  if (filePos < this.start || filePos + length > this.end) {
3212
3274
  throw new RangeError("Slicing outside of original slice.");
@@ -6667,6 +6729,25 @@ var Mediabunny = (() => {
6667
6729
  return 6;
6668
6730
  }
6669
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
+ };
6670
6751
  var measureSignedInt = (value) => {
6671
6752
  if (value >= -(1 << 6) && value < 1 << 6) {
6672
6753
  return 1;
@@ -6750,6 +6831,13 @@ var Mediabunny = (() => {
6750
6831
  }
6751
6832
  this.writer.write(this.helper.subarray(0, pos));
6752
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
+ }
6753
6841
  writeSignedInt(value, width = measureSignedInt(value)) {
6754
6842
  if (value < 0) {
6755
6843
  value += 2 ** (width * 8);
@@ -6833,6 +6921,10 @@ var Mediabunny = (() => {
6833
6921
  const size = data.size ?? measureUnsignedInt(data.data);
6834
6922
  this.writeVarInt(size);
6835
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);
6836
6928
  } else if (typeof data.data === "string") {
6837
6929
  this.writeVarInt(data.data.length);
6838
6930
  this.writeAsciiString(data.data);
@@ -6905,6 +6997,17 @@ var Mediabunny = (() => {
6905
6997
  }
6906
6998
  return value;
6907
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
+ };
6908
7011
  var readSignedInt = (slice, width) => {
6909
7012
  let value = readUnsignedInt(slice, width);
6910
7013
  if (value & 1 << width * 8 - 1) {
@@ -7417,11 +7520,10 @@ var Mediabunny = (() => {
7417
7520
  }
7418
7521
  maybeCreateAttachments() {
7419
7522
  const metadataTags = this.output._metadataTags;
7420
- if (!metadataTags.images || metadataTags.images.length === 0) {
7421
- return;
7422
- }
7523
+ const elements = [];
7423
7524
  const existingFileUids = /* @__PURE__ */ new Set();
7424
- this.attachmentsElement = { id: 423732329 /* Attachments */, data: metadataTags.images.map((image) => {
7525
+ const images = metadataTags.images ?? [];
7526
+ for (const image of images) {
7425
7527
  let imageName = image.name;
7426
7528
  if (imageName === void 0) {
7427
7529
  const baseName = image.kind === "coverFront" ? "cover" : image.kind === "coverBack" ? "back" : "image";
@@ -7429,13 +7531,17 @@ var Mediabunny = (() => {
7429
7531
  }
7430
7532
  let fileUid;
7431
7533
  while (true) {
7432
- fileUid = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
7433
- 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)) {
7434
7540
  break;
7435
7541
  }
7436
7542
  }
7437
7543
  existingFileUids.add(fileUid);
7438
- return {
7544
+ elements.push({
7439
7545
  id: 24999 /* AttachedFile */,
7440
7546
  data: [
7441
7547
  image.description !== void 0 ? { id: 18046 /* FileDescription */, data: new EBMLUnicodeString(image.description) } : null,
@@ -7444,8 +7550,34 @@ var Mediabunny = (() => {
7444
7550
  { id: 18012 /* FileData */, data: image.data },
7445
7551
  { id: 18094 /* FileUID */, data: fileUid }
7446
7552
  ]
7447
- };
7448
- }) };
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 };
7449
7581
  }
7450
7582
  createSegment() {
7451
7583
  this.createTracks();
@@ -8053,11 +8185,11 @@ ${cue.notes ?? ""}`;
8053
8185
  if (layer === 0) {
8054
8186
  return 0;
8055
8187
  } else if (layer === 1) {
8056
- return Math.round(144 * bitrate / (sampleRate << lowSamplingFrequency)) + padding;
8188
+ return Math.floor(144 * bitrate / (sampleRate << lowSamplingFrequency)) + padding;
8057
8189
  } else if (layer === 2) {
8058
- return Math.round(144 * bitrate / sampleRate) + padding;
8190
+ return Math.floor(144 * bitrate / sampleRate) + padding;
8059
8191
  } else {
8060
- return (Math.round(12 * bitrate / sampleRate) + padding) * 4;
8192
+ return (Math.floor(12 * bitrate / sampleRate) + padding) * 4;
8061
8193
  }
8062
8194
  };
8063
8195
  var getXingOffset = (mpegVersionId, channel) => {
@@ -15590,7 +15722,7 @@ ${cue.notes ?? ""}`;
15590
15722
  const abortController = new AbortController();
15591
15723
  const response = await retriedFetch(
15592
15724
  this._url,
15593
- mergeObjectsDeeply(this._options.requestInit ?? {}, {
15725
+ mergeRequestInit(this._options.requestInit ?? {}, {
15594
15726
  headers: {
15595
15727
  // We could also send a non-range request to request the same bytes (all of them), but doing it like
15596
15728
  // this is an easy way to check if the server supports range requests in the first place
@@ -15641,7 +15773,7 @@ ${cue.notes ?? ""}`;
15641
15773
  abortController = new AbortController();
15642
15774
  response = await retriedFetch(
15643
15775
  this._url,
15644
- mergeObjectsDeeply(this._options.requestInit ?? {}, {
15776
+ mergeRequestInit(this._options.requestInit ?? {}, {
15645
15777
  headers: {
15646
15778
  Range: `bytes=${worker.currentPos}-`
15647
15779
  },
@@ -20072,12 +20204,23 @@ ${cue.notes ?? ""}`;
20072
20204
  {
20073
20205
  if (!this.currentSegment) break;
20074
20206
  this.currentAttachedFile = {
20207
+ fileUid: null,
20075
20208
  fileName: null,
20076
20209
  fileMediaType: null,
20077
20210
  fileData: null,
20078
20211
  fileDescription: null
20079
20212
  };
20080
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
+ }
20081
20224
  if (this.currentAttachedFile.fileMediaType?.startsWith("image/") && this.currentAttachedFile.fileData) {
20082
20225
  const fileName = this.currentAttachedFile.fileName;
20083
20226
  let kind = "unknown";
@@ -20089,8 +20232,8 @@ ${cue.notes ?? ""}`;
20089
20232
  kind = "coverBack";
20090
20233
  }
20091
20234
  }
20092
- this.currentSegment.metadataTags.images ??= [];
20093
- this.currentSegment.metadataTags.images.push({
20235
+ tags.images ??= [];
20236
+ tags.images.push({
20094
20237
  data: this.currentAttachedFile.fileData,
20095
20238
  mimeType: this.currentAttachedFile.fileMediaType,
20096
20239
  kind,
@@ -20102,6 +20245,13 @@ ${cue.notes ?? ""}`;
20102
20245
  }
20103
20246
  ;
20104
20247
  break;
20248
+ case 18094 /* FileUID */:
20249
+ {
20250
+ if (!this.currentAttachedFile) break;
20251
+ this.currentAttachedFile.fileUid = readUnsignedBigInt(slice, size);
20252
+ }
20253
+ ;
20254
+ break;
20105
20255
  case 18030 /* FileName */:
20106
20256
  {
20107
20257
  if (!this.currentAttachedFile) break;
@@ -22164,7 +22314,8 @@ ${cue.notes ?? ""}`;
22164
22314
  let currentPos = 4;
22165
22315
  return this.metadataPromise ??= (async () => {
22166
22316
  while (this.reader.fileSize === null || currentPos < this.reader.fileSize) {
22167
- const sizeSlice = await this.reader.requestSlice(currentPos, 4);
22317
+ let sizeSlice = this.reader.requestSlice(currentPos, 4);
22318
+ if (sizeSlice instanceof Promise) sizeSlice = await sizeSlice;
22168
22319
  currentPos += 4;
22169
22320
  if (sizeSlice === null) {
22170
22321
  throw new Error(
@@ -22178,10 +22329,11 @@ ${cue.notes ?? ""}`;
22178
22329
  const metaBlockType = byte & 127;
22179
22330
  switch (metaBlockType) {
22180
22331
  case 0 /* STREAMINFO */: {
22181
- const streamInfoBlock = await this.reader.requestSlice(
22332
+ let streamInfoBlock = this.reader.requestSlice(
22182
22333
  currentPos,
22183
22334
  size
22184
22335
  );
22336
+ if (streamInfoBlock instanceof Promise) streamInfoBlock = await streamInfoBlock;
22185
22337
  assert(streamInfoBlock);
22186
22338
  if (streamInfoBlock === null) {
22187
22339
  throw new Error(
@@ -22217,25 +22369,24 @@ ${cue.notes ?? ""}`;
22217
22369
  break;
22218
22370
  }
22219
22371
  case 4 /* VORBIS_COMMENT */: {
22220
- const vorbisCommentBlock = await this.reader.requestSlice(
22372
+ let vorbisCommentBlock = this.reader.requestSlice(
22221
22373
  currentPos,
22222
22374
  size
22223
22375
  );
22376
+ if (vorbisCommentBlock instanceof Promise) vorbisCommentBlock = await vorbisCommentBlock;
22224
22377
  assert(vorbisCommentBlock);
22225
22378
  readVorbisComments(
22226
- vorbisCommentBlock.bytes.subarray(
22227
- vorbisCommentBlock.start,
22228
- vorbisCommentBlock.end
22229
- ),
22379
+ readBytes(vorbisCommentBlock, size),
22230
22380
  this.metadataTags
22231
22381
  );
22232
22382
  break;
22233
22383
  }
22234
22384
  case 6 /* PICTURE */: {
22235
- const pictureBlock = await this.reader.requestSlice(
22385
+ let pictureBlock = this.reader.requestSlice(
22236
22386
  currentPos,
22237
22387
  size
22238
22388
  );
22389
+ if (pictureBlock instanceof Promise) pictureBlock = await pictureBlock;
22239
22390
  assert(pictureBlock);
22240
22391
  const pictureType = readU32Be(pictureBlock);
22241
22392
  const mediaTypeLength = readU32Be(pictureBlock);
@@ -22538,10 +22689,11 @@ ${cue.notes ?? ""}`;
22538
22689
  if (options.metadataOnly) {
22539
22690
  data = PLACEHOLDER_DATA;
22540
22691
  } else {
22541
- const slice = await this.demuxer.reader.requestSlice(
22692
+ let slice = this.demuxer.reader.requestSlice(
22542
22693
  rawSample.byteOffset,
22543
22694
  rawSample.byteSize
22544
22695
  );
22696
+ if (slice instanceof Promise) slice = await slice;
22545
22697
  if (!slice) {
22546
22698
  return null;
22547
22699
  }
@@ -22740,7 +22892,7 @@ ${cue.notes ?? ""}`;
22740
22892
  if (id3V2HeaderFound) {
22741
22893
  return true;
22742
22894
  }
22743
- currentPos = firstResult.startPos += firstResult.header.totalSize;
22895
+ currentPos = firstResult.startPos + firstResult.header.totalSize;
22744
22896
  const secondResult = await readNextFrameHeader(input._reader, currentPos, currentPos + FRAME_HEADER_SIZE);
22745
22897
  if (!secondResult) {
22746
22898
  return false;