diff --git a/tests/test_downloads_tab_workflow_route.mjs b/tests/test_downloads_tab_workflow_route.mjs index bde6ca8..97c1650 100644 --- a/tests/test_downloads_tab_workflow_route.mjs +++ b/tests/test_downloads_tab_workflow_route.mjs @@ -4840,7 +4840,10 @@ test('manual URL table row preserves the explicit provenance boundary', () => { test('source table rows preserve context-specific source labels', () => { const getDownloadSourceTableRow = eval(`(${extractMethod(searchPanelMethodsSource, 'getDownloadSourceTableRow')})`); const getCustomUrlResultTableRow = eval(`(${extractMethod(searchPanelMethodsSource, 'getCustomUrlResultTableRow')})`); + const prepareSearchResultRow = eval(`(${extractMethod(downloadTargetMethodsSource, 'prepareSearchResultRow')})`); + const metadataCalls = { path: [], download: [] }; const dialog = { + prepareSearchResultRow, getFilenameFromPath(value = '') { return String(value).split(/[\\/]/).at(-1) || ''; }, @@ -4856,11 +4859,13 @@ test('source table rows preserve context-specific source labels', () => { getVersionedModelName(name, version) { return version ? `${name} ${version}` : name; }, - getDownloadPathMetadata() { - return {}; + getDownloadPathMetadata(_missing, source) { + metadataCalls.path.push(source); + return { type: 'path' }; }, - getDownloadMetadata() { - return {}; + getDownloadMetadata(_missing, source, options) { + metadataCalls.download.push({ source, options }); + return { type: 'download' }; }, getLocalHashMatchIdentitiesForResult() { return []; @@ -4904,6 +4909,13 @@ test('source table rows preserve context-specific source labels', () => { assert.equal(providerRow.sourceLabel, 'CivitAI'); assert.equal(workflowRow.sourceLabel, 'Workflow URL'); assert.equal(customRow.sourceLabel, 'Custom URL'); + assert.deepEqual(providerRow.pathMetadata, { type: 'path' }); + assert.deepEqual(providerRow.downloadMetadata, { type: 'download' }); + assert.equal(metadataCalls.path[0].filename, 'model.safetensors'); + assert.equal(metadataCalls.path[0].model, 'model.safetensors'); + assert.equal(metadataCalls.download[0].source.filename, 'model.safetensors'); + assert.equal(metadataCalls.download[0].options.filename, 'model.safetensors'); + assert.equal(metadataCalls.download[0].options.url, 'https://example.test/download'); }); test('search suggestion does not let a delayed incompatible result replace workflow path context', () => { @@ -7181,6 +7193,7 @@ test('search result refreshes preserve keyed progress and result DOM', () => { test('search provider rows preserve a shared result row contract', () => { const displaySearchResults = eval(`(${extractMethod(resolveDownloadMethodsSource, 'displaySearchResults')})`); const buildSearchResultRow = eval(`(${extractMethod(searchPanelMethodsSource, 'buildSearchResultRow')})`); + const prepareSearchResultRow = eval(`(${extractMethod(downloadTargetMethodsSource, 'prepareSearchResultRow')})`); const previousGlobals = { getModelCardUrl: globalThis.getModelCardUrl, getCivitaiModelUrl: globalThis.getCivitaiModelUrl, @@ -7189,6 +7202,7 @@ test('search provider rows preserve a shared result row contract', () => { const rows = []; const dialog = { buildSearchResultRow, + prepareSearchResultRow, getHashMatchLabelMap() { return null; }, diff --git a/web/resolver/actions/resolve_download_methods.js b/web/resolver/actions/resolve_download_methods.js index 493c0de..97379bb 100644 --- a/web/resolver/actions/resolve_download_methods.js +++ b/web/resolver/actions/resolve_download_methods.js @@ -3703,18 +3703,7 @@ export const resolveDownloadMethods = { const rowKeys = new Set(); const addRow = (row) => { if (!row) return; - if (!row.pathMetadata) { - row.pathMetadata = this.getDownloadPathMetadata(missing, row.detailsContext || row); - } - if (!row.downloadMetadata) { - row.downloadMetadata = this.getDownloadMetadata(missing, row.detailsContext || row, { - filename: row.downloadFilename || row.filename || '', - category: row.category || missing.category || '', - url: row.downloadUrl || '', - openUrl: row.openUrl || '', - pathMetadata: row.pathMetadata - }); - } + row = this.prepareSearchResultRow(missing, row); const rowKey = `${row.sourceKey}:${row.downloadUrl || row.openUrl || `${row.model}:${row.filename}`}`; if (rowKeys.has(rowKey)) return; rowKeys.add(rowKey); diff --git a/web/resolver/search/download_target_methods.js b/web/resolver/search/download_target_methods.js index b21c6cc..81fd8ef 100644 --- a/web/resolver/search/download_target_methods.js +++ b/web/resolver/search/download_target_methods.js @@ -1248,6 +1248,26 @@ export const downloadTargetMethods = { }; }, + prepareSearchResultRow(missing = {}, row = {}, metadataSource = null, pathMetadataSource = null) { + if (!row || typeof row !== 'object') return row; + + const source = metadataSource || row.detailsContext || row; + const pathSource = pathMetadataSource || source; + if (!row.pathMetadata) { + row.pathMetadata = this.getDownloadPathMetadata(missing, pathSource); + } + if (!row.downloadMetadata) { + row.downloadMetadata = this.getDownloadMetadata(missing, source, { + filename: row.downloadFilename || row.filename || '', + category: row.category || missing.category || '', + url: row.downloadUrl || '', + openUrl: row.openUrl || '', + pathMetadata: row.pathMetadata + }); + } + return row; + }, + getDownloadPathMetadata(missing = {}, source = {}) { const { merged } = this.getDownloadSourceContext(missing, source, { respectProvenanceBoundary: false diff --git a/web/resolver/search/search_panel.js b/web/resolver/search/search_panel.js index 92745d5..ee45e0a 100644 --- a/web/resolver/search/search_panel.js +++ b/web/resolver/search/search_panel.js @@ -1696,13 +1696,6 @@ export const searchPanelMethods = { fullModelName && fullModelName !== downloadFilename ? downloadFilename : '', baseModel ].filter(Boolean); - const rowPathMetadata = this.getDownloadPathMetadata(missing, { - ...downloadSource, - filename: downloadFilename, - model: modelName, - version: modelParts.version, - category: rowCategory - }); const rowDetailsContext = ['civitai', 'civarchive', 'huggingface'].includes(normalizedSource) ? { ...downloadSource, @@ -1723,6 +1716,14 @@ export const searchPanelMethods = { category: rowCategory } : null; + const rowPathMetadataSource = { + ...downloadSource, + filename: downloadFilename, + model: modelName, + version: modelParts.version, + category: rowCategory + }; + const rowMetadataSource = rowDetailsContext || rowPathMetadataSource; const localHashMatches = Array.isArray(missing.matches) ? missing.matches.filter(match => match?.hash_lookup_source) : []; @@ -1738,7 +1739,7 @@ export const searchPanelMethods = { localHashMatchIdentities ) || ''; - return { + const row = { sourceKey, sourceLabel, model: modelName, @@ -1752,25 +1753,12 @@ export const searchPanelMethods = { openUrl: modelUrl, searchedAt: this.getSearchResultTimestamp(downloadSource), localHashMatchIdentities, - pathMetadata: rowPathMetadata, - downloadMetadata: this.getDownloadMetadata(missing, rowDetailsContext || { - ...downloadSource, - filename: downloadFilename, - model: modelName, - version: modelParts.version, - category: rowCategory - }, { - filename: downloadFilename, - category: rowCategory, - url: downloadSource.url, - openUrl: modelUrl, - pathMetadata: rowPathMetadata - }), detailsContext: rowDetailsContext, match: isFromWorkflow ? { label: 'Provided', className: 'strong' } : this.getSearchResultMatchDisplay(downloadSource, 'Known', 'strong', hashMatchLabel) }; + return this.prepareSearchResultRow(missing, row, rowMetadataSource, rowPathMetadataSource); }, getCustomUrlResultTableRow(missing, result = {}, hashLabelMap = null, localHashMatches = []) {