Closed qy8502 closed 8 months ago
Fooocus uses Gradio. https://www.gradio.app/guides You can look at the API Clients that Gradio have made available.
When I use websocket to access Gradio's interface, if the socket connection for this painting breaks, the resulting image will be mangled. The image obtained by the next request is the image of the lost connection request.
So I want to have some kind of http request apis. Such as http://127.0.0.1:8080/image/submit and http://127.0.0.1:8080/image/process.
`
@Override
public String drawImageForUrl(String prompt, int width, int height, Map<String, Object> ext) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
String uri = "ws://" + getFooocusDrawingProcessorProperties().getHost() + "/queue/join";
drawingPrompt = chatProcessor.translateToEnglish(prompt);
drawingStyle = getRandomElement(getFooocusDrawingProcessorProperties().getStyles());
drawingSize = width + "×" + height;
ext.put("style", drawingStyle);
ext.put("prompt", drawingPrompt);
sessionLatch = new CountDownLatch(1); // 创建计数器,初始值为1
sessionError = "Unknown Error";
sessionFilePath = null;
container.connectToServer(this, URI.create(uri));
sessionLatch.await(); // 等待计数器归零
if (sessionFilePath != null) {
return "http://" + getFooocusDrawingProcessorProperties().getHost() + "/file=" + sessionFilePath;
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
throw new RuntimeException(sessionError);
}
private void handleSessionException(String message, Throwable ex) {
log.error(message, ex);
this.sessionError = message + ": " + ex.getMessage();
if (session != null) {
try {
session.close();
} catch (IOException e) {
sessionLatch.countDown(); // 递减计数器
log.error("FooocusDrawingProcessor.handleSessionException关闭session发生异常", ex);
}
}
}
@OnOpen
public void onOpen(Session session) {
this.session = session;
session.setMaxTextMessageBufferSize(Integer.MAX_VALUE);
session.setMaxBinaryMessageBufferSize(Integer.MAX_VALUE);
sessionHash = Integer.toHexString(session.hashCode());
log.info("FooocusDrawingProcessor.onOpen: Connected to WebSocket server");
}
@OnMessage
public void onMessage(String message) {
log.info("FooocusDrawingProcessor.onMessage: Received message: " + (message.length() > 1024 ? message.substring(0, 1024) + "..." : message));
try {
Map<String, Object> map = JsonUtil.readValue(message, Map.class);
if (map.containsKey("msg")) {
String msg = (String) map.get("msg");
switch (msg) {
case "send_hash":
sendHash();
return;
case "send_data":
sendData();
return;
case "estimation":
case "process_starts":
case "process_generating":
return;
case "process_completed":
processCompleted(map);
break;
default:
log.info("FooocusDrawingProcessor.onMessage: Unknown message type: " + msg);
}
} else {
log.error("FooocusDrawingProcessor.onMessage: Unknown message format: " + message);
}
session.close();
} catch (Exception e) {
handleSessionException("FooocusDrawingProcessor.onMessage处理消息发生异常", e);
}
}
private void processCompleted(Map<String, Object> map) {
if (map.containsKey("output")) {
Map<String, Object> output = (Map<String, Object>) map.get("output");
if (output.containsKey("data")) {
List<Object> data = (List<Object>) output.get("data");
if (!data.isEmpty()) {
Map<String, Object> value = (Map<String, Object>) data.get(data.size() - 1);
if (value.containsKey("value")) {
List<Map<String, Object>> values = (List<Map<String, Object>>) value.get("value");
if (!values.isEmpty()) {
Map<String, Object> firstValue = values.get(0);
if (firstValue.containsKey("name")) {
sessionFilePath = (String) firstValue.get("name");
log.info("FooocusDrawingProcessor.processCompleted: File Path: " + sessionFilePath);
}
}
}
}
}
}
}
private void sendData() throws IOException {
Map<String, Object> sendData = new HashMap<>();
sendData.put("data", Arrays.asList(drawingPrompt, getFooocusDrawingProcessorProperties().getNegativePrompt(),
Collections.singletonList(drawingStyle), "Speed", drawingSize, 1, new Random().nextInt(Integer.MAX_VALUE), 2,
"sd_xl_base_1.0_0.9vae.safetensors", "sd_xl_refiner_1.0_0.9vae.safetensors", "sd_xl_offset_example-lora_1.0.safetensors",
0.5, "None", 0.5, "None", 0.5, "None", 0.5, "None", 0.5, false, "uov", "Disabled", null, Collections.emptyList(), null));
sendData.put("event_data", null);
sendData.put("fn_index", 9);
sendData.put("session_hash", sessionHash);
String jsonData = JsonUtil.writeValueAsString(sendData);
// 发送消息
session.getBasicRemote().sendText(jsonData);
log.info("FooocusDrawingProcessor.sendData: Sent message: " + jsonData);
}
private void sendHash() throws IOException {
// 构建要发送的消息
Map<String, Object> sendHash = new HashMap<>();
sendHash.put("fn_index", 9);
sendHash.put("session_hash", sessionHash);
String jsonHash = JsonUtil.writeValueAsString(sendHash);
// 发送消息
session.getBasicRemote().sendText(jsonHash);
log.info("FooocusDrawingProcessor.sendHash: Sent message: " + jsonHash);
}
@OnClose
public void onClose(CloseReason reason) {
log.info("FooocusDrawingProcessor.onClose: Disconnected from WebSocket server: " + reason.getReasonPhrase());
sessionLatch.countDown(); // 递减计数器
}
`
I suggest that you contribute to the Gradio App https://github.com/gradio-app/gradio if you want to create a Java API client interface. Currently, the gradio client supports Javascript and Python.
In addition, I think that the better solution is to add headless interaction to Fooocus, as suggested in #385
So that generation can be triggered with a script such as generate.py
, instead of webui.py
.
Headless and batch generating has a pull request: #471
Simply run your generations using the --headless
argument, without starting the UI and then get the data from the outputfolder.
You could also add your own --api flag to headless.py which could return Base64 representation of the images as per your requirement.
You can use the fork https://github.com/konieshadow/Fooocus-API, which implements a full REST API for Fooocus.
I have a java program that wants to access Fooocus to draw, but I can only access the Fooocus interface through websocket. It's possible, but it's not friendly. Can provide http request api interface, can be more easily provided to third-party programs to use.