music-metadata 11.11.2 → 11.12.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.
@@ -4,7 +4,7 @@ export interface IGenericTag {
4
4
  id: keyof ICommonTagsResult;
5
5
  value: AnyTagValue;
6
6
  }
7
- export type GenericTagId = 'track' | 'disk' | 'year' | 'title' | 'artist' | 'artists' | 'albumartist' | 'album' | 'date' | 'originaldate' | 'originalyear' | 'releasedate' | 'comment' | 'genre' | 'picture' | 'composer' | 'lyrics' | 'albumsort' | 'titlesort' | 'work' | 'artistsort' | 'albumartistsort' | 'composersort' | 'lyricist' | 'writer' | 'conductor' | 'remixer' | 'arranger' | 'engineer' | 'technician' | 'producer' | 'djmixer' | 'mixer' | 'publisher' | 'label' | 'grouping' | 'subtitle' | 'discsubtitle' | 'totaltracks' | 'totaldiscs' | 'compilation' | 'rating' | 'bpm' | 'mood' | 'media' | 'catalognumber' | 'tvShow' | 'tvShowSort' | 'tvEpisode' | 'tvEpisodeId' | 'tvNetwork' | 'tvSeason' | 'podcast' | 'podcasturl' | 'releasestatus' | 'releasetype' | 'releasecountry' | 'script' | 'language' | 'copyright' | 'license' | 'encodedby' | 'encodersettings' | 'gapless' | 'barcode' | 'isrc' | 'asin' | 'musicbrainz_recordingid' | 'musicbrainz_trackid' | 'musicbrainz_albumid' | 'musicbrainz_artistid' | 'musicbrainz_albumartistid' | 'musicbrainz_releasegroupid' | 'musicbrainz_workid' | 'musicbrainz_trmid' | 'musicbrainz_discid' | 'acoustid_id' | 'acoustid_fingerprint' | 'musicip_puid' | 'musicip_fingerprint' | 'website' | 'performer:instrument' | 'peakLevel' | 'averageLevel' | 'notes' | 'key' | 'originalalbum' | 'originalartist' | 'discogs_artist_id' | 'discogs_label_id' | 'discogs_master_release_id' | 'discogs_rating' | 'discogs_release_id' | 'discogs_votes' | 'replaygain_track_gain' | 'replaygain_track_peak' | 'replaygain_album_gain' | 'replaygain_album_peak' | 'replaygain_track_minmax' | 'replaygain_album_minmax' | 'replaygain_undo' | 'description' | 'longDescription' | 'category' | 'hdVideo' | 'keywords' | 'movement' | 'movementIndex' | 'movementTotal' | 'podcastId' | 'showMovement' | 'stik' | 'playCounter';
7
+ export type GenericTagId = 'track' | 'disk' | 'year' | 'title' | 'artist' | 'artists' | 'albumartist' | 'albumartists' | 'album' | 'date' | 'originaldate' | 'originalyear' | 'releasedate' | 'comment' | 'genre' | 'picture' | 'composer' | 'lyrics' | 'albumsort' | 'titlesort' | 'work' | 'artistsort' | 'albumartistsort' | 'composersort' | 'lyricist' | 'writer' | 'conductor' | 'remixer' | 'arranger' | 'engineer' | 'technician' | 'producer' | 'djmixer' | 'mixer' | 'publisher' | 'label' | 'grouping' | 'subtitle' | 'discsubtitle' | 'totaltracks' | 'totaldiscs' | 'compilation' | 'rating' | 'bpm' | 'mood' | 'media' | 'catalognumber' | 'tvShow' | 'tvShowSort' | 'tvEpisode' | 'tvEpisodeId' | 'tvNetwork' | 'tvSeason' | 'podcast' | 'podcasturl' | 'releasestatus' | 'releasetype' | 'releasecountry' | 'script' | 'language' | 'copyright' | 'license' | 'encodedby' | 'encodersettings' | 'gapless' | 'barcode' | 'isrc' | 'asin' | 'musicbrainz_recordingid' | 'musicbrainz_trackid' | 'musicbrainz_albumid' | 'musicbrainz_artistid' | 'musicbrainz_albumartistid' | 'musicbrainz_releasegroupid' | 'musicbrainz_workid' | 'musicbrainz_trmid' | 'musicbrainz_discid' | 'acoustid_id' | 'acoustid_fingerprint' | 'musicip_puid' | 'musicip_fingerprint' | 'website' | 'performer:instrument' | 'peakLevel' | 'averageLevel' | 'notes' | 'key' | 'originalalbum' | 'originalartist' | 'discogs_artist_id' | 'discogs_label_id' | 'discogs_master_release_id' | 'discogs_rating' | 'discogs_release_id' | 'discogs_votes' | 'replaygain_track_gain' | 'replaygain_track_peak' | 'replaygain_album_gain' | 'replaygain_album_peak' | 'replaygain_track_minmax' | 'replaygain_album_minmax' | 'replaygain_undo' | 'description' | 'longDescription' | 'category' | 'hdVideo' | 'keywords' | 'movement' | 'movementIndex' | 'movementTotal' | 'podcastId' | 'showMovement' | 'stik' | 'playCounter';
8
8
  export interface INativeTagMap {
9
9
  [index: string]: GenericTagId;
10
10
  }
@@ -9,6 +9,7 @@ const commonTags = {
9
9
  artist: defaultTagInfo,
10
10
  artists: { multiple: true, unique: true },
11
11
  albumartist: defaultTagInfo,
12
+ albumartists: { multiple: true, unique: true },
12
13
  album: defaultTagInfo,
13
14
  date: defaultTagInfo,
14
15
  originaldate: defaultTagInfo,
@@ -61,6 +61,14 @@ export declare class MetadataCollector implements INativeMetadataCollector {
61
61
  * @returns {IAudioMetadata} Native + common tags
62
62
  */
63
63
  toCommonMetadata(): IAudioMetadata;
64
+ /**
65
+ * Handle singular artist tags (artist, albumartist) and cross-populate to plural form
66
+ */
67
+ private handleSingularArtistTag;
68
+ /**
69
+ * Handle plural artist tags (artists, albumartists) and cross-populate to singular form
70
+ */
71
+ private handlePluralArtistTag;
64
72
  /**
65
73
  * Fix some common issues with picture object
66
74
  * @param picture Picture
@@ -84,26 +84,13 @@ export class MetadataCollector {
84
84
  // it is emitted to the user. e.g. genre (20) -> Electronic
85
85
  switch (tag.id) {
86
86
  case 'artist':
87
- if (this.commonOrigin.artist === this.originPriority[tagType]) {
88
- // Assume the artist field is used as artists
89
- return this.postMap('artificial', { id: 'artists', value: tag.value });
90
- }
91
- if (!this.common.artists) {
92
- // Fill artists using artist source
93
- this.setGenericTag('artificial', { id: 'artists', value: tag.value });
94
- }
95
- break;
87
+ return this.handleSingularArtistTag(tagType, tag, 'artist', 'artists');
88
+ case 'albumartist':
89
+ return this.handleSingularArtistTag(tagType, tag, 'albumartist', 'albumartists');
96
90
  case 'artists':
97
- if (!this.common.artist || this.commonOrigin.artist === this.originPriority.artificial) {
98
- if (!this.common.artists || this.common.artists.indexOf(tag.value) === -1) {
99
- // Fill artist using artists source
100
- const artists = (this.common.artists || []).concat([tag.value]);
101
- const value = joinArtists(artists);
102
- const artistTag = { id: 'artist', value };
103
- this.setGenericTag('artificial', artistTag);
104
- }
105
- }
106
- break;
91
+ return this.handlePluralArtistTag(tagType, tag, 'artist', 'artists');
92
+ case 'albumartists':
93
+ return this.handlePluralArtistTag(tagType, tag, 'albumartist', 'albumartists');
107
94
  case 'picture':
108
95
  return this.postFixPicture(tag.value).then(picture => {
109
96
  if (picture !== null) {
@@ -209,6 +196,34 @@ export class MetadataCollector {
209
196
  common: this.common
210
197
  };
211
198
  }
199
+ /**
200
+ * Handle singular artist tags (artist, albumartist) and cross-populate to plural form
201
+ */
202
+ handleSingularArtistTag(tagType, tag, singularId, pluralId) {
203
+ if (this.commonOrigin[singularId] === this.originPriority[tagType]) {
204
+ // Assume the singular field is used as plural (multiple values from same source)
205
+ return this.postMap('artificial', { id: pluralId, value: tag.value });
206
+ }
207
+ if (!this.common[pluralId]) {
208
+ // Fill plural using singular source
209
+ this.setGenericTag('artificial', { id: pluralId, value: tag.value });
210
+ }
211
+ this.setGenericTag(tagType, tag);
212
+ }
213
+ /**
214
+ * Handle plural artist tags (artists, albumartists) and cross-populate to singular form
215
+ */
216
+ handlePluralArtistTag(tagType, tag, singularId, pluralId) {
217
+ if (!this.common[singularId] || this.commonOrigin[singularId] === this.originPriority.artificial) {
218
+ if (!this.common[pluralId] || this.common[pluralId].indexOf(tag.value) === -1) {
219
+ // Fill singular using plural source
220
+ const values = (this.common[pluralId] || []).concat([tag.value]);
221
+ const value = joinArtists(values);
222
+ this.setGenericTag('artificial', { id: singularId, value });
223
+ }
224
+ }
225
+ this.setGenericTag(tagType, tag);
226
+ }
212
227
  /**
213
228
  * Fix some common issues with picture object
214
229
  * @param picture Picture
package/lib/type.d.ts CHANGED
@@ -69,9 +69,13 @@ export interface ICommonTagsResult {
69
69
  */
70
70
  artists?: string[];
71
71
  /**
72
- * Track album artists
72
+ * Track album artist/s. Contains the first album artist if multiple tags exist, or maybe several album artists written in a single string.
73
73
  */
74
74
  albumartist?: string;
75
+ /**
76
+ * Track album artists, aims to capture every album artist in a different string.
77
+ */
78
+ albumartists?: string[];
75
79
  /**
76
80
  * Album title
77
81
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "music-metadata",
3
3
  "description": "Music metadata parser for Node.js, supporting virtual any audio and tag format.",
4
- "version": "11.11.2",
4
+ "version": "11.12.0",
5
5
  "author": {
6
6
  "name": "Borewit",
7
7
  "url": "https://github.com/Borewit"
@@ -120,14 +120,14 @@
120
120
  "win-guid": "^0.2.1"
121
121
  },
122
122
  "devDependencies": {
123
- "@biomejs/biome": "2.3.13",
123
+ "@biomejs/biome": "2.3.14",
124
124
  "@types/chai": "^5.2.3",
125
125
  "@types/chai-as-promised": "^8.0.2",
126
126
  "@types/content-type": "^1.1.9",
127
127
  "@types/debug": "^4.1.12",
128
128
  "@types/media-typer": "^1.1.3",
129
129
  "@types/mocha": "^10.0.10",
130
- "@types/node": "^25.1.0",
130
+ "@types/node": "^25.2.0",
131
131
  "c8": "^10.1.3",
132
132
  "chai": "^6.2.2",
133
133
  "chai-as-promised": "^8.0.2",