diff --git a/endpoint.py b/endpoint.py index 8c968f7..7f600c4 100644 --- a/endpoint.py +++ b/endpoint.py @@ -66,12 +66,23 @@ def ACTIONS_installDependency(dependency_names: list[str] | None = None): # break +def ACTIONS_getUserImageFolders(): + input_dir = Path(folder_paths.get_input_directory()) + output_dir = Path(folder_paths.get_output_directory()) + + input_subdirs = [x.name for x in input_dir.iterdir() if x.is_dir()] + output_subdirs = [x.name for x in output_dir.iterdir() if x.is_dir()] + + return {"input": input_subdirs, "output": output_subdirs} + + def ACTIONS_getUserImages( mode: Literal["input", "output"], count=1000, offset=0, sort: str | None = None, include_subfolders: bool = False, + subfolder=None, ): # enabled = "MTB_EXPOSE" in os.environ # if not enabled: @@ -84,6 +95,13 @@ def ACTIONS_getUserImages( output_dir = Path(folder_paths.get_output_directory()) entry_dir = input_dir if mode == "input" else output_dir + if subfolder: + entry_dir = entry_dir / subfolder + + if not entry_dir.exists(): + return { + "error": f"Subfolder {entry_dir.name} doesn't exists in {entry_dir.parent.as_posix()}" + } supported = ["png", "jpg", "jpeg", "webp", "gif"] entries = {} @@ -105,7 +123,7 @@ def ACTIONS_getUserImages( imgs = { img.name: ( - f"/mtb/view?filename={img.name}&width=512&type={mode}&subfolder=" + f"/mtb/view?filename={img.name}&width=512&type={mode}&subfolder={subfolder or ''}" f"{img.parent.relative_to(entry_dir) if include_subfolders else ''}" f"&preview=&rand={secrets.randbelow(424242)}" ) diff --git a/web/comfy_shared.js b/web/comfy_shared.js index 94b50f7..dd49144 100644 --- a/web/comfy_shared.js +++ b/web/comfy_shared.js @@ -1,10 +1,9 @@ /** + * @module Shared utilities * File: comfy_shared.js * Project: comfy_mtb * Author: Mel Massadian - * * Copyright (c) 2023-2024 Mel Massadian - * */ // Reference the shared typedefs file @@ -1095,7 +1094,33 @@ export const addDeprecation = (nodeType, reason) => { // #endregion -// #region API / graph utilities +// #region Actions API +export const runAction = async (name, ...args) => { + const req = await api.fetchApi('/mtb/actions', { + method: 'POST', + body: JSON.stringify({ + name, + args, + }), + }) + + const res = await req.json() + return res.result +} +export const getServerInfo = async () => { + const res = await api.fetchApi('/mtb/server-info') + return await res.json() +} +export const setServerInfo = async (opts) => { + await api.fetchApi('/mtb/server-info', { + method: 'POST', + body: JSON.stringify(opts), + }) +} + +// #endregion + +// #region Authoring API / graph utilities export const getAPIInputs = () => { const inputs = {} let counter = 1 @@ -1148,3 +1173,4 @@ export const getNodes = (skip_unused) => { } return nodes } +// #endregion diff --git a/web/mtb_input_output_sidebar.js b/web/mtb_input_output_sidebar.js index e23577c..8599087 100644 --- a/web/mtb_input_output_sidebar.js +++ b/web/mtb_input_output_sidebar.js @@ -1,7 +1,7 @@ import { app } from '../../scripts/app.js' import { api } from '../../scripts/api.js' -// import * as shared from './comfy_shared.js' +import * as shared from './comfy_shared.js' import { // defineCSSClass, @@ -15,6 +15,8 @@ import { const offset = 0 let currentWidth = 200 let currentMode = 'input' +let modes = [] +let subfolder = '' let currentSort = 'None' const IMAGE_NODES = ['LoadImage'] @@ -76,19 +78,23 @@ const getImgsFromUrls = (urls, target) => { return imgs } -const getUrls = async () => { +const getModes = async () => { + const inputs = await shared.runAction('getUserImageFolders') + return inputs +} +const getUrls = async (subfolder) => { const count = (await api.getSetting('mtb.io-sidebar.count')) || 1000 console.log('Sidebar count', count) - const inputs = await api.fetchApi('/mtb/actions', { - method: 'POST', - body: JSON.stringify({ - name: 'getUserImages', - // mode, count, offset - args: [currentMode, count, offset, currentSort], - }), - }) - const output = await inputs.json() - return output?.result || {} + const output = await shared.runAction( + 'getUserImages', + currentMode, + count, + offset, + currentSort, + false, + subfolder, + ) + return output || {} } //NOTE: do not load if using the old ui @@ -187,21 +193,39 @@ if (window?.__COMFYUI_FRONTEND_VERSION__) { el.parentNode.style.overflowY = 'clip' } - const urls = await getUrls(currentMode) + const allModes = await getModes() + const input_modes = allModes.input.map((m) => `input - ${m}`) + const output_modes = allModes.output.map((m) => `output - ${m}`) + const urls = await getUrls() let imgs = {} const cont = makeElement('div.mtb_sidebar') const imgGrid = makeElement('div.mtb_img_grid') - const selector = makeSelect(['input', 'output'], currentMode) + const selector = makeSelect( + ['input', 'output', ...output_modes, ...input_modes], + currentMode, + ) selector.addEventListener('change', async (e) => { - const newMode = e.target.value - const changed = newMode !== currentMode + let newMode = e.target.value + let changed = false + let newSub = '' + if (newMode !== 'input' || newMode !== 'output') { + if (newMode.startsWith('input - ')) { + newSub = newMode.replace('input - ', '') + newMode = 'input' + } else { + newSub = newMode.replace('output - ', '') + newMode = 'output' + } + } + changed = newMode !== currentMode || newSub !== subfolder currentMode = newMode + subfolder = newSub if (changed) { imgGrid.innerHTML = '' - const urls = await getUrls() + const urls = await getUrls(subfolder) if (urls) { imgs = getImgsFromUrls(urls, imgGrid) } @@ -220,7 +244,7 @@ if (window?.__COMFYUI_FRONTEND_VERSION__) { currentSort = newSort if (changed) { imgGrid.innerHTML = '' - const urls = await getUrls() + const urls = await getUrls(subfolder) if (urls) { imgs = getImgsFromUrls(urls, imgGrid) } @@ -229,7 +253,6 @@ if (window?.__COMFYUI_FRONTEND_VERSION__) { const sizeSlider = makeSlider(64, 1024, currentWidth, 1) imgTools.appendChild(orderSelect) - imgTools.appendChild(sizeSlider) imgs = getImgsFromUrls(urls, imgGrid) diff --git a/web/mtb_widgets.js b/web/mtb_widgets.js index 2a67f09..18d58ec 100644 --- a/web/mtb_widgets.js +++ b/web/mtb_widgets.js @@ -672,8 +672,7 @@ const mtb_widgets = { init: async () => { infoLogger('Registering mtb.widgets') try { - const res = await api.fetchApi('/mtb/server-info') - const msg = await res.json() + const msg = await shared.getServerInfo() if (!window.MTB) { window.MTB = {} } @@ -716,17 +715,11 @@ const mtb_widgets = { infoLogger('Enabled DEBUG mode') } - await api - .fetchApi('/mtb/server-info', { - method: 'POST', - body: JSON.stringify({ - debug: value, - }), - }) - .then((_response) => {}) - .catch((error) => { - console.error('Error:', error) - }) + try { + shared.setServerInfo({ debug: value }) + } catch (err) { + console.error('Error:', err) + } }, }) }, @@ -1109,13 +1102,10 @@ const mtb_widgets = { const getStyle = async (node) => { try { - const getStyles = await api.fetchApi('/mtb/actions', { - method: 'POST', - body: JSON.stringify({ - name: 'getStyles', - args: node.widgets?.[0].value ? node.widgets[0].value : '', - }), - }) + const getStyles = await runAction( + 'getStyles', + node.widgets?.[0].value ? node.widgets[0].value : '', + ) const output = await getStyles.json() return output?.result