init
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [1.0.0] - 2025-01-15
|
||||
|
||||
### Added
|
||||
- Initial release of AVIF Animation node for ComfyUI
|
||||
- Full transparency (alpha channel) support
|
||||
- Adjustable FPS, quality, alpha quality, and encoding speed
|
||||
- Automatic transparency channel detection and addition
|
||||
- Multi-threaded image processing
|
||||
- Memory optimization with automatic cleanup
|
||||
- Comprehensive error handling and user feedback
|
||||
- Support for infinite loop playback
|
||||
|
||||
### Features
|
||||
- Uses libavif's avifenc for optimal AVIF encoding
|
||||
- YUV444 color format support
|
||||
- Non-premultiplied alpha channel
|
||||
- Automatic avifenc installation check
|
||||
- Detailed console output for debugging
|
||||
- File size reporting after encoding
|
||||
- Transparency verification after encoding
|
||||
|
||||
### Documentation
|
||||
- Comprehensive README with usage examples
|
||||
- Installation guide for Windows, macOS, and Linux
|
||||
- Format comparison table
|
||||
- Troubleshooting section
|
||||
- Example workflow JSON
|
||||
- MIT License
|
||||
|
||||
### Compatibility
|
||||
- ComfyUI Stable and Daily versions
|
||||
- Windows 10/11
|
||||
- macOS 12+ (Monterey)
|
||||
- Linux (Ubuntu 20.04+, Fedora 35+)
|
||||
- iOS 16+ and macOS Ventura+ for AVIF playback
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Planned
|
||||
- GPU acceleration support (if available in future avifenc versions)
|
||||
- Batch processing for multiple animations
|
||||
- Custom output directory option
|
||||
- Preview functionality within ComfyUI
|
||||
- Additional AVIF encoding parameters
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
# Installation Guide
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Install avifenc (Automatic)
|
||||
|
||||
**Run the installer:**
|
||||
```bash
|
||||
cd ComfyUI-AVIF-Animation
|
||||
python install_avifenc.py
|
||||
```
|
||||
|
||||
This will automatically:
|
||||
- Detect your platform (Windows/macOS/Linux)
|
||||
- Download avifenc from GitHub releases
|
||||
- Install it to Python's Scripts directory
|
||||
- Set executable permissions
|
||||
|
||||
**No need to:**
|
||||
- ❌ Manually download avifenc
|
||||
- ❌ Modify PATH or system variables
|
||||
- ❌ Use package managers (winget/brew/apt)
|
||||
|
||||
### 2. Install ComfyUI Node
|
||||
|
||||
**Option A: Clone from Git**
|
||||
```bash
|
||||
cd ComfyUI/custom_nodes
|
||||
git clone https://github.com/yourusername/ComfyUI-AVIF-Animation.git
|
||||
```
|
||||
|
||||
**Option B: Download ZIP**
|
||||
1. Download ZIP file from GitHub
|
||||
2. Extract to `ComfyUI/custom_nodes/ComfyUI-AVIF-Animation/`
|
||||
3. Restart ComfyUI
|
||||
|
||||
### 3. Verify Installation
|
||||
|
||||
1. Start ComfyUI
|
||||
2. Check if "AVIF Animation (avifenc)" node appears in node menu
|
||||
3. Test with a simple workflow
|
||||
|
||||
### 4. Verify avifenc Installation
|
||||
|
||||
```bash
|
||||
python -c "import sys; import os; print(os.path.join(os.path.join(sys.prefix, 'Scripts' if os.name == 'nt' else 'bin'), 'avifenc'))"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### avifenc not found
|
||||
|
||||
**Symptom:** Node shows "avifenc未安装" error
|
||||
|
||||
**Solution:**
|
||||
1. Run the installer:
|
||||
```bash
|
||||
cd ComfyUI-AVIF-Animation
|
||||
python install_avifenc.py
|
||||
```
|
||||
2. Verify avifenc is installed:
|
||||
```bash
|
||||
python -c "import sys; import os; print(os.path.join(os.path.join(sys.prefix, 'Scripts' if os.name == 'nt' else 'bin'), 'avifenc'))"
|
||||
```
|
||||
3. Restart ComfyUI after installing
|
||||
|
||||
### Installer fails
|
||||
|
||||
**Symptom:** `install_avifenc.py` fails to download
|
||||
|
||||
**Solution:**
|
||||
1. Check internet connection
|
||||
2. Verify Python installation
|
||||
3. Try manual download from https://github.com/AOMediaCodec/libavif/releases
|
||||
4. Check GitHub releases for latest version
|
||||
|
||||
### Node not appearing in menu
|
||||
|
||||
**Symptom:** "AVIF Animation (avifenc)" node not found
|
||||
|
||||
**Solution:**
|
||||
1. Check file structure:
|
||||
```
|
||||
ComfyUI/custom_nodes/ComfyUI-AVIF-Animation/
|
||||
├── __init__.py
|
||||
├── nodes.py
|
||||
├── install_avifenc.py
|
||||
├── README.md
|
||||
└── ...
|
||||
```
|
||||
2. Restart ComfyUI
|
||||
3. Check ComfyUI console for errors
|
||||
|
||||
### Permission denied on Windows
|
||||
|
||||
**Symptom:** "Access denied" when saving files
|
||||
|
||||
**Solution:**
|
||||
1. Run ComfyUI as Administrator
|
||||
2. Or check output directory permissions
|
||||
|
||||
### Large file size
|
||||
|
||||
**Symptom:** AVIF files are too large
|
||||
|
||||
**Solution:**
|
||||
- Lower `quality` parameter (try 40-60)
|
||||
- Lower `alpha_quality` parameter (try 40-60)
|
||||
- Reduce `fps` parameter (try 15-24)
|
||||
|
||||
### Slow encoding
|
||||
|
||||
**Symptom:** Encoding takes too long
|
||||
|
||||
**Solution:**
|
||||
- Increase `speed` parameter (try 8-10)
|
||||
- Reduce image resolution
|
||||
- Reduce number of frames
|
||||
|
||||
## System Requirements
|
||||
|
||||
### Minimum
|
||||
- Python 3.8+
|
||||
- ComfyUI
|
||||
- avifenc 1.0+ (installed by `install_avifenc.py`)
|
||||
|
||||
### Recommended
|
||||
- Python 3.10+
|
||||
- ComfyUI latest
|
||||
- avifenc 1.3+
|
||||
- NVIDIA GPU (for ComfyUI)
|
||||
|
||||
## Compatibility
|
||||
|
||||
### Tested Platforms
|
||||
- ✅ Windows 10/11
|
||||
- ✅ macOS 12+ (Monterey)
|
||||
- ✅ Linux (Ubuntu 20.04+, Fedora 35+)
|
||||
|
||||
### ComfyUI Versions
|
||||
- ✅ ComfyUI Stable
|
||||
- ✅ ComfyUI Daily
|
||||
- ✅ ComfyUI Portable
|
||||
|
||||
## Support
|
||||
|
||||
For additional help:
|
||||
- GitHub Issues: https://github.com/yourusername/ComfyUI-AVIF-Animation/issues
|
||||
- ComfyUI Discord: https://discord.gg/comfyui
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 ComfyUI-AVIF-Animation Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,126 @@
|
||||
# Project Structure
|
||||
|
||||
```
|
||||
ComfyUI-AVIF-Animation/
|
||||
├── __init__.py # Node registration and initialization
|
||||
├── nodes.py # Main node implementation
|
||||
├── README.md # Full documentation (English)
|
||||
├── README_CN.md # Quick start (Chinese)
|
||||
├── INSTALL.md # Installation guide
|
||||
├── CHANGELOG.md # Version history
|
||||
├── LICENSE # MIT License
|
||||
├── requirements.txt # Python dependencies (none required)
|
||||
├── .gitignore # Git ignore rules
|
||||
└── example_workflow.json # Example ComfyUI workflow
|
||||
```
|
||||
|
||||
## File Descriptions
|
||||
|
||||
### Core Files
|
||||
|
||||
**__init__.py**
|
||||
- Registers the AVIFAnimationNode with ComfyUI
|
||||
- Maps node class to display name
|
||||
- Required for ComfyUI to recognize the custom node
|
||||
|
||||
**nodes.py**
|
||||
- Main implementation of AVIFAnimationNode class
|
||||
- Handles image processing and avifenc execution
|
||||
- Includes helper functions for image saving and memory management
|
||||
- Comprehensive error handling and user feedback
|
||||
|
||||
### Documentation Files
|
||||
|
||||
**README.md**
|
||||
- Complete documentation in English
|
||||
- Features, requirements, usage examples
|
||||
- Format comparison table
|
||||
- Troubleshooting guide
|
||||
|
||||
**README_CN.md**
|
||||
- Quick start guide in Chinese
|
||||
- Basic installation and usage
|
||||
- Links to full documentation
|
||||
|
||||
**INSTALL.md**
|
||||
- Detailed installation instructions
|
||||
- Platform-specific commands
|
||||
- Troubleshooting common issues
|
||||
- System requirements
|
||||
|
||||
**CHANGELOG.md**
|
||||
- Version history and release notes
|
||||
- Feature additions and changes
|
||||
- Planned features for future releases
|
||||
|
||||
### Configuration Files
|
||||
|
||||
**requirements.txt**
|
||||
- Python dependencies (none required)
|
||||
- Notes about system dependencies (avifenc)
|
||||
|
||||
**.gitignore**
|
||||
- Git ignore patterns
|
||||
- Excludes Python cache, IDE files, OS files
|
||||
|
||||
**LICENSE**
|
||||
- MIT License text
|
||||
- Allows free use, modification, and distribution
|
||||
|
||||
**example_workflow.json**
|
||||
- Sample ComfyUI workflow
|
||||
- Demonstrates node usage
|
||||
- Can be imported directly into ComfyUI
|
||||
|
||||
## Installation
|
||||
|
||||
Copy the entire `ComfyUI-AVIF-Animation/` folder to:
|
||||
```
|
||||
ComfyUI/custom_nodes/
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
To modify or extend this node:
|
||||
|
||||
1. Edit `nodes.py` for node logic
|
||||
2. Edit `__init__.py` for node registration
|
||||
3. Test changes in ComfyUI
|
||||
4. Update documentation as needed
|
||||
5. Update CHANGELOG.md for version changes
|
||||
|
||||
## Dependencies
|
||||
|
||||
### System (Required)
|
||||
- avifenc (libavif tools)
|
||||
- Windows: winget install jark006.jarkViewer
|
||||
- macOS: brew install libavif
|
||||
- Linux: sudo apt-get install libavif-bin
|
||||
|
||||
### Python (No additional dependencies)
|
||||
- torch (already required by ComfyUI)
|
||||
- pillow (already required by ComfyUI)
|
||||
- Standard library modules:
|
||||
- os, subprocess, tempfile
|
||||
- concurrent.futures, gc
|
||||
|
||||
## Versioning
|
||||
|
||||
This project follows Semantic Versioning (SemVer):
|
||||
- MAJOR.MINOR.PATCH
|
||||
- Example: 1.0.0
|
||||
|
||||
## Contributing
|
||||
|
||||
When contributing:
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Make your changes
|
||||
4. Update documentation
|
||||
5. Submit a pull request
|
||||
|
||||
## Support
|
||||
|
||||
For issues and questions:
|
||||
- GitHub Issues
|
||||
- ComfyUI Discord community
|
||||
@@ -0,0 +1,175 @@
|
||||
# ComfyUI-AVIF-Animation - 项目完成总结
|
||||
|
||||
## 📦 项目文件清单
|
||||
|
||||
### 核心文件 (2个)
|
||||
✅ `__init__.py` - 节点注册和初始化
|
||||
✅ `nodes.py` - 主节点实现(AVIFAnimationNode类)
|
||||
|
||||
### 文档文件 (6个)
|
||||
✅ `README.md` - 完整英文文档
|
||||
✅ `README_CN.md` - 中文快速开始指南
|
||||
✅ `INSTALL.md` - 详细安装指南
|
||||
✅ `CHANGELOG.md` - 版本历史
|
||||
✅ `PROJECT_STRUCTURE.md` - 项目结构说明
|
||||
✅ `PUBLISH.md` - 发布到GitHub的指南
|
||||
|
||||
### 配置文件 (4个)
|
||||
✅ `requirements.txt` - Python依赖(无额外依赖)
|
||||
✅ `.gitignore` - Git忽略规则
|
||||
✅ `LICENSE` - MIT许可证
|
||||
✅ `example_workflow.json` - 示例工作流
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 1. 安装avifenc
|
||||
|
||||
**Windows:**
|
||||
```bash
|
||||
winget install jark006.jarkViewer
|
||||
```
|
||||
|
||||
**macOS:**
|
||||
```bash
|
||||
brew install libavif
|
||||
```
|
||||
|
||||
**Linux:**
|
||||
```bash
|
||||
sudo apt-get install libavif-bin
|
||||
```
|
||||
|
||||
### 2. 安装节点
|
||||
|
||||
将整个 `ComfyUI-AVIF-Animation/` 文件夹复制到:
|
||||
```
|
||||
ComfyUI/custom_nodes/
|
||||
```
|
||||
|
||||
### 3. 重启ComfyUI
|
||||
|
||||
重启后,在节点菜单中找到 "AVIF Animation (avifenc)" 节点
|
||||
|
||||
## 📋 节点参数
|
||||
|
||||
### 输入
|
||||
- `images` (IMAGE) - 输入图像序列
|
||||
- `fps` (INT) - 帧率 (1-120, 默认: 24)
|
||||
- `quality` (INT) - 颜色质量 (0-100, 默认: 60)
|
||||
- `alpha_quality` (INT) - 透明质量 (0-100, 默认: 60)
|
||||
- `speed` (INT) - 编码速度 (0-10, 默认: 6)
|
||||
|
||||
### 输出
|
||||
- `avif_path` (STRING) - 生成的AVIF文件路径
|
||||
|
||||
## 🎯 核心特性
|
||||
|
||||
✅ **透明通道支持** - 完整的alpha通道支持
|
||||
✅ **高质量编码** - 使用libavif的avifenc
|
||||
✅ **苹果兼容** - iOS 16+ 和 macOS Ventura+ 支持
|
||||
✅ **高效压缩** - 文件大小合理
|
||||
✅ **自动循环** - 无限循环播放
|
||||
✅ **灵活参数** - 可调节FPS、质量、速度
|
||||
✅ **内存优化** - 自动清理内存
|
||||
✅ **多线程处理** - 并行处理图像
|
||||
✅ **详细日志** - 完整的控制台输出
|
||||
|
||||
## 📊 格式对比
|
||||
|
||||
| 格式 | 透明 | 文件大小 | 苹果兼容 | 推荐度 |
|
||||
|------|------|----------|----------|--------|
|
||||
| **AVIF (avifenc)** | ✅ | 中等 | ✅ | ⭐⭐⭐⭐⭐ |
|
||||
| WebP | ✅ | 最小 | ✅ | ⭐⭐⭐⭐ |
|
||||
| WebM (VP9) | ✅ | 小 | ❌ | ⭐⭐ |
|
||||
| MOV (ProRes) | ✅ | 大 | ✅ | ⭐⭐ |
|
||||
| GIF | ❌ | 中等 | ✅ | ⭐ |
|
||||
|
||||
## 📝 使用示例
|
||||
|
||||
### 基础用法
|
||||
```
|
||||
Load Images → AVIF Animation (avifenc) → Save/Preview
|
||||
```
|
||||
|
||||
### 高质量动画
|
||||
- FPS: 30
|
||||
- Quality: 80
|
||||
- Alpha Quality: 80
|
||||
- Speed: 4
|
||||
|
||||
### 快速编码
|
||||
- FPS: 24
|
||||
- Quality: 50
|
||||
- Alpha Quality: 50
|
||||
- Speed: 8
|
||||
|
||||
### 小文件大小
|
||||
- FPS: 15
|
||||
- Quality: 40
|
||||
- Alpha Quality: 40
|
||||
- Speed: 6
|
||||
|
||||
## 🔧 技术细节
|
||||
|
||||
### 编码流程
|
||||
1. 输入tensor序列 → PNG帧(临时文件)
|
||||
2. PNG帧 → avifenc → AVIF动画
|
||||
3. AVIF文件 → ComfyUI输出目录
|
||||
|
||||
### 支持的功能
|
||||
- AVIF 1.0 规范
|
||||
- YUV444 颜色格式
|
||||
- Alpha通道(非预乘)
|
||||
- 无限循环播放
|
||||
- 可变帧率
|
||||
|
||||
## 🌐 发布到GitHub
|
||||
|
||||
### 步骤
|
||||
1. 创建GitHub仓库:`ComfyUI-AVIF-Animation`
|
||||
2. 上传所有文件
|
||||
3. 创建Release:`v1.0.0`
|
||||
4. 更新README
|
||||
5. 提交到ComfyUI Registry
|
||||
|
||||
详细步骤见 `PUBLISH.md`
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
MIT License - 可自由使用、修改和分发
|
||||
|
||||
## 💡 下一步
|
||||
|
||||
### 可选改进
|
||||
- GPU加速支持(如果未来avifenc版本支持)
|
||||
- 批量处理多个动画
|
||||
- 自定义输出目录选项
|
||||
- ComfyUI内预览功能
|
||||
- 更多AVIF编码参数
|
||||
|
||||
## 🆘 故障排除
|
||||
|
||||
### avifenc未安装
|
||||
**解决:** 按照INSTALL.md安装avifenc
|
||||
|
||||
### 节点未出现在菜单
|
||||
**解决:** 检查文件结构,重启ComfyUI
|
||||
|
||||
### 文件太大
|
||||
**解决:** 降低quality和alpha_quality参数
|
||||
|
||||
### 编码太慢
|
||||
**解决:** 增加speed参数(尝试8-10)
|
||||
|
||||
## 📞 支持
|
||||
|
||||
- GitHub Issues: https://github.com/YOUR_USERNAME/ComfyUI-AVIF-Animation/issues
|
||||
- ComfyUI Discord: https://discord.gg/comfyui
|
||||
|
||||
## ✅ 完成状态
|
||||
|
||||
所有文件已创建完成,项目可以直接发布!
|
||||
|
||||
**版本:** 1.0.0
|
||||
**发布日期:** 2025-01-15
|
||||
**状态:** ✅ 准备发布
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
# ComfyUI-AVIF-Animation
|
||||
|
||||
## Release Checklist
|
||||
|
||||
### Files Created
|
||||
|
||||
✅ **Core Files**
|
||||
- `__init__.py` - Node registration
|
||||
- `nodes.py` - Main node implementation
|
||||
|
||||
✅ **Documentation**
|
||||
- `README.md` - Full documentation (English)
|
||||
- `README_CN.md` - Quick start (Chinese)
|
||||
- `INSTALL.md` - Installation guide
|
||||
- `CHANGELOG.md` - Version history
|
||||
- `PROJECT_STRUCTURE.md` - Project documentation
|
||||
|
||||
✅ **Configuration**
|
||||
- `requirements.txt` - Python dependencies
|
||||
- `.gitignore` - Git ignore rules
|
||||
- `LICENSE` - MIT License
|
||||
- `example_workflow.json` - Example workflow
|
||||
|
||||
## Publishing to GitHub
|
||||
|
||||
### Step 1: Create Repository
|
||||
|
||||
1. Go to https://github.com/new
|
||||
2. Repository name: `ComfyUI-AVIF-Animation`
|
||||
3. Description: `A ComfyUI custom node for creating AVIF animations with transparency support using avifenc`
|
||||
4. Visibility: Public
|
||||
5. Initialize with: README (we'll replace it)
|
||||
6. Click "Create repository"
|
||||
|
||||
### Step 2: Upload Files
|
||||
|
||||
**Option A: Using Git Command Line**
|
||||
```bash
|
||||
cd ComfyUI-AVIF-Animation
|
||||
git init
|
||||
git add .
|
||||
git commit -m "Initial release: AVIF Animation node for ComfyUI"
|
||||
git branch -M main
|
||||
git remote add origin https://github.com/YOUR_USERNAME/ComfyUI-AVIF-Animation.git
|
||||
git push -u origin main
|
||||
```
|
||||
|
||||
**Option B: Using GitHub Desktop**
|
||||
1. Open GitHub Desktop
|
||||
2. File > Add Local Repository
|
||||
3. Select `ComfyUI-AVIF-Animation` folder
|
||||
4. Commit changes with message "Initial release"
|
||||
5. Push to GitHub
|
||||
|
||||
**Option C: Using Web Interface**
|
||||
1. Go to your repository on GitHub
|
||||
2. Click "uploading an existing file"
|
||||
3. Drag and drop all files
|
||||
4. Commit changes: "Initial release"
|
||||
|
||||
### Step 3: Create Release
|
||||
|
||||
1. Go to repository on GitHub
|
||||
2. Click "Releases" > "Create a new release"
|
||||
3. Tag version: `v1.0.0`
|
||||
4. Release title: `v1.0.0 - Initial Release`
|
||||
5. Description:
|
||||
```
|
||||
## Features
|
||||
- Full transparency (alpha channel) support
|
||||
- High quality AVIF encoding
|
||||
- Apple compatible (iOS 16+, macOS Ventura+)
|
||||
- Efficient file sizes
|
||||
- Automatic loop playback
|
||||
- Flexible parameters (FPS, quality, speed)
|
||||
|
||||
## Installation
|
||||
See [INSTALL.md](INSTALL.md) for detailed instructions.
|
||||
|
||||
## Requirements
|
||||
- avifenc (libavif tools)
|
||||
- ComfyUI
|
||||
|
||||
## Documentation
|
||||
- [README.md](README.md) - Full documentation
|
||||
- [INSTALL.md](INSTALL.md) - Installation guide
|
||||
```
|
||||
6. Click "Publish release"
|
||||
|
||||
### Step 4: Update README
|
||||
|
||||
Replace the default README with `README.md`:
|
||||
1. Go to repository on GitHub
|
||||
2. Click `README.md`
|
||||
3. Click "Edit this file"
|
||||
4. Copy content from `README.md`
|
||||
5. Commit changes
|
||||
|
||||
## ComfyUI Registry
|
||||
|
||||
To publish to ComfyUI Registry:
|
||||
|
||||
1. Go to https://registry.comfyui.org/
|
||||
2. Click "Submit Custom Node"
|
||||
3. Fill in details:
|
||||
- Name: ComfyUI-AVIF-Animation
|
||||
- Author: Your GitHub username
|
||||
- Description: Create AVIF animations with transparency support
|
||||
- Repository: https://github.com/YOUR_USERNAME/ComfyUI-AVIF-Animation
|
||||
- Tags: avif, animation, transparency, video
|
||||
4. Submit
|
||||
|
||||
## Promotion
|
||||
|
||||
### Social Media
|
||||
- Twitter/X: Share with #ComfyUI #AI #AVIF
|
||||
- Reddit: Post to r/StableDiffusion, r/ComfyUI
|
||||
- Discord: Share in ComfyUI Discord server
|
||||
|
||||
### Communities
|
||||
- ComfyUI GitHub Discussions
|
||||
- Stable Diffusion forums
|
||||
- AI art communities
|
||||
|
||||
## Version Information
|
||||
|
||||
**Current Version:** 1.0.0
|
||||
**Release Date:** 2025-01-15
|
||||
|
||||
## Support
|
||||
|
||||
For issues and questions:
|
||||
- GitHub Issues: https://github.com/YOUR_USERNAME/ComfyUI-AVIF-Animation/issues
|
||||
- ComfyUI Discord: https://discord.gg/comfyui
|
||||
|
||||
## License
|
||||
|
||||
MIT License - See LICENSE file for details
|
||||
@@ -0,0 +1,139 @@
|
||||
# ComfyUI AVIF Animation Node
|
||||
|
||||
A ComfyUI custom node for generating AVIF animations with transparency support using avifenc.
|
||||
|
||||
## Features
|
||||
|
||||
- ✅ Generate AVIF animations from image sequences
|
||||
- ✅ Full transparency (alpha channel) support
|
||||
- ✅ Adjustable quality parameters
|
||||
- ✅ Configurable encoding speed
|
||||
- ✅ Cross-platform support (Windows, macOS, Linux)
|
||||
- ✅ No Python dependencies required
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.8+
|
||||
- ComfyUI
|
||||
- avifenc (libavif command-line tool)
|
||||
|
||||
## Installation
|
||||
|
||||
### Step 1: Install avifenc
|
||||
|
||||
1. Visit: https://github.com/AOMediaCodec/libavif/releases
|
||||
2. Download the latest version of avifenc for your platform:
|
||||
- Windows: `libavif-*.windows-x64.zip`
|
||||
- macOS: `libavif-*.darwin-arm64.zip` (M1/M2) or `libavif-*.darwin-x86_64.zip` (Intel)
|
||||
- Linux: `libavif-*.linux-x86_64.zip`
|
||||
3. Extract the zip file
|
||||
4. Copy `avifenc` (or `avifenc.exe` on Windows) to one of these locations:
|
||||
- Windows: `C:\Users\[username]\Miniconda3\envs\[your_env]\Scripts\avifenc.exe`
|
||||
- macOS/Linux: `/usr/local/bin/avifenc` or add to PATH
|
||||
|
||||
### Step 2: Install the Node
|
||||
|
||||
1. Clone or download this repository to your ComfyUI custom_nodes directory:
|
||||
|
||||
```bash
|
||||
cd ComfyUI/custom_nodes
|
||||
git clone https://github.com/yourusername/ComfyUI-AVIF-Animation.git
|
||||
```
|
||||
|
||||
2. Restart ComfyUI
|
||||
|
||||
## Usage
|
||||
|
||||
1. Open ComfyUI
|
||||
2. Right-click in the node graph area
|
||||
3. Navigate to `image/animation` category
|
||||
4. Select `AVIF Animation (avifenc)` node
|
||||
5. Connect your image sequence to `images` input
|
||||
6. Adjust parameters:
|
||||
- **FPS**: Frames per second (1-120, default: 24)
|
||||
- **Quality**: Overall quality (0-100, default: 50)
|
||||
- **Alpha Quality**: Transparency quality (0-100, default: 60)
|
||||
- **Speed**: Encoding speed (0-10, default: 6)
|
||||
7. Execute the node to generate AVIF animation
|
||||
|
||||
## Parameters
|
||||
|
||||
### FPS (Frames Per Second)
|
||||
- Range: 1-120
|
||||
- Default: 24
|
||||
- Description: Controls the playback speed of the animation
|
||||
|
||||
### Quality
|
||||
- Range: 0-100
|
||||
- Default: 50
|
||||
- Description: Overall image quality (higher = better quality, larger file size)
|
||||
|
||||
### Alpha Quality
|
||||
- Range: 0-100
|
||||
- Default: 60
|
||||
- Description: Transparency channel quality (higher = better transparency quality, larger file size)
|
||||
|
||||
### Speed
|
||||
- Range: 0-10
|
||||
- Default: 6
|
||||
- Description: Encoding speed (0 = slowest/best quality, 10 = fastest/lower quality)
|
||||
|
||||
## Output
|
||||
|
||||
The node outputs the path to the generated AVIF file in the ComfyUI output directory.
|
||||
|
||||
## Example Workflow
|
||||
|
||||
```python
|
||||
# Example: Generate AVIF animation from image sequence
|
||||
1. Load images using "Load Image" node
|
||||
2. Connect to "AVIF Animation" node
|
||||
3. Set FPS to 24
|
||||
4. Set Quality to 50
|
||||
5. Set Alpha Quality to 60
|
||||
6. Set Speed to 6
|
||||
7. Execute to generate AVIF animation
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### avifenc not found
|
||||
```
|
||||
Error: ❌ avifenc未找到!
|
||||
```
|
||||
Solution: Install avifenc following the installation steps above.
|
||||
|
||||
### Import errors
|
||||
```
|
||||
Error: ❌ 导入错误: No module named 'torch'
|
||||
```
|
||||
Solution: Install torch: `pip install torch`
|
||||
|
||||
### Transparency not working
|
||||
Make sure your input images have 4 channels (RGBA). If they have 3 channels (RGB), the node will automatically add an opaque alpha channel.
|
||||
|
||||
## Technical Details
|
||||
|
||||
This node uses:
|
||||
- **avifenc**: Command-line tool from libavif for AVIF encoding
|
||||
- **PIL/Pillow**: Image processing
|
||||
- **torch**: Tensor operations for ComfyUI compatibility
|
||||
|
||||
The AVIF format provides:
|
||||
- Better compression than JPEG, PNG, and WebP
|
||||
- Full transparency support
|
||||
- Hardware-accelerated decoding on modern devices
|
||||
- Wide browser support (Chrome, Firefox, Safari)
|
||||
|
||||
## License
|
||||
|
||||
MIT License
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||
|
||||
## Credits
|
||||
|
||||
- [libavif](https://github.com/AOMediaCodec/libavif) - AVIF codec library
|
||||
- [ComfyUI](https://github.com/comfyanonymous/ComfyUI) - UI framework
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
# ComfyUI AVIF 动画节点
|
||||
|
||||
使用 avifenc 生成带透明通道的 AVIF 动画的 ComfyUI 自定义节点。
|
||||
|
||||
## 特性
|
||||
|
||||
- ✅ 从图像序列生成 AVIF 动画
|
||||
- ✅ 完整的透明通道支持
|
||||
- ✅ 可调节的质量参数
|
||||
- ✅ 可配置的编码速度
|
||||
- ✅ 跨平台支持(Windows、macOS、Linux)
|
||||
- ✅ 无需 Python 依赖
|
||||
- ✅ **自动安装 avifenc**
|
||||
|
||||
## 系统要求
|
||||
|
||||
- Python 3.8+
|
||||
- ComfyUI
|
||||
- 网络连接(用于自动下载 avifenc)
|
||||
|
||||
## 安装
|
||||
|
||||
### 自动安装(推荐)
|
||||
|
||||
1. 将此仓库克隆或下载到 ComfyUI 的 custom_nodes 目录:
|
||||
|
||||
```bash
|
||||
cd ComfyUI/custom_nodes
|
||||
git clone https://github.com/yourusername/ComfyUI-AVIF-Animation.git
|
||||
```
|
||||
|
||||
2. 重启 ComfyUI
|
||||
|
||||
3. 首次启动时,节点会自动检测并安装 avifenc 到 Python 的 Scripts 目录
|
||||
|
||||
### 手动安装(如果自动安装失败)
|
||||
|
||||
如果自动安装失败,您可以手动安装 avifenc:
|
||||
|
||||
1. 访问:https://github.com/AOMediaCodec/libavif/releases
|
||||
2. 下载适合您平台的最新版本 avifenc:
|
||||
- Windows: `libavif-*.windows-x64.zip`
|
||||
- macOS: `libavif-*.darwin-arm64.zip` (M1/M2) 或 `libavif-*.darwin-x86_64.zip` (Intel)
|
||||
- Linux: `libavif-*.linux-x86_64.zip`
|
||||
3. 解压 zip 文件
|
||||
4. 将 `avifenc`(Windows 上是 `avifenc.exe`)复制到以下位置之一:
|
||||
- Windows: `C:\Users\[用户名]\Miniconda3\envs\[你的环境]\Scripts\avifenc.exe`
|
||||
- macOS/Linux: `/usr/local/bin/avifenc` 或添加到 PATH
|
||||
5. 重启 ComfyUI
|
||||
|
||||
## 使用
|
||||
|
||||
1. 打开 ComfyUI
|
||||
2. 在节点图中右键点击
|
||||
3. 导航到 `image/animation` 类别
|
||||
4. 选择 `AVIF Animation (avifenc)` 节点
|
||||
5. 将图像序列连接到 `images` 输入
|
||||
6. 调整参数:
|
||||
- **FPS**: 每秒帧数(1-120,默认:24)
|
||||
- **质量**: 整体质量(0-100,默认:50)
|
||||
- **透明质量**: 透明度质量(0-100,默认:60)
|
||||
- **速度**: 编码速度(0-10,默认:6)
|
||||
7. 执行节点生成 AVIF 动画
|
||||
|
||||
## 参数说明
|
||||
|
||||
### FPS(每秒帧数)
|
||||
- 范围:1-120
|
||||
- 默认:24
|
||||
- 说明:控制动画的播放速度
|
||||
|
||||
### 质量
|
||||
- 范围:0-100
|
||||
- 默认:50
|
||||
- 说明:整体图像质量(越高 = 质量越好,文件越大)
|
||||
|
||||
### 透明质量
|
||||
- 范围:0-100
|
||||
- 默认:60
|
||||
- 说明:透明通道质量(越高 = 透明度质量越好,文件越大)
|
||||
|
||||
### 速度
|
||||
- 范围:0-10
|
||||
- 默认:6
|
||||
- 说明:编码速度(0 = 最慢/最佳质量,10 = 最快/较低质量)
|
||||
|
||||
## 输出
|
||||
|
||||
节点会输出生成的 AVIF 文件路径,文件保存在 ComfyUI 的输出目录中。
|
||||
|
||||
## 示例工作流
|
||||
|
||||
```python
|
||||
# 示例:从图像序列生成 AVIF 动画
|
||||
1. 使用"Load Image"节点加载图像
|
||||
2. 连接到"AVIF Animation"节点
|
||||
3. 设置 FPS 为 24
|
||||
4. 设置质量为 50
|
||||
5. 设置透明质量为 60
|
||||
6. 设置速度为 6
|
||||
7. 执行以生成 AVIF 动画
|
||||
```
|
||||
|
||||
## 故障排除
|
||||
|
||||
### avifenc 未找到
|
||||
```
|
||||
错误:❌ avifenc未找到!
|
||||
```
|
||||
解决方案:按照上述安装步骤安装 avifenc。
|
||||
|
||||
### 导入错误
|
||||
```
|
||||
错误:❌ 导入错误: No module named 'torch'
|
||||
```
|
||||
解决方案:安装 torch:`pip install torch`
|
||||
|
||||
### 透明度不工作
|
||||
确保输入图像有 4 个通道(RGBA)。如果它们有 3 个通道(RGB),节点会自动添加不透明的 alpha 通道。
|
||||
|
||||
## 技术细节
|
||||
|
||||
此节点使用:
|
||||
- **avifenc**: 来自 libavif 的 AVIF 编码命令行工具
|
||||
- **PIL/Pillow**: 图像处理
|
||||
- **torch**: ComfyUI 兼容性的张量操作
|
||||
|
||||
AVIF 格式提供:
|
||||
- 比 JPEG、PNG 和 WebP 更好的压缩
|
||||
- 完整的透明度支持
|
||||
- 现代设备上的硬件加速解码
|
||||
- 广泛的浏览器支持(Chrome、Firefox、Safari)
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT License
|
||||
|
||||
## 贡献
|
||||
|
||||
欢迎贡献!请随时提交 Pull Request。
|
||||
|
||||
## 致谢
|
||||
|
||||
- [libavif](https://github.com/AOMediaCodec/libavif) - AVIF 编解码器库
|
||||
- [ComfyUI](https://github.com/comfyanonymous/ComfyUI) - UI 框架
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
def check_and_install_avifenc():
|
||||
"""检查并自动安装avifenc"""
|
||||
avifenc_paths = [
|
||||
os.path.join(sys.prefix, "Scripts", "avifenc.exe"),
|
||||
os.path.join(sys.prefix, "bin", "avifenc"),
|
||||
os.path.join(os.path.dirname(__file__), "avifenc.exe"),
|
||||
]
|
||||
|
||||
avifenc_exists = False
|
||||
for path in avifenc_paths:
|
||||
if os.path.exists(path) or shutil.which("avifenc"):
|
||||
avifenc_exists = True
|
||||
break
|
||||
|
||||
if not avifenc_exists:
|
||||
print("⚠️ avifenc未找到,尝试自动安装...")
|
||||
try:
|
||||
from .install_avifenc import install_avifenc
|
||||
install_avifenc()
|
||||
except Exception as e:
|
||||
print(f"❌ 自动安装失败: {e}")
|
||||
print("📋 请手动安装avifenc:")
|
||||
print("1. 访问: https://github.com/AOMediaCodec/libavif/releases")
|
||||
print("2. 下载最新版本的avifenc (Windows x64)")
|
||||
print("3. 解压zip文件")
|
||||
print(f"4. 将avifenc.exe复制到: {os.path.join(sys.prefix, 'Scripts')}")
|
||||
print("5. 重启ComfyUI")
|
||||
|
||||
check_and_install_avifenc()
|
||||
|
||||
from .nodes import AVIFAnimationNode
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"AVIF Animation": AVIFAnimationNode
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"AVIF Animation": "AVIF Animation (avifenc)"
|
||||
}
|
||||
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
|
||||
@@ -0,0 +1,34 @@
|
||||
import os
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
|
||||
avif_path = r"D:\ai\ComfyUI-aki-v1.6-8180\ComfyUI\output\test_transparency.avif"
|
||||
|
||||
print(f"🔍 检查 AVIF 文件: {avif_path}")
|
||||
|
||||
try:
|
||||
img = Image.open(avif_path)
|
||||
print(f"✅ 成功打开 AVIF 文件")
|
||||
print(f" 模式: {img.mode}")
|
||||
print(f" 大小: {img.size}")
|
||||
|
||||
if img.mode == 'RGBA':
|
||||
img_array = np.array(img)
|
||||
alpha_channel = img_array[:, :, 3]
|
||||
|
||||
print(f" Alpha范围: {alpha_channel.min()} - {alpha_channel.max()}")
|
||||
print(f" 透明像素: {(alpha_channel < 128).sum()}/{alpha_channel.size}")
|
||||
print(f" 透明像素占比: {(alpha_channel < 128).sum() / alpha_channel.size * 100:.2f}%")
|
||||
|
||||
# 检查是否有真正的透明区域
|
||||
if (alpha_channel < 128).sum() > 0:
|
||||
print("✅ AVIF 文件包含透明区域")
|
||||
else:
|
||||
print("❌ AVIF 文件没有透明区域")
|
||||
else:
|
||||
print(f"❌ AVIF 文件不是 RGBA 模式,而是 {img.mode}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 错误: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
@@ -0,0 +1,690 @@
|
||||
{
|
||||
"id": "68b40f8d-e50b-4552-b913-cf5c5ec6741f",
|
||||
"revision": 0,
|
||||
"last_node_id": 30,
|
||||
"last_link_id": 34,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 30,
|
||||
"type": "RMBG",
|
||||
"pos": [
|
||||
8696.579560149266,
|
||||
1564.6048377778534
|
||||
],
|
||||
"size": [
|
||||
320,
|
||||
302
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"label": "image",
|
||||
"localized_name": "图像",
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 33
|
||||
},
|
||||
{
|
||||
"label": "model",
|
||||
"localized_name": "模型",
|
||||
"name": "model",
|
||||
"type": "COMBO",
|
||||
"widget": {
|
||||
"name": "model"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "sensitivity",
|
||||
"localized_name": "灵敏度",
|
||||
"name": "sensitivity",
|
||||
"shape": 7,
|
||||
"type": "FLOAT",
|
||||
"widget": {
|
||||
"name": "sensitivity"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "process_res",
|
||||
"localized_name": "处理分辨率",
|
||||
"name": "process_res",
|
||||
"shape": 7,
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "process_res"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "mask_blur",
|
||||
"localized_name": "遮罩模糊",
|
||||
"name": "mask_blur",
|
||||
"shape": 7,
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "mask_blur"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "mask_offset",
|
||||
"localized_name": "遮罩偏移",
|
||||
"name": "mask_offset",
|
||||
"shape": 7,
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "mask_offset"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "invert_output",
|
||||
"localized_name": "反转输出",
|
||||
"name": "invert_output",
|
||||
"shape": 7,
|
||||
"type": "BOOLEAN",
|
||||
"widget": {
|
||||
"name": "invert_output"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "refine_foreground",
|
||||
"localized_name": "精细前景优化",
|
||||
"name": "refine_foreground",
|
||||
"shape": 7,
|
||||
"type": "BOOLEAN",
|
||||
"widget": {
|
||||
"name": "refine_foreground"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "background",
|
||||
"localized_name": "背景类型",
|
||||
"name": "background",
|
||||
"shape": 7,
|
||||
"type": "COMBO",
|
||||
"widget": {
|
||||
"name": "background"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "background_color",
|
||||
"localized_name": "背景颜色",
|
||||
"name": "background_color",
|
||||
"shape": 7,
|
||||
"type": "COLOR",
|
||||
"widget": {
|
||||
"name": "background_color"
|
||||
},
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"label": "IMAGE",
|
||||
"localized_name": "图像",
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
34
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "MASK",
|
||||
"localized_name": "遮罩",
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null
|
||||
},
|
||||
{
|
||||
"label": "MASK_IMAGE",
|
||||
"localized_name": "遮罩图像",
|
||||
"name": "MASK_IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfyui-rmbg",
|
||||
"ver": "2.3.2",
|
||||
"widget_ue_connectable": {},
|
||||
"Node name for S&R": "RMBG"
|
||||
},
|
||||
"widgets_values": [
|
||||
"RMBG-2.0",
|
||||
1,
|
||||
1024,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
"Alpha",
|
||||
"#ffffff"
|
||||
],
|
||||
"color": "#222e40",
|
||||
"bgcolor": "#364254"
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"type": "AVIF Animation",
|
||||
"pos": [
|
||||
9115.026576433382,
|
||||
1563.8927629348113
|
||||
],
|
||||
"size": [
|
||||
386.1084575013283,
|
||||
429.4583417955414
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"label": "images",
|
||||
"localized_name": "images",
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 34
|
||||
},
|
||||
{
|
||||
"label": "fps",
|
||||
"localized_name": "fps",
|
||||
"name": "fps",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "fps"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "quality",
|
||||
"localized_name": "quality",
|
||||
"name": "quality",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "quality"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "alpha_quality",
|
||||
"localized_name": "alpha_quality",
|
||||
"name": "alpha_quality",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "alpha_quality"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "speed",
|
||||
"localized_name": "speed",
|
||||
"name": "speed",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "speed"
|
||||
},
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"label": "avif_path",
|
||||
"localized_name": "avif_path",
|
||||
"name": "avif_path",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
32
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "AVIF Animation",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
25,
|
||||
50,
|
||||
60,
|
||||
6
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"type": "ImageResizeKJv2",
|
||||
"pos": [
|
||||
8305.902663670184,
|
||||
1574.1431546669041
|
||||
],
|
||||
"size": [
|
||||
270,
|
||||
336
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"label": "image",
|
||||
"localized_name": "image",
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 17
|
||||
},
|
||||
{
|
||||
"label": "mask",
|
||||
"localized_name": "mask",
|
||||
"name": "mask",
|
||||
"shape": 7,
|
||||
"type": "MASK",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "width",
|
||||
"localized_name": "width",
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "width"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "height",
|
||||
"localized_name": "height",
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "height"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "upscale_method",
|
||||
"localized_name": "upscale_method",
|
||||
"name": "upscale_method",
|
||||
"type": "COMBO",
|
||||
"widget": {
|
||||
"name": "upscale_method"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "keep_proportion",
|
||||
"localized_name": "keep_proportion",
|
||||
"name": "keep_proportion",
|
||||
"type": "COMBO",
|
||||
"widget": {
|
||||
"name": "keep_proportion"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "pad_color",
|
||||
"localized_name": "pad_color",
|
||||
"name": "pad_color",
|
||||
"type": "STRING",
|
||||
"widget": {
|
||||
"name": "pad_color"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "crop_position",
|
||||
"localized_name": "crop_position",
|
||||
"name": "crop_position",
|
||||
"type": "COMBO",
|
||||
"widget": {
|
||||
"name": "crop_position"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "divisible_by",
|
||||
"localized_name": "divisible_by",
|
||||
"name": "divisible_by",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "divisible_by"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "device",
|
||||
"localized_name": "device",
|
||||
"name": "device",
|
||||
"shape": 7,
|
||||
"type": "COMBO",
|
||||
"widget": {
|
||||
"name": "device"
|
||||
},
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"label": "IMAGE",
|
||||
"localized_name": "图像",
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
33
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "width",
|
||||
"localized_name": "width",
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"links": null
|
||||
},
|
||||
{
|
||||
"label": "height",
|
||||
"localized_name": "height",
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"links": null
|
||||
},
|
||||
{
|
||||
"label": "mask",
|
||||
"localized_name": "mask",
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfyui-kjnodes",
|
||||
"ver": "3fcd22f2fe2be69c3229f192362b91888277cbcb",
|
||||
"Node name for S&R": "ImageResizeKJv2",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
480,
|
||||
832,
|
||||
"lanczos",
|
||||
"resize",
|
||||
"0, 0, 0",
|
||||
"center",
|
||||
2,
|
||||
"cpu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"type": "VHS_LoadVideo",
|
||||
"pos": [
|
||||
7808.9144718314765,
|
||||
1567.7402459856683
|
||||
],
|
||||
"size": [
|
||||
374.53125,
|
||||
683.5246478873239
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"label": "meta_batch",
|
||||
"localized_name": "meta_batch",
|
||||
"name": "meta_batch",
|
||||
"shape": 7,
|
||||
"type": "VHS_BatchManager",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "vae",
|
||||
"localized_name": "vae",
|
||||
"name": "vae",
|
||||
"shape": 7,
|
||||
"type": "VAE",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "video",
|
||||
"localized_name": "video",
|
||||
"name": "video",
|
||||
"type": "COMBO",
|
||||
"widget": {
|
||||
"name": "video"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "force_rate",
|
||||
"localized_name": "force_rate",
|
||||
"name": "force_rate",
|
||||
"type": "FLOAT",
|
||||
"widget": {
|
||||
"name": "force_rate"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "custom_width",
|
||||
"localized_name": "custom_width",
|
||||
"name": "custom_width",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "custom_width"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "custom_height",
|
||||
"localized_name": "custom_height",
|
||||
"name": "custom_height",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "custom_height"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "frame_load_cap",
|
||||
"localized_name": "frame_load_cap",
|
||||
"name": "frame_load_cap",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "frame_load_cap"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "skip_first_frames",
|
||||
"localized_name": "skip_first_frames",
|
||||
"name": "skip_first_frames",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "skip_first_frames"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "select_every_nth",
|
||||
"localized_name": "select_every_nth",
|
||||
"name": "select_every_nth",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "select_every_nth"
|
||||
},
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "format",
|
||||
"localized_name": "format",
|
||||
"name": "format",
|
||||
"shape": 7,
|
||||
"type": "COMBO",
|
||||
"widget": {
|
||||
"name": "format"
|
||||
},
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"label": "IMAGE",
|
||||
"localized_name": "图像",
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
17
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "frame_count",
|
||||
"localized_name": "frame_count",
|
||||
"name": "frame_count",
|
||||
"type": "INT"
|
||||
},
|
||||
{
|
||||
"label": "audio",
|
||||
"localized_name": "audio",
|
||||
"name": "audio",
|
||||
"type": "AUDIO",
|
||||
"links": []
|
||||
},
|
||||
{
|
||||
"label": "video_info",
|
||||
"localized_name": "video_info",
|
||||
"name": "video_info",
|
||||
"type": "VHS_VIDEOINFO",
|
||||
"links": []
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfyui-videohelpersuite",
|
||||
"ver": "1.6.1",
|
||||
"Node name for S&R": "VHS_LoadVideo",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": {
|
||||
"video": "aaa111.webm",
|
||||
"force_rate": 0,
|
||||
"custom_width": 0,
|
||||
"custom_height": 0,
|
||||
"frame_load_cap": 7,
|
||||
"skip_first_frames": 0,
|
||||
"select_every_nth": 1,
|
||||
"format": "AnimateDiff",
|
||||
"videopreview": {
|
||||
"paused": false,
|
||||
"hidden": false,
|
||||
"params": {
|
||||
"force_rate": 0,
|
||||
"filename": "aaa111.webm",
|
||||
"select_every_nth": 1,
|
||||
"frame_load_cap": 7,
|
||||
"format": "video/webm",
|
||||
"skip_first_frames": 0,
|
||||
"type": "input"
|
||||
},
|
||||
"muted": false
|
||||
}
|
||||
},
|
||||
"color": "#232",
|
||||
"bgcolor": "#353",
|
||||
"shape": 1
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"type": "ShowAnything|Mie",
|
||||
"pos": [
|
||||
9609.8560745413,
|
||||
1549.3578161526268
|
||||
],
|
||||
"size": [
|
||||
302.3412014838126,
|
||||
221.05270867351783
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"label": "anything",
|
||||
"localized_name": "anything",
|
||||
"name": "anything",
|
||||
"type": "*",
|
||||
"link": 32
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"label": "STRING",
|
||||
"localized_name": "字符串",
|
||||
"name": "STRING",
|
||||
"type": "STRING",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfyui_mienodes",
|
||||
"ver": "1.0.6",
|
||||
"Node name for S&R": "ShowAnything|Mie",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
"D:\\ai\\ComfyUI-aki-v1.6-8180\\ComfyUI\\output\\avif_animation_20260115181804.avif"
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
17,
|
||||
19,
|
||||
0,
|
||||
20,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
32,
|
||||
28,
|
||||
0,
|
||||
26,
|
||||
0,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
33,
|
||||
20,
|
||||
0,
|
||||
30,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
34,
|
||||
30,
|
||||
0,
|
||||
28,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"workflowRendererVersion": "LG",
|
||||
"ue_links": [],
|
||||
"ds": {
|
||||
"scale": 0.7972024500000019,
|
||||
"offset": [
|
||||
-6461.918644276937,
|
||||
-813.5337506893557
|
||||
]
|
||||
},
|
||||
"links_added_by_ue": []
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import urllib.request
|
||||
import zipfile
|
||||
import tempfile
|
||||
import platform
|
||||
|
||||
def get_avifenc_url():
|
||||
system = platform.system()
|
||||
if system == "Windows":
|
||||
return "https://github.com/AOMediaCodec/libavif/releases/download/v1.3.0/windows-artifacts.zip"
|
||||
elif system == "Darwin":
|
||||
return "https://github.com/AOMediaCodec/libavif/releases/download/v1.3.0/macOS-artifacts.zip"
|
||||
elif system == "Linux":
|
||||
return "https://github.com/AOMediaCodec/libavif/releases/download/v1.3.0/linux-artifacts.zip"
|
||||
else:
|
||||
raise ValueError(f"Unsupported system: {system}")
|
||||
|
||||
def install_avifenc():
|
||||
print("📦 开始安装avifenc...")
|
||||
|
||||
try:
|
||||
url = get_avifenc_url()
|
||||
print(f"📥 下载avifenc from: {url}")
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
zip_path = os.path.join(temp_dir, "avifenc.zip")
|
||||
|
||||
try:
|
||||
urllib.request.urlretrieve(url, zip_path)
|
||||
print("✅ 下载完成")
|
||||
except Exception as e:
|
||||
print(f"❌ 下载失败: {str(e)}")
|
||||
print("\n📋 手动下载步骤:")
|
||||
print("1. 访问: https://github.com/AOMediaCodec/libavif/releases")
|
||||
print("2. 下载最新版本的avifenc (Windows x64)")
|
||||
print("3. 解压zip文件")
|
||||
print(f"4. 将avifenc.exe复制到: {os.path.join(sys.prefix, 'Scripts')}")
|
||||
print("5. 重启ComfyUI")
|
||||
return False
|
||||
|
||||
try:
|
||||
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
||||
zip_ref.extractall(temp_dir)
|
||||
print("✅ 解压完成")
|
||||
except Exception as e:
|
||||
print(f"❌ 解压失败: {str(e)}")
|
||||
return False
|
||||
|
||||
avifenc_dir = os.path.join(temp_dir, "bin")
|
||||
if not os.path.exists(avifenc_dir):
|
||||
avifenc_dir = temp_dir
|
||||
|
||||
scripts_dir = os.path.join(sys.prefix, "Scripts")
|
||||
if not os.path.exists(scripts_dir):
|
||||
scripts_dir = os.path.join(sys.prefix, "bin")
|
||||
|
||||
os.makedirs(scripts_dir, exist_ok=True)
|
||||
|
||||
avifenc_found = False
|
||||
for file in os.listdir(avifenc_dir):
|
||||
if file.startswith("avifenc"):
|
||||
src = os.path.join(avifenc_dir, file)
|
||||
dst = os.path.join(scripts_dir, file)
|
||||
shutil.copy2(src, dst)
|
||||
print(f"✅ 复制 {file} 到 {scripts_dir}")
|
||||
avifenc_found = True
|
||||
|
||||
if not avifenc_found:
|
||||
print(f"❌ 未找到avifenc文件在 {avifenc_dir}")
|
||||
print(f"📂 目录内容: {os.listdir(avifenc_dir)}")
|
||||
return False
|
||||
|
||||
avifenc_path = os.path.join(scripts_dir, "avifenc")
|
||||
if os.path.exists(avifenc_path):
|
||||
os.chmod(avifenc_path, 0o755)
|
||||
print(f"✅ avifenc安装成功!")
|
||||
print(f"📁 安装路径: {avifenc_path}")
|
||||
print(f"🔧 请确保 {scripts_dir} 在PATH中")
|
||||
|
||||
zip_dst = os.path.join(scripts_dir, "avifenc.zip")
|
||||
if os.path.exists(zip_dst):
|
||||
os.remove(zip_dst)
|
||||
print(f"✅ 删除临时文件: {zip_dst}")
|
||||
|
||||
return True
|
||||
else:
|
||||
print(f"❌ avifenc未找到在 {avifenc_dir}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 安装失败: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
install_avifenc()
|
||||
@@ -0,0 +1,220 @@
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import tempfile
|
||||
import shutil
|
||||
import numpy as np
|
||||
|
||||
try:
|
||||
import torch
|
||||
from PIL import Image
|
||||
import folder_paths
|
||||
except ImportError as e:
|
||||
print(f"❌ Import error: {e}")
|
||||
print("📋 Please ensure installed: pip install torch pillow")
|
||||
|
||||
def save_image(image, path):
|
||||
"""Save image to file"""
|
||||
try:
|
||||
img = image.squeeze().cpu().numpy()
|
||||
|
||||
if img.ndim == 3:
|
||||
if img.shape[0] == 1:
|
||||
img = img[0]
|
||||
elif img.shape[0] == 3:
|
||||
img = img.transpose(1, 2, 0)
|
||||
elif img.shape[0] == 4:
|
||||
img = img.transpose(1, 2, 0)
|
||||
|
||||
if img.ndim == 2:
|
||||
img = Image.fromarray((img * 255).astype('uint8'), mode='L')
|
||||
elif img.shape[2] == 3:
|
||||
img = Image.fromarray((img * 255).astype('uint8'), mode='RGB')
|
||||
elif img.shape[2] == 4:
|
||||
rgba_array = (img * 255).astype('uint8')
|
||||
img = Image.fromarray(rgba_array, mode='RGBA')
|
||||
|
||||
img.save(path)
|
||||
except Exception as e:
|
||||
print(f"❌ Error saving image {path}: {e}")
|
||||
raise
|
||||
|
||||
def clear_memory():
|
||||
"""Clear memory"""
|
||||
if torch.cuda.is_available():
|
||||
torch.cuda.empty_cache()
|
||||
torch.cuda.ipc_collect()
|
||||
|
||||
class AVIFAnimationNode:
|
||||
"""AVIF animation generation node - using avifenc"""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"images": ("IMAGE",),
|
||||
"fps": ("INT", {
|
||||
"default": 24,
|
||||
"min": 1,
|
||||
"max": 120,
|
||||
"step": 1,
|
||||
"display": "FPS"
|
||||
}),
|
||||
"quality": ("INT", {
|
||||
"default": 50,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"step": 1,
|
||||
"display": "Quality"
|
||||
}),
|
||||
"alpha_quality": ("INT", {
|
||||
"default": 60,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"step": 1,
|
||||
"display": "Alpha Quality"
|
||||
}),
|
||||
"speed": ("INT", {
|
||||
"default": 6,
|
||||
"min": 0,
|
||||
"max": 10,
|
||||
"step": 1,
|
||||
"display": "Encoding Speed"
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("avif_path",)
|
||||
FUNCTION = "images_to_avif"
|
||||
CATEGORY = "image/animation"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def find_avifenc(self):
|
||||
"""Find avifenc executable"""
|
||||
avifenc_paths = [
|
||||
os.path.join(sys.prefix, "Scripts", "avifenc.exe"),
|
||||
os.path.join(sys.prefix, "bin", "avifenc"),
|
||||
os.path.join(os.path.dirname(__file__), "avifenc.exe"),
|
||||
"avifenc",
|
||||
]
|
||||
|
||||
for path in avifenc_paths:
|
||||
if os.path.exists(path) or shutil.which(path):
|
||||
return path
|
||||
|
||||
return None
|
||||
|
||||
def images_to_avif(self, images, fps, quality, alpha_quality, speed):
|
||||
"""Convert image sequence to AVIF animation"""
|
||||
try:
|
||||
avifenc_path = self.find_avifenc()
|
||||
|
||||
if not avifenc_path:
|
||||
raise ValueError(
|
||||
"❌ avifenc not found!\n"
|
||||
"📋 Please install avifenc:\n"
|
||||
"1. Visit: https://github.com/AOMediaCodec/libavif/releases\n"
|
||||
"2. Download latest avifenc (Windows x64)\n"
|
||||
"3. Extract zip file\n"
|
||||
"4. Copy avifenc.exe to: " + os.path.join(sys.prefix, "Scripts") + "\n"
|
||||
"5. Restart ComfyUI"
|
||||
)
|
||||
|
||||
has_transparent_input = any(image.shape[-1] == 4 for image in images)
|
||||
|
||||
print(f"\n🔍 Input image analysis:")
|
||||
print(f" Total frames: {len(images)}")
|
||||
try:
|
||||
output_dir = folder_paths.output_directory
|
||||
except ImportError:
|
||||
import comfy
|
||||
output_dir = os.path.join(comfy.__path__[0], '../output')
|
||||
output_dir = os.path.abspath(output_dir)
|
||||
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
|
||||
try:
|
||||
count = 0
|
||||
frame_files = []
|
||||
|
||||
for image in images:
|
||||
temp_image_path = os.path.join(temp_dir, f"frame_{count:09d}.png")
|
||||
frame_files.append(temp_image_path)
|
||||
|
||||
try:
|
||||
save_image(image, temp_image_path)
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to save frame {count+1}: {e}")
|
||||
raise
|
||||
|
||||
count += 1
|
||||
|
||||
import time
|
||||
avif_filename = f"avif_animation_{time.strftime('%Y%m%d%H%M%S')}.avif"
|
||||
avif_path = os.path.join(output_dir, avif_filename)
|
||||
|
||||
print("\n⚙️ Starting AVIF animation generation:")
|
||||
print(f"📁 Output path: {avif_path}")
|
||||
print(f"🎨 Frames: {count}")
|
||||
print(f"⚡ FPS: {fps}")
|
||||
print(f"🎯 Quality: {quality}")
|
||||
print(f"🔍 Alpha Quality: {alpha_quality}")
|
||||
print(f"🚀 Encoding Speed: {speed}")
|
||||
print(f"🔧 avifenc path: {avifenc_path}")
|
||||
|
||||
cmd = [
|
||||
avifenc_path,
|
||||
'--fps', str(fps),
|
||||
'-q', str(quality),
|
||||
'--qalpha', str(alpha_quality),
|
||||
'-s', str(speed),
|
||||
]
|
||||
cmd.extend(frame_files)
|
||||
cmd.append(avif_path)
|
||||
|
||||
print(f"\n📋 Executing command: {' '.join(cmd[:5])} ... {len(frame_files)} frames -> {avif_path}")
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
print(f"❌ avifenc execution failed:")
|
||||
print(f"Standard output: {result.stdout}")
|
||||
print(f"Error output: {result.stderr}")
|
||||
raise ValueError(f"avifenc execution failed: {result.stderr}")
|
||||
|
||||
print(f"\n✅ AVIF animation generated successfully!")
|
||||
|
||||
file_size = os.path.getsize(avif_path) / 1024
|
||||
print(f"📊 Output file size: {file_size:.2f} KB")
|
||||
|
||||
if has_transparent_input:
|
||||
print("✅ Successfully generated AVIF file with transparency!")
|
||||
else:
|
||||
print("ℹ️ Generated AVIF file does not contain transparency")
|
||||
|
||||
ui_images = []
|
||||
if avif_path and os.path.exists(avif_path):
|
||||
avif_filename = os.path.basename(avif_path)
|
||||
ui_images.append({
|
||||
"filename": avif_filename,
|
||||
"subfolder": "",
|
||||
"type": "output"
|
||||
})
|
||||
|
||||
return (avif_path,), {"ui": {"images": ui_images}}
|
||||
|
||||
finally:
|
||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
raise ValueError(f"Error during processing: {str(e)}")
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"AVIF Animation (avifenc)": AVIFAnimationNode
|
||||
}
|
||||
Reference in New Issue
Block a user