fix(uxp): null-safe Time object access in readActiveSequence
getStartTime/getEndTime/getInPoint/getOutPoint can return null for non-clip track items (gaps, transitions) that slip past the getProjectItem check. Accessing .seconds on null threw a TypeError that the outer catch swallowed — silently dropping every clip and leaving clips[] empty, so the export panel never opened. Also skip clips where all four time values resolve to 0 (filler items).
This commit is contained in:
parent
382f432693
commit
60e5093c6b
1 changed files with 35 additions and 16 deletions
|
|
@ -1,4 +1,4 @@
|
|||
// timeline.js — v2.1.5
|
||||
// timeline.js — v2.1.6
|
||||
// premierepro API: docs say sync, runtime returns Promises. Await everything.
|
||||
|
||||
(function () {
|
||||
|
|
@ -11,23 +11,31 @@
|
|||
return Timeline._ppro;
|
||||
}
|
||||
|
||||
// Safe helper — returns the numeric seconds value from a Time object,
|
||||
// or 0 if the object is null/undefined/malformed.
|
||||
function _secs(t) {
|
||||
if (!t) return 0;
|
||||
if (typeof t.seconds === 'number') return t.seconds;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ── Read active sequence ─────────────────────────────────────────
|
||||
Timeline.readActiveSequence = async function () {
|
||||
const P = ppro();
|
||||
const project = await P.Project.getActiveProject();
|
||||
if (!project) throw new Error('No active Premiere project');
|
||||
const seq = await project.getActiveSequence();
|
||||
if (!seq) throw new Error('No active sequence');
|
||||
if (!seq) throw new Error('No active sequence — open a sequence in the timeline first');
|
||||
|
||||
const name = seq.name || (await seq.getName && await seq.getName()) || 'Sequence 1';
|
||||
const name = seq.name || (seq.getName ? await seq.getName() : '') || 'Sequence 1';
|
||||
const settings = await seq.getSettings();
|
||||
let frameRate = 29.97;
|
||||
try {
|
||||
const fr = settings.videoFrameRate;
|
||||
const fr = settings && settings.videoFrameRate;
|
||||
if (fr && typeof fr.seconds === 'number' && fr.seconds > 0) frameRate = 1 / fr.seconds;
|
||||
} catch (_) {}
|
||||
const width = settings.videoFrameWidth || 1920;
|
||||
const height = settings.videoFrameHeight || 1080;
|
||||
const width = (settings && settings.videoFrameWidth) || 1920;
|
||||
const height = (settings && settings.videoFrameHeight) || 1080;
|
||||
|
||||
const clips = [];
|
||||
const trackCount = await seq.getVideoTrackCount();
|
||||
|
|
@ -43,21 +51,30 @@
|
|||
try {
|
||||
const projItem = await clip.getProjectItem();
|
||||
if (!projItem) continue;
|
||||
|
||||
let filePath = '';
|
||||
try {
|
||||
const clipItem = await P.ClipProjectItem.cast(projItem);
|
||||
filePath = await clipItem.getMediaFilePath() || '';
|
||||
} catch (_) {}
|
||||
|
||||
const clipName = await clip.getName().catch(() => '');
|
||||
const fileName = clipName || (filePath ? path.basename(filePath) : 'clip');
|
||||
const startT = await clip.getStartTime();
|
||||
const endT = await clip.getEndTime();
|
||||
const inT = await clip.getInPoint();
|
||||
const outT = await clip.getOutPoint();
|
||||
const tlIn = startT.seconds;
|
||||
const tlOut = endT.seconds;
|
||||
const srcIn = inT.seconds;
|
||||
const srcOut = outT.seconds;
|
||||
|
||||
// Null-safe time access — non-clip items can return null Time objects
|
||||
const startT = await clip.getStartTime().catch(() => null);
|
||||
const endT = await clip.getEndTime().catch(() => null);
|
||||
const inT = await clip.getInPoint().catch(() => null);
|
||||
const outT = await clip.getOutPoint().catch(() => null);
|
||||
|
||||
const tlIn = _secs(startT);
|
||||
const tlOut = _secs(endT);
|
||||
const srcIn = _secs(inT);
|
||||
const srcOut = _secs(outT);
|
||||
|
||||
// Skip filler/gap items where all times are zero
|
||||
if (tlIn === 0 && tlOut === 0 && srcIn === 0 && srcOut === 0 && !filePath) continue;
|
||||
|
||||
clips.push({
|
||||
fileName, filePath, trackIndex: ti,
|
||||
timelineInSec: tlIn, timelineOutSec: tlOut,
|
||||
|
|
@ -67,7 +84,9 @@
|
|||
sourceInFrames: Math.round(srcIn * frameRate),
|
||||
sourceOutFrames: Math.round(srcOut * frameRate),
|
||||
});
|
||||
} catch (_) {}
|
||||
} catch (clipErr) {
|
||||
console.warn('[df] readActiveSequence: skipped clip on track', ti, '—', clipErr && clipErr.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
return { sequenceName: name, frameRate, width, height, clips };
|
||||
|
|
@ -131,7 +150,7 @@
|
|||
Timeline.pushToMAM = async function (seqName, projectId, td) {
|
||||
const resolved = Library.resolveClipsToAssets(td.clips || []);
|
||||
const matched = resolved.filter(c => c.asset_id);
|
||||
if (!matched.length) throw new Error('No clips matched MAM assets — import proxies first');
|
||||
if (!matched.length) throw new Error('No clips matched MAM assets — import proxies first so the plugin can map file paths to asset IDs');
|
||||
const seqs = await API.listSequences(projectId);
|
||||
let seqId;
|
||||
const existing = seqs.find(s => s.name === seqName);
|
||||
|
|
|
|||
Loading…
Reference in a new issue