This test requires real PyTorch for tensor iteration which isn't available
in CI due to module mocking. The test runs correctly in local dev with torch.
- test_media.py: Use Python math.log2 instead of numpy to avoid mock issues
- test_image_node_sanitization.py: Patch tensor_to_numpy_uint8 and Image.fromarray
to properly handle mocked torch tensors
- Add numpy to requirements-nodes.txt
- Create conftest.py for test configuration
- Fix test_numpy_subprocess.py to import real numpy before any mocking
- Fix test_media.py to import real numpy early
- Fix test_image_node_sanitization.py to use MockTensor instead of torch.zeros
- Added `sanitize_token_from_text` helper to `shared/discord/webhook_client.py`.
- Updated `DiscordWebhookClient._send_with_retry` to sanitize `response.text` before returning it in error details.
- Updated `nodes/image_node.py` and `nodes/video_node.py` to sanitize `response.text` before printing error messages.
- Added regression test `tests/test_webhook_security.py`.
This prevents sensitive Discord webhook tokens from being leaked in ComfyUI console logs when the Discord API returns an error (e.g. 400 Bad Request) that echoes the request URL.
- Implements `process_batched_images` generator in `nodes/video_node.py` to process video frames in batches (default 20), significantly reducing GPU-CPU synchronization overhead.
- Optimizes `tensor_to_numpy_uint8` in `shared/media/image_processing.py` to use in-place operations (`.clamp_()`), saving memory allocations for large tensors.
- Reduces performance bottlenecks in video encoding pipelines.
- Updated `resize_to_power_of_2` tooltip to mention it uses the selected `resize_method`.
- Updated `resize_method` tooltip to explicitly state it is only used when `resize_to_power_of_2` is enabled.
- Added UX learning to `.Jules/palette.md`.
This prevents user confusion regarding why the resize method selection might not seem to affect standard outputs.
- Remove unused imports from nodes (time, torch, requests, itertools)
- Move uuid import to top-level in bot/bot.py
- Add 4 new test files with 86 tests for shared utilities
- Update CHANGELOG.md with version 2.0.0 refactoring summary
- Update REFACTOR_PRD.md to mark Phase 5 complete
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add WebSocket reconnection with exponential backoff (1s-60s, ±10% jitter)
- Add error delivery to notify users when jobs fail
- Create templates cog with /template save/load/list/delete commands
- Create history cog with /history (paginated) and /rerun commands
- Fix BotConfig import in bot.py
- Enable templates and history cogs in bot loader
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The build_metadata_section function used "\n".join(metadata_lines)
which lacks a trailing newline. When dimensions were appended later,
they concatenated directly without proper spacing, producing malformed
output like "**Format:** PNG**Original Dimensions:** 1024x1024".
Added trailing newline so subsequent content appears on a new line.
Fixes issue identified in PR #35 review.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The refactored code checked only `if save_cdn_urls:` instead of the
original `if save_cdn_urls and response.status_code == 200:`. While
extract_cdn_urls_from_response correctly returns empty for 204 responses,
accumulated URLs from prior iterations could still trigger the
`if batch_cdn_urls:` condition, causing duplicate sends.
Fixed in both individual image send and batch send sections.
Fixes issue identified in PR #35 review.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The refactored prompt extractor only detected CLIPTextEncode nodes,
losing support for SDXL-specific nodes that was present in the original
code. This caused SDXL workflows using SDXLPromptEncoder or SDXLTextEncode
nodes to not have their prompts included in Discord messages.
Changes:
- Add PROMPT_NODE_TYPES list with CLIPTextEncode, SDXLPromptEncoder,
and SDXLTextEncode
- Rename functions from clip-specific to generic prompt node naming
- Add fallback detection for custom text/encode/prompt nodes
- Update docstrings to reflect broader node type support
Fixes regression identified in PR #35 review.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When add_dimensions or resize_to_power_of_2 was enabled but add_date,
add_time, and include_format_in_message were all disabled, dimensions
would be appended to the Discord message without the "Image Information:"
section header.
Now checks if dimensions will be added but no other metadata exists,
and adds the section header in that case.
Fixes issue identified in PR #35 review.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The else block for error handling was incorrectly associated with the
`if save_cdn_urls:` condition instead of the `if response.status_code
in [200, 204]:` check. This caused discord_send_success to always be
set to False after a successful batch send, regardless of the actual
outcome.
Fixes control flow bug identified in PR #35 review.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create nodes/base_node.py with BaseDiscordNode class
- Provide common INPUT_TYPES generators (discord, cdn, filename)
- Extract shared methods:
- sanitize_workflow_data()
- build_filename_prefix()
- get_dest_folder()
- extract_workflow_from_metadata()
- build_prompt_message()
- send_discord_files()
- send_cdn_urls_to_discord()
- update_github_cdn()
The base class is ready for nodes to inherit from, enabling
gradual adoption of shared functionality without disrupting
existing node implementations.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create shared/media/format_utils.py with format detection and validation
- Create shared/media/video_encoder.py with FFmpegEncoder and PILEncoder classes
- Extract validate_video_for_discord to shared utility
- Extract ffmpeg detection to shared detect_ffmpeg() function
- Extract Discord video optimization to shared optimize_video_for_discord()
- Remove duplicate code from video_node.py (-109 lines)
Phase 3 summary:
- video_node.py: 1201 -> 1092 lines (9% reduction this phase)
- Total reduction from original: 1562 -> 1092 lines (30% reduction)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create shared/filename_utils.py for date/time/dimension filename building
- Create shared/path_utils.py for output directory handling
- Create shared/discord/message_builder.py for Discord message construction
- Create shared/discord/cdn_extractor.py for CDN URL extraction
- Refactor image_node.py to use shared utilities (-161 lines, 16.3%)
- Refactor video_node.py to use shared utilities (-361 lines, 23.1%)
- Fix setup_logging missing from logging_config.py
- Fix test imports to use new module paths (nodes.* instead of discord_*_node)
- Total reduction: 522 lines (20.5%), exceeding PRD target of ~200 lines
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Updated `tests/test_image_node_sanitization.py` to include `github_token` in test inputs.
- Verified test now correctly covers github token sanitization.
- Updated `tests/test_image_node_sanitization.py` to avoid bare `try...except` that swallowed `AssertionError`.
- Verified the test fails correctly when sanitization is broken.
I removed a try-except block that attempted to import non-existent variables (`ffmpeg_path`, `get_audio`, etc.) from `discordsend_utils`. These variables were not used in the codebase and were causing import errors or false positives in static analysis.
- Replaced the failing import block with explicit definitions for `ffmpeg_path`, `ENCODE_ARGS`, and `has_vhs_formats`.
- Imported `ProgressBar` from `comfy.utils` with a fallback for standalone usage.
- Removed unused variables `floatOrInt`, `imageOrLatent`, and `BIGMAX`.
- Verified that the removed variables and functions are dead code.
- Removed redundant calls to `sanitize_json_for_export` in `discord_image_node.py` loop.
- Verified ~33ms performance gain per batch via benchmark.
- Added regression test `tests/test_image_node_sanitization.py`.
- Update `filename_prefix` tooltip to clarify `%batch_num%` usage.
- Update `github_repo` tooltip with an example format.
- Update `github_token` tooltip with navigation instructions to find the token.
- Standardize `webhook_url` tooltip across image and video nodes.
- Add `.jules/palette.md` for UX learnings.
Phase 0 of separation-of-concerns refactor:
- Fix BotConfig class name mismatch in bot/__main__.py
(was importing 'Config' but class is 'BotConfig')
- Add missing 'import json' to bot/services/delivery.py
(json.loads was called without import)
- Remove unused PermissionLevel import from bot/cogs/admin.py
- Fix config attribute path: config.comfyui.url (was config.comfyui_url)
Also adds REFACTOR_PRD.md to .gitignore for project planning docs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>