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:
parent
a855ea7885
commit
5bb22c17c8
1 changed files with 124 additions and 0 deletions
|
|
@ -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
|
||||
// ============================================================================
|
||||
|
|
|
|||
Loading…
Reference in a new issue