Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 160 additions & 73 deletions glob/cnr_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import time
from dataclasses import dataclass
from typing import List
from urllib.parse import urlencode

import logging
import manager_core
import manager_util
import requests
Expand All @@ -18,108 +20,193 @@

is_cache_loading = False

async def get_cnr_data(cache_mode=True, dont_wait=True):
async def get_cnr_data(sync_mode=None, dont_wait=True, **kwargs):
# For backwards compatibility with keyword argument cache_mode
if sync_mode is None:
sync_mode = kwargs.get('cache_mode', 'cache')
try:
return await _get_cnr_data(cache_mode, dont_wait)
return await _get_cnr_data(sync_mode, dont_wait)
except asyncio.TimeoutError:
print("A timeout occurred during the fetch process from ComfyRegistry.")
return await _get_cnr_data(cache_mode=True, dont_wait=True) # timeout fallback
logging.error(f"[ComfyUI-Manager] A timeout occurred during the fetch process from ComfyRegistry.")
return await _get_cnr_data(sync_mode='cache', dont_wait=True) # timeout fallback

async def _get_cnr_data(cache_mode=True, dont_wait=True):
def get_comfyui_ver():
is_desktop = bool(os.environ.get('__COMFYUI_DESKTOP_VERSION__'))
if is_desktop:
return manager_core.get_current_comfyui_ver() or 'unknown'
else:
return manager_core.get_comfyui_tag() or 'unknown'


def get_form_factor():
is_desktop = bool(os.environ.get('__COMFYUI_DESKTOP_VERSION__'))
system = platform.system().lower()
is_windows = system == 'windows'
is_mac = system == 'darwin'
is_linux = system == 'linux'

if is_desktop:
if is_windows:
return 'desktop-win'
elif is_mac:
return 'desktop-mac'
else:
return 'other'
else:
if is_windows:
return 'git-windows'
elif is_mac:
return 'git-mac'
elif is_linux:
return 'git-linux'
else:
return 'other'


def get_node_timestamp(node):
latest_ver = node.get('latest_version')
if isinstance(latest_ver, dict):
t = latest_ver.get('createdAt')
if t:
return t
return node.get('created_at')


async def _get_cnr_data(sync_mode=None, dont_wait=True, **kwargs):
global is_cache_loading

uri = f'{base_url}/nodes'
# For backwards compatibility with keyword argument cache_mode
if sync_mode is None:
sync_mode = kwargs.get('cache_mode', 'cache')

# Normalize sync_mode for backwards
# True | 'cache' | 'local' -> cache (just use cache)
# 'force' -> force (invalidate and create new cache)
# 'remote' -> remote (update comfyui remote with local cache)
if sync_mode is True or sync_mode == 'cache' or sync_mode == 'local':
normalized_mode = 'cache'
elif sync_mode == 'force':
normalized_mode = 'force'
else:
normalized_mode = 'remote'

async def fetch_all():
remained = True
page = 1
uri = f'{base_url}/nodes'
cache_path = manager_util.get_cache_path(uri)

full_nodes = {}
comfyui_ver = get_comfyui_ver()
form_factor = get_form_factor()


# Determine form factor based on environment and platform
is_desktop = bool(os.environ.get('__COMFYUI_DESKTOP_VERSION__'))
system = platform.system().lower()
is_windows = system == 'windows'
is_mac = system == 'darwin'
is_linux = system == 'linux'
cached_data = None
last_updated = None
full_nodes = {}

# Get ComfyUI version tag
if is_desktop:
# extract version from pyproject.toml instead of git tag
comfyui_ver = manager_core.get_current_comfyui_ver() or 'unknown'
else:
comfyui_ver = manager_core.get_comfyui_tag() or 'unknown'
if normalized_mode != 'force' and manager_util.get_cache_state(uri, expired_days=None) == 'cached':
try:
with open(cache_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
cached_data = json.load(json_file)

if is_desktop:
if is_windows:
form_factor = 'desktop-win'
elif is_mac:
form_factor = 'desktop-mac'
else:
form_factor = 'other'
else:
if is_windows:
form_factor = 'git-windows'
elif is_mac:
form_factor = 'git-mac'
elif is_linux:
form_factor = 'git-linux'
if (cached_data.get('comfyui_ver') == comfyui_ver and
cached_data.get('form_factor') == form_factor):
last_updated = cached_data.get('last_updated')
for node in cached_data.get('nodes', []):
full_nodes[node['id']] = node
else:
form_factor = 'other'

logging.info("[ComfyUI-Manager] Environment change detected. Invalidating local ComfyRegistry cache.")
cached_data = None
full_nodes = {}
last_updated = None
except Exception as e:
logging.error(f"[ComfyUI-Manager] Failed to read cached data: {e}")
cached_data = None
full_nodes = {}
last_updated = None

if normalized_mode == 'cache':
is_cache_loading = True

if dont_wait:
if cached_data is not None:
return cached_data.get('nodes', [])
return []

if cached_data is not None and manager_util.get_cache_state(uri, expired_days=1) == 'cached':
return cached_data.get('nodes', [])

async def fetch_all(timestamp_filter, existing_nodes):
remained = True
page = 1
nodes_map = dict(existing_nodes)

while remained:
# Add comfyui_version and form_factor to the API request
sub_uri = f'{base_url}/nodes?page={page}&limit=30&comfyui_version={comfyui_ver}&form_factor={form_factor}'
sub_json_obj = await asyncio.wait_for(manager_util.get_data_with_cache(sub_uri, cache_mode=False, silent=True, dont_cache=True), timeout=30)
params = {
'page': page,
'limit': 30,
'comfyui_version': comfyui_ver,
'form_factor': form_factor,
}
if timestamp_filter:
params['timestamp'] = timestamp_filter
sub_uri = f'{base_url}/nodes?{urlencode(params)}'

sub_json_obj = await asyncio.wait_for(
manager_util.get_data_with_cache(sub_uri, cache_mode=False, silent=True, dont_cache=True),
timeout=30
)
remained = page < sub_json_obj['totalPages']

for x in sub_json_obj['nodes']:
full_nodes[x['id']] = x
nodes_map[x['id']] = x

if page % 5 == 0:
print(f"FETCH ComfyRegistry Data: {page}/{sub_json_obj['totalPages']}")
logging.info(f"[ComfyUI-Manager] FETCH ComfyRegistry Data: {page}/{sub_json_obj['totalPages']}")

page += 1
time.sleep(0.5)
await asyncio.sleep(0.5)

print("FETCH ComfyRegistry Data [DONE]")
logging.info(f"[ComfyUI-Manager] FETCH ComfyRegistry Data [DONE]")

for v in full_nodes.values():
for v in nodes_map.values():
if 'latest_version' not in v:
v['latest_version'] = dict(version='nightly')

return {'nodes': list(full_nodes.values())}

if cache_mode:
is_cache_loading = True
cache_state = manager_util.get_cache_state(uri)

if dont_wait:
if cache_state == 'not-cached':
return {}
else:
print("[ComfyUI-Manager] The ComfyRegistry cache update is still in progress, so an outdated cache is being used.")
with open(manager_util.get_cache_path(uri), 'r', encoding="UTF-8", errors="ignore") as json_file:
return json.load(json_file)['nodes']

if cache_state == 'cached':
with open(manager_util.get_cache_path(uri), 'r', encoding="UTF-8", errors="ignore") as json_file:
return json.load(json_file)['nodes']
return {'nodes': list(nodes_map.values())}

try:
json_obj = await fetch_all()
manager_util.save_to_cache(uri, json_obj)
from datetime import datetime, timezone, timedelta
json_obj = await fetch_all(last_updated, full_nodes)

# Set cache's timestamp as the maximum timestamp from fetched nodes.
# This way, in the next run, only the latest updates will be fetched.
timestamps = [get_node_timestamp(node) for node in json_obj['nodes'] if get_node_timestamp(node)]
max_timestamp = max(timestamps) if timestamps else None

if max_timestamp:
try:
ts_str = max_timestamp.replace('Z', '+00:00')
dt = datetime.fromisoformat(ts_str) - timedelta(seconds=10)
new_timestamp = dt.strftime("%Y-%m-%dT%H:%M:%SZ")
except Exception:
new_timestamp = max_timestamp
else:
new_timestamp = last_updated if last_updated else datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")

cache_to_save = {
'nodes': json_obj['nodes'],
'comfyui_ver': comfyui_ver,
'form_factor': form_factor,
'last_updated': new_timestamp
}
manager_util.save_to_cache(uri, cache_to_save)
return json_obj['nodes']
except:
res = {}
print("Cannot connect to comfyregistry.")
except Exception as e:
logging.error(f"[ComfyUI-Manager] Cannot connect to comfyregistry or failed sync: {e}")
if cached_data is not None:
return cached_data.get('nodes', [])
return []
finally:
if cache_mode:
if normalized_mode == 'cache':
is_cache_loading = False

return res


@dataclass
class NodeVersion:
Expand Down Expand Up @@ -237,7 +324,7 @@ def generate_cnr_id(fullpath, cnr_id):
with open(cnr_id_path, "w") as f:
return f.write(cnr_id)
except:
print(f"[ComfyUI Manager] unable to create file: {cnr_id_path}")
logging.error(f"[ComfyUI-Manager] unable to create file: {cnr_id_path}")


def read_cnr_id(fullpath):
Expand Down
2 changes: 1 addition & 1 deletion glob/manager_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,7 @@ async def reload(self, cache_mode, dont_wait=True):
dont_wait = True

# reload 'cnr_map' and 'repo_cnr_map'
cnrs = await cnr_utils.get_cnr_data(cache_mode=cache_mode=='cache', dont_wait=dont_wait)
cnrs = await cnr_utils.get_cnr_data(sync_mode=cache_mode, dont_wait=dont_wait)

for x in cnrs:
self.cnr_map[x['id']] = x
Expand Down
12 changes: 8 additions & 4 deletions glob/manager_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,17 +166,21 @@ def simple_hash(input_string):

return hash_value

def is_file_created_within_days(file_path, days):
if days is None:
return True

def is_file_created_within_one_day(file_path):
if not os.path.exists(file_path):
return False

file_creation_time = os.path.getctime(file_path)
current_time = datetime.now().timestamp()
time_difference = current_time - file_creation_time

return time_difference <= 86400
return time_difference <= (days * 86400)

def is_file_created_within_one_day(file_path):
return is_file_created_within_days(file_path, 1)

async def get_data(uri, silent=False):
if not silent:
Expand Down Expand Up @@ -214,12 +218,12 @@ def get_cache_path(uri):
return os.path.join(cache_dir, cache_uri+'.json')


def get_cache_state(uri):
def get_cache_state(uri, expired_days=1):
cache_uri = get_cache_path(uri)

if not os.path.exists(cache_uri):
return "not-cached"
elif is_file_created_within_one_day(cache_uri):
elif is_file_created_within_days(cache_uri, expired_days):
return "cached"

return "expired"
Expand Down