From 19f20da8cc891fb8cf827808a718a500d38e89d7 Mon Sep 17 00:00:00 2001 From: dk949 <56653556+dk949@users.noreply.github.com> Date: Thu, 28 Oct 2021 00:44:50 +0100 Subject: [PATCH 1/6] fix(base): added resolution to base generator --- src/baseGenerator.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/baseGenerator.ts b/src/baseGenerator.ts index e9bcc22..bcab3bf 100644 --- a/src/baseGenerator.ts +++ b/src/baseGenerator.ts @@ -7,6 +7,7 @@ export abstract class Generator { protected frameRate: number; protected clipName: string; protected options: Options; + protected resolution: string | null; constructor(project: Project, options: Options) { @@ -20,6 +21,7 @@ export abstract class Generator { } this.frameRate = this.project.frameRate; this.clipName = this.layer.sourceFile; + this.resolution = this.project.resolution; } From 78e5d5aeaee8ab0bcfc26ad95cae5325fb34fc17 Mon Sep 17 00:00:00 2001 From: dk949 <56653556+dk949@users.noreply.github.com> Date: Thu, 28 Oct 2021 00:45:28 +0100 Subject: [PATCH 2/6] feat(xml): added indents to xml --- src/XML/common.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/XML/common.ts b/src/XML/common.ts index b7e6079..c7d262e 100644 --- a/src/XML/common.ts +++ b/src/XML/common.ts @@ -1,25 +1,30 @@ export class baseXMLBUilder { protected _data: string[] = []; + private tab: string = " "; + private indent: number = 0; protected putTag(name: string, attr: {[key: string]: string}, contents?: (() => void) | string) { - const attrStr = - Object.entries(attr) + const attrStr = (() => { + const a = Object.entries(attr) .map(([key, value]) => {return `${key}="${value}"`;}) .join(' '); + return a ? " " + a : ""; + })(); if (!contents) { - this._data.push(`<${name} ${attrStr}/>`); + this._data.push(`${this.tab.repeat(this.indent)}<${name}${attrStr} />`); return; } - this._data.push(`<${name} ${attrStr}>`); + this._data.push(`${this.tab.repeat(this.indent)}<${name}${attrStr}>`); if (typeof contents === "function") { + this.indent++; contents(); - this._data.push(``); + this.indent-- + this._data.push(`${this.tab.repeat(this.indent)}`); } else { - const tmp: string[] = [contents, ``]; - this._data[this._data.length - 1] += tmp.join(""); + this._data[this._data.length - 1] += `${contents}`; } } From 551f90f80e80acb0507e78e93d6b838b5f7de12f Mon Sep 17 00:00:00 2001 From: dk949 <56653556+dk949@users.noreply.github.com> Date: Thu, 28 Oct 2021 00:46:33 +0100 Subject: [PATCH 3/6] fix(xml): added resolution to xml generator --- src/XML/genXML.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/XML/genXML.ts b/src/XML/genXML.ts index 7aa135c..a642ad7 100644 --- a/src/XML/genXML.ts +++ b/src/XML/genXML.ts @@ -2,12 +2,17 @@ import path from "path"; import {Generator} from "../baseGenerator"; import {xmlBuilder} from "./helpersXML"; +// https://fcp.co/final-cut-pro/tutorials/1912-demystifying-final-cut-pro-xmls-by-philip-hodgetts-and-gregory-clarke export class XML extends Generator { generate(): string { const builder = new xmlBuilder(); - builder.buildContext(path.parse(this.clipName).name, this.frameRate, this.cuts[this.cuts.length - 1].end, () => { + const [width, height] = (() => { + const split = this.resolution?.split('x'); + return split ? split.map(parseInt) : [null, null]; + })(); + builder.buildContext(path.parse(this.clipName).name, width, height, this.frameRate, this.cuts[this.cuts.length - 1].end, () => { this.cuts.forEach((cut) => { - builder.putClipitem(this.clipName, cut.start, cut.end, 720, 1270); // FIXME: needs resolution!!!!!!! + builder.putClipitem(this.clipName, cut.start, cut.end); }); }); return builder.data; From 48d7dd38979eb11137036ac5da11e0cecf1c610d Mon Sep 17 00:00:00 2001 From: dk949 <56653556+dk949@users.noreply.github.com> Date: Thu, 28 Oct 2021 00:47:14 +0100 Subject: [PATCH 4/6] fix(xml): attempting to fix xml issues getting final xml to look more like original one produced by renderV1 limitations: * in/out tags are missing (I don't know what they do) * resolution is still missing if it is passed in as null from electron --- src/XML/helpersXML.ts | 93 ++++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 31 deletions(-) diff --git a/src/XML/helpersXML.ts b/src/XML/helpersXML.ts index 286b883..c5f3508 100644 --- a/src/XML/helpersXML.ts +++ b/src/XML/helpersXML.ts @@ -5,24 +5,35 @@ export class xmlBuilder extends baseXMLBUilder { private clipitemIDs: string[] = []; private timeBase: number = -1; private duration: string = ""; - public buildContext(name: string, frameRate: number, duration: number, l: () => void) { + private registeredFile: boolean = false; + private width: number | null = -1; + private height: number | null = -1; + public buildContext(name: string, width: number | null, height: number | null, frameRate: number, duration: number, l: () => void) { + this.width = width; + this.height = height; this.timeBase = frameRate; this.duration = (frameRate * duration).toFixed(0); - this._data.push(""); this.putTag("xmeml", {version: this.conf.version}, () => { this.putTag("project", {}, () => { this.putTag("name", {}, name); this.putTag("children", {}, () => { this.putTag("sequence", {id: this.conf.sequenceID}, () => { this.putTag("name", {}, name); - this.putTag("duration", {}, this.duration); + this.putTag("duration", {}, this.duration); // FIXME: Duration is only up to the end of the last cut this.putTag("rate", {}, () => { this.putTag("timebase", {}, this.timeBase.toString()); - this.putTag("ntsc", {}, this.putBool(this.conf.ntsc)); + this.putTag("ntsc", {}, this.putBool(this.conf.ntsc)); // FIXME: ??? }); this.putTag("media", {}, () => { this.putTag("video", {}, () => { this.putTag("track", {}, l); + this.putTag("format", {}, () => { + this.putTag("samplecharacteristics", {}, () => { + this.height && this.putTag("height", {}, this.height.toString()); + this.width && this.putTag("width", {}, this.width.toString()) + this.putTag("pixelaspectratio", {}, this.conf.pixelAspectRatio); + }) + }); }); this.putTag("audio", {}, () => { this.putTag("track", {}, () => { @@ -33,51 +44,44 @@ export class xmlBuilder extends baseXMLBUilder { }); }); + this.putTag("timecode", {}, () => { + this.putTag("rate", {}, () => { + this.putTag("timebase", {}, this.timeBase.toString()); + this.putTag("ntsc", {}, this.putBool(this.conf.ntsc)); + }); + this.putTag("string", {}, this.conf.zeroTimecode); // why??? + this.putTag("frame", {}, this.conf.zeroFrame); + this.putTag("displayformat", {}, this.conf.displayFormat); + }); }); }); }); }); } - public putClipitem(filePath: string, start: number, end: number, height: number, width: number) { + public putClipitem(filePath: string, start: number, end: number) { const rate = () => { this.putTag("timebase", {}, this.timeBase.toString()); this.putTag("ntsc", {}, this.putBool(this.conf.ntsc)); }; - - - this.putTag("clipitem", { - frameBlend: this.putBool(this.conf.frameBlend), - id: this.genID(), - }, () => { + this.putTag("clipitem", {frameBlend: this.putBool(this.conf.frameBlend), id: this.genID(), }, () => { this.putTag("media", {}, () => { this.putTag("video", {}, () => { this.putTag("samplecharacteristics", {}, () => { - this.putTag("height", {}, height.toString()); - this.putTag("width", {}, width.toString()); - }); - }); - }); - this.putTag("file", {id: this.conf.fileID}, () => { - this.putTag("pathurl", {}, filePath); - this.putTag("name", {}, path.basename(filePath)); - this.putTag("rate", {}, rate); - this.putTag("duration", {}, this.duration); - this.putTag("timecode", {}, () => { - this.putTag("rate", {}, rate); - this.putTag("string", {}, this.conf.zeroTimecode); // why??? - this.putTag("frame", {}, this.conf.zeroFrame); - this.putTag("displayformat", {}, this.conf.displayFormat); - this.putTag("media", {}, () => { - this.putTag("video", {}); - this.putTag("audio", {}); + this.height && this.putTag("height", {}, this.height.toString()); + this.width && this.putTag("width", {}, this.width.toString()) }); }); }); + this.putTag("file", {id: this.conf.fileID}, this.genFile(filePath, rate)); this.putTag("name", {}, filePath); this.putTag("rate", {}, rate); - this.putTag("start", {}, (start * this.timeBase).toFixed()); - this.putTag("end", {}, (end * this.timeBase).toFixed()); + this.putTag("rate", {}, rate); // XXX: no idea. Original did this and original works so ¯\_(ツ)_/¯ + const startF = start * this.timeBase; + const endF = end * this.timeBase; + this.putTag("duration", {}, (endF - startF).toFixed()); + this.putTag("start", {}, startF.toFixed()); + this.putTag("end", {}, endF.toFixed()); }); } @@ -91,6 +95,32 @@ export class xmlBuilder extends baseXMLBUilder { return id; } + private genFile(filePath: string, rate: () => void): (() => void) | undefined { + /* + XXX: this looks weird, but it is being passed to a function with an optional parameter, + so it's expecting another function or undefined. + */ + if (!this.registeredFile) { + this.registeredFile = true; + return () => { + this.putTag("pathurl", {}, filePath); + this.putTag("name", {}, path.basename(filePath)); + this.putTag("rate", {}, rate); + this.putTag("duration", {}, this.duration); + this.putTag("timecode", {}, () => { + this.putTag("rate", {}, rate); + this.putTag("string", {}, this.conf.zeroTimecode); // why??? + this.putTag("frame", {}, this.conf.zeroFrame); + this.putTag("displayformat", {}, this.conf.displayFormat); + }); + this.putTag("media", {}, () => { + this.putTag("video", {}); + this.putTag("audio", {}); + }); + }; + } + } + private conf = { version: "4", @@ -101,6 +131,7 @@ export class xmlBuilder extends baseXMLBUilder { zeroTimecode: "00:00:00:00", zeroFrame: "0", displayFormat: "NDF", + pixelAspectRatio: 1.0.toFixed(1) } as const; } From e464bbdc4e02ee27c39250d6171e0fef68b93b44 Mon Sep 17 00:00:00 2001 From: dk949 <56653556+dk949@users.noreply.github.com> Date: Thu, 28 Oct 2021 01:03:05 +0100 Subject: [PATCH 5/6] chore(changelog): updated to v0.0.5 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91a0515..f0f3090 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## [0.0.5](https://github.com/JumpCutter/JC-ProjectExportAPI/compare/v0.0.4...v0.0.5) (2021-10-28) +### Changes +- __fixed__: Could not import into Premiere Pro. Needs more testing but is now more likely to work. + ## [0.0.4](https://github.com/JumpCutter/JC-ProjectExportAPI/compare/v0.0.3...v0.0.4) (2021-10-18) ### Changes - __BREAKING__: Constructor of Generator now takes second parameter - an `Options` object From 7f6bbe7d2f5d3f7bf76a2c4fb1f857cd7b356987 Mon Sep 17 00:00:00 2001 From: dk949 <56653556+dk949@users.noreply.github.com> Date: Thu, 28 Oct 2021 01:06:33 +0100 Subject: [PATCH 6/6] chore(changelog): updated changelg... again --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0f3090..8115fc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## [0.0.6](https://github.com/JumpCutter/JC-ProjectExportAPI/compare/v0.0.5...v0.0.6) (2021-10-28) +### Changes +- __fixed__: messed up tags on last release... +- __fixed__: Could not import into Premiere Pro. Needs more testing but is now more likely to work. + ## [0.0.5](https://github.com/JumpCutter/JC-ProjectExportAPI/compare/v0.0.4...v0.0.5) (2021-10-28) ### Changes - __fixed__: Could not import into Premiere Pro. Needs more testing but is now more likely to work.