项目重命名: ReelForge => Pixelle-Video

This commit is contained in:
puke
2025-10-31 10:23:38 +08:00
parent dfdba73b74
commit 136514e466
60 changed files with 384 additions and 383 deletions

View File

@@ -1,11 +1,11 @@
<div align="center">
<h1 align="center">ReelForge 🎬</h1>
<h1 align="center">Pixelle-Video 🎬</h1>
<p align="center">
<a href="https://github.com/JarvisAIHub/ReelForge/stargazers"><img src="https://img.shields.io/github/stars/JarvisAIHub/ReelForge.svg?style=for-the-badge" alt="Stargazers"></a>
<a href="https://github.com/JarvisAIHub/ReelForge/issues"><img src="https://img.shields.io/github/issues/JarvisAIHub/ReelForge.svg?style=for-the-badge" alt="Issues"></a>
<a href="https://github.com/JarvisAIHub/ReelForge/network/members"><img src="https://img.shields.io/github/forks/JarvisAIHub/ReelForge.svg?style=for-the-badge" alt="Forks"></a>
<a href="https://github.com/JarvisAIHub/ReelForge/blob/main/LICENSE"><img src="https://img.shields.io/github/license/JarvisAIHub/ReelForge.svg?style=for-the-badge" alt="License"></a>
<a href="https://github.com/PixelleLab/Pixelle-Video/stargazers"><img src="https://img.shields.io/github/stars/PixelleLab/Pixelle-Video.svg?style=for-the-badge" alt="Stargazers"></a>
<a href="https://github.com/PixelleLab/Pixelle-Video/issues"><img src="https://img.shields.io/github/issues/PixelleLab/Pixelle-Video.svg?style=for-the-badge" alt="Issues"></a>
<a href="https://github.com/PixelleLab/Pixelle-Video/network/members"><img src="https://img.shields.io/github/forks/PixelleLab/Pixelle-Video.svg?style=for-the-badge" alt="Forks"></a>
<a href="https://github.com/PixelleLab/Pixelle-Video/blob/main/LICENSE"><img src="https://img.shields.io/github/license/PixelleLab/Pixelle-Video.svg?style=for-the-badge" alt="License"></a>
</p>
<br>
@@ -14,7 +14,7 @@
<br>
只需输入一个 **主题**ReelForge 就能自动完成:
只需输入一个 **主题**Pixelle-Video 就能自动完成:
- ✍️ 撰写视频文案
- 🎨 生成 AI 配图
- 🗣️ 合成语音解说
@@ -54,8 +54,8 @@
### 第一步:下载项目
```bash
git clone https://github.com/JarvisAIHub/ReelForge.git
cd ReelForge
git clone https://github.com/PixelleLab/Pixelle-Video.git
cd Pixelle-Video
```
### 第二步:启动 Web 界面
@@ -207,7 +207,7 @@ A: **本项目完全支持免费运行!**
## 🤝 参考项目
ReelForge 的设计受到以下优秀开源项目的启发:
Pixelle-Video 的设计受到以下优秀开源项目的启发:
- [Pixelle-MCP](https://github.com/AIDC-AI/Pixelle-MCP) - ComfyUI MCP 服务器,让 AI 助手直接调用 ComfyUI
- [MoneyPrinterTurbo](https://github.com/harry0703/MoneyPrinterTurbo) - 优秀的视频生成工具
@@ -221,8 +221,8 @@ ReelForge 的设计受到以下优秀开源项目的启发:
## 📢 反馈与支持
- 🐛 **遇到问题**: 提交 [Issue](https://github.com/JarvisAIHub/ReelForge/issues)
- 💡 **功能建议**: 提交 [Feature Request](https://github.com/JarvisAIHub/ReelForge/issues)
- 🐛 **遇到问题**: 提交 [Issue](https://github.com/PixelleLab/Pixelle-Video/issues)
- 💡 **功能建议**: 提交 [Feature Request](https://github.com/PixelleLab/Pixelle-Video/issues)
-**给个 Star**: 如果这个项目对你有帮助,欢迎给个 Star 支持一下!
---
@@ -235,12 +235,12 @@ ReelForge 的设计受到以下优秀开源项目的启发:
## ⭐ Star History
[![Star History Chart](https://api.star-history.com/svg?repos=JarvisAIHub/ReelForge&type=Date)](https://star-history.com/#JarvisAIHub/ReelForge&Date)
[![Star History Chart](https://api.star-history.com/svg?repos=PixelleLab/Pixelle-Video&type=Date)](https://star-history.com/#PixelleLab/Pixelle-Video&Date)
---
<div align="center">
<p>Made with ❤️ by JarvisAIHub</p>
<p>Made with ❤️ by PixelleLab</p>
<p>
<a href="#top">回到顶部 ⬆️</a>
</p>

View File

@@ -1,5 +1,5 @@
"""
ReelForge API Layer
Pixelle-Video API Layer
FastAPI-based REST API for video generation services.
"""

View File

@@ -1,5 +1,5 @@
"""
ReelForge FastAPI Application
Pixelle-Video FastAPI Application
Main FastAPI app with all routers and middleware.
@@ -18,7 +18,7 @@ from loguru import logger
from api.config import api_config
from api.tasks import task_manager
from api.dependencies import shutdown_reelforge
from api.dependencies import shutdown_pixelle_video
# Import routers
from api.routers import (
@@ -41,24 +41,24 @@ async def lifespan(app: FastAPI):
Handles startup and shutdown events.
"""
# Startup
logger.info("🚀 Starting ReelForge API...")
logger.info("🚀 Starting Pixelle-Video API...")
await task_manager.start()
logger.info("ReelForge API started successfully\n")
logger.info("Pixelle-Video API started successfully\n")
yield
# Shutdown
logger.info("🛑 Shutting down ReelForge API...")
logger.info("🛑 Shutting down Pixelle-Video API...")
await task_manager.stop()
await shutdown_reelforge()
logger.info("ReelForge API shutdown complete")
await shutdown_pixelle_video()
logger.info("Pixelle-Video API shutdown complete")
# Create FastAPI app
app = FastAPI(
title="ReelForge API",
title="Pixelle-Video API",
description="""
## ReelForge - AI Video Generation Platform API
## Pixelle-Video - AI Video Generation Platform API
### Features
- 🤖 **LLM**: Large language model integration
@@ -113,7 +113,7 @@ app.include_router(files_router, prefix=api_config.api_prefix)
async def root():
"""Root endpoint with API information"""
return {
"service": "ReelForge API",
"service": "Pixelle-Video API",
"version": "0.1.0",
"docs": api_config.docs_url,
"health": "/health",
@@ -132,7 +132,7 @@ if __name__ == "__main__":
import uvicorn
# Parse command line arguments
parser = argparse.ArgumentParser(description="Start ReelForge API Server")
parser = argparse.ArgumentParser(description="Start Pixelle-Video API Server")
parser.add_argument("--host", default="0.0.0.0", help="Host to bind to")
parser.add_argument("--port", type=int, default=8000, help="Port to bind to")
parser.add_argument("--reload", action="store_true", help="Enable auto-reload")
@@ -142,7 +142,7 @@ if __name__ == "__main__":
# Print startup banner
print(f"""
╔══════════════════════════════════════════════════════════════╗
ReelForge API Server ║
Pixelle-Video API Server ║
╚══════════════════════════════════════════════════════════════╝
Starting server at http://{args.host}:{args.port}

View File

@@ -1,45 +1,45 @@
"""
FastAPI Dependencies
Provides dependency injection for ReelForgeCore and other services.
Provides dependency injection for PixelleVideoCore and other services.
"""
from typing import Annotated
from fastapi import Depends
from loguru import logger
from reelforge.service import ReelForgeCore
from pixelle_video.service import PixelleVideoCore
# Global ReelForge instance
_reelforge_instance: ReelForgeCore = None
# Global Pixelle-Video instance
_pixelle_video_instance: PixelleVideoCore = None
async def get_reelforge() -> ReelForgeCore:
async def get_pixelle_video() -> PixelleVideoCore:
"""
Get ReelForge core instance (dependency injection)
Get Pixelle-Video core instance (dependency injection)
Returns:
ReelForgeCore instance
PixelleVideoCore instance
"""
global _reelforge_instance
global _pixelle_video_instance
if _reelforge_instance is None:
_reelforge_instance = ReelForgeCore()
await _reelforge_instance.initialize()
logger.info("ReelForge initialized for API")
if _pixelle_video_instance is None:
_pixelle_video_instance = PixelleVideoCore()
await _pixelle_video_instance.initialize()
logger.info("Pixelle-Video initialized for API")
return _reelforge_instance
return _pixelle_video_instance
async def shutdown_reelforge():
"""Shutdown ReelForge instance"""
global _reelforge_instance
if _reelforge_instance:
logger.info("Shutting down ReelForge...")
_reelforge_instance = None
async def shutdown_pixelle_video():
"""Shutdown Pixelle-Video instance"""
global _pixelle_video_instance
if _pixelle_video_instance:
logger.info("Shutting down Pixelle-Video...")
_pixelle_video_instance = None
# Type alias for dependency injection
ReelForgeDep = Annotated[ReelForgeCore, Depends(get_reelforge)]
PixelleVideoDep = Annotated[PixelleVideoCore, Depends(get_pixelle_video)]

View File

@@ -7,7 +7,7 @@ Endpoints for generating narrations, image prompts, and titles.
from fastapi import APIRouter, HTTPException
from loguru import logger
from api.dependencies import ReelForgeDep
from api.dependencies import PixelleVideoDep
from api.schemas.content import (
NarrationGenerateRequest,
NarrationGenerateResponse,
@@ -23,7 +23,7 @@ router = APIRouter(prefix="/content", tags=["Content Generation"])
@router.post("/narration", response_model=NarrationGenerateResponse)
async def generate_narration(
request: NarrationGenerateRequest,
reelforge: ReelForgeDep
pixelle_video: PixelleVideoDep
):
"""
Generate narrations from text
@@ -41,7 +41,7 @@ async def generate_narration(
logger.info(f"Generating {request.n_scenes} narrations from text")
# Call narration generator service
narrations = await reelforge.narration_generator(
narrations = await pixelle_video.narration_generator(
text=request.text,
n_scenes=request.n_scenes,
min_words=request.min_words,
@@ -60,7 +60,7 @@ async def generate_narration(
@router.post("/image-prompt", response_model=ImagePromptGenerateResponse)
async def generate_image_prompt(
request: ImagePromptGenerateRequest,
reelforge: ReelForgeDep
pixelle_video: PixelleVideoDep
):
"""
Generate image prompts from narrations
@@ -77,7 +77,7 @@ async def generate_image_prompt(
logger.info(f"Generating image prompts for {len(request.narrations)} narrations")
# Call image prompt generator service
image_prompts = await reelforge.image_prompt_generator(
image_prompts = await pixelle_video.image_prompt_generator(
narrations=request.narrations,
min_words=request.min_words,
max_words=request.max_words
@@ -95,7 +95,7 @@ async def generate_image_prompt(
@router.post("/title", response_model=TitleGenerateResponse)
async def generate_title(
request: TitleGenerateRequest,
reelforge: ReelForgeDep
pixelle_video: PixelleVideoDep
):
"""
Generate video title from text
@@ -111,7 +111,7 @@ async def generate_title(
logger.info("Generating title from text")
# Call title generator service
title = await reelforge.title_generator(
title = await pixelle_video.title_generator(
text=request.text
)

View File

@@ -12,7 +12,7 @@ class HealthResponse(BaseModel):
"""Health check response"""
status: str = "healthy"
version: str = "0.1.0"
service: str = "ReelForge API"
service: str = "Pixelle-Video API"
class CapabilitiesResponse(BaseModel):

View File

@@ -5,7 +5,7 @@ Image generation endpoints
from fastapi import APIRouter, HTTPException
from loguru import logger
from api.dependencies import ReelForgeDep
from api.dependencies import PixelleVideoDep
from api.schemas.image import ImageGenerateRequest, ImageGenerateResponse
router = APIRouter(prefix="/image", tags=["Image"])
@@ -14,7 +14,7 @@ router = APIRouter(prefix="/image", tags=["Image"])
@router.post("/generate", response_model=ImageGenerateResponse)
async def image_generate(
request: ImageGenerateRequest,
reelforge: ReelForgeDep
pixelle_video: PixelleVideoDep
):
"""
Image generation endpoint
@@ -32,7 +32,7 @@ async def image_generate(
logger.info(f"Image generation request: {request.prompt[:50]}...")
# Call image service
image_path = await reelforge.image(
image_path = await pixelle_video.image(
prompt=request.prompt,
width=request.width,
height=request.height,

View File

@@ -5,7 +5,7 @@ LLM (Large Language Model) endpoints
from fastapi import APIRouter, HTTPException
from loguru import logger
from api.dependencies import ReelForgeDep
from api.dependencies import PixelleVideoDep
from api.schemas.llm import LLMChatRequest, LLMChatResponse
router = APIRouter(prefix="/llm", tags=["LLM"])
@@ -14,7 +14,7 @@ router = APIRouter(prefix="/llm", tags=["LLM"])
@router.post("/chat", response_model=LLMChatResponse)
async def llm_chat(
request: LLMChatRequest,
reelforge: ReelForgeDep
pixelle_video: PixelleVideoDep
):
"""
LLM chat endpoint
@@ -31,7 +31,7 @@ async def llm_chat(
logger.info(f"LLM chat request: {request.prompt[:50]}...")
# Call LLM service
response = await reelforge.llm(
response = await pixelle_video.llm(
prompt=request.prompt,
temperature=request.temperature,
max_tokens=request.max_tokens

View File

@@ -5,9 +5,9 @@ TTS (Text-to-Speech) endpoints
from fastapi import APIRouter, HTTPException
from loguru import logger
from api.dependencies import ReelForgeDep
from api.dependencies import PixelleVideoDep
from api.schemas.tts import TTSSynthesizeRequest, TTSSynthesizeResponse
from reelforge.utils.tts_util import get_audio_duration
from pixelle_video.utils.tts_util import get_audio_duration
router = APIRouter(prefix="/tts", tags=["TTS"])
@@ -15,7 +15,7 @@ router = APIRouter(prefix="/tts", tags=["TTS"])
@router.post("/synthesize", response_model=TTSSynthesizeResponse)
async def tts_synthesize(
request: TTSSynthesizeRequest,
reelforge: ReelForgeDep
pixelle_video: PixelleVideoDep
):
"""
Text-to-Speech synthesis endpoint
@@ -31,7 +31,7 @@ async def tts_synthesize(
logger.info(f"TTS synthesis request: {request.text[:50]}...")
# Call TTS service
audio_path = await reelforge.tts(
audio_path = await pixelle_video.tts(
text=request.text,
voice_id=request.voice_id
)

View File

@@ -8,7 +8,7 @@ import os
from fastapi import APIRouter, HTTPException, Request
from loguru import logger
from api.dependencies import ReelForgeDep
from api.dependencies import PixelleVideoDep
from api.schemas.video import (
VideoGenerateRequest,
VideoGenerateResponse,
@@ -32,7 +32,7 @@ def path_to_url(request: Request, file_path: str) -> str:
@router.post("/generate/sync", response_model=VideoGenerateResponse)
async def generate_video_sync(
request_body: VideoGenerateRequest,
reelforge: ReelForgeDep,
pixelle_video: PixelleVideoDep,
request: Request
):
"""
@@ -52,7 +52,7 @@ async def generate_video_sync(
logger.info(f"Sync video generation: {request_body.text[:50]}...")
# Call video generator service
result = await reelforge.generate_video(
result = await pixelle_video.generate_video(
text=request_body.text,
mode=request_body.mode,
title=request_body.title,
@@ -94,7 +94,7 @@ async def generate_video_sync(
@router.post("/generate/async", response_model=VideoGenerateAsyncResponse)
async def generate_video_async(
request_body: VideoGenerateRequest,
reelforge: ReelForgeDep,
pixelle_video: PixelleVideoDep,
request: Request
):
"""
@@ -126,7 +126,7 @@ async def generate_video_async(
# Define async execution function
async def execute_video_generation():
"""Execute video generation in background"""
result = await reelforge.generate_video(
result = await pixelle_video.generate_video(
text=request_body.text,
mode=request_body.mode,
title=request_body.title,

View File

@@ -13,7 +13,7 @@ class TTSSynthesizeRequest(BaseModel):
class Config:
json_schema_extra = {
"example": {
"text": "Hello, welcome to ReelForge!",
"text": "Hello, welcome to Pixelle-Video!",
"voice_id": "[Chinese] zh-CN Yunjian"
}
}

View File

@@ -1,8 +1,8 @@
# ReelForge Configuration
# Pixelle-Video Configuration
# Copy this file to config.yaml and fill in your settings
# ⚠️ Never commit config.yaml to Git!
project_name: ReelForge
project_name: Pixelle-Video
# ==================== LLM Configuration ====================
# Supports any OpenAI SDK compatible API

View File

@@ -1,10 +1,10 @@
# ReelForge Capabilities Guide
# Pixelle-Video Capabilities Guide
> Complete guide to using LLM, TTS, and Image generation capabilities
## Overview
ReelForge provides three core AI capabilities:
Pixelle-Video provides three core AI capabilities:
- **LLM**: Text generation using LiteLLM (supports 100+ models)
- **TTS**: Text-to-speech using Edge TTS (free, 400+ voices)
- **Image**: Image generation using ComfyKit (local or cloud)
@@ -12,16 +12,16 @@ ReelForge provides three core AI capabilities:
## Quick Start
```python
from reelforge.service import reelforge
from pixelle_video.service import pixelle_video
# LLM - Generate text
answer = await reelforge.llm("Summarize 'Atomic Habits' in 3 sentences")
answer = await pixelle_video.llm("Summarize 'Atomic Habits' in 3 sentences")
# TTS - Generate speech
audio_path = await reelforge.tts("Hello, world!")
audio_path = await pixelle_video.tts("Hello, world!")
# Image - Generate images
image_url = await reelforge.image(
image_url = await pixelle_video.image(
workflow="workflows/book_cover_simple.json",
prompt="minimalist book cover design"
)
@@ -62,10 +62,10 @@ llm:
```python
# Basic usage
answer = await reelforge.llm("What is machine learning?")
answer = await pixelle_video.llm("What is machine learning?")
# With parameters
answer = await reelforge.llm(
answer = await pixelle_video.llm(
prompt="Explain atomic habits",
temperature=0.7, # 0.0-2.0 (lower = more deterministic)
max_tokens=2000
@@ -107,18 +107,18 @@ tts:
```python
# Basic usage (auto-generates temp path)
audio_path = await reelforge.tts("Hello, world!")
audio_path = await pixelle_video.tts("Hello, world!")
# Returns: "temp/abc123def456.mp3"
# With Chinese text
audio_path = await reelforge.tts(
audio_path = await pixelle_video.tts(
text="你好,世界!",
voice="zh-CN-YunjianNeural"
)
# With custom parameters
audio_path = await reelforge.tts(
text="Welcome to ReelForge",
audio_path = await pixelle_video.tts(
text="Welcome to Pixelle-Video",
voice="en-US-JennyNeural",
rate="+20%", # Speed: +50% = faster, -20% = slower
volume="+0%",
@@ -126,7 +126,7 @@ audio_path = await reelforge.tts(
)
# Specify output path
audio_path = await reelforge.tts(
audio_path = await pixelle_video.tts(
text="Hello",
output_path="output/greeting.mp3"
)
@@ -149,13 +149,13 @@ audio_path = await reelforge.tts(
```python
# Get all available voices
voices = await reelforge.tts.list_voices()
voices = await pixelle_video.tts.list_voices()
# Get Chinese voices only
voices = await reelforge.tts.list_voices(locale="zh-CN")
voices = await pixelle_video.tts.list_voices(locale="zh-CN")
# Get English voices only
voices = await reelforge.tts.list_voices(locale="en-US")
voices = await pixelle_video.tts.list_voices(locale="en-US")
```
---
@@ -182,13 +182,13 @@ image:
```python
# Basic usage (local ComfyUI)
image_url = await reelforge.image(
image_url = await pixelle_video.image(
workflow="workflows/book_cover_simple.json",
prompt="minimalist book cover design, blue and white"
)
# With full parameters
image_url = await reelforge.image(
image_url = await pixelle_video.image(
workflow="workflows/book_cover_simple.json",
prompt="book cover for 'Atomic Habits', professional, minimalist",
negative_prompt="ugly, blurry, low quality",
@@ -199,13 +199,13 @@ image_url = await reelforge.image(
)
# Using RunningHub cloud
image_url = await reelforge.image(
image_url = await pixelle_video.image(
workflow="12345", # RunningHub workflow ID
prompt="a beautiful landscape"
)
# Check available workflows
workflows = reelforge.image.list_workflows()
workflows = pixelle_video.image.list_workflows()
print(f"Available workflows: {workflows}")
```
@@ -221,7 +221,7 @@ export RUNNINGHUB_API_KEY="rh-key-xxx"
### Workflow DSL
ReelForge uses ComfyKit's DSL for workflow parameters:
Pixelle-Video uses ComfyKit's DSL for workflow parameters:
```json
{
@@ -252,27 +252,27 @@ Generate a complete book cover with narration:
```python
import asyncio
from reelforge.service import reelforge
from pixelle_video.service import pixelle_video
async def create_book_content(book_title, author):
"""Generate book summary, audio, and cover image"""
# 1. Generate book summary with LLM
summary = await reelforge.llm(
summary = await pixelle_video.llm(
prompt=f"Write a compelling 2-sentence summary for a book titled '{book_title}' by {author}",
max_tokens=100
)
print(f"Summary: {summary}")
# 2. Generate audio narration with TTS
audio_path = await reelforge.tts(
audio_path = await pixelle_video.tts(
text=summary,
voice="en-US-JennyNeural"
)
print(f"Audio: {audio_path}")
# 3. Generate book cover image
image_url = await reelforge.image(
image_url = await pixelle_video.image(
workflow="workflows/book_cover_simple.json",
prompt=f"book cover for '{book_title}' by {author}, professional, modern design",
width=1024,
@@ -339,5 +339,5 @@ result = asyncio.run(create_book_content("Atomic Habits", "James Clear"))
---
**Happy creating with ReelForge!** 📚🎬
**Happy creating with Pixelle-Video!** 📚🎬

26
pixelle_video/__init__.py Normal file
View File

@@ -0,0 +1,26 @@
"""
Pixelle-Video - AI-powered video generator
Convention-based system with unified configuration management.
Usage:
from pixelle_video import pixelle_video
# Initialize
await pixelle_video.initialize()
# Use capabilities
answer = await pixelle_video.llm("Explain atomic habits")
audio = await pixelle_video.tts("Hello world")
# Generate video
result = await pixelle_video.generate_video(topic="AI in 2024")
"""
from pixelle_video.service import PixelleVideoCore, pixelle_video
from pixelle_video.config import config_manager
__version__ = "0.1.0"
__all__ = ["PixelleVideoCore", "pixelle_video", "config_manager"]

View File

@@ -1,18 +1,18 @@
"""
ReelForge CLI
Pixelle-Video CLI
"""
import asyncio
from loguru import logger
from reelforge.service import reelforge
from pixelle_video.service import pixelle_video
async def test_llm():
"""Test LLM capability"""
# Initialize reelforge
await reelforge.initialize()
# Initialize pixelle_video
await pixelle_video.initialize()
# Test prompt
prompt = "Explain the concept of atomic habits in 3 sentences."
@@ -20,14 +20,14 @@ async def test_llm():
logger.info(f"\n📝 Test Prompt: {prompt}\n")
# Call LLM
result = await reelforge.llm(prompt)
result = await pixelle_video.llm(prompt)
logger.info(f"\n✨ Result:\n{result}\n")
def main():
"""Main CLI entry point"""
logger.info("🚀 ReelForge CLI\n")
logger.info("🚀 Pixelle-Video CLI\n")
# Run test
asyncio.run(test_llm())

View File

@@ -1,10 +1,10 @@
"""
ReelForge Configuration System
Pixelle-Video Configuration System
Unified configuration management with Pydantic validation.
Usage:
from reelforge.config import config_manager
from pixelle_video.config import config_manager
# Access config (type-safe)
api_key = config_manager.config.llm.api_key
@@ -17,7 +17,7 @@ Usage:
if config_manager.validate():
print("Config is valid!")
"""
from .schema import ReelForgeConfig, LLMConfig, ComfyUIConfig, TTSSubConfig, ImageSubConfig
from .schema import PixelleVideoConfig, LLMConfig, ComfyUIConfig, TTSSubConfig, ImageSubConfig
from .manager import ConfigManager
from .loader import load_config_dict, save_config_dict
@@ -25,7 +25,7 @@ from .loader import load_config_dict, save_config_dict
config_manager = ConfigManager()
__all__ = [
"ReelForgeConfig",
"PixelleVideoConfig",
"LLMConfig",
"ComfyUIConfig",
"TTSSubConfig",

View File

@@ -6,7 +6,7 @@ Provides unified access to configuration with automatic validation.
from pathlib import Path
from typing import Any, Optional
from loguru import logger
from .schema import ReelForgeConfig
from .schema import PixelleVideoConfig
from .loader import load_config_dict, save_config_dict
@@ -29,13 +29,13 @@ class ConfigManager:
return
self.config_path = Path(config_path)
self.config: ReelForgeConfig = self._load()
self.config: PixelleVideoConfig = self._load()
self._initialized = True
def _load(self) -> ReelForgeConfig:
def _load(self) -> PixelleVideoConfig:
"""Load configuration from file"""
data = load_config_dict(str(self.config_path))
return ReelForgeConfig(**data)
return PixelleVideoConfig(**data)
def reload(self):
"""Reload configuration from file"""
@@ -65,7 +65,7 @@ class ConfigManager:
return base
merged = deep_merge(current, updates)
self.config = ReelForgeConfig(**merged)
self.config = PixelleVideoConfig(**merged)
def get(self, key: str, default: Any = None) -> Any:
"""Dict-like access (for backward compatibility)"""

View File

@@ -35,9 +35,9 @@ class ComfyUIConfig(BaseModel):
image: ImageSubConfig = Field(default_factory=ImageSubConfig, description="Image-specific configuration")
class ReelForgeConfig(BaseModel):
"""ReelForge main configuration"""
project_name: str = Field(default="ReelForge", description="Project name")
class PixelleVideoConfig(BaseModel):
"""Pixelle-Video main configuration"""
project_name: str = Field(default="Pixelle-Video", description="Project name")
llm: LLMConfig = Field(default_factory=LLMConfig)
comfyui: ComfyUIConfig = Field(default_factory=ComfyUIConfig)

View File

@@ -1,6 +1,6 @@
# Prompts Directory
Centralized prompt management for all LLM interactions in ReelForge.
Centralized prompt management for all LLM interactions in Pixelle-Video.
## Structure
@@ -22,7 +22,7 @@ prompts/
All builder functions are exported from the package root:
```python
from reelforge.prompts import (
from pixelle_video.prompts import (
build_topic_narration_prompt,
build_content_narration_prompt,
build_script_split_prompt,
@@ -86,7 +86,7 @@ To add a new prompt:
3. Export the builder function in `__init__.py`
4. Use it in service code:
```python
from reelforge.prompts import build_my_new_prompt
from pixelle_video.prompts import build_my_new_prompt
```
## Design Principles

View File

@@ -5,17 +5,17 @@ Centralized prompt management for all LLM interactions.
"""
# Narration prompts
from reelforge.prompts.topic_narration import build_topic_narration_prompt
from reelforge.prompts.content_narration import build_content_narration_prompt
from reelforge.prompts.title_generation import build_title_generation_prompt
from pixelle_video.prompts.topic_narration import build_topic_narration_prompt
from pixelle_video.prompts.content_narration import build_content_narration_prompt
from pixelle_video.prompts.title_generation import build_title_generation_prompt
# Image prompts
from reelforge.prompts.image_generation import (
from pixelle_video.prompts.image_generation import (
build_image_prompt_prompt,
IMAGE_STYLE_PRESETS,
DEFAULT_IMAGE_STYLE
)
from reelforge.prompts.style_conversion import build_style_conversion_prompt
from pixelle_video.prompts.style_conversion import build_style_conversion_prompt
__all__ = [

View File

@@ -1,5 +1,5 @@
"""
ReelForge Core - Service Layer
Pixelle-Video Core - Service Layer
Provides unified access to all capabilities (LLM, TTS, Image, etc.)
"""
@@ -8,40 +8,40 @@ from typing import Optional
from loguru import logger
from reelforge.config import config_manager
from reelforge.services.llm_service import LLMService
from reelforge.services.tts_service import TTSService
from reelforge.services.image import ImageService
from reelforge.services.narration_generator import NarrationGeneratorService
from reelforge.services.image_prompt_generator import ImagePromptGeneratorService
from reelforge.services.title_generator import TitleGeneratorService
from reelforge.services.frame_processor import FrameProcessor
from reelforge.services.video_generator import VideoGeneratorService
from pixelle_video.config import config_manager
from pixelle_video.services.llm_service import LLMService
from pixelle_video.services.tts_service import TTSService
from pixelle_video.services.image import ImageService
from pixelle_video.services.narration_generator import NarrationGeneratorService
from pixelle_video.services.image_prompt_generator import ImagePromptGeneratorService
from pixelle_video.services.title_generator import TitleGeneratorService
from pixelle_video.services.frame_processor import FrameProcessor
from pixelle_video.services.video_generator import VideoGeneratorService
class ReelForgeCore:
class PixelleVideoCore:
"""
ReelForge Core - Service Layer
Pixelle-Video Core - Service Layer
Provides unified access to all capabilities.
Usage:
from reelforge import reelforge
from pixelle_video import pixelle_video
# Initialize
await reelforge.initialize()
await pixelle_video.initialize()
# Use capabilities directly
answer = await reelforge.llm("Explain atomic habits")
audio = await reelforge.tts("Hello world")
image = await reelforge.image(prompt="a cat")
answer = await pixelle_video.llm("Explain atomic habits")
audio = await pixelle_video.tts("Hello world")
image = await pixelle_video.image(prompt="a cat")
# Check active capabilities
print(f"Using LLM: {reelforge.llm.active}")
print(f"Available TTS: {reelforge.tts.available}")
print(f"Using LLM: {pixelle_video.llm.active}")
print(f"Available TTS: {pixelle_video.tts.available}")
Architecture (Simplified):
ReelForgeCore (this class)
PixelleVideoCore (this class)
config (configuration)
llm (LLM service - direct OpenAI SDK)
tts (TTS service - ComfyKit workflows)
@@ -50,7 +50,7 @@ class ReelForgeCore:
def __init__(self, config_path: str = "config.yaml"):
"""
Initialize ReelForge Core
Initialize Pixelle-Video Core
Args:
config_path: Path to configuration file
@@ -82,13 +82,13 @@ class ReelForgeCore:
This initializes all services and must be called before using any capabilities.
Example:
await reelforge.initialize()
await pixelle_video.initialize()
"""
if self._initialized:
logger.warning("ReelForge already initialized")
logger.warning("Pixelle-Video already initialized")
return
logger.info("🚀 Initializing ReelForge...")
logger.info("🚀 Initializing Pixelle-Video...")
# 1. Initialize core services (no capability layer)
self.llm = LLMService(self.config)
@@ -107,18 +107,18 @@ class ReelForgeCore:
self.generate_video = VideoGeneratorService(self)
self._initialized = True
logger.info("ReelForge initialized successfully\n")
logger.info("Pixelle-Video initialized successfully\n")
@property
def project_name(self) -> str:
"""Get project name from config"""
return self.config.get("project_name", "ReelForge")
return self.config.get("project_name", "Pixelle-Video")
def __repr__(self) -> str:
"""String representation"""
status = "initialized" if self._initialized else "not initialized"
return f"<ReelForgeCore project={self.project_name!r} status={status}>"
return f"<PixelleVideoCore project={self.project_name!r} status={status}>"
# Global instance
reelforge = ReelForgeCore()
pixelle_video = PixelleVideoCore()

View File

@@ -0,0 +1,30 @@
"""
Pixelle-Video Services
Unified service layer providing simplified access to capabilities.
"""
from pixelle_video.services.comfy_base_service import ComfyBaseService
from pixelle_video.services.llm_service import LLMService
from pixelle_video.services.tts_service import TTSService
from pixelle_video.services.image import ImageService
from pixelle_video.services.video import VideoService
from pixelle_video.services.narration_generator import NarrationGeneratorService
from pixelle_video.services.image_prompt_generator import ImagePromptGeneratorService
from pixelle_video.services.title_generator import TitleGeneratorService
from pixelle_video.services.frame_processor import FrameProcessor
from pixelle_video.services.video_generator import VideoGeneratorService
__all__ = [
"ComfyBaseService",
"LLMService",
"TTSService",
"ImageService",
"VideoService",
"NarrationGeneratorService",
"ImagePromptGeneratorService",
"TitleGeneratorService",
"FrameProcessor",
"VideoGeneratorService",
]

View File

@@ -253,7 +253,7 @@ class HTMLFrameGenerator:
# Use provided output path or auto-generate
if output_path is None:
# Fallback: auto-generate (for backward compatibility)
from reelforge.utils.os_util import get_output_path
from pixelle_video.utils.os_util import get_output_path
output_filename = f"frame_{uuid.uuid4().hex[:16]}.png"
output_path = get_output_path(output_filename)
else:

View File

@@ -9,21 +9,21 @@ from typing import Callable, Optional
import httpx
from loguru import logger
from reelforge.models.progress import ProgressEvent
from reelforge.models.storyboard import Storyboard, StoryboardFrame, StoryboardConfig
from pixelle_video.models.progress import ProgressEvent
from pixelle_video.models.storyboard import Storyboard, StoryboardFrame, StoryboardConfig
class FrameProcessor:
"""Frame processor"""
def __init__(self, reelforge_core):
def __init__(self, pixelle_video_core):
"""
Initialize
Args:
reelforge_core: ReelForgeCore instance
pixelle_video_core: PixelleVideoCore instance
"""
self.core = reelforge_core
self.core = pixelle_video_core
async def __call__(
self,
@@ -121,7 +121,7 @@ class FrameProcessor:
logger.debug(f" 1/4: Generating audio for frame {frame.index}...")
# Generate output path using task_id
from reelforge.utils.os_util import get_task_frame_path
from pixelle_video.utils.os_util import get_task_frame_path
output_path = get_task_frame_path(config.task_id, frame.index, "audio")
# Call TTS with specific output path and workflow
@@ -172,7 +172,7 @@ class FrameProcessor:
logger.debug(f" 3/4: Composing frame {frame.index}...")
# Generate output path using task_id
from reelforge.utils.os_util import get_task_frame_path
from pixelle_video.utils.os_util import get_task_frame_path
output_path = get_task_frame_path(config.task_id, frame.index, "composed")
# Use HTML template to compose frame
@@ -190,7 +190,7 @@ class FrameProcessor:
output_path: str
) -> str:
"""Compose frame using HTML template"""
from reelforge.services.frame_html import HTMLFrameGenerator
from pixelle_video.services.frame_html import HTMLFrameGenerator
from pathlib import Path
# Resolve template path
@@ -241,11 +241,11 @@ class FrameProcessor:
logger.debug(f" 4/4: Creating video segment for frame {frame.index}...")
# Generate output path using task_id
from reelforge.utils.os_util import get_task_frame_path
from pixelle_video.utils.os_util import get_task_frame_path
output_path = get_task_frame_path(config.task_id, frame.index, "segment")
# Call video compositor to create video from image + audio
from reelforge.services.video import VideoService
from pixelle_video.services.video import VideoService
video_service = VideoService()
segment_path = video_service.create_video_from_image(
@@ -278,7 +278,7 @@ class FrameProcessor:
async def _download_image(self, url: str, frame_index: int, task_id: str) -> str:
"""Download image from URL to local file"""
from reelforge.utils.os_util import get_task_frame_path
from pixelle_video.utils.os_util import get_task_frame_path
output_path = get_task_frame_path(task_id, frame_index, "image")
async with httpx.AsyncClient() as client:

View File

@@ -7,7 +7,7 @@ from typing import Optional
from comfykit import ComfyKit
from loguru import logger
from reelforge.services.comfy_base_service import ComfyBaseService
from pixelle_video.services.comfy_base_service import ComfyBaseService
class ImageService(ComfyBaseService):
@@ -18,16 +18,16 @@ class ImageService(ComfyBaseService):
Usage:
# Use default workflow (workflows/image_flux.json)
image_url = await reelforge.image(prompt="a cat")
image_url = await pixelle_video.image(prompt="a cat")
# Use specific workflow
image_url = await reelforge.image(
image_url = await pixelle_video.image(
prompt="a cat",
workflow="image_flux.json"
)
# List available workflows
workflows = reelforge.image.list_workflows()
workflows = pixelle_video.image.list_workflows()
"""
WORKFLOW_PREFIX = "image_"
@@ -82,16 +82,16 @@ class ImageService(ComfyBaseService):
Examples:
# Simplest: use default workflow (workflows/image_flux.json)
image_url = await reelforge.image(prompt="a beautiful cat")
image_url = await pixelle_video.image(prompt="a beautiful cat")
# Use specific workflow
image_url = await reelforge.image(
image_url = await pixelle_video.image(
prompt="a cat",
workflow="image_flux.json"
)
# With additional parameters
image_url = await reelforge.image(
image_url = await pixelle_video.image(
prompt="a cat",
workflow="image_flux.json",
width=1024,
@@ -101,13 +101,13 @@ class ImageService(ComfyBaseService):
)
# With absolute path
image_url = await reelforge.image(
image_url = await pixelle_video.image(
prompt="a cat",
workflow="/path/to/custom.json"
)
# With custom ComfyUI server
image_url = await reelforge.image(
image_url = await pixelle_video.image(
prompt="a cat",
comfyui_url="http://192.168.1.100:8188"
)

View File

@@ -8,21 +8,21 @@ from typing import List, Optional, Callable
from loguru import logger
from reelforge.models.storyboard import StoryboardConfig
from reelforge.prompts import build_image_prompt_prompt
from pixelle_video.models.storyboard import StoryboardConfig
from pixelle_video.prompts import build_image_prompt_prompt
class ImagePromptGeneratorService:
"""Image prompt generation service"""
def __init__(self, reelforge_core):
def __init__(self, pixelle_video_core):
"""
Initialize
Args:
reelforge_core: ReelForgeCore instance
pixelle_video_core: PixelleVideoCore instance
"""
self.core = reelforge_core
self.core = pixelle_video_core
async def generate_image_prompts(
self,
@@ -114,7 +114,7 @@ class ImagePromptGeneratorService:
logger.info(f"✅ All batches completed. Total prompts: {len(base_prompts)}")
# 5. Apply prompt prefix to each prompt
from reelforge.utils.prompt_helper import build_image_prompt
from pixelle_video.utils.prompt_helper import build_image_prompt
# Get prompt prefix from config (fix: correct path is comfyui.image.prompt_prefix)
image_config = self.core.config.get("comfyui", {}).get("image", {})

View File

@@ -26,10 +26,10 @@ class LLMService:
Usage:
# Direct call
answer = await reelforge.llm("Explain atomic habits")
answer = await pixelle_video.llm("Explain atomic habits")
# With parameters
answer = await reelforge.llm(
answer = await pixelle_video.llm(
prompt="Explain atomic habits in 3 sentences",
temperature=0.7,
max_tokens=2000
@@ -121,10 +121,10 @@ class LLMService:
Examples:
# Use config from config.yaml
answer = await reelforge.llm("Explain atomic habits")
answer = await pixelle_video.llm("Explain atomic habits")
# Override with custom parameters
answer = await reelforge.llm(
answer = await pixelle_video.llm(
prompt="Explain atomic habits in 3 sentences",
api_key="sk-custom-key",
base_url="https://api.custom.com/v1",
@@ -172,7 +172,7 @@ class LLMService:
Active model name
Example:
print(f"Using model: {reelforge.llm.active}")
print(f"Using model: {pixelle_video.llm.active}")
"""
return self._get_config_value("model", "gpt-3.5-turbo")

View File

@@ -12,8 +12,8 @@ from typing import List, Optional, Literal
from loguru import logger
from reelforge.models.storyboard import StoryboardConfig, ContentMetadata
from reelforge.prompts import (
from pixelle_video.models.storyboard import StoryboardConfig, ContentMetadata
from pixelle_video.prompts import (
build_topic_narration_prompt,
build_content_narration_prompt,
)
@@ -22,14 +22,14 @@ from reelforge.prompts import (
class NarrationGeneratorService:
"""Narration generation service"""
def __init__(self, reelforge_core):
def __init__(self, pixelle_video_core):
"""
Initialize
Args:
reelforge_core: ReelForgeCore instance (for calling llm)
pixelle_video_core: PixelleVideoCore instance (for calling llm)
"""
self.core = reelforge_core
self.core = pixelle_video_core
async def generate_narrations(
self,

View File

@@ -24,14 +24,14 @@ class TitleGeneratorService:
- llm: Always use LLM to generate title
"""
def __init__(self, reelforge_core):
def __init__(self, pixelle_video_core):
"""
Initialize title generator service
Args:
reelforge_core: ReelForgeCore instance
pixelle_video_core: PixelleVideoCore instance
"""
self.core = reelforge_core
self.core = pixelle_video_core
async def __call__(
self,
@@ -107,7 +107,7 @@ class TitleGeneratorService:
Returns:
LLM-generated title
"""
from reelforge.prompts import build_title_generation_prompt
from pixelle_video.prompts import build_title_generation_prompt
# Build prompt using template
prompt = build_title_generation_prompt(content, max_length=500)

View File

@@ -7,7 +7,7 @@ from typing import Optional
from comfykit import ComfyKit
from loguru import logger
from reelforge.services.comfy_base_service import ComfyBaseService
from pixelle_video.services.comfy_base_service import ComfyBaseService
class TTSService(ComfyBaseService):
@@ -18,16 +18,16 @@ class TTSService(ComfyBaseService):
Usage:
# Use default workflow
audio_path = await reelforge.tts(text="Hello, world!")
audio_path = await pixelle_video.tts(text="Hello, world!")
# Use specific workflow
audio_path = await reelforge.tts(
audio_path = await pixelle_video.tts(
text="你好,世界!",
workflow="tts_edge.json"
)
# List available workflows
workflows = reelforge.tts.list_workflows()
workflows = pixelle_video.tts.list_workflows()
"""
WORKFLOW_PREFIX = "tts_"
@@ -76,16 +76,16 @@ class TTSService(ComfyBaseService):
Examples:
# Simplest: use default workflow
audio_path = await reelforge.tts(text="Hello, world!")
audio_path = await pixelle_video.tts(text="Hello, world!")
# Use specific workflow
audio_path = await reelforge.tts(
audio_path = await pixelle_video.tts(
text="你好,世界!",
workflow="tts_edge.json"
)
# With voice and speed
audio_path = await reelforge.tts(
audio_path = await pixelle_video.tts(
text="Hello",
workflow="tts_edge.json",
voice="[Chinese] zh-CN Xiaoxiao",
@@ -93,13 +93,13 @@ class TTSService(ComfyBaseService):
)
# With absolute path
audio_path = await reelforge.tts(
audio_path = await pixelle_video.tts(
text="Hello",
workflow="/path/to/custom_tts.json"
)
# With custom ComfyUI server
audio_path = await reelforge.tts(
audio_path = await pixelle_video.tts(
text="Hello",
comfyui_url="http://192.168.1.100:8188"
)

View File

@@ -10,8 +10,8 @@ from typing import Optional, Callable, Literal
from loguru import logger
from reelforge.models.progress import ProgressEvent
from reelforge.models.storyboard import (
from pixelle_video.models.progress import ProgressEvent
from pixelle_video.models.storyboard import (
Storyboard,
StoryboardFrame,
StoryboardConfig,
@@ -32,14 +32,14 @@ class VideoGeneratorService:
5. Add BGM (optional)
"""
def __init__(self, reelforge_core):
def __init__(self, pixelle_video_core):
"""
Initialize video generator service
Args:
reelforge_core: ReelForgeCore instance
pixelle_video_core: PixelleVideoCore instance
"""
self.core = reelforge_core
self.core = pixelle_video_core
async def __call__(
self,
@@ -149,7 +149,7 @@ class VideoGeneratorService:
Examples:
# Generate mode: LLM creates narrations from topic
>>> result = await reelforge.generate_video(
>>> result = await pixelle_video.generate_video(
... text="如何在信息爆炸时代保持深度思考",
... mode="generate",
... n_scenes=5,
@@ -161,7 +161,7 @@ class VideoGeneratorService:
... 第一个技巧是专注力训练每天冥想10分钟
... 第二个技巧是主动回忆学完立即复述
... 第三个技巧是间隔重复学习后定期复习'''
>>> result = await reelforge.generate_video(
>>> result = await pixelle_video.generate_video(
... text=script,
... mode="fixed",
... title="三个学习技巧"
@@ -190,7 +190,7 @@ class VideoGeneratorService:
logger.info(f" Title: '{final_title}' (LLM-generated)")
# ========== Step 0.5: Create isolated task directory ==========
from reelforge.utils.os_util import (
from pixelle_video.utils.os_util import (
create_task_output_dir,
get_task_final_video_path
)
@@ -352,7 +352,7 @@ class VideoGeneratorService:
self._report_progress(progress_callback, "concatenating", 0.85)
segment_paths = [frame.video_segment_path for frame in storyboard.frames]
from reelforge.services.video import VideoService
from pixelle_video.services.video import VideoService
video_service = VideoService()
final_video_path = video_service.concat_videos(

View File

@@ -0,0 +1,3 @@
"""
Pixelle-Video utilities
"""

View File

@@ -1,7 +1,7 @@
"""
OS utilities for file and path management
Provides utilities for managing paths and files in ReelForge.
Provides utilities for managing paths and files in Pixelle-Video.
Inspired by Pixelle-MCP's os_util.py.
"""
@@ -12,9 +12,9 @@ from pathlib import Path
from typing import Optional, Tuple, Literal
def get_reelforge_root_path() -> str:
def get_pixelle_video_root_path() -> str:
"""
Get ReelForge root path - current working directory
Get Pixelle-Video root path - current working directory
Returns:
Current working directory as string
@@ -22,14 +22,14 @@ def get_reelforge_root_path() -> str:
return str(Path.cwd())
def ensure_reelforge_root_path() -> str:
def ensure_pixelle_video_root_path() -> str:
"""
Ensure ReelForge root path exists and return the path
Ensure Pixelle-Video root path exists and return the path
Returns:
Root path as string
"""
root_path = get_reelforge_root_path()
root_path = get_pixelle_video_root_path()
root_path_obj = Path(root_path)
output_dir = root_path_obj / 'output'
output_dir.mkdir(parents=True, exist_ok=True)
@@ -39,7 +39,7 @@ def ensure_reelforge_root_path() -> str:
def get_root_path(*paths: str) -> str:
"""
Get path relative to ReelForge root path
Get path relative to Pixelle-Video root path
Args:
*paths: Path components to join
@@ -51,7 +51,7 @@ def get_root_path(*paths: str) -> str:
get_root_path("temp", "audio.mp3")
# Returns: "/path/to/project/temp/audio.mp3"
"""
root_path = ensure_reelforge_root_path()
root_path = ensure_pixelle_video_root_path()
if paths:
return os.path.join(root_path, *paths)
return root_path
@@ -59,7 +59,7 @@ def get_root_path(*paths: str) -> str:
def get_temp_path(*paths: str) -> str:
"""
Get path relative to ReelForge temp folder
Get path relative to Pixelle-Video temp folder
Args:
*paths: Path components to join
@@ -79,7 +79,7 @@ def get_temp_path(*paths: str) -> str:
def get_data_path(*paths: str) -> str:
"""
Get path relative to ReelForge data folder
Get path relative to Pixelle-Video data folder
Args:
*paths: Path components to join
@@ -99,7 +99,7 @@ def get_data_path(*paths: str) -> str:
def get_output_path(*paths: str) -> str:
"""
Get path relative to ReelForge output folder
Get path relative to Pixelle-Video output folder
Args:
*paths: Path components to join

View File

@@ -1,9 +1,9 @@
[project]
name = "reelforge"
name = "pixelle-video"
version = "0.1.0"
description = "The modular video creation platform - Forge your perfect reel engine"
description = "AI-powered video creation platform - Part of Pixelle ecosystem"
authors = [
{name = "ReelForge Team"}
{name = "PixelleLab"}
]
readme = "README.md"
requires-python = ">=3.11"
@@ -34,7 +34,8 @@ dev = [
]
[project.scripts]
reelforge = "reelforge.cli:main"
pixelle-video = "pixelle_video.cli:main"
pvideo = "pixelle_video.cli:main"
[build-system]
requires = ["hatchling"]

View File

@@ -1,26 +0,0 @@
"""
ReelForge - AI-powered video generator
Convention-based system with unified configuration management.
Usage:
from reelforge import reelforge
# Initialize
await reelforge.initialize()
# Use capabilities
answer = await reelforge.llm("Explain atomic habits")
audio = await reelforge.tts("Hello world")
# Generate video
result = await reelforge.generate_video(topic="AI in 2024")
"""
from reelforge.service import ReelForgeCore, reelforge
from reelforge.config import config_manager
__version__ = "0.1.0"
__all__ = ["ReelForgeCore", "reelforge", "config_manager"]

View File

@@ -1,30 +0,0 @@
"""
ReelForge Services
Unified service layer providing simplified access to capabilities.
"""
from reelforge.services.comfy_base_service import ComfyBaseService
from reelforge.services.llm_service import LLMService
from reelforge.services.tts_service import TTSService
from reelforge.services.image import ImageService
from reelforge.services.video import VideoService
from reelforge.services.narration_generator import NarrationGeneratorService
from reelforge.services.image_prompt_generator import ImagePromptGeneratorService
from reelforge.services.title_generator import TitleGeneratorService
from reelforge.services.frame_processor import FrameProcessor
from reelforge.services.video_generator import VideoGeneratorService
__all__ = [
"ComfyBaseService",
"LLMService",
"TTSService",
"ImageService",
"VideoService",
"NarrationGeneratorService",
"ImagePromptGeneratorService",
"TitleGeneratorService",
"FrameProcessor",
"VideoGeneratorService",
]

View File

@@ -1,3 +0,0 @@
"""
ReelForge utilities
"""

View File

@@ -1,7 +1,7 @@
#!/bin/bash
# Restart ReelForge Web UI on port 8502
# Restart Pixelle-Video Web UI on port 8502
echo "🔄 Restarting ReelForge Web UI on port 8502..."
echo "🔄 Restarting Pixelle-Video Web UI on port 8502..."
echo ""
# Check if config.yaml exists
@@ -28,7 +28,7 @@ if [ ! -z "$PID" ]; then
fi
# Start Streamlit in background with nohup
echo "🚀 Starting ReelForge Web UI in background..."
echo "🚀 Starting Pixelle-Video Web UI in background..."
nohup uv run streamlit run web/app.py --server.port $PORT > nohup.out 2>&1 &
# Wait a moment and check if the process started
@@ -36,12 +36,12 @@ sleep 2
NEW_PID=$(lsof -ti:$PORT)
if [ ! -z "$NEW_PID" ]; then
echo "✅ ReelForge Web UI started successfully!"
echo "✅ Pixelle-Video Web UI started successfully!"
echo "📝 Process ID: $NEW_PID"
echo "🌐 Access at: http://localhost:$PORT"
echo "📄 Logs: nohup.out"
else
echo "❌ Failed to start ReelForge Web UI"
echo "❌ Failed to start Pixelle-Video Web UI"
echo "📄 Check nohup.out for error details"
exit 1
fi

View File

@@ -1,7 +1,7 @@
#!/bin/bash
# Start ReelForge Web UI
# Start Pixelle-Video Web UI
echo "🚀 Starting ReelForge Web UI..."
echo "🚀 Starting Pixelle-Video Web UI..."
echo ""
# Check if config.yaml exists

View File

@@ -527,7 +527,7 @@
<div class="author-desc">Open Source Omnimodal AI Creative Agent</div>
</div>
<div class="logo-section">
<div class="logo">ReelForge</div>
<div class="logo">Pixelle-Video</div>
<div class="logo-marks">
<div class="logo-mark"></div>
<div class="logo-mark active"></div>

View File

@@ -558,7 +558,7 @@
<div class="author-desc">Open Source Omnimodal AI Creative Agent</div>
</div>
<div class="logo-wrapper">
<div class="logo">ReelForge</div>
<div class="logo">Pixelle-Video</div>
<div class="logo-dots">
<div class="logo-dot"></div>
<div class="logo-dot"></div>

View File

@@ -923,7 +923,7 @@
</div>
<div class="slogan">Open Source Omnimodal AI Creative Agent</div>
<div class="cta">
<div class="follow">ReelForge</div>
<div class="follow">Pixelle-Video</div>
<div class="hashtags">
<span>#AI创作</span>
<span>#短视频</span>

108
uv.lock generated
View File

@@ -1640,6 +1640,60 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/34/e7/ae39f538fd6844e982063c3a5e4598b8ced43b9633baa3a85ef33af8c05c/pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8", size = 6984598 },
]
[[package]]
name = "pixelle-video"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "certifi" },
{ name = "comfykit" },
{ name = "edge-tts" },
{ name = "fastapi" },
{ name = "fastmcp" },
{ name = "ffmpeg-python" },
{ name = "html2image" },
{ name = "httpx" },
{ name = "loguru" },
{ name = "openai" },
{ name = "pillow" },
{ name = "pydantic" },
{ name = "python-multipart" },
{ name = "pyyaml" },
{ name = "streamlit" },
{ name = "uvicorn", extra = ["standard"] },
]
[package.optional-dependencies]
dev = [
{ name = "pytest" },
{ name = "pytest-asyncio" },
{ name = "ruff" },
]
[package.metadata]
requires-dist = [
{ name = "certifi", specifier = ">=2025.10.5" },
{ name = "comfykit", specifier = ">=0.1.0" },
{ name = "edge-tts", specifier = ">=7.2.3" },
{ name = "fastapi", specifier = ">=0.115.0" },
{ name = "fastmcp", specifier = ">=2.0.0" },
{ name = "ffmpeg-python", specifier = ">=0.2.0" },
{ name = "html2image", specifier = ">=2.0.7" },
{ name = "httpx", specifier = ">=0.28.1" },
{ name = "loguru", specifier = ">=0.7.0" },
{ name = "openai", specifier = ">=2.6.0" },
{ name = "pillow", specifier = ">=10.0.0,<12" },
{ name = "pydantic", specifier = ">=2.0.0" },
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" },
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.0" },
{ name = "python-multipart", specifier = ">=0.0.12" },
{ name = "pyyaml", specifier = ">=6.0.0" },
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.6.0" },
{ name = "streamlit", specifier = ">=1.40.0" },
{ name = "uvicorn", extras = ["standard"], specifier = ">=0.32.0" },
]
provides-extras = ["dev"]
[[package]]
name = "pluggy"
version = "1.6.0"
@@ -2094,60 +2148,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341 },
]
[[package]]
name = "reelforge"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "certifi" },
{ name = "comfykit" },
{ name = "edge-tts" },
{ name = "fastapi" },
{ name = "fastmcp" },
{ name = "ffmpeg-python" },
{ name = "html2image" },
{ name = "httpx" },
{ name = "loguru" },
{ name = "openai" },
{ name = "pillow" },
{ name = "pydantic" },
{ name = "python-multipart" },
{ name = "pyyaml" },
{ name = "streamlit" },
{ name = "uvicorn", extra = ["standard"] },
]
[package.optional-dependencies]
dev = [
{ name = "pytest" },
{ name = "pytest-asyncio" },
{ name = "ruff" },
]
[package.metadata]
requires-dist = [
{ name = "certifi", specifier = ">=2025.10.5" },
{ name = "comfykit", specifier = ">=0.1.0" },
{ name = "edge-tts", specifier = ">=7.2.3" },
{ name = "fastapi", specifier = ">=0.115.0" },
{ name = "fastmcp", specifier = ">=2.0.0" },
{ name = "ffmpeg-python", specifier = ">=0.2.0" },
{ name = "html2image", specifier = ">=2.0.7" },
{ name = "httpx", specifier = ">=0.28.1" },
{ name = "loguru", specifier = ">=0.7.0" },
{ name = "openai", specifier = ">=2.6.0" },
{ name = "pillow", specifier = ">=10.0.0,<12" },
{ name = "pydantic", specifier = ">=2.0.0" },
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" },
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.0" },
{ name = "python-multipart", specifier = ">=0.0.12" },
{ name = "pyyaml", specifier = ">=6.0.0" },
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.6.0" },
{ name = "streamlit", specifier = ">=1.40.0" },
{ name = "uvicorn", extras = ["standard"], specifier = ">=0.32.0" },
]
provides-extras = ["dev"]
[[package]]
name = "referencing"
version = "0.36.2"

View File

@@ -1,5 +1,5 @@
"""
ReelForge Web UI
Pixelle-Video Web UI
A simple web interface for generating short videos from content.
"""
@@ -14,12 +14,12 @@ from loguru import logger
# Import i18n and config manager
from web.i18n import load_locales, set_language, tr, get_available_languages, get_language
from reelforge.config import config_manager
from reelforge.models.progress import ProgressEvent
from pixelle_video.config import config_manager
from pixelle_video.models.progress import ProgressEvent
# Setup page config (must be first)
st.set_page_config(
page_title="ReelForge - AI Video Generator",
page_title="Pixelle-Video - AI Video Generator",
page_icon="🎬",
layout="wide",
initial_sidebar_state="collapsed",
@@ -62,19 +62,19 @@ def init_i18n():
# ============================================================================
# Initialize ReelForge
# Initialize Pixelle-Video
# ============================================================================
def get_reelforge():
"""Get initialized ReelForge instance (no caching - always fresh)"""
from reelforge.service import ReelForgeCore
def get_pixelle_video():
"""Get initialized Pixelle-Video instance (no caching - always fresh)"""
from pixelle_video.service import PixelleVideoCore
logger.info("Initializing ReelForge...")
reelforge = ReelForgeCore()
run_async(reelforge.initialize())
logger.info("ReelForge initialized")
logger.info("Initializing Pixelle-Video...")
pixelle_video = PixelleVideoCore()
run_async(pixelle_video.initialize())
logger.info("Pixelle-Video initialized")
return reelforge
return pixelle_video
# ============================================================================
@@ -110,7 +110,7 @@ def render_advanced_settings():
st.markdown(f"**{tr('settings.llm.title')}**")
# Quick preset selection
from reelforge.llm_presets import get_preset_names, get_preset, find_preset_by_base_url_and_model
from pixelle_video.llm_presets import get_preset_names, get_preset, find_preset_by_base_url_and_model
# Custom at the end
preset_names = get_preset_names() + ["Custom"]
@@ -266,8 +266,8 @@ def render_advanced_settings():
with col2:
if st.button(tr("btn.reset_config"), use_container_width=True, key="reset_config_btn"):
# Reset to default
from reelforge.config.schema import ReelForgeConfig
config_manager.config = ReelForgeConfig()
from pixelle_video.config.schema import PixelleVideoConfig
config_manager.config = PixelleVideoConfig()
config_manager.save()
st.success(tr("status.config_reset"))
safe_rerun()
@@ -315,8 +315,8 @@ def main():
with col2:
render_language_selector()
# Initialize ReelForge
reelforge = get_reelforge()
# Initialize Pixelle-Video
pixelle_video = get_pixelle_video()
# ========================================================================
# System Configuration (Required)
@@ -443,7 +443,7 @@ def main():
st.markdown(tr("tts.how"))
# Get available TTS workflows
tts_workflows = reelforge.tts.list_workflows()
tts_workflows = pixelle_video.tts.list_workflows()
# Build options for selectbox
tts_workflow_options = [wf["display_name"] for wf in tts_workflows]
@@ -486,7 +486,7 @@ def main():
with st.spinner(tr("tts.previewing")):
try:
# Generate preview audio using selected workflow (use default voice and speed)
audio_path = run_async(reelforge.tts(
audio_path = run_async(pixelle_video.tts(
text=preview_text,
workflow=tts_workflow_key
))
@@ -522,8 +522,8 @@ def main():
st.markdown(f"**{tr('help.how')}**")
st.markdown(tr("style.workflow_how"))
# Get available workflows from reelforge (with source info)
workflows = reelforge.image.list_workflows()
# Get available workflows from pixelle_video (with source info)
workflows = pixelle_video.image.list_workflows()
# Build options for selectbox
# Display: "image_flux.json - Runninghub"
@@ -586,13 +586,13 @@ def main():
if st.button(tr("style.preview"), key="preview_style", use_container_width=True):
with st.spinner(tr("style.previewing")):
try:
from reelforge.utils.prompt_helper import build_image_prompt
from pixelle_video.utils.prompt_helper import build_image_prompt
# Build final prompt with prefix
final_prompt = build_image_prompt(test_prompt, prompt_prefix)
# Generate preview image (small size for speed)
preview_image_path = run_async(reelforge.image(
preview_image_path = run_async(pixelle_video.image(
prompt=final_prompt,
workflow=workflow_key,
width=512,
@@ -707,7 +707,7 @@ def main():
if st.button(tr("template.preview_button"), key="btn_preview_template", use_container_width=True):
with st.spinner(tr("template.preview_generating")):
try:
from reelforge.services.frame_html import HTMLFrameGenerator
from pixelle_video.services.frame_html import HTMLFrameGenerator
# Use the currently selected template
template_path = f"templates/{frame_template}"
@@ -801,7 +801,7 @@ def main():
progress_bar.progress(min(int(event.progress * 100), 99)) # Cap at 99% until complete
# Generate video (directly pass parameters)
result = run_async(reelforge.generate_video(
result = run_async(pixelle_video.generate_video(
text=text,
mode=mode,
title=title if title else None,

View File

@@ -1,5 +1,5 @@
"""
International language support for ReelForge Web UI
International language support for Pixelle-Video Web UI
"""
import json
@@ -64,7 +64,7 @@ def tr(key: str, fallback: Optional[str] = None, **kwargs) -> str:
Translated text
Example:
tr("app.title") # => "ReelForge"
tr("app.title") # => "Pixelle-Video"
tr("error.missing_field", field="API Key") # => "请填写 API Key"
"""
locale = _locales.get(_current_language, {})

View File

@@ -1,8 +1,8 @@
{
"language_name": "English",
"t": {
"app.title": "⚡ ReelForge - AI Auto Short Video Engine",
"app.subtitle": "Forge your perfect reel engine",
"app.title": "⚡ Pixelle-Video - AI Auto Short Video Engine",
"app.subtitle": "Powered by Pixelle AI",
"section.content_input": "📖 Content Input",
"section.bgm": "🎵 Background Music",
@@ -78,7 +78,7 @@
"template.preview_param_width": "Width",
"template.preview_param_height": "Height",
"template.preview_default_title": "AI Changes Content Creation",
"template.preview_default_text": "Artificial intelligence is transforming the way we create content, making it easy for everyone to produce professional-grade videos.",
"template.preview_default_text": "Artificial intelligence is transforming the way PixelleLab creates content, making it easy for everyone to produce professional-grade videos.",
"template.preview_button": "🖼️ Generate Preview",
"template.preview_generating": "Generating template preview...",
"template.preview_success": "✅ Preview generated successfully!",
@@ -172,7 +172,7 @@
"tts.preview_success": "✅ Preview generated successfully!",
"tts.preview_failed": "❌ Preview failed: {error}",
"welcome.first_time": "🎉 Welcome to ReelForge! Please complete basic configuration",
"welcome.first_time": "🎉 Welcome to Pixelle-Video! Please complete basic configuration",
"welcome.config_hint": "💡 First-time setup requires API Key configuration, you can modify it in advanced settings later",
"wizard.llm_required": "🤖 Large Language Model Configuration (Required)",

View File

@@ -1,7 +1,7 @@
{
"language_name": "简体中文",
"t": {
"app.title": "⚡ ReelForge - AI 全自动短视频引擎",
"app.title": "⚡ Pixelle-Video - AI 全自动短视频引擎",
"app.subtitle": "打造专属你的视频创作引擎",
"section.content_input": "📖 内容输入",
@@ -172,7 +172,7 @@
"tts.preview_success": "✅ 预览生成成功!",
"tts.preview_failed": "❌ 预览失败:{error}",
"welcome.first_time": "🎉 欢迎使用 ReelForge!请先完成基础配置",
"welcome.first_time": "🎉 欢迎使用 Pixelle-Video!请先完成基础配置",
"welcome.config_hint": "💡 首次使用需要配置 API Key后续可以在高级设置中修改",
"wizard.llm_required": "🤖 大语言模型配置(必需)",