lllyasviel / Fooocus

Focus on prompting and generating
GNU General Public License v3.0
40.15k stars 5.57k forks source link

[Request] Supports http restful apis #442

Closed qy8502 closed 8 months ago

qy8502 commented 12 months ago

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.

TimothyAlexisVass commented 12 months ago

Fooocus uses Gradio. https://www.gradio.app/guides You can look at the API Clients that Gradio have made available.

qy8502 commented 12 months ago

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();  // 递减计数器
}

`

TimothyAlexisVass commented 12 months ago

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.

TimothyAlexisVass commented 12 months ago

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.

TimothyAlexisVass commented 11 months ago

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.

mashb1t commented 8 months ago

You can use the fork https://github.com/konieshadow/Fooocus-API, which implements a full REST API for Fooocus.