music-metadata 8.1.0 → 8.1.2

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/README.md CHANGED
@@ -4,8 +4,7 @@
4
4
  [![npm downloads](http://img.shields.io/npm/dm/music-metadata.svg)](https://npmcharts.com/compare/music-metadata,jsmediatags,musicmetadata,node-id3,mp3-parser,id3-parser,wav-file-info?start=600)
5
5
  [![Coverage Status](https://coveralls.io/repos/github/Borewit/music-metadata/badge.svg?branch=master)](https://coveralls.io/github/Borewit/music-metadata?branch=master)
6
6
  [![Codacy Badge](https://api.codacy.com/project/badge/Grade/57d731b05c9e41889a2a17cb4b0384d7)](https://app.codacy.com/app/Borewit/music-metadata?utm_source=github.com&utm_medium=referral&utm_content=Borewit/music-metadata&utm_campaign=Badge_Grade_Dashboard)
7
- [![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/Borewit/music-metadata.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Borewit/music-metadata/context:javascript)
8
- [![Total alerts](https://img.shields.io/lgtm/alerts/g/Borewit/music-metadata.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Borewit/music-metadata/alerts/)
7
+ [![CodeQL](https://github.com/Borewit/music-metadata/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/Borewit/music-metadata/actions/workflows/codeql-analysis.yml)
9
8
  [![DeepScan grade](https://deepscan.io/api/teams/5165/projects/6938/branches/61821/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=5165&pid=6938&bid=61821)
10
9
  [![Known Vulnerabilities](https://snyk.io/test/github/Borewit/music-metadata/badge.svg?targetFile=package.json)](https://snyk.io/test/github/Borewit/music-metadata?targetFile=package.json)
11
10
  [![Discord](https://img.shields.io/discord/460524735235883049.svg)](https://discord.gg/KyBr6sb)
@@ -192,7 +191,7 @@ import { parseBuffer } from 'music-metadata';
192
191
 
193
192
  (async () => {
194
193
  try {
195
- const metadata = parseBuffer(someBuffer, 'audio/mpeg');
194
+ const metadata = await parseBuffer(someBuffer, 'audio/mpeg');
196
195
  console.log(metadata);
197
196
  } catch (error) {
198
197
  console.error(error.message);
@@ -334,9 +334,6 @@ MetadataLibraryObjectState.guid = GUID.MetadataLibraryObject;
334
334
  * Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd757977(v=vs.85).aspx
335
335
  */
336
336
  export class WmPictureToken {
337
- constructor(len) {
338
- this.len = len;
339
- }
340
337
  static fromBase64(base64str) {
341
338
  return this.fromBuffer(Buffer.from(base64str, 'base64'));
342
339
  }
@@ -344,6 +341,9 @@ export class WmPictureToken {
344
341
  const pic = new WmPictureToken(buffer.length);
345
342
  return pic.get(buffer, 0);
346
343
  }
344
+ constructor(len) {
345
+ this.len = len;
346
+ }
347
347
  get(buffer, offset) {
348
348
  const typeId = buffer.readUInt8(offset++);
349
349
  const size = buffer.readInt32LE(offset);
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  import { Buffer } from 'node:buffer';
3
3
  import { DataType } from './AsfObject.js';
4
- export declare type AttributeParser = (buf: Buffer) => boolean | string | number | bigint | Buffer;
4
+ export type AttributeParser = (buf: Buffer) => boolean | string | number | bigint | Buffer;
5
5
  export declare class AsfUtil {
6
6
  static getParserForAttr(i: DataType): AttributeParser;
7
7
  static parseUnicodeAttr(uint8Array: Uint8Array): string;
package/lib/asf/GUID.js CHANGED
@@ -13,9 +13,6 @@
13
13
  * - https://github.com/dji-sdk/FFmpeg/blob/master/libavformat/asf.c
14
14
  */
15
15
  export default class GUID {
16
- constructor(str) {
17
- this.str = str;
18
- }
19
16
  static fromBin(bin, offset = 0) {
20
17
  return new GUID(this.decode(bin, offset));
21
18
  }
@@ -62,6 +59,9 @@ export default class GUID {
62
59
  Buffer.from(str.slice(24), "hex").copy(bin, 10);
63
60
  return bin;
64
61
  }
62
+ constructor(str) {
63
+ this.str = str;
64
+ }
65
65
  equals(guid) {
66
66
  return this.str === guid.str;
67
67
  }
@@ -1,8 +1,4 @@
1
1
  export class CommonTagMapper {
2
- constructor(tagTypes, tagMap) {
3
- this.tagTypes = tagTypes;
4
- this.tagMap = tagMap;
5
- }
6
2
  static toIntOrNull(str) {
7
3
  const cleaned = parseInt(str, 10);
8
4
  return isNaN(cleaned) ? null : cleaned;
@@ -17,6 +13,10 @@ export class CommonTagMapper {
17
13
  of: parseInt(split[1], 10) || null
18
14
  };
19
15
  }
16
+ constructor(tagTypes, tagMap) {
17
+ this.tagTypes = tagTypes;
18
+ this.tagMap = tagMap;
19
+ }
20
20
  /**
21
21
  * Process and set common tags
22
22
  * write common tags to
@@ -1,9 +1,9 @@
1
- export declare type TagType = 'vorbis' | 'ID3v1' | 'ID3v2.2' | 'ID3v2.3' | 'ID3v2.4' | 'APEv2' | 'asf' | 'iTunes' | 'exif' | 'matroska' | 'AIFF';
1
+ export type TagType = 'vorbis' | 'ID3v1' | 'ID3v2.2' | 'ID3v2.3' | 'ID3v2.4' | 'APEv2' | 'asf' | 'iTunes' | 'exif' | 'matroska' | 'AIFF';
2
2
  export interface IGenericTag {
3
3
  id: GenericTagId;
4
4
  value: any;
5
5
  }
6
- export declare type GenericTagId = 'track' | 'disk' | 'year' | 'title' | 'artist' | 'artists' | 'albumartist' | 'album' | 'date' | 'originaldate' | 'originalyear' | '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';
6
+ export type GenericTagId = 'track' | 'disk' | 'year' | 'title' | 'artist' | 'artists' | 'albumartist' | 'album' | 'date' | 'originaldate' | 'originalyear' | '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';
7
7
  export interface INativeTagMap {
8
8
  [index: string]: GenericTagId;
9
9
  }
@@ -1,5 +1,5 @@
1
1
  import { IRatio } from '../type.js';
2
- export declare type StringEncoding = 'ascii' | 'utf8' | 'utf16le' | 'ucs2' | 'base64url' | 'latin1' | 'hex';
2
+ export type StringEncoding = 'ascii' | 'utf8' | 'utf16le' | 'ucs2' | 'base64url' | 'latin1' | 'hex';
3
3
  export interface ITextEncoding {
4
4
  encoding: StringEncoding;
5
5
  bom?: boolean;
@@ -27,7 +27,7 @@ export declare enum AttachedPictureType {
27
27
  'Band/artist logotype' = 19,
28
28
  'Publisher/Studio logotype' = 20
29
29
  }
30
- export declare type ID3v2MajorVersion = 2 | 3 | 4;
30
+ export type ID3v2MajorVersion = 2 | 3 | 4;
31
31
  export interface IExtendedHeader {
32
32
  size: number;
33
33
  extendedFlags: number;
@@ -42,9 +42,11 @@ export class MatroskaParser extends BasicParser {
42
42
  const info = matroska.segment.info;
43
43
  if (info) {
44
44
  const timecodeScale = info.timecodeScale ? info.timecodeScale : 1000000;
45
- const duration = info.duration * timecodeScale / 1000000000;
46
- this.addTag('segment:title', info.title);
47
- this.metadata.setFormat('duration', duration);
45
+ if (typeof info.duration === 'number') {
46
+ const duration = info.duration * timecodeScale / 1000000000;
47
+ this.addTag('segment:title', info.title);
48
+ this.metadata.setFormat('duration', duration);
49
+ }
48
50
  }
49
51
  const audioTracks = matroska.segment.tracks;
50
52
  if (audioTracks && audioTracks.entries) {
package/lib/mp4/Atom.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as AtomToken from './AtomToken.js';
2
2
  import { ITokenizer } from 'strtok3/core';
3
- export declare type AtomDataHandler = (atom: Atom, remaining: number) => Promise<void>;
3
+ export type AtomDataHandler = (atom: Atom, remaining: number) => Promise<void>;
4
4
  export declare class Atom {
5
5
  readonly header: AtomToken.IAtomHeader;
6
6
  extended: boolean;
package/lib/mp4/Atom.js CHANGED
@@ -2,13 +2,6 @@ import initDebug from 'debug';
2
2
  import * as AtomToken from './AtomToken.js';
3
3
  const debug = initDebug('music-metadata:parser:MP4:Atom');
4
4
  export class Atom {
5
- constructor(header, extended, parent) {
6
- this.header = header;
7
- this.extended = extended;
8
- this.parent = parent;
9
- this.children = [];
10
- this.atomPath = (this.parent ? this.parent.atomPath + '.' : '') + this.header.name;
11
- }
12
5
  static async readAtom(tokenizer, dataHandler, parent, remaining) {
13
6
  // Parse atom header
14
7
  const offset = tokenizer.position;
@@ -24,6 +17,13 @@ export class Atom {
24
17
  await atomBean.readData(tokenizer, dataHandler, payloadLength);
25
18
  return atomBean;
26
19
  }
20
+ constructor(header, extended, parent) {
21
+ this.header = header;
22
+ this.extended = extended;
23
+ this.parent = parent;
24
+ this.children = [];
25
+ this.atomPath = (this.parent ? this.parent.atomPath + '.' : '') + this.header.name;
26
+ }
27
27
  getHeaderLength() {
28
28
  return this.extended ? 16 : 8;
29
29
  }
@@ -10,9 +10,6 @@ import { SpeexParser } from './speex/SpeexParser.js';
10
10
  import { TheoraParser } from './theora/TheoraParser.js';
11
11
  const debug = initDebug('music-metadata:parser:ogg');
12
12
  export class SegmentTable {
13
- constructor(header) {
14
- this.len = header.page_segments;
15
- }
16
13
  static sum(buf, off, len) {
17
14
  let s = 0;
18
15
  for (let i = off; i < off + len; ++i) {
@@ -20,6 +17,9 @@ export class SegmentTable {
20
17
  }
21
18
  return s;
22
19
  }
20
+ constructor(header) {
21
+ this.len = header.page_segments;
22
+ }
23
23
  get(buf, off) {
24
24
  return {
25
25
  totalPageSize: SegmentTable.sum(buf, off, this.len)
@@ -7,9 +7,6 @@ import { AttachedPictureType } from '../../id3v2/ID3v2Token.js';
7
7
  * // ToDo: move to ID3 / APIC?
8
8
  */
9
9
  export class VorbisPictureToken {
10
- constructor(len) {
11
- this.len = len;
12
- }
13
10
  static fromBase64(base64str) {
14
11
  return this.fromBuffer(Buffer.from(base64str, 'base64'));
15
12
  }
@@ -17,6 +14,9 @@ export class VorbisPictureToken {
17
14
  const pic = new VorbisPictureToken(buffer.length);
18
15
  return pic.get(buffer, 0);
19
16
  }
17
+ constructor(len) {
18
+ this.len = len;
19
+ }
20
20
  get(buffer, offset) {
21
21
  const type = AttachedPictureType[Token.UINT32_BE.get(buffer, offset)];
22
22
  const mimeLen = Token.UINT32_BE.get(buffer, offset += 4);
package/lib/type.d.ts CHANGED
@@ -357,7 +357,7 @@ export interface IRatio {
357
357
  */
358
358
  dB: number;
359
359
  }
360
- export declare type FormatId = 'container' | 'duration' | 'bitrate' | 'sampleRate' | 'bitsPerSample' | 'codec' | 'tool' | 'codecProfile' | 'lossless' | 'numberOfChannels' | 'numberOfSamples' | 'audioMD5' | 'chapters' | 'modificationTime' | 'creationTime' | 'trackPeakLevel' | 'trackGain' | 'albumGain';
360
+ export type FormatId = 'container' | 'duration' | 'bitrate' | 'sampleRate' | 'bitsPerSample' | 'codec' | 'tool' | 'codecProfile' | 'lossless' | 'numberOfChannels' | 'numberOfSamples' | 'audioMD5' | 'chapters' | 'modificationTime' | 'creationTime' | 'trackPeakLevel' | 'trackGain' | 'albumGain';
361
361
  export interface IAudioTrack {
362
362
  samplingFrequency?: number;
363
363
  outputSamplingFrequency?: number;
@@ -508,7 +508,7 @@ export interface IAudioMetadata extends INativeAudioMetadata {
508
508
  /**
509
509
  * Corresponds with parser module name
510
510
  */
511
- export declare type ParserType = 'mpeg' | 'apev2' | 'mp4' | 'asf' | 'flac' | 'ogg' | 'aiff' | 'wavpack' | 'riff' | 'musepack' | 'dsf' | 'dsdiff' | 'adts' | 'matroska';
511
+ export type ParserType = 'mpeg' | 'apev2' | 'mp4' | 'asf' | 'flac' | 'ogg' | 'aiff' | 'wavpack' | 'riff' | 'musepack' | 'dsf' | 'dsdiff' | 'adts' | 'matroska';
512
512
  export interface IOptions {
513
513
  /**
514
514
  * default: `false`, if set to `true`, it will parse the whole media file if required to determine the duration.
@@ -571,7 +571,7 @@ export interface IMetadataEvent {
571
571
  */
572
572
  metadata: IAudioMetadata;
573
573
  }
574
- export declare type Observer = (update: IMetadataEvent) => void;
574
+ export type Observer = (update: IMetadataEvent) => void;
575
575
  /**
576
576
  * Provides random data read access
577
577
  * Used read operations on file of buffers
@@ -96,7 +96,12 @@ export class WaveParser extends BasicParser {
96
96
  this.metadata.setFormat('numberOfSamples', numberOfSamples);
97
97
  this.metadata.setFormat('duration', numberOfSamples / this.metadata.format.sampleRate);
98
98
  }
99
- this.metadata.setFormat('bitrate', this.metadata.format.numberOfChannels * this.blockAlign * this.metadata.format.sampleRate); // ToDo: check me
99
+ if (this.metadata.format.codec === 'ADPCM') { // ADPCM is 4 bits lossy encoding resulting in 352kbps
100
+ this.metadata.setFormat('bitrate', 352000);
101
+ }
102
+ else {
103
+ this.metadata.setFormat('bitrate', this.blockAlign * this.metadata.format.sampleRate * 8);
104
+ }
100
105
  await this.tokenizer.ignore(header.chunkSize);
101
106
  break;
102
107
  case 'bext': // Broadcast Audio Extension chunk https://tech.ebu.ch/docs/tech/tech3285.pdf
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": "8.1.0",
4
+ "version": "8.1.2",
5
5
  "author": {
6
6
  "name": "Borewit",
7
7
  "url": "https://github.com/Borewit"
@@ -98,33 +98,33 @@
98
98
  "token-types": "^5.0.1"
99
99
  },
100
100
  "devDependencies": {
101
- "@types/chai": "^4.3.3",
101
+ "@types/chai": "^4.3.4",
102
102
  "@types/chai-as-promised": "^7.1.5",
103
103
  "@types/debug": "^4.1.7",
104
104
  "@types/file-type": "^10.9.1",
105
- "@types/mocha": "^9.1.1",
106
- "@types/node": "^18.7.18",
107
- "@typescript-eslint/eslint-plugin": "^5.37.0",
108
- "@typescript-eslint/parser": "^5.37.0",
105
+ "@types/mocha": "^10.0.0",
106
+ "@types/node": "^18.11.18",
107
+ "@typescript-eslint/eslint-plugin": "^5.48.0",
108
+ "@typescript-eslint/parser": "^5.48.0",
109
109
  "c8": "^7.12.0",
110
- "chai": "^4.3.6",
110
+ "chai": "^4.3.7",
111
111
  "chai-as-promised": "^7.1.1",
112
112
  "del-cli": "5.0.0",
113
- "eslint": "^8.23.1",
114
- "eslint-config-prettier": "^8.5.0",
115
- "eslint-import-resolver-typescript": "^3.5.1",
113
+ "eslint": "^8.31.0",
114
+ "eslint-config-prettier": "^8.6.0",
115
+ "eslint-import-resolver-typescript": "^3.5.2",
116
116
  "eslint-plugin-import": "^2.26.0",
117
- "eslint-plugin-jsdoc": "^39.3.6",
117
+ "eslint-plugin-jsdoc": "^39.6.4",
118
118
  "eslint-plugin-node": "^11.1.0",
119
- "eslint-plugin-unicorn": "^43.0.2",
119
+ "eslint-plugin-unicorn": "^45.0.2",
120
120
  "mime": "^3.0.0",
121
- "mocha": "^10.0.0",
121
+ "mocha": "^10.1.0",
122
122
  "npm-run-all": "^4.1.5",
123
- "prettier": "^2.7.1",
123
+ "prettier": "^2.8.2",
124
124
  "remark-cli": "^11.0.0",
125
125
  "remark-preset-lint-recommended": "^6.1.2",
126
126
  "ts-node": "^10.9.1",
127
- "typescript": "^4.8.3"
127
+ "typescript": "^4.9.4"
128
128
  },
129
129
  "engines": {
130
130
  "node": "^14.13.1 || >=16.0.0"