Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a4c564d
removing max frameRate fixes error on Safari (#8)
llandau Mar 17, 2025
2a89781
Combobox support for control panel (#18)
JJassonn69 Mar 19, 2025
119537f
fix: temporarily disable sam-2 node (#46)
eliteprox Mar 21, 2025
ac02e2e
fix comfystream preview js error (#47)
eliteprox Mar 21, 2025
1bfcfd8
workflow: Add workflow for testing/linting (#36)
hjpotter92 Mar 28, 2025
f3f5d1a
workflows: Build more of comfyui-base docker image (#66)
hjpotter92 Mar 28, 2025
670a997
add librosa, remove requirements.txt (#37)
eliteprox Mar 28, 2025
6755be2
changes the branch to master for ComfyUI-StreamPack (#67)
eliteprox Mar 29, 2025
79f6775
Revert "fix: temporarily disable sam-2 node (#46)" (#68)
eliteprox Mar 29, 2025
0c564ba
update ver to 0.0.5 (#69)
eliteprox Mar 29, 2025
1f837fa
feat(server): make ComfyUI log level configurable (#16)
rickstaa Apr 1, 2025
91d897c
chore(deps): bump next from 15.1.6 to 15.2.4 in /ui (#58)
dependabot[bot] Apr 1, 2025
d92a6f5
changes supporting ai-runner upgrade to comfystream 0.0.4 (#56)
eliteprox Apr 1, 2025
e53eed2
fix conda env activation (#78)
eliteprox Apr 2, 2025
a9080db
disable automatic env activation (#81)
eliteprox Apr 2, 2025
d7c096f
feat(models): add Depth Anything V2 Large model (#79)
rickstaa Apr 4, 2025
9ff4b39
Support for super-resolution node with opencv-cuda (#65)
JJassonn69 Apr 4, 2025
1375def
update script with with min/max width/height (#107)
pschroedl Apr 9, 2025
15ddda2
Feat/dynamic resolution (#38)
ryanontheinside Apr 10, 2025
dd0c709
don't use GPU for comfyui-base workflow (#110)
pwilczynskiclearcode Apr 11, 2025
4ac3649
update icon for comfyui registry (#115)
eliteprox Apr 11, 2025
e70b88c
update to ver 0.0.6 (#113)
eliteprox Apr 11, 2025
9ab0b0d
Revert "don't use GPU for comfyui-base workflow (#110)" (#117)
eliteprox Apr 11, 2025
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
17 changes: 15 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,19 @@ charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[.gitignore]
insert_final_newline = unset

[*.py]
indent_size = 4

[workflows/comfy*/*.json]
insert_final_newline = unset

[Dockerfile*]
indent_style = tab
indent_size = 8
indent_size = 4

[*.{md,txt,mkdn}]
indent_size = 4
indent_size = unset
trim_trailing_whitespace = false
2 changes: 2 additions & 0 deletions .github/codeql-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
paths-ignore:
- "docker/"
14 changes: 4 additions & 10 deletions .github/workflows/comfyui-base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,13 @@ name: Build and push comfyui-base docker image

on:
pull_request:
paths:
- docker/Dockerfile.base
- src/comfystream/scripts/
- configs/
- .github/workflows/comfyui-base.yaml
paths-ignore:
- "ui/*"
branches:
- main
push:
paths:
- docker/Dockerfile.base
- src/comfystream/scripts/
- configs/
- .github/workflows/comfyui-base.yaml
paths-ignore:
- "ui/*"
branches:
- main
tags:
Expand Down
86 changes: 86 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Test project

on:
pull_request:
branches:
- "main"
push:
branches:
- "main"

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
codeql:
name: Perform CodeQL analysis
if: ${{ github.repository == 'livepeer/comfystream' }}
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: typescript,javascript,python
config-file: ./.github/codeql-config.yaml

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3


editorconfig:
name: Run editorconfig checker
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
with:
# Check https://github.com/livepeer/go-livepeer/pull/1891
# for ref value discussion
ref: ${{ github.event.pull_request.head.sha }}

- name: Install editorconfig-checker
uses: editorconfig-checker/action-editorconfig-checker@main

- name: Run editorconfig checker against the repo
if: false
# disabled editorconfig lint rule for now
run: editorconfig-checker --format github-actions

test:
name: Run Tests
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
with:
# Check https://github.com/livepeer/go-livepeer/pull/1891
# for ref value discussion
ref: ${{ github.event.pull_request.head.sha }}

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: pip

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install '.[dev]'

- name: Run tests
run: pytest --cov --verbose --showlocals

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CI_CODECOV_TOKEN }}
name: ${{ github.event.repository.name }}
2 changes: 1 addition & 1 deletion configs/nodes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,5 @@ nodes:
comfyui-stream-pack:
name: "ComfyUI Stream Pack"
url: "https://github.com/livepeer/ComfyUI-Stream-Pack"
branch: "expose_feature_bank_to_comfyui"
branch: "main"
type: "utility"
15 changes: 0 additions & 15 deletions nodes/web/js/comfystream_ui_preview_node.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,25 +111,10 @@ app.registerExtension({
});
});

// Update iframe size
this.updateIframeSize();

return result;
};

// Override the resize method to allow both expanding and shrinking
nodeType.prototype.onResize = function(size) {
// Update the size
this.size[0] = size[0];
this.size[1] = size[1];

// Update the iframe size
this.updateIframeSize();

// Force canvas update
this.setDirtyCanvas(true, true);
};

// Add a helper method to update iframe size
nodeType.prototype.updateIframeSize = function() {
if (this.iframeWidget) {
Expand Down
7 changes: 4 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "comfystream"
description = "Build Live AI Video with ComfyUI"
version = "0.0.4"
version = "0.0.5"
license = { file = "LICENSE" }
dependencies = [
"asyncio",
Expand All @@ -15,10 +15,11 @@ dependencies = [
"toml",
"twilio",
"prometheus_client",
"librosa"
]

[project.optional-dependencies]
dev = ["pytest"]
dev = ["pytest", "pytest-cov"]

[project.urls]
repository = "https://github.com/yondonfu/comfystream"
Expand All @@ -33,4 +34,4 @@ package-dir = {"" = "src"}
packages = {find = {where = ["src", "nodes"]}}

[tool.setuptools.dynamic]
dependencies = {file = ["requirements.txt"]}
dependencies = {file = ["requirements.txt"]}
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ aiohttp
toml
twilio
prometheus_client
librosa
105 changes: 69 additions & 36 deletions src/comfystream/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ async def run_prompt(self, prompt_index: int):

async def cleanup(self):
async with self.cleanup_lock:
for task in self.running_prompts.values():
tasks_to_cancel = list(self.running_prompts.values())
for task in tasks_to_cancel:
task.cancel()
try:
await task
Expand All @@ -54,7 +55,11 @@ async def cleanup(self):
self.running_prompts.clear()

if self.comfy_client.is_running:
await self.comfy_client.__aexit__()
try:
await self.comfy_client.__aexit__()
except Exception as e:
logger.error(f"Error during ComfyClient cleanup: {e}")


await self.cleanup_queues()
logger.info("Client cleanup complete")
Expand Down Expand Up @@ -110,77 +115,105 @@ async def get_available_nodes(self):
for node_id, node in prompt.items()
}
nodes_info = {}

# Only process nodes until we've found all the ones we need
for class_type, node_class in nodes.NODE_CLASS_MAPPINGS.items():
if not remaining_nodes: # Exit early if we've found all needed nodes
break

if class_type not in needed_class_types:
continue

# Get metadata for this node type (same as original get_node_metadata)
input_data = node_class.INPUT_TYPES() if hasattr(node_class, 'INPUT_TYPES') else {}
input_info = {}

# Process required inputs
if 'required' in input_data:
for name, value in input_data['required'].items():
if isinstance(value, tuple) and len(value) == 2:
input_type, config = value
input_info[name] = {
'type': input_type,
'required': True,
'min': config.get('min', None),
'max': config.get('max', None),
'widget': config.get('widget', None)
}
if isinstance(value, tuple):
Copy link

Copilot AI Mar 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add explicit handling for tuple inputs with unexpected lengths to prevent silent failures or misconfiguration in input processing.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's track and address later since this is a standard release pr #193

if len(value) == 1 and isinstance(value[0], list):
# Handle combo box case where value is ([option1, option2, ...],)
input_info[name] = {
'type': 'combo',
'value': value[0], # The list of options becomes the value
}
elif len(value) == 2:
input_type, config = value
input_info[name] = {
'type': input_type,
'required': True,
'min': config.get('min', None),
'max': config.get('max', None),
'widget': config.get('widget', None)
}
elif len(value) == 1:
# Handle simple type case like ('IMAGE',)
input_info[name] = {
'type': value[0]
}
else:
logger.error(f"Unexpected structure for required input {name}: {value}")
# Process optional inputs

# Process optional inputs with same logic
if 'optional' in input_data:
for name, value in input_data['optional'].items():
if isinstance(value, tuple) and len(value) == 2:
input_type, config = value
input_info[name] = {
'type': input_type,
'required': False,
'min': config.get('min', None),
'max': config.get('max', None),
'widget': config.get('widget', None)
}
if isinstance(value, tuple):
if len(value) == 1 and isinstance(value[0], list):
# Handle combo box case where value is ([option1, option2, ...],)
input_info[name] = {
'type': 'combo',
'value': value[0], # The list of options becomes the value
}
elif len(value) == 2:
input_type, config = value
input_info[name] = {
'type': input_type,
'required': False,
'min': config.get('min', None),
'max': config.get('max', None),
'widget': config.get('widget', None)
}
elif len(value) == 1:
# Handle simple type case like ('IMAGE',)
input_info[name] = {
'type': value[0]
}
else:
logger.error(f"Unexpected structure for optional input {name}: {value}")

# Now process any nodes in our prompt that use this class_type
for node_id in list(remaining_nodes):
node = prompt[node_id]
if node.get('class_type') != class_type:
continue

node_info = {
'class_type': class_type,
'inputs': {}
}

if 'inputs' in node:
for input_name, input_value in node['inputs'].items():
input_metadata = input_info.get(input_name, {})
node_info['inputs'][input_name] = {
'value': input_value,
'type': input_info.get(input_name, {}).get('type', 'unknown'),
'min': input_info.get(input_name, {}).get('min', None),
'max': input_info.get(input_name, {}).get('max', None),
'widget': input_info.get(input_name, {}).get('widget', None)
'type': input_metadata.get('type', 'unknown'),
'min': input_metadata.get('min', None),
'max': input_metadata.get('max', None),
'widget': input_metadata.get('widget', None)
}

# For combo type inputs, include the list of options
if input_metadata.get('type') == 'combo':
node_info['inputs'][input_name]['value'] = input_metadata.get('value', [])

nodes_info[node_id] = node_info
remaining_nodes.remove(node_id)

all_prompts_nodes_info[prompt_index] = nodes_info

return all_prompts_nodes_info

except Exception as e:
logger.error(f"Error getting node info: {str(e)}")
return {}
4 changes: 2 additions & 2 deletions ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ui",
"version": "0.0.4",
"version": "0.0.5",
"private": true,
"scripts": {
"dev": "cross-env NEXT_PUBLIC_DEV=true next dev",
Expand Down
Loading