-
Notifications
You must be signed in to change notification settings - Fork 11
Audit MPEG-4 box types #125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -55,9 +55,12 @@ export default class AppleTag extends Tag { | |||||||||
| public set title(v: string) { this.setQuickTimeString(Mpeg4BoxType.NAM, v); } | ||||||||||
|
|
||||||||||
| /** @inheritDoc */ | ||||||||||
| public get subtitle(): string { return this.getFirstQuickTimeString(Mpeg4BoxType.SUBT); } | ||||||||||
| public get subtitle(): string { | ||||||||||
| return this.getFirstQuickTimeString(Mpeg4BoxType.SUBT) // @TODO: Backwards compat for pre-6.0.2 releases | ||||||||||
| || this.getFirstQuickTimeString(Mpeg4BoxType.ST3); | ||||||||||
| } | ||||||||||
| /** @inheritDoc */ | ||||||||||
| public set subtitle(v: string) { this.setQuickTimeString(Mpeg4BoxType.SUBT, v); } | ||||||||||
| public set subtitle(v: string) { this.setQuickTimeString(Mpeg4BoxType.ST3, v); } | ||||||||||
|
|
||||||||||
| /** @inheritDoc */ | ||||||||||
| public get description(): string { return this.getFirstQuickTimeString(Mpeg4BoxType.DESC); } | ||||||||||
|
|
@@ -209,9 +212,12 @@ export default class AppleTag extends Tag { | |||||||||
| } | ||||||||||
|
|
||||||||||
| /** @inheritDoc */ | ||||||||||
| public get conductor(): string { return this.getFirstQuickTimeString(Mpeg4BoxType.COND); } | ||||||||||
| public get conductor(): string { | ||||||||||
| return this.getFirstQuickTimeString(Mpeg4BoxType.COND) // @TODO: Backwards compat for pre-6.0.2 releases | ||||||||||
| || this.getFirstQuickTimeString(Mpeg4BoxType.CON); | ||||||||||
|
Comment on lines
+216
to
+217
|
||||||||||
| 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 |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,8 +1,11 @@ | ||||||
| import {ByteVector, StringType} from "../byteVector"; | ||||||
|
|
||||||
| /** | ||||||
| * Provides references to different box types used by the library. This class is used to severely reduce the number | ||||||
| * of times these types are created in {@link AppleTag,} greatly improving the speed at which warm files are read. | ||||||
| * Provides references to different box types used by the library. This class is used to severely | ||||||
| * reduce the number of times these types are created in {@link AppleTag,} greatly improving the | ||||||
benrr101 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| * speed at which warm files are read. | ||||||
| * | ||||||
| * These box types were cross-referenced with FFMPEG source and Exiftool database. | ||||||
| */ | ||||||
| export default class Mpeg4BoxType { | ||||||
| /** QuickTime album artist box */ | ||||||
|
|
@@ -13,7 +16,15 @@ export default class Mpeg4BoxType { | |||||
| public static readonly ART = this.getType("©ART"); | ||||||
| /** QuickTime comment box */ | ||||||
| public static readonly CMT = this.getType("©cmt"); | ||||||
| /** QuickTime conductor box? @TODO: Verify this works should not be ©con */ | ||||||
| /** | ||||||
| * QuickTime conductor box. This is listed in the FFMPEG source and Exiftool. | ||||||
| */ | ||||||
| public static readonly CON = this.getType("©con"); | ||||||
| /** | ||||||
| * Conductor box from original .NET source. This is not listed anywhere in the Exiftool or | ||||||
| * FFMPEG docs. | ||||||
| * @TODO: Remove this when backwards compat time has ended. | ||||||
| */ | ||||||
| public static readonly COND = this.getType("cond"); | ||||||
| /** QuickTime cover art box */ | ||||||
| public static readonly COVR = this.getType("covr"); | ||||||
|
|
@@ -91,19 +102,30 @@ export default class Mpeg4BoxType { | |||||
| public static readonly STCO = this.getType("stco"); | ||||||
| /** ISO sample description box */ | ||||||
| public static readonly STSD = this.getType("stsd"); | ||||||
| /** Subtitle box? @TODO: There's no record of this one */ | ||||||
| /** | ||||||
| * QuickTime subtitle box. This is listed in the FFMPEG source and Exiftool. | ||||||
| */ | ||||||
| public static readonly ST3 = this.getType("©st3") | ||||||
|
||||||
| public static readonly ST3 = this.getType("©st3") | |
| public static readonly ST3 = this.getType("©st3"); |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -81,7 +81,17 @@ import {TagTypes} from "../../src/tag"; | |||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| @test | ||||||||||||||||||||||||||||||||||||||||||||||
| public subtitle() { | ||||||||||||||||||||||||||||||||||||||||||||||
| this.testQuickTimeString((t, v) => t.subtitle = v, (t) => t.subtitle, Mpeg4BoxType.SUBT); | ||||||||||||||||||||||||||||||||||||||||||||||
| this.testQuickTimeString((t, v) => t.subtitle = v, (t) => t.subtitle, Mpeg4BoxType.ST3); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| @test | ||||||||||||||||||||||||||||||||||||||||||||||
| public subtitle_existingSubt() { | ||||||||||||||||||||||||||||||||||||||||||||||
| // Arrange | ||||||||||||||||||||||||||||||||||||||||||||||
| const box1 = this.getQuickTimeBox(Mpeg4BoxType.SUBT, "foobarbaz"); | ||||||||||||||||||||||||||||||||||||||||||||||
| const testTag = this.getEmptyTag([box1.box]); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Act / Assert | ||||||||||||||||||||||||||||||||||||||||||||||
| assert.strictEqual(testTag.tag.subtitle, "foobarbaz"); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| @test | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
| @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 |
Copilot
AI
Apr 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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" | |
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
subtitlegetter checks the legacySubtbox first, but the setter now writes to©st3only. If a file already containsSubt, callingtag.subtitle = ...will leave the oldSubtbox in place and subsequent reads will still return the stale legacy value. Prefer reading©st3first (and/or remove the legacySubtboxes when writing) so the updated value is observed and the file doesn't accumulate conflicting boxes.