music-metadata 7.13.4 → 7.14.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.
@@ -178,7 +178,7 @@ class MP4Parser extends BasicParser_1.BasicParser {
178
178
  this.getTrackDescription().sampleToChunkTable = stsc.entries;
179
179
  },
180
180
  /**
181
- * time to sample
181
+ * time-to-sample table
182
182
  */
183
183
  stts: async (len) => {
184
184
  const stts = await this.tokenizer.readToken(new AtomToken.SttsAtom(len));
@@ -275,13 +275,22 @@ class MP4Parser extends BasicParser_1.BasicParser {
275
275
  });
276
276
  if (audioTracks.length >= 1) {
277
277
  const audioTrack = audioTracks[0];
278
- const duration = audioTrack.duration / audioTrack.timeScale;
279
- this.metadata.setFormat('duration', duration); // calculate duration in seconds
278
+ if (audioTrack.timeScale > 0) {
279
+ const duration = audioTrack.duration / audioTrack.timeScale; // calculate duration in seconds
280
+ this.metadata.setFormat('duration', duration);
281
+ }
280
282
  const ssd = audioTrack.soundSampleDescription[0];
281
283
  if (ssd.description) {
282
284
  this.metadata.setFormat('sampleRate', ssd.description.sampleRate);
283
285
  this.metadata.setFormat('bitsPerSample', ssd.description.sampleSize);
284
286
  this.metadata.setFormat('numberOfChannels', ssd.description.numAudioChannels);
287
+ if (audioTrack.timeScale === 0 && audioTrack.timeToSampleTable.length > 0) {
288
+ const totalSampleSize = audioTrack.timeToSampleTable
289
+ .map(ttstEntry => ttstEntry.count * ttstEntry.duration)
290
+ .reduce((total, sampleSize) => total + sampleSize);
291
+ const duration = totalSampleSize / ssd.description.sampleRate;
292
+ this.metadata.setFormat('duration', duration);
293
+ }
285
294
  }
286
295
  const encoderInfo = encoderDict[ssd.dataFormat];
287
296
  if (encoderInfo) {
@@ -335,14 +344,11 @@ class MP4Parser extends BasicParser_1.BasicParser {
335
344
  case 'data': // value atom
336
345
  return this.parseValueAtom(tagKey, child);
337
346
  case 'name': // name atom (optional)
347
+ case 'mean':
348
+ case 'rate':
338
349
  const name = await this.tokenizer.readToken(new AtomToken.NameAtom(payLoadLength));
339
350
  tagKey += ':' + name.name;
340
351
  break;
341
- case 'mean': // name atom (optional)
342
- const mean = await this.tokenizer.readToken(new AtomToken.NameAtom(payLoadLength));
343
- // console.log(" %s[%s] = %s", tagKey, header.name, mean.name);
344
- tagKey += ':' + mean.name;
345
- break;
346
352
  default:
347
353
  const dataAtom = await this.tokenizer.readToken(new Token.BufferType(payLoadLength));
348
354
  this.addWarning('Unsupported meta-item: ' + tagKey + '[' + child.header.name + '] => value=' + dataAtom.toString('hex') + ' ascii=' + dataAtom.toString('ascii'));
@@ -372,9 +378,12 @@ class MP4Parser extends BasicParser_1.BasicParser {
372
378
  // console.log(" %s[data] = %s", tagKey, genreStr);
373
379
  this.addTag(tagKey, genreStr);
374
380
  break;
381
+ case 'rate':
382
+ const rate = dataAtom.value.toString('ascii');
383
+ this.addTag(tagKey, rate);
384
+ break;
375
385
  default:
376
- // console.log(" reserved-data: name=%s, len=%s, set=%s, type=%s, locale=%s, value{ hex=%s, ascii=%s }",
377
- // header.name, header.length, dataAtom.type.set, dataAtom.type.type, dataAtom.locale, dataAtom.value.toString('hex'), dataAtom.value.toString('ascii'));
386
+ debug('unknown proprietary value type for: ' + metaAtom.atomPath);
378
387
  }
379
388
  break;
380
389
  case 1: // UTF-8: Without any count or NULL terminator
@@ -1,5 +1,8 @@
1
1
  import { CaseInsensitiveTagMap } from '../common/CaseInsensitiveTagMap';
2
+ import { ITag } from "../type";
3
+ import { INativeMetadataCollector } from "../common/MetadataCollector";
2
4
  export declare const tagType = "iTunes";
3
5
  export declare class MP4TagMapper extends CaseInsensitiveTagMap {
4
6
  constructor();
7
+ protected postMap(tag: ITag, warnings: INativeMetadataCollector): void;
5
8
  }
@@ -104,13 +104,24 @@ const mp4TagMap = {
104
104
  hdvd: 'hdVideo',
105
105
  keyw: 'keywords',
106
106
  shwm: 'showMovement',
107
- stik: 'stik'
107
+ stik: 'stik',
108
+ rate: 'rating'
108
109
  };
109
110
  exports.tagType = 'iTunes';
110
111
  class MP4TagMapper extends CaseInsensitiveTagMap_1.CaseInsensitiveTagMap {
111
112
  constructor() {
112
113
  super([exports.tagType], mp4TagMap);
113
114
  }
115
+ postMap(tag, warnings) {
116
+ switch (tag.id) {
117
+ case 'rate':
118
+ tag.value = {
119
+ source: undefined,
120
+ rating: parseFloat(tag.value) / 100
121
+ };
122
+ break;
123
+ }
124
+ }
114
125
  }
115
126
  exports.MP4TagMapper = MP4TagMapper;
116
127
  //# sourceMappingURL=MP4TagMapper.js.map
@@ -1,7 +1,7 @@
1
1
  import { CommonTagMapper } from '../../common/GenericTagMapper';
2
2
  import { IRating, ITag } from '../../type';
3
3
  export declare class VorbisTagMapper extends CommonTagMapper {
4
- static toRating(email: string, rating: string): IRating;
4
+ static toRating(email: string, rating: string, maxScore: number): IRating;
5
5
  constructor();
6
6
  protected postMap(tag: ITag): void;
7
7
  }
@@ -112,19 +112,23 @@ const vorbisTagMap = {
112
112
  REPLAYGAIN_UNDO: 'replaygain_undo'
113
113
  };
114
114
  class VorbisTagMapper extends GenericTagMapper_1.CommonTagMapper {
115
- static toRating(email, rating) {
115
+ static toRating(email, rating, maxScore) {
116
116
  return {
117
117
  source: email ? email.toLowerCase() : email,
118
- rating: parseFloat(rating) * GenericTagMapper_1.CommonTagMapper.maxRatingScore
118
+ rating: (parseFloat(rating) / maxScore) * GenericTagMapper_1.CommonTagMapper.maxRatingScore
119
119
  };
120
120
  }
121
121
  constructor() {
122
122
  super(['vorbis'], vorbisTagMap);
123
123
  }
124
124
  postMap(tag) {
125
- if (tag.id.indexOf('RATING:') === 0) {
125
+ if (tag.id === 'RATING') {
126
+ // The way Winamp 5.666 assigns rating
127
+ tag.value = VorbisTagMapper.toRating(undefined, tag.value, 100);
128
+ }
129
+ else if (tag.id.indexOf('RATING:') === 0) {
126
130
  const keys = tag.id.split(':');
127
- tag.value = VorbisTagMapper.toRating(keys[1], tag.value);
131
+ tag.value = VorbisTagMapper.toRating(keys[1], tag.value, 1);
128
132
  tag.id = keys[0];
129
133
  }
130
134
  }
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": "7.13.4",
4
+ "version": "7.14.0",
5
5
  "author": {
6
6
  "name": "Borewit",
7
7
  "url": "https://github.com/Borewit"
@@ -92,30 +92,30 @@
92
92
  "token-types": "^4.2.1"
93
93
  },
94
94
  "devDependencies": {
95
- "@types/chai": "^4.3.4",
96
- "@types/chai-as-promised": "^7.1.5",
97
- "@types/debug": "^4.1.7",
95
+ "@types/chai": "^4.3.9",
96
+ "@types/chai-as-promised": "^7.1.7",
97
+ "@types/debug": "^4.1.10",
98
98
  "@types/file-type": "^10.9.1",
99
99
  "@types/mocha": "^9.1.1",
100
- "@types/node": "^18.15.11",
101
- "@typescript-eslint/eslint-plugin": "^5.57.0",
102
- "@typescript-eslint/parser": "^5.57.0",
103
- "chai": "^4.3.7",
100
+ "@types/node": "^20.8.9",
101
+ "@typescript-eslint/eslint-plugin": "^5.62.0",
102
+ "@typescript-eslint/parser": "^5.62.0",
103
+ "chai": "^4.3.10",
104
104
  "chai-as-promised": "^7.1.1",
105
- "del-cli": "5.0.0",
106
- "eslint": "^8.37.0",
107
- "eslint-config-prettier": "^8.8.0",
108
- "eslint-import-resolver-typescript": "^3.5.4",
109
- "eslint-plugin-import": "^2.27.5",
110
- "eslint-plugin-jsdoc": "^40.1.0",
105
+ "del-cli": "5.1.0",
106
+ "eslint": "^8.53.0",
107
+ "eslint-config-prettier": "^9.0.0",
108
+ "eslint-import-resolver-typescript": "^3.6.1",
109
+ "eslint-plugin-import": "^2.29.0",
110
+ "eslint-plugin-jsdoc": "^46.8.2",
111
111
  "eslint-plugin-node": "^11.1.0",
112
- "eslint-plugin-unicorn": "^46.0.0",
112
+ "eslint-plugin-unicorn": "^49.0.0",
113
113
  "mime": "^3.0.0",
114
114
  "mocha": "^9.2.2",
115
115
  "npm-run-all": "^4.1.5",
116
116
  "nyc": "^15.1.0",
117
- "remark-cli": "^11.0.0",
118
- "remark-preset-lint-recommended": "^6.1.2",
117
+ "remark-cli": "^12.0.0",
118
+ "remark-preset-lint-recommended": "^6.1.3",
119
119
  "source-map-support": "^0.5.21",
120
120
  "ts-node": "^10.9.1",
121
121
  "typescript": "^5.0.2"