fix: Remove hardcoded ports, support custom port configuration
- Replace all hardcoded localhost:8000/3000/8501 with environment variables - Frontend: Use API_PORT env var in next.config.ts - Backend: Use API_PORT env var in editor.py and quality.py - Web UI: Use API_PORT and EDITOR_PORT env vars in all Streamlit pages - Update dev.sh to pass environment variables to all services - Add .env.example with port configuration template Now supports custom ports via environment variables: API_PORT=8080 EDITOR_PORT=3001 WEB_PORT=8502 ./dev.sh Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
21
.env.example
Normal file
21
.env.example
Normal file
@@ -0,0 +1,21 @@
|
||||
# Pixelle-Video Environment Configuration
|
||||
# Copy this file to .env and customize as needed
|
||||
|
||||
# ============================================================================
|
||||
# Port Configuration
|
||||
# ============================================================================
|
||||
|
||||
# FastAPI Backend Port
|
||||
API_PORT=8000
|
||||
|
||||
# Next.js Editor Port
|
||||
EDITOR_PORT=3000
|
||||
|
||||
# Streamlit Web UI Port
|
||||
WEB_PORT=8501
|
||||
|
||||
# ============================================================================
|
||||
# Other Configuration
|
||||
# ============================================================================
|
||||
|
||||
# Add other environment variables here as needed
|
||||
@@ -47,18 +47,25 @@ from api.schemas.editor import (
|
||||
from fastapi import BackgroundTasks
|
||||
import asyncio
|
||||
import uuid as uuid_module
|
||||
import os
|
||||
|
||||
router = APIRouter(prefix="/editor", tags=["Editor"])
|
||||
|
||||
# Export task storage
|
||||
_export_tasks: dict = {}
|
||||
|
||||
# Get API port from environment
|
||||
API_PORT = os.getenv("API_PORT", "8000")
|
||||
|
||||
def _path_to_url(file_path: str, base_url: str = "http://localhost:8000") -> str:
|
||||
|
||||
def _path_to_url(file_path: str, base_url: str = None) -> str:
|
||||
"""Convert local file path to URL accessible through API"""
|
||||
if not file_path:
|
||||
return None
|
||||
|
||||
if base_url is None:
|
||||
base_url = f"http://localhost:{API_PORT}"
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
@@ -951,8 +958,9 @@ async def export_video(
|
||||
for frame in sorted_frames:
|
||||
path = frame.get("video_segment_path", "")
|
||||
if path.startswith("http"):
|
||||
# Extract path from URL
|
||||
path = path.replace("http://localhost:8000/api/files/", "output/")
|
||||
# Extract path from URL (format: http://localhost:{port}/api/files/{relative_path})
|
||||
if "/api/files/" in path:
|
||||
path = "output/" + path.split("/api/files/")[-1]
|
||||
video_segments.append(path)
|
||||
|
||||
_export_tasks[task_id]["progress"] = 0.3
|
||||
|
||||
@@ -349,8 +349,9 @@ async def extract_style(
|
||||
# Convert URL to file path if needed
|
||||
actual_path = image_path
|
||||
if image_path.startswith("http"):
|
||||
# Extract path from URL like http://localhost:8000/api/files/...
|
||||
actual_path = image_path.replace("http://localhost:8000/api/files/", "output/")
|
||||
# Extract path from URL (format: http://localhost:{port}/api/files/{relative_path})
|
||||
if "/api/files/" in image_path:
|
||||
actual_path = "output/" + image_path.split("/api/files/")[-1]
|
||||
|
||||
# Check if file exists
|
||||
import os
|
||||
|
||||
6
dev.sh
6
dev.sh
@@ -54,7 +54,7 @@ print_banner() {
|
||||
|
||||
start_api() {
|
||||
echo -e "${GREEN}🚀 Starting FastAPI Backend...${NC}"
|
||||
uv run python api/app.py --port $API_PORT --reload &
|
||||
API_PORT=$API_PORT uv run python api/app.py --port $API_PORT --reload &
|
||||
echo $! > "$PID_DIR/api.pid"
|
||||
echo -e " ${GREEN}✓${NC} API running at: ${YELLOW}http://localhost:$API_PORT${NC}"
|
||||
echo -e " ${GREEN}✓${NC} API Docs at: ${YELLOW}http://localhost:$API_PORT/docs${NC}"
|
||||
@@ -63,7 +63,7 @@ start_api() {
|
||||
start_editor() {
|
||||
echo -e "${GREEN}🎬 Starting Next.js Editor...${NC}"
|
||||
cd "$PROJECT_ROOT/frontend"
|
||||
PORT=$EDITOR_PORT npm run dev &
|
||||
API_PORT=$API_PORT PORT=$EDITOR_PORT npm run dev &
|
||||
echo $! > "$PID_DIR/editor.pid"
|
||||
cd "$PROJECT_ROOT"
|
||||
echo -e " ${GREEN}✓${NC} Editor running at: ${YELLOW}http://localhost:$EDITOR_PORT${NC}"
|
||||
@@ -71,7 +71,7 @@ start_editor() {
|
||||
|
||||
start_web() {
|
||||
echo -e "${GREEN}🌐 Starting Streamlit Web UI...${NC}"
|
||||
uv run streamlit run web/app.py --server.port $WEB_PORT &
|
||||
API_PORT=$API_PORT EDITOR_PORT=$EDITOR_PORT uv run streamlit run web/app.py --server.port $WEB_PORT &
|
||||
echo $! > "$PID_DIR/web.pid"
|
||||
echo -e " ${GREEN}✓${NC} Web UI running at: ${YELLOW}http://localhost:$WEB_PORT${NC}"
|
||||
}
|
||||
|
||||
@@ -2,10 +2,11 @@ import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
async rewrites() {
|
||||
const apiPort = process.env.API_PORT || '8000';
|
||||
return [
|
||||
{
|
||||
source: '/api/:path*',
|
||||
destination: 'http://localhost:8000/api/:path*',
|
||||
destination: `http://localhost:${apiPort}/api/:path*`,
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
@@ -26,6 +26,10 @@ from web.utils.async_helpers import run_async
|
||||
from pixelle_video.models.progress import ProgressEvent
|
||||
from pixelle_video.config import config_manager
|
||||
|
||||
# Get ports from environment
|
||||
API_PORT = os.getenv("API_PORT", "8000")
|
||||
EDITOR_PORT = os.getenv("EDITOR_PORT", "3000")
|
||||
|
||||
|
||||
def render_output_preview(pixelle_video, video_params):
|
||||
"""Render output preview section (right column)"""
|
||||
@@ -135,7 +139,7 @@ def render_single_output(pixelle_video, video_params):
|
||||
|
||||
# Submit to async API
|
||||
response = requests.post(
|
||||
"http://localhost:8000/api/video/generate/async",
|
||||
f"http://localhost:{API_PORT}/api/video/generate/async",
|
||||
json=api_payload,
|
||||
timeout=30
|
||||
)
|
||||
@@ -309,7 +313,7 @@ def render_single_output(pixelle_video, video_params):
|
||||
pass
|
||||
|
||||
if task_id:
|
||||
editor_url = f"http://localhost:3000/editor?storyboard_id={task_id}"
|
||||
editor_url = f"http://localhost:{EDITOR_PORT}/editor?storyboard_id={task_id}"
|
||||
st.markdown(
|
||||
f'''
|
||||
<a href="{editor_url}" target="_blank" style="text-decoration: none;">
|
||||
|
||||
@@ -33,6 +33,9 @@ from web.components.header import render_header
|
||||
from web.i18n import tr
|
||||
from web.utils.async_helpers import run_async
|
||||
|
||||
# Get ports from environment
|
||||
EDITOR_PORT = os.getenv("EDITOR_PORT", "3000")
|
||||
|
||||
# Page config
|
||||
st.set_page_config(
|
||||
page_title="History - Pixelle-Video",
|
||||
@@ -363,7 +366,7 @@ def render_task_detail_modal(task_id: str, pixelle_video):
|
||||
)
|
||||
|
||||
# Open in Editor button
|
||||
editor_url = f"http://localhost:3000/editor?storyboard_id={task_id}"
|
||||
editor_url = f"http://localhost:{EDITOR_PORT}/editor?storyboard_id={task_id}"
|
||||
st.markdown(
|
||||
f'''
|
||||
<a href="{editor_url}" target="_blank" style="text-decoration: none;">
|
||||
|
||||
@@ -22,6 +22,7 @@ Features:
|
||||
import streamlit as st
|
||||
import requests
|
||||
import time
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
from web.i18n import tr, get_language
|
||||
@@ -33,8 +34,12 @@ st.set_page_config(
|
||||
layout="wide",
|
||||
)
|
||||
|
||||
# Get ports from environment
|
||||
API_PORT = os.getenv("API_PORT", "8000")
|
||||
EDITOR_PORT = os.getenv("EDITOR_PORT", "3000")
|
||||
|
||||
# API endpoint
|
||||
API_BASE = "http://localhost:8000/api"
|
||||
API_BASE = f"http://localhost:{API_PORT}/api"
|
||||
|
||||
|
||||
def get_all_tasks():
|
||||
@@ -183,7 +188,7 @@ def render_task_card(task):
|
||||
with col_a:
|
||||
st.success("✨ 视频生成成功")
|
||||
with col_b:
|
||||
editor_url = f"http://localhost:3000/editor?storyboard_id={task_id}"
|
||||
editor_url = f"http://localhost:{EDITOR_PORT}/editor?storyboard_id={task_id}"
|
||||
st.markdown(
|
||||
f'''
|
||||
<a href="{editor_url}" target="_blank" style="text-decoration: none;">
|
||||
|
||||
Reference in New Issue
Block a user