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

View File

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

View File

@@ -1,45 +1,45 @@
""" """
FastAPI Dependencies FastAPI Dependencies
Provides dependency injection for ReelForgeCore and other services. Provides dependency injection for PixelleVideoCore and other services.
""" """
from typing import Annotated from typing import Annotated
from fastapi import Depends from fastapi import Depends
from loguru import logger from loguru import logger
from reelforge.service import ReelForgeCore from pixelle_video.service import PixelleVideoCore
# Global ReelForge instance # Global Pixelle-Video instance
_reelforge_instance: ReelForgeCore = None _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: Returns:
ReelForgeCore instance PixelleVideoCore instance
""" """
global _reelforge_instance global _pixelle_video_instance
if _reelforge_instance is None: if _pixelle_video_instance is None:
_reelforge_instance = ReelForgeCore() _pixelle_video_instance = PixelleVideoCore()
await _reelforge_instance.initialize() await _pixelle_video_instance.initialize()
logger.info("ReelForge initialized for API") logger.info("Pixelle-Video initialized for API")
return _reelforge_instance return _pixelle_video_instance
async def shutdown_reelforge(): async def shutdown_pixelle_video():
"""Shutdown ReelForge instance""" """Shutdown Pixelle-Video instance"""
global _reelforge_instance global _pixelle_video_instance
if _reelforge_instance: if _pixelle_video_instance:
logger.info("Shutting down ReelForge...") logger.info("Shutting down Pixelle-Video...")
_reelforge_instance = None _pixelle_video_instance = None
# Type alias for dependency injection # 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 fastapi import APIRouter, HTTPException
from loguru import logger from loguru import logger
from api.dependencies import ReelForgeDep from api.dependencies import PixelleVideoDep
from api.schemas.content import ( from api.schemas.content import (
NarrationGenerateRequest, NarrationGenerateRequest,
NarrationGenerateResponse, NarrationGenerateResponse,
@@ -23,7 +23,7 @@ router = APIRouter(prefix="/content", tags=["Content Generation"])
@router.post("/narration", response_model=NarrationGenerateResponse) @router.post("/narration", response_model=NarrationGenerateResponse)
async def generate_narration( async def generate_narration(
request: NarrationGenerateRequest, request: NarrationGenerateRequest,
reelforge: ReelForgeDep pixelle_video: PixelleVideoDep
): ):
""" """
Generate narrations from text Generate narrations from text
@@ -41,7 +41,7 @@ async def generate_narration(
logger.info(f"Generating {request.n_scenes} narrations from text") logger.info(f"Generating {request.n_scenes} narrations from text")
# Call narration generator service # Call narration generator service
narrations = await reelforge.narration_generator( narrations = await pixelle_video.narration_generator(
text=request.text, text=request.text,
n_scenes=request.n_scenes, n_scenes=request.n_scenes,
min_words=request.min_words, min_words=request.min_words,
@@ -60,7 +60,7 @@ async def generate_narration(
@router.post("/image-prompt", response_model=ImagePromptGenerateResponse) @router.post("/image-prompt", response_model=ImagePromptGenerateResponse)
async def generate_image_prompt( async def generate_image_prompt(
request: ImagePromptGenerateRequest, request: ImagePromptGenerateRequest,
reelforge: ReelForgeDep pixelle_video: PixelleVideoDep
): ):
""" """
Generate image prompts from narrations 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") logger.info(f"Generating image prompts for {len(request.narrations)} narrations")
# Call image prompt generator service # Call image prompt generator service
image_prompts = await reelforge.image_prompt_generator( image_prompts = await pixelle_video.image_prompt_generator(
narrations=request.narrations, narrations=request.narrations,
min_words=request.min_words, min_words=request.min_words,
max_words=request.max_words max_words=request.max_words
@@ -95,7 +95,7 @@ async def generate_image_prompt(
@router.post("/title", response_model=TitleGenerateResponse) @router.post("/title", response_model=TitleGenerateResponse)
async def generate_title( async def generate_title(
request: TitleGenerateRequest, request: TitleGenerateRequest,
reelforge: ReelForgeDep pixelle_video: PixelleVideoDep
): ):
""" """
Generate video title from text Generate video title from text
@@ -111,7 +111,7 @@ async def generate_title(
logger.info("Generating title from text") logger.info("Generating title from text")
# Call title generator service # Call title generator service
title = await reelforge.title_generator( title = await pixelle_video.title_generator(
text=request.text text=request.text
) )

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -13,7 +13,7 @@ class TTSSynthesizeRequest(BaseModel):
class Config: class Config:
json_schema_extra = { json_schema_extra = {
"example": { "example": {
"text": "Hello, welcome to ReelForge!", "text": "Hello, welcome to Pixelle-Video!",
"voice_id": "[Chinese] zh-CN Yunjian" "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 # Copy this file to config.yaml and fill in your settings
# ⚠️ Never commit config.yaml to Git! # ⚠️ Never commit config.yaml to Git!
project_name: ReelForge project_name: Pixelle-Video
# ==================== LLM Configuration ==================== # ==================== LLM Configuration ====================
# Supports any OpenAI SDK compatible API # 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 > Complete guide to using LLM, TTS, and Image generation capabilities
## Overview ## Overview
ReelForge provides three core AI capabilities: Pixelle-Video provides three core AI capabilities:
- **LLM**: Text generation using LiteLLM (supports 100+ models) - **LLM**: Text generation using LiteLLM (supports 100+ models)
- **TTS**: Text-to-speech using Edge TTS (free, 400+ voices) - **TTS**: Text-to-speech using Edge TTS (free, 400+ voices)
- **Image**: Image generation using ComfyKit (local or cloud) - **Image**: Image generation using ComfyKit (local or cloud)
@@ -12,16 +12,16 @@ ReelForge provides three core AI capabilities:
## Quick Start ## Quick Start
```python ```python
from reelforge.service import reelforge from pixelle_video.service import pixelle_video
# LLM - Generate text # 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 # TTS - Generate speech
audio_path = await reelforge.tts("Hello, world!") audio_path = await pixelle_video.tts("Hello, world!")
# Image - Generate images # Image - Generate images
image_url = await reelforge.image( image_url = await pixelle_video.image(
workflow="workflows/book_cover_simple.json", workflow="workflows/book_cover_simple.json",
prompt="minimalist book cover design" prompt="minimalist book cover design"
) )
@@ -62,10 +62,10 @@ llm:
```python ```python
# Basic usage # Basic usage
answer = await reelforge.llm("What is machine learning?") answer = await pixelle_video.llm("What is machine learning?")
# With parameters # With parameters
answer = await reelforge.llm( answer = await pixelle_video.llm(
prompt="Explain atomic habits", prompt="Explain atomic habits",
temperature=0.7, # 0.0-2.0 (lower = more deterministic) temperature=0.7, # 0.0-2.0 (lower = more deterministic)
max_tokens=2000 max_tokens=2000
@@ -107,18 +107,18 @@ tts:
```python ```python
# Basic usage (auto-generates temp path) # 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" # Returns: "temp/abc123def456.mp3"
# With Chinese text # With Chinese text
audio_path = await reelforge.tts( audio_path = await pixelle_video.tts(
text="你好,世界!", text="你好,世界!",
voice="zh-CN-YunjianNeural" voice="zh-CN-YunjianNeural"
) )
# With custom parameters # With custom parameters
audio_path = await reelforge.tts( audio_path = await pixelle_video.tts(
text="Welcome to ReelForge", text="Welcome to Pixelle-Video",
voice="en-US-JennyNeural", voice="en-US-JennyNeural",
rate="+20%", # Speed: +50% = faster, -20% = slower rate="+20%", # Speed: +50% = faster, -20% = slower
volume="+0%", volume="+0%",
@@ -126,7 +126,7 @@ audio_path = await reelforge.tts(
) )
# Specify output path # Specify output path
audio_path = await reelforge.tts( audio_path = await pixelle_video.tts(
text="Hello", text="Hello",
output_path="output/greeting.mp3" output_path="output/greeting.mp3"
) )
@@ -149,13 +149,13 @@ audio_path = await reelforge.tts(
```python ```python
# Get all available voices # Get all available voices
voices = await reelforge.tts.list_voices() voices = await pixelle_video.tts.list_voices()
# Get Chinese voices only # 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 # 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 ```python
# Basic usage (local ComfyUI) # Basic usage (local ComfyUI)
image_url = await reelforge.image( image_url = await pixelle_video.image(
workflow="workflows/book_cover_simple.json", workflow="workflows/book_cover_simple.json",
prompt="minimalist book cover design, blue and white" prompt="minimalist book cover design, blue and white"
) )
# With full parameters # With full parameters
image_url = await reelforge.image( image_url = await pixelle_video.image(
workflow="workflows/book_cover_simple.json", workflow="workflows/book_cover_simple.json",
prompt="book cover for 'Atomic Habits', professional, minimalist", prompt="book cover for 'Atomic Habits', professional, minimalist",
negative_prompt="ugly, blurry, low quality", negative_prompt="ugly, blurry, low quality",
@@ -199,13 +199,13 @@ image_url = await reelforge.image(
) )
# Using RunningHub cloud # Using RunningHub cloud
image_url = await reelforge.image( image_url = await pixelle_video.image(
workflow="12345", # RunningHub workflow ID workflow="12345", # RunningHub workflow ID
prompt="a beautiful landscape" prompt="a beautiful landscape"
) )
# Check available workflows # Check available workflows
workflows = reelforge.image.list_workflows() workflows = pixelle_video.image.list_workflows()
print(f"Available workflows: {workflows}") print(f"Available workflows: {workflows}")
``` ```
@@ -221,7 +221,7 @@ export RUNNINGHUB_API_KEY="rh-key-xxx"
### Workflow DSL ### Workflow DSL
ReelForge uses ComfyKit's DSL for workflow parameters: Pixelle-Video uses ComfyKit's DSL for workflow parameters:
```json ```json
{ {
@@ -252,27 +252,27 @@ Generate a complete book cover with narration:
```python ```python
import asyncio import asyncio
from reelforge.service import reelforge from pixelle_video.service import pixelle_video
async def create_book_content(book_title, author): async def create_book_content(book_title, author):
"""Generate book summary, audio, and cover image""" """Generate book summary, audio, and cover image"""
# 1. Generate book summary with LLM # 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}", prompt=f"Write a compelling 2-sentence summary for a book titled '{book_title}' by {author}",
max_tokens=100 max_tokens=100
) )
print(f"Summary: {summary}") print(f"Summary: {summary}")
# 2. Generate audio narration with TTS # 2. Generate audio narration with TTS
audio_path = await reelforge.tts( audio_path = await pixelle_video.tts(
text=summary, text=summary,
voice="en-US-JennyNeural" voice="en-US-JennyNeural"
) )
print(f"Audio: {audio_path}") print(f"Audio: {audio_path}")
# 3. Generate book cover image # 3. Generate book cover image
image_url = await reelforge.image( image_url = await pixelle_video.image(
workflow="workflows/book_cover_simple.json", workflow="workflows/book_cover_simple.json",
prompt=f"book cover for '{book_title}' by {author}, professional, modern design", prompt=f"book cover for '{book_title}' by {author}, professional, modern design",
width=1024, 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 import asyncio
from loguru import logger from loguru import logger
from reelforge.service import reelforge from pixelle_video.service import pixelle_video
async def test_llm(): async def test_llm():
"""Test LLM capability""" """Test LLM capability"""
# Initialize reelforge # Initialize pixelle_video
await reelforge.initialize() await pixelle_video.initialize()
# Test prompt # Test prompt
prompt = "Explain the concept of atomic habits in 3 sentences." 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") logger.info(f"\n📝 Test Prompt: {prompt}\n")
# Call LLM # Call LLM
result = await reelforge.llm(prompt) result = await pixelle_video.llm(prompt)
logger.info(f"\n✨ Result:\n{result}\n") logger.info(f"\n✨ Result:\n{result}\n")
def main(): def main():
"""Main CLI entry point""" """Main CLI entry point"""
logger.info("🚀 ReelForge CLI\n") logger.info("🚀 Pixelle-Video CLI\n")
# Run test # Run test
asyncio.run(test_llm()) asyncio.run(test_llm())

View File

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

View File

@@ -6,7 +6,7 @@ Provides unified access to configuration with automatic validation.
from pathlib import Path from pathlib import Path
from typing import Any, Optional from typing import Any, Optional
from loguru import logger from loguru import logger
from .schema import ReelForgeConfig from .schema import PixelleVideoConfig
from .loader import load_config_dict, save_config_dict from .loader import load_config_dict, save_config_dict
@@ -29,13 +29,13 @@ class ConfigManager:
return return
self.config_path = Path(config_path) self.config_path = Path(config_path)
self.config: ReelForgeConfig = self._load() self.config: PixelleVideoConfig = self._load()
self._initialized = True self._initialized = True
def _load(self) -> ReelForgeConfig: def _load(self) -> PixelleVideoConfig:
"""Load configuration from file""" """Load configuration from file"""
data = load_config_dict(str(self.config_path)) data = load_config_dict(str(self.config_path))
return ReelForgeConfig(**data) return PixelleVideoConfig(**data)
def reload(self): def reload(self):
"""Reload configuration from file""" """Reload configuration from file"""
@@ -65,7 +65,7 @@ class ConfigManager:
return base return base
merged = deep_merge(current, updates) merged = deep_merge(current, updates)
self.config = ReelForgeConfig(**merged) self.config = PixelleVideoConfig(**merged)
def get(self, key: str, default: Any = None) -> Any: def get(self, key: str, default: Any = None) -> Any:
"""Dict-like access (for backward compatibility)""" """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") image: ImageSubConfig = Field(default_factory=ImageSubConfig, description="Image-specific configuration")
class ReelForgeConfig(BaseModel): class PixelleVideoConfig(BaseModel):
"""ReelForge main configuration""" """Pixelle-Video main configuration"""
project_name: str = Field(default="ReelForge", description="Project name") project_name: str = Field(default="Pixelle-Video", description="Project name")
llm: LLMConfig = Field(default_factory=LLMConfig) llm: LLMConfig = Field(default_factory=LLMConfig)
comfyui: ComfyUIConfig = Field(default_factory=ComfyUIConfig) comfyui: ComfyUIConfig = Field(default_factory=ComfyUIConfig)

View File

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

View File

@@ -5,17 +5,17 @@ Centralized prompt management for all LLM interactions.
""" """
# Narration prompts # Narration prompts
from reelforge.prompts.topic_narration import build_topic_narration_prompt from pixelle_video.prompts.topic_narration import build_topic_narration_prompt
from reelforge.prompts.content_narration import build_content_narration_prompt from pixelle_video.prompts.content_narration import build_content_narration_prompt
from reelforge.prompts.title_generation import build_title_generation_prompt from pixelle_video.prompts.title_generation import build_title_generation_prompt
# Image prompts # Image prompts
from reelforge.prompts.image_generation import ( from pixelle_video.prompts.image_generation import (
build_image_prompt_prompt, build_image_prompt_prompt,
IMAGE_STYLE_PRESETS, IMAGE_STYLE_PRESETS,
DEFAULT_IMAGE_STYLE 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__ = [ __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.) Provides unified access to all capabilities (LLM, TTS, Image, etc.)
""" """
@@ -8,40 +8,40 @@ from typing import Optional
from loguru import logger from loguru import logger
from reelforge.config import config_manager from pixelle_video.config import config_manager
from reelforge.services.llm_service import LLMService from pixelle_video.services.llm_service import LLMService
from reelforge.services.tts_service import TTSService from pixelle_video.services.tts_service import TTSService
from reelforge.services.image import ImageService from pixelle_video.services.image import ImageService
from reelforge.services.narration_generator import NarrationGeneratorService from pixelle_video.services.narration_generator import NarrationGeneratorService
from reelforge.services.image_prompt_generator import ImagePromptGeneratorService from pixelle_video.services.image_prompt_generator import ImagePromptGeneratorService
from reelforge.services.title_generator import TitleGeneratorService from pixelle_video.services.title_generator import TitleGeneratorService
from reelforge.services.frame_processor import FrameProcessor from pixelle_video.services.frame_processor import FrameProcessor
from reelforge.services.video_generator import VideoGeneratorService 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. Provides unified access to all capabilities.
Usage: Usage:
from reelforge import reelforge from pixelle_video import pixelle_video
# Initialize # Initialize
await reelforge.initialize() await pixelle_video.initialize()
# Use capabilities directly # Use capabilities directly
answer = await reelforge.llm("Explain atomic habits") answer = await pixelle_video.llm("Explain atomic habits")
audio = await reelforge.tts("Hello world") audio = await pixelle_video.tts("Hello world")
image = await reelforge.image(prompt="a cat") image = await pixelle_video.image(prompt="a cat")
# Check active capabilities # Check active capabilities
print(f"Using LLM: {reelforge.llm.active}") print(f"Using LLM: {pixelle_video.llm.active}")
print(f"Available TTS: {reelforge.tts.available}") print(f"Available TTS: {pixelle_video.tts.available}")
Architecture (Simplified): Architecture (Simplified):
ReelForgeCore (this class) PixelleVideoCore (this class)
config (configuration) config (configuration)
llm (LLM service - direct OpenAI SDK) llm (LLM service - direct OpenAI SDK)
tts (TTS service - ComfyKit workflows) tts (TTS service - ComfyKit workflows)
@@ -50,7 +50,7 @@ class ReelForgeCore:
def __init__(self, config_path: str = "config.yaml"): def __init__(self, config_path: str = "config.yaml"):
""" """
Initialize ReelForge Core Initialize Pixelle-Video Core
Args: Args:
config_path: Path to configuration file config_path: Path to configuration file
@@ -82,13 +82,13 @@ class ReelForgeCore:
This initializes all services and must be called before using any capabilities. This initializes all services and must be called before using any capabilities.
Example: Example:
await reelforge.initialize() await pixelle_video.initialize()
""" """
if self._initialized: if self._initialized:
logger.warning("ReelForge already initialized") logger.warning("Pixelle-Video already initialized")
return return
logger.info("🚀 Initializing ReelForge...") logger.info("🚀 Initializing Pixelle-Video...")
# 1. Initialize core services (no capability layer) # 1. Initialize core services (no capability layer)
self.llm = LLMService(self.config) self.llm = LLMService(self.config)
@@ -107,18 +107,18 @@ class ReelForgeCore:
self.generate_video = VideoGeneratorService(self) self.generate_video = VideoGeneratorService(self)
self._initialized = True self._initialized = True
logger.info("ReelForge initialized successfully\n") logger.info("Pixelle-Video initialized successfully\n")
@property @property
def project_name(self) -> str: def project_name(self) -> str:
"""Get project name from config""" """Get project name from config"""
return self.config.get("project_name", "ReelForge") return self.config.get("project_name", "Pixelle-Video")
def __repr__(self) -> str: def __repr__(self) -> str:
"""String representation""" """String representation"""
status = "initialized" if self._initialized else "not initialized" 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 # 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 # Use provided output path or auto-generate
if output_path is None: if output_path is None:
# Fallback: auto-generate (for backward compatibility) # 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_filename = f"frame_{uuid.uuid4().hex[:16]}.png"
output_path = get_output_path(output_filename) output_path = get_output_path(output_filename)
else: else:

View File

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

View File

@@ -7,7 +7,7 @@ from typing import Optional
from comfykit import ComfyKit from comfykit import ComfyKit
from loguru import logger from loguru import logger
from reelforge.services.comfy_base_service import ComfyBaseService from pixelle_video.services.comfy_base_service import ComfyBaseService
class ImageService(ComfyBaseService): class ImageService(ComfyBaseService):
@@ -18,16 +18,16 @@ class ImageService(ComfyBaseService):
Usage: Usage:
# Use default workflow (workflows/image_flux.json) # 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 # Use specific workflow
image_url = await reelforge.image( image_url = await pixelle_video.image(
prompt="a cat", prompt="a cat",
workflow="image_flux.json" workflow="image_flux.json"
) )
# List available workflows # List available workflows
workflows = reelforge.image.list_workflows() workflows = pixelle_video.image.list_workflows()
""" """
WORKFLOW_PREFIX = "image_" WORKFLOW_PREFIX = "image_"
@@ -82,16 +82,16 @@ class ImageService(ComfyBaseService):
Examples: Examples:
# Simplest: use default workflow (workflows/image_flux.json) # 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 # Use specific workflow
image_url = await reelforge.image( image_url = await pixelle_video.image(
prompt="a cat", prompt="a cat",
workflow="image_flux.json" workflow="image_flux.json"
) )
# With additional parameters # With additional parameters
image_url = await reelforge.image( image_url = await pixelle_video.image(
prompt="a cat", prompt="a cat",
workflow="image_flux.json", workflow="image_flux.json",
width=1024, width=1024,
@@ -101,13 +101,13 @@ class ImageService(ComfyBaseService):
) )
# With absolute path # With absolute path
image_url = await reelforge.image( image_url = await pixelle_video.image(
prompt="a cat", prompt="a cat",
workflow="/path/to/custom.json" workflow="/path/to/custom.json"
) )
# With custom ComfyUI server # With custom ComfyUI server
image_url = await reelforge.image( image_url = await pixelle_video.image(
prompt="a cat", prompt="a cat",
comfyui_url="http://192.168.1.100:8188" 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 loguru import logger
from reelforge.models.storyboard import StoryboardConfig from pixelle_video.models.storyboard import StoryboardConfig
from reelforge.prompts import build_image_prompt_prompt from pixelle_video.prompts import build_image_prompt_prompt
class ImagePromptGeneratorService: class ImagePromptGeneratorService:
"""Image prompt generation service""" """Image prompt generation service"""
def __init__(self, reelforge_core): def __init__(self, pixelle_video_core):
""" """
Initialize Initialize
Args: Args:
reelforge_core: ReelForgeCore instance pixelle_video_core: PixelleVideoCore instance
""" """
self.core = reelforge_core self.core = pixelle_video_core
async def generate_image_prompts( async def generate_image_prompts(
self, self,
@@ -114,7 +114,7 @@ class ImagePromptGeneratorService:
logger.info(f"✅ All batches completed. Total prompts: {len(base_prompts)}") logger.info(f"✅ All batches completed. Total prompts: {len(base_prompts)}")
# 5. Apply prompt prefix to each prompt # 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) # Get prompt prefix from config (fix: correct path is comfyui.image.prompt_prefix)
image_config = self.core.config.get("comfyui", {}).get("image", {}) image_config = self.core.config.get("comfyui", {}).get("image", {})

View File

@@ -26,10 +26,10 @@ class LLMService:
Usage: Usage:
# Direct call # Direct call
answer = await reelforge.llm("Explain atomic habits") answer = await pixelle_video.llm("Explain atomic habits")
# With parameters # With parameters
answer = await reelforge.llm( answer = await pixelle_video.llm(
prompt="Explain atomic habits in 3 sentences", prompt="Explain atomic habits in 3 sentences",
temperature=0.7, temperature=0.7,
max_tokens=2000 max_tokens=2000
@@ -121,10 +121,10 @@ class LLMService:
Examples: Examples:
# Use config from config.yaml # Use config from config.yaml
answer = await reelforge.llm("Explain atomic habits") answer = await pixelle_video.llm("Explain atomic habits")
# Override with custom parameters # Override with custom parameters
answer = await reelforge.llm( answer = await pixelle_video.llm(
prompt="Explain atomic habits in 3 sentences", prompt="Explain atomic habits in 3 sentences",
api_key="sk-custom-key", api_key="sk-custom-key",
base_url="https://api.custom.com/v1", base_url="https://api.custom.com/v1",
@@ -172,7 +172,7 @@ class LLMService:
Active model name Active model name
Example: 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") 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 loguru import logger
from reelforge.models.storyboard import StoryboardConfig, ContentMetadata from pixelle_video.models.storyboard import StoryboardConfig, ContentMetadata
from reelforge.prompts import ( from pixelle_video.prompts import (
build_topic_narration_prompt, build_topic_narration_prompt,
build_content_narration_prompt, build_content_narration_prompt,
) )
@@ -22,14 +22,14 @@ from reelforge.prompts import (
class NarrationGeneratorService: class NarrationGeneratorService:
"""Narration generation service""" """Narration generation service"""
def __init__(self, reelforge_core): def __init__(self, pixelle_video_core):
""" """
Initialize Initialize
Args: 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( async def generate_narrations(
self, self,

View File

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

View File

@@ -7,7 +7,7 @@ from typing import Optional
from comfykit import ComfyKit from comfykit import ComfyKit
from loguru import logger from loguru import logger
from reelforge.services.comfy_base_service import ComfyBaseService from pixelle_video.services.comfy_base_service import ComfyBaseService
class TTSService(ComfyBaseService): class TTSService(ComfyBaseService):
@@ -18,16 +18,16 @@ class TTSService(ComfyBaseService):
Usage: Usage:
# Use default workflow # Use default workflow
audio_path = await reelforge.tts(text="Hello, world!") audio_path = await pixelle_video.tts(text="Hello, world!")
# Use specific workflow # Use specific workflow
audio_path = await reelforge.tts( audio_path = await pixelle_video.tts(
text="你好,世界!", text="你好,世界!",
workflow="tts_edge.json" workflow="tts_edge.json"
) )
# List available workflows # List available workflows
workflows = reelforge.tts.list_workflows() workflows = pixelle_video.tts.list_workflows()
""" """
WORKFLOW_PREFIX = "tts_" WORKFLOW_PREFIX = "tts_"
@@ -76,16 +76,16 @@ class TTSService(ComfyBaseService):
Examples: Examples:
# Simplest: use default workflow # Simplest: use default workflow
audio_path = await reelforge.tts(text="Hello, world!") audio_path = await pixelle_video.tts(text="Hello, world!")
# Use specific workflow # Use specific workflow
audio_path = await reelforge.tts( audio_path = await pixelle_video.tts(
text="你好,世界!", text="你好,世界!",
workflow="tts_edge.json" workflow="tts_edge.json"
) )
# With voice and speed # With voice and speed
audio_path = await reelforge.tts( audio_path = await pixelle_video.tts(
text="Hello", text="Hello",
workflow="tts_edge.json", workflow="tts_edge.json",
voice="[Chinese] zh-CN Xiaoxiao", voice="[Chinese] zh-CN Xiaoxiao",
@@ -93,13 +93,13 @@ class TTSService(ComfyBaseService):
) )
# With absolute path # With absolute path
audio_path = await reelforge.tts( audio_path = await pixelle_video.tts(
text="Hello", text="Hello",
workflow="/path/to/custom_tts.json" workflow="/path/to/custom_tts.json"
) )
# With custom ComfyUI server # With custom ComfyUI server
audio_path = await reelforge.tts( audio_path = await pixelle_video.tts(
text="Hello", text="Hello",
comfyui_url="http://192.168.1.100:8188" 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 loguru import logger
from reelforge.models.progress import ProgressEvent from pixelle_video.models.progress import ProgressEvent
from reelforge.models.storyboard import ( from pixelle_video.models.storyboard import (
Storyboard, Storyboard,
StoryboardFrame, StoryboardFrame,
StoryboardConfig, StoryboardConfig,
@@ -32,14 +32,14 @@ class VideoGeneratorService:
5. Add BGM (optional) 5. Add BGM (optional)
""" """
def __init__(self, reelforge_core): def __init__(self, pixelle_video_core):
""" """
Initialize video generator service Initialize video generator service
Args: Args:
reelforge_core: ReelForgeCore instance pixelle_video_core: PixelleVideoCore instance
""" """
self.core = reelforge_core self.core = pixelle_video_core
async def __call__( async def __call__(
self, self,
@@ -149,7 +149,7 @@ class VideoGeneratorService:
Examples: Examples:
# Generate mode: LLM creates narrations from topic # Generate mode: LLM creates narrations from topic
>>> result = await reelforge.generate_video( >>> result = await pixelle_video.generate_video(
... text="如何在信息爆炸时代保持深度思考", ... text="如何在信息爆炸时代保持深度思考",
... mode="generate", ... mode="generate",
... n_scenes=5, ... n_scenes=5,
@@ -161,7 +161,7 @@ class VideoGeneratorService:
... 第一个技巧是专注力训练每天冥想10分钟 ... 第一个技巧是专注力训练每天冥想10分钟
... 第二个技巧是主动回忆学完立即复述 ... 第二个技巧是主动回忆学完立即复述
... 第三个技巧是间隔重复学习后定期复习''' ... 第三个技巧是间隔重复学习后定期复习'''
>>> result = await reelforge.generate_video( >>> result = await pixelle_video.generate_video(
... text=script, ... text=script,
... mode="fixed", ... mode="fixed",
... title="三个学习技巧" ... title="三个学习技巧"
@@ -190,7 +190,7 @@ class VideoGeneratorService:
logger.info(f" Title: '{final_title}' (LLM-generated)") logger.info(f" Title: '{final_title}' (LLM-generated)")
# ========== Step 0.5: Create isolated task directory ========== # ========== Step 0.5: Create isolated task directory ==========
from reelforge.utils.os_util import ( from pixelle_video.utils.os_util import (
create_task_output_dir, create_task_output_dir,
get_task_final_video_path get_task_final_video_path
) )
@@ -352,7 +352,7 @@ class VideoGeneratorService:
self._report_progress(progress_callback, "concatenating", 0.85) self._report_progress(progress_callback, "concatenating", 0.85)
segment_paths = [frame.video_segment_path for frame in storyboard.frames] 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() video_service = VideoService()
final_video_path = video_service.concat_videos( 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 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. Inspired by Pixelle-MCP's os_util.py.
""" """
@@ -12,9 +12,9 @@ from pathlib import Path
from typing import Optional, Tuple, Literal 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: Returns:
Current working directory as string Current working directory as string
@@ -22,14 +22,14 @@ def get_reelforge_root_path() -> str:
return str(Path.cwd()) 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: Returns:
Root path as string Root path as string
""" """
root_path = get_reelforge_root_path() root_path = get_pixelle_video_root_path()
root_path_obj = Path(root_path) root_path_obj = Path(root_path)
output_dir = root_path_obj / 'output' output_dir = root_path_obj / 'output'
output_dir.mkdir(parents=True, exist_ok=True) 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: def get_root_path(*paths: str) -> str:
""" """
Get path relative to ReelForge root path Get path relative to Pixelle-Video root path
Args: Args:
*paths: Path components to join *paths: Path components to join
@@ -51,7 +51,7 @@ def get_root_path(*paths: str) -> str:
get_root_path("temp", "audio.mp3") get_root_path("temp", "audio.mp3")
# Returns: "/path/to/project/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: if paths:
return os.path.join(root_path, *paths) return os.path.join(root_path, *paths)
return root_path return root_path
@@ -59,7 +59,7 @@ def get_root_path(*paths: str) -> str:
def get_temp_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: Args:
*paths: Path components to join *paths: Path components to join
@@ -79,7 +79,7 @@ def get_temp_path(*paths: str) -> str:
def get_data_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: Args:
*paths: Path components to join *paths: Path components to join
@@ -99,7 +99,7 @@ def get_data_path(*paths: str) -> str:
def get_output_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: Args:
*paths: Path components to join *paths: Path components to join

View File

@@ -1,9 +1,9 @@
[project] [project]
name = "reelforge" name = "pixelle-video"
version = "0.1.0" 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 = [ authors = [
{name = "ReelForge Team"} {name = "PixelleLab"}
] ]
readme = "README.md" readme = "README.md"
requires-python = ">=3.11" requires-python = ">=3.11"
@@ -34,7 +34,8 @@ dev = [
] ]
[project.scripts] [project.scripts]
reelforge = "reelforge.cli:main" pixelle-video = "pixelle_video.cli:main"
pvideo = "pixelle_video.cli:main"
[build-system] [build-system]
requires = ["hatchling"] 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 #!/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 "" echo ""
# Check if config.yaml exists # Check if config.yaml exists
@@ -28,7 +28,7 @@ if [ ! -z "$PID" ]; then
fi fi
# Start Streamlit in background with nohup # 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 & nohup uv run streamlit run web/app.py --server.port $PORT > nohup.out 2>&1 &
# Wait a moment and check if the process started # Wait a moment and check if the process started
@@ -36,12 +36,12 @@ sleep 2
NEW_PID=$(lsof -ti:$PORT) NEW_PID=$(lsof -ti:$PORT)
if [ ! -z "$NEW_PID" ]; then if [ ! -z "$NEW_PID" ]; then
echo "✅ ReelForge Web UI started successfully!" echo "✅ Pixelle-Video Web UI started successfully!"
echo "📝 Process ID: $NEW_PID" echo "📝 Process ID: $NEW_PID"
echo "🌐 Access at: http://localhost:$PORT" echo "🌐 Access at: http://localhost:$PORT"
echo "📄 Logs: nohup.out" echo "📄 Logs: nohup.out"
else else
echo "❌ Failed to start ReelForge Web UI" echo "❌ Failed to start Pixelle-Video Web UI"
echo "📄 Check nohup.out for error details" echo "📄 Check nohup.out for error details"
exit 1 exit 1
fi fi

View File

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

View File

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

View File

@@ -923,7 +923,7 @@
</div> </div>
<div class="slogan">Open Source Omnimodal AI Creative Agent</div> <div class="slogan">Open Source Omnimodal AI Creative Agent</div>
<div class="cta"> <div class="cta">
<div class="follow">ReelForge</div> <div class="follow">Pixelle-Video</div>
<div class="hashtags"> <div class="hashtags">
<span>#AI创作</span> <span>#AI创作</span>
<span>#短视频</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 }, { 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]] [[package]]
name = "pluggy" name = "pluggy"
version = "1.6.0" 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 }, { 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]] [[package]]
name = "referencing" name = "referencing"
version = "0.36.2" 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. A simple web interface for generating short videos from content.
""" """
@@ -14,12 +14,12 @@ from loguru import logger
# Import i18n and config manager # Import i18n and config manager
from web.i18n import load_locales, set_language, tr, get_available_languages, get_language from web.i18n import load_locales, set_language, tr, get_available_languages, get_language
from reelforge.config import config_manager from pixelle_video.config import config_manager
from reelforge.models.progress import ProgressEvent from pixelle_video.models.progress import ProgressEvent
# Setup page config (must be first) # Setup page config (must be first)
st.set_page_config( st.set_page_config(
page_title="ReelForge - AI Video Generator", page_title="Pixelle-Video - AI Video Generator",
page_icon="🎬", page_icon="🎬",
layout="wide", layout="wide",
initial_sidebar_state="collapsed", initial_sidebar_state="collapsed",
@@ -62,19 +62,19 @@ def init_i18n():
# ============================================================================ # ============================================================================
# Initialize ReelForge # Initialize Pixelle-Video
# ============================================================================ # ============================================================================
def get_reelforge(): def get_pixelle_video():
"""Get initialized ReelForge instance (no caching - always fresh)""" """Get initialized Pixelle-Video instance (no caching - always fresh)"""
from reelforge.service import ReelForgeCore from pixelle_video.service import PixelleVideoCore
logger.info("Initializing ReelForge...") logger.info("Initializing Pixelle-Video...")
reelforge = ReelForgeCore() pixelle_video = PixelleVideoCore()
run_async(reelforge.initialize()) run_async(pixelle_video.initialize())
logger.info("ReelForge initialized") 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')}**") st.markdown(f"**{tr('settings.llm.title')}**")
# Quick preset selection # 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 # Custom at the end
preset_names = get_preset_names() + ["Custom"] preset_names = get_preset_names() + ["Custom"]
@@ -266,8 +266,8 @@ def render_advanced_settings():
with col2: with col2:
if st.button(tr("btn.reset_config"), use_container_width=True, key="reset_config_btn"): if st.button(tr("btn.reset_config"), use_container_width=True, key="reset_config_btn"):
# Reset to default # Reset to default
from reelforge.config.schema import ReelForgeConfig from pixelle_video.config.schema import PixelleVideoConfig
config_manager.config = ReelForgeConfig() config_manager.config = PixelleVideoConfig()
config_manager.save() config_manager.save()
st.success(tr("status.config_reset")) st.success(tr("status.config_reset"))
safe_rerun() safe_rerun()
@@ -315,8 +315,8 @@ def main():
with col2: with col2:
render_language_selector() render_language_selector()
# Initialize ReelForge # Initialize Pixelle-Video
reelforge = get_reelforge() pixelle_video = get_pixelle_video()
# ======================================================================== # ========================================================================
# System Configuration (Required) # System Configuration (Required)
@@ -443,7 +443,7 @@ def main():
st.markdown(tr("tts.how")) st.markdown(tr("tts.how"))
# Get available TTS workflows # Get available TTS workflows
tts_workflows = reelforge.tts.list_workflows() tts_workflows = pixelle_video.tts.list_workflows()
# Build options for selectbox # Build options for selectbox
tts_workflow_options = [wf["display_name"] for wf in tts_workflows] tts_workflow_options = [wf["display_name"] for wf in tts_workflows]
@@ -486,7 +486,7 @@ def main():
with st.spinner(tr("tts.previewing")): with st.spinner(tr("tts.previewing")):
try: try:
# Generate preview audio using selected workflow (use default voice and speed) # 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, text=preview_text,
workflow=tts_workflow_key workflow=tts_workflow_key
)) ))
@@ -522,8 +522,8 @@ def main():
st.markdown(f"**{tr('help.how')}**") st.markdown(f"**{tr('help.how')}**")
st.markdown(tr("style.workflow_how")) st.markdown(tr("style.workflow_how"))
# Get available workflows from reelforge (with source info) # Get available workflows from pixelle_video (with source info)
workflows = reelforge.image.list_workflows() workflows = pixelle_video.image.list_workflows()
# Build options for selectbox # Build options for selectbox
# Display: "image_flux.json - Runninghub" # Display: "image_flux.json - Runninghub"
@@ -586,13 +586,13 @@ def main():
if st.button(tr("style.preview"), key="preview_style", use_container_width=True): if st.button(tr("style.preview"), key="preview_style", use_container_width=True):
with st.spinner(tr("style.previewing")): with st.spinner(tr("style.previewing")):
try: 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 # Build final prompt with prefix
final_prompt = build_image_prompt(test_prompt, prompt_prefix) final_prompt = build_image_prompt(test_prompt, prompt_prefix)
# Generate preview image (small size for speed) # 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, prompt=final_prompt,
workflow=workflow_key, workflow=workflow_key,
width=512, width=512,
@@ -707,7 +707,7 @@ def main():
if st.button(tr("template.preview_button"), key="btn_preview_template", use_container_width=True): if st.button(tr("template.preview_button"), key="btn_preview_template", use_container_width=True):
with st.spinner(tr("template.preview_generating")): with st.spinner(tr("template.preview_generating")):
try: try:
from reelforge.services.frame_html import HTMLFrameGenerator from pixelle_video.services.frame_html import HTMLFrameGenerator
# Use the currently selected template # Use the currently selected template
template_path = f"templates/{frame_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 progress_bar.progress(min(int(event.progress * 100), 99)) # Cap at 99% until complete
# Generate video (directly pass parameters) # Generate video (directly pass parameters)
result = run_async(reelforge.generate_video( result = run_async(pixelle_video.generate_video(
text=text, text=text,
mode=mode, mode=mode,
title=title if title else None, 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 import json
@@ -64,7 +64,7 @@ def tr(key: str, fallback: Optional[str] = None, **kwargs) -> str:
Translated text Translated text
Example: Example:
tr("app.title") # => "ReelForge" tr("app.title") # => "Pixelle-Video"
tr("error.missing_field", field="API Key") # => "请填写 API Key" tr("error.missing_field", field="API Key") # => "请填写 API Key"
""" """
locale = _locales.get(_current_language, {}) locale = _locales.get(_current_language, {})

View File

@@ -1,8 +1,8 @@
{ {
"language_name": "English", "language_name": "English",
"t": { "t": {
"app.title": "⚡ ReelForge - AI Auto Short Video Engine", "app.title": "⚡ Pixelle-Video - AI Auto Short Video Engine",
"app.subtitle": "Forge your perfect reel engine", "app.subtitle": "Powered by Pixelle AI",
"section.content_input": "📖 Content Input", "section.content_input": "📖 Content Input",
"section.bgm": "🎵 Background Music", "section.bgm": "🎵 Background Music",
@@ -78,7 +78,7 @@
"template.preview_param_width": "Width", "template.preview_param_width": "Width",
"template.preview_param_height": "Height", "template.preview_param_height": "Height",
"template.preview_default_title": "AI Changes Content Creation", "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_button": "🖼️ Generate Preview",
"template.preview_generating": "Generating template preview...", "template.preview_generating": "Generating template preview...",
"template.preview_success": "✅ Preview generated successfully!", "template.preview_success": "✅ Preview generated successfully!",
@@ -172,7 +172,7 @@
"tts.preview_success": "✅ Preview generated successfully!", "tts.preview_success": "✅ Preview generated successfully!",
"tts.preview_failed": "❌ Preview failed: {error}", "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", "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)", "wizard.llm_required": "🤖 Large Language Model Configuration (Required)",

View File

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