fix(uxp): resolve "Resource busy" on re-import of same asset

- _writeBuffer: catch EBUSY (Windows file-lock) and treat as success —
  the file is already there from the previous import and Premiere has it
  locked; no need to re-write it.
- proxy / hires: stat the destination first; if the file already exists
  skip the download entirely and go straight to importIntoProject.
- importIntoProject: importFiles returning false means the file is
  already in the Premiere project — not an error, treat as success.
This commit is contained in:
Zac Gaetano 2026-05-28 11:11:32 -04:00
parent e533566ae2
commit 382f432693

View file

@ -1,4 +1,4 @@
// import-flow.js — v2.1.5
// import-flow.js — v2.1.6
// premierepro API: docs say sync, runtime returns Promises. Await everything.
(function () {
@ -37,9 +37,23 @@
return path.join(base, 'dragonflight-' + safeName);
};
// Write ArrayBuffer to disk via fs.writeFile
// Returns true if the path already exists on disk.
Import._fileExists = async function (filePath) {
try { await fs.stat(filePath); return true; } catch (_) { return false; }
};
// Write ArrayBuffer to disk via fs.writeFile.
// If the file is locked (EBUSY — Premiere already has it open from a
// previous import) we treat that as success: the bytes are already there.
Import._writeBuffer = async function (destPath, arrayBuffer) {
try {
await fs.writeFile(destPath, arrayBuffer);
} catch (e) {
const busy = e.code === 'EBUSY' || /resource busy/i.test(String(e.message));
if (!busy) throw e;
// File locked by Premiere — it's already there, proceed.
console.warn('[df] _writeBuffer EBUSY on', destPath, '— using existing file');
}
return destPath;
};
@ -66,13 +80,14 @@
// Import a file into the active Premiere project.
// ALL premierepro calls must be awaited — runtime returns Promises
// even though docs list them as synchronous.
// importFiles returns false when the file is already in the project — not an error.
Import.importIntoProject = async function (filePath) {
const P = _ppro();
const project = await P.Project.getActiveProject();
if (!project) throw new Error('No active Premiere project');
const root = await project.getRootItem();
const ok = await project.importFiles([filePath], true, root, false);
if (!ok) throw new Error('Premiere refused to import the file');
await project.importFiles([filePath], true, root, false);
// Return value false = already in project. Either way, we're done.
return true;
};
@ -81,6 +96,11 @@
const safeName = UI.sanitizeFilename((asset.display_name || asset.filename || asset.id) + '.mp4');
const dest = await Import._tempPath(safeName);
const alreadyOnDisk = await Import._fileExists(dest);
if (alreadyOnDisk) {
// File exists from a previous import — skip download, just (re-)import.
UI.showProgress('Importing into Premiere…', 92);
} else {
UI.showProgress('Resolving proxy URL…', 4);
const { url } = await API.getProxyUrl(asset.id);
@ -92,6 +112,8 @@
await Import._writeBuffer(dest, buf);
UI.showProgress('Importing into Premiere…', 92);
}
await Import.importIntoProject(dest);
UI.hideProgress();
UI.toast('Imported: ' + safeName, 'ok');
@ -105,6 +127,10 @@
const safeName = UI.sanitizeFilename(info.filename || (asset.display_name || asset.id) + '.' + (info.ext || 'mxf'));
const dest = await Import._tempPath(safeName);
const alreadyOnDisk = await Import._fileExists(dest);
if (alreadyOnDisk) {
UI.showProgress('Importing into Premiere…', 92);
} else {
UI.showProgress('Downloading ' + safeName + ' (' + UI.formatBytes(Number(info.file_size || 0)) + ')…', 8);
const r = await _fetch(info.url, false); // presigned S3 — no auth
@ -113,6 +139,8 @@
await Import._writeBuffer(dest, buf);
UI.showProgress('Importing into Premiere…', 92);
}
await Import.importIntoProject(dest);
UI.hideProgress();
UI.toast('Hi-res imported: ' + safeName, 'ok');