项目重命名: ReelForge => Pixelle-Video
This commit is contained in:
26
README.md
26
README.md
@@ -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
|
||||
|
||||
[](https://star-history.com/#JarvisAIHub/ReelForge&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>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
ReelForge API Layer
|
||||
Pixelle-Video API Layer
|
||||
|
||||
FastAPI-based REST API for video generation services.
|
||||
"""
|
||||
|
||||
24
api/app.py
24
api/app.py
@@ -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}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
26
pixelle_video/__init__.py
Normal 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"]
|
||||
|
||||
@@ -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())
|
||||
@@ -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",
|
||||
@@ -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)"""
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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__ = [
|
||||
@@ -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()
|
||||
30
pixelle_video/services/__init__.py
Normal file
30
pixelle_video/services/__init__.py
Normal 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",
|
||||
]
|
||||
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
@@ -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"
|
||||
)
|
||||
@@ -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", {})
|
||||
@@ -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")
|
||||
|
||||
@@ -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,
|
||||
@@ -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)
|
||||
@@ -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"
|
||||
)
|
||||
@@ -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(
|
||||
3
pixelle_video/utils/__init__.py
Normal file
3
pixelle_video/utils/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pixelle-Video utilities
|
||||
"""
|
||||
@@ -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
|
||||
@@ -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"]
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
"""
|
||||
ReelForge utilities
|
||||
"""
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
108
uv.lock
generated
@@ -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"
|
||||
|
||||
52
web/app.py
52
web/app.py
@@ -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,
|
||||
|
||||
@@ -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, {})
|
||||
|
||||
@@ -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)",
|
||||
|
||||
@@ -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": "🤖 大语言模型配置(必需)",
|
||||
|
||||
Reference in New Issue
Block a user