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,24 +47,31 @@ from api.schemas.editor import (
|
|||||||
from fastapi import BackgroundTasks
|
from fastapi import BackgroundTasks
|
||||||
import asyncio
|
import asyncio
|
||||||
import uuid as uuid_module
|
import uuid as uuid_module
|
||||||
|
import os
|
||||||
|
|
||||||
router = APIRouter(prefix="/editor", tags=["Editor"])
|
router = APIRouter(prefix="/editor", tags=["Editor"])
|
||||||
|
|
||||||
# Export task storage
|
# Export task storage
|
||||||
_export_tasks: dict = {}
|
_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"""
|
"""Convert local file path to URL accessible through API"""
|
||||||
if not file_path:
|
if not file_path:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
if base_url is None:
|
||||||
|
base_url = f"http://localhost:{API_PORT}"
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Normalize path separators
|
# Normalize path separators
|
||||||
file_path = file_path.replace("\\", "/")
|
file_path = file_path.replace("\\", "/")
|
||||||
|
|
||||||
# Extract relative path from output directory
|
# Extract relative path from output directory
|
||||||
parts = file_path.split("/")
|
parts = file_path.split("/")
|
||||||
try:
|
try:
|
||||||
@@ -73,7 +80,7 @@ def _path_to_url(file_path: str, base_url: str = "http://localhost:8000") -> str
|
|||||||
relative_path = "/".join(relative_parts)
|
relative_path = "/".join(relative_parts)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
relative_path = Path(file_path).name
|
relative_path = Path(file_path).name
|
||||||
|
|
||||||
return f"{base_url}/api/files/{relative_path}"
|
return f"{base_url}/api/files/{relative_path}"
|
||||||
|
|
||||||
|
|
||||||
@@ -951,8 +958,9 @@ async def export_video(
|
|||||||
for frame in sorted_frames:
|
for frame in sorted_frames:
|
||||||
path = frame.get("video_segment_path", "")
|
path = frame.get("video_segment_path", "")
|
||||||
if path.startswith("http"):
|
if path.startswith("http"):
|
||||||
# Extract path from URL
|
# Extract path from URL (format: http://localhost:{port}/api/files/{relative_path})
|
||||||
path = path.replace("http://localhost:8000/api/files/", "output/")
|
if "/api/files/" in path:
|
||||||
|
path = "output/" + path.split("/api/files/")[-1]
|
||||||
video_segments.append(path)
|
video_segments.append(path)
|
||||||
|
|
||||||
_export_tasks[task_id]["progress"] = 0.3
|
_export_tasks[task_id]["progress"] = 0.3
|
||||||
|
|||||||
@@ -349,8 +349,9 @@ async def extract_style(
|
|||||||
# Convert URL to file path if needed
|
# Convert URL to file path if needed
|
||||||
actual_path = image_path
|
actual_path = image_path
|
||||||
if image_path.startswith("http"):
|
if image_path.startswith("http"):
|
||||||
# Extract path from URL like http://localhost:8000/api/files/...
|
# Extract path from URL (format: http://localhost:{port}/api/files/{relative_path})
|
||||||
actual_path = image_path.replace("http://localhost:8000/api/files/", "output/")
|
if "/api/files/" in image_path:
|
||||||
|
actual_path = "output/" + image_path.split("/api/files/")[-1]
|
||||||
|
|
||||||
# Check if file exists
|
# Check if file exists
|
||||||
import os
|
import os
|
||||||
|
|||||||
6
dev.sh
6
dev.sh
@@ -54,7 +54,7 @@ print_banner() {
|
|||||||
|
|
||||||
start_api() {
|
start_api() {
|
||||||
echo -e "${GREEN}🚀 Starting FastAPI Backend...${NC}"
|
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 $! > "$PID_DIR/api.pid"
|
||||||
echo -e " ${GREEN}✓${NC} API running at: ${YELLOW}http://localhost:$API_PORT${NC}"
|
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}"
|
echo -e " ${GREEN}✓${NC} API Docs at: ${YELLOW}http://localhost:$API_PORT/docs${NC}"
|
||||||
@@ -63,7 +63,7 @@ start_api() {
|
|||||||
start_editor() {
|
start_editor() {
|
||||||
echo -e "${GREEN}🎬 Starting Next.js Editor...${NC}"
|
echo -e "${GREEN}🎬 Starting Next.js Editor...${NC}"
|
||||||
cd "$PROJECT_ROOT/frontend"
|
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"
|
echo $! > "$PID_DIR/editor.pid"
|
||||||
cd "$PROJECT_ROOT"
|
cd "$PROJECT_ROOT"
|
||||||
echo -e " ${GREEN}✓${NC} Editor running at: ${YELLOW}http://localhost:$EDITOR_PORT${NC}"
|
echo -e " ${GREEN}✓${NC} Editor running at: ${YELLOW}http://localhost:$EDITOR_PORT${NC}"
|
||||||
@@ -71,7 +71,7 @@ start_editor() {
|
|||||||
|
|
||||||
start_web() {
|
start_web() {
|
||||||
echo -e "${GREEN}🌐 Starting Streamlit Web UI...${NC}"
|
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 $! > "$PID_DIR/web.pid"
|
||||||
echo -e " ${GREEN}✓${NC} Web UI running at: ${YELLOW}http://localhost:$WEB_PORT${NC}"
|
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 = {
|
const nextConfig: NextConfig = {
|
||||||
async rewrites() {
|
async rewrites() {
|
||||||
|
const apiPort = process.env.API_PORT || '8000';
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
source: '/api/:path*',
|
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.models.progress import ProgressEvent
|
||||||
from pixelle_video.config import config_manager
|
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):
|
def render_output_preview(pixelle_video, video_params):
|
||||||
"""Render output preview section (right column)"""
|
"""Render output preview section (right column)"""
|
||||||
@@ -135,7 +139,7 @@ def render_single_output(pixelle_video, video_params):
|
|||||||
|
|
||||||
# Submit to async API
|
# Submit to async API
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
"http://localhost:8000/api/video/generate/async",
|
f"http://localhost:{API_PORT}/api/video/generate/async",
|
||||||
json=api_payload,
|
json=api_payload,
|
||||||
timeout=30
|
timeout=30
|
||||||
)
|
)
|
||||||
@@ -309,7 +313,7 @@ def render_single_output(pixelle_video, video_params):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
if task_id:
|
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(
|
st.markdown(
|
||||||
f'''
|
f'''
|
||||||
<a href="{editor_url}" target="_blank" style="text-decoration: none;">
|
<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.i18n import tr
|
||||||
from web.utils.async_helpers import run_async
|
from web.utils.async_helpers import run_async
|
||||||
|
|
||||||
|
# Get ports from environment
|
||||||
|
EDITOR_PORT = os.getenv("EDITOR_PORT", "3000")
|
||||||
|
|
||||||
# Page config
|
# Page config
|
||||||
st.set_page_config(
|
st.set_page_config(
|
||||||
page_title="History - Pixelle-Video",
|
page_title="History - Pixelle-Video",
|
||||||
@@ -363,7 +366,7 @@ def render_task_detail_modal(task_id: str, pixelle_video):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Open in Editor button
|
# 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(
|
st.markdown(
|
||||||
f'''
|
f'''
|
||||||
<a href="{editor_url}" target="_blank" style="text-decoration: none;">
|
<a href="{editor_url}" target="_blank" style="text-decoration: none;">
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ Features:
|
|||||||
import streamlit as st
|
import streamlit as st
|
||||||
import requests
|
import requests
|
||||||
import time
|
import time
|
||||||
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from web.i18n import tr, get_language
|
from web.i18n import tr, get_language
|
||||||
@@ -33,8 +34,12 @@ st.set_page_config(
|
|||||||
layout="wide",
|
layout="wide",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Get ports from environment
|
||||||
|
API_PORT = os.getenv("API_PORT", "8000")
|
||||||
|
EDITOR_PORT = os.getenv("EDITOR_PORT", "3000")
|
||||||
|
|
||||||
# API endpoint
|
# API endpoint
|
||||||
API_BASE = "http://localhost:8000/api"
|
API_BASE = f"http://localhost:{API_PORT}/api"
|
||||||
|
|
||||||
|
|
||||||
def get_all_tasks():
|
def get_all_tasks():
|
||||||
@@ -183,7 +188,7 @@ def render_task_card(task):
|
|||||||
with col_a:
|
with col_a:
|
||||||
st.success("✨ 视频生成成功")
|
st.success("✨ 视频生成成功")
|
||||||
with col_b:
|
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(
|
st.markdown(
|
||||||
f'''
|
f'''
|
||||||
<a href="{editor_url}" target="_blank" style="text-decoration: none;">
|
<a href="{editor_url}" target="_blank" style="text-decoration: none;">
|
||||||
|
|||||||
Reference in New Issue
Block a user