From c29245758c74bab68684e88929fad1c727e666ea Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 31 Mar 2026 19:53:22 -0400 Subject: [PATCH] Handle unlinked high-res proxy blocks with fallback lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When proxyLinks is empty (no explicit .gves→hires linkage), now also process unlinked high-res proxy blocks that have: - IsProxy: true - FilePath: null - Title metadata (e.g. 'File 1', 'File 2') Uses fallback lookup to find matching files in hiresMediaFolder by Title, then injects FilePath into the XML Media block. This handles the case where FramelightX creates proxies but doesn't establish explicit linkages. --- prproj-remapper.js | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/prproj-remapper.js b/prproj-remapper.js index f737c08..2834201 100644 --- a/prproj-remapper.js +++ b/prproj-remapper.js @@ -323,6 +323,12 @@ function performSwaps(xml, mediaBlocks, proxyLinks, options = {}) { // Collect all unique .gves UIDs that have a mapped high-res block const gvesUids = Object.keys(proxyLinks); + // Also collect any .gves blocks that aren't in proxyLinks (for unlinked proxies) + const allGvesBlocks = Object.entries(mediaBlocks) + .filter(([uid, block]) => block.isGves) + .map(([uid, block]) => ({ uid, block })); + + // Process linked proxy pairs first for (const gvesUid of gvesUids) { const hiresUid = proxyLinks[gvesUid]; const gvesBlock = mediaBlocks[gvesUid]; @@ -400,6 +406,59 @@ function performSwaps(xml, mediaBlocks, proxyLinks, options = {}) { ); } + // Handle unlinked high-res proxy blocks (have IsProxy: true, FilePath: null, no proxyLinks entry) + // These occur when FramelightX created proxy blocks but didn't link them explicitly + if (options.hiresMediaFolder && allGvesBlocks.length > 0) { + // Get the first .gves block as reference (for path lookup) + const gvesRef = allGvesBlocks[0].block; + + for (const [uid, hiresBlock] of Object.entries(mediaBlocks)) { + // Skip if already processed, not a proxy, or already has a path + if (gvesUids.includes(uid) || !hiresBlock.isProxy || hiresBlock.filePath) continue; + + // Try to find a high-res file for this unlinked proxy using its Title + let hiresPath = findHighResFileForGves( + gvesRef.filePath, // Use first gves path for reference + hiresBlock.title, + options.hiresMediaFolder, + fs + ); + + if (!hiresPath) continue; + + const hiresTitle = hiresBlock.title || pathToTitle(hiresPath); + + // Perform replacements for this unlinked proxy + // We'll replace the Title in the XML with the found file + if (hiresBlock.title) { + const titleEscaped = escapeRegex(hiresBlock.title); + remappedXml = remappedXml.replace( + new RegExp(`${titleEscaped}`, 'g'), + `${hiresTitle}` + ); + } + + // Add a FilePath to the high-res block's Media section + // Look for the Media block with this Title and add FilePath if missing + const mediaBlockRegex = new RegExp( + `]*>([^<]*${escapeRegex(hiresBlock.title)}[^<]*)`, + 'g' + ); + remappedXml = remappedXml.replace(mediaBlockRegex, (match) => { + if (match.includes('')) return match; // Already has path + return match.replace(``, `${hiresPath}`); + }); + + swaps.push({ + gvesUid: 'unlinked', + hiresUid: uid, + oldPath: 'null', + newPath: hiresPath, + newTitle: hiresTitle + }); + } + } + // Remove IsProxy and OfflineReason from high-res blocks so they're treated as online remappedXml = remappedXml.replace(/\s*true<\/IsProxy>/g, ''); remappedXml = remappedXml.replace(/\s*\d+<\/OfflineReason>/g, '');