tiny-essentials 1.28.14 → 1.28.15

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.
@@ -33,31 +33,33 @@ import { countObj, isJsonObject } from './objChecker.mjs';
33
33
  */
34
34
  /**
35
35
  * @typedef {Object} ILyricsText
36
- * @property {string} text - The lyric text content.
36
+ * @property {string} [text] - The lyric text content.
37
37
  * @property {number} [timestamp] - The timestamp associated with this lyric line.
38
38
  */
39
39
  /**
40
40
  * @typedef {Object} TimestampFormat
41
- * @property {number} notSynchronized - Indicates if the timestamp is not synchronized.
42
- * @property {number} mpegFrameNumber - The MPEG frame number.
43
- * @property {number} milliseconds - The time in milliseconds.
41
+ * @property {number} [notSynchronized] - Indicates if the timestamp is not synchronized.
42
+ * @property {number} [mpegFrameNumber] - The MPEG frame number.
43
+ * @property {number} [milliseconds] - The time in milliseconds.
44
44
  */
45
45
  /**
46
46
  * @typedef {Object} LyricsContentType
47
- * @property {number} other - Content type for other.
48
- * @property {number} lyrics - Content type for lyrics.
49
- * @property {number} text - Content type for text.
50
- * @property {number} movement_part - Content type for movement parts.
51
- * @property {number} events - Content type for events.
52
- * @property {number} chord - Content type for chords.
53
- * @property {number} trivia_pop - Content type for trivia/pop.
47
+ * @property {number} [other] - Content type for other.
48
+ * @property {number} [lyrics] - Content type for lyrics.
49
+ * @property {number} [text] - Content type for text.
50
+ * @property {number} [movement_part] - Content type for movement parts.
51
+ * @property {number} [events] - Content type for events.
52
+ * @property {number} [chord] - Content type for chords.
53
+ * @property {number} [trivia_pop] - Content type for trivia/pop.
54
54
  */
55
55
  /**
56
56
  * @typedef {Object} ILyricsTag
57
57
  * @property {string} [text] - The text content of the lyrics.
58
- * @property {ILyricsText[]} syncText - An array of synchronized lyric text objects.
59
- * @property {TimestampFormat} timeStampFormat - The format of the timestamp.
60
- * @property {LyricsContentType} contentType - The type of lyrical content.
58
+ * @property {string} [descriptor] - A descriptor for the lyrics.
59
+ * @property {string} [language] - The language of the lyrics.
60
+ * @property {ILyricsText[]} [syncText] - An array of synchronized lyric text objects.
61
+ * @property {TimestampFormat} [timeStampFormat] - The format of the timestamp.
62
+ * @property {LyricsContentType} [contentType] - The type of lyrical content.
61
63
  */
62
64
  /**
63
65
  * @typedef {Object} IRating
@@ -623,24 +625,29 @@ export const valMediaContentMetadata = (common) => {
623
625
  isString(v.text)))
624
626
  throw new TypeError('Invalid metadata: "comment" must be an array of valid IComment objects.');
625
627
  if (!isArray(common.lyrics, (v) => {
626
- const isLyricsContentType = (/** @type {LyricsContentType} */ v) => v !== null &&
627
- typeof v === 'object' &&
628
- isNumber(v.other, true) &&
629
- isNumber(v.lyrics, true) &&
630
- isNumber(v.text, true) &&
631
- isNumber(v.movement_part, true) &&
632
- isNumber(v.events, true) &&
633
- isNumber(v.chord, true) &&
634
- isNumber(v.trivia_pop, true);
635
- const isTimestampFormat = (/** @type {TimestampFormat} */ v) => v !== null &&
636
- typeof v === 'object' &&
637
- isNumber(v.notSynchronized, true) &&
638
- isNumber(v.mpegFrameNumber, true) &&
639
- isNumber(v.milliseconds, true);
628
+ const isLyricsContentType = (/** @type {LyricsContentType|null|undefined} */ v) => v === undefined ||
629
+ (v !== null &&
630
+ typeof v === 'object' &&
631
+ isNumber(v.other) &&
632
+ isNumber(v.lyrics) &&
633
+ isNumber(v.text) &&
634
+ isNumber(v.movement_part) &&
635
+ isNumber(v.events) &&
636
+ isNumber(v.chord) &&
637
+ isNumber(v.trivia_pop));
638
+ const isTimestampFormat = (/** @type {TimestampFormat|null|undefined} */ v) => v === undefined ||
639
+ (v !== null &&
640
+ typeof v === 'object' &&
641
+ isNumber(v.notSynchronized) &&
642
+ isNumber(v.mpegFrameNumber) &&
643
+ isNumber(v.milliseconds));
640
644
  return (v !== null &&
641
645
  typeof v === 'object' &&
642
646
  isString(v.text) &&
643
- isArray(v.syncText, (v) => v !== null && typeof v === 'object' && isString(v.text, true) && isNumber(v.timestamp)) &&
647
+ isString(v.descriptor) &&
648
+ isString(v.language) &&
649
+ (!Array.isArray(v) ||
650
+ isArray(v.syncText, (v) => v !== null && typeof v === 'object' && isString(v.text) && isNumber(v.timestamp))) &&
644
651
  isTimestampFormat(v.timeStampFormat) &&
645
652
  isLyricsContentType(v.contentType));
646
653
  }))
@@ -683,172 +690,164 @@ export const extractMediaId3Tags = async (url, parseFile, convertBase64toBlob =
683
690
  // Argument Validation
684
691
  checkString(url, 'url');
685
692
  checkFunction(parseFile, 'parseFile');
686
- try {
687
- // 1. Download the file from the provided URL
688
- const response = await fetch(url);
689
- if (!response.ok)
690
- throw new Error(`Failed to fetch audio file: ${response.status} ${response.statusText}`);
691
- // 2. Convert the response into a Blob so the parser can read it
692
- const blob = await response.blob();
693
- // 3. Use the provided parser function on the Blob
694
- const metadata = await parseFile(blob);
695
- // 4. Complete Validation of the parsed metadata structure
696
- if (!metadata || typeof metadata.common !== 'object')
697
- throw new TypeError('Invalid metadata: "common" property is missing.');
698
- if (typeof metadata.common['performer:instrument'] !== undefined)
699
- // @ts-ignore
700
- metadata.common.performer_instrument = metadata.common['performer:instrument'];
701
- valFetchMediaContent(metadata);
702
- const common = metadata.common;
703
- valMediaContentMetadata(common);
704
- /**
705
- * @type {(value: [string, any]) => boolean}
706
- */
707
- const filterContent = ([_, value]) => Array.isArray(value)
708
- ? value.length > 0
709
- : isJsonObject(value)
710
- ? (() => {
711
- value;
712
- const d = Object.fromEntries(Object.entries(value).filter(filterContent));
713
- const amount = countObj(d);
714
- return amount > 0;
715
- })()
716
- : value !== null;
717
- // Fix blob values.
718
- const picture = common?.picture?.map((value) => ({
693
+ // 1. Download the file from the provided URL
694
+ const response = await fetch(url);
695
+ if (!response.ok)
696
+ throw new Error(`Failed to fetch audio file: ${response.status} ${response.statusText}`);
697
+ // 2. Convert the response into a Blob so the parser can read it
698
+ const blob = await response.blob();
699
+ // 3. Use the provided parser function on the Blob to read metadata
700
+ const metadata = await parseFile(blob);
701
+ // 4. Complete Validation of the parsed metadata structure
702
+ if (!metadata || typeof metadata.common !== 'object')
703
+ throw new TypeError('Invalid metadata: "common" property is missing.');
704
+ if (typeof metadata.common['performer:instrument'] !== undefined)
705
+ // @ts-ignore
706
+ metadata.common.performer_instrument = metadata.common['performer:instrument'];
707
+ valFetchMediaContent(metadata);
708
+ const common = metadata.common;
709
+ valMediaContentMetadata(common);
710
+ /**
711
+ * @type {(value: [string, any]) => boolean}
712
+ */
713
+ const filterContent = ([_, value]) => Array.isArray(value)
714
+ ? value.length > 0
715
+ : isJsonObject(value)
716
+ ? (() => {
717
+ value;
718
+ const d = Object.fromEntries(Object.entries(value).filter(filterContent));
719
+ const amount = countObj(d);
720
+ return amount > 0;
721
+ })()
722
+ : value !== null;
723
+ // Fix blob values.
724
+ const picture = common?.picture?.map((value) => ({
725
+ ...value,
726
+ data: convertBase64toBlob ? convertToBlobUrl(value.data, value.format) : value.data,
727
+ })) ?? [];
728
+ metadata.common = {
729
+ ...metadata.common,
730
+ picture: picture.map((value) => ({
719
731
  ...value,
720
- data: convertBase64toBlob ? convertToBlobUrl(value.data, value.format) : value.data,
721
- })) ?? [];
722
- metadata.common = {
723
- ...metadata.common,
724
- picture: picture.map((value) => ({
725
- ...value,
726
- data: '',
727
- })),
728
- };
729
- // Sanitize metadata.native to prevent Uint8Array leakage
730
- if (convertBase64toBlob) {
731
- Object.keys(metadata.native).forEach((key) => {
732
- metadata.native[key] = metadata.native[key].map((item) => {
733
- // Check if item.value is an object and contains a 'data' property that is a Uint8Array
734
- if (item.value &&
735
- typeof item.value === 'object' &&
736
- item.value.data instanceof Uint8Array) {
737
- const index = common?.picture?.findIndex((item2) => item2.data === item.value.data &&
738
- item2.format === item.value.format &&
739
- item2.description === item.value.description &&
740
- item2.name === item.value.name) ?? -1;
741
- const data = picture[index];
742
- if (data) {
743
- return { ...item, value: { ...item.value, data: '' } };
744
- }
732
+ data: '',
733
+ })),
734
+ };
735
+ // Sanitize metadata.native to prevent Uint8Array leakage
736
+ if (convertBase64toBlob) {
737
+ Object.keys(metadata.native).forEach((key) => {
738
+ metadata.native[key] = metadata.native[key].map((item) => {
739
+ // Check if item.value is an object and contains a 'data' property that is a Uint8Array
740
+ if (item.value && typeof item.value === 'object' && item.value.data instanceof Uint8Array) {
741
+ const index = common?.picture?.findIndex((item2) => item2.data === item.value.data &&
742
+ item2.format === item.value.format &&
743
+ item2.description === item.value.description &&
744
+ item2.name === item.value.name) ?? -1;
745
+ const data = picture[index];
746
+ if (data) {
747
+ return { ...item, value: { ...item.value, data: '' } };
745
748
  }
746
- return item;
747
- });
749
+ }
750
+ return item;
748
751
  });
749
- }
750
- // 5. Return the specific metadata fields requested
751
- // We structure the return to match the MediaContentMetadata typedef
752
- /** @type {MediaContentMetadata<PictureDataType>} */
753
- const result = Object.fromEntries(Object.entries({
754
- title: common?.title ?? null,
755
- album: common?.album ?? null,
756
- albumartist: common?.albumartist ?? null,
757
- albumartists: common?.albumartists ?? [],
758
- genre: common?.genre ?? [],
759
- label: common?.label ?? [],
760
- composer: common?.composer ?? [],
761
- year: common?.year ?? null,
762
- artist: common?.artist ?? null,
763
- artists: common?.artists ?? [],
764
- disk: common?.disk ? { no: common.disk.no, of: common.disk.of } : null,
765
- track: common?.track ? { no: common.track.no, of: common.track.of } : null,
766
- movementIndex: common?.movementIndex
767
- ? { no: common.movementIndex.no, of: common.movementIndex.of }
768
- : null,
769
- picture,
770
- date: common?.date ?? null,
771
- originaldate: common?.originaldate ?? null,
772
- originalyear: common?.originalyear ?? null,
773
- releasedate: common?.releasedate ?? null,
774
- comment: common?.comment ?? [],
775
- lyrics: common?.lyrics ?? [],
776
- albumsort: common?.albumsort ?? null,
777
- titlesort: common?.titlesort ?? null,
778
- work: common?.work ?? null,
779
- artistsort: common?.artistsort ?? null,
780
- albumartistsort: common?.albumartistsort ?? null,
781
- composersort: common?.composersort ?? null,
782
- lyricist: common?.lyricist ?? [],
783
- writer: common?.writer ?? [],
784
- conductor: common?.conductor ?? [],
785
- remixer: common?.remixer ?? [],
786
- arranger: common?.arranger ?? [],
787
- engineer: common?.engineer ?? [],
788
- publisher: common?.publisher ?? [],
789
- producer: common?.producer ?? [],
790
- djmixer: common?.djmixer ?? [],
791
- mixer: common?.mixer ?? [],
792
- technician: common?.technician ?? [],
793
- grouping: common?.grouping ?? null,
794
- subtitle: common?.subtitle ?? [],
795
- description: common?.description ?? [],
796
- longDescription: common?.longDescription ?? null,
797
- discsubtitle: common?.discsubtitle ?? [],
798
- totaltracks: common?.totaltracks ?? null,
799
- totaldiscs: common?.totaldiscs ?? null,
800
- movementTotal: common?.movementTotal ?? null,
801
- compilation: common?.compilation ?? null,
802
- rating: common?.rating ?? [],
803
- bpm: common?.bpm ?? null,
804
- mood: common?.mood ?? null,
805
- media: common?.media ?? null,
806
- catalognumber: common?.catalognumber ?? [],
807
- tvShow: common?.tvShow ?? null,
808
- tvShowSort: common?.tvShowSort ?? null,
809
- tvSeason: common?.tvSeason ?? null,
810
- tvEpisode: common?.tvEpisode ?? null,
811
- tvEpisodeId: common?.tvEpisodeId ?? null,
812
- tvNetwork: common?.tvNetwork ?? null,
813
- podcast: common?.podcast ?? null,
814
- podcasturl: common?.podcasturl ?? null,
815
- releasestatus: common?.releasestatus ?? null,
816
- releasetype: common?.releasetype ?? [],
817
- releasecountry: common?.releasecountry ?? null,
818
- script: common?.script ?? null,
819
- language: common?.language ?? null,
820
- copyright: common?.copyright ?? null,
821
- license: common?.license ?? null,
822
- encodedby: common?.encodedby ?? null,
823
- encodersettings: common?.encodersettings ?? null,
824
- gapless: common?.gapless ?? null,
825
- barcode: common?.barcode ?? null,
826
- isrc: common?.isrc ?? [],
827
- asin: common?.asin ?? null,
828
- website: common?.website ?? null,
829
- performer_instrument: common?.performer_instrument ?? [],
830
- averageLevel: common?.averageLevel ?? null,
831
- peakLevel: common?.peakLevel ?? null,
832
- notes: common?.notes ?? [],
833
- originalalbum: common?.originalalbum ?? null,
834
- originalartist: common?.originalartist ?? null,
835
- key: common?.key ?? null,
836
- category: common?.category ?? [],
837
- hdVideo: common?.hdVideo ?? null,
838
- keywords: common?.keywords ?? [],
839
- movement: common?.movement ?? null,
840
- podcastId: common?.podcastId ?? null,
841
- showMovement: common?.showMovement ?? null,
842
- stik: common?.stik ?? null,
843
- playCounter: common?.playCounter ?? null,
844
- }).filter(filterContent));
845
- result._fetchData = metadata;
846
- return result;
847
- }
848
- catch (error) {
849
- // Re-throwing the error allows the caller to handle specific failure cases
850
- throw error;
752
+ });
851
753
  }
754
+ // 5. Return the specific metadata fields requested
755
+ // We structure the return to match the MediaContentMetadata typedef
756
+ /** @type {MediaContentMetadata<PictureDataType>} */
757
+ const result = Object.fromEntries(Object.entries({
758
+ title: common?.title ?? null,
759
+ album: common?.album ?? null,
760
+ albumartist: common?.albumartist ?? null,
761
+ albumartists: common?.albumartists ?? [],
762
+ genre: common?.genre ?? [],
763
+ label: common?.label ?? [],
764
+ composer: common?.composer ?? [],
765
+ year: common?.year ?? null,
766
+ artist: common?.artist ?? null,
767
+ artists: common?.artists ?? [],
768
+ disk: common?.disk ? { no: common.disk.no, of: common.disk.of } : null,
769
+ track: common?.track ? { no: common.track.no, of: common.track.of } : null,
770
+ movementIndex: common?.movementIndex
771
+ ? { no: common.movementIndex.no, of: common.movementIndex.of }
772
+ : null,
773
+ picture,
774
+ date: common?.date ?? null,
775
+ originaldate: common?.originaldate ?? null,
776
+ originalyear: common?.originalyear ?? null,
777
+ releasedate: common?.releasedate ?? null,
778
+ comment: common?.comment ?? [],
779
+ lyrics: common?.lyrics ?? [],
780
+ albumsort: common?.albumsort ?? null,
781
+ titlesort: common?.titlesort ?? null,
782
+ work: common?.work ?? null,
783
+ artistsort: common?.artistsort ?? null,
784
+ albumartistsort: common?.albumartistsort ?? null,
785
+ composersort: common?.composersort ?? null,
786
+ lyricist: common?.lyricist ?? [],
787
+ writer: common?.writer ?? [],
788
+ conductor: common?.conductor ?? [],
789
+ remixer: common?.remixer ?? [],
790
+ arranger: common?.arranger ?? [],
791
+ engineer: common?.engineer ?? [],
792
+ publisher: common?.publisher ?? [],
793
+ producer: common?.producer ?? [],
794
+ djmixer: common?.djmixer ?? [],
795
+ mixer: common?.mixer ?? [],
796
+ technician: common?.technician ?? [],
797
+ grouping: common?.grouping ?? null,
798
+ subtitle: common?.subtitle ?? [],
799
+ description: common?.description ?? [],
800
+ longDescription: common?.longDescription ?? null,
801
+ discsubtitle: common?.discsubtitle ?? [],
802
+ totaltracks: common?.totaltracks ?? null,
803
+ totaldiscs: common?.totaldiscs ?? null,
804
+ movementTotal: common?.movementTotal ?? null,
805
+ compilation: common?.compilation ?? null,
806
+ rating: common?.rating ?? [],
807
+ bpm: common?.bpm ?? null,
808
+ mood: common?.mood ?? null,
809
+ media: common?.media ?? null,
810
+ catalognumber: common?.catalognumber ?? [],
811
+ tvShow: common?.tvShow ?? null,
812
+ tvShowSort: common?.tvShowSort ?? null,
813
+ tvSeason: common?.tvSeason ?? null,
814
+ tvEpisode: common?.tvEpisode ?? null,
815
+ tvEpisodeId: common?.tvEpisodeId ?? null,
816
+ tvNetwork: common?.tvNetwork ?? null,
817
+ podcast: common?.podcast ?? null,
818
+ podcasturl: common?.podcasturl ?? null,
819
+ releasestatus: common?.releasestatus ?? null,
820
+ releasetype: common?.releasetype ?? [],
821
+ releasecountry: common?.releasecountry ?? null,
822
+ script: common?.script ?? null,
823
+ language: common?.language ?? null,
824
+ copyright: common?.copyright ?? null,
825
+ license: common?.license ?? null,
826
+ encodedby: common?.encodedby ?? null,
827
+ encodersettings: common?.encodersettings ?? null,
828
+ gapless: common?.gapless ?? null,
829
+ barcode: common?.barcode ?? null,
830
+ isrc: common?.isrc ?? [],
831
+ asin: common?.asin ?? null,
832
+ website: common?.website ?? null,
833
+ performer_instrument: common?.performer_instrument ?? [],
834
+ averageLevel: common?.averageLevel ?? null,
835
+ peakLevel: common?.peakLevel ?? null,
836
+ notes: common?.notes ?? [],
837
+ originalalbum: common?.originalalbum ?? null,
838
+ originalartist: common?.originalartist ?? null,
839
+ key: common?.key ?? null,
840
+ category: common?.category ?? [],
841
+ hdVideo: common?.hdVideo ?? null,
842
+ keywords: common?.keywords ?? [],
843
+ movement: common?.movement ?? null,
844
+ podcastId: common?.podcastId ?? null,
845
+ showMovement: common?.showMovement ?? null,
846
+ stik: common?.stik ?? null,
847
+ playCounter: common?.playCounter ?? null,
848
+ }).filter(filterContent));
849
+ result._fetchData = metadata;
850
+ return result;
852
851
  };
853
852
  /**
854
853
  * A Static Factory Method that prepares a MediaContent object by
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiny-essentials",
3
- "version": "1.28.14",
3
+ "version": "1.28.15",
4
4
  "description": "Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.",
5
5
  "bin": {
6
6
  "tiny-essentials-fork": "./TinyFork.mjs"