feat(plugin): add exportTimelineData() and getProjectItems() to ExtendScript

exportTimelineData() walks all video tracks in the active sequence and
returns clip source/timeline frame positions + file paths so the panel JS
can map them back to MAM asset IDs for timeline export.

getProjectItems() enumerates all ProjectItems with paths — useful for
rebuilding the import mapping after a Premiere restart.
This commit is contained in:
Zac Gaetano 2026-05-20 00:35:18 -04:00
parent a855ea7885
commit 5bb22c17c8

View file

@ -309,6 +309,130 @@ function exportSequence(outputPath, presetPath) {
}
}
// ============================================================================
// Timeline Export read sequence clips for MAM round-trip
// ============================================================================
/**
* Reads all clips from all video tracks in the active sequence.
* Returns JSON with sequence settings and a flat clip array.
*
* Each clip entry contains:
* filePath absolute OS path to source media (for MAM asset ID lookup)
* fileName project panel display name
* trackIndex 0-based video track index (0 = V1, 1 = V2, )
* sourceInFrames in-point within source media, in frames
* sourceOutFrames out-point within source media, in frames
* timelineInFrames clip start position on timeline, in frames
* timelineOutFrames clip end position on timeline, in frames
*
* sequence.timebase is ticks-per-frame. Premiere uses 254,016,000,000
* ticks/second, so fps = 254016000000 / timebase.
*/
function exportTimelineData() {
var result = {
success: false,
message: "",
sequenceName: "",
frameRate: 59.94,
width: 1920,
height: 1080,
clips: []
};
try {
if (!app.project) {
result.message = "No active project";
return JSON.stringify(result);
}
var sequence = app.project.activeSequence;
if (!sequence) {
result.message = "No active sequence";
return JSON.stringify(result);
}
result.sequenceName = sequence.name;
var TICKS_PER_SECOND = 254016000000;
var timebaseTicks = parseFloat(sequence.timebase) || 4240384;
result.frameRate = parseFloat((TICKS_PER_SECOND / timebaseTicks).toFixed(4));
try { result.width = sequence.frameSizeHorizontal || 1920; } catch (e) {}
try { result.height = sequence.frameSizeVertical || 1080; } catch (e) {}
var videoTracks = sequence.videoTracks;
for (var t = 0; t < videoTracks.numTracks; t++) {
var track = videoTracks[t];
if (!track || !track.clips) continue;
var numClips = track.clips.numItems;
for (var c = 0; c < numClips; c++) {
try {
var clip = track.clips[c];
if (!clip || !clip.projectItem) continue;
var filePath = "";
try { filePath = clip.projectItem.getMediaPath(); } catch (e) {}
var srcIn = Math.round(parseFloat(clip.inPoint.ticks) / timebaseTicks);
var srcOut = Math.round(parseFloat(clip.outPoint.ticks) / timebaseTicks);
var recIn = Math.round(parseFloat(clip.start.ticks) / timebaseTicks);
var recOut = Math.round(parseFloat(clip.end.ticks) / timebaseTicks);
// Skip degenerate clips (zero or negative duration)
if (srcOut <= srcIn || recOut <= recIn) continue;
result.clips.push({
trackIndex: t,
filePath: filePath,
fileName: clip.projectItem.name || "",
sourceInFrames: srcIn,
sourceOutFrames: srcOut,
timelineInFrames: recIn,
timelineOutFrames: recOut
});
} catch (e) { /* skip malformed clip */ }
}
}
result.success = true;
result.message = result.clips.length + " clip(s) across " + videoTracks.numTracks + " track(s)";
return JSON.stringify(result);
} catch (error) {
result.message = "Error reading timeline: " + error.message;
return JSON.stringify(result);
}
}
/**
* Returns all media ProjectItems in the current project with name + file path.
* Useful for rebuilding the asset-path lookup map after a Premiere restart
* when temp-file paths may have changed.
*/
function getProjectItems() {
var result = { items: [] };
try {
if (!app.project) return JSON.stringify(result);
_collectProjectItems(app.project.rootItem, result.items);
} catch (e) {}
return JSON.stringify(result);
}
function _collectProjectItems(bin, out) {
if (!bin || !bin.children) return;
for (var i = 0; i < bin.children.numItems; i++) {
var item = bin.children[i];
if (!item) continue;
if (item.type === ProjectItemType.BIN) {
_collectProjectItems(item, out);
} else {
var path = "";
try { path = item.getMediaPath(); } catch (e) {}
out.push({ name: item.name, path: path });
}
}
}
// ============================================================================
// Helper Functions
// ============================================================================