From 1fb790a569b514bb6a3160d1a129a13071109de7 Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Thu, 28 May 2026 09:05:49 -0400 Subject: [PATCH] =?UTF-8?q?UXP=20v2.1.5:=20import-flow=20=E2=80=94=20await?= =?UTF-8?q?=20all=20premierepro=20calls=20(runtime=20is=20async=20despite?= =?UTF-8?q?=20docs=20saying=20sync)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../premiere-plugin-uxp/src/import-flow.js | 72 +++++-------------- 1 file changed, 18 insertions(+), 54 deletions(-) diff --git a/services/premiere-plugin-uxp/src/import-flow.js b/services/premiere-plugin-uxp/src/import-flow.js index 544d750..f0b6881 100644 --- a/services/premiere-plugin-uxp/src/import-flow.js +++ b/services/premiere-plugin-uxp/src/import-flow.js @@ -1,20 +1,11 @@ -// import-flow.js — v2.1.4 -// Root cause of "Cannot read properties of undefined (reading)": -// response.body is null when redirect:'manual' is used — that fetch option -// is NOT supported in UXP. UXP auto-follows redirects and does NOT expose -// manual redirect control. Dropping redirect:'manual' entirely. -// -// Download strategy: -// response.arrayBuffer() → write entire buffer via fs.writeFile() -// Simpler than fd-based chunked write, works for proxy files (typically <2GB). -// Progress reporting is approximate (0% → 100% on completion). +// import-flow.js — v2.1.5 +// premierepro API: docs say sync, runtime returns Promises. Await everything. (function () { const Import = {}; const fs = require('fs'); // window.path is a UXP global (v6.4+) — no require('path') - // os.tmpdir() not in UXP — use env.TEMP or uxp storage let os; try { os = require('os'); } catch (_) { os = {}; } let uxpFs; try { uxpFs = require('uxp').storage.localFileSystem; } catch (_) { uxpFs = null; } @@ -46,25 +37,21 @@ return path.join(base, 'dragonflight-' + safeName); }; - // ── Write ArrayBuffer to disk ──────────────────────────────────── - // fs.writeFile with flag:'w' creates/overwrites the file. + // Write ArrayBuffer to disk via fs.writeFile Import._writeBuffer = async function (destPath, arrayBuffer) { await fs.writeFile(destPath, arrayBuffer); return destPath; }; - // ── Fetch with auth — UXP-safe ─────────────────────────────────── - // UXP auto-follows redirects. 'redirect' option is NOT supported. - // We only add Authorization for same-origin requests (server URL base). - // For S3 presigned URLs (off-origin) we do NOT add Bearer — that would - // break the presigned signature. + // Fetch — no redirect option (not supported in UXP) + // addAuth: only for same-origin proxy URLs, NOT for S3 presigned URLs async function _fetch(url, addAuth) { const headers = {}; if (addAuth && API.state.apiToken) { headers['Authorization'] = 'Bearer ' + API.state.apiToken; } const r = await fetch(url, { headers }); - if (!r.ok) throw new Error('HTTP ' + r.status + ' from ' + url); + if (!r.ok) throw new Error('HTTP ' + r.status + ' fetching ' + url); return r; } @@ -76,15 +63,16 @@ return Import._ppro_mod; } - // Import a file already on disk into the active Premiere project. - // project.importFiles() is async (it actually imports the file). + // Import a file into the active Premiere project. + // ALL premierepro calls must be awaited — runtime returns Promises + // even though docs list them as synchronous. Import.importIntoProject = async function (filePath) { const P = _ppro(); - const project = P.Project.getActiveProject(); // sync + const project = await P.Project.getActiveProject(); if (!project) throw new Error('No active Premiere project'); - const root = project.getRootItem(); // sync + const root = await project.getRootItem(); const ok = await project.importFiles([filePath], true, root, false); - if (!ok) throw new Error('Premiere refused to import file'); + if (!ok) throw new Error('Premiere refused to import the file'); return true; }; @@ -97,8 +85,7 @@ const { url } = await API.getProxyUrl(asset.id); UI.showProgress('Downloading ' + safeName + '…', 10); - // Same-origin proxy URL — add auth - const r = await _fetch(url, true); + const r = await _fetch(url, true); // same-origin — add auth UI.showProgress('Writing to disk…', 70); const buf = await r.arrayBuffer(); @@ -119,8 +106,7 @@ const dest = await Import._tempPath(safeName); UI.showProgress('Downloading ' + safeName + ' (' + UI.formatBytes(Number(info.file_size || 0)) + ')…', 8); - // Presigned S3 URL — no auth header (would break signature) - const r = await _fetch(info.url, false); + const r = await _fetch(info.url, false); // presigned S3 — no auth UI.showProgress('Writing to disk…', 75); const buf = await r.arrayBuffer(); @@ -133,32 +119,10 @@ return { localPath: dest, safeName }; }; - // Expose for timeline.js batch relink (it downloads hi-res files too) - Import._streamToFile = async function (response, destPath, onProgress) { - // In UXP, response.body may be null — fall back to arrayBuffer() - if (response.body && response.body.getReader) { - const total = Number(response.headers.get('content-length') || 0); - const reader = response.body.getReader(); - const fd = await fs.open(destPath, 'w'); - let received = 0, filePos = 0; - try { - while (true) { - const { value, done } = await reader.read(); - if (done) break; - const buf = value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength); - const { bytesWritten } = await fs.write(fd, buf, 0, buf.byteLength, filePos); - filePos += bytesWritten; - received += value.byteLength; - if (onProgress) onProgress({ received, total }); - } - } finally { await fs.close(fd); } - } else { - // Fallback: buffer entire response - if (onProgress) onProgress({ received: 0, total: 0 }); - const buf = await response.arrayBuffer(); - await Import._writeBuffer(destPath, buf); - if (onProgress) onProgress({ received: buf.byteLength, total: buf.byteLength }); - } + // Used by timeline.js batch relink + Import._streamToFile = async function (response, destPath /*, onProgress */) { + const buf = await response.arrayBuffer(); + await Import._writeBuffer(destPath, buf); return destPath; };