diff --git a/__init__.py b/__init__.py index c18b0ab2..7b00495d 100644 --- a/__init__.py +++ b/__init__.py @@ -115,6 +115,7 @@ def write_config(): 'git_exe': get_config()['git_exe'], 'channel_url': get_config()['channel_url'], 'channel_url_list': get_config()['channel_url_list'], + 'share_option': get_config()['share_option'], 'bypass_ssl': get_config()['bypass_ssl'] } with open(config_path, 'w') as configfile: @@ -146,6 +147,7 @@ def read_config(): 'git_exe': default_conf['git_exe'] if 'git_exe' in default_conf else '', 'channel_url': default_conf['channel_url'] if 'channel_url' in default_conf else 'https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main', 'channel_url_list': ch_url_list, + 'share_option': default_conf['share_option'] if 'share_option' in default_conf else 'all', 'bypass_ssl': default_conf['bypass_ssl'] if 'bypass_ssl' in default_conf else False, } @@ -156,6 +158,7 @@ def read_config(): 'git_exe': '', 'channel_url': 'https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main', 'channel_url_list': '', + 'share_option': 'all', 'bypass_ssl': False } @@ -401,7 +404,7 @@ def git_pull(path): origin = repo.remote(name='origin') origin.pull() repo.git.submodule('update', '--init', '--recursive') - + repo.close() return True @@ -1344,7 +1347,7 @@ async def install_model(request): if json_data['url'].startswith('https://github.com') or json_data['url'].startswith('https://huggingface.co'): model_dir = get_model_dir(json_data) download_url(json_data['url'], model_dir) - + return web.json_response({}, content_type='application/json') else: res = download_url_with_agent(json_data['url'], model_path) @@ -1408,6 +1411,29 @@ async def channel_url_list(request): return web.Response(status=200) + +@server.PromptServer.instance.routes.get("/manager/share_option") +async def channel_url_list(request): + if "value" in request.rel_url.query: + get_config()['share_option'] = request.rel_url.query['value'] + write_config() + else: + return web.Response(text=get_config()['share_option'], status=200) + + return web.Response(status=200) + + +def get_openart_auth(): + if not os.path.exists(os.path.join(folder_paths.base_path, ".openart_key")): + return None + try: + with open(os.path.join(folder_paths.base_path, ".openart_key"), "r") as f: + openart_key = f.read().strip() + return openart_key if openart_key else None + except: + return None + + def get_matrix_auth(): if not os.path.exists(os.path.join(folder_paths.base_path, "matrix_auth")): return None @@ -1437,6 +1463,22 @@ def get_comfyworkflows_auth(): except: return None +@server.PromptServer.instance.routes.get("/manager/get_openart_auth") +async def api_get_openart_auth(request): + # print("Getting stored Matrix credentials...") + openart_key = get_openart_auth() + if not openart_key: + return web.Response(status=404) + return web.json_response({"openart_key": openart_key}) + +@server.PromptServer.instance.routes.post("/manager/set_openart_auth") +async def api_set_openart_auth(request): + json_data = await request.json() + openart_key = json_data['openart_key'] + with open(os.path.join(folder_paths.base_path, ".openart_key"), "w") as f: + f.write(openart_key) + return web.Response(status=200) + @server.PromptServer.instance.routes.get("/manager/get_matrix_auth") async def api_get_matrix_auth(request): # print("Getting stored Matrix credentials...") @@ -1491,13 +1533,13 @@ async def share_art(request): prompt = json_data['prompt'] potential_outputs = json_data['potential_outputs'] selected_output_index = json_data['selected_output_index'] - + try: output_to_share = potential_outputs[int(selected_output_index)] except: # for now, pick the first output output_to_share = potential_outputs[0] - + assert output_to_share['type'] in ('image', 'output') output_dir = folder_paths.get_output_directory() @@ -1522,7 +1564,7 @@ async def share_art(request): if "comfyworkflows" in share_destinations: share_website_host = "https://comfyworkflows.com" share_endpoint = f"{share_website_host}/api" - + # get presigned urls async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session: async with session.post( @@ -1530,7 +1572,7 @@ async def share_art(request): json={ "assetFileName": asset_filename, "assetFileType": assetFileType, - "workflowJsonFileName" : 'workflow.json', + "workflowJsonFileName" : 'workflow.json', "workflowJsonFileType" : 'application/json', }, @@ -1541,7 +1583,7 @@ async def share_art(request): assetFileKey = presigned_urls_json["assetFileKey"] workflowJsonFilePresignedUrl = presigned_urls_json["workflowJsonFilePresignedUrl"] workflowJsonFileKey = presigned_urls_json["workflowJsonFileKey"] - + # upload asset async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session: async with session.put(assetFilePresignedUrl, data=open(asset_filepath, "rb")) as resp: @@ -1592,7 +1634,7 @@ async def share_art(request): homeserver = homeserver.replace("http://", "https://") if not homeserver.startswith("https://"): homeserver = "https://" + homeserver - + client = MatrixClient(homeserver) try: token = client.login(username=matrix_auth['username'], password=matrix_auth['password']) @@ -1604,7 +1646,7 @@ async def share_art(request): matrix = MatrixHttpApi(homeserver, token=token) with open(asset_filepath, 'rb') as f: mxc_url = matrix.media_upload(f.read(), content_type, filename=filename)['content_uri'] - + workflow_json_mxc_url = matrix.media_upload(prompt['workflow'], 'application/json', filename='workflow.json')['content_uri'] text_content = "" @@ -1621,7 +1663,7 @@ async def share_art(request): import traceback traceback.print_exc() return web.json_response({"error" : "An error occurred when sharing your art to Matrix."}, content_type='application/json', status=500) - + return web.json_response({ "comfyworkflows" : { "url" : None if "comfyworkflows" not in share_destinations else f"{share_website_host}/workflows/{workflowId}", diff --git a/js/comfyui-manager.js b/js/comfyui-manager.js index 5dd9e18e..d6e9c8e9 100644 --- a/js/comfyui-manager.js +++ b/js/comfyui-manager.js @@ -36,6 +36,7 @@ var update_comfyui_button = null; var fetch_updates_button = null; var update_all_button = null; var badge_mode = "none"; +let share_option = 'all'; // copied style from https://github.com/pythongosssss/ComfyUI-Custom-Scripts const style = ` @@ -79,7 +80,16 @@ async function init_badge_mode() { .then(data => { badge_mode = data; }) } +async function init_share_option() { + api.fetchApi('/manager/share_option') + .then(response => response.text()) + .then(data => { + share_option = data || 'all'; + }); +} + await init_badge_mode(); +await init_share_option(); async function fetchNicknames() { @@ -413,9 +423,18 @@ class ManagerMenuDialog extends ComfyDialog { for (const option of share_options) { share_combo.appendChild($el('option', { value: option[0], text: `Share: ${option[1]}` }, [])); } + + api.fetchApi('/manager/share_option') + .then(response => response.text()) + .then(data => { + share_combo.value = data || 'all'; + share_option = data || 'all'; + }); + share_combo.addEventListener('change', function (event) { const value = event.target.value; - localStorage.setItem("share_option", value); + share_option = value; + api.fetchApi(`/manager/share_option?value=${value}`); const shareButton = document.getElementById("shareButton"); if (value === 'none') { shareButton.style.display = "none"; @@ -423,7 +442,6 @@ class ManagerMenuDialog extends ComfyDialog { shareButton.style.display = "inline-block"; } }); - share_combo.value = localStorage.getItem("share_option") || 'all'; return [ $el("div", {}, [this.local_mode_checkbox, checkbox_text, this.update_check_checkbox, uc_checkbox_text]), @@ -611,12 +629,11 @@ app.registerExtension({ shareButton.id = "shareButton"; shareButton.textContent = "Share"; shareButton.onclick = () => { - const shareOption = localStorage.getItem("share_option") || 'all'; - if (shareOption === 'openart') { + if (share_option === 'openart') { showOpenArtShareDialog(); return; - } else if (shareOption === 'matrix' || shareOption === 'comfyworkflows') { - showShareDialog(); + } else if (share_option === 'matrix' || share_option === 'comfyworkflows') { + showShareDialog(share_option); return; } @@ -631,7 +648,7 @@ app.registerExtension({ // Load share option from local storage to determine whether to show // the share button. - const shouldShowShareButton = localStorage.getItem("share_option") !== 'none'; + const shouldShowShareButton = share_option !== 'none'; shareButton.style.display = shouldShowShareButton ? "inline-block" : "none"; menu.append(shareButton); diff --git a/js/comfyui-share-common.js b/js/comfyui-share-common.js index d871c83a..9f100ab1 100644 --- a/js/comfyui-share-common.js +++ b/js/comfyui-share-common.js @@ -159,28 +159,31 @@ export const showOpenArtShareDialog = () => { OpenArtShareDialog.instance.show(); } -export const showShareDialog = () => { +export const showShareDialog = async (share_option) => { if (!ShareDialog.instance) { - ShareDialog.instance = new ShareDialog(); + ShareDialog.instance = new ShareDialog(share_option); } - app.graphToPrompt().then(prompt => { - // console.log({ prompt }) - return app.graph._nodes; - }).then(nodes => { - // console.log({ nodes }); - const { potential_outputs, potential_output_nodes } = getPotentialOutputsAndOutputNodes(nodes); - if (potential_outputs.length === 0) { - if (potential_output_nodes.length === 0) { - // todo: add support for other output node types (animatediff combine, etc.) - const supported_nodes_string = SUPPORTED_OUTPUT_NODE_TYPES.join(", "); - alert(`No supported output node found (${supported_nodes_string}). To share this workflow, please add an output node to your graph and re-run your prompt.`); - } else { - alert("To share this, first run a prompt. Once it's done, click 'Share'.\n\nNOTE: Images of the Share target can only be selected in the PreviewImage, SaveImage, and VHS_VideoCombine nodes. In the case of VHS_VideoCombine, only the image/gif and image/webp formats are supported."); - } - return; - } - ShareDialog.instance.show({ potential_outputs, potential_output_nodes }); - }); + return app.graphToPrompt() + .then(prompt => { + // console.log({ prompt }) + return app.graph._nodes; + }) + .then(nodes => { + // console.log({ nodes }); + const { potential_outputs, potential_output_nodes } = getPotentialOutputsAndOutputNodes(nodes); + if (potential_outputs.length === 0) { + if (potential_output_nodes.length === 0) { + // todo: add support for other output node types (animatediff combine, etc.) + const supported_nodes_string = SUPPORTED_OUTPUT_NODE_TYPES.join(", "); + alert(`No supported output node found (${supported_nodes_string}). To share this workflow, please add an output node to your graph and re-run your prompt.`); + } else { + alert("To share this, first run a prompt. Once it's done, click 'Share'.\n\nNOTE: Images of the Share target can only be selected in the PreviewImage, SaveImage, and VHS_VideoCombine nodes. In the case of VHS_VideoCombine, only the image/gif and image/webp formats are supported."); + } + return false; + } + ShareDialog.instance.show({ potential_outputs, potential_output_nodes, share_option }); + return true; + }); } export class ShareDialogChooser extends ComfyDialog { @@ -215,9 +218,10 @@ export class ShareDialogChooser extends ComfyDialog { textContent: "Matrix Server", website: "https://app.element.io/#/room/%23comfyui_space%3Amatrix.org", description: "Share your art on the official ComfyUI matrix server", - onclick: () => { - showShareDialog(); - this.close(); + onclick: async () => { + showShareDialog('matrix').then((suc) => { + suc && this.close(); + }) } }, { @@ -226,8 +230,9 @@ export class ShareDialogChooser extends ComfyDialog { website: "https://comfyworkflows.com", description: "Share ComfyUI art on comfyworkflows.com", onclick: () => { - showShareDialog(); - this.close(); + showShareDialog('comfyworkflows').then((suc) => { + suc && this.close(); + }) } }, ]; @@ -370,8 +375,9 @@ export class ShareDialog extends ComfyDialog { static matrix_auth = { homeserver: "matrix.org", username: "", password: "" }; static cw_sharekey = ""; - constructor() { + constructor(share_option) { super(); + this.share_option = share_option; this.element = $el("div.comfy-modal", { parent: document.body, style: { 'overflow-y': "auto", @@ -398,12 +404,12 @@ export class ShareDialog extends ComfyDialog { this.matrix_destination_checkbox = $el("input", { type: 'checkbox', id: "matrix_destination" }, []) const matrix_destination_checkbox_text = $el("label", {}, [" ComfyUI Matrix server"]) this.matrix_destination_checkbox.style.color = "var(--fg-color)"; - this.matrix_destination_checkbox.checked = localStorage.getItem("share_option") === 'matrix'; //true; + this.matrix_destination_checkbox.checked = this.share_option === 'matrix'; //true; this.comfyworkflows_destination_checkbox = $el("input", { type: 'checkbox', id: "comfyworkflows_destination" }, []) const comfyworkflows_destination_checkbox_text = $el("label", {}, [" ComfyWorkflows.com"]) this.comfyworkflows_destination_checkbox.style.color = "var(--fg-color)"; - this.comfyworkflows_destination_checkbox.checked = localStorage.getItem("share_option") !== 'matrix'; + this.comfyworkflows_destination_checkbox.checked = this.share_option !== 'matrix'; this.matrix_homeserver_input = $el("input", { type: 'text', id: "matrix_homeserver", placeholder: "matrix.org", value: ShareDialog.matrix_auth.homeserver || 'matrix.org' }, []); this.matrix_username_input = $el("input", { type: 'text', placeholder: "Username", value: ShareDialog.matrix_auth.username || '' }, []); @@ -460,8 +466,8 @@ export class ShareDialog extends ComfyDialog { textContent: "Close", onclick: () => { // Reset state - this.matrix_destination_checkbox.checked = localStorage.getItem("share_option") === 'matrix'; - this.comfyworkflows_destination_checkbox.checked = localStorage.getItem("share_option") !== 'matrix'; + this.matrix_destination_checkbox.checked = this.share_option === 'matrix'; + this.comfyworkflows_destination_checkbox.checked = this.share_option !== 'matrix'; this.share_button.textContent = "Share"; this.share_button.style.display = "inline-block"; this.final_message.innerHTML = ""; @@ -632,8 +638,8 @@ export class ShareDialog extends ComfyDialog { textContent: "Close", onclick: () => { // Reset state - this.matrix_destination_checkbox.checked = localStorage.getItem("share_option") === 'matrix'; - this.comfyworkflows_destination_checkbox.checked = localStorage.getItem("share_option") !== 'matrix'; + this.matrix_destination_checkbox.checked = this.share_option === 'matrix'; + this.comfyworkflows_destination_checkbox.checked = this.share_option !== 'matrix'; this.share_button.textContent = "Share"; this.share_button.style.display = "inline-block"; this.final_message.innerHTML = ""; @@ -825,7 +831,7 @@ export class ShareDialog extends ComfyDialog { return res; } - show({ potential_outputs, potential_output_nodes }) { + show({ potential_outputs, potential_output_nodes, share_option }) { // console.log({ potential_outputs, potential_output_nodes }) this.radio_buttons.innerHTML = ""; // clear the radio buttons const new_radio_buttons = $el("div", { @@ -900,5 +906,14 @@ export class ShareDialog extends ComfyDialog { // this.radio_buttons.appendChild(subheader); this.radio_buttons.appendChild(new_radio_buttons); this.element.style.display = "block"; + + share_option = share_option || this.share_option; + if (share_option === 'comfyworkflows') { + this.matrix_destination_checkbox.checked = false; + this.comfyworkflows_destination_checkbox.checked = true; + } else { + this.matrix_destination_checkbox.checked = true; + this.comfyworkflows_destination_checkbox.checked = false; + } } } diff --git a/js/comfyui-share-openart.js b/js/comfyui-share-openart.js index a52c7678..d0b7337e 100644 --- a/js/comfyui-share-openart.js +++ b/js/comfyui-share-openart.js @@ -40,12 +40,35 @@ export class OpenArtShareDialog extends ComfyDialog { this.selectedOutputIndex = 0; this.uploadedImages = []; } - readKeyFromLocalStorage() { - return localStorage.getItem(LOCAL_STORAGE_KEY) || ""; + + async readKey() { + let key = "" + try { + // console.log("Fetching openart key") + key = await api.fetchApi(`/manager/get_openart_auth`) + .then(response => response.json()) + .then(data => { + return data.openart_key; + }) + .catch(error => { + // console.log(error); + }); + } catch (error) { + // console.log(error); + } + return key || ""; } - saveKeyToLocalStorage(value) { - localStorage.setItem(LOCAL_STORAGE_KEY, value); + + async saveKey(value) { + await api.fetchApi(`/manager/set_openart_auth`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + openart_key: value + }) + }); } + createButtons() { const sectionStyle = { marginBottom: "10px", @@ -242,6 +265,7 @@ export class OpenArtShareDialog extends ComfyDialog { return layout; } + async fetchApi(path, options, statusText) { if (statusText) { this.message.textContent = statusText; @@ -275,6 +299,7 @@ export class OpenArtShareDialog extends ComfyDialog { data, }; } + async uploadThumbnail(uploadFile) { const form = new FormData(); form.append("file", uploadFile); @@ -304,9 +329,10 @@ export class OpenArtShareDialog extends ComfyDialog { } } } + async handleShareButtonClick() { this.message.textContent = ""; - this.saveKeyToLocalStorage(this.keyInput.value); + await this.saveKey(this.keyInput.value); try { this.shareButton.disabled = true; this.shareButton.textContent = "Sharing..."; @@ -317,6 +343,7 @@ export class OpenArtShareDialog extends ComfyDialog { this.shareButton.disabled = false; this.shareButton.textContent = "Share"; } + async share() { const prompt = await app.graphToPrompt(); const workflowJSON = prompt["workflow"]; @@ -382,10 +409,10 @@ export class OpenArtShareDialog extends ComfyDialog { throw new Error("Error sharing workflow: " + e.message); } } - show({ potential_outputs, potential_output_nodes } = {}) { + + async show({ potential_outputs, potential_output_nodes } = {}) { this.element.style.display = "block"; - // read key from local storage and set it to the input - const key = this.readKeyFromLocalStorage(); + const key = await this.readKey(); this.keyInput.value = key; } }