feat: implement deep auto-refresh for ComfyUI bridge with backend reset and frontend combo update

This commit is contained in:
Thomas
2026-05-09 23:30:50 +02:00
parent 9ba1fac932
commit 2f11b87dcd
9 changed files with 442 additions and 691 deletions
+78 -10
View File
@@ -55,7 +55,6 @@ public class Main extends JFrame {
private final IModelValidator modelValidator;
private final de.tki.comfymodels.service.impl.RestBridgeService restBridge;
private final de.tki.comfymodels.service.impl.ArchiveService archiveService;
private final de.tki.comfymodels.service.impl.ComfyUIBridgeClient bridgeClient;
@Autowired
private ConfigService configService;
@@ -90,8 +89,7 @@ public class Main extends JFrame {
public Main(IModelAnalyzer analyzer, IDownloadManager downloadManager,
IWorkflowService workflowService, IModelSearchService searchService,
IModelValidator modelValidator, de.tki.comfymodels.service.impl.RestBridgeService restBridge,
de.tki.comfymodels.service.impl.ArchiveService archiveService,
de.tki.comfymodels.service.impl.ComfyUIBridgeClient bridgeClient) {
de.tki.comfymodels.service.impl.ArchiveService archiveService) {
this.analyzer = analyzer;
this.downloadManager = downloadManager;
this.workflowService = workflowService;
@@ -99,7 +97,6 @@ public class Main extends JFrame {
this.modelValidator = modelValidator;
this.restBridge = restBridge;
this.archiveService = archiveService;
this.bridgeClient = bridgeClient;
}
public void launch(String[] args) {
@@ -354,7 +351,7 @@ public class Main extends JFrame {
try {
configService.unlock(pass);
restBridge.setApiToken(configService.getApiToken());
syncBridgeToken();
syncBridgeFiles(); // NEW: Automatically sync code & token on every unlock
// NEW: Auto-import model list on first start or vault reset
if (configService.isVaultFresh()) {
@@ -769,7 +766,6 @@ public class Main extends JFrame {
() -> SwingUtilities.invokeLater(() -> {
downloadButton.setEnabled(true);
statusLabel.setText("Queue finished.");
bridgeClient.notifyModelDownloaded();
if (configService.isShutdownAfterDownloadEnabled()) performSystemShutdown();
})
);
@@ -843,7 +839,7 @@ public class Main extends JFrame {
return;
}
configService.setComfyUIPath(selectedPath);
syncBridgeToken();
syncBridgeFiles();
JOptionPane.showMessageDialog(dialog, "API Token synchronized successfully.");
});
@@ -1447,6 +1443,36 @@ public class Main extends JFrame {
JTable archiveTable = new JTable(archiveTableModel);
archiveTable.setRowHeight(25);
archiveTable.getColumnModel().getColumn(0).setMaxWidth(50);
// Enable sorting with custom comparator for human-readable sizes
javax.swing.table.TableRowSorter<DefaultTableModel> archiveSorter = new javax.swing.table.TableRowSorter<>(archiveTableModel);
archiveSorter.setComparator(3, (s1, s2) -> Long.compare(parseSizeToBytes((String)s1), parseSizeToBytes((String)s2)));
archiveSorter.setSortable(0, false); // Disable sorting for checkbox column to allow "Select All"
archiveTable.setRowSorter(archiveSorter);
// Add "Select All" functionality to header
archiveTable.getTableHeader().addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
int viewIdx = archiveTable.columnAtPoint(e.getPoint());
if (viewIdx == -1) return;
int modelIdx = archiveTable.convertColumnIndexToModel(viewIdx);
if (modelIdx == 0) {
boolean allSelected = true;
for (int i = 0; i < archiveTableModel.getRowCount(); i++) {
if (!(Boolean) archiveTableModel.getValueAt(i, 0)) {
allSelected = false;
break;
}
}
boolean newValue = !allSelected;
for (int i = 0; i < archiveTableModel.getRowCount(); i++) {
archiveTableModel.setValueAt(newValue, i, 0);
}
}
}
});
JScrollPane archiveScroll = new JScrollPane(archiveTable);
archivePanel.add(archiveScroll, BorderLayout.CENTER);
@@ -1494,6 +1520,36 @@ public class Main extends JFrame {
JTable restoreTable = new JTable(restoreTableModel);
restoreTable.setRowHeight(25);
restoreTable.getColumnModel().getColumn(0).setMaxWidth(50);
// Enable sorting with custom comparator for human-readable sizes
javax.swing.table.TableRowSorter<DefaultTableModel> restoreSorter = new javax.swing.table.TableRowSorter<>(restoreTableModel);
restoreSorter.setComparator(3, (s1, s2) -> Long.compare(parseSizeToBytes((String)s1), parseSizeToBytes((String)s2)));
restoreSorter.setSortable(0, false); // Disable sorting for checkbox column to allow "Select All"
restoreTable.setRowSorter(restoreSorter);
// Add "Select All" functionality to header
restoreTable.getTableHeader().addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
int viewIdx = restoreTable.columnAtPoint(e.getPoint());
if (viewIdx == -1) return;
int modelIdx = restoreTable.convertColumnIndexToModel(viewIdx);
if (modelIdx == 0) {
boolean allSelected = true;
for (int i = 0; i < restoreTableModel.getRowCount(); i++) {
if (!(Boolean) restoreTableModel.getValueAt(i, 0)) {
allSelected = false;
break;
}
}
boolean newValue = !allSelected;
for (int i = 0; i < restoreTableModel.getRowCount(); i++) {
restoreTableModel.setValueAt(newValue, i, 0);
}
}
}
});
JScrollPane restoreScroll = new JScrollPane(restoreTable);
restorePanel.add(restoreScroll, BorderLayout.CENTER);
@@ -1611,6 +1667,7 @@ public class Main extends JFrame {
JOptionPane.showMessageDialog(this, finalSuccess + " models successfully " + title.toLowerCase() + "ed.",
"Operation Complete", JOptionPane.INFORMATION_MESSAGE);
analyzeJsonContent();
downloadManager.notifyComfyUI();
});
}).start();
@@ -1735,7 +1792,7 @@ public class Main extends JFrame {
helpDialog.setVisible(true);
}
private void syncBridgeToken() {
private void syncBridgeFiles() {
String comfyPath = configService.getComfyUIPath();
if (comfyPath == null || comfyPath.isEmpty()) return;
@@ -1747,8 +1804,19 @@ public class Main extends JFrame {
Path targetDir = customNodesDir.resolve("comfyui-model-downloader");
if (Files.exists(targetDir) && Files.isDirectory(targetDir)) {
writeExtensionConfig(targetDir.toFile());
System.out.println("Synchronized API token with ComfyUI bridge at: " + targetDir);
try {
// Always sync latest code files from resources to target
extractResource("/comfyui-bridge/__init__.py", targetDir.resolve("__init__.py").toFile());
Path webDir = targetDir.resolve("web");
Files.createDirectories(webDir);
extractResource("/comfyui-bridge/web/downloader.js", webDir.resolve("downloader.js").toFile());
// Also update config
writeExtensionConfig(targetDir.toFile());
System.out.println("[Bridge-Sync] Successfully synchronized latest bridge code and token.");
} catch (IOException e) {
System.err.println("[Bridge-Sync] Failed to sync code: " + e.getMessage());
}
}
}
@@ -11,6 +11,11 @@ public interface IDownloadManager {
void stop();
boolean isPaused();
/**
* Triggers a refresh signal to the ComfyUI bridge.
*/
void notifyComfyUI();
/**
* Returns a map of current statuses for each model in the queue.
*/
@@ -1,52 +0,0 @@
package de.tki.comfymodels.service.impl;
import org.springframework.stereotype.Service;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
@Service
public class ComfyUIBridgeClient {
private final HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(2))
.build();
private String comfyUrl = "http://127.0.0.1:8188";
public void setComfyUrl(String url) {
if (url != null && !url.isEmpty()) {
this.comfyUrl = url.endsWith("/") ? url.substring(0, url.length() - 1) : url;
}
}
public void notifyModelDownloaded() {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(comfyUrl + "/kippster/model-downloaded"))
.POST(HttpRequest.BodyPublishers.noBody())
.timeout(Duration.ofSeconds(5))
.build();
System.out.println("Notifying ComfyUI at: " + request.uri());
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenAccept(response -> {
if (response.statusCode() >= 200 && response.statusCode() < 300) {
System.out.println("ComfyUI notified successfully: " + response.body());
} else {
System.err.println("Failed to notify ComfyUI. Status: " + response.statusCode());
}
})
.exceptionally(ex -> {
System.err.println("Could not reach ComfyUI for refresh: " + ex.getMessage());
return null;
});
} catch (Exception e) {
System.err.println("Error creating notification request: " + e.getMessage());
}
}
}
@@ -154,6 +154,15 @@ public class ConfigService {
save();
}
public String getComfyUIUrl() { return settings.optString("comfyui_url", "http://127.0.0.1:8188"); }
public void setComfyUIUrl(String url) {
if (url != null && !url.isEmpty()) {
String sanitized = url.endsWith("/") ? url.substring(0, url.length() - 1) : url;
settings.put("comfyui_url", sanitized);
save();
}
}
public String getApiToken() {
String token = settings.optString("api_token", "");
if (token.isEmpty() && isUnlocked()) {
@@ -121,20 +121,68 @@ public class DefaultDownloadManager implements IDownloadManager {
}
// NEU: Benachrichtigt ComfyUI
private void notifyComfyUI() {
@Override
public void notifyComfyUI() {
String comfyUrl = configService.getComfyUIUrl();
if (sendRefreshPing(comfyUrl)) {
return;
}
// Auto-Discovery: If configured URL fails, search for ComfyUI on common ports
System.out.println("🔍 [Model-Downloader] ComfyUI unter " + comfyUrl + " nicht erreicht. Suche automatisch...");
String discoveredUrl = discoverComfyUrl();
if (discoveredUrl != null) {
System.out.println("✨ [Model-Downloader] ComfyUI automatisch gefunden unter: " + discoveredUrl);
configService.setComfyUIUrl(discoveredUrl);
sendRefreshPing(discoveredUrl);
} else {
System.err.println("❌ [Model-Downloader] ComfyUI konnte nicht automatisch gefunden werden. Bitte stelle sicher, dass es läuft.");
}
}
private boolean sendRefreshPing(String url) {
try {
System.out.println("🚀 [Model-Downloader] Sende Refresh-Ping an ComfyUI (Port 8188)...");
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://127.0.0.1:8188/kippster/model-downloaded"))
.uri(URI.create(url + "/kippster/model-downloaded"))
.timeout(java.time.Duration.ofSeconds(2))
.POST(HttpRequest.BodyPublishers.noBody())
.build();
// Synchron senden, damit wir sehen, was passiert
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("✅ [Model-Downloader] ComfyUI hat geantwortet mit Status: " + response.statusCode());
} catch (Exception e) {
System.err.println("❌ [Model-Downloader] Konnte ComfyUI nicht erreichen. Läuft es auf Port 8188? Fehler: " + e.getMessage());
if (response.statusCode() == 200) {
System.out.println("✅ [Model-Downloader] ComfyUI (" + url + ") erfolgreich benachrichtigt.");
return true;
}
} catch (Exception ignored) {}
return false;
}
private String discoverComfyUrl() {
// Scan typical ComfyUI ports, including common alternatives like 8000
int[] commonPorts = {8188, 8000, 8189, 8190, 3000, 8080};
for (int port : commonPorts) {
if (checkPort(port)) return "http://127.0.0.1:" + port;
}
// Expanded range scan
for (int port = 8191; port <= 8199; port++) {
if (checkPort(port)) return "http://127.0.0.1:" + port;
}
return null;
}
private boolean checkPort(int port) {
String url = "http://127.0.0.1:" + port;
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url + "/system_stats"))
.timeout(java.time.Duration.ofMillis(300))
.GET()
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
return response.statusCode() == 200 && response.body().contains("system");
} catch (Exception ignored) {}
return false;
}
private boolean isSelected(int index) {