diff --git a/backend/invoke_ai_web_server.py b/backend/invoke_ai_web_server.py index 1c21cdd096..bf14133681 100644 --- a/backend/invoke_ai_web_server.py +++ b/backend/invoke_ai_web_server.py @@ -10,7 +10,7 @@ import base64 import os from werkzeug.utils import secure_filename -from flask import Flask, redirect, send_from_directory, flash, request, url_for, jsonify +from flask import Flask, redirect, send_from_directory, request, make_response from flask_socketio import SocketIO from PIL import Image, ImageOps from PIL.Image import Image as ImageType @@ -107,15 +107,22 @@ class InvokeAIWebServer: @self.app.route("/upload", methods=["POST"]) def upload(): try: + filename = "" # check if the post request has the file part - if "file" not in request.files: - return "No file part", 400 - file = request.files["file"] - - # If the user does not select a file, the browser submits an - # empty file without a filename. - if file.filename == "": - return "No selected file", 400 + if "file" in request.files: + file = request.files["file"] + # If the user does not select a file, the browser submits an + # empty file without a filename. + if file.filename == "": + return make_response("No file selected", 400) + filename = file.filename + elif "dataURL" in request.form: + file = dataURL_to_bytes(request.form["dataURL"]) + if "filename" not in request.form or request.form["filename"] == "": + return make_response("No filename provided", 400) + filename = request.form["filename"] + else: + return make_response("No file or dataURL", 400) kind = request.form["kind"] @@ -128,15 +135,15 @@ class InvokeAIWebServer: elif kind == "mask": path = self.mask_image_path else: - return f"Invalid upload kind: {kind}", 400 + return make_response(f"Invalid upload kind: {kind}", 400) - if not self.allowed_file(file.filename): - return ( + if not self.allowed_file(filename): + return make_response( f'Invalid file type, must be one of: {", ".join(self.ALLOWED_EXTENSIONS)}', 400, ) - secured_filename = secure_filename(file.filename) + secured_filename = secure_filename(filename) uuid = uuid4().hex truncated_uuid = uuid[:8] @@ -146,7 +153,11 @@ class InvokeAIWebServer: file_path = os.path.join(path, name) - file.save(file_path) + if "dataURL" in request.form: + with open(file_path, "wb") as f: + f.write(file) + else: + file.save(file_path) mtime = os.path.getmtime(file_path) (width, height) = Image.open(file_path).size @@ -160,7 +171,7 @@ class InvokeAIWebServer: }, } - return response, 200 + return make_response(response, 200) except Exception as e: self.socketio.emit("error", {"message": (str(e))}) @@ -168,7 +179,7 @@ class InvokeAIWebServer: traceback.print_exc() print("\n") - return "Error uploading file", 500 + return make_response("Error uploading file", 500) self.load_socketio_listeners(self.socketio) @@ -916,11 +927,18 @@ class InvokeAIWebServer: (width, height) = image.size + generated_image_outdir = ( + self.result_path + if generation_parameters["generation_mode"] + in ["txt2img", "img2img"] + else self.temp_image_path + ) + path = self.save_result_image( image, command, metadata, - self.result_path, + generated_image_outdir, postprocessing=postprocessing, ) diff --git a/frontend/src/app/socketio/actions.ts b/frontend/src/app/socketio/actions.ts index faad169b07..64cf705a77 100644 --- a/frontend/src/app/socketio/actions.ts +++ b/frontend/src/app/socketio/actions.ts @@ -26,8 +26,8 @@ export const requestNewImages = createAction( export const cancelProcessing = createAction( 'socketio/cancelProcessing' ); -export const uploadImage = createAction('socketio/uploadImage'); -export const uploadMaskImage = createAction('socketio/uploadMaskImage'); +// export const uploadImage = createAction('socketio/uploadImage'); +// export const uploadMaskImage = createAction('socketio/uploadMaskImage'); export const requestSystemConfig = createAction( 'socketio/requestSystemConfig' diff --git a/frontend/src/app/socketio/emitters.ts b/frontend/src/app/socketio/emitters.ts index d0e3abed36..9307f625fb 100644 --- a/frontend/src/app/socketio/emitters.ts +++ b/frontend/src/app/socketio/emitters.ts @@ -180,13 +180,13 @@ const makeSocketIOEmitters = ( emitCancelProcessing: () => { socketio.emit('cancel'); }, - emitUploadImage: (payload: InvokeAI.UploadImagePayload) => { - const { file, destination } = payload; - socketio.emit('uploadImage', file, file.name, destination); - }, - emitUploadMaskImage: (file: File) => { - socketio.emit('uploadMaskImage', file, file.name); - }, + // emitUploadImage: (payload: InvokeAI.UploadImagePayload) => { + // const { file, destination } = payload; + // socketio.emit('uploadImage', file, file.name, destination); + // }, + // emitUploadMaskImage: (file: File) => { + // socketio.emit('uploadMaskImage', file, file.name); + // }, emitRequestSystemConfig: () => { socketio.emit('requestSystemConfig'); }, diff --git a/frontend/src/app/socketio/listeners.ts b/frontend/src/app/socketio/listeners.ts index 935fd9c42d..1ec854d61e 100644 --- a/frontend/src/app/socketio/listeners.ts +++ b/frontend/src/app/socketio/listeners.ts @@ -103,20 +103,28 @@ const makeSocketIOListeners = ( onGenerationResult: (data: InvokeAI.ImageResultResponse) => { try { const { shouldLoopback, activeTab } = getState().options; + const { boundingBox: _, generationMode, ...rest } = data; + const newImage = { uuid: uuidv4(), - ...data, - category: 'result', + ...rest, }; - dispatch( - addImage({ - category: 'result', - image: newImage, - }) - ); + if (['txt2img', 'img2img'].includes(generationMode)) { + newImage.category = 'result'; + dispatch( + addImage({ + category: 'result', + image: newImage, + }) + ); + } - if (data.generationMode === 'outpainting' && data.boundingBox) { + if ( + ['inpainting', 'outpainting'].includes(generationMode) && + data.boundingBox + ) { + newImage.category = 'temp'; const { boundingBox } = data; dispatch( addImageToOutpainting({ @@ -140,6 +148,8 @@ const makeSocketIOListeners = ( } } + dispatch(clearIntermediateImage()); + dispatch( addLogEntry({ timestamp: dateFormat(new Date(), 'isoDateTime'), @@ -368,16 +378,16 @@ const makeSocketIOListeners = ( /** * Callback to run when we receive a 'maskImageUploaded' event. */ - onMaskImageUploaded: (data: InvokeAI.ImageUrlResponse) => { - const { url } = data; - dispatch(setMaskPath(url)); - dispatch( - addLogEntry({ - timestamp: dateFormat(new Date(), 'isoDateTime'), - message: `Mask image uploaded: ${url}`, - }) - ); - }, + // onMaskImageUploaded: (data: InvokeAI.ImageUrlResponse) => { + // const { url } = data; + // dispatch(setMaskPath(url)); + // dispatch( + // addLogEntry({ + // timestamp: dateFormat(new Date(), 'isoDateTime'), + // message: `Mask image uploaded: ${url}`, + // }) + // ); + // }, onSystemConfig: (data: InvokeAI.SystemConfig) => { dispatch(setSystemConfig(data)); }, diff --git a/frontend/src/app/socketio/middleware.ts b/frontend/src/app/socketio/middleware.ts index 4095706e27..ac17884128 100644 --- a/frontend/src/app/socketio/middleware.ts +++ b/frontend/src/app/socketio/middleware.ts @@ -44,7 +44,7 @@ export const socketioMiddleware = () => { onProcessingCanceled, onImageDeleted, // onImageUploaded, - onMaskImageUploaded, + // onMaskImageUploaded, onSystemConfig, onModelChanged, onModelChangeFailed, @@ -58,8 +58,8 @@ export const socketioMiddleware = () => { emitRequestImages, emitRequestNewImages, emitCancelProcessing, - emitUploadImage, - emitUploadMaskImage, + // emitUploadImage, + // emitUploadMaskImage, emitRequestSystemConfig, emitRequestModelChange, } = makeSocketIOEmitters(store, socketio); @@ -108,9 +108,9 @@ export const socketioMiddleware = () => { // onImageUploaded(data); // }); - socketio.on('maskImageUploaded', (data: InvokeAI.ImageUrlResponse) => { - onMaskImageUploaded(data); - }); + // socketio.on('maskImageUploaded', (data: InvokeAI.ImageUrlResponse) => { + // onMaskImageUploaded(data); + // }); socketio.on('systemConfig', (data: InvokeAI.SystemConfig) => { onSystemConfig(data); @@ -166,15 +166,15 @@ export const socketioMiddleware = () => { break; } - case 'socketio/uploadImage': { - emitUploadImage(action.payload); - break; - } + // case 'socketio/uploadImage': { + // emitUploadImage(action.payload); + // break; + // } - case 'socketio/uploadMaskImage': { - emitUploadMaskImage(action.payload); - break; - } + // case 'socketio/uploadMaskImage': { + // emitUploadMaskImage(action.payload); + // break; + // } case 'socketio/requestSystemConfig': { emitRequestSystemConfig(); diff --git a/frontend/src/features/canvas/util/layerToBlob.ts b/frontend/src/features/canvas/util/layerToDataURL.ts similarity index 67% rename from frontend/src/features/canvas/util/layerToBlob.ts rename to frontend/src/features/canvas/util/layerToDataURL.ts index 7f8681ff27..4317003025 100644 --- a/frontend/src/features/canvas/util/layerToBlob.ts +++ b/frontend/src/features/canvas/util/layerToDataURL.ts @@ -1,6 +1,6 @@ import Konva from 'konva'; -const layerToBlob = async (layer: Konva.Layer, stageScale: number) => { +const layerToDataURL = (layer: Konva.Layer, stageScale: number) => { const tempScale = layer.scale(); const { x: relativeX, y: relativeY } = layer.getClientRect({ @@ -15,12 +15,12 @@ const layerToBlob = async (layer: Konva.Layer, stageScale: number) => { const clientRect = layer.getClientRect(); - const blob = await layer.toBlob(clientRect); + const dataURL = layer.toDataURL(clientRect); // Unscale the canvas layer.scale(tempScale); - return { blob, relativeX, relativeY }; + return { dataURL, relativeX, relativeY }; }; -export default layerToBlob; +export default layerToDataURL; diff --git a/frontend/src/features/canvas/util/mergeAndUploadCanvas.ts b/frontend/src/features/canvas/util/mergeAndUploadCanvas.ts index 28105b6d63..55f69394da 100644 --- a/frontend/src/features/canvas/util/mergeAndUploadCanvas.ts +++ b/frontend/src/features/canvas/util/mergeAndUploadCanvas.ts @@ -4,7 +4,7 @@ import Konva from 'konva'; import { MutableRefObject } from 'react'; import * as InvokeAI from 'app/invokeai'; import { v4 as uuidv4 } from 'uuid'; -import layerToBlob from './layerToBlob'; +import layerToDataURL from './layerToDataURL'; export const mergeAndUploadCanvas = createAsyncThunk( 'canvas/mergeAndUploadCanvas', @@ -25,16 +25,17 @@ export const mergeAndUploadCanvas = createAsyncThunk( if (!canvasImageLayerRef.current) return; - const { blob, relativeX, relativeY } = await layerToBlob( + const { dataURL, relativeX, relativeY } = layerToDataURL( canvasImageLayerRef.current, stageScale ); - if (!blob) return; + if (!dataURL) return; const formData = new FormData(); - formData.append('file', blob as Blob, 'merged_canvas.png'); + formData.append('dataURL', dataURL); + formData.append('filename', 'merged_canvas.png'); formData.append('kind', saveToGallery ? 'result' : 'temp'); const response = await fetch(window.location.origin + '/upload', {