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.
|
// premierepro API: docs say sync, runtime returns Promises. Await everything.
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
|
|
@ -11,23 +11,31 @@
|
||||||
return Timeline._ppro;
|
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 ─────────────────────────────────────────
|
// ── Read active sequence ─────────────────────────────────────────
|
||||||
Timeline.readActiveSequence = async function () {
|
Timeline.readActiveSequence = async function () {
|
||||||
const P = ppro();
|
const P = ppro();
|
||||||
const project = await P.Project.getActiveProject();
|
const project = await P.Project.getActiveProject();
|
||||||
if (!project) throw new Error('No active Premiere project');
|
if (!project) throw new Error('No active Premiere project');
|
||||||
const seq = await project.getActiveSequence();
|
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();
|
const settings = await seq.getSettings();
|
||||||
let frameRate = 29.97;
|
let frameRate = 29.97;
|
||||||
try {
|
try {
|
||||||
const fr = settings.videoFrameRate;
|
const fr = settings && settings.videoFrameRate;
|
||||||
if (fr && typeof fr.seconds === 'number' && fr.seconds > 0) frameRate = 1 / fr.seconds;
|
if (fr && typeof fr.seconds === 'number' && fr.seconds > 0) frameRate = 1 / fr.seconds;
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
const width = settings.videoFrameWidth || 1920;
|
const width = (settings && settings.videoFrameWidth) || 1920;
|
||||||
const height = settings.videoFrameHeight || 1080;
|
const height = (settings && settings.videoFrameHeight) || 1080;
|
||||||
|
|
||||||
const clips = [];
|
const clips = [];
|
||||||
const trackCount = await seq.getVideoTrackCount();
|
const trackCount = await seq.getVideoTrackCount();
|
||||||
|
|
@ -43,21 +51,30 @@
|
||||||
try {
|
try {
|
||||||
const projItem = await clip.getProjectItem();
|
const projItem = await clip.getProjectItem();
|
||||||
if (!projItem) continue;
|
if (!projItem) continue;
|
||||||
|
|
||||||
let filePath = '';
|
let filePath = '';
|
||||||
try {
|
try {
|
||||||
const clipItem = await P.ClipProjectItem.cast(projItem);
|
const clipItem = await P.ClipProjectItem.cast(projItem);
|
||||||
filePath = await clipItem.getMediaFilePath() || '';
|
filePath = await clipItem.getMediaFilePath() || '';
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
const clipName = await clip.getName().catch(() => '');
|
const clipName = await clip.getName().catch(() => '');
|
||||||
const fileName = clipName || (filePath ? path.basename(filePath) : 'clip');
|
const fileName = clipName || (filePath ? path.basename(filePath) : 'clip');
|
||||||
const startT = await clip.getStartTime();
|
|
||||||
const endT = await clip.getEndTime();
|
// Null-safe time access — non-clip items can return null Time objects
|
||||||
const inT = await clip.getInPoint();
|
const startT = await clip.getStartTime().catch(() => null);
|
||||||
const outT = await clip.getOutPoint();
|
const endT = await clip.getEndTime().catch(() => null);
|
||||||
const tlIn = startT.seconds;
|
const inT = await clip.getInPoint().catch(() => null);
|
||||||
const tlOut = endT.seconds;
|
const outT = await clip.getOutPoint().catch(() => null);
|
||||||
const srcIn = inT.seconds;
|
|
||||||
const srcOut = outT.seconds;
|
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({
|
clips.push({
|
||||||
fileName, filePath, trackIndex: ti,
|
fileName, filePath, trackIndex: ti,
|
||||||
timelineInSec: tlIn, timelineOutSec: tlOut,
|
timelineInSec: tlIn, timelineOutSec: tlOut,
|
||||||
|
|
@ -67,7 +84,9 @@
|
||||||
sourceInFrames: Math.round(srcIn * frameRate),
|
sourceInFrames: Math.round(srcIn * frameRate),
|
||||||
sourceOutFrames: Math.round(srcOut * 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 };
|
return { sequenceName: name, frameRate, width, height, clips };
|
||||||
|
|
@ -131,7 +150,7 @@
|
||||||
Timeline.pushToMAM = async function (seqName, projectId, td) {
|
Timeline.pushToMAM = async function (seqName, projectId, td) {
|
||||||
const resolved = Library.resolveClipsToAssets(td.clips || []);
|
const resolved = Library.resolveClipsToAssets(td.clips || []);
|
||||||
const matched = resolved.filter(c => c.asset_id);
|
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);
|
const seqs = await API.listSequences(projectId);
|
||||||
let seqId;
|
let seqId;
|
||||||
const existing = seqs.find(s => s.name === seqName);
|
const existing = seqs.find(s => s.name === seqName);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue