Fix: direct .gves→hires lookup, add /hires-media mount, debug logging

- Added /mnt/Hitchcock/bmg_video/Media as read-only bind mount at /hires-media in container
- Added direct .gves→hires fallback that works even when proxyLinks is empty
- For each .gves block, searches unlinked high-res proxy block titles against media folder
- Added console logging to trace the lookup process for debugging
- This handles the case where FramelightX creates no explicit linkages at all
This commit is contained in:
Claude 2026-03-31 19:55:29 -04:00
parent c29245758c
commit 36e104ebdd
2 changed files with 76 additions and 34 deletions

View file

@ -38,6 +38,8 @@ services:
- /mnt/smb-ame/Watch:/watch - /mnt/smb-ame/Watch:/watch
- /mnt/smb-ame/Output:/output - /mnt/smb-ame/Output:/output
- /mnt/smb-ame/Logs:/ame-logs - /mnt/smb-ame/Logs:/ame-logs
# High-res media folder for fallback lookup
- /mnt/Hitchcock/bmg_video/Media:/hires-media:ro
volumes: volumes:
app_data: app_data:

View file

@ -246,6 +246,8 @@ function parseProxyLinks(xml, mediaBlocks) {
function findHighResFileForGves(gvesPath, hiresTitle, hiresMediaFolder, fs) { function findHighResFileForGves(gvesPath, hiresTitle, hiresMediaFolder, fs) {
if (!hiresMediaFolder) return null; if (!hiresMediaFolder) return null;
console.log(`[HIRES LOOKUP] Searching for title="${hiresTitle}" in folder="${hiresMediaFolder}"`);
try { try {
// Normalize the folder path // Normalize the folder path
const searchFolder = hiresMediaFolder.replace(/\\/g, '/').replace(/\/$/, ''); const searchFolder = hiresMediaFolder.replace(/\\/g, '/').replace(/\/$/, '');
@ -266,6 +268,7 @@ function findHighResFileForGves(gvesPath, hiresTitle, hiresMediaFolder, fs) {
// Match if filename contains the title // Match if filename contains the title
if (fileLower.includes(titleLower)) { if (fileLower.includes(titleLower)) {
const fullPath = `${searchFolder}/${file}`; const fullPath = `${searchFolder}/${file}`;
console.log(`[HIRES LOOKUP] MATCH FOUND: "${file}" contains "${hiresTitle}"`);
return fullPath.replace(/\//g, '\\'); 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) // ─── Direct .gves → high-res lookup (when proxyLinks is empty) ───
// These occur when FramelightX created proxy blocks but didn't link them explicitly // When FramelightX doesn't create explicit proxy linkages, we need to
if (options.hiresMediaFolder && allGvesBlocks.length > 0) { // directly find high-res files for each .gves block in the media folder.
// Get the first .gves block as reference (for path lookup) if (options.hiresMediaFolder) {
const gvesRef = allGvesBlocks[0].block; const processedGves = new Set(gvesUids); // Already handled above
for (const [uid, hiresBlock] of Object.entries(mediaBlocks)) { for (const { uid: gvesUid, block: gvesBlock } of allGvesBlocks) {
// Skip if already processed, not a proxy, or already has a path if (processedGves.has(gvesUid)) continue;
if (gvesUids.includes(uid) || !hiresBlock.isProxy || hiresBlock.filePath) 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( let hiresPath = findHighResFileForGves(
gvesRef.filePath, // Use first gves path for reference gvesBlock.filePath,
hiresBlock.title, gvesBlock.title,
options.hiresMediaFolder, options.hiresMediaFolder,
fs 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; 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 console.log(`[FALLBACK SWAP] ${gvesPath}${hiresPath}`);
// We'll replace the Title in the XML with the found file
if (hiresBlock.title) { swaps.push({
const titleEscaped = escapeRegex(hiresBlock.title); gvesUid,
hiresUid: 'direct-lookup',
oldPath: gvesPath,
newPath: hiresPath,
newTitle: hiresTitle
});
// Replace FilePath tags
remappedXml = remappedXml.replace( remappedXml = remappedXml.replace(
new RegExp(`<Title>${titleEscaped}</Title>`, 'g'), new RegExp(`<FilePath>${gvesPathEscaped}</FilePath>`, 'g'),
`<FilePath>${hiresPath}</FilePath>`
);
// Replace ActualMediaFilePath tags
remappedXml = remappedXml.replace(
new RegExp(`<ActualMediaFilePath>${gvesPathEscaped}</ActualMediaFilePath>`, 'g'),
`<ActualMediaFilePath>${hiresPath}</ActualMediaFilePath>`
);
// Replace RelativePath tags
if (gvesBlock.relativePath) {
const relPathEscaped = escapeRegex(gvesBlock.relativePath);
remappedXml = remappedXml.replace(
new RegExp(`<RelativePath>${relPathEscaped}</RelativePath>`, 'g'),
`<RelativePath>${hiresPath}</RelativePath>`
);
}
// Replace Title tags that match the .gves filename
if (gvesFilename) {
const gvesTitleEscaped = escapeRegex(gvesFilename);
remappedXml = remappedXml.replace(
new RegExp(`<Title>${gvesTitleEscaped}</Title>`, 'g'),
`<Title>${hiresTitle}</Title>` `<Title>${hiresTitle}</Title>`
); );
} }
// Add a FilePath to the high-res block's Media section // Replace ImplementationID
// Look for the Media block with this Title and add FilePath if missing remappedXml = remappedXml.replace(
const mediaBlockRegex = new RegExp( new RegExp(`<ImplementationID>${escapeRegex(FRAMELIGHTX_IMPL_ID)}</ImplementationID>`, 'g'),
`<Media[^>]*>([^<]*<Title>${escapeRegex(hiresBlock.title)}</Title>[^<]*)</Media>`, `<ImplementationID>${NULL_IMPL_ID}</ImplementationID>`
'g'
); );
remappedXml = remappedXml.replace(mediaBlockRegex, (match) => {
if (match.includes('<FilePath>')) return match; // Already has path
return match.replace(`</Media>`, `<FilePath>${hiresPath}</FilePath></Media>`);
});
swaps.push({
gvesUid: 'unlinked',
hiresUid: uid,
oldPath: 'null',
newPath: hiresPath,
newTitle: hiresTitle
});
} }
} }