refactor: remove dead code and unused frontend handlers across backend and web
This commit is contained in:
@@ -242,32 +242,6 @@ def _is_civitai_api_download_url(url: str) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def _resolve_civitai_download_url_for_aria2(
|
||||
url: str,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
) -> str:
|
||||
"""Resolve CivitAI's API download redirect before handing the URL to aria2."""
|
||||
validated_url = validate_public_http_url(url)
|
||||
if not _is_civitai_api_download_url(validated_url):
|
||||
return validated_url
|
||||
|
||||
try:
|
||||
response, resolved_url, _ = request_public_url(
|
||||
"GET",
|
||||
validated_url,
|
||||
headers=build_download_headers(validated_url, headers),
|
||||
timeout=20,
|
||||
stream=True,
|
||||
max_redirects=1,
|
||||
)
|
||||
response.close()
|
||||
log.debug("Resolved CivitAI download redirect for aria2")
|
||||
return resolved_url
|
||||
except Exception as exc:
|
||||
log.warning(f"Could not pre-resolve CivitAI download URL for aria2: {exc}")
|
||||
return validated_url
|
||||
|
||||
|
||||
def _resolve_download_url_for_aria2(
|
||||
url: str,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
|
||||
@@ -1969,12 +1969,6 @@ def _is_hash_verified_exact_match(result: Optional[Dict[str, Any]], confidence:
|
||||
return bool(result.get("sha256") or result.get("hash"))
|
||||
|
||||
|
||||
|
||||
|
||||
def _build_search_queries(filename: str) -> List[str]:
|
||||
return build_filename_search_queries(filename)
|
||||
|
||||
|
||||
def search_civarchive(
|
||||
query: str,
|
||||
model_type: Optional[str] = None,
|
||||
|
||||
@@ -1252,24 +1252,6 @@ def _enrich_model_info_with_details(
|
||||
return result
|
||||
|
||||
|
||||
def search_civitai_by_hash(
|
||||
hash_value: str, api_key: Optional[str] = None
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""Look up a model by file hash on CivitAI."""
|
||||
result = get_model_info_by_hash(hash_value, api_key=api_key, use_cache=False)
|
||||
if not result:
|
||||
return None
|
||||
return {
|
||||
"source": result.get("source"),
|
||||
"model_id": result.get("model_id"),
|
||||
"version_id": result.get("version_id"),
|
||||
"name": result.get("model_name") or result.get("name", ""),
|
||||
"url": result.get("url"),
|
||||
"download_url": result.get("download_url"),
|
||||
"filename": result.get("filename", ""),
|
||||
"size": result.get("size"),
|
||||
}
|
||||
|
||||
|
||||
def resolve_urn(
|
||||
model_id: int, version_id: int, api_key: Optional[str] = None
|
||||
|
||||
@@ -8,7 +8,6 @@ if parent_dir not in sys.path:
|
||||
sys.path.insert(0, parent_dir)
|
||||
|
||||
from core.matcher import MODEL_TITLE_MATCH_THRESHOLD
|
||||
from core.sources.civitai import search_civitai_by_hash
|
||||
from core.sources.lora_manager_archive import _normalize_model_type
|
||||
from core.type_utils import (
|
||||
normalize_lora_manager_type,
|
||||
@@ -65,36 +64,6 @@ class TestRefactoringUnification(unittest.TestCase):
|
||||
self.assertEqual(res["filename"], "test.safetensors")
|
||||
self.assertEqual(res["extra_field"], "hello_world")
|
||||
|
||||
@patch("core.sources.civitai.get_model_info_by_hash")
|
||||
def test_search_civitai_by_hash(self, mock_get_info):
|
||||
mock_get_info.return_value = {
|
||||
"source": "civitai",
|
||||
"model_id": 500,
|
||||
"version_id": 600,
|
||||
"model_name": "My Civitai Model",
|
||||
"url": "http://civitai/model",
|
||||
"download_url": "http://civitai/download",
|
||||
"filename": "my_model.safetensors",
|
||||
"size": 9999,
|
||||
}
|
||||
res = search_civitai_by_hash("fake_hash", "fake_key")
|
||||
mock_get_info.assert_called_once_with("fake_hash", api_key="fake_key", use_cache=False)
|
||||
self.assertIsNotNone(res)
|
||||
self.assertEqual(res["source"], "civitai")
|
||||
self.assertEqual(res["model_id"], 500)
|
||||
self.assertEqual(res["version_id"], 600)
|
||||
self.assertEqual(res["name"], "My Civitai Model")
|
||||
self.assertEqual(res["url"], "http://civitai/model")
|
||||
self.assertEqual(res["download_url"], "http://civitai/download")
|
||||
self.assertEqual(res["filename"], "my_model.safetensors")
|
||||
self.assertEqual(res["size"], 9999)
|
||||
|
||||
@patch("core.sources.civitai.get_model_info_by_hash")
|
||||
def test_search_civitai_by_hash_not_found(self, mock_get_info):
|
||||
mock_get_info.return_value = None
|
||||
res = search_civitai_by_hash("fake_hash")
|
||||
self.assertIsNone(res)
|
||||
|
||||
@patch("requests.get")
|
||||
def test_request_page_text_success(self, mock_get):
|
||||
from core.sources.civarchive import _request_page_text
|
||||
|
||||
@@ -14,7 +14,7 @@ from core.downloader import (
|
||||
get_metadata_sidecar_path,
|
||||
download_model,
|
||||
get_progress,
|
||||
_resolve_civitai_download_url_for_aria2,
|
||||
_resolve_download_url_for_aria2,
|
||||
start_aria2_daemon,
|
||||
_delete_partial_download_files,
|
||||
download_progress,
|
||||
@@ -307,10 +307,8 @@ class ModelResolverRobustnessTests(unittest.TestCase):
|
||||
def test_civitai_redirection_link_resolution_for_aria2(self):
|
||||
"""
|
||||
Covers Integration Requirement: Redirect resolution for Aria2.
|
||||
Verifies _resolve_civitai_download_url_for_aria2 pre-resolves redirect URLs,
|
||||
and skips non-CivitAI URL requests completely.
|
||||
Verifies _resolve_download_url_for_aria2 pre-resolves redirect URLs.
|
||||
"""
|
||||
# Scenario 1: CivitAI URL redirect
|
||||
api_url = "https://civitai.com/api/download/models/123"
|
||||
redirect_url = "https://civitai-delivery.net/models/123/model.safetensors?token=abc"
|
||||
|
||||
@@ -328,20 +326,10 @@ class ModelResolverRobustnessTests(unittest.TestCase):
|
||||
"core.network_utils.validate_public_http_url",
|
||||
side_effect=lambda value: value,
|
||||
):
|
||||
resolved = _resolve_civitai_download_url_for_aria2(api_url)
|
||||
resolved, _ = _resolve_download_url_for_aria2(api_url)
|
||||
self.assertEqual(resolved, redirect_url)
|
||||
self.assertEqual(mock_get.call_count, 2)
|
||||
|
||||
# Scenario 2: Non-CivitAI URL (e.g. HuggingFace) should bypass redirects immediately
|
||||
hf_url = "https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/model.safetensors"
|
||||
with patch("requests.get") as mock_get_hf, patch(
|
||||
"core.downloader.validate_public_http_url",
|
||||
side_effect=lambda value: value,
|
||||
):
|
||||
resolved_hf = _resolve_civitai_download_url_for_aria2(hf_url)
|
||||
self.assertEqual(resolved_hf, hf_url)
|
||||
mock_get_hf.assert_not_called()
|
||||
|
||||
def test_job_progress_tracker_clamping_and_thread_safety(self):
|
||||
"""
|
||||
Covers Requirement: Thread-safe progress tracking and validation.
|
||||
|
||||
@@ -29,7 +29,6 @@ from core.sources.civarchive import (
|
||||
_normalize_archive_version,
|
||||
parse_civarchive_url,
|
||||
_prepare_size_probe_url,
|
||||
_build_search_queries,
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -483,24 +482,6 @@ class PrepareSizeProbeUrlTests(unittest.TestCase):
|
||||
self.assertIsNone(_prepare_size_probe_url(None))
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# civarchive - _build_search_queries
|
||||
# ===========================================================================
|
||||
class BuildSearchQueriesTests(unittest.TestCase):
|
||||
|
||||
def test_returns_list_of_strings(self):
|
||||
result = _build_search_queries("my_model_v2.safetensors")
|
||||
self.assertIsInstance(result, list)
|
||||
self.assertTrue(all(isinstance(q, str) for q in result))
|
||||
|
||||
def test_non_empty_for_valid_filename(self):
|
||||
result = _build_search_queries("cool-lora-style_v1.safetensors")
|
||||
self.assertGreater(len(result), 0)
|
||||
|
||||
def test_empty_string_returns_list(self):
|
||||
result = _build_search_queries("")
|
||||
self.assertIsInstance(result, list)
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# civitai - parse_civitai_url
|
||||
|
||||
@@ -40,5 +40,6 @@ app.registerExtension({
|
||||
},
|
||||
afterConfigureGraph() {
|
||||
modelResolver.configureWorkflowDependencyMarkerNodes();
|
||||
modelResolver.checkAndOpenForMissingModels();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1540,11 +1540,6 @@ export const queueMethods = {
|
||||
}
|
||||
},
|
||||
|
||||
// Collapse/expand queue panel
|
||||
toggleQueueCollapsed() {
|
||||
this.setQueueCollapsed(!this.queueCollapsed);
|
||||
},
|
||||
|
||||
setQueueCollapsed(collapsed, { persist = true, updatePanel = true } = {}) {
|
||||
const nextCollapsed = !!collapsed;
|
||||
const wasCollapsed = !!this.queueCollapsed;
|
||||
@@ -2907,19 +2902,6 @@ export const queueMethods = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle click on Download All / Cancel All button
|
||||
*/
|
||||
handleDownloadAllClick() {
|
||||
if (this.getActiveQueuePanelDownloadIds().length > 0) {
|
||||
// Cancel all active downloads
|
||||
this.cancelAllDownloads();
|
||||
} else {
|
||||
// Start downloading all missing
|
||||
this.downloadAllMissing();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Cancel all active downloads
|
||||
*/
|
||||
|
||||
@@ -1059,162 +1059,6 @@ export class ModelResolver {
|
||||
// Run auto-resolve for 100% matches - returns the updated workflow
|
||||
const updatedWorkflow = await this.dialog.autoResolve100Percent();
|
||||
|
||||
// Always open Model Resolver to show remaining unresolved models
|
||||
// Pass the updated workflow if available to avoid race condition
|
||||
this.dialog.show(updatedWorkflow || null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark resolved model items in the popup as linked (green) and hide download buttons
|
||||
*/
|
||||
removeResolvedFromPopup(dialog, resolvedFilenames) {
|
||||
log.debug('Model Resolver: Looking for resolved filenames:', resolvedFilenames);
|
||||
|
||||
// Strategy: For each filename, find text nodes containing it,
|
||||
// then find the nearest Download button and mark that row
|
||||
for (const filename of resolvedFilenames) {
|
||||
// Get all text in the dialog and find elements containing our filename
|
||||
const walker = document.createTreeWalker(
|
||||
dialog,
|
||||
NodeFilter.SHOW_TEXT,
|
||||
null,
|
||||
false
|
||||
);
|
||||
|
||||
let node;
|
||||
while ((node = walker.nextNode())) {
|
||||
if (node.textContent?.toLowerCase().includes(filename)) {
|
||||
// Found text containing filename - now find parent with Download button
|
||||
let parent = node.parentElement;
|
||||
let attempts = 0;
|
||||
|
||||
while (parent && parent !== dialog && attempts < 10) {
|
||||
// Look for Download button at this level
|
||||
const downloadBtn = Array.from(parent.querySelectorAll('button'))
|
||||
.find(btn => btn.textContent?.includes('Download') &&
|
||||
!btn.id?.includes('model-resolver'));
|
||||
|
||||
if (downloadBtn) {
|
||||
log.debug('Model Resolver: Found entry for', filename);
|
||||
this.markEntryAsResolved(parent, downloadBtn);
|
||||
break;
|
||||
}
|
||||
|
||||
parent = parent.parentElement;
|
||||
attempts++;
|
||||
}
|
||||
|
||||
// Only process first match for this filename
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a model entry as resolved with visual feedback
|
||||
*/
|
||||
markEntryAsResolved(container, downloadBtn) {
|
||||
// Already marked?
|
||||
if (container.dataset.resolved === 'true') return;
|
||||
container.dataset.resolved = 'true';
|
||||
|
||||
log.debug('Model Resolver: Marking entry as resolved', container);
|
||||
|
||||
// Add green background/styling to the container
|
||||
container.classList.add('mr-resolved-entry');
|
||||
|
||||
// Hide the Download button and replace with badge
|
||||
if (downloadBtn) {
|
||||
// Create badge
|
||||
const badge = document.createElement('span');
|
||||
badge.className = 'mr-resolved-badge';
|
||||
badge.textContent = '✓ Linked';
|
||||
|
||||
// Replace download button with badge
|
||||
downloadBtn.style.display = 'none';
|
||||
downloadBtn.parentElement?.insertBefore(badge, downloadBtn);
|
||||
}
|
||||
|
||||
// Find and hide Copy URL button
|
||||
const allBtns = container.querySelectorAll('button');
|
||||
for (const btn of allBtns) {
|
||||
if (btn.textContent?.includes('Copy URL')) {
|
||||
btn.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Count remaining model items in the popup
|
||||
*/
|
||||
countRemainingItems(dialog) {
|
||||
// Count elements that look like model entries (have Download buttons)
|
||||
const downloadButtons = dialog.querySelectorAll('button');
|
||||
let count = 0;
|
||||
for (const btn of downloadButtons) {
|
||||
if (btn.textContent?.includes('Download') && !btn.id?.includes('model-resolver')) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update nodes directly in the graph without triggering a full workflow reload
|
||||
* This prevents the Missing Models popup from closing
|
||||
*/
|
||||
updateNodesDirectly(resolutions) {
|
||||
if (!app?.graph) {
|
||||
console.warn('Model Resolver: Cannot update nodes - graph not available');
|
||||
return;
|
||||
}
|
||||
|
||||
for (const resolution of resolutions) {
|
||||
const nodeId = resolution.node_id;
|
||||
const widgetIndex = resolution.widget_index;
|
||||
const resolvedPath = resolution.resolved_path;
|
||||
|
||||
// Find the node in the graph
|
||||
const node = app.graph.getNodeById(nodeId);
|
||||
if (!node) {
|
||||
console.warn(`Model Resolver: Node ${nodeId} not found in graph`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update the widget value
|
||||
if (node.widgets && node.widgets[widgetIndex]) {
|
||||
const widget = node.widgets[widgetIndex];
|
||||
widget.value = resolvedPath;
|
||||
|
||||
// Trigger widget callback if it exists
|
||||
if (widget.callback) {
|
||||
widget.callback(resolvedPath, app.graph, node, null, null);
|
||||
}
|
||||
|
||||
log.debug(`Model Resolver: Updated node ${nodeId} widget ${widgetIndex} to ${resolvedPath}`);
|
||||
} else if (node.widgets_values) {
|
||||
// Fallback: update widgets_values array directly
|
||||
node.widgets_values[widgetIndex] = resolvedPath;
|
||||
log.debug(`Model Resolver: Updated node ${nodeId} widgets_values[${widgetIndex}] to ${resolvedPath}`);
|
||||
}
|
||||
|
||||
// Mark node as dirty to trigger redraw
|
||||
if (node.setDirtyCanvas) {
|
||||
node.setDirtyCanvas(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger canvas redraw
|
||||
if (app.graph.setDirtyCanvas) {
|
||||
app.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if auto-open is enabled in user settings
|
||||
*/
|
||||
isAutoOpenEnabled() {
|
||||
return localStorage.getItem('ModelResolver.autoOpenOnMissing') !== 'false';
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user