diff --git a/src/mpeg4/appleTag.ts b/src/mpeg4/appleTag.ts index bdca9a63..dc36cfca 100644 --- a/src/mpeg4/appleTag.ts +++ b/src/mpeg4/appleTag.ts @@ -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); + } /** @inheritDoc */ - public set conductor(v: string) { this.setQuickTimeString(Mpeg4BoxType.COND, v); } + public set conductor(v: string) { this.setQuickTimeString(Mpeg4BoxType.CON, v); } /** @inheritDoc */ public get copyright(): string { return this.getFirstQuickTimeString(Mpeg4BoxType.CPRT); } diff --git a/src/mpeg4/mpeg4BoxType.ts b/src/mpeg4/mpeg4BoxType.ts index 6855f42c..207d78d1 100644 --- a/src/mpeg4/mpeg4BoxType.ts +++ b/src/mpeg4/mpeg4BoxType.ts @@ -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 + * 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,7 +102,15 @@ 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") + /** + * Subtitle 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 SUBT = this.getType("Subt"); /** Alias text box? @TODO: There's no record of this one */ public static readonly TEXT = this.getType("text"); @@ -99,11 +118,14 @@ export default class Mpeg4BoxType { public static readonly TMPO = this.getType("tmpo"); /** ISO track container box */ public static readonly TRAK = this.getType("trak"); - /** QuickTime track number box */ + /** QuickTime track number box @TODO: What about ©TRK as per FFMPEG source? */ public static readonly TRKN = this.getType("trkn"); /** ISO User data box */ public static readonly UDTA = this.getType("udta"); - /** Alias URL box? @TODO: There's no record of this one */ + /** + * Alias URL box? + * @remarks Specified in FFMPEG source but not in Exiftool. + */ public static readonly URL = this.getType("©url"); /** ISO user extension box */ public static readonly UUID = this.getType("uuid"); diff --git a/test-unit/mpeg4/appleTagTests.ts b/test-unit/mpeg4/appleTagTests.ts index 16699f61..11b35ceb 100644 --- a/test-unit/mpeg4/appleTagTests.ts +++ b/test-unit/mpeg4/appleTagTests.ts @@ -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 @@ -169,7 +179,7 @@ import {TagTypes} from "../../src/tag"; ByteVector.fromShort(0), AppleDataBoxFlagType.ContainsData ); - const box4 = this.getQuickTimeBox(Mpeg4BoxType.GNRE, ByteVector.fromString("foo", StringType.UTF8)); + const box4 = this.getQuickTimeBox(Mpeg4BoxType.GNRE, "foo"); const tag = this.getEmptyTag([box3.box, box4.box, box1.box, box2.box]); // Act @@ -210,7 +220,7 @@ import {TagTypes} from "../../src/tag"; // Arrange const box1 = this.getQuickTimeBox(Mpeg4BoxType.GNRE, ByteVector.fromShort(1)); const value2 = "foo; bar; baz"; - const box2 = this.getQuickTimeBox(Mpeg4BoxType.GEN, ByteVector.fromString(value2, StringType.UTF8)); + const box2 = this.getQuickTimeBox(Mpeg4BoxType.GEN, value2); const tag = this.getEmptyTag([box1.box, box2.box]); // Act @@ -265,9 +275,9 @@ import {TagTypes} from "../../src/tag"; @test public year_multipleBoxes() { // Arrange - const box1 = this.getQuickTimeBox(Mpeg4BoxType.DAY, ByteVector.fromString("asdf", StringType.UTF8)); - const box2 = this.getQuickTimeBox(Mpeg4BoxType.DAY, ByteVector.fromString("123456", StringType.UTF8)); - const box3 = this.getQuickTimeBox(Mpeg4BoxType.DAY, ByteVector.fromString("234", StringType.UTF8)); + const box1 = this.getQuickTimeBox(Mpeg4BoxType.DAY, "asdf"); + const box2 = this.getQuickTimeBox(Mpeg4BoxType.DAY, "123456"); + const box3 = this.getQuickTimeBox(Mpeg4BoxType.DAY, "234"); const testTag = this.getEmptyTag([box1.box, box2.box, box3.box]); // Act / Assert @@ -277,7 +287,7 @@ import {TagTypes} from "../../src/tag"; @test public year_setMultipleBoxes() { // Arrange - const box1 = this.getQuickTimeBox(Mpeg4BoxType.DAY, ByteVector.fromString("1234", StringType.UTF8)); + const box1 = this.getQuickTimeBox(Mpeg4BoxType.DAY, "1234"); const testTag = this.getEmptyTag([box1.box, box1.box, box1.box]); // Act / Assert @@ -289,7 +299,7 @@ import {TagTypes} from "../../src/tag"; @test public year_setZero_clearsMultiple() { // Arrange - const box1 = this.getQuickTimeBox(Mpeg4BoxType.DAY, ByteVector.fromString("1234", StringType.UTF8)); + const box1 = this.getQuickTimeBox(Mpeg4BoxType.DAY, "1234"); const testTag = this.getEmptyTag([box1.box, box1.box, box1.box]); // Act / Assert @@ -429,7 +439,17 @@ import {TagTypes} from "../../src/tag"; @test public conductor() { - this.testQuickTimeString((t, v) => t.conductor = v, (t) => t.conductor, Mpeg4BoxType.COND); + this.testQuickTimeString((t, v) => t.conductor = v, (t) => t.conductor, Mpeg4BoxType.CON); + } + + @test + public conductor_existingCondBox() { + // Arrange + const box1 = this.getQuickTimeBox(Mpeg4BoxType.COND, "foobarbaz"); + const testTag = this.getEmptyTag([box1.box]); + + // Act / Assert + assert.strictEqual(testTag.tag.conductor, "foobarbaz"); } @test @@ -464,18 +484,9 @@ import {TagTypes} from "../../src/tag"; @test public dateTagged_multipleBoxes() { // Arrange - const box1 = this.getQuickTimeBox( - Mpeg4BoxType.DTAG, - ByteVector.fromString("asdf", StringType.UTF8) - ); - const box2 = this.getQuickTimeBox( - Mpeg4BoxType.DTAG, - ByteVector.fromString("2023-10-21T10:46:00", StringType.UTF8) - ); - const box3 = this.getQuickTimeBox( - Mpeg4BoxType.DTAG, - ByteVector.fromString("2023-10-21", StringType.UTF8) - ); + const box1 = this.getQuickTimeBox(Mpeg4BoxType.DTAG, "asdf"); + const box2 = this.getQuickTimeBox(Mpeg4BoxType.DTAG, "2023-10-21T10:46:00"); + const box3 = this.getQuickTimeBox(Mpeg4BoxType.DTAG, "2023-10-21"); const testTag = this.getEmptyTag([box1.box, box2.box, box3.box]); // Act / Assert @@ -485,10 +496,7 @@ import {TagTypes} from "../../src/tag"; @test public dateTagged_setMultipleBoxes() { // Arrange - const box1 = this.getQuickTimeBox( - Mpeg4BoxType.DTAG, - ByteVector.fromString("1900-10-21T10:56:00", StringType.UTF8) - ); + const box1 = this.getQuickTimeBox(Mpeg4BoxType.DTAG, "1900-10-21T10:56:00"); const testTag = this.getEmptyTag([box1.box, box1.box, box1.box]); const testValue = new Date("2023-10-21 10:59:00"); @@ -504,10 +512,7 @@ import {TagTypes} from "../../src/tag"; @test public dateTagged_setZero_clearsMultiple() { // Arrange - const box1 = this.getQuickTimeBox( - Mpeg4BoxType.DTAG, - ByteVector.fromString("2021-10-21T11:04:00", StringType.UTF8) - ); + const box1 = this.getQuickTimeBox(Mpeg4BoxType.DTAG, "2021-10-21T11:04:00"); const testTag = this.getEmptyTag([box1.box, box1.box, box1.box]); // Act / Assert @@ -1187,9 +1192,9 @@ import {TagTypes} from "../../src/tag"; // TEST CASE 3: Multiple boxes return all valid instances ---------- // Valid box type and flags - const box1 = this.getQuickTimeBox(boxType, ByteVector.fromString("foo", StringType.UTF8)); + const box1 = this.getQuickTimeBox(boxType, "foo"); // Valid box type and flags - const box2 = this.getQuickTimeBox(boxType, ByteVector.fromString("bar", StringType.UTF8)); + const box2 = this.getQuickTimeBox(boxType, "bar"); // Valid box type, invalid flags const box3 = this.getQuickTimeBox( boxType, @@ -1202,7 +1207,7 @@ import {TagTypes} from "../../src/tag"; box4.addChild(box2.dataBox); box4.addChild(box3.dataBox); // Multiple values in single data box - const box5 = this.getQuickTimeBox(boxType, ByteVector.fromString("fux; bux; quxx", StringType.UTF8)); + const box5 = this.getQuickTimeBox(boxType, "fux; bux; quxx"); const testTag2 = this.getEmptyTag([box1.box, box2.box, box3.box, box4, box5.box]); @@ -1454,10 +1459,14 @@ import {TagTypes} from "../../src/tag"; private getQuickTimeBox( boxType: ByteVector, - value: ByteVector, + value: ByteVector|string, flags: AppleDataBoxFlagType = AppleDataBoxFlagType.ContainsText ): {box: AppleAnnotationBox, dataBox: AppleDataBox} { + value = typeof(value) === "string" + ? ByteVector.fromString(value, StringType.UTF8) + : value; const dataBox = AppleDataBox.fromDataAndFlags(value, flags); + const box = AppleAnnotationBox.fromType(boxType); box.addChild(dataBox);