music-metadata 8.0.1 → 8.1.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.
package/README.md CHANGED
@@ -55,6 +55,7 @@ Following tag header formats are supported:
55
55
  * [iTunes](https://github.com/sergiomb2/libmp4v2/wiki/iTunesMetadata)
56
56
  * [RIFF](https://wikipedia.org/wiki/Resource_Interchange_File_Format)/INFO
57
57
  * [Vorbis comment](https://wikipedia.org/wiki/Vorbis_comment)
58
+ * [AIFF](https://wikipedia.org/wiki/Audio_Interchange_File_Format)
58
59
 
59
60
  It allows many tags to be accessed in audio format, and tag format independent way.
60
61
 
@@ -79,7 +80,7 @@ Support for encoding / format details:
79
80
 
80
81
  Module: version 8 migrated from [CommonJS](https://en.wikipedia.org/wiki/CommonJS) to [pure ECMAScript Module (ESM)](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c).
81
82
  JavaScript is compliant with [ECMAScript 2019 (ES10)](https://en.wikipedia.org/wiki/ECMAScript#10th_Edition_%E2%80%93_ECMAScript_2019).
82
- Requires Node.js ≥ 12.20 engine.
83
+ Requires Node.js ≥ 14.13.1 engine.
83
84
 
84
85
  ### Browser Support
85
86
 
@@ -91,8 +92,8 @@ import * as mm from 'music-metadata/lib/core';
91
92
  ```
92
93
 
93
94
  | function | `music-metadata` | `music-metadata/lib/core` |
94
- | -----------------------------------------------------| ---------------------------|----------------------------|
95
- | [`parseBuffer`](#parsefile-function) | ✓ | ✓ |
95
+ |------------------------------------------------------| ---------------------------|----------------------------|
96
+ | [`parseBuffer`](#parsebuffer-function) | ✓ | ✓ |
96
97
  | [`parseStream`](#parsestream-function) * | ✓ | ✓ |
97
98
  | [`parseFromTokenizer`](#parsefromtokenizer-function) | ✓ | ✓ |
98
99
  | [`parseFile`](#parsefile-function) | ✓ | |
@@ -11,4 +11,5 @@ export declare class AIFFParser extends BasicParser {
11
11
  private isCompressed;
12
12
  parse(): Promise<void>;
13
13
  readData(header: iff.IChunkHeader): Promise<number>;
14
+ readTextChunk(header: iff.IChunkHeader): Promise<number>;
14
15
  }
@@ -48,7 +48,6 @@ export class AIFFParser extends BasicParser {
48
48
  while (!this.tokenizer.fileInfo.size || this.tokenizer.fileInfo.size - this.tokenizer.position >= iff.Header.len) {
49
49
  debug('Reading AIFF chunk at offset=' + this.tokenizer.position);
50
50
  const chunkHeader = await this.tokenizer.readToken(iff.Header);
51
- debug(`Chunk id=${chunkHeader.chunkID}`);
52
51
  const nextChunk = 2 * Math.round(chunkHeader.chunkSize / 2);
53
52
  const bytesRead = await this.readData(chunkHeader);
54
53
  await this.tokenizer.ignore(nextChunk - bytesRead);
@@ -85,8 +84,21 @@ export class AIFFParser extends BasicParser {
85
84
  this.metadata.setFormat('bitrate', 8 * header.chunkSize / this.metadata.format.duration);
86
85
  }
87
86
  return 0;
87
+ case 'NAME': // Sample name chunk
88
+ case 'AUTH': // Author chunk
89
+ case '(c) ': // Copyright chunk
90
+ case 'ANNO': // Annotation chunk
91
+ return this.readTextChunk(header);
88
92
  default:
93
+ debug(`Ignore chunk id=${header.chunkID}, size=${header.chunkSize}`);
89
94
  return 0;
90
95
  }
91
96
  }
97
+ async readTextChunk(header) {
98
+ const value = await this.tokenizer.readToken(new Token.StringType(header.chunkSize, 'ascii'));
99
+ value.split('\0').map(v => v.trim()).filter(v => v && v.length > 0).forEach(v => {
100
+ this.metadata.addTag('AIFF', header.chunkID, v.trim());
101
+ });
102
+ return header.chunkSize;
103
+ }
92
104
  }
@@ -0,0 +1,4 @@
1
+ import { CommonTagMapper } from '../common/GenericTagMapper.js';
2
+ export declare class AiffTagMapper extends CommonTagMapper {
3
+ constructor();
4
+ }
@@ -0,0 +1,16 @@
1
+ import { CommonTagMapper } from '../common/GenericTagMapper.js';
2
+ /**
3
+ * ID3v1 tag mappings
4
+ */
5
+ const tagMap = {
6
+ NAME: 'title',
7
+ AUTH: 'artist',
8
+ '(c) ': 'copyright',
9
+ ANNO: 'comment'
10
+ };
11
+ export class AiffTagMapper extends CommonTagMapper {
12
+ constructor() {
13
+ super(['AIFF'], tagMap);
14
+ }
15
+ }
16
+ //# sourceMappingURL=AiffTagMap.js.map
@@ -7,6 +7,7 @@ import { MP4TagMapper } from '../mp4/MP4TagMapper.js';
7
7
  import { VorbisTagMapper } from '../ogg/vorbis/VorbisTagMapper.js';
8
8
  import { RiffInfoTagMapper } from '../riff/RiffInfoTagMap.js';
9
9
  import { MatroskaTagMapper } from '../matroska/MatroskaTagMapper.js';
10
+ import { AiffTagMapper } from '../aiff/AiffTagMap.js';
10
11
  export class CombinedTagMapper {
11
12
  constructor() {
12
13
  this.tagMappers = {};
@@ -20,7 +21,8 @@ export class CombinedTagMapper {
20
21
  new APEv2TagMapper(),
21
22
  new AsfTagMapper(),
22
23
  new RiffInfoTagMapper(),
23
- new MatroskaTagMapper()
24
+ new MatroskaTagMapper(),
25
+ new AiffTagMapper()
24
26
  ].forEach(mapper => {
25
27
  this.registerTagMapper(mapper);
26
28
  });
@@ -8,11 +8,8 @@ export const FourCcToken = {
8
8
  len: 4,
9
9
  get: (buf, off) => {
10
10
  const id = buf.toString('binary', off, off + FourCcToken.len);
11
- switch (id) {
12
- default:
13
- if (!id.match(validFourCC)) {
14
- throw new Error(`FourCC contains invalid characters: ${util.a2hex(id)} "${id}"`);
15
- }
11
+ if (!id.match(validFourCC)) {
12
+ throw new Error(`FourCC contains invalid characters: ${util.a2hex(id)} "${id}"`);
16
13
  }
17
14
  return id;
18
15
  },
@@ -1,4 +1,4 @@
1
- export declare type TagType = 'vorbis' | 'ID3v1' | 'ID3v2.2' | 'ID3v2.3' | 'ID3v2.4' | 'APEv2' | 'asf' | 'iTunes' | 'exif' | 'matroska';
1
+ export declare 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;
@@ -6,7 +6,7 @@ import { CommonTagMapper } from './GenericTagMapper.js';
6
6
  import { toRatio } from './Util.js';
7
7
  import { fileTypeFromBuffer } from 'file-type';
8
8
  const debug = initDebug('music-metadata:collector');
9
- const TagPriority = ['matroska', 'APEv2', 'vorbis', 'ID3v2.4', 'ID3v2.3', 'ID3v2.2', 'exif', 'asf', 'iTunes', 'ID3v1'];
9
+ const TagPriority = ['matroska', 'APEv2', 'vorbis', 'ID3v2.4', 'ID3v2.3', 'ID3v2.2', 'exif', 'asf', 'iTunes', 'AIFF', 'ID3v1'];
10
10
  /**
11
11
  * Provided to the parser to uodate the metadata result.
12
12
  * Responsible for triggering async updates
@@ -41,7 +41,7 @@ export class MetadataCollector {
41
41
  this.originPriority[tagType] = priority++;
42
42
  }
43
43
  this.originPriority.artificial = 500; // Filled using alternative tags
44
- this.originPriority.id3v1 = 600; // Consider worst due to field length limit
44
+ this.originPriority.id3v1 = 600; // Consider as the worst because of the field length limit
45
45
  }
46
46
  /**
47
47
  * @returns {boolean} true if one or more tags have been found
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.0.1",
4
+ "version": "8.1.0",
5
5
  "author": {
6
6
  "name": "Borewit",
7
7
  "url": "https://github.com/Borewit"
@@ -92,29 +92,29 @@
92
92
  "@tokenizer/token": "^0.3.0",
93
93
  "content-type": "^1.0.4",
94
94
  "debug": "^4.3.4",
95
- "file-type": "^17.1.6",
95
+ "file-type": "^18.0.0",
96
96
  "media-typer": "^1.1.0",
97
97
  "strtok3": "^7.0.0",
98
98
  "token-types": "^5.0.1"
99
99
  },
100
100
  "devDependencies": {
101
- "@types/chai": "^4.3.1",
101
+ "@types/chai": "^4.3.3",
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.0",
106
- "@types/node": "^18.6.4",
107
- "@typescript-eslint/eslint-plugin": "^5.32.0",
108
- "@typescript-eslint/parser": "^5.32.0",
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",
109
109
  "c8": "^7.12.0",
110
110
  "chai": "^4.3.6",
111
111
  "chai-as-promised": "^7.1.1",
112
112
  "del-cli": "5.0.0",
113
- "eslint": "^8.21.0",
113
+ "eslint": "^8.23.1",
114
114
  "eslint-config-prettier": "^8.5.0",
115
- "eslint-import-resolver-typescript": "^3.4.0",
115
+ "eslint-import-resolver-typescript": "^3.5.1",
116
116
  "eslint-plugin-import": "^2.26.0",
117
- "eslint-plugin-jsdoc": "^39.3.4",
117
+ "eslint-plugin-jsdoc": "^39.3.6",
118
118
  "eslint-plugin-node": "^11.1.0",
119
119
  "eslint-plugin-unicorn": "^43.0.2",
120
120
  "mime": "^3.0.0",
@@ -124,10 +124,10 @@
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.7.4"
127
+ "typescript": "^4.8.3"
128
128
  },
129
129
  "engines": {
130
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
130
+ "node": "^14.13.1 || >=16.0.0"
131
131
  },
132
132
  "repository": {
133
133
  "type": "git",
package/lib/browser.js DELETED
@@ -1,99 +0,0 @@
1
- import initDebug from 'debug';
2
- import { ReadableWebToNodeStream } from 'readable-web-to-node-stream';
3
- import * as mm from './core.js';
4
- export { parseBuffer, parseFromTokenizer, orderTags, ratingToStars, selectCover } from './core.js';
5
- const debug = initDebug('music-metadata-browser:main');
6
- /**
7
- * Parse audio Stream
8
- * @param stream - ReadableStream
9
- * @param contentType - MIME-Type
10
- * @param options - Parsing options
11
- * @returns Metadata
12
- */
13
- export const parseNodeStream = mm.parseStream;
14
- /**
15
- * Parse Web API ReadableStream: https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream
16
- * @param stream - ReadableStream (web stream according WTWG Streams Standard)
17
- * @param fileInfo FileInfo object or MIME-Type
18
- * @param options - Parsing options
19
- * @returns Metadata
20
- */
21
- export async function parseReadableStream(stream, fileInfo, options) {
22
- const ns = new ReadableWebToNodeStream(stream);
23
- const res = await parseNodeStream(ns, typeof fileInfo === 'string' ? { mimeType: fileInfo } : fileInfo, options);
24
- await ns.close();
25
- return res;
26
- }
27
- /**
28
- * Parse Web API File
29
- * @param blob - Blob to parse
30
- * @param options - Parsing options
31
- * @returns Metadata
32
- */
33
- export async function parseBlob(blob, options) {
34
- const fileInfo = { mimeType: blob.type, size: blob.size };
35
- if (blob instanceof File) {
36
- fileInfo.path = blob.name;
37
- }
38
- const stream = blob.stream ? blob.stream() : convertBlobToReadableStream(blob);
39
- return parseReadableStream(stream, { mimeType: blob.type, size: blob.size }, options);
40
- }
41
- /**
42
- * Convert Blob to ReadableStream
43
- * Fallback for Safari versions < 14.1
44
- * @param blob
45
- */
46
- function convertBlobToReadableStream(blob) {
47
- const fileReader = new FileReader();
48
- return new ReadableStream({
49
- start(controller) {
50
- // The following function handles each data chunk
51
- fileReader.onloadend = event => {
52
- let data = event.target.result;
53
- if (data instanceof ArrayBuffer) {
54
- data = new Uint8Array(data);
55
- }
56
- controller.enqueue(data);
57
- controller.close();
58
- };
59
- fileReader.onerror = error => {
60
- controller.close();
61
- };
62
- fileReader.onabort = error => {
63
- controller.close();
64
- };
65
- fileReader.readAsArrayBuffer(blob);
66
- }
67
- });
68
- }
69
- /**
70
- * Parse fetched file, using the Web Fetch API
71
- * @param audioTrackUrl - URL to download the audio track from
72
- * @param options - Parsing options
73
- * @returns Metadata
74
- */
75
- export async function fetchFromUrl(audioTrackUrl, options) {
76
- const response = await fetch(audioTrackUrl);
77
- const fileInfo = {
78
- size: parseInt(response.headers.get('Content-Length'), 10),
79
- mimeType: response.headers.get('Content-Type')
80
- };
81
- if (response.ok) {
82
- if (response.body) {
83
- const res = await parseReadableStream(response.body, fileInfo, options);
84
- debug('Closing HTTP-readable-stream...');
85
- if (!response.body.locked) { // Prevent error in Firefox
86
- await response.body.cancel();
87
- }
88
- debug('HTTP-readable-stream closed.');
89
- return res;
90
- }
91
- else {
92
- // Fall back on Blob
93
- return parseBlob(await response.blob(), options);
94
- }
95
- }
96
- else {
97
- throw new Error(`HTTP error status=${response.status}: ${response.statusText}`);
98
- }
99
- }