From 046d99f57aa65eb76f438ff8754aedcd5ccbd9ad Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Thu, 28 May 2026 07:47:44 -0400 Subject: [PATCH] =?UTF-8?q?UXP=20v2.1.3:=20import-flow=20=E2=80=94=20use?= =?UTF-8?q?=20window.path.join,=20replace=20fs.createWriteStream=20with=20?= =?UTF-8?q?fd-based=20chunked=20write?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../premiere-plugin-uxp/src/import-flow.js | 78 +++++++++++-------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/services/premiere-plugin-uxp/src/import-flow.js b/services/premiere-plugin-uxp/src/import-flow.js index 48f3e97..559ba1b 100644 --- a/services/premiere-plugin-uxp/src/import-flow.js +++ b/services/premiere-plugin-uxp/src/import-flow.js @@ -1,71 +1,80 @@ -// import-flow.js — v2.1.2 -// Download asset → write to temp → importFiles() via UXP premierepro. -// NOTE: `path` module is NOT available in PPro 26 UXP. Use _join() helper. -// Also exposes _tempPath / _streamToFile for timeline.js batch relink. +// import-flow.js — v2.1.3 +// Fixes: +// • require('path') → window.path (global, no require needed, UXP v6.4+) +// • fs.createWriteStream does NOT exist in UXP fs — replaced with +// fd-based chunked write: fs.open → loop fs.write(fd,buf,...) → fs.close +// • os.tmpdir() not documented → use os.homedir() / process.env.TEMP fallback (function () { const Import = {}; const fs = require('fs'); - // `path` is stripped in this UXP build — manual join instead. + // path is window.path (global) — no require('path') in UXP + // os.tmpdir() missing from docs; os.homedir() is available let os; try { os = require('os'); } catch (_) { os = {}; } let uxpFs; try { uxpFs = require('uxp').storage.localFileSystem; } catch (_) { uxpFs = null; } - // Simple path join that works on Windows (backslash) without require('path'). - function _join(base, name) { - const sep = base.indexOf('/') === -1 ? '\\' : '/'; - return base.replace(/[\\/]+$/, '') + sep + name; - } - // ── Temp folder ────────────────────────────────────────────────── + // os.tmpdir() not in UXP docs → fall through to other methods async function _getTempBase() { - // 1. os.tmpdir (may exist in some UXP builds) - try { if (os.tmpdir) { const t = os.tmpdir(); if (typeof t === 'string' && t.length) return t; } } catch (_) {} - // 2. UXP storage API + // 1. UXP storage API (most portable) if (uxpFs && uxpFs.getTemporaryFolder) { - try { const tmp = await uxpFs.getTemporaryFolder(); if (tmp && tmp.nativePath) return tmp.nativePath; } catch (_) {} + try { + const tmp = await uxpFs.getTemporaryFolder(); + if (tmp && tmp.nativePath) return tmp.nativePath; + } catch (_) {} } - // 3. Windows env vars (always present under PPro on Windows) + // 2. Windows env vars (always set under PPro on Windows) try { const e = (typeof process !== 'undefined' && process.env) || {}; - if (e.TEMP) return e.TEMP; - if (e.TMP) return e.TMP; + if (e.TEMP && e.TEMP.length) return e.TEMP; + if (e.TMP && e.TMP.length) return e.TMP; if (e.LOCALAPPDATA) return e.LOCALAPPDATA + '\\Temp'; } catch (_) {} - // 4. Homedir fallback (no path.join needed) - try { if (os.homedir) { const h = os.homedir(); if (h) return h + '\\AppData\\Local\\Temp'; } } catch (_) {} + // 3. os.homedir() is documented in UXP os module + try { + if (os.homedir) { + const h = os.homedir(); + if (h) return h + '\\AppData\\Local\\Temp'; + } + } catch (_) {} throw new Error('Cannot find writable temp folder'); } + // window.path is a documented UXP global (v6.4+) — no require needed Import._tempPath = async function (safeName) { const base = await _getTempBase(); - return _join(base, 'dragonflight-' + safeName); + return path.join(base, 'dragonflight-' + safeName); }; - // ── Stream response body to disk ───────────────────────────────── + // ── Stream response body to disk via fd-based chunked write ────── + // fs.createWriteStream does NOT exist in UXP's require('fs'). + // Use open() → write() chunks → close() instead. + // Chunk size 256 KB — balances memory vs syscall overhead. Import._streamToFile = async function (response, destPath, onProgress) { const total = Number(response.headers.get('content-length') || 0); const reader = response.body.getReader(); - const out = fs.createWriteStream(destPath); + + // Open file for writing (create/truncate) + const fd = await fs.open(destPath, 'w'); let received = 0; - const closed = new Promise((resolve, reject) => { - out.on('finish', resolve); - out.on('error', reject); - }); + let filePos = 0; + try { while (true) { const { value, done } = await reader.read(); if (done) break; - if (!out.write(Buffer.from(value))) { - await new Promise(r => out.once('drain', r)); - } + + // value is Uint8Array; fs.write needs ArrayBuffer + 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 { - out.end(); + await fs.close(fd); } - await closed; return destPath; }; @@ -100,11 +109,12 @@ return Import._ppro_mod; } + // Import a file already on disk into the active Premiere project. Import.importIntoProject = async function (filePath) { const P = _ppro(); - const project = await P.Project.getActiveProject(); + const project = P.Project.getActiveProject(); if (!project) throw new Error('No active Premiere project'); - const root = await project.getRootItem(); + const root = project.getRootItem(); const ok = await project.importFiles([filePath], true, root, false); if (!ok) throw new Error('Premiere refused to import file'); return true;