diff --git a/lib/mp4/MP4Parser.ts b/lib/mp4/MP4Parser.ts index 316a2c6b5..d311fb604 100644 --- a/lib/mp4/MP4Parser.ts +++ b/lib/mp4/MP4Parser.ts @@ -296,14 +296,14 @@ export class MP4Parser extends BasicParser { return this.parseValueAtom(tagKey, child); case 'name': // name atom (optional) + case 'mean': const name = await this.tokenizer.readToken(new AtomToken.NameAtom(payLoadLength)); tagKey += ':' + name.name; break; - case 'mean': // name atom (optional) - const mean = await this.tokenizer.readToken(new AtomToken.NameAtom(payLoadLength)); - // console.log(" %s[%s] = %s", tagKey, header.name, mean.name); - tagKey += ':' + mean.name; + case 'rate': + const rate = await this.tokenizer.readToken(new AtomToken.NameAtom(payLoadLength)); + tagKey += ':' + rate.name; break; default: @@ -342,6 +342,11 @@ export class MP4Parser extends BasicParser { this.addTag(tagKey, genreStr); break; + case 'rate': + const rate = new Token.StringType(2, 'ascii').get(dataAtom.value, 0); + this.addTag(tagKey, rate); + break; + default: // console.log(" reserved-data: name=%s, len=%s, set=%s, type=%s, locale=%s, value{ hex=%s, ascii=%s }", // header.name, header.length, dataAtom.type.set, dataAtom.type.type, dataAtom.locale, dataAtom.value.toString('hex'), dataAtom.value.toString('ascii')); diff --git a/lib/mp4/MP4TagMapper.ts b/lib/mp4/MP4TagMapper.ts index b6f6f6c91..6c446549a 100644 --- a/lib/mp4/MP4TagMapper.ts +++ b/lib/mp4/MP4TagMapper.ts @@ -1,5 +1,9 @@ import { CaseInsensitiveTagMap } from '../common/CaseInsensitiveTagMap.js'; import { INativeTagMap } from '../common/GenericTagTypes.js'; +import {ITag} from "../type.js"; +import {INativeMetadataCollector} from "../common/MetadataCollector.js"; +import * as util from "../common/Util.js"; +import {CommonTagMapper} from "../common/GenericTagMapper.js"; /** * Ref: https://github.com/sergiomb2/libmp4v2/wiki/iTunesMetadata @@ -105,7 +109,8 @@ const mp4TagMap: INativeTagMap = { hdvd: 'hdVideo', keyw: 'keywords', shwm: 'showMovement', - stik: 'stik' + stik: 'stik', + rate: 'rating' }; export const tagType = 'iTunes'; @@ -116,4 +121,17 @@ export class MP4TagMapper extends CaseInsensitiveTagMap { super([tagType], mp4TagMap); } + protected postMap(tag: ITag, warnings: INativeMetadataCollector): void { + + switch (tag.id) { + + case 'rate': + tag.value = { + source: undefined, + rating: parseFloat(tag.value) / 100 + }; + break; + } + } + } diff --git a/test/samples/rating/testcase.m4a b/test/samples/rating/testcase.m4a new file mode 100644 index 000000000..c2ecb8086 Binary files /dev/null and b/test/samples/rating/testcase.m4a differ diff --git a/test/test-file-mp4.ts b/test/test-file-mp4.ts index c0f07bb64..55b7ac816 100644 --- a/test/test-file-mp4.ts +++ b/test/test-file-mp4.ts @@ -460,4 +460,14 @@ describe('Parse MPEG-4 files with iTunes metadata', () => { assert.strictEqual('S2E32 : Audio', common.title, 'common.title'); }); + it('moov.udta.meta.ilst.rate mapping', async () => { + + const filePath = path.join(samplePath, 'rating', 'testcase.m4a'); + const {format, common, native} = await mm.parseFile(filePath); + + assert.isDefined(common.rating, 'Expect rating property to be present'); + assert.equal(common.rating[0].rating, 0.80, 'Vorbis tag rating score of 80%'); + assert.equal(mm.ratingToStars(common.rating[0].rating), 4, 'Vorbis tag rating conversion'); + }); + });