diff --git a/prproj-remapper.js b/prproj-remapper.js index 5a54cb9..038c267 100644 --- a/prproj-remapper.js +++ b/prproj-remapper.js @@ -31,9 +31,10 @@ const NULL_IMPL_ID = '00000000-0000-0000-0000-000000000000'; * Premiere expects (XML parsers tend to reformat and break .prproj files). * * @param {Buffer} prprojBuffer - The raw .prproj file (gzipped) + * @param {Object} options - Optional config { hiresMediaFolder: string, fsModule?: fs } * @returns {Object} { buffer: Buffer, report: Object } */ -async function remapPrproj(prprojBuffer) { +async function remapPrproj(prprojBuffer, options = {}) { // Step 1: Decompress const xml = zlib.gunzipSync(prprojBuffer).toString('utf-8'); @@ -44,7 +45,7 @@ async function remapPrproj(prprojBuffer) { const proxyLinks = parseProxyLinks(xml, mediaBlocks); // Step 4: Perform the path swaps - const { remappedXml, swaps } = performSwaps(xml, mediaBlocks, proxyLinks); + const { remappedXml, swaps } = performSwaps(xml, mediaBlocks, proxyLinks, options); // Step 5: Recompress const outputBuffer = zlib.gzipSync(Buffer.from(remappedXml, 'utf-8')); @@ -223,11 +224,94 @@ function parseProxyLinks(xml, mediaBlocks) { } /** - * Perform the actual path swaps in the XML string. + * Try to find a high-res file in the media folder when the .prproj doesn't have a path. + * Strategy: Look for files in the high-res media folder that might correspond to this .gves. + * + * For example: + * - .gves file: "01KMR6YXTCJHG0AGMWVF98JRMWC--01.gves" + * - Media blocks may have metadata (Title) like "ZacIsCool--File7" + * - High-res folder has: "ZacIsCool--File7.mp4" + * + * Fallback strategy: + * 1. If hiresBlock has a Title, search for that title in the high-res folder + * 2. Search for common video/audio extensions (.mp4, .mov, .wav, etc.) + * 3. If still not found, return null (path won't be swapped) + * + * @param {string} gvesPath - The .gves file path + * @param {string} hiresTitle - Title from the high-res Media block + * @param {string} hiresMediaFolder - UNC/SMB path to search + * @param {Object} fs - File system module + * @returns {string|null} The high-res file path if found, null otherwise */ -function performSwaps(xml, mediaBlocks, proxyLinks) { +function findHighResFileForGves(gvesPath, hiresTitle, hiresMediaFolder, fs) { + if (!hiresMediaFolder) return null; + + try { + // Normalize the folder path + const searchFolder = hiresMediaFolder.replace(/\\/g, '/').replace(/\/$/, ''); + + // If Title is provided, search for it + if (hiresTitle) { + const videoExts = ['.mp4', '.mov', '.mxf', '.mov', '.wav', '.aif', '.aiff']; + + for (const ext of videoExts) { + const candidate = `${searchFolder}/${hiresTitle}${ext}`; + try { + if (fs.existsSync(candidate)) { + // Convert back to UNC format if it was a local path + return candidate.replace(/\//g, '\\'); + } + } catch (e) { + // Ignore individual file check failures + } + } + } + + // Fallback: list files in the folder and try to match by base name + try { + const files = fs.readdirSync(searchFolder); + const videoExts = new Set(['.mp4', '.mov', '.mxf', '.wav', '.aif', '.aiff']); + + // Extract base name from .gves path (without extension) + const gvesFilename = gvesPath.split('\\').pop().split('/').pop(); + const gvesBaseName = gvesFilename.replace(/\.[^.]+$/, '').toLowerCase(); + + // Look for files that might match + for (const file of files) { + const ext = '.' + file.split('.').pop().toLowerCase(); + if (!videoExts.has(ext)) continue; + + const fileBaseName = file.replace(/\.[^.]+$/, '').toLowerCase(); + + // Partial match: if the file name contains a key part of the gves name + if (fileBaseName.includes(gvesBaseName.substring(0, Math.min(8, gvesBaseName.length)))) { + const fullPath = `${searchFolder}/${file}`; + return fullPath.replace(/\//g, '\\'); + } + } + } catch (e) { + // Folder read failed, return null + return null; + } + } catch (e) { + // Any errors during lookup, return null + return null; + } + + return null; +} + +/** + * Perform the actual path swaps in the XML string. + * @param {string} xml - The project XML + * @param {Object} mediaBlocks - Parsed media blocks + * @param {Object} proxyLinks - Proxy linkage map + * @param {Object} options - { hiresMediaFolder?: string, fsModule?: fs } + */ +function performSwaps(xml, mediaBlocks, proxyLinks, options = {}) { let remappedXml = xml; const swaps = []; + const fs = options.fsModule || require('fs'); // Collect all unique .gves UIDs that have a mapped high-res block const gvesUids = Object.keys(proxyLinks); @@ -237,7 +321,21 @@ function performSwaps(xml, mediaBlocks, proxyLinks) { const gvesBlock = mediaBlocks[gvesUid]; const hiresBlock = mediaBlocks[hiresUid]; - if (!gvesBlock || !hiresBlock || !hiresBlock.filePath) continue; + if (!gvesBlock || !hiresBlock) continue; + + let hiresPath = hiresBlock.filePath; + + // Fallback: if high-res path is null, try to find it in the media folder + if (!hiresPath && options.hiresMediaFolder && gvesBlock.filePath) { + hiresPath = findHighResFileForGves( + gvesBlock.filePath, + hiresBlock.title || gvesBlock.title, + options.hiresMediaFolder, + fs + ); + } + + if (!hiresPath) continue; const hiresPath = hiresBlock.filePath; const hiresTitle = hiresBlock.title || pathToTitle(hiresPath); diff --git a/public/index.html b/public/index.html index 1617136..ba65e6c 100644 --- a/public/index.html +++ b/public/index.html @@ -604,6 +604,11 @@
AMEEncodingLog.txt. On macOS: /Users/<user>/Documents/Adobe/Adobe Media Encoder/<version>