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:
empty
2026-01-10 16:13:02 +08:00
parent 6bf16936af
commit 3f59b324ad
8 changed files with 61 additions and 18 deletions

21
.env.example Normal file
View 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

View File

@@ -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

View File

@@ -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
View File

@@ -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}"
}

View File

@@ -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*`,
},
]
},

View File

@@ -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;">

View File

@@ -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;">

View File

@@ -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;">