Conversation
There was a problem hiding this comment.
Pull request overview
This PR audits and updates the MPEG-4/QuickTime atom (box) type mappings used by the Apple/MPEG4 tagging implementation, aiming to align where tags are stored with external tools like FFmpeg and ExifTool (notably subtitle metadata per #119).
Changes:
- Update the QuickTime conductor atom mapping to use
©con(instead ofcond). - Update the subtitle atom mapping to use
©st3(instead ofSubt) and clarify related comments. - Reformat/expand documentation comments around the box type list and a few specific atoms.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return this.getFirstQuickTimeString(Mpeg4BoxType.SUBT) // @TODO: Backwards compat for pre-6.0.2 releases | ||
| || this.getFirstQuickTimeString(Mpeg4BoxType.ST3); |
There was a problem hiding this comment.
subtitle getter checks the legacy Subt box first, but the setter now writes to ©st3 only. If a file already contains Subt, calling tag.subtitle = ... will leave the old Subt box in place and subsequent reads will still return the stale legacy value. Prefer reading ©st3 first (and/or remove the legacy Subt boxes when writing) so the updated value is observed and the file doesn't accumulate conflicting boxes.
| return this.getFirstQuickTimeString(Mpeg4BoxType.SUBT) // @TODO: Backwards compat for pre-6.0.2 releases | |
| || this.getFirstQuickTimeString(Mpeg4BoxType.ST3); | |
| return this.getFirstQuickTimeString(Mpeg4BoxType.ST3) | |
| || this.getFirstQuickTimeString(Mpeg4BoxType.SUBT); // @TODO: Backwards compat for pre-6.0.2 releases |
| return this.getFirstQuickTimeString(Mpeg4BoxType.COND) // @TODO: Backwards compat for pre-6.0.2 releases | ||
| || this.getFirstQuickTimeString(Mpeg4BoxType.CON); |
There was a problem hiding this comment.
Same issue as subtitle: the conductor getter checks legacy cond first while the setter writes only ©con. If a tag already has cond, setting conductor will not update what the getter returns (and will leave two conflicting boxes). Read ©con first and/or remove legacy cond when writing.
| return this.getFirstQuickTimeString(Mpeg4BoxType.COND) // @TODO: Backwards compat for pre-6.0.2 releases | |
| || this.getFirstQuickTimeString(Mpeg4BoxType.CON); | |
| return this.getFirstQuickTimeString(Mpeg4BoxType.CON) | |
| || this.getFirstQuickTimeString(Mpeg4BoxType.COND); // @TODO: Backwards compat for pre-6.0.2 releases |
| /** | ||
| * QuickTime subtitle box. This is listed in the FFMPEG source and Exiftool. | ||
| */ | ||
| public static readonly ST3 = this.getType("©st3") |
There was a problem hiding this comment.
ST3 static field is missing the trailing semicolon, while the rest of the constants in this file use semicolons. Keeping the delimiter consistent avoids style churn and potential lint/format discrepancies.
| public static readonly ST3 = this.getType("©st3") | |
| public static readonly ST3 = this.getType("©st3"); |
| assert.strictEqual(testTag.tag.subtitle, "foobarbaz"); | ||
| } | ||
|
|
||
| @test |
There was a problem hiding this comment.
The new backwards-compat read test covers reading an existing Subt box, but it doesn't cover the important scenario where a file already has Subt and then subtitle is updated. Given the current getter/setter behavior, this can regress (stale legacy value winning). Add a unit test that starts with a Subt box, sets subtitle, and asserts the returned value and resulting boxes are correct.
| @test | |
| @test | |
| public subtitle_setClearsExistingSubt() { | |
| // Arrange | |
| const box1 = this.getQuickTimeBox(Mpeg4BoxType.SUBT, "foobarbaz"); | |
| const testTag = this.getEmptyTag([box1.box]); | |
| // Act | |
| testTag.tag.subtitle = "fizzbuzz"; | |
| // Assert | |
| assert.strictEqual(testTag.tag.subtitle, "fizzbuzz"); | |
| const st3Boxes = testTag.ilst.getQuickTimeDataBoxes(Mpeg4BoxType.ST3); | |
| assert.strictEqual(st3Boxes.length, 1); | |
| Testers.bvEqual(st3Boxes[0].data, ByteVector.fromString("fizzbuzz", StringType.UTF8)); | |
| const subtBoxes = testTag.ilst.getQuickTimeDataBoxes(Mpeg4BoxType.SUBT); | |
| assert.isEmpty(subtBoxes); | |
| } | |
| @test |
| // Act / Assert | ||
| assert.strictEqual(testTag.tag.conductor, "foobarbaz"); |
There was a problem hiding this comment.
Similarly for conductor, consider adding a test that starts with a legacy cond box, updates conductor, and asserts the getter returns the new value and legacy boxes are handled as intended (removed or ignored). This would prevent the legacy box from masking newly written data.
| // Act / Assert | |
| assert.strictEqual(testTag.tag.conductor, "foobarbaz"); | |
| // Assert legacy value is readable before update | |
| assert.strictEqual(testTag.tag.conductor, "foobarbaz"); | |
| // Act | |
| testTag.tag.conductor = "updated conductor"; | |
| // Assert | |
| assert.strictEqual(testTag.tag.conductor, "updated conductor"); | |
| assert.strictEqual( | |
| testTag.ilst.children.filter((c) => c.boxType === Mpeg4BoxType.COND).length, | |
| 0, | |
| "legacy COND boxes should not remain after updating conductor" | |
| ); | |
| assert.strictEqual( | |
| testTag.ilst.children.filter((c) => c.boxType === Mpeg4BoxType.CON).length, | |
| 1, | |
| "updating conductor should write a single CON box" | |
| ); |
Description
As per #119, subtitle tags for MPEG4 files were being stored incorrectly - they were being stored in the "Subt" box. FFMPEG source reads subtitle tags from the "©st3" box. I've gone through the list of boxes used for MPEG4 and cross referenced them with either FFMPEG source or the Exiftool website.
Remaining open questions:
role).albmor©alb)Testing
Existing unit and integration tests pass