diff --git a/docker-compose.yml b/docker-compose.yml index acd13f9..d0acf08 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -38,6 +38,8 @@ services: - /mnt/smb-ame/Watch:/watch - /mnt/smb-ame/Output:/output - /mnt/smb-ame/Logs:/ame-logs + # High-res media folder for fallback lookup + - /mnt/Hitchcock/bmg_video/Media:/hires-media:ro volumes: app_data: diff --git a/prproj-remapper.js b/prproj-remapper.js index 2834201..a8289de 100644 --- a/prproj-remapper.js +++ b/prproj-remapper.js @@ -246,6 +246,8 @@ function parseProxyLinks(xml, mediaBlocks) { function findHighResFileForGves(gvesPath, hiresTitle, hiresMediaFolder, fs) { if (!hiresMediaFolder) return null; + console.log(`[HIRES LOOKUP] Searching for title="${hiresTitle}" in folder="${hiresMediaFolder}"`); + try { // Normalize the folder path const searchFolder = hiresMediaFolder.replace(/\\/g, '/').replace(/\/$/, ''); @@ -266,6 +268,7 @@ function findHighResFileForGves(gvesPath, hiresTitle, hiresMediaFolder, fs) { // Match if filename contains the title if (fileLower.includes(titleLower)) { const fullPath = `${searchFolder}/${file}`; + console.log(`[HIRES LOOKUP] MATCH FOUND: "${file}" contains "${hiresTitle}"`); return fullPath.replace(/\//g, '\\'); } } @@ -406,56 +409,93 @@ 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; + // ─── Direct .gves → high-res lookup (when proxyLinks is empty) ─── + // When FramelightX doesn't create explicit proxy linkages, we need to + // directly find high-res files for each .gves block in the media folder. + if (options.hiresMediaFolder) { + const processedGves = new Set(gvesUids); // Already handled above - 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; + for (const { uid: gvesUid, block: gvesBlock } of allGvesBlocks) { + if (processedGves.has(gvesUid)) continue; - // Try to find a high-res file for this unlinked proxy using its Title + // Extract the .gves filename to use as a lookup key + const gvesFilename = gvesBlock.filePath.split('\\').pop().split('/').pop(); + const gvesBaseName = gvesFilename.replace(/\.[^.]+$/, ''); + + // Try to find a matching high-res file using the .gves block's title + // or by searching the high-res media folder let hiresPath = findHighResFileForGves( - gvesRef.filePath, // Use first gves path for reference - hiresBlock.title, + gvesBlock.filePath, + gvesBlock.title, options.hiresMediaFolder, fs ); + // Also try matching against unlinked high-res proxy blocks' titles + if (!hiresPath) { + for (const [huid, hblock] of Object.entries(mediaBlocks)) { + if (!hblock.isProxy || hblock.filePath) continue; + hiresPath = findHighResFileForGves( + gvesBlock.filePath, + hblock.title, + options.hiresMediaFolder, + fs + ); + if (hiresPath) break; + } + } + if (!hiresPath) continue; - const hiresTitle = hiresBlock.title || pathToTitle(hiresPath); + const hiresTitle = pathToTitle(hiresPath); + const gvesPath = gvesBlock.filePath; + const gvesPathEscaped = escapeRegex(gvesPath); - // 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); + console.log(`[FALLBACK SWAP] ${gvesPath} → ${hiresPath}`); + + swaps.push({ + gvesUid, + hiresUid: 'direct-lookup', + oldPath: gvesPath, + newPath: hiresPath, + newTitle: hiresTitle + }); + + // Replace FilePath tags + remappedXml = remappedXml.replace( + new RegExp(`${gvesPathEscaped}`, 'g'), + `${hiresPath}` + ); + + // Replace ActualMediaFilePath tags + remappedXml = remappedXml.replace( + new RegExp(`${gvesPathEscaped}`, 'g'), + `${hiresPath}` + ); + + // Replace RelativePath tags + if (gvesBlock.relativePath) { + const relPathEscaped = escapeRegex(gvesBlock.relativePath); remappedXml = remappedXml.replace( - new RegExp(`${titleEscaped}`, 'g'), + new RegExp(`${relPathEscaped}`, 'g'), + `${hiresPath}` + ); + } + + // Replace Title tags that match the .gves filename + if (gvesFilename) { + const gvesTitleEscaped = escapeRegex(gvesFilename); + remappedXml = remappedXml.replace( + new RegExp(`${gvesTitleEscaped}`, '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' + // Replace ImplementationID + remappedXml = remappedXml.replace( + new RegExp(`${escapeRegex(FRAMELIGHTX_IMPL_ID)}`, 'g'), + `${NULL_IMPL_ID}` ); - 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 - }); } }