chore: import upstream snapshot with attribution
Deploy Documentation / deploy (push) Failing after 1s
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "Pixelle-Video Codespace",
|
||||
"image": "mcr.microsoft.com/devcontainers/python:1-3.11",
|
||||
"forwardPorts": [8501],
|
||||
"postCreateCommand": ".devcontainer/postCreate.sh",
|
||||
"postStartCommand": ".devcontainer/postStart.sh",
|
||||
"remoteUser": "vscode"
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
set -uo pipefail
|
||||
|
||||
echo "[devcontainer] Running postCreate tasks..."
|
||||
|
||||
cd /workspaces/Pixelle-Video
|
||||
|
||||
# ============================================================================
|
||||
# System Dependencies Installation
|
||||
# ============================================================================
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Remove problematic Yarn repository if it exists
|
||||
echo "[devcontainer] Removing problematic repositories..."
|
||||
sudo rm -f /etc/apt/sources.list.d/yarn.sources 2>/dev/null || true
|
||||
sudo rm -f /etc/apt/sources.list.d/yarn.list 2>/dev/null || true
|
||||
|
||||
# Update package lists
|
||||
echo "[devcontainer] Updating package lists..."
|
||||
sudo apt-get update -y || {
|
||||
echo "[devcontainer] Warning: apt-get update had issues, continuing anyway..."
|
||||
true
|
||||
}
|
||||
|
||||
# Install system packages needed by the project
|
||||
echo "[devcontainer] Installing system packages..."
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
ffmpeg \
|
||||
fontconfig \
|
||||
fonts-liberation \
|
||||
fonts-noto-cjk \
|
||||
wget \
|
||||
xdg-utils \
|
||||
ca-certificates || true
|
||||
|
||||
# Verify installation
|
||||
echo "[devcontainer] Verifying system packages..."
|
||||
echo "[devcontainer] Chinese fonts (sample):"
|
||||
fc-list :lang=zh | head -n 10 || true
|
||||
|
||||
# ============================================================================
|
||||
# Python Dependencies Installation
|
||||
# ============================================================================
|
||||
|
||||
# Install uv package manager
|
||||
echo "[devcontainer] Installing uv package manager..."
|
||||
pip install uv --quiet
|
||||
|
||||
# Install Python dependencies with uv
|
||||
echo "[devcontainer] Installing Python dependencies with uv..."
|
||||
uv sync --frozen
|
||||
|
||||
# Install Playwright browser (Chromium for HTML template rendering)
|
||||
echo "[devcontainer] Installing Playwright Chromium browser..."
|
||||
uv run playwright install --with-deps chromium || true
|
||||
|
||||
echo "[devcontainer] postCreate complete. Streamlit will start automatically via postStart.sh"
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "[devcontainer] postStart: launching Pixelle-Video Web UI in background..."
|
||||
cd /workspaces/Pixelle-Video
|
||||
|
||||
# Set Streamlit config for headless mode and proper port binding
|
||||
export STREAMLIT_SERVER_PORT=8501
|
||||
export STREAMLIT_SERVER_ADDRESS=0.0.0.0
|
||||
export STREAMLIT_SERVER_HEADLESS=true
|
||||
export STREAMLIT_LOGGER_LEVEL=info
|
||||
export UV_LINK_MODE=copy
|
||||
|
||||
# Start the web UI in background so the forwarded port is ready
|
||||
nohup bash start_web.sh > /tmp/pixelle_streamlit.log 2>&1 &
|
||||
WEB_PID=$!
|
||||
echo "[devcontainer] Streamlit started with PID $WEB_PID (logs: /tmp/pixelle_streamlit.log)"
|
||||
|
||||
# Wait briefly for startup and show success message
|
||||
sleep 3
|
||||
if ps -p $WEB_PID > /dev/null 2>&1; then
|
||||
echo ""
|
||||
echo "✅ Pixelle-Video Web UI is launching on port 8501"
|
||||
echo "🌐 The URL will be available shortly (usually within 5-10 seconds)"
|
||||
echo ""
|
||||
else
|
||||
echo ""
|
||||
echo "⚠️ Warning: Process may have exited. Check logs with: tail -f /tmp/pixelle_streamlit.log"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo "Common commands:"
|
||||
echo "1. View logs:"
|
||||
echo " tail -f /tmp/pixelle_streamlit.log"
|
||||
echo "2. Stop service:"
|
||||
echo " pkill -f 'streamlit run web/app.py'"
|
||||
echo "3. Restart service:"
|
||||
echo " pkill -f 'streamlit run web/app.py' && nohup bash start_web.sh > /tmp/pixelle_streamlit.log 2>&1 &"
|
||||
echo "4. Check port usage:"
|
||||
echo " lsof -i:8501"
|
||||
echo "5. View processes:"
|
||||
echo " ps aux | grep streamlit"
|
||||
echo ""
|
||||
echo "For more help, see README or run 'ps aux | grep streamlit'."
|
||||
@@ -0,0 +1,71 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
*.egg-info/
|
||||
dist/
|
||||
build/
|
||||
*.egg
|
||||
|
||||
# Virtual environments
|
||||
.venv/
|
||||
venv/
|
||||
ENV/
|
||||
env/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Git
|
||||
.git/
|
||||
.gitignore
|
||||
.gitattributes
|
||||
|
||||
# Documentation
|
||||
docs/*
|
||||
!docs/images/
|
||||
!docs/FAQ*.md
|
||||
*.md
|
||||
!README.md
|
||||
|
||||
# Plans and development files
|
||||
plans/
|
||||
repositories/
|
||||
examples/
|
||||
|
||||
# Test files
|
||||
test_*.py
|
||||
tests/
|
||||
*.log
|
||||
|
||||
# Output and temporary files
|
||||
output/*
|
||||
!output/.gitkeep
|
||||
temp/
|
||||
*.tmp
|
||||
|
||||
# User data (will be mounted)
|
||||
data/users/*
|
||||
!data/.gitkeep
|
||||
|
||||
# Config (will be mounted)
|
||||
config.yaml
|
||||
config.yaml.bak
|
||||
config.example.yaml
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Misc
|
||||
*.bak
|
||||
restart_web.sh
|
||||
start_web.sh
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
name: Deploy Documentation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- 'mkdocs.yml'
|
||||
- '.github/workflows/docs.yml'
|
||||
workflow_dispatch: # Allow manual trigger
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for git-revision-date-localized plugin
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('requirements-docs.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install -r requirements-docs.txt
|
||||
|
||||
- name: Build and deploy documentation
|
||||
run: mkdocs gh-deploy --force
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# Virtual environments
|
||||
venv/
|
||||
ENV/
|
||||
env/
|
||||
.venv/
|
||||
|
||||
# IDEs
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.cursorrules
|
||||
.cursorignore
|
||||
.kiro/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Config files with sensitive data
|
||||
config.yaml
|
||||
config.yaml.bak
|
||||
*.yaml.bak
|
||||
.env
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# Test outputs
|
||||
test_outputs/
|
||||
*.mp3
|
||||
*.wav
|
||||
*.bak
|
||||
test_*.py
|
||||
|
||||
!bgm/default.mp3
|
||||
|
||||
# Temp files
|
||||
temp/
|
||||
tmp/
|
||||
.cache/
|
||||
|
||||
# MkDocs build output
|
||||
site/
|
||||
|
||||
data
|
||||
output
|
||||
|
||||
plans/
|
||||
examples/
|
||||
repositories/
|
||||
|
||||
*.out
|
||||
@@ -0,0 +1,77 @@
|
||||
# Pixelle-Video Docker Image
|
||||
# Based on Python 3.11 slim for smaller image size
|
||||
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Build arguments for mirror configuration
|
||||
# USE_CN_MIRROR: whether to use China mirrors (true/false)
|
||||
ARG USE_CN_MIRROR=false
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Replace apt sources with China mirrors if needed
|
||||
# Debian 12 uses DEB822 format in /etc/apt/sources.list.d/debian.sources
|
||||
RUN if [ "$USE_CN_MIRROR" = "true" ]; then \
|
||||
sed -i 's|deb.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list.d/debian.sources && \
|
||||
sed -i 's|security.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list.d/debian.sources; \
|
||||
fi
|
||||
|
||||
# Install system dependencies
|
||||
# - curl: for health checks and downloads
|
||||
# - ffmpeg: for video/audio processing
|
||||
# - fonts-noto-cjk: for CJK character support
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
ffmpeg \
|
||||
fonts-noto-cjk \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install uv package manager
|
||||
# For China: use pip to install uv from mirror (faster and more stable)
|
||||
# For International: use official installer script
|
||||
RUN if [ "$USE_CN_MIRROR" = "true" ]; then \
|
||||
pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple/ uv; \
|
||||
else \
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh; \
|
||||
fi
|
||||
ENV PATH="/root/.local/bin:$PATH"
|
||||
RUN uv --version
|
||||
|
||||
# Copy dependency files and source code for building
|
||||
# Note: pixelle_video is needed for hatchling to build the package
|
||||
COPY pyproject.toml uv.lock README.md ./
|
||||
COPY pixelle_video ./pixelle_video
|
||||
|
||||
# Create virtual environment and install dependencies
|
||||
# Use -i flag to specify mirror when USE_CN_MIRROR=true
|
||||
RUN export UV_HTTP_TIMEOUT=300 && \
|
||||
uv venv && \
|
||||
if [ "$USE_CN_MIRROR" = "true" ]; then \
|
||||
uv pip install -e . -i https://pypi.tuna.tsinghua.edu.cn/simple; \
|
||||
else \
|
||||
uv pip install -e .; \
|
||||
fi && \
|
||||
uv run playwright install --with-deps chromium
|
||||
|
||||
# Copy rest of application code
|
||||
COPY api ./api
|
||||
COPY web ./web
|
||||
COPY bgm ./bgm
|
||||
COPY templates ./templates
|
||||
COPY workflows ./workflows
|
||||
COPY resources ./resources
|
||||
COPY docs/images ./docs/images
|
||||
COPY docs/FAQ*.md ./docs/
|
||||
|
||||
# Create output, data and temp directories
|
||||
RUN mkdir -p /app/output /app/data /app/temp
|
||||
|
||||
# Expose ports
|
||||
# 8000: API service
|
||||
# 8501: Web UI service
|
||||
EXPOSE 8000 8501
|
||||
|
||||
# Default command (can be overridden in docker-compose)
|
||||
CMD ["uv", "run", "python", "api/app.py"]
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,150 @@
|
||||
Copyright (C) 2025 AIDC-AI
|
||||
This project incorporates components from the Open Source Software below.
|
||||
The original copyright notices and the licenses under which we received such components are set forth below for informational purposes.
|
||||
|
||||
Open Source Software Licensed under the MIT-CMU License:
|
||||
--------------------------------------------------------------------
|
||||
1. pillow 11.3.0
|
||||
Terms of the MIT-CMU:
|
||||
--------------------------------------------------------------------
|
||||
By obtaining, using, and/or copying this software and/or its associated documentation, you agree that you have read, understood, and will comply with the following terms and conditions:
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holder not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission.
|
||||
|
||||
THE COPYRIGHT HOLDER DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM THE LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
|
||||
Open Source Software Licensed under the BSD-3-Clause License:
|
||||
--------------------------------------------------------------------
|
||||
1. httpx 0.28.1 https://pypi.org/project/httpx
|
||||
copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
Terms of the BSD-3-Clause:
|
||||
--------------------------------------------------------------------
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
|
||||
Open Source Software Licensed under the Apache-2.0 License:
|
||||
--------------------------------------------------------------------
|
||||
1. streamlit 1.40.0 https://pypi.org/project/streamlit
|
||||
Copyright 2018 Adrien Treuille
|
||||
Copyright 2018 Streamlit Inc. All rights reserved.
|
||||
Copyright 2008 Google Inc. All rights reserved.
|
||||
2. openai 2.6.0
|
||||
3. python-multipart 0.0.20 https://pypi.org/project/python-multipart
|
||||
Copyright (c) 2010 by Armin Ronacher.
|
||||
Copyright 2012; Andrew Dunham
|
||||
4. ffmpeg-python 0.2.0 https://pypi.org/project/ffmpeg-python
|
||||
Copyright 2017 Karl Kroening
|
||||
Terms of the Apache-2.0:
|
||||
--------------------------------------------------------------------
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
|
||||
Open Source Software Licensed under the MIT License:
|
||||
--------------------------------------------------------------------
|
||||
1. fastmcp 2.0.0 https://pypi.org/project/fastmcp
|
||||
2. pyyaml 6.0.0
|
||||
3. comfykit 0.1.6
|
||||
4. certifi 2025.10.5
|
||||
5. playwright 1.58.0 https://pypi.org/project/playwright
|
||||
Copyright (c) Microsoft Corporation
|
||||
6. edge-tts 7.2.3
|
||||
7. fastapi 0.115.0 https://pypi.org/project/fastapi
|
||||
Copyright (c) 2018 Sebasti n Ram rez
|
||||
Copyright (c) 2018 Sebasti..n Ram..rez
|
||||
Copyright (c) 2018 Sebasti10n Ram.:rez
|
||||
8. pydantic 2.0.0
|
||||
9. loguru 0.7.0 https://pypi.org/project/loguru
|
||||
Copyright (c) 2017
|
||||
Terms of the MIT:
|
||||
--------------------------------------------------------------------
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,476 @@
|
||||
<h1 align="center">🎬 Pixelle-Video —— AI 全自动短视频引擎</h1>
|
||||
|
||||
<p align="center"><a href="README_EN.md">English</a> | <b>中文</b></p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.bilibili.com/video/BV1WzyGBnEVp/?vd_source=e7e7d4ca8db9a18c80f17a24a6582fca" target="_blank"><img src="https://img.shields.io/badge/🎥 视频教程-EA4C89" alt="视频教程"></a>
|
||||
<a href="https://github.com/AIDC-AI/Pixelle-Video/releases" target="_blank"><img src="https://img.shields.io/badge/📦 Windows包-50C878" alt="Windows整合包"></a>
|
||||
<a href="https://aidc-ai.github.io/Pixelle-Video/zh" target="_blank"><img src="https://img.shields.io/badge/📘 使用文档-4A90E2" alt="使用文档"></a>
|
||||
<a href="https://github.com/AIDC-AI/Pixelle-Video/stargazers"><img src="https://img.shields.io/github/stars/AIDC-AI/Pixelle-Video.svg" alt="Stargazers"></a>
|
||||
<a href="https://github.com/AIDC-AI/Pixelle-Video/issues"><img src="https://img.shields.io/github/issues/AIDC-AI/Pixelle-Video.svg" alt="Issues"></a>
|
||||
<a href="https://github.com/AIDC-AI/Pixelle-Video/network/members"><img src="https://img.shields.io/github/forks/AIDC-AI/Pixelle-Video.svg" alt="Forks"></a>
|
||||
<a href="https://github.com/AIDC-AI/Pixelle-Video/blob/main/LICENSE"><img src="https://img.shields.io/github/license/AIDC-AI/Pixelle-Video.svg" alt="License"></a>
|
||||
</p>
|
||||
|
||||
https://github.com/user-attachments/assets/a42e7457-fcc8-40da-83fc-784c45a8b95d
|
||||
|
||||
<br/>
|
||||
|
||||
只需输入一个 **主题**,Pixelle-Video 就能自动完成:
|
||||
- ✍️ 撰写视频文案
|
||||
- 🎨 生成 AI 配图/视频
|
||||
- 🗣️ 合成语音解说
|
||||
- 🎵 添加背景音乐
|
||||
- 🎬 一键合成视频
|
||||
|
||||
**零门槛,零剪辑经验**,让视频创作成为一句话的事!
|
||||
|
||||
|
||||
## 🖥️ Web 界面预览
|
||||
|
||||

|
||||
|
||||
|
||||
## 📋 最近更新
|
||||
|
||||
- ✅ **2026-06-01**: 新增直连 API 媒体模型配置,支持在 WebUI 中配置图像/视频模型供应商、Base URL 与代理开关
|
||||
- ✅ **2026-01-26**: 新增「动作迁移」模块,上传参考视频和图片进行动作迁移
|
||||
- ✅ **2026-01-14**: 新增「数字人口播」和「图生视频」流水线,新增多语言 TTS 音色支持
|
||||
- ✅ **2026-01-06**: 新增 RunningHub 48G 显存机器调用支持
|
||||
- ✅ **2025-12-28**: 支持 RunningHub 并发限制可配置,优化 LLM 返回结构化数据的逻辑
|
||||
- ✅ **2025-12-17**: 支持 ComfyUI API Key 配置,支持 Nano Banana 模型调用,API 接口支持模板自定义参数
|
||||
- ✅ **2025-12-10**: 侧边栏内置 FAQ,锁定 edge-tts 版本修复 TTS 服务不稳定问题
|
||||
- ✅ **2025-12-08**: 支持固定脚本多种分割方式(段落/行/句子),优化模板选择交互逻辑支持直接预览选择
|
||||
- ✅ **2025-12-06**: 修复视频生成 API 返回 URL 路径处理,支持跨平台兼容
|
||||
- ✅ **2025-12-05**: 新增 Windows 整合包下载,优化图片与视频反推工作流
|
||||
- ✅ **2025-12-04**: 新增「自定义素材」功能,支持用户上传自己的照片和视频,AI 智能分析生成脚本
|
||||
- ✅ **2025-11-18**: 优化 RunningHub 服务调用支持并行处理,新增历史记录页面,支持批量创建视频任务
|
||||
|
||||
|
||||
## ✨ 功能亮点
|
||||
|
||||
- ✅ **全自动生成** - 输入主题,自动生成完整视频
|
||||
- ✅ **AI 智能文案** - 根据主题智能创作解说词,无需自己写脚本
|
||||
- ✅ **AI 生成配图** - 每句话都配上精美的 AI 插图
|
||||
- ✅ **AI 生成视频** - 支持使用 AI 视频生成模型(如 WAN 2.1)创建动态视频内容
|
||||
- ✅ **直连模型 API** - 可直接调用 DashScope、OpenAI、Seedream、Seedance、Kling 等图像/视频生成服务
|
||||
- ✅ **AI 生成语音** - 支持 Edge-TTS、Index-TTS 等众多主流 TTS 方案
|
||||
- ✅ **背景音乐** - 支持添加 BGM,让视频更有氛围
|
||||
- ✅ **视觉风格** - 多种模板可选,打造独特视频风格
|
||||
- ✅ **灵活尺寸** - 支持竖屏、横屏等多种视频尺寸
|
||||
- ✅ **多种 AI 模型** - 支持 GPT、通义千问、DeepSeek、Ollama 等
|
||||
- ✅ **原子能力灵活组合** - 支持 ComfyUI / RunningHub 工作流,也支持直连 API 模型,可按需替换图像、视频、TTS、VLM 等能力
|
||||
|
||||
|
||||
## 📊 视频生成流程
|
||||
|
||||
Pixelle-Video 采用模块化设计,整个视频生成流程清晰简洁:
|
||||
|
||||

|
||||
|
||||
从输入文本到最终视频输出,整个流程简洁清晰:**文案生成 → 配图规划 → 逐帧处理 → 视频合成**
|
||||
|
||||
每个环节都支持灵活定制,可选择不同的 AI 模型、音频引擎、视觉风格等,满足个性化创作需求。
|
||||
|
||||
|
||||
## 🎬 视频示例
|
||||
|
||||
以下是使用 Pixelle-Video 生成的实际案例,展示了不同主题和风格的视频效果:
|
||||
|
||||
### 📱 扩展模块视频展示
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%">
|
||||
<h3>👤 数字人口播</h3>
|
||||
<video src="https://github.com/user-attachments/assets/7c122563-c2e0-4dcd-a73c-25ba1d4fa2dd" controls width="100%"></video>
|
||||
<p align="center"><b>韩语数字人口播</b></p>
|
||||
</td>
|
||||
<td width="33%">
|
||||
<h3>🖼️ 图生视频</h3>
|
||||
<video src="https://github.com/user-attachments/assets/5b4eef17-07d0-4bde-9748-2ed68cc9888e" controls width="100%"></video>
|
||||
<p align="center"><b>卡通视频</b></p>
|
||||
</td>
|
||||
<td width="33%">
|
||||
<h3>💃 动作迁移</h3>
|
||||
<video src="https://github.com/user-attachments/assets/7b1240bc-e965-434c-b343-118ec4793d4f" controls width="100%"></video>
|
||||
<p align="center"><b>跳舞小猫</b></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
### 📱 竖屏视频展示
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%">
|
||||
<h3>🌄 人文纪实类 - 视频默认模版</h3>
|
||||
<video src="https://github.com/user-attachments/assets/e6716c1d-78de-453d-84c2-10873c8c595f" controls width="100%"></video>
|
||||
<p align="center"><b>旅行路上的风景让人流连忘返</b></p>
|
||||
</td>
|
||||
<td width="33%">
|
||||
<h3>🔍 文化解构类 - 视频默认模版</h3>
|
||||
<video src="https://github.com/user-attachments/assets/f5de75f6-135a-4ab4-9f5f-079f649764d5" controls width="100%"></video>
|
||||
<p align="center"><b>Santa ID</b></p>
|
||||
</td>
|
||||
<td width="33%">
|
||||
<h3>🔭 科学思辨类 - 视频默认模版</h3>
|
||||
<video src="https://github.com/user-attachments/assets/ceb8b0df-8331-4e1f-88e7-db5b295a1c1d" controls width="100%"></video>
|
||||
<p align="center"><b>为什么我们还没有找到外星文明?</b></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33%">
|
||||
<h3>🌱 个人成长类 - 克隆音色</h3>
|
||||
<video src="https://github.com/user-attachments/assets/1bad9a49-df83-4905-9cc8-9a7640e9c7d8" controls width="100%"></video>
|
||||
<p align="center"><b>如何提升自己</b></p>
|
||||
</td>
|
||||
<td width="33%">
|
||||
<h3>🧠 深度思考类 - 默认模板</h3>
|
||||
<video src="https://github.com/user-attachments/assets/663b705a-2aea-44bc-b266-4bb27aa255a8" controls width="100%"></video>
|
||||
<p align="center"><b>如何理解反脆弱</b></p>
|
||||
</td>
|
||||
<td width="33%">
|
||||
<h3>🏯 历史文化类 - 固定画面</h3>
|
||||
<video src="https://github.com/user-attachments/assets/56e0a018-fa99-47eb-a97f-fc2fa8915724" controls width="100%"></video>
|
||||
<p align="center"><b>资治通鉴</b></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33%">
|
||||
<h3>☀️ 情感类 - 克隆音色</h3>
|
||||
<video src="https://github.com/user-attachments/assets/4687df95-dd21-4a7b-b01e-f33a7b646644" controls width="100%"></video>
|
||||
<p align="center"><b>冬日暖阳</b></p>
|
||||
</td>
|
||||
<td width="33%">
|
||||
<h3>📜 小说解说类 - 自创脚本</h3>
|
||||
<video src="https://github.com/user-attachments/assets/d354465e-3fa8-40b4-93e9-61ad75ef0697" controls width="100%"></video>
|
||||
<p align="center"><b>斗破苍穹</b></p>
|
||||
</td>
|
||||
<td width="33%">
|
||||
<h3>🧬 知识科普类 - Qwen生图</h3>
|
||||
<video src="https://github.com/user-attachments/assets/8ac21768-41ce-4d41-acdd-e3dd3eb9725a" controls width="100%"></video>
|
||||
<p align="center"><b>养生知识</b></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### 🖥️ 横屏视频展示
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%">
|
||||
<h3>💰 副业赚钱 - 电影模板</h3>
|
||||
<video src="https://github.com/user-attachments/assets/c9209d4e-73a6-4b82-aaad-cf102248c9e2" controls width="100%"></video>
|
||||
<p align="center"><b>副业赚钱</b></p>
|
||||
</td>
|
||||
<td width="50%">
|
||||
<h3>🏛️ 历史解说 - 自定义模板</h3>
|
||||
<video src="https://github.com/user-attachments/assets/a767c452-d5f1-4cff-bb34-b80fff0d4c3e" controls width="100%"></video>
|
||||
<p align="center"><b>资治通鉴启示录</b></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
> 💡 **提示**: 这些视频都是通过输入一个主题关键词,由 AI 全自动生成的,无需任何视频剪辑经验!
|
||||
|
||||
|
||||
<div id="tutorial-start" />
|
||||
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 🪟 Windows 一键整合包(推荐 Windows 用户使用)
|
||||
|
||||
**无需安装 Python、uv 或 ffmpeg,一键开箱即用!**
|
||||
|
||||
👉 **[下载 Windows 一键整合包](https://github.com/AIDC-AI/Pixelle-Video/releases/latest)**
|
||||
|
||||
1. 下载最新的 Windows 一键整合包并解压
|
||||
2. 双击运行 `start.bat` 启动 Web 界面
|
||||
3. 浏览器会自动打开 http://localhost:8501
|
||||
4. 在「⚙️ 系统配置」中配置 LLM API 和图像生成服务
|
||||
5. 开始生成视频!
|
||||
|
||||
> 💡 **提示**: 整合包已包含所有依赖,无需手动安装任何环境。首次使用只需配置 API 密钥即可。
|
||||
|
||||
|
||||
### 从源码安装(适合 macOS / Linux 用户或需要自定义的用户)
|
||||
|
||||
#### 前置环境依赖
|
||||
|
||||
在开始之前,需要先安装 Python 包管理器 `uv` 和视频处理工具 `ffmpeg`:
|
||||
|
||||
##### 安装 uv
|
||||
|
||||
请访问 uv 官方文档查看适合你系统的安装方法:
|
||||
👉 **[uv 安装指南](https://docs.astral.sh/uv/getting-started/installation/)**
|
||||
|
||||
安装完成后,在终端中运行 `uv --version` 验证安装成功。
|
||||
|
||||
##### 安装 ffmpeg
|
||||
|
||||
**macOS**
|
||||
```bash
|
||||
brew install ffmpeg
|
||||
```
|
||||
|
||||
**Ubuntu / Debian**
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install ffmpeg
|
||||
```
|
||||
|
||||
**Windows**
|
||||
- 下载地址:https://ffmpeg.org/download.html
|
||||
- 下载后解压,将 `bin` 目录添加到系统环境变量 PATH 中
|
||||
|
||||
安装完成后,在终端中运行 `ffmpeg -version` 验证安装成功。
|
||||
|
||||
|
||||
#### 第一步:下载项目
|
||||
|
||||
```bash
|
||||
git clone https://github.com/AIDC-AI/Pixelle-Video.git
|
||||
cd Pixelle-Video
|
||||
```
|
||||
|
||||
#### 第二步:启动 Web 界面
|
||||
|
||||
```bash
|
||||
# 使用 uv 运行(推荐,会自动安装依赖)
|
||||
uv run streamlit run web/app.py
|
||||
```
|
||||
|
||||
浏览器会自动打开 http://localhost:8501
|
||||
|
||||
#### 第三步:在 Web 界面配置
|
||||
|
||||
首次使用时,展开「⚙️ 系统配置」面板,填写:
|
||||
- **LLM 配置**: 选择 AI 模型(如通义千问、GPT 等)并填入 API Key
|
||||
- **ComfyUI / RunningHub 配置**: 如需使用工作流生成图片、视频或语音,配置本地 ComfyUI 地址或 RunningHub API Key
|
||||
- **API 媒体模型配置**: 如需直连图像/视频模型,配置 DashScope、OpenAI、ARK、Kling 等供应商的 API Key、Base URL 和代理选项
|
||||
|
||||
配置好后点击「保存配置」,就可以开始生成视频了!
|
||||
|
||||
<div id="tutorial-end" />
|
||||
|
||||
## 💻 使用方法
|
||||
|
||||
打开 Web 界面后,你会看到三栏布局,下面详细讲解每个部分:
|
||||
|
||||
|
||||
### ⚙️ 系统配置(首次必填)
|
||||
|
||||
首次使用时需要配置,点击展开「⚙️ 系统配置」面板:
|
||||
|
||||
#### 1. LLM 配置(大语言模型)
|
||||
用于生成视频文案的 AI。
|
||||
|
||||
**快速选择预设**
|
||||
- 通过下拉菜单选择预设模型(通义千问、GPT-4o、DeepSeek 等)
|
||||
- 选择后会自动填充 base_url 和 model
|
||||
- 点击「🔑 获取 API Key」链接去注册并获取密钥
|
||||
|
||||
**手动配置**
|
||||
- API Key: 填入你的密钥
|
||||
- Base URL: API 地址
|
||||
- Model: 模型名称
|
||||
|
||||
#### 2. ComfyUI / RunningHub 配置
|
||||
用于通过 ComfyUI 工作流生成视频配图、视频片段或语音。
|
||||
|
||||
**本地部署(推荐)**
|
||||
- ComfyUI URL: 本地 ComfyUI 服务地址(默认 http://127.0.0.1:8188)
|
||||
- 点击「测试连接」确认服务可用
|
||||
|
||||
**云端部署**
|
||||
- RunningHub API Key: 云端图像生成服务的密钥
|
||||
|
||||
#### 3. API 媒体模型配置
|
||||
用于不依赖 ComfyUI/RunningHub,直接调用模型供应商的图像、视频或素材分析能力。
|
||||
|
||||
**支持的供应商**
|
||||
- OpenAI / GPT Image:用于 GPT 图像生成模型
|
||||
- DashScope / Wan / HappyHorse:用于通义万象图像、视频生成
|
||||
- Volcengine ARK / Seedream / Seedance:用于字节 Seedream 图像和 Seedance 视频生成
|
||||
- Kling AI / 可灵:用于可灵视频生成
|
||||
|
||||
**可配置项**
|
||||
- API Key / Access Key / Secret Key:模型供应商鉴权信息
|
||||
- Base URL:模型服务地址,WebUI 会提供官方默认地址
|
||||
- 本地代理:如 `http://127.0.0.1:9090`
|
||||
- 启用代理:每个供应商可单独选择是否走本地代理
|
||||
- 打印模型请求参数:调试用,会在终端打印发送给模型的 prompt、模型名和输入文件路径
|
||||
|
||||
> 💡 如果你只使用 ComfyUI 或 RunningHub,可以不填写 API 媒体模型配置;如果你选择 `api/...` 工作流,则需要配置对应供应商的密钥。
|
||||
|
||||
配置完成后点击「保存配置」。
|
||||
|
||||
|
||||
### 📝 内容输入(左侧栏)
|
||||
|
||||
#### 生成模式
|
||||
- **AI 生成内容**: 输入主题,AI 自动创作文案
|
||||
- 适合:想快速生成视频,让 AI 写稿
|
||||
- 例如:「为什么要养成阅读习惯」
|
||||
- **固定文案内容**: 直接输入完整文案,跳过 AI 创作
|
||||
- 适合:已有现成文案,直接生成视频
|
||||
|
||||
#### 背景音乐(BGM)
|
||||
- **无 BGM**: 纯人声解说
|
||||
- **内置音乐**: 选择预置的背景音乐(如 default.mp3)
|
||||
- **自定义音乐**: 将你的音乐文件(MP3/WAV 等)放到 `bgm/` 文件夹
|
||||
- 点击「试听 BGM」可以预览音乐
|
||||
|
||||
|
||||
### 🎤 语音设置(中间栏)
|
||||
|
||||
#### TTS 工作流
|
||||
- 从下拉菜单选择 TTS 工作流(支持 Edge-TTS、Index-TTS 等)
|
||||
- 系统会自动扫描 `workflows/` 文件夹中的 TTS 工作流
|
||||
- 如果懂 ComfyUI,可以自定义 TTS 工作流
|
||||
|
||||
#### 参考音频(可选)
|
||||
- 上传参考音频文件用于声音克隆(支持 MP3/WAV/FLAC 等格式)
|
||||
- 适用于支持声音克隆的 TTS 工作流(如 Index-TTS)
|
||||
- 上传后可以直接试听
|
||||
|
||||
#### 预览功能
|
||||
- 输入测试文本,点击「预览语音」即可试听效果
|
||||
- 支持使用参考音频进行预览
|
||||
|
||||
|
||||
### 🎨 视觉设置(中间栏)
|
||||
|
||||
#### 图像生成
|
||||
决定 AI 生成什么风格的配图。
|
||||
|
||||
**ComfyUI 工作流**
|
||||
- 从下拉菜单选择图像生成工作流
|
||||
- 支持本地部署(selfhost)和云端(RunningHub)工作流
|
||||
- 也支持选择 `api/...` 直连图像模型工作流(需先在系统配置中填写对应供应商密钥)
|
||||
- 默认使用 `image_flux.json`
|
||||
- 如果懂 ComfyUI,可以放自己的工作流到 `workflows/` 文件夹
|
||||
|
||||
**图像尺寸**
|
||||
- 设置生成图像的宽度和高度(单位:像素)
|
||||
- 默认 1024x1024,可根据需要调整
|
||||
- 注意:不同的模型对尺寸有不同的限制
|
||||
|
||||
**提示词前缀(Prompt Prefix)**
|
||||
- 控制图像的整体风格(语言需要是英文的)
|
||||
- 例如:Minimalist black-and-white matchstick figure style illustration, clean lines, simple sketch style
|
||||
- 点击「预览风格」可以测试效果
|
||||
|
||||
#### 视频模板
|
||||
决定视频画面的布局和设计。
|
||||
|
||||
**模板命名规范**
|
||||
- `static_*.html`: 静态模板(无需AI生成媒体,纯文字样式)
|
||||
- `image_*.html`: 图片模板(使用AI生成的图片作为背景)
|
||||
- `video_*.html`: 视频模板(使用AI生成的视频作为背景)
|
||||
|
||||
**使用方法**
|
||||
- 从下拉菜单选择模板,按尺寸分组显示(竖屏/横屏/方形)
|
||||
- 点击「预览模板」可以自定义参数测试效果
|
||||
- 如果懂 HTML,可以在 `templates/` 文件夹创建自己的模板
|
||||
- 🔗 [查看所有模板效果图](https://aidc-ai.github.io/Pixelle-Video/zh/user-guide/templates/#_3)
|
||||
|
||||
#### API 视频生成
|
||||
当选择支持动态视频的模板或扩展工作流时,可以使用直连 API 视频模型生成片段。
|
||||
|
||||
- 支持 DashScope Wan / HappyHorse、Kling、Seedance 等视频模型
|
||||
- 支持按模型能力显示分辨率、画幅比例、时长、水印、原生音频等参数
|
||||
- 支持网络下载重试与内容审核失败后的提示词中性化重试
|
||||
- 在「自定义素材」工作流中,API 视频片段会尽量根据旁白音频时长生成,并使用相邻片段信息提升连贯性
|
||||
|
||||
|
||||
### 🎬 生成视频(右侧栏)
|
||||
|
||||
#### 生成按钮
|
||||
- 配置好所有参数后,点击「🎬 生成视频」
|
||||
- 会显示实时进度(生成文案 → 生成配图 → 合成语音 → 合成视频)
|
||||
- 生成完成后自动显示视频预览
|
||||
|
||||
#### 进度显示
|
||||
- 实时显示当前步骤
|
||||
- 例如:「分镜 3/5 - 生成插图」
|
||||
|
||||
#### 视频预览
|
||||
- 生成完成后自动播放
|
||||
- 显示视频时长、文件大小、分镜数等信息
|
||||
- 视频文件保存在 `output/` 文件夹
|
||||
|
||||
|
||||
### ❓ 常见问题
|
||||
|
||||
**Q: 第一次使用需要多久?**
|
||||
A: 生成时长取决于视频分镜数量、网络状况和 AI 推理速度,通常几分钟内即可完成。
|
||||
|
||||
**Q: 视频效果不满意怎么办?**
|
||||
A: 可以尝试:
|
||||
1. 更换 LLM 模型(不同模型文案风格不同)
|
||||
2. 调整图像尺寸和提示词前缀(改变配图风格)
|
||||
3. 更换 TTS 工作流或上传参考音频(改变语音效果)
|
||||
4. 尝试不同的视频模板和尺寸
|
||||
|
||||
**Q: 费用大概多少?**
|
||||
A: **本项目完全支持免费运行!**
|
||||
|
||||
- **完全免费方案**: LLM 使用 Ollama(本地运行)+ ComfyUI 本地部署 = 0 元
|
||||
- **推荐方案**: LLM 使用通义千问(成本极低,性价比高)+ ComfyUI 本地部署
|
||||
- **云端方案**: LLM 使用 OpenAI + 图像使用 RunningHub(费用较高但无需本地环境)
|
||||
|
||||
**选择建议**:本地有显卡建议完全免费方案,否则推荐使用通义千问(性价比高)
|
||||
|
||||
|
||||
## 🤝 参考项目
|
||||
|
||||
Pixelle-Video 的设计受到以下优秀开源项目的启发:
|
||||
|
||||
- [Pixelle-MCP](https://github.com/AIDC-AI/Pixelle-MCP) - ComfyUI MCP 服务器,让 AI 助手直接调用 ComfyUI
|
||||
- [MoneyPrinterTurbo](https://github.com/harry0703/MoneyPrinterTurbo) - 优秀的视频生成工具
|
||||
- [NarratoAI](https://github.com/linyqh/NarratoAI) - 影视解说自动化工具
|
||||
- [MoneyPrinterPlus](https://github.com/ddean2009/MoneyPrinterPlus) - 视频创作平台
|
||||
- [ComfyKit](https://github.com/puke3615/ComfyKit) - ComfyUI 工作流封装库
|
||||
|
||||
感谢这些项目的开源精神!🙏
|
||||
|
||||
|
||||
## 💬 社区交流
|
||||
|
||||
扫描下方二维码加入我们的社区,获取最新动态和技术支持:
|
||||
|
||||
| 微信群 | Discord 社区 |
|
||||
| ---- | ---- |
|
||||
| <img src="resources/wechat.png" alt="微信交流群" width="250" /> | <img src="resources/discord.png" alt="Discord 社区" width="250" /> |
|
||||
|
||||
|
||||
## 📢 反馈与支持
|
||||
|
||||
- 🐛 **遇到问题**: 提交 [Issue](https://github.com/AIDC-AI/Pixelle-Video/issues)
|
||||
- 💡 **功能建议**: 提交 [Feature Request](https://github.com/AIDC-AI/Pixelle-Video/issues)
|
||||
- ⭐ **给个 Star**: 如果这个项目对你有帮助,欢迎给个 Star 支持一下!
|
||||
|
||||
|
||||
## 📝 许可证
|
||||
|
||||
本项目采用 Apache 2.0 许可证,详情请查看 [LICENSE](LICENSE) 文件。
|
||||
|
||||
|
||||
## 📚 系列工作
|
||||
|
||||
| 框架图 | 论文信息 |
|
||||
|:---:|---|
|
||||
| <img src="https://github.com/HITsz-TMG/VideoClaw/blob/main/FilmAgent-pics/framework.png" width="420" alt="FilmAgent framework"/> | **[SIGGRAPH Asia 2024] FilmAgent: Automating Virtual Film Production Through a Multi-Agent Collaborative Framework**<br>*Zhenran Xu, Longyue Wang, Jifang Wang, Zhouyi Li, Senbao Shi, Xue Yang, Yiyu Wang, Baotian Hu, Jun Yu, Min Zhang*<br>[[Paper](https://arxiv.org/pdf/2501.12909)] [[GitHub](https://github.com/HITsz-TMG/VideoClaw/blob/main/FilmAgent)] |
|
||||
| <img src="https://github.com/HITsz-TMG/Anim-Director/blob/main/Anim-Director/assets/visualeg.png" width="420" alt="Anim-Director result"/> | **[SIGGRAPH Asia 2024] Anim-Director: A Large Multimodal Model Powered Agent for Controllable Animation Video Generation**<br>*Yunxin Li, Haoyuan Shi, Baotian Hu, Longyue Wang, Jiashun Zhu, Jinyi Xu, Zhen Zhao, Min Zhang*<br>[[Paper](https://doi.org/10.1145/3680528.3687688)] [[GitHub](https://github.com/HITsz-TMG/Anim-Director/tree/main/Anim-Director)] |
|
||||
| <img src="https://github.com/AIDC-AI/ComfyUI-Copilot/blob/main/assets/Framework-v3.png" width="420" alt="Anim-Director result"/> | **[ACL 2025] ComfyUI-Copilot: An Intelligent Assistant for Automated Workflow Development**<br>*Zhenran Xu, Xue Yang, Yiyu Wang, Qingli Hu, Zijiao Wu, Longyue Wang, Weihua Luo, Kaifu Zhang, Baotian Hu, Min Zhang*<br>[[Paper](https://aclanthology.org/2025.acl-demo.61/)] [[GitHub](https://github.com/AIDC-AI/ComfyUI-Copilot)] |
|
||||
| <img src="https://raw.githubusercontent.com/HITsz-TMG/Anim-Director/main/AniMaker/assets/pipeline.png" width="420" alt="AniMaker pipeline"/> | **[SIGGRAPH Asia 2025] AniMaker: Multi-Agent Animated Storytelling with MCTS-Driven Clip Generation**<br>*Haoyuan Shi, Yunxin Li, Xinyu Chen, Longyue Wang, Baotian Hu, Min Zhang*<br>[[Paper](https://doi.org/10.1145/3757377.3764009)] [[GitHub](https://github.com/HITsz-TMG/Anim-Director/tree/main/AniMaker)] |
|
||||
|
||||
|
||||
|
||||
## ⭐ Star History
|
||||
|
||||
[](https://star-history.com/#AIDC-AI/Pixelle-Video&Date)
|
||||
@@ -0,0 +1,7 @@
|
||||
# WeHub 来源说明
|
||||
|
||||
- 原始项目:`ATH-MaaS/Pixelle-Video`
|
||||
- 原始仓库:https://github.com/ATH-MaaS/Pixelle-Video
|
||||
- 导入方式:上游默认分支的最新快照
|
||||
- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准
|
||||
- 本文件仅用于记录来源,不代表 WeHub 是原项目作者
|
||||
@@ -0,0 +1,469 @@
|
||||
<h1 align="center">🎬 Pixelle-Video —— AI Fully Automated Short Video Engine</h1>
|
||||
|
||||
<p align="center"><b>English</b> | <a href="README.md">中文</a></p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.youtube.com/watch?v=uUkx-lRxLjc" target="_blank"><img src="https://img.shields.io/badge/🎥 Video%20Tutorial-EA4C89" alt="Video Tutorial"></a>
|
||||
<a href="https://github.com/AIDC-AI/Pixelle-Video/releases" target="_blank"><img src="https://img.shields.io/badge/📦 Windows-50C878" alt="Windows Package"></a>
|
||||
<a href="https://aidc-ai.github.io/Pixelle-Video" target="_blank"><img src="https://img.shields.io/badge/📘 Documentation-4A90E2" alt="Documentation"></a>
|
||||
<a href="https://github.com/AIDC-AI/Pixelle-Video/stargazers"><img src="https://img.shields.io/github/stars/AIDC-AI/Pixelle-Video.svg" alt="Stargazers"></a>
|
||||
<a href="https://github.com/AIDC-AI/Pixelle-Video/issues"><img src="https://img.shields.io/github/issues/AIDC-AI/Pixelle-Video.svg" alt="Issues"></a>
|
||||
<a href="https://github.com/AIDC-AI/Pixelle-Video/network/members"><img src="https://img.shields.io/github/forks/AIDC-AI/Pixelle-Video.svg" alt="Forks"></a>
|
||||
<a href="https://github.com/AIDC-AI/Pixelle-Video/blob/main/LICENSE"><img src="https://img.shields.io/github/license/AIDC-AI/Pixelle-Video.svg" alt="License"></a>
|
||||
</p>
|
||||
|
||||
https://github.com/user-attachments/assets/a42e7457-fcc8-40da-83fc-784c45a8b95d
|
||||
|
||||
Just input a **topic**, and Pixelle-Video will automatically:
|
||||
- ✍️ Write video script
|
||||
- 🎨 Generate AI images/videos
|
||||
- 🗣️ Synthesize voice narration
|
||||
- 🎵 Add background music
|
||||
- 🎬 Create video with one click
|
||||
|
||||
|
||||
**Zero threshold, zero editing experience** - Make video creation as simple as typing a sentence!
|
||||
|
||||
|
||||
## 🖥️ Web Interface Preview
|
||||
|
||||

|
||||
|
||||
|
||||
## 📋 Recent Updates
|
||||
|
||||
- ✅ **2026-06-01**: Added direct API media model configuration in WebUI, including image/video provider credentials, Base URLs, and per-provider proxy toggles
|
||||
- ✅ **2026-01-26**: Added the Motion Transfer pipeline — upload a reference video and an image to transfer motion.
|
||||
- ✅ **2026-01-14**: Added "Digital Human" and "Image-to-Video" pipelines, multi-language TTS voices support
|
||||
- ✅ **2026-01-06**: Added RunningHub 48G VRAM machine support
|
||||
- ✅ **2025-12-28**: Configurable RunningHub concurrency limit, improved LLM structured data response handling
|
||||
- ✅ **2025-12-17**: Added ComfyUI API Key configuration, Nano Banana model support, API template custom parameters
|
||||
- ✅ **2025-12-10**: Built-in FAQ in sidebar, fixed edge-tts version to resolve TTS service instability
|
||||
- ✅ **2025-12-08**: Support multiple script split modes (paragraph/line/sentence), improved template selection with direct preview
|
||||
- ✅ **2025-12-06**: Fixed video generation API URL path handling with cross-platform compatibility
|
||||
- ✅ **2025-12-05**: Added Windows all-in-one package download, optimized image and video analysis workflows
|
||||
- ✅ **2025-12-04**: New "Custom Media" feature - upload your photos/videos with AI-powered analysis and script generation
|
||||
- ✅ **2025-11-18**: Parallel processing for RunningHub, added history page, batch video task creation support
|
||||
|
||||
|
||||
## ✨ Key Features
|
||||
|
||||
- ✅ **Fully Automatic Generation** - Input a topic, automatically generate complete video
|
||||
- ✅ **AI Smart Copywriting** - Intelligently create narration based on topic, no need to write scripts yourself
|
||||
- ✅ **AI Generated Images** - Each sentence comes with beautiful AI illustrations
|
||||
- ✅ **AI Generated Videos** - Support AI video generation models (like WAN 2.1) to create dynamic video content
|
||||
- ✅ **Direct Model APIs** - Directly call image/video generation services from DashScope, OpenAI, Seedream, Seedance, Kling, and more
|
||||
- ✅ **AI Generated Voice** - Support Edge-TTS, Index-TTS and many other mainstream TTS solutions
|
||||
- ✅ **Background Music** - Support adding BGM to make videos more atmospheric
|
||||
- ✅ **Visual Styles** - Multiple templates to choose from, create unique video styles
|
||||
- ✅ **Flexible Dimensions** - Support portrait, landscape and other video dimensions
|
||||
- ✅ **Multiple AI Models** - Support GPT, Qwen, DeepSeek, Ollama and more
|
||||
- ✅ **Flexible Atomic Capability Combination** - Supports ComfyUI / RunningHub workflows and direct API models, allowing image, video, TTS, VLM and other capabilities to be swapped as needed
|
||||
|
||||
|
||||
## 📊 Video Generation Pipeline
|
||||
|
||||
Pixelle-Video adopts a modular design, the entire video generation process is clear and concise:
|
||||
|
||||

|
||||
|
||||
From input text to final video output, the entire process is clear and simple: **Script Generation → Image Planning → Frame-by-Frame Processing → Video Composition**
|
||||
|
||||
Each step supports flexible customization, allowing you to choose different AI models, audio engines, visual styles, etc., to meet personalized creation needs.
|
||||
|
||||
|
||||
## 🎬 Video Examples
|
||||
|
||||
Here are actual cases generated using Pixelle-Video, showcasing video effects with different themes and styles:
|
||||
|
||||
### 📱 Extension Module Video Showcase
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%">
|
||||
<h3>👤 AI Digital Avatar</h3>
|
||||
<video src="https://github.com/user-attachments/assets/7c122563-c2e0-4dcd-a73c-25ba1d4fa2dd" controls width="100%"></video>
|
||||
<p align="center"><b>Korean-speaking AI Avatar</b></p>
|
||||
</td>
|
||||
<td width="33%">
|
||||
<h3>🖼️ Image-to-Video</h3>
|
||||
<video src="https://github.com/user-attachments/assets/5b4eef17-07d0-4bde-9748-2ed68cc9888e" controls width="100%"></video>
|
||||
<p align="center"><b>Animated Cartoon Video</b></p>
|
||||
</td>
|
||||
<td width="33%">
|
||||
<h3>💃 Motion Transfer</h3>
|
||||
<video src="https://github.com/user-attachments/assets/7b1240bc-e965-434c-b343-118ec4793d4f" controls width="100%"></video>
|
||||
<p align="center"><b>Dancing Kitten</b></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### 📱 Portrait Video Showcase
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="33%">
|
||||
<h3>🌄 Documentary & Lifestyle – Default Template</h3>
|
||||
<video src="https://github.com/user-attachments/assets/e6716c1d-78de-453d-84c2-10873c8c595f" controls width="100%"></video>
|
||||
<p align="center"><b>The Scenery Along the Journey</b></p>
|
||||
</td>
|
||||
<td width="33%">
|
||||
<h3>🔍 Cultural Deconstruction – Default Template</h3>
|
||||
<video src="https://github.com/user-attachments/assets/f5de75f6-135a-4ab4-9f5f-079f649764d5" controls width="100%"></video>
|
||||
<p align="center"><b>Santa ID</b></p>
|
||||
</td>
|
||||
<td width="33%">
|
||||
<h3>🔭 Scientific Inquiry – Default Template</h3>
|
||||
<video src="https://github.com/user-attachments/assets/ceb8b0df-8331-4e1f-88e7-db5b295a1c1d" controls width="100%"></video>
|
||||
<p align="center"><b>Why Haven’t We Found Alien Civilizations Yet?</b></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33%">
|
||||
<h3>🌱 Personal Growth – Cloned Voice</h3>
|
||||
<video src="https://github.com/user-attachments/assets/1bad9a49-df83-4905-9cc8-9a7640e9c7d8" controls width="100%"></video>
|
||||
<p align="center"><b>How to Level Up Yourself</b></p>
|
||||
</td>
|
||||
<td width="33%">
|
||||
<h3>🧠 Deep Thinking – Default Template</h3>
|
||||
<video src="https://github.com/user-attachments/assets/663b705a-2aea-44bc-b266-4bb27aa255a8" controls width="100%"></video>
|
||||
<p align="center"><b>Understanding Antifragility</b></p>
|
||||
</td>
|
||||
<td width="33%">
|
||||
<h3>🏯 History & Culture – Static Frame</h3>
|
||||
<video src="https://github.com/user-attachments/assets/56e0a018-fa99-47eb-a97f-fc2fa8915724" controls width="100%"></video>
|
||||
<p align="center"><b>Zizhi Tongjian (Comprehensive Mirror for Aid in Governance)</b></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33%">
|
||||
<h3>☀️ Emotional Storytelling – Cloned Voice</h3>
|
||||
<video src="https://github.com/user-attachments/assets/4687df95-dd21-4a7b-b01e-f33a7b646644" controls width="100%"></video>
|
||||
<p align="center"><b>Winter Sunlight</b></p>
|
||||
</td>
|
||||
<td width="33%">
|
||||
<h3>📜 Novel Adaptation – Custom Script</h3>
|
||||
<video src="https://github.com/user-attachments/assets/d354465e-3fa8-40b4-93e9-61ad75ef0697" controls width="100%"></video>
|
||||
<p align="center"><b>Doupo Cangqiong (Battle Through the Heavens)</b></p>
|
||||
</td>
|
||||
<td width="33%">
|
||||
<h3>🧬 Knowledge Explainer – Qwen Image Generation</h3>
|
||||
<video src="https://github.com/user-attachments/assets/8ac21768-41ce-4d41-acdd-e3dd3eb9725a" controls width="100%"></video>
|
||||
<p align="center"><b>Essential Wellness Tips</b></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### 🖥️ Landscape Video Showcase
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%">
|
||||
<h3>💰 Side Hustle Money Making - Movie Template</h3>
|
||||
<video src="https://github.com/user-attachments/assets/c9209d4e-73a6-4b82-aaad-cf102248c9e2" controls width="100%"></video>
|
||||
<p align="center"><b>Side Hustle Money Making</b></p>
|
||||
</td>
|
||||
<td width="50%">
|
||||
<h3>🏛️ Historical Commentary - Custom Template</h3>
|
||||
<video src="https://github.com/user-attachments/assets/a767c452-d5f1-4cff-bb34-b80fff0d4c3e" controls width="100%"></video>
|
||||
<p align="center"><b>Insights from Zizhi Tongjian</b></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
> 💡 **Tip**: All these videos are fully automatically generated by AI just by inputting a topic keyword, without any video editing experience required!
|
||||
|
||||
<div id="tutorial-start" />
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### 🪟 Windows All-in-One Package (Recommended for Windows Users)
|
||||
|
||||
**No need to install Python, uv, or ffmpeg - ready to use out of the box!**
|
||||
|
||||
👉 **[Download Windows All-in-One Package](https://github.com/AIDC-AI/Pixelle-Video/releases/latest)**
|
||||
|
||||
1. Download the latest Windows All-in-One Package and extract it
|
||||
2. Double-click `start.bat` to launch the Web interface
|
||||
3. Browser will automatically open http://localhost:8501
|
||||
4. Configure LLM API and image generation service in "⚙️ System Configuration"
|
||||
5. Start generating videos!
|
||||
|
||||
> 💡 **Tip**: The package includes all dependencies, no need to manually install any environment. On first use, you only need to configure API keys.
|
||||
|
||||
|
||||
### Install from Source (For macOS / Linux Users or Users Who Need Customization)
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
Before starting, you need to install Python package manager `uv` and video processing tool `ffmpeg`:
|
||||
|
||||
##### Install uv
|
||||
|
||||
Please visit the uv official documentation to see the installation method for your system:
|
||||
👉 **[uv Installation Guide](https://docs.astral.sh/uv/getting-started/installation/)**
|
||||
|
||||
After installation, run `uv --version` in the terminal to verify successful installation.
|
||||
|
||||
##### Install ffmpeg
|
||||
|
||||
**macOS**
|
||||
```bash
|
||||
brew install ffmpeg
|
||||
```
|
||||
|
||||
**Ubuntu / Debian**
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install ffmpeg
|
||||
```
|
||||
|
||||
**Windows**
|
||||
- Download URL: https://ffmpeg.org/download.html
|
||||
- After downloading, extract and add the `bin` directory to the system environment variable PATH
|
||||
|
||||
After installation, run `ffmpeg -version` in the terminal to verify successful installation.
|
||||
|
||||
|
||||
#### Step 1: Clone Project
|
||||
|
||||
```bash
|
||||
git clone https://github.com/AIDC-AI/Pixelle-Video.git
|
||||
cd Pixelle-Video
|
||||
```
|
||||
|
||||
#### Step 2: Launch Web Interface
|
||||
|
||||
```bash
|
||||
# Run with uv (recommended, will automatically install dependencies)
|
||||
uv run streamlit run web/app.py
|
||||
```
|
||||
|
||||
Browser will automatically open http://localhost:8501
|
||||
|
||||
#### Step 3: Configure in Web Interface
|
||||
|
||||
On first use, expand the "⚙️ System Configuration" panel and fill in:
|
||||
- **LLM Configuration**: Select AI model (such as Qwen, GPT, etc.) and enter API Key
|
||||
- **ComfyUI / RunningHub Configuration**: Configure local ComfyUI or RunningHub API Key if you want to use workflow-based image, video, or voice generation
|
||||
- **API Media Model Configuration**: Configure API Key, Base URL, and proxy options for direct image/video model providers such as DashScope, OpenAI, ARK, and Kling
|
||||
|
||||
After configuration, click "Save Configuration", and you can start generating videos!
|
||||
|
||||
<div id="tutorial-end" />
|
||||
|
||||
## 💻 Usage
|
||||
|
||||
After opening the Web interface, you will see a three-column layout. Here's a detailed explanation of each part:
|
||||
|
||||
|
||||
### ⚙️ System Configuration (Required on First Use)
|
||||
|
||||
Configuration is required on first use. Click to expand the "⚙️ System Configuration" panel:
|
||||
|
||||
#### 1. LLM Configuration (Large Language Model)
|
||||
Used for generating video scripts.
|
||||
|
||||
**Quick Select Preset**
|
||||
- Select preset model from dropdown menu (Qwen, GPT-4o, DeepSeek, etc.)
|
||||
- After selection, base_url and model will be automatically filled
|
||||
- Click "🔑 Get API Key" link to register and obtain key
|
||||
|
||||
**Manual Configuration**
|
||||
- API Key: Enter your key
|
||||
- Base URL: API address
|
||||
- Model: Model name
|
||||
|
||||
#### 2. ComfyUI / RunningHub Configuration
|
||||
Used for generating video images, video clips, or voices through ComfyUI workflows.
|
||||
|
||||
**Local Deployment (Recommended)**
|
||||
- ComfyUI URL: Local ComfyUI service address (default http://127.0.0.1:8188)
|
||||
- Click "Test Connection" to confirm service is available
|
||||
|
||||
**Cloud Deployment**
|
||||
- RunningHub API Key: Cloud image generation service key
|
||||
|
||||
#### 3. API Media Model Configuration
|
||||
Used to directly call image, video, or asset-analysis model providers without relying on ComfyUI/RunningHub.
|
||||
|
||||
**Supported Providers**
|
||||
- OpenAI / GPT Image: for GPT image generation models
|
||||
- DashScope / Wan / HappyHorse: for Alibaba Tongyi Wan image and video generation
|
||||
- Volcengine ARK / Seedream / Seedance: for Seedream image generation and Seedance video generation
|
||||
- Kling AI: for Kling video generation
|
||||
|
||||
**Configurable Items**
|
||||
- API Key / Access Key / Secret Key: provider credentials
|
||||
- Base URL: model service endpoint, with official defaults prefilled in WebUI
|
||||
- Local proxy: for example `http://127.0.0.1:9090`
|
||||
- Use proxy: each provider can independently choose whether to route requests through the local proxy
|
||||
- Print model request parameters: debug option that prints prompts, model names, and input file paths to the terminal
|
||||
|
||||
> 💡 If you only use ComfyUI or RunningHub, you can leave API Media Model Configuration empty. If you choose an `api/...` workflow, configure the corresponding provider credentials first.
|
||||
|
||||
After configuration, click "Save Configuration".
|
||||
|
||||
|
||||
### 📝 Content Input (Left Column)
|
||||
|
||||
#### Generation Mode
|
||||
- **AI Generated Content**: Input topic, AI automatically creates script
|
||||
- Suitable for: Want to quickly generate video, let AI write script
|
||||
- Example: "Why develop a reading habit"
|
||||
- **Fixed Script Content**: Directly input complete script, skip AI creation
|
||||
- Suitable for: Already have ready-made script, directly generate video
|
||||
|
||||
#### Background Music (BGM)
|
||||
- **No BGM**: Pure voice narration
|
||||
- **Built-in Music**: Select preset background music (such as default.mp3)
|
||||
- **Custom Music**: Put your music files (MP3/WAV, etc.) in the `bgm/` folder
|
||||
- Click "Preview BGM" to preview music
|
||||
|
||||
|
||||
### 🎤 Voice Settings (Middle Column)
|
||||
|
||||
#### TTS Workflow
|
||||
- Select TTS workflow from dropdown menu (supports Edge-TTS, Index-TTS, etc.)
|
||||
- System will automatically scan TTS workflows in the `workflows/` folder
|
||||
- If you know ComfyUI, you can customize TTS workflows
|
||||
|
||||
#### Reference Audio (Optional)
|
||||
- Upload reference audio file for voice cloning (supports MP3/WAV/FLAC and other formats)
|
||||
- Suitable for TTS workflows that support voice cloning (such as Index-TTS)
|
||||
- Can listen directly after upload
|
||||
|
||||
#### Preview Function
|
||||
- Enter test text, click "Preview Voice" to listen to the effect
|
||||
- Supports using reference audio for preview
|
||||
|
||||
|
||||
### 🎨 Visual Settings (Middle Column)
|
||||
|
||||
#### Image Generation
|
||||
Determine what style of images AI generates.
|
||||
|
||||
**ComfyUI Workflow**
|
||||
- Select image generation workflow from dropdown menu
|
||||
- Supports local deployment (selfhost) and cloud (RunningHub) workflows
|
||||
- Also supports `api/...` direct image model workflows after configuring the corresponding provider credentials
|
||||
- Default uses `image_flux.json`
|
||||
- If you know ComfyUI, you can put your own workflows in the `workflows/` folder
|
||||
|
||||
**Image Dimensions**
|
||||
- Set width and height of generated images (unit: pixels)
|
||||
- Default 1024x1024, can be adjusted as needed
|
||||
- Note: Different models have different dimension limitations
|
||||
|
||||
**Prompt Prefix**
|
||||
- Controls overall image style (language needs to be English)
|
||||
- Example: Minimalist black-and-white matchstick figure style illustration, clean lines, simple sketch style
|
||||
- Click "Preview Style" to test effect
|
||||
|
||||
#### Video Template
|
||||
Determines video layout and design.
|
||||
|
||||
**Template Naming Convention**
|
||||
- `static_*.html`: Static templates (no AI-generated media, text-only styles)
|
||||
- `image_*.html`: Image templates (uses AI-generated images as background)
|
||||
- `video_*.html`: Video templates (uses AI-generated videos as background)
|
||||
|
||||
**Usage**
|
||||
- Select template from dropdown menu, displayed grouped by dimension (portrait/landscape/square)
|
||||
- Click "Preview Template" to test effect with custom parameters
|
||||
- If you know HTML, you can create your own templates in the `templates/` folder
|
||||
- 🔗 [View All Template Previews](https://aidc-ai.github.io/Pixelle-Video/user-guide/templates/#built-in-template-preview)
|
||||
|
||||
#### API Video Generation
|
||||
When using dynamic video templates or extension workflows, you can generate clips through direct API video models.
|
||||
|
||||
- Supports DashScope Wan / HappyHorse, Kling, Seedance and other video models
|
||||
- Displays model-aware options such as resolution, aspect ratio, duration, watermark, and native audio
|
||||
- Supports network/download retries and LLM-based prompt neutralization retry for content-inspection failures
|
||||
- In the Custom Media workflow, API video segments try to follow narration audio duration and use neighboring segment information to improve continuity
|
||||
|
||||
|
||||
### 🎬 Generate Video (Right Column)
|
||||
|
||||
#### Generate Button
|
||||
- After configuring all parameters, click "🎬 Generate Video"
|
||||
- Shows real-time progress (generating script → generating images → synthesizing voice → composing video)
|
||||
- Automatically shows video preview after completion
|
||||
|
||||
#### Progress Display
|
||||
- Shows current step in real-time
|
||||
- Example: "Frame 3/5 - Generating Image"
|
||||
|
||||
#### Video Preview
|
||||
- Automatically plays after generation
|
||||
- Shows video duration, file size, number of frames, etc.
|
||||
- Video files are saved in the `output/` folder
|
||||
|
||||
|
||||
### ❓ FAQ
|
||||
|
||||
**Q: How long does it take to use for the first time?**
|
||||
A: Generation time depends on the number of video frames, network conditions, and AI inference speed, typically completed within a few minutes.
|
||||
|
||||
**Q: What if I'm not satisfied with the video?**
|
||||
A: You can try:
|
||||
1. Change LLM model (different models have different script styles)
|
||||
2. Adjust image dimensions and prompt prefix (change image style)
|
||||
3. Change TTS workflow or upload reference audio (change voice effect)
|
||||
4. Try different video templates and dimensions
|
||||
|
||||
**Q: What about the cost?**
|
||||
A: **This project fully supports free operation!**
|
||||
|
||||
- **Completely Free Solution**: LLM using Ollama (local) + ComfyUI local deployment = 0 cost
|
||||
- **Recommended Solution**: LLM using Qwen (extremely low cost, highly cost-effective) + ComfyUI local deployment
|
||||
- **Cloud Solution**: LLM using OpenAI + Image using RunningHub (higher cost but no need for local environment)
|
||||
|
||||
**Selection Suggestion**: If you have a local GPU, recommend completely free solution, otherwise recommend using Qwen (cost-effective)
|
||||
|
||||
|
||||
## 🤝 Referenced Projects
|
||||
|
||||
Pixelle-Video design is inspired by the following excellent open-source projects:
|
||||
|
||||
- [Pixelle-MCP](https://github.com/AIDC-AI/Pixelle-MCP) - ComfyUI MCP server, allows AI assistants to directly call ComfyUI
|
||||
- [MoneyPrinterTurbo](https://github.com/harry0703/MoneyPrinterTurbo) - Excellent video generation tool
|
||||
- [NarratoAI](https://github.com/linyqh/NarratoAI) - Film commentary automation tool
|
||||
- [MoneyPrinterPlus](https://github.com/ddean2009/MoneyPrinterPlus) - Video creation platform
|
||||
- [ComfyKit](https://github.com/puke3615/ComfyKit) - ComfyUI workflow wrapper library
|
||||
|
||||
Thanks for the open-source spirit of these projects! 🙏
|
||||
|
||||
|
||||
## 💬 Community
|
||||
|
||||
Scan the QR codes below to join our communities for latest updates and technical support:
|
||||
|
||||
| Discord Community | WeChat Group |
|
||||
| ---- | ---- |
|
||||
| <img src="resources/discord.png" alt="Discord Community" width="250" /> | <img src="resources/wechat.png" alt="WeChat Group" width="250" /> |
|
||||
|
||||
|
||||
## 📢 Feedback and Support
|
||||
|
||||
- 🐛 **Encountered Issues**: Submit [Issue](https://github.com/AIDC-AI/Pixelle-Video/issues)
|
||||
- 💡 **Feature Suggestions**: Submit [Feature Request](https://github.com/AIDC-AI/Pixelle-Video/issues)
|
||||
- ⭐ **Give a Star**: If this project helps you, feel free to give a Star for support!
|
||||
|
||||
|
||||
## 📝 License
|
||||
|
||||
This project is released under the Apache License 2.0. For details, please see the [LICENSE](LICENSE) file.
|
||||
|
||||
## 📚 Research Series
|
||||
|
||||
| Framework | Paper |
|
||||
|:---:|---|
|
||||
| <img src="https://github.com/HITsz-TMG/VideoClaw/blob/main/FilmAgent-pics/framework.png" width="420" alt="FilmAgent framework"/> | **[SIGGRAPH Asia 2024] FilmAgent: Automating Virtual Film Production Through a Multi-Agent Collaborative Framework**<br>*Zhenran Xu, Longyue Wang, Jifang Wang, Zhouyi Li, Senbao Shi, Xue Yang, Yiyu Wang, Baotian Hu, Jun Yu, Min Zhang*<br>[[Paper](https://arxiv.org/pdf/2501.12909)] [[GitHub](https://github.com/HITsz-TMG/VideoClaw/blob/main/FilmAgent)] |
|
||||
| <img src="https://github.com/AIDC-AI/ComfyUI-Copilot/blob/main/assets/Framework-v3.png" width="420" alt="Anim-Director result"/> | **[ACL 2025] ComfyUI-Copilot: An Intelligent Assistant for Automated Workflow Development**<br>*Zhenran Xu, Xue Yang, Yiyu Wang, Qingli Hu, Zijiao Wu, Longyue Wang, Weihua Luo, Kaifu Zhang, Baotian Hu, Min Zhang*<br>[[Paper](https://aclanthology.org/2025.acl-demo.61/)] [[GitHub](https://github.com/AIDC-AI/ComfyUI-Copilot)] |
|
||||
| <img src="https://raw.githubusercontent.com/HITsz-TMG/Anim-Director/main/AniMaker/assets/pipeline.png" width="420" alt="AniMaker pipeline"/> | **[SIGGRAPH Asia 2025] AniMaker: Multi-Agent Animated Storytelling with MCTS-Driven Clip Generation**<br>*Haoyuan Shi, Yunxin Li, Xinyu Chen, Longyue Wang, Baotian Hu, Min Zhang*<br>[[Paper](https://doi.org/10.1145/3757377.3764009)] [[GitHub](https://github.com/HITsz-TMG/Anim-Director/tree/main/AniMaker)] |
|
||||
|
||||
|
||||
## ⭐ Star History
|
||||
|
||||
[](https://star-history.com/#AIDC-AI/Pixelle-Video&Date)
|
||||
@@ -0,0 +1,17 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Pixelle-Video API Layer
|
||||
|
||||
FastAPI-based REST API for video generation services.
|
||||
"""
|
||||
@@ -0,0 +1,191 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Pixelle-Video FastAPI Application
|
||||
|
||||
Main FastAPI app with all routers and middleware.
|
||||
|
||||
Run this script to start the FastAPI server:
|
||||
uv run python api/app.py
|
||||
|
||||
Or with custom settings:
|
||||
uv run python api/app.py --host 0.0.0.0 --port 8080 --reload
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add project root to sys.path for module imports
|
||||
# This ensures imports work correctly in both development and packaged environments
|
||||
_script_dir = Path(__file__).resolve().parent
|
||||
_project_root = _script_dir.parent
|
||||
if str(_project_root) not in sys.path:
|
||||
sys.path.insert(0, str(_project_root))
|
||||
|
||||
import argparse
|
||||
from contextlib import asynccontextmanager
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from loguru import logger
|
||||
|
||||
from api.config import api_config
|
||||
from api.tasks import task_manager
|
||||
from api.dependencies import shutdown_pixelle_video
|
||||
|
||||
# Import routers
|
||||
from api.routers import (
|
||||
health_router,
|
||||
llm_router,
|
||||
tts_router,
|
||||
image_router,
|
||||
content_router,
|
||||
video_router,
|
||||
tasks_router,
|
||||
files_router,
|
||||
resources_router,
|
||||
frame_router,
|
||||
)
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""
|
||||
Application lifespan manager
|
||||
|
||||
Handles startup and shutdown events.
|
||||
"""
|
||||
# Startup
|
||||
logger.info("🚀 Starting Pixelle-Video API...")
|
||||
await task_manager.start()
|
||||
logger.info("✅ Pixelle-Video API started successfully\n")
|
||||
|
||||
yield
|
||||
|
||||
# Shutdown
|
||||
logger.info("🛑 Shutting down Pixelle-Video API...")
|
||||
await task_manager.stop()
|
||||
await shutdown_pixelle_video()
|
||||
logger.info("✅ Pixelle-Video API shutdown complete")
|
||||
|
||||
|
||||
# Create FastAPI app
|
||||
app = FastAPI(
|
||||
title="Pixelle-Video API",
|
||||
description="""
|
||||
## Pixelle-Video - AI Video Generation Platform API
|
||||
|
||||
### Features
|
||||
- 🤖 **LLM**: Large language model integration
|
||||
- 🔊 **TTS**: Text-to-speech synthesis
|
||||
- 🎨 **Image**: AI image generation
|
||||
- 📝 **Content**: Automated content generation
|
||||
- 🎬 **Video**: End-to-end video generation
|
||||
|
||||
### Video Generation Modes
|
||||
- **Sync**: `/api/video/generate/sync` - For small videos (< 30s)
|
||||
- **Async**: `/api/video/generate/async` - For large videos with task tracking
|
||||
|
||||
### Getting Started
|
||||
1. Check health: `GET /health`
|
||||
2. Generate narrations: `POST /api/content/narration`
|
||||
3. Generate video: `POST /api/video/generate/sync` or `/async`
|
||||
4. Track task progress: `GET /api/tasks/{task_id}`
|
||||
""",
|
||||
version="0.1.0",
|
||||
docs_url=api_config.docs_url,
|
||||
redoc_url=api_config.redoc_url,
|
||||
openapi_url=api_config.openapi_url,
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
# Add CORS middleware
|
||||
if api_config.cors_enabled:
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=api_config.cors_origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
logger.info(f"CORS enabled for origins: {api_config.cors_origins}")
|
||||
|
||||
# Include routers
|
||||
# Health check (no prefix)
|
||||
app.include_router(health_router)
|
||||
|
||||
# API routers (with /api prefix)
|
||||
app.include_router(llm_router, prefix=api_config.api_prefix)
|
||||
app.include_router(tts_router, prefix=api_config.api_prefix)
|
||||
app.include_router(image_router, prefix=api_config.api_prefix)
|
||||
app.include_router(content_router, prefix=api_config.api_prefix)
|
||||
app.include_router(video_router, prefix=api_config.api_prefix)
|
||||
app.include_router(tasks_router, prefix=api_config.api_prefix)
|
||||
app.include_router(files_router, prefix=api_config.api_prefix)
|
||||
app.include_router(resources_router, prefix=api_config.api_prefix)
|
||||
app.include_router(frame_router, prefix=api_config.api_prefix)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
"""Root endpoint with API information"""
|
||||
return {
|
||||
"service": "Pixelle-Video API",
|
||||
"version": "0.1.0",
|
||||
"docs": api_config.docs_url,
|
||||
"health": "/health",
|
||||
"api": {
|
||||
"llm": f"{api_config.api_prefix}/llm",
|
||||
"tts": f"{api_config.api_prefix}/tts",
|
||||
"image": f"{api_config.api_prefix}/image",
|
||||
"content": f"{api_config.api_prefix}/content",
|
||||
"video": f"{api_config.api_prefix}/video",
|
||||
"tasks": f"{api_config.api_prefix}/tasks",
|
||||
"files": f"{api_config.api_prefix}/files",
|
||||
"resources": f"{api_config.api_prefix}/resources",
|
||||
"frame": f"{api_config.api_prefix}/frame",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
# Parse command line arguments
|
||||
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")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Print startup banner
|
||||
print(f"""
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ Pixelle-Video API Server ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
|
||||
Starting server at http://{args.host}:{args.port}
|
||||
API Docs: http://{args.host}:{args.port}/docs
|
||||
ReDoc: http://{args.host}:{args.port}/redoc
|
||||
|
||||
Press Ctrl+C to stop the server
|
||||
""")
|
||||
|
||||
# Start server
|
||||
uvicorn.run(
|
||||
"api.app:app",
|
||||
host=args.host,
|
||||
port=args.port,
|
||||
reload=args.reload,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
API Configuration
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class APIConfig(BaseModel):
|
||||
"""API configuration"""
|
||||
|
||||
# Server settings
|
||||
host: str = "0.0.0.0"
|
||||
port: int = 8000
|
||||
reload: bool = False
|
||||
|
||||
# CORS settings
|
||||
cors_enabled: bool = True
|
||||
cors_origins: list[str] = ["*"]
|
||||
|
||||
# Task settings
|
||||
max_concurrent_tasks: int = 5
|
||||
task_cleanup_interval: int = 3600 # Clean completed tasks every hour
|
||||
task_retention_time: int = 86400 # Keep task results for 24 hours
|
||||
|
||||
# File upload settings
|
||||
max_upload_size: int = 100 * 1024 * 1024 # 100MB
|
||||
|
||||
# API settings
|
||||
api_prefix: str = "/api"
|
||||
docs_url: Optional[str] = "/docs"
|
||||
redoc_url: Optional[str] = "/redoc"
|
||||
openapi_url: Optional[str] = "/openapi.json"
|
||||
|
||||
|
||||
# Global config instance
|
||||
api_config = APIConfig()
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
FastAPI Dependencies
|
||||
|
||||
Provides dependency injection for PixelleVideoCore and other services.
|
||||
"""
|
||||
|
||||
from typing import Annotated
|
||||
from fastapi import Depends
|
||||
from loguru import logger
|
||||
|
||||
from pixelle_video.service import PixelleVideoCore
|
||||
|
||||
|
||||
# Global Pixelle-Video instance
|
||||
_pixelle_video_instance: PixelleVideoCore = None
|
||||
|
||||
|
||||
async def get_pixelle_video() -> PixelleVideoCore:
|
||||
"""
|
||||
Get Pixelle-Video core instance (dependency injection)
|
||||
|
||||
Returns:
|
||||
PixelleVideoCore instance
|
||||
"""
|
||||
global _pixelle_video_instance
|
||||
|
||||
if _pixelle_video_instance is None:
|
||||
_pixelle_video_instance = PixelleVideoCore()
|
||||
await _pixelle_video_instance.initialize()
|
||||
logger.info("✅ Pixelle-Video initialized for API")
|
||||
|
||||
return _pixelle_video_instance
|
||||
|
||||
|
||||
async def shutdown_pixelle_video():
|
||||
"""Shutdown Pixelle-Video instance and cleanup resources"""
|
||||
global _pixelle_video_instance
|
||||
if _pixelle_video_instance:
|
||||
logger.info("Shutting down Pixelle-Video...")
|
||||
await _pixelle_video_instance.cleanup()
|
||||
_pixelle_video_instance = None
|
||||
|
||||
from pixelle_video.services.frame_html import HTMLFrameGenerator
|
||||
await HTMLFrameGenerator.close_browser()
|
||||
|
||||
|
||||
# Type alias for dependency injection
|
||||
PixelleVideoDep = Annotated[PixelleVideoCore, Depends(get_pixelle_video)]
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
API Routers
|
||||
"""
|
||||
|
||||
from api.routers.health import router as health_router
|
||||
from api.routers.llm import router as llm_router
|
||||
from api.routers.tts import router as tts_router
|
||||
from api.routers.image import router as image_router
|
||||
from api.routers.content import router as content_router
|
||||
from api.routers.video import router as video_router
|
||||
from api.routers.tasks import router as tasks_router
|
||||
from api.routers.files import router as files_router
|
||||
from api.routers.resources import router as resources_router
|
||||
from api.routers.frame import router as frame_router
|
||||
|
||||
__all__ = [
|
||||
"health_router",
|
||||
"llm_router",
|
||||
"tts_router",
|
||||
"image_router",
|
||||
"content_router",
|
||||
"video_router",
|
||||
"tasks_router",
|
||||
"files_router",
|
||||
"resources_router",
|
||||
"frame_router",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Content generation endpoints
|
||||
|
||||
Endpoints for generating narrations, image prompts, and titles.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from loguru import logger
|
||||
|
||||
from api.dependencies import PixelleVideoDep
|
||||
from api.schemas.content import (
|
||||
NarrationGenerateRequest,
|
||||
NarrationGenerateResponse,
|
||||
ImagePromptGenerateRequest,
|
||||
ImagePromptGenerateResponse,
|
||||
TitleGenerateRequest,
|
||||
TitleGenerateResponse,
|
||||
)
|
||||
from pixelle_video.utils.content_generators import (
|
||||
generate_narrations_from_topic,
|
||||
generate_image_prompts,
|
||||
generate_title,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/content", tags=["Content Generation"])
|
||||
|
||||
|
||||
@router.post("/narration", response_model=NarrationGenerateResponse)
|
||||
async def generate_narration(
|
||||
request: NarrationGenerateRequest,
|
||||
pixelle_video: PixelleVideoDep
|
||||
):
|
||||
"""
|
||||
Generate narrations from text
|
||||
|
||||
Uses LLM to break down text into multiple narration segments.
|
||||
|
||||
- **text**: Source text
|
||||
- **n_scenes**: Number of narrations to generate
|
||||
- **min_words**: Minimum words per narration
|
||||
- **max_words**: Maximum words per narration
|
||||
|
||||
Returns list of narration strings.
|
||||
"""
|
||||
try:
|
||||
logger.info(f"Generating {request.n_scenes} narrations from text")
|
||||
|
||||
# Call narration generator utility function
|
||||
narrations = await generate_narrations_from_topic(
|
||||
llm_service=pixelle_video.llm,
|
||||
topic=request.text,
|
||||
n_scenes=request.n_scenes,
|
||||
min_words=request.min_words,
|
||||
max_words=request.max_words
|
||||
)
|
||||
|
||||
return NarrationGenerateResponse(
|
||||
narrations=narrations
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Narration generation error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/image-prompt", response_model=ImagePromptGenerateResponse)
|
||||
async def generate_image_prompt(
|
||||
request: ImagePromptGenerateRequest,
|
||||
pixelle_video: PixelleVideoDep
|
||||
):
|
||||
"""
|
||||
Generate image prompts from narrations
|
||||
|
||||
Uses LLM to create detailed image generation prompts.
|
||||
|
||||
- **narrations**: List of narration texts
|
||||
- **min_words**: Minimum words per prompt
|
||||
- **max_words**: Maximum words per prompt
|
||||
|
||||
Returns list of image prompts.
|
||||
"""
|
||||
try:
|
||||
logger.info(f"Generating image prompts for {len(request.narrations)} narrations")
|
||||
|
||||
# Call image prompt generator utility function
|
||||
image_prompts = await generate_image_prompts(
|
||||
llm_service=pixelle_video.llm,
|
||||
narrations=request.narrations,
|
||||
min_words=request.min_words,
|
||||
max_words=request.max_words
|
||||
)
|
||||
|
||||
return ImagePromptGenerateResponse(
|
||||
image_prompts=image_prompts
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Image prompt generation error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/title", response_model=TitleGenerateResponse)
|
||||
async def generate_title_endpoint(
|
||||
request: TitleGenerateRequest,
|
||||
pixelle_video: PixelleVideoDep
|
||||
):
|
||||
"""
|
||||
Generate video title from text
|
||||
|
||||
Uses LLM to create an engaging title.
|
||||
|
||||
- **text**: Source text
|
||||
- **style**: Optional title style hint
|
||||
|
||||
Returns generated title.
|
||||
"""
|
||||
try:
|
||||
logger.info("Generating title from text")
|
||||
|
||||
# Call title generator utility function
|
||||
title = await generate_title(
|
||||
llm_service=pixelle_video.llm,
|
||||
content=request.text,
|
||||
strategy="llm"
|
||||
)
|
||||
|
||||
return TitleGenerateResponse(
|
||||
title=title
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Title generation error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
File service endpoints
|
||||
|
||||
Provides access to generated files (videos, images, audio) and resource files.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import FileResponse
|
||||
from loguru import logger
|
||||
|
||||
router = APIRouter(prefix="/files", tags=["Files"])
|
||||
|
||||
|
||||
@router.get("/{file_path:path}")
|
||||
async def get_file(file_path: str):
|
||||
"""
|
||||
Get file by path
|
||||
|
||||
Serves files from allowed directories:
|
||||
- output/ - Generated files (videos, images, audio)
|
||||
- workflows/ - ComfyUI workflow files
|
||||
- templates/ - HTML templates
|
||||
- bgm/ - Background music
|
||||
- data/bgm/ - Custom background music
|
||||
- data/templates/ - Custom templates
|
||||
- resources/ - Other resources (images, fonts, etc.)
|
||||
|
||||
- **file_path**: File path relative to allowed directories
|
||||
|
||||
Examples:
|
||||
- "abc123.mp4" → output/abc123.mp4
|
||||
- "workflows/runninghub/image_flux.json" → workflows/runninghub/image_flux.json
|
||||
- "templates/1080x1920/default.html" → templates/1080x1920/default.html
|
||||
- "bgm/default.mp3" → bgm/default.mp3
|
||||
- "resources/example.png" → resources/example.png
|
||||
|
||||
Returns file for download or preview.
|
||||
"""
|
||||
try:
|
||||
# Define allowed directories (in priority order)
|
||||
allowed_prefixes = [
|
||||
"output/",
|
||||
"workflows/",
|
||||
"templates/",
|
||||
"bgm/",
|
||||
"data/bgm/",
|
||||
"data/templates/",
|
||||
"resources/",
|
||||
]
|
||||
|
||||
# Check if path starts with allowed prefix, otherwise try output/
|
||||
full_path = None
|
||||
for prefix in allowed_prefixes:
|
||||
if file_path.startswith(prefix):
|
||||
full_path = file_path
|
||||
break
|
||||
|
||||
# If no prefix matched, assume it's in output/ (backward compatibility)
|
||||
if full_path is None:
|
||||
full_path = f"output/{file_path}"
|
||||
|
||||
abs_path = Path.cwd() / full_path
|
||||
|
||||
if not abs_path.exists():
|
||||
raise HTTPException(status_code=404, detail=f"File not found: {file_path}")
|
||||
|
||||
if not abs_path.is_file():
|
||||
raise HTTPException(status_code=400, detail=f"Path is not a file: {file_path}")
|
||||
|
||||
# Security: only allow access to specified directories
|
||||
try:
|
||||
rel_path = abs_path.relative_to(Path.cwd())
|
||||
rel_path_str = str(rel_path)
|
||||
|
||||
# Check if path starts with any allowed prefix
|
||||
is_allowed = any(rel_path_str.startswith(prefix.rstrip('/')) for prefix in allowed_prefixes)
|
||||
|
||||
if not is_allowed:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail=f"Access denied: only {', '.join(p.rstrip('/') for p in allowed_prefixes)} directories are accessible"
|
||||
)
|
||||
except ValueError:
|
||||
raise HTTPException(status_code=403, detail="Access denied")
|
||||
|
||||
# Determine media type
|
||||
suffix = abs_path.suffix.lower()
|
||||
media_types = {
|
||||
'.mp4': 'video/mp4',
|
||||
'.mp3': 'audio/mpeg',
|
||||
'.wav': 'audio/wav',
|
||||
'.png': 'image/png',
|
||||
'.jpg': 'image/jpeg',
|
||||
'.jpeg': 'image/jpeg',
|
||||
'.gif': 'image/gif',
|
||||
'.html': 'text/html',
|
||||
'.json': 'application/json',
|
||||
}
|
||||
media_type = media_types.get(suffix, 'application/octet-stream')
|
||||
|
||||
# Use inline disposition for browser preview
|
||||
return FileResponse(
|
||||
path=str(abs_path),
|
||||
media_type=media_type,
|
||||
headers={
|
||||
"Content-Disposition": f'inline; filename="{abs_path.name}"'
|
||||
}
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"File access error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Frame/Template rendering endpoints
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from loguru import logger
|
||||
|
||||
from api.dependencies import PixelleVideoDep
|
||||
from api.schemas.frame import FrameRenderRequest, FrameRenderResponse, TemplateParamsResponse
|
||||
from pixelle_video.services.frame_html import HTMLFrameGenerator
|
||||
from pixelle_video.utils.template_util import parse_template_size, resolve_template_path
|
||||
|
||||
router = APIRouter(prefix="/frame", tags=["Frame Rendering"])
|
||||
|
||||
|
||||
@router.post("/render", response_model=FrameRenderResponse)
|
||||
async def render_frame(
|
||||
request: FrameRenderRequest,
|
||||
pixelle_video: PixelleVideoDep
|
||||
):
|
||||
"""
|
||||
Render a single frame using HTML template
|
||||
|
||||
Generates a frame image by combining template, title, text, and image.
|
||||
This is useful for previewing templates or generating custom frames.
|
||||
|
||||
- **template**: Template key (e.g., '1080x1920/default.html')
|
||||
- **title**: Optional title text
|
||||
- **text**: Frame text content
|
||||
- **image**: Image path (can be local path or URL)
|
||||
|
||||
Returns path to generated frame image.
|
||||
|
||||
Example:
|
||||
```json
|
||||
{
|
||||
"template": "1080x1920/modern.html",
|
||||
"title": "Welcome",
|
||||
"text": "This is a beautiful frame with custom styling",
|
||||
"image": "resources/example.png"
|
||||
}
|
||||
```
|
||||
"""
|
||||
try:
|
||||
logger.info(f"Frame render request: template={request.template}")
|
||||
|
||||
# Resolve template path (returns absolute path with "templates/" or "data/templates/" prefix)
|
||||
template_path = resolve_template_path(request.template)
|
||||
|
||||
# Parse template size
|
||||
width, height = parse_template_size(template_path)
|
||||
|
||||
# Create HTML frame generator
|
||||
generator = HTMLFrameGenerator(template_path)
|
||||
|
||||
# Generate frame
|
||||
frame_path = await generator.generate_frame(
|
||||
title=request.title,
|
||||
text=request.text,
|
||||
image=request.image
|
||||
)
|
||||
|
||||
return FrameRenderResponse(
|
||||
frame_path=frame_path,
|
||||
width=width,
|
||||
height=height
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Frame render error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/template/params", response_model=TemplateParamsResponse)
|
||||
async def get_template_params(
|
||||
template: str
|
||||
):
|
||||
"""
|
||||
Get custom parameters for a template
|
||||
|
||||
Returns the custom parameters defined in the template HTML file.
|
||||
These parameters can be passed via `template_params` in video generation requests.
|
||||
|
||||
Template parameters are defined using syntax: `{{param_name:type=default}}`
|
||||
|
||||
Supported types:
|
||||
- `text`: String input
|
||||
- `number`: Numeric input
|
||||
- `color`: Color picker (hex format)
|
||||
- `bool`: Boolean checkbox
|
||||
|
||||
Example template syntax:
|
||||
```html
|
||||
<div style="color: {{accent_color:color=#ff0000}}">
|
||||
{{custom_text:text=Hello World}}
|
||||
</div>
|
||||
```
|
||||
|
||||
Args:
|
||||
template: Template path (e.g., '1080x1920/image_default.html')
|
||||
|
||||
Returns:
|
||||
Template parameters with their types, defaults, and labels
|
||||
|
||||
Example response:
|
||||
```json
|
||||
{
|
||||
"template": "1080x1920/image_default.html",
|
||||
"media_width": 1080,
|
||||
"media_height": 1440,
|
||||
"params": {
|
||||
"accent_color": {
|
||||
"type": "color",
|
||||
"default": "#ff0000",
|
||||
"label": "accent_color"
|
||||
},
|
||||
"background": {
|
||||
"type": "text",
|
||||
"default": "https://example.com/bg.jpg",
|
||||
"label": "background"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
"""
|
||||
try:
|
||||
logger.info(f"Get template params: {template}")
|
||||
|
||||
# Resolve template path
|
||||
template_path = resolve_template_path(template)
|
||||
|
||||
# Create generator and parse parameters
|
||||
generator = HTMLFrameGenerator(template_path)
|
||||
params = generator.parse_template_parameters()
|
||||
media_width, media_height = generator.get_media_size()
|
||||
|
||||
return TemplateParamsResponse(
|
||||
template=template,
|
||||
media_width=media_width,
|
||||
media_height=media_height,
|
||||
params=params
|
||||
)
|
||||
|
||||
except FileNotFoundError:
|
||||
raise HTTPException(status_code=404, detail=f"Template not found: {template}")
|
||||
except Exception as e:
|
||||
logger.error(f"Get template params error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Health check and system info endpoints
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
|
||||
router = APIRouter(tags=["Health"])
|
||||
|
||||
|
||||
class HealthResponse(BaseModel):
|
||||
"""Health check response"""
|
||||
status: str = "healthy"
|
||||
version: str = "0.1.0"
|
||||
service: str = "Pixelle-Video API"
|
||||
|
||||
|
||||
class CapabilitiesResponse(BaseModel):
|
||||
"""Capabilities response"""
|
||||
success: bool = True
|
||||
capabilities: dict
|
||||
|
||||
|
||||
@router.get("/health", response_model=HealthResponse)
|
||||
async def health_check():
|
||||
"""
|
||||
Health check endpoint
|
||||
|
||||
Returns service status and version information.
|
||||
"""
|
||||
return HealthResponse()
|
||||
|
||||
|
||||
@router.get("/version", response_model=HealthResponse)
|
||||
async def get_version():
|
||||
"""
|
||||
Get API version
|
||||
|
||||
Returns version information.
|
||||
"""
|
||||
return HealthResponse()
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Image generation endpoints
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from loguru import logger
|
||||
|
||||
from api.dependencies import PixelleVideoDep
|
||||
from api.schemas.image import ImageGenerateRequest, ImageGenerateResponse
|
||||
|
||||
router = APIRouter(prefix="/image", tags=["Basic Services"])
|
||||
|
||||
|
||||
@router.post("/generate", response_model=ImageGenerateResponse)
|
||||
async def image_generate(
|
||||
request: ImageGenerateRequest,
|
||||
pixelle_video: PixelleVideoDep
|
||||
):
|
||||
"""
|
||||
Image generation endpoint
|
||||
|
||||
Generate image from text prompt using ComfyKit.
|
||||
|
||||
- **prompt**: Image description/prompt
|
||||
- **width**: Image width (512-2048)
|
||||
- **height**: Image height (512-2048)
|
||||
- **workflow**: Optional custom workflow filename
|
||||
|
||||
Returns path to generated image.
|
||||
"""
|
||||
try:
|
||||
logger.info(f"Image generation request: {request.prompt[:50]}...")
|
||||
|
||||
# Call media service (backward compatible with image API)
|
||||
media_result = await pixelle_video.media(
|
||||
prompt=request.prompt,
|
||||
width=request.width,
|
||||
height=request.height,
|
||||
workflow=request.workflow
|
||||
)
|
||||
|
||||
# For backward compatibility, only support image results in /image endpoint
|
||||
if media_result.is_video:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Video workflow used. Please use /media/generate endpoint for video generation."
|
||||
)
|
||||
|
||||
return ImageGenerateResponse(
|
||||
image_path=media_result.url
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Image generation error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
LLM (Large Language Model) endpoints
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from loguru import logger
|
||||
|
||||
from api.dependencies import PixelleVideoDep
|
||||
from api.schemas.llm import LLMChatRequest, LLMChatResponse
|
||||
|
||||
router = APIRouter(prefix="/llm", tags=["Basic Services"])
|
||||
|
||||
|
||||
@router.post("/chat", response_model=LLMChatResponse)
|
||||
async def llm_chat(
|
||||
request: LLMChatRequest,
|
||||
pixelle_video: PixelleVideoDep
|
||||
):
|
||||
"""
|
||||
LLM chat endpoint
|
||||
|
||||
Generate text response using configured LLM.
|
||||
|
||||
- **prompt**: User prompt/question
|
||||
- **temperature**: Creativity level (0.0-2.0, lower = more deterministic)
|
||||
- **max_tokens**: Maximum response length
|
||||
|
||||
Returns generated text response.
|
||||
"""
|
||||
try:
|
||||
logger.info(f"LLM chat request: {request.prompt[:50]}...")
|
||||
|
||||
# Call LLM service
|
||||
response = await pixelle_video.llm(
|
||||
prompt=request.prompt,
|
||||
temperature=request.temperature,
|
||||
max_tokens=request.max_tokens
|
||||
)
|
||||
|
||||
return LLMChatResponse(
|
||||
content=response,
|
||||
tokens_used=None # Can add token counting if needed
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"LLM chat error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Resource discovery endpoints
|
||||
|
||||
Provides endpoints to discover available workflows, templates, and BGM.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from loguru import logger
|
||||
|
||||
from api.dependencies import PixelleVideoDep
|
||||
from api.schemas.resources import (
|
||||
WorkflowInfo,
|
||||
WorkflowListResponse,
|
||||
TemplateInfo,
|
||||
TemplateListResponse,
|
||||
BGMInfo,
|
||||
BGMListResponse,
|
||||
)
|
||||
from pixelle_video.utils.os_util import list_resource_files, get_root_path, get_data_path
|
||||
from pixelle_video.utils.template_util import get_all_templates_with_info
|
||||
|
||||
router = APIRouter(prefix="/resources", tags=["Resources"])
|
||||
|
||||
|
||||
@router.get("/workflows/tts", response_model=WorkflowListResponse)
|
||||
async def list_tts_workflows(pixelle_video: PixelleVideoDep):
|
||||
"""
|
||||
List available TTS workflows
|
||||
|
||||
Returns list of TTS workflows from both RunningHub and self-hosted sources.
|
||||
|
||||
Example response:
|
||||
```json
|
||||
{
|
||||
"workflows": [
|
||||
{
|
||||
"name": "tts_edge.json",
|
||||
"display_name": "tts_edge.json - Runninghub",
|
||||
"source": "runninghub",
|
||||
"path": "workflows/runninghub/tts_edge.json",
|
||||
"key": "runninghub/tts_edge.json",
|
||||
"workflow_id": "123456"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
"""
|
||||
try:
|
||||
# Get all workflows from TTS service
|
||||
all_workflows = pixelle_video.tts.list_workflows()
|
||||
|
||||
# Filter to TTS workflows only (filename starts with "tts_")
|
||||
tts_workflows = [
|
||||
WorkflowInfo(**wf)
|
||||
for wf in all_workflows
|
||||
if wf["name"].startswith("tts_")
|
||||
]
|
||||
|
||||
return WorkflowListResponse(workflows=tts_workflows)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"List TTS workflows error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/workflows/media", response_model=WorkflowListResponse)
|
||||
async def list_media_workflows(pixelle_video: PixelleVideoDep):
|
||||
"""
|
||||
List available media workflows (both image and video)
|
||||
|
||||
Returns list of all media workflows from both RunningHub and self-hosted sources.
|
||||
|
||||
Example response:
|
||||
```json
|
||||
{
|
||||
"workflows": [
|
||||
{
|
||||
"name": "image_flux.json",
|
||||
"display_name": "image_flux.json - Runninghub",
|
||||
"source": "runninghub",
|
||||
"path": "workflows/runninghub/image_flux.json",
|
||||
"key": "runninghub/image_flux.json",
|
||||
"workflow_id": "123456"
|
||||
},
|
||||
{
|
||||
"name": "video_wan2.1.json",
|
||||
"display_name": "video_wan2.1.json - Runninghub",
|
||||
"source": "runninghub",
|
||||
"path": "workflows/runninghub/video_wan2.1.json",
|
||||
"key": "runninghub/video_wan2.1.json",
|
||||
"workflow_id": "123457"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
"""
|
||||
try:
|
||||
# Get all workflows from media service (includes both image and video)
|
||||
all_workflows = pixelle_video.media.list_workflows()
|
||||
|
||||
media_workflows = [WorkflowInfo(**wf) for wf in all_workflows]
|
||||
|
||||
return WorkflowListResponse(workflows=media_workflows)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"List media workflows error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# Keep old endpoint for backward compatibility
|
||||
@router.get("/workflows/image", response_model=WorkflowListResponse)
|
||||
async def list_image_workflows(pixelle_video: PixelleVideoDep):
|
||||
"""
|
||||
List available image workflows (deprecated, use /workflows/media instead)
|
||||
|
||||
This endpoint is kept for backward compatibility but will filter to image_ workflows only.
|
||||
"""
|
||||
try:
|
||||
all_workflows = pixelle_video.media.list_workflows()
|
||||
|
||||
# Filter to image workflows only (filename starts with "image_")
|
||||
image_workflows = [
|
||||
WorkflowInfo(**wf)
|
||||
for wf in all_workflows
|
||||
if wf["name"].startswith("image_")
|
||||
]
|
||||
|
||||
return WorkflowListResponse(workflows=image_workflows)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"List image workflows error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/templates", response_model=TemplateListResponse)
|
||||
async def list_templates():
|
||||
"""
|
||||
List available video templates
|
||||
|
||||
Returns list of HTML templates grouped by size (portrait, landscape, square).
|
||||
Templates are merged from both default (templates/) and custom (data/templates/) directories.
|
||||
|
||||
Example response:
|
||||
```json
|
||||
{
|
||||
"templates": [
|
||||
{
|
||||
"name": "default.html",
|
||||
"display_name": "default.html",
|
||||
"size": "1080x1920",
|
||||
"width": 1080,
|
||||
"height": 1920,
|
||||
"orientation": "portrait",
|
||||
"path": "templates/1080x1920/default.html",
|
||||
"key": "1080x1920/default.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
"""
|
||||
try:
|
||||
# Get all templates with info
|
||||
all_templates = get_all_templates_with_info()
|
||||
|
||||
# Convert to API response format
|
||||
templates = []
|
||||
for t in all_templates:
|
||||
templates.append(TemplateInfo(
|
||||
name=t.display_info.name,
|
||||
display_name=t.display_info.name,
|
||||
size=t.display_info.size,
|
||||
width=t.display_info.width,
|
||||
height=t.display_info.height,
|
||||
orientation=t.display_info.orientation,
|
||||
path=t.template_path,
|
||||
key=t.template_path
|
||||
))
|
||||
|
||||
return TemplateListResponse(templates=templates)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"List templates error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/bgm", response_model=BGMListResponse)
|
||||
async def list_bgm():
|
||||
"""
|
||||
List available background music files
|
||||
|
||||
Returns list of BGM files merged from both default (bgm/) and custom (data/bgm/) directories.
|
||||
Custom files take precedence over default files with the same name.
|
||||
|
||||
Supported formats: mp3, wav, flac, m4a, aac, ogg
|
||||
|
||||
Example response:
|
||||
```json
|
||||
{
|
||||
"bgm_files": [
|
||||
{
|
||||
"name": "default.mp3",
|
||||
"path": "bgm/default.mp3",
|
||||
"source": "default"
|
||||
},
|
||||
{
|
||||
"name": "happy.mp3",
|
||||
"path": "data/bgm/happy.mp3",
|
||||
"source": "custom"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
"""
|
||||
try:
|
||||
# Supported audio extensions
|
||||
audio_extensions = ('.mp3', '.wav', '.flac', '.m4a', '.aac', '.ogg')
|
||||
|
||||
# Collect BGM files from both locations
|
||||
bgm_files_dict = {} # {filename: {"path": str, "source": str}}
|
||||
|
||||
# Scan default bgm/ directory
|
||||
default_bgm_dir = Path(get_root_path("bgm"))
|
||||
if default_bgm_dir.exists() and default_bgm_dir.is_dir():
|
||||
for item in default_bgm_dir.iterdir():
|
||||
if item.is_file() and item.suffix.lower() in audio_extensions:
|
||||
bgm_files_dict[item.name] = {
|
||||
"path": f"bgm/{item.name}",
|
||||
"source": "default"
|
||||
}
|
||||
|
||||
# Scan custom data/bgm/ directory (overrides default)
|
||||
custom_bgm_dir = Path(get_data_path("bgm"))
|
||||
if custom_bgm_dir.exists() and custom_bgm_dir.is_dir():
|
||||
for item in custom_bgm_dir.iterdir():
|
||||
if item.is_file() and item.suffix.lower() in audio_extensions:
|
||||
bgm_files_dict[item.name] = {
|
||||
"path": f"data/bgm/{item.name}",
|
||||
"source": "custom"
|
||||
}
|
||||
|
||||
# Convert to response format
|
||||
bgm_files = [
|
||||
BGMInfo(
|
||||
name=name,
|
||||
path=info["path"],
|
||||
source=info["source"]
|
||||
)
|
||||
for name, info in sorted(bgm_files_dict.items())
|
||||
]
|
||||
|
||||
return BGMListResponse(bgm_files=bgm_files)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"List BGM error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Task management endpoints
|
||||
|
||||
Endpoints for managing async tasks (checking status, canceling, etc.)
|
||||
"""
|
||||
|
||||
from typing import List, Optional
|
||||
from fastapi import APIRouter, HTTPException, Query
|
||||
from loguru import logger
|
||||
|
||||
from api.tasks import task_manager, Task, TaskStatus
|
||||
|
||||
router = APIRouter(prefix="/tasks", tags=["Tasks"])
|
||||
|
||||
|
||||
@router.get("", response_model=List[Task])
|
||||
async def list_tasks(
|
||||
status: Optional[TaskStatus] = Query(None, description="Filter by status"),
|
||||
limit: int = Query(100, ge=1, le=1000, description="Maximum number of tasks")
|
||||
):
|
||||
"""
|
||||
List tasks
|
||||
|
||||
Retrieve list of tasks with optional filtering.
|
||||
|
||||
- **status**: Optional filter by status (pending/running/completed/failed/cancelled)
|
||||
- **limit**: Maximum number of tasks to return (default 100)
|
||||
|
||||
Returns list of tasks sorted by creation time (newest first).
|
||||
"""
|
||||
try:
|
||||
tasks = task_manager.list_tasks(status=status, limit=limit)
|
||||
return tasks
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"List tasks error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/{task_id}", response_model=Task)
|
||||
async def get_task(task_id: str):
|
||||
"""
|
||||
Get task details
|
||||
|
||||
Retrieve detailed information about a specific task.
|
||||
|
||||
- **task_id**: Task ID
|
||||
|
||||
Returns task details including status, progress, and result (if completed).
|
||||
"""
|
||||
try:
|
||||
task = task_manager.get_task(task_id)
|
||||
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail=f"Task {task_id} not found")
|
||||
|
||||
return task
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Get task error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.delete("/{task_id}")
|
||||
async def cancel_task(task_id: str):
|
||||
"""
|
||||
Cancel task
|
||||
|
||||
Cancel a running or pending task.
|
||||
|
||||
- **task_id**: Task ID
|
||||
|
||||
Returns success status.
|
||||
"""
|
||||
try:
|
||||
success = task_manager.cancel_task(task_id)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(status_code=404, detail=f"Task {task_id} not found")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Task {task_id} cancelled successfully"
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Cancel task error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
TTS (Text-to-Speech) endpoints
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from loguru import logger
|
||||
|
||||
from api.dependencies import PixelleVideoDep
|
||||
from api.schemas.tts import TTSSynthesizeRequest, TTSSynthesizeResponse
|
||||
from pixelle_video.utils.tts_util import get_audio_duration
|
||||
|
||||
router = APIRouter(prefix="/tts", tags=["Basic Services"])
|
||||
|
||||
|
||||
@router.post("/synthesize", response_model=TTSSynthesizeResponse)
|
||||
async def tts_synthesize(
|
||||
request: TTSSynthesizeRequest,
|
||||
pixelle_video: PixelleVideoDep
|
||||
):
|
||||
"""
|
||||
Text-to-Speech synthesis endpoint
|
||||
|
||||
Convert text to speech audio using ComfyUI workflows.
|
||||
|
||||
- **text**: Text to synthesize
|
||||
- **workflow**: TTS workflow key (optional, uses default if not specified)
|
||||
- **ref_audio**: Reference audio for voice cloning (optional)
|
||||
- **voice_id**: (Deprecated) Voice ID for legacy compatibility
|
||||
|
||||
Returns path to generated audio file and duration.
|
||||
|
||||
Examples:
|
||||
```json
|
||||
{
|
||||
"text": "Hello, welcome to Pixelle-Video!",
|
||||
"workflow": "runninghub/tts_edge.json"
|
||||
}
|
||||
```
|
||||
|
||||
With voice cloning:
|
||||
```json
|
||||
{
|
||||
"text": "Hello, this is a cloned voice",
|
||||
"workflow": "runninghub/tts_index2.json",
|
||||
"ref_audio": "path/to/reference.wav"
|
||||
}
|
||||
```
|
||||
"""
|
||||
try:
|
||||
logger.info(f"TTS synthesis request: {request.text[:50]}...")
|
||||
|
||||
# Build TTS parameters
|
||||
tts_params = {"text": request.text}
|
||||
|
||||
# Add workflow if specified
|
||||
if request.workflow:
|
||||
tts_params["workflow"] = request.workflow
|
||||
|
||||
# Add ref_audio if specified
|
||||
if request.ref_audio:
|
||||
tts_params["ref_audio"] = request.ref_audio
|
||||
|
||||
# Legacy voice_id support (deprecated)
|
||||
if request.voice_id and not request.workflow:
|
||||
logger.warning("voice_id parameter is deprecated, please use workflow instead")
|
||||
tts_params["voice"] = request.voice_id
|
||||
|
||||
# Call TTS service
|
||||
audio_path = await pixelle_video.tts(**tts_params)
|
||||
|
||||
# Get audio duration
|
||||
duration = get_audio_duration(audio_path)
|
||||
|
||||
return TTSSynthesizeResponse(
|
||||
audio_path=audio_path,
|
||||
duration=duration
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"TTS synthesis error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Video generation endpoints
|
||||
|
||||
Supports both synchronous and asynchronous video generation.
|
||||
"""
|
||||
|
||||
import os
|
||||
from fastapi import APIRouter, HTTPException, Request
|
||||
from loguru import logger
|
||||
|
||||
from api.dependencies import PixelleVideoDep
|
||||
from api.schemas.video import (
|
||||
VideoGenerateRequest,
|
||||
VideoGenerateResponse,
|
||||
VideoGenerateAsyncResponse,
|
||||
)
|
||||
from api.tasks import task_manager, TaskType
|
||||
|
||||
router = APIRouter(prefix="/video", tags=["Video Generation"])
|
||||
|
||||
|
||||
def path_to_url(request: Request, file_path: str) -> str:
|
||||
"""
|
||||
Convert file path to accessible URL
|
||||
|
||||
Handles both absolute and relative paths, extracting the path relative
|
||||
to the output directory for URL construction.
|
||||
|
||||
Args:
|
||||
request: FastAPI Request object (provides base_url from actual request)
|
||||
file_path: Absolute or relative file path
|
||||
|
||||
Returns:
|
||||
Full URL to access the file
|
||||
|
||||
Examples:
|
||||
Windows: G:\\...\\output\\20251205_233630_c939\\final.mp4
|
||||
-> http://localhost:8000/api/files/20251205_233630_c939/final.mp4
|
||||
|
||||
Linux: /home/user/.../output/20251205_233630_c939/final.mp4
|
||||
-> http://localhost:8000/api/files/20251205_233630_c939/final.mp4
|
||||
|
||||
Domain: With domain request -> https://your-domain.com/api/files/...
|
||||
"""
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
# Normalize path separators to forward slashes first (for cross-platform compatibility)
|
||||
file_path = file_path.replace("\\", "/")
|
||||
|
||||
# Check if it's an absolute path (works for both Windows and Linux)
|
||||
is_absolute = os.path.isabs(file_path) or Path(file_path).is_absolute()
|
||||
|
||||
if is_absolute:
|
||||
# Find "output" in the path and get everything after it
|
||||
# Split by / to work with normalized paths
|
||||
parts = file_path.split("/")
|
||||
try:
|
||||
output_idx = parts.index("output")
|
||||
# Get all parts after "output" and join them
|
||||
relative_parts = parts[output_idx + 1:]
|
||||
file_path = "/".join(relative_parts)
|
||||
except ValueError:
|
||||
# If "output" not in path, use the filename only
|
||||
file_path = Path(file_path).name
|
||||
else:
|
||||
# If relative path starting with "output/", remove it
|
||||
if file_path.startswith("output/"):
|
||||
file_path = file_path[7:] # Remove "output/"
|
||||
|
||||
# Build URL using request's base_url (automatically matches the request host)
|
||||
base_url = str(request.base_url).rstrip('/')
|
||||
return f"{base_url}/api/files/{file_path}"
|
||||
|
||||
|
||||
@router.post("/generate/sync", response_model=VideoGenerateResponse)
|
||||
async def generate_video_sync(
|
||||
request_body: VideoGenerateRequest,
|
||||
pixelle_video: PixelleVideoDep,
|
||||
request: Request
|
||||
):
|
||||
"""
|
||||
Generate video synchronously
|
||||
|
||||
This endpoint blocks until video generation is complete.
|
||||
Suitable for small videos (< 30 seconds).
|
||||
|
||||
**Note**: May timeout for large videos. Use `/generate/async` instead.
|
||||
|
||||
Request body includes all video generation parameters.
|
||||
See VideoGenerateRequest schema for details.
|
||||
|
||||
Returns path to generated video, duration, and file size.
|
||||
"""
|
||||
try:
|
||||
logger.info(f"Sync video generation: {request_body.text[:50]}...")
|
||||
|
||||
# Auto-determine media_width and media_height from template meta tags (required)
|
||||
if not request_body.frame_template:
|
||||
raise ValueError("frame_template is required to determine media size")
|
||||
|
||||
from pixelle_video.services.frame_html import HTMLFrameGenerator
|
||||
from pixelle_video.utils.template_util import resolve_template_path
|
||||
template_path = resolve_template_path(request_body.frame_template)
|
||||
generator = HTMLFrameGenerator(template_path)
|
||||
media_width, media_height = generator.get_media_size()
|
||||
logger.debug(f"Auto-determined media size from template: {media_width}x{media_height}")
|
||||
|
||||
# Build video generation parameters
|
||||
video_params = {
|
||||
"text": request_body.text,
|
||||
"mode": request_body.mode,
|
||||
"title": request_body.title,
|
||||
"n_scenes": request_body.n_scenes,
|
||||
"min_narration_words": request_body.min_narration_words,
|
||||
"max_narration_words": request_body.max_narration_words,
|
||||
"min_image_prompt_words": request_body.min_image_prompt_words,
|
||||
"max_image_prompt_words": request_body.max_image_prompt_words,
|
||||
"media_width": media_width,
|
||||
"media_height": media_height,
|
||||
"media_workflow": request_body.media_workflow,
|
||||
"video_fps": request_body.video_fps,
|
||||
"frame_template": request_body.frame_template,
|
||||
"prompt_prefix": request_body.prompt_prefix,
|
||||
"bgm_path": request_body.bgm_path,
|
||||
"bgm_volume": request_body.bgm_volume,
|
||||
}
|
||||
|
||||
# Add TTS workflow if specified
|
||||
if request_body.tts_workflow:
|
||||
video_params["tts_workflow"] = request_body.tts_workflow
|
||||
|
||||
# Add ref_audio if specified
|
||||
if request_body.ref_audio:
|
||||
video_params["ref_audio"] = request_body.ref_audio
|
||||
|
||||
# Legacy voice_id support (deprecated)
|
||||
if request_body.voice_id:
|
||||
logger.warning("voice_id parameter is deprecated, please use tts_workflow instead")
|
||||
video_params["voice_id"] = request_body.voice_id
|
||||
|
||||
# Add custom template parameters if specified
|
||||
if request_body.template_params:
|
||||
video_params["template_params"] = request_body.template_params
|
||||
|
||||
# Call video generator service
|
||||
result = await pixelle_video.generate_video(**video_params)
|
||||
|
||||
# Get file size
|
||||
file_size = os.path.getsize(result.video_path) if os.path.exists(result.video_path) else 0
|
||||
|
||||
# Convert path to URL
|
||||
video_url = path_to_url(request, result.video_path)
|
||||
|
||||
return VideoGenerateResponse(
|
||||
video_url=video_url,
|
||||
duration=result.duration,
|
||||
file_size=file_size
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Sync video generation error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/generate/async", response_model=VideoGenerateAsyncResponse)
|
||||
async def generate_video_async(
|
||||
request_body: VideoGenerateRequest,
|
||||
pixelle_video: PixelleVideoDep,
|
||||
request: Request
|
||||
):
|
||||
"""
|
||||
Generate video asynchronously
|
||||
|
||||
Creates a background task for video generation.
|
||||
Returns immediately with a task_id for tracking progress.
|
||||
|
||||
**Workflow:**
|
||||
1. Submit video generation request
|
||||
2. Receive task_id in response
|
||||
3. Poll `/api/tasks/{task_id}` to check status
|
||||
4. When status is "completed", retrieve video from result
|
||||
|
||||
Request body includes all video generation parameters.
|
||||
See VideoGenerateRequest schema for details.
|
||||
|
||||
Returns task_id for tracking progress.
|
||||
"""
|
||||
try:
|
||||
logger.info(f"Async video generation: {request_body.text[:50]}...")
|
||||
|
||||
# Create task
|
||||
task = task_manager.create_task(
|
||||
task_type=TaskType.VIDEO_GENERATION,
|
||||
request_params=request_body.model_dump()
|
||||
)
|
||||
|
||||
# Define async execution function
|
||||
async def execute_video_generation():
|
||||
"""Execute video generation in background"""
|
||||
# Auto-determine media_width and media_height from template meta tags (required)
|
||||
if not request_body.frame_template:
|
||||
raise ValueError("frame_template is required to determine media size")
|
||||
|
||||
from pixelle_video.services.frame_html import HTMLFrameGenerator
|
||||
from pixelle_video.utils.template_util import resolve_template_path
|
||||
template_path = resolve_template_path(request_body.frame_template)
|
||||
generator = HTMLFrameGenerator(template_path)
|
||||
media_width, media_height = generator.get_media_size()
|
||||
logger.debug(f"Auto-determined media size from template: {media_width}x{media_height}")
|
||||
|
||||
# Build video generation parameters
|
||||
video_params = {
|
||||
"text": request_body.text,
|
||||
"mode": request_body.mode,
|
||||
"title": request_body.title,
|
||||
"n_scenes": request_body.n_scenes,
|
||||
"min_narration_words": request_body.min_narration_words,
|
||||
"max_narration_words": request_body.max_narration_words,
|
||||
"min_image_prompt_words": request_body.min_image_prompt_words,
|
||||
"max_image_prompt_words": request_body.max_image_prompt_words,
|
||||
"media_width": media_width,
|
||||
"media_height": media_height,
|
||||
"media_workflow": request_body.media_workflow,
|
||||
"video_fps": request_body.video_fps,
|
||||
"frame_template": request_body.frame_template,
|
||||
"prompt_prefix": request_body.prompt_prefix,
|
||||
"bgm_path": request_body.bgm_path,
|
||||
"bgm_volume": request_body.bgm_volume,
|
||||
# Progress callback can be added here if needed
|
||||
# "progress_callback": lambda event: task_manager.update_progress(...)
|
||||
}
|
||||
|
||||
# Add TTS workflow if specified
|
||||
if request_body.tts_workflow:
|
||||
video_params["tts_workflow"] = request_body.tts_workflow
|
||||
|
||||
# Add ref_audio if specified
|
||||
if request_body.ref_audio:
|
||||
video_params["ref_audio"] = request_body.ref_audio
|
||||
|
||||
# Legacy voice_id support (deprecated)
|
||||
if request_body.voice_id:
|
||||
logger.warning("voice_id parameter is deprecated, please use tts_workflow instead")
|
||||
video_params["voice_id"] = request_body.voice_id
|
||||
|
||||
# Add custom template parameters if specified
|
||||
if request_body.template_params:
|
||||
video_params["template_params"] = request_body.template_params
|
||||
|
||||
result = await pixelle_video.generate_video(**video_params)
|
||||
|
||||
# Get file size
|
||||
file_size = os.path.getsize(result.video_path) if os.path.exists(result.video_path) else 0
|
||||
|
||||
# Convert path to URL
|
||||
video_url = path_to_url(request, result.video_path)
|
||||
|
||||
return {
|
||||
"video_url": video_url,
|
||||
"duration": result.duration,
|
||||
"file_size": file_size
|
||||
}
|
||||
|
||||
# Start execution
|
||||
await task_manager.execute_task(
|
||||
task_id=task.task_id,
|
||||
coro_func=execute_video_generation
|
||||
)
|
||||
|
||||
return VideoGenerateAsyncResponse(
|
||||
task_id=task.task_id
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Async video generation error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
API Schemas (Pydantic models)
|
||||
"""
|
||||
|
||||
from api.schemas.base import BaseResponse, ErrorResponse
|
||||
from api.schemas.llm import LLMChatRequest, LLMChatResponse
|
||||
from api.schemas.tts import TTSSynthesizeRequest, TTSSynthesizeResponse
|
||||
from api.schemas.image import ImageGenerateRequest, ImageGenerateResponse
|
||||
from api.schemas.content import (
|
||||
NarrationGenerateRequest,
|
||||
NarrationGenerateResponse,
|
||||
ImagePromptGenerateRequest,
|
||||
ImagePromptGenerateResponse,
|
||||
TitleGenerateRequest,
|
||||
TitleGenerateResponse,
|
||||
)
|
||||
from api.schemas.video import (
|
||||
VideoGenerateRequest,
|
||||
VideoGenerateResponse,
|
||||
VideoGenerateAsyncResponse,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
# Base
|
||||
"BaseResponse",
|
||||
"ErrorResponse",
|
||||
# LLM
|
||||
"LLMChatRequest",
|
||||
"LLMChatResponse",
|
||||
# TTS
|
||||
"TTSSynthesizeRequest",
|
||||
"TTSSynthesizeResponse",
|
||||
# Image
|
||||
"ImageGenerateRequest",
|
||||
"ImageGenerateResponse",
|
||||
# Content
|
||||
"NarrationGenerateRequest",
|
||||
"NarrationGenerateResponse",
|
||||
"ImagePromptGenerateRequest",
|
||||
"ImagePromptGenerateResponse",
|
||||
"TitleGenerateRequest",
|
||||
"TitleGenerateResponse",
|
||||
# Video
|
||||
"VideoGenerateRequest",
|
||||
"VideoGenerateResponse",
|
||||
"VideoGenerateAsyncResponse",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Base schemas
|
||||
"""
|
||||
|
||||
from typing import Any, Optional
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class BaseResponse(BaseModel):
|
||||
"""Base API response"""
|
||||
success: bool = True
|
||||
message: str = "Success"
|
||||
data: Optional[Any] = None
|
||||
|
||||
|
||||
class ErrorResponse(BaseModel):
|
||||
"""Error response"""
|
||||
success: bool = False
|
||||
message: str
|
||||
error: Optional[str] = None
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Content generation API schemas
|
||||
"""
|
||||
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Narration Generation
|
||||
# ============================================================================
|
||||
|
||||
class NarrationGenerateRequest(BaseModel):
|
||||
"""Narration generation request"""
|
||||
text: str = Field(..., description="Source text to generate narrations from")
|
||||
n_scenes: int = Field(5, ge=1, le=20, description="Number of scenes")
|
||||
min_words: int = Field(5, ge=1, le=100, description="Minimum words per narration")
|
||||
max_words: int = Field(20, ge=1, le=200, description="Maximum words per narration")
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"text": "Atomic Habits is about making small changes that lead to remarkable results.",
|
||||
"n_scenes": 5,
|
||||
"min_words": 5,
|
||||
"max_words": 20
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class NarrationGenerateResponse(BaseModel):
|
||||
"""Narration generation response"""
|
||||
success: bool = True
|
||||
message: str = "Success"
|
||||
narrations: List[str] = Field(..., description="Generated narrations")
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Image Prompt Generation
|
||||
# ============================================================================
|
||||
|
||||
class ImagePromptGenerateRequest(BaseModel):
|
||||
"""Image prompt generation request"""
|
||||
narrations: List[str] = Field(..., description="List of narrations")
|
||||
min_words: int = Field(30, ge=10, le=100, description="Minimum words per prompt")
|
||||
max_words: int = Field(60, ge=10, le=200, description="Maximum words per prompt")
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"narrations": [
|
||||
"Small habits compound over time",
|
||||
"Focus on systems, not goals"
|
||||
],
|
||||
"min_words": 30,
|
||||
"max_words": 60
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ImagePromptGenerateResponse(BaseModel):
|
||||
"""Image prompt generation response"""
|
||||
success: bool = True
|
||||
message: str = "Success"
|
||||
image_prompts: List[str] = Field(..., description="Generated image prompts")
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Title Generation
|
||||
# ============================================================================
|
||||
|
||||
class TitleGenerateRequest(BaseModel):
|
||||
"""Title generation request"""
|
||||
text: str = Field(..., description="Source text")
|
||||
style: Optional[str] = Field(None, description="Title style (e.g., 'engaging', 'formal')")
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"text": "Atomic Habits is about making small changes that lead to remarkable results.",
|
||||
"style": "engaging"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TitleGenerateResponse(BaseModel):
|
||||
"""Title generation response"""
|
||||
success: bool = True
|
||||
message: str = "Success"
|
||||
title: str = Field(..., description="Generated title")
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Frame/Template rendering API schemas
|
||||
"""
|
||||
|
||||
from typing import Optional, Dict, Any, List
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class FrameRenderRequest(BaseModel):
|
||||
"""Frame rendering request"""
|
||||
template: str = Field(
|
||||
...,
|
||||
description="Template key (e.g., '1080x1920/default.html'). Can also be just filename (e.g., 'default.html') to use default size."
|
||||
)
|
||||
title: Optional[str] = Field(None, description="Frame title (optional)")
|
||||
text: str = Field(..., description="Frame text content")
|
||||
image: Optional[str] = Field(None, description="Image path or URL (optional)")
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"template": "1080x1920/default.html",
|
||||
"title": "Sample Title",
|
||||
"text": "This is a sample text for the frame.",
|
||||
"image": "resources/example.png"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class FrameRenderResponse(BaseModel):
|
||||
"""Frame rendering response"""
|
||||
success: bool = True
|
||||
message: str = "Success"
|
||||
frame_path: str = Field(..., description="Path to generated frame image")
|
||||
width: int = Field(..., description="Frame width in pixels")
|
||||
height: int = Field(..., description="Frame height in pixels")
|
||||
|
||||
|
||||
class TemplateParamConfig(BaseModel):
|
||||
"""Single template parameter configuration"""
|
||||
type: str = Field(..., description="Parameter type: 'text', 'number', 'color', 'bool'")
|
||||
default: Any = Field(..., description="Default value")
|
||||
label: str = Field(..., description="Display label for the parameter")
|
||||
|
||||
|
||||
class TemplateParamsResponse(BaseModel):
|
||||
"""Template parameters response"""
|
||||
success: bool = True
|
||||
message: str = "Success"
|
||||
template: str = Field(..., description="Template path")
|
||||
media_width: int = Field(..., description="Media width from template meta tags")
|
||||
media_height: int = Field(..., description="Media height from template meta tags")
|
||||
params: Dict[str, TemplateParamConfig] = Field(
|
||||
default_factory=dict,
|
||||
description="Custom parameters defined in template. Key is parameter name, value is config."
|
||||
)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Image generation API schemas
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class ImageGenerateRequest(BaseModel):
|
||||
"""Image generation request"""
|
||||
prompt: str = Field(..., description="Image generation prompt")
|
||||
width: int = Field(1024, ge=512, le=2048, description="Image width")
|
||||
height: int = Field(1024, ge=512, le=2048, description="Image height")
|
||||
workflow: Optional[str] = Field(None, description="Custom workflow filename")
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"prompt": "A serene mountain landscape at sunset, photorealistic style",
|
||||
"width": 1024,
|
||||
"height": 1024
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ImageGenerateResponse(BaseModel):
|
||||
"""Image generation response"""
|
||||
success: bool = True
|
||||
message: str = "Success"
|
||||
image_path: str = Field(..., description="Path to generated image")
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
LLM API schemas
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class LLMChatRequest(BaseModel):
|
||||
"""LLM chat request"""
|
||||
prompt: str = Field(..., description="User prompt")
|
||||
temperature: float = Field(0.7, ge=0.0, le=2.0, description="Temperature (0.0-2.0)")
|
||||
max_tokens: int = Field(2000, ge=1, le=32000, description="Maximum tokens")
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"prompt": "Explain the concept of atomic habits in 3 sentences",
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 2000
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class LLMChatResponse(BaseModel):
|
||||
"""LLM chat response"""
|
||||
success: bool = True
|
||||
message: str = "Success"
|
||||
content: str = Field(..., description="Generated response")
|
||||
tokens_used: Optional[int] = Field(None, description="Tokens used (if available)")
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Resource discovery API schemas
|
||||
"""
|
||||
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class WorkflowInfo(BaseModel):
|
||||
"""Workflow information"""
|
||||
name: str = Field(..., description="Workflow filename")
|
||||
display_name: str = Field(..., description="Display name with source info")
|
||||
source: str = Field(..., description="Source (runninghub or selfhost)")
|
||||
path: str = Field(..., description="Full path to workflow file")
|
||||
key: str = Field(..., description="Workflow key (source/name)")
|
||||
workflow_id: Optional[str] = Field(None, description="RunningHub workflow ID (if applicable)")
|
||||
|
||||
|
||||
class WorkflowListResponse(BaseModel):
|
||||
"""Workflow list response"""
|
||||
success: bool = True
|
||||
message: str = "Success"
|
||||
workflows: List[WorkflowInfo] = Field(..., description="List of available workflows")
|
||||
|
||||
|
||||
class TemplateInfo(BaseModel):
|
||||
"""Template information"""
|
||||
name: str = Field(..., description="Template filename")
|
||||
display_name: str = Field(..., description="Display name")
|
||||
size: str = Field(..., description="Size (e.g., 1080x1920)")
|
||||
width: int = Field(..., description="Width in pixels")
|
||||
height: int = Field(..., description="Height in pixels")
|
||||
orientation: str = Field(..., description="Orientation (portrait/landscape/square)")
|
||||
path: str = Field(..., description="Full path to template file")
|
||||
key: str = Field(..., description="Template key (size/name)")
|
||||
|
||||
|
||||
class TemplateListResponse(BaseModel):
|
||||
"""Template list response"""
|
||||
success: bool = True
|
||||
message: str = "Success"
|
||||
templates: List[TemplateInfo] = Field(..., description="List of available templates")
|
||||
|
||||
|
||||
class BGMInfo(BaseModel):
|
||||
"""BGM information"""
|
||||
name: str = Field(..., description="BGM filename")
|
||||
path: str = Field(..., description="Full path to BGM file")
|
||||
source: str = Field(..., description="Source (default or custom)")
|
||||
|
||||
|
||||
class BGMListResponse(BaseModel):
|
||||
"""BGM list response"""
|
||||
success: bool = True
|
||||
message: str = "Success"
|
||||
bgm_files: List[BGMInfo] = Field(..., description="List of available BGM files")
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
TTS API schemas
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class TTSSynthesizeRequest(BaseModel):
|
||||
"""TTS synthesis request"""
|
||||
text: str = Field(..., description="Text to synthesize")
|
||||
workflow: Optional[str] = Field(
|
||||
None,
|
||||
description="TTS workflow key (e.g., 'runninghub/tts_edge.json' or 'selfhost/tts_edge.json'). If not specified, uses default workflow from config."
|
||||
)
|
||||
ref_audio: Optional[str] = Field(
|
||||
None,
|
||||
description="Reference audio path for voice cloning (optional). Can be a local file path or URL."
|
||||
)
|
||||
voice_id: Optional[str] = Field(
|
||||
None,
|
||||
description="Voice ID (deprecated, use workflow instead)"
|
||||
)
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"text": "Hello, welcome to Pixelle-Video!",
|
||||
"workflow": "runninghub/tts_edge.json",
|
||||
"ref_audio": None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TTSSynthesizeResponse(BaseModel):
|
||||
"""TTS synthesis response"""
|
||||
success: bool = True
|
||||
message: str = "Success"
|
||||
audio_path: str = Field(..., description="Path to generated audio file")
|
||||
duration: float = Field(..., description="Audio duration in seconds")
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Video generation API schemas
|
||||
"""
|
||||
|
||||
from typing import Optional, Literal, Dict, Any
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class VideoGenerateRequest(BaseModel):
|
||||
"""Video generation request"""
|
||||
|
||||
# === Input ===
|
||||
text: str = Field(..., description="Source text for video generation")
|
||||
|
||||
# === Processing Mode ===
|
||||
mode: Literal["generate", "fixed"] = Field(
|
||||
"generate",
|
||||
description="Processing mode: 'generate' (AI generates narrations) or 'fixed' (use text as-is)"
|
||||
)
|
||||
|
||||
# === Optional Title ===
|
||||
title: Optional[str] = Field(None, description="Video title (auto-generated if not provided)")
|
||||
|
||||
# === Basic Config ===
|
||||
n_scenes: Optional[int] = Field(5, ge=1, le=20, description="Number of scenes (only used in 'generate' mode, ignored in 'fixed' mode)")
|
||||
|
||||
# === TTS Parameters ===
|
||||
tts_workflow: Optional[str] = Field(
|
||||
None,
|
||||
description="TTS workflow key (e.g., 'runninghub/tts_edge.json'). If not specified, uses default workflow from config."
|
||||
)
|
||||
ref_audio: Optional[str] = Field(
|
||||
None,
|
||||
description="Reference audio path for voice cloning (optional)"
|
||||
)
|
||||
voice_id: Optional[str] = Field(
|
||||
None,
|
||||
description="(Deprecated) TTS voice ID for legacy compatibility"
|
||||
)
|
||||
|
||||
# === LLM Parameters ===
|
||||
min_narration_words: int = Field(5, ge=1, le=100, description="Min narration words")
|
||||
max_narration_words: int = Field(20, ge=1, le=200, description="Max narration words")
|
||||
min_image_prompt_words: int = Field(30, ge=10, le=100, description="Min image prompt words")
|
||||
max_image_prompt_words: int = Field(60, ge=10, le=200, description="Max image prompt words")
|
||||
|
||||
# === Media Parameters ===
|
||||
# Note: media_width and media_height are auto-determined from template meta tags
|
||||
media_workflow: Optional[str] = Field(None, description="Custom media workflow (image or video)")
|
||||
|
||||
# === Video Parameters ===
|
||||
video_fps: int = Field(30, ge=15, le=60, description="Video FPS")
|
||||
|
||||
# === Frame Template (determines video size) ===
|
||||
frame_template: Optional[str] = Field(
|
||||
None,
|
||||
description="HTML template path with size (e.g., '1080x1920/default.html'). Video size is auto-determined from template."
|
||||
)
|
||||
|
||||
# === Template Custom Parameters ===
|
||||
template_params: Optional[Dict[str, Any]] = Field(
|
||||
None,
|
||||
description="Custom template parameters (e.g., {'accent_color': '#ff0000', 'background': 'url'}). "
|
||||
"Available parameters depend on the template. Use GET /api/templates/{template_path}/params to discover them."
|
||||
)
|
||||
|
||||
# === Image Style ===
|
||||
prompt_prefix: Optional[str] = Field(None, description="Image style prefix")
|
||||
|
||||
# === BGM ===
|
||||
bgm_path: Optional[str] = Field(None, description="Background music path")
|
||||
bgm_volume: float = Field(0.3, ge=0.0, le=1.0, description="BGM volume (0.0-1.0)")
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"text": "Atomic Habits teaches us that small changes compound over time to produce remarkable results.",
|
||||
"mode": "generate",
|
||||
"n_scenes": 5,
|
||||
"frame_template": "1080x1920/image_default.html",
|
||||
"template_params": {
|
||||
"accent_color": "#3498db",
|
||||
"background": "https://example.com/custom-bg.jpg"
|
||||
},
|
||||
"title": "The Power of Atomic Habits"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class VideoGenerateResponse(BaseModel):
|
||||
"""Video generation response (synchronous)"""
|
||||
success: bool = True
|
||||
message: str = "Success"
|
||||
video_url: str = Field(..., description="URL to access generated video")
|
||||
duration: float = Field(..., description="Video duration in seconds")
|
||||
file_size: int = Field(..., description="File size in bytes")
|
||||
|
||||
|
||||
class VideoGenerateAsyncResponse(BaseModel):
|
||||
"""Video generation async response"""
|
||||
success: bool = True
|
||||
message: str = "Task created successfully"
|
||||
task_id: str = Field(..., description="Task ID for tracking progress")
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Task management for async operations
|
||||
"""
|
||||
|
||||
from api.tasks.models import Task, TaskStatus, TaskType
|
||||
from api.tasks.manager import task_manager
|
||||
|
||||
__all__ = ["Task", "TaskStatus", "TaskType", "task_manager"]
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Task Manager
|
||||
|
||||
In-memory task management for video generation jobs.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import uuid
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, List, Optional, Callable
|
||||
from loguru import logger
|
||||
|
||||
from api.tasks.models import Task, TaskStatus, TaskType, TaskProgress
|
||||
from api.config import api_config
|
||||
|
||||
|
||||
class TaskManager:
|
||||
"""
|
||||
Task manager for handling async video generation tasks
|
||||
|
||||
Features:
|
||||
- In-memory storage (can be replaced with Redis later)
|
||||
- Task lifecycle management
|
||||
- Progress tracking
|
||||
- Auto cleanup of old tasks
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._tasks: Dict[str, Task] = {}
|
||||
self._task_futures: Dict[str, asyncio.Task] = {}
|
||||
self._cleanup_task: Optional[asyncio.Task] = None
|
||||
self._running = False
|
||||
|
||||
async def start(self):
|
||||
"""Start task manager and cleanup scheduler"""
|
||||
if self._running:
|
||||
logger.warning("Task manager already running")
|
||||
return
|
||||
|
||||
self._running = True
|
||||
self._cleanup_task = asyncio.create_task(self._cleanup_loop())
|
||||
logger.info("✅ Task manager started")
|
||||
|
||||
async def stop(self):
|
||||
"""Stop task manager and cancel all tasks"""
|
||||
self._running = False
|
||||
|
||||
# Cancel cleanup task
|
||||
if self._cleanup_task:
|
||||
self._cleanup_task.cancel()
|
||||
try:
|
||||
await self._cleanup_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
# Cancel all running tasks
|
||||
for task_id, future in self._task_futures.items():
|
||||
if not future.done():
|
||||
future.cancel()
|
||||
logger.info(f"Cancelled task: {task_id}")
|
||||
|
||||
self._tasks.clear()
|
||||
self._task_futures.clear()
|
||||
logger.info("✅ Task manager stopped")
|
||||
|
||||
def create_task(
|
||||
self,
|
||||
task_type: TaskType,
|
||||
request_params: Optional[dict] = None
|
||||
) -> Task:
|
||||
"""
|
||||
Create a new task
|
||||
|
||||
Args:
|
||||
task_type: Type of task
|
||||
request_params: Original request parameters
|
||||
|
||||
Returns:
|
||||
Created task
|
||||
"""
|
||||
task_id = str(uuid.uuid4())
|
||||
task = Task(
|
||||
task_id=task_id,
|
||||
task_type=task_type,
|
||||
status=TaskStatus.PENDING,
|
||||
request_params=request_params,
|
||||
)
|
||||
|
||||
self._tasks[task_id] = task
|
||||
logger.info(f"Created task {task_id} ({task_type})")
|
||||
return task
|
||||
|
||||
async def execute_task(
|
||||
self,
|
||||
task_id: str,
|
||||
coro_func: Callable,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
"""
|
||||
Execute task asynchronously
|
||||
|
||||
Args:
|
||||
task_id: Task ID
|
||||
coro_func: Async function to execute
|
||||
*args: Positional arguments
|
||||
**kwargs: Keyword arguments
|
||||
"""
|
||||
task = self._tasks.get(task_id)
|
||||
if not task:
|
||||
logger.error(f"Task {task_id} not found")
|
||||
return
|
||||
|
||||
# Create async task
|
||||
async def _execute():
|
||||
try:
|
||||
task.status = TaskStatus.RUNNING
|
||||
task.started_at = datetime.now()
|
||||
logger.info(f"Task {task_id} started")
|
||||
|
||||
# Execute the actual work
|
||||
result = await coro_func(*args, **kwargs)
|
||||
|
||||
# Update task with result
|
||||
task.status = TaskStatus.COMPLETED
|
||||
task.result = result
|
||||
task.completed_at = datetime.now()
|
||||
logger.info(f"Task {task_id} completed")
|
||||
|
||||
except Exception as e:
|
||||
task.status = TaskStatus.FAILED
|
||||
task.error = str(e)
|
||||
task.completed_at = datetime.now()
|
||||
logger.error(f"Task {task_id} failed: {e}")
|
||||
|
||||
# Start execution
|
||||
future = asyncio.create_task(_execute())
|
||||
self._task_futures[task_id] = future
|
||||
|
||||
def get_task(self, task_id: str) -> Optional[Task]:
|
||||
"""Get task by ID"""
|
||||
return self._tasks.get(task_id)
|
||||
|
||||
def list_tasks(
|
||||
self,
|
||||
status: Optional[TaskStatus] = None,
|
||||
limit: int = 100
|
||||
) -> List[Task]:
|
||||
"""
|
||||
List tasks with optional filtering
|
||||
|
||||
Args:
|
||||
status: Filter by status
|
||||
limit: Maximum number of tasks to return
|
||||
|
||||
Returns:
|
||||
List of tasks
|
||||
"""
|
||||
tasks = list(self._tasks.values())
|
||||
|
||||
if status:
|
||||
tasks = [t for t in tasks if t.status == status]
|
||||
|
||||
# Sort by created_at descending
|
||||
tasks.sort(key=lambda t: t.created_at, reverse=True)
|
||||
|
||||
return tasks[:limit]
|
||||
|
||||
def update_progress(
|
||||
self,
|
||||
task_id: str,
|
||||
current: int,
|
||||
total: int,
|
||||
message: str = ""
|
||||
):
|
||||
"""
|
||||
Update task progress
|
||||
|
||||
Args:
|
||||
task_id: Task ID
|
||||
current: Current progress
|
||||
total: Total steps
|
||||
message: Progress message
|
||||
"""
|
||||
task = self._tasks.get(task_id)
|
||||
if not task:
|
||||
return
|
||||
|
||||
percentage = (current / total * 100) if total > 0 else 0
|
||||
task.progress = TaskProgress(
|
||||
current=current,
|
||||
total=total,
|
||||
percentage=percentage,
|
||||
message=message
|
||||
)
|
||||
|
||||
def cancel_task(self, task_id: str) -> bool:
|
||||
"""
|
||||
Cancel a running task
|
||||
|
||||
Args:
|
||||
task_id: Task ID
|
||||
|
||||
Returns:
|
||||
True if cancelled, False otherwise
|
||||
"""
|
||||
task = self._tasks.get(task_id)
|
||||
if not task:
|
||||
return False
|
||||
|
||||
# Do not cancel already-terminal tasks
|
||||
if task.status in [TaskStatus.COMPLETED, TaskStatus.FAILED, TaskStatus.CANCELLED]:
|
||||
return False
|
||||
|
||||
# Cancel future if running
|
||||
future = self._task_futures.get(task_id)
|
||||
if future and not future.done():
|
||||
future.cancel()
|
||||
|
||||
# Update task status
|
||||
task.status = TaskStatus.CANCELLED
|
||||
task.completed_at = datetime.now()
|
||||
logger.info(f"Cancelled task {task_id}")
|
||||
return True
|
||||
|
||||
async def _cleanup_loop(self):
|
||||
"""Periodically clean up old completed tasks"""
|
||||
while self._running:
|
||||
try:
|
||||
await asyncio.sleep(api_config.task_cleanup_interval)
|
||||
self._cleanup_old_tasks()
|
||||
except asyncio.CancelledError:
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error(f"Error in cleanup loop: {e}")
|
||||
|
||||
def _cleanup_old_tasks(self):
|
||||
"""Remove old completed/failed tasks"""
|
||||
cutoff_time = datetime.now() - timedelta(seconds=api_config.task_retention_time)
|
||||
|
||||
tasks_to_remove = []
|
||||
for task_id, task in self._tasks.items():
|
||||
if task.status in [TaskStatus.COMPLETED, TaskStatus.FAILED, TaskStatus.CANCELLED]:
|
||||
if task.completed_at and task.completed_at < cutoff_time:
|
||||
tasks_to_remove.append(task_id)
|
||||
|
||||
for task_id in tasks_to_remove:
|
||||
del self._tasks[task_id]
|
||||
if task_id in self._task_futures:
|
||||
del self._task_futures[task_id]
|
||||
|
||||
if tasks_to_remove:
|
||||
logger.info(f"Cleaned up {len(tasks_to_remove)} old tasks")
|
||||
|
||||
|
||||
# Global task manager instance
|
||||
task_manager = TaskManager()
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# Copyright (C) 2025 AIDC-AI
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Task data models
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Any, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class TaskStatus(str, Enum):
|
||||
"""Task status"""
|
||||
PENDING = "pending"
|
||||
RUNNING = "running"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
CANCELLED = "cancelled"
|
||||
|
||||
|
||||
class TaskType(str, Enum):
|
||||
"""Task type"""
|
||||
VIDEO_GENERATION = "video_generation"
|
||||
|
||||
|
||||
class TaskProgress(BaseModel):
|
||||
"""Task progress information"""
|
||||
current: int = 0
|
||||
total: int = 0
|
||||
percentage: float = 0.0
|
||||
message: str = ""
|
||||
|
||||
|
||||
class Task(BaseModel):
|
||||
"""Task model"""
|
||||
task_id: str
|
||||
task_type: TaskType
|
||||
status: TaskStatus = TaskStatus.PENDING
|
||||
|
||||
# Progress tracking
|
||||
progress: Optional[TaskProgress] = None
|
||||
|
||||
# Result
|
||||
result: Optional[Any] = None
|
||||
error: Optional[str] = None
|
||||
|
||||
# Metadata
|
||||
created_at: datetime = Field(default_factory=datetime.now)
|
||||
started_at: Optional[datetime] = None
|
||||
completed_at: Optional[datetime] = None
|
||||
|
||||
# Request parameters (for reference)
|
||||
request_params: Optional[dict] = None
|
||||
|
||||
class Config:
|
||||
json_encoders = {
|
||||
datetime: lambda v: v.isoformat()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# Pixelle-Video Configuration
|
||||
# Copy this file to config.yaml and fill in your settings
|
||||
# ⚠️ Never commit config.yaml to Git!
|
||||
|
||||
project_name: Pixelle-Video
|
||||
|
||||
# ==================== LLM Configuration ====================
|
||||
# Supports any OpenAI SDK compatible API
|
||||
llm:
|
||||
api_key: ""
|
||||
base_url: ""
|
||||
model: ""
|
||||
|
||||
# Popular presets:
|
||||
# Qwen Max: base_url: "https://dashscope.aliyuncs.com/compatible-mode/v1" model: "qwen-max"
|
||||
# OpenAI GPT-4o: base_url: "https://api.openai.com/v1" model: "gpt-4o"
|
||||
# DeepSeek: base_url: "https://api.deepseek.com" model: "deepseek-chat"
|
||||
# Ollama (Local): base_url: "http://localhost:11434/v1" model: "llama3.2"
|
||||
|
||||
# ==================== Direct API Provider Configuration ====================
|
||||
# Optional: use providers directly for image/video/VLM generation without ComfyUI workflows.
|
||||
api_providers:
|
||||
common:
|
||||
print_model_input: false
|
||||
local_proxy: ""
|
||||
openai:
|
||||
api_key: ""
|
||||
base_url: "https://api.openai.com/v1"
|
||||
use_proxy: false
|
||||
dashscope:
|
||||
api_key: ""
|
||||
base_url: "https://dashscope.aliyuncs.com/api/v1"
|
||||
use_proxy: false
|
||||
ark:
|
||||
api_key: ""
|
||||
base_url: "https://ark.cn-beijing.volces.com/api/v3"
|
||||
use_proxy: false
|
||||
kling:
|
||||
base_url: "https://api-beijing.klingai.com"
|
||||
access_key: ""
|
||||
secret_key: ""
|
||||
use_proxy: false
|
||||
|
||||
# ==================== ComfyUI Configuration ====================
|
||||
comfyui:
|
||||
# Global ComfyUI settings
|
||||
comfyui_url: http://127.0.0.1:8188 # ComfyUI server URL (required for selfhost workflows)
|
||||
comfyui_api_key: "" # ComfyUI API key (optional, get from https://platform.comfy.org/profile/api-keys)
|
||||
# Note for Docker users: Use host.docker.internal:8188 (Mac/Windows) or host IP address (Linux)
|
||||
runninghub_api_key: "" # RunningHub API key (required for runninghub workflows)
|
||||
runninghub_concurrent_limit: 1 # Concurrent execution limit for RunningHub (1-10, default 1 for regular members)
|
||||
|
||||
# TTS-specific configuration
|
||||
tts:
|
||||
default_workflow: selfhost/tts_edge.json # TTS workflow to use
|
||||
|
||||
# Image-specific configuration
|
||||
image:
|
||||
# Required: Default workflow to use (no fallback)
|
||||
# Options: runninghub/image_flux.json (recommended, no local setup)
|
||||
# selfhost/image_flux.json (requires local ComfyUI)
|
||||
default_workflow: runninghub/image_flux.json
|
||||
|
||||
# Image prompt prefix (optional)
|
||||
prompt_prefix: "Minimalist black-and-white matchstick figure style illustration, clean lines, simple sketch style"
|
||||
|
||||
# Video-specific configuration
|
||||
video:
|
||||
# Required: Default workflow to use (no fallback)
|
||||
# Options: runninghub/video_wan2.1_fusionx.json (recommended, no local setup)
|
||||
# selfhost/video_wan2.1_fusionx.json (requires local ComfyUI)
|
||||
default_workflow: runninghub/video_wan2.1_fusionx.json
|
||||
|
||||
# Video prompt prefix (optional)
|
||||
prompt_prefix: "Minimalist black-and-white matchstick figure style illustration, clean lines, simple sketch style"
|
||||
|
||||
# ==================== Template Configuration ====================
|
||||
# Configure default template for video generation
|
||||
template:
|
||||
# Default frame template to use when not explicitly specified
|
||||
# Determines video aspect ratio and layout style
|
||||
# Template naming convention:
|
||||
# - static_*.html: Static style templates (no AI-generated media)
|
||||
# - image_*.html: Templates requiring AI-generated images
|
||||
# - video_*.html: Templates requiring AI-generated videos
|
||||
# Options:
|
||||
# - 1080x1920 (vertical/portrait): image_default.html, image_modern.html, image_elegant.html, static_simple.html, etc.
|
||||
# - 1080x1080 (square): image_minimal_framed.html, etc.
|
||||
# - 1920x1080 (horizontal/landscape): image_film.html, image_full.html, etc.
|
||||
# See templates/ directory for all available templates
|
||||
default_template: "1080x1920/image_default.html"
|
||||
@@ -0,0 +1,123 @@
|
||||
version: '3.8'
|
||||
|
||||
# Build Arguments Configuration
|
||||
# You can override these by setting environment variables before running docker-compose
|
||||
#
|
||||
# Example for China environment (auto uses Tsinghua mirror):
|
||||
# USE_CN_MIRROR=true docker-compose up -d
|
||||
#
|
||||
# Example for international environment (default):
|
||||
# docker-compose up -d
|
||||
|
||||
services:
|
||||
# Init Service - Ensures config.yaml exists before other services start
|
||||
# This fixes the Docker issue where mounting a non-existent file creates a directory
|
||||
init:
|
||||
image: alpine:latest
|
||||
volumes:
|
||||
- ./:/workspace
|
||||
command: >
|
||||
sh -c '
|
||||
if [ -d /workspace/config.yaml ]; then
|
||||
echo "⚠️ config.yaml is a directory, removing it...";
|
||||
rm -rf /workspace/config.yaml;
|
||||
fi;
|
||||
if [ ! -f /workspace/config.yaml ] && [ -f /workspace/config.example.yaml ]; then
|
||||
echo "📋 Creating config.yaml from config.example.yaml...";
|
||||
cp /workspace/config.example.yaml /workspace/config.yaml;
|
||||
echo "✅ config.yaml created successfully!";
|
||||
else
|
||||
echo "✅ config.yaml already exists.";
|
||||
fi
|
||||
'
|
||||
restart: "no"
|
||||
|
||||
# API Service - FastAPI backend
|
||||
api:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
USE_CN_MIRROR: ${USE_CN_MIRROR:-false}
|
||||
container_name: pixelle-video-api
|
||||
command: .venv/bin/python api/app.py --host 0.0.0.0 --port 8000
|
||||
depends_on:
|
||||
init:
|
||||
condition: service_completed_successfully
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
# Mount config file (read-write to allow saving from Web UI)
|
||||
# Note: init service auto-creates config.yaml from config.example.yaml if not exists
|
||||
- ./config.yaml:/app/config.yaml
|
||||
# Mount data directories for persistence
|
||||
# data/ contains: users/, bgm/, templates/, workflows/ (custom resources)
|
||||
- ./data:/app/data
|
||||
- ./output:/app/output
|
||||
# Note: Default resources (bgm/, templates/, workflows/) are baked into the image
|
||||
# Custom resources in data/* will override defaults
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
networks:
|
||||
- pixelle-network
|
||||
|
||||
# Web UI Service - Streamlit frontend
|
||||
web:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
USE_CN_MIRROR: ${USE_CN_MIRROR:-false}
|
||||
container_name: pixelle-video-web
|
||||
command: .venv/bin/streamlit run web/app.py --server.port 8501 --server.address 0.0.0.0
|
||||
depends_on:
|
||||
init:
|
||||
condition: service_completed_successfully
|
||||
ports:
|
||||
- "8501:8501"
|
||||
volumes:
|
||||
# Mount config file (read-write to allow saving from Web UI)
|
||||
# Note: init service auto-creates config.yaml from config.example.yaml if not exists
|
||||
- ./config.yaml:/app/config.yaml
|
||||
# Mount data directories for persistence
|
||||
# data/ contains: users/, bgm/, templates/, workflows/ (custom resources)
|
||||
- ./data:/app/data
|
||||
- ./output:/app/output
|
||||
# Note: Default resources (bgm/, templates/, workflows/) are baked into the image
|
||||
# Custom resources in data/* will override defaults
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- STREAMLIT_SERVER_PORT=8501
|
||||
- STREAMLIT_SERVER_ADDRESS=0.0.0.0
|
||||
- STREAMLIT_BROWSER_GATHER_USAGE_STATS=false
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8501/_stcore/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 15s
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
networks:
|
||||
- pixelle-network
|
||||
|
||||
networks:
|
||||
pixelle-network:
|
||||
driver: bridge
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
#!/bin/bash
|
||||
# Pixelle-Video Docker Quick Start Script
|
||||
|
||||
set -e
|
||||
|
||||
echo "🐳 Pixelle-Video Docker Deployment"
|
||||
echo "=================================="
|
||||
echo ""
|
||||
|
||||
# Check if config.yaml exists as a directory (Docker mount issue)
|
||||
if [ -d config.yaml ]; then
|
||||
echo "⚠️ config.yaml is a directory (Docker mount issue), removing it..."
|
||||
rm -rf config.yaml
|
||||
fi
|
||||
|
||||
# Check if config.yaml exists, if not, create from example
|
||||
if [ ! -f config.yaml ]; then
|
||||
echo "⚠️ config.yaml not found, creating from config.example.yaml..."
|
||||
if [ -f config.example.yaml ]; then
|
||||
cp config.example.yaml config.yaml
|
||||
echo "✅ config.yaml created successfully!"
|
||||
echo ""
|
||||
echo "⚠️ IMPORTANT: Please edit config.yaml and fill in:"
|
||||
echo " - LLM API key and settings"
|
||||
echo " - ComfyUI URL (use host.docker.internal:8188 for local Mac/Windows)"
|
||||
echo " - RunningHub API key (optional, for cloud workflows)"
|
||||
echo ""
|
||||
echo "You can also configure these settings in the Web UI after starting."
|
||||
echo ""
|
||||
else
|
||||
echo "❌ Error: config.example.yaml not found!"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if docker-compose is available
|
||||
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
|
||||
echo "❌ Error: docker-compose not found!"
|
||||
echo ""
|
||||
echo "Please install Docker Compose first:"
|
||||
echo " https://docs.docker.com/compose/install/"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Use docker-compose or docker compose based on availability
|
||||
if command -v docker-compose &> /dev/null; then
|
||||
DOCKER_COMPOSE="docker-compose"
|
||||
else
|
||||
DOCKER_COMPOSE="docker compose"
|
||||
fi
|
||||
|
||||
echo "📦 Building Docker images..."
|
||||
$DOCKER_COMPOSE build
|
||||
|
||||
echo ""
|
||||
echo "🚀 Starting services..."
|
||||
$DOCKER_COMPOSE up -d
|
||||
|
||||
echo ""
|
||||
echo "⏳ Waiting for services to be ready..."
|
||||
sleep 5
|
||||
|
||||
echo ""
|
||||
echo "✅ Pixelle-Video is now running!"
|
||||
echo ""
|
||||
echo "Services:"
|
||||
echo " 🌐 Web UI: http://localhost:8501"
|
||||
echo " 🔌 API: http://localhost:8000"
|
||||
echo " 📚 API Docs: http://localhost:8000/docs"
|
||||
echo ""
|
||||
echo "Custom Resources (optional):"
|
||||
echo " 📁 data/bgm/ - Custom background music (overrides default)"
|
||||
echo " 📁 data/templates/ - Custom HTML templates (overrides default)"
|
||||
echo " 📁 data/workflows/ - Custom ComfyUI workflows (overrides default)"
|
||||
echo ""
|
||||
echo "Useful commands:"
|
||||
echo " View logs: $DOCKER_COMPOSE logs -f"
|
||||
echo " Stop: $DOCKER_COMPOSE down"
|
||||
echo " Restart: $DOCKER_COMPOSE restart"
|
||||
echo " Rebuild: $DOCKER_COMPOSE up -d --build"
|
||||
echo ""
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# 🙋♀️ Pixelle-Video FAQ
|
||||
|
||||
### How to integrate custom local workflows?
|
||||
|
||||
If you want to integrate your own ComfyUI workflows, please follow these specifications:
|
||||
|
||||
1. **Run Locally First**: Ensure the workflow runs correctly in your local ComfyUI.
|
||||
2. **Parameter Binding**: Find the Text node (CLIP Text Encode or similar text input node) where prompt words need to be dynamically passed by the program.
|
||||
- Edit the **Title** of that node.
|
||||
- Change the title to `$prompt.text!` or `$prompt.value!` (depending on the input type accepted by the node).
|
||||
<img src="https://github.com/user-attachments/assets/ddb1962c-9272-486f-84ab-8019c3fb5bf4" width="600" alt="参数绑定示例" />
|
||||
|
||||
- *Reference Example: Check the editing method of existing JSON files in the `workflows/selfhost/` directory.*
|
||||
3. **Export Format**: Export the modified workflow as **API Format** (Save (API Format)).
|
||||
4. **File Naming**: Place the exported JSON file into the `workflows/` directory and adhere to the following naming prefixes:
|
||||
- **Image Workflows**: Prefix must be `image_` (e.g., `image_my_style.json`)
|
||||
- **Video Workflows**: Prefix must be `video_`
|
||||
- **TTS Workflows**: Prefix must be `tts_`
|
||||
|
||||
### How to debug RunningHub workflows locally?
|
||||
|
||||
If you want to test workflows locally that were originally intended for RunningHub cloud usage:
|
||||
|
||||
1. **Get ID**: Open the RunningHub workflow file and find the ID.
|
||||
2. **Load Workflow**: Paste the ID onto the end of the RunningHub URL (e.g., https://www.runninghub.cn/workflow/1983513964837543938) to enter the workflow page.
|
||||
<img src="https://github.com/user-attachments/assets/e5330b3a-5475-44f2-81e4-057d33fdf71b" width="600" alt="参数绑定示例" />
|
||||
|
||||
|
||||
3. **Download to Local**: Download the workflow as a JSON file from the workbench.
|
||||
4. **Local Testing**: Drag the downloaded file into your local ComfyUI canvas for testing and debugging.
|
||||
|
||||
### Common Errors and Solutions
|
||||
|
||||
#### 1. TTS (Text-to-Speech) Errors
|
||||
- **Reason**: The default Edge-TTS calls Microsoft's free interface, which may fail frequently due to network instability.
|
||||
- **Solution**:
|
||||
- Check your network connection.
|
||||
- It is recommended to switch to **ComfyUI TTS** workflows (select workflows with the `tts_` prefix) for higher stability.
|
||||
|
||||
#### 2. LLM (Large Language Model) Errors
|
||||
- **Troubleshooting Steps**:
|
||||
1. Check if the **Base URL** is correct (ensure no extra spaces or incorrect suffixes).
|
||||
2. Check if the **API Key** is valid and has sufficient balance.
|
||||
3. Check if the **Model Name** is spelled correctly.
|
||||
- *Tip: Please consult the official API documentation of your model provider (e.g., OpenAI, DeepSeek, Alibaba Cloud, etc.) for accurate configuration.*
|
||||
|
||||
#### 3. Error Message "Could not find a Chrome executable..."
|
||||
- **Reason**: Your computer system lacks the Chrome browser core, causing features dependent on the browser to fail.
|
||||
- **Solution**: Please download and install the Google Chrome browser.
|
||||
|
||||
### Where are generated videos saved?
|
||||
|
||||
All generated videos are automatically saved in the `output/` folder within the project directory. Upon completion, the interface will display the video duration, file size, number of shots, and a download link.
|
||||
|
||||
### Community Resources
|
||||
|
||||
- **GitHub Repository**: https://github.com/AIDC-AI/Pixelle-Video
|
||||
- **Issue Reporting**: Submit bugs or feature requests via GitHub Issues.
|
||||
- **Community Support**: Join discussion groups for help and experience sharing.
|
||||
- **Contribution**: The project is under the MIT license and welcomes contributions.
|
||||
|
||||
💡 **Tip**: If you cannot find the answer you need in this FAQ, please submit an issue on GitHub or join the community discussion. We will continue to update this FAQ based on user feedback!
|
||||
@@ -0,0 +1,65 @@
|
||||
# 🙋♀️ Pixelle-Video 常见问题解答 (FAQ)
|
||||
|
||||
|
||||
### 本地自己开发的工作流如何集成使用?
|
||||
|
||||
如果您想集成自己开发的 ComfyUI 工作流,请遵循以下规范:
|
||||
|
||||
1. **本地跑通**:首先确保工作流在您的本地 ComfyUI 中能正常运行。
|
||||
2. **参数绑定**:找到需要由程序动态传入提示词的 Text 节点(CLIP Text Encode 或类似文本输入节点)。
|
||||
- 编辑该节点的**标题 (Title)**。
|
||||
- 修改标题为 `$prompt.text!` 或 `$prompt.value!`(根据节点接受的输入类型决定)。
|
||||
<img src="https://github.com/user-attachments/assets/ddb1962c-9272-486f-84ab-8019c3fb5bf4" width="600" alt="参数绑定示例" />
|
||||
|
||||
- *参考示例:可以查看 `workflows/selfhost/` 目录下现有 JSON 文件的编辑方式。*
|
||||
3. **导出格式**:将修改好的工作流导出为 **API 格式** (Save (API Format))。
|
||||
4. **文件命名**:将导出的 JSON 文件放入 `workflows/` 目录,并遵守以下命名前缀:
|
||||
- **图片类工作流**:前缀必须是 `image_` (例如 `image_my_style.json`)
|
||||
- **视频类工作流**:前缀必须是 `video_`
|
||||
- **语音合成类**:前缀必须是 `tts_`
|
||||
|
||||
### 如何在本地调试项目中的 RunningHub 工作流?
|
||||
|
||||
如果您想在本地测试项目中原本用于 RunningHub 云端的工作流:
|
||||
|
||||
1. **获取 ID**:打开runninghub工作流文件,找到id
|
||||
2. **加载工作流**:将 ID 粘贴到 RunningHub 网站 URL 后缀上,如:https://www.runninghub.cn/workflow/1983513964837543938 进入该工作流页面。
|
||||
<img src="https://github.com/user-attachments/assets/e5330b3a-5475-44f2-81e4-057d33fdf71b" width="600" alt="参数绑定示例" />
|
||||
|
||||
|
||||
4. **下载到本地**:在工作台中将工作流下载为 JSON 文件。
|
||||
5. **本地测试**:将下载的文件拖入您本地的 ComfyUI 画布进行测试和调试。
|
||||
|
||||
|
||||
### 常见的报错及解决方案
|
||||
|
||||
#### 1. TTS (语音合成) 报错
|
||||
- **原因**:默认的 Edge-TTS 是调用微软的免费接口,可能会受网络波动影响,导致失败频率较高。
|
||||
- **解决方案**:
|
||||
- 检查网络连接。
|
||||
- 建议切换使用 **ComfyUI 合成 TTS** 的工作流(选择前缀为 `tts_` 的工作流),稳定性更高。
|
||||
|
||||
#### 2. LLM (大模型) 报错
|
||||
- **排查步骤**:
|
||||
1. 检查 **Base URL** 是否正确(不要多出空格或错误的后缀)。
|
||||
2. 检查 **API Key** 是否有效且有余额。
|
||||
3. 检查 **Model Name** 是否拼写正确。
|
||||
- *提示:请查阅您所使用的模型服务商(如 OpenAI、DeepSeek、阿里云等)的官方 API 文档获取准确配置。*
|
||||
|
||||
#### 3. 错误提示 "Could not find a Chrome executable..."
|
||||
- **原因**:您的电脑系统中缺少 Chrome 浏览器内核,导致部分依赖浏览器的功能无法运行。
|
||||
- **解决方案**:请下载并安装 Google Chrome 浏览器。
|
||||
|
||||
|
||||
### 生成的视频保存在哪里?
|
||||
|
||||
所有生成的视频自动保存到项目目录的 `output/` 文件夹中。生成完成后,界面会显示视频时长、文件大小、分镜数量及下载链接。
|
||||
|
||||
### 有哪些社区资源?
|
||||
|
||||
- **GitHub 仓库**:https://github.com/AIDC-AI/Pixelle-Video
|
||||
- **问题反馈**:通过 GitHub Issues 提交 bug 或功能请求
|
||||
- **社区支持**:加入讨论群组获取帮助和分享经验
|
||||
- **贡献代码**:项目在 MIT 许可证下欢迎贡献
|
||||
|
||||
💡 **提示**:如果在此 FAQ 中找不到您需要的答案,请在 GitHub 提交 issue 或加入社区讨论。我们会根据用户反馈持续更新此 FAQ!
|
||||
@@ -0,0 +1,54 @@
|
||||
# Architecture
|
||||
|
||||
Technical architecture overview of Pixelle-Video.
|
||||
|
||||
---
|
||||
|
||||
## Core Architecture
|
||||
|
||||
Pixelle-Video uses a layered architecture design:
|
||||
|
||||
- **Web Layer**: Streamlit Web interface
|
||||
- **Service Layer**: Core business logic
|
||||
- **ComfyUI Layer**: Image and TTS generation
|
||||
|
||||
---
|
||||
|
||||
## Main Components
|
||||
|
||||
### PixelleVideoCore
|
||||
|
||||
Core service class coordinating all sub-services.
|
||||
|
||||
### LLM Service
|
||||
|
||||
Responsible for calling large language models to generate scripts.
|
||||
|
||||
### Image Service
|
||||
|
||||
Responsible for calling ComfyUI to generate images.
|
||||
|
||||
### TTS Service
|
||||
|
||||
Responsible for calling ComfyUI to generate speech.
|
||||
|
||||
### Video Generator
|
||||
|
||||
Responsible for composing the final video.
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- **Backend**: Python 3.10+, AsyncIO
|
||||
- **Web**: Streamlit
|
||||
- **AI**: OpenAI API, ComfyUI
|
||||
- **Configuration**: YAML
|
||||
- **Tools**: uv (package management)
|
||||
|
||||
---
|
||||
|
||||
## More Information
|
||||
|
||||
Detailed architecture documentation coming soon.
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# Contributing
|
||||
|
||||
Thank you for your interest in contributing to Pixelle-Video!
|
||||
|
||||
---
|
||||
|
||||
## How to Contribute
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch (`git checkout -b feature/AmazingFeature`)
|
||||
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. Push to the branch (`git push origin feature/AmazingFeature`)
|
||||
5. Open a Pull Request
|
||||
|
||||
---
|
||||
|
||||
## Development Setup
|
||||
|
||||
```bash
|
||||
# Clone your fork
|
||||
git clone https://github.com/your-username/Pixelle-Video.git
|
||||
cd Pixelle-Video
|
||||
|
||||
# Install development dependencies
|
||||
uv sync
|
||||
|
||||
# Run tests
|
||||
pytest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Code Standards
|
||||
|
||||
- All code and comments in English
|
||||
- Follow PEP 8 standards
|
||||
- Add appropriate tests
|
||||
|
||||
---
|
||||
|
||||
## Submit Issues
|
||||
|
||||
Having problems or feature suggestions? Please submit at [GitHub Issues](https://github.com/AIDC-AI/Pixelle-Video/issues).
|
||||
|
||||
---
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Please be friendly and respectful. We are committed to fostering an inclusive community environment.
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# FAQ
|
||||
|
||||
Frequently Asked Questions.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Q: How to install uv?
|
||||
|
||||
```bash
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
```
|
||||
|
||||
### Q: Can I use something other than uv?
|
||||
|
||||
Yes, you can use traditional pip + venv approach.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### Q: Do I need to configure ComfyUI?
|
||||
|
||||
**Not necessarily** - it depends on your template choice:
|
||||
|
||||
| Template Type | ComfyUI | Best For | Speed |
|
||||
|--------------|---------|----------|-------|
|
||||
| Text-only<br/>(e.g., `simple.html`) | ❌ Not needed | Quotes, announcements, reading prompts | ⚡⚡⚡ Very fast |
|
||||
| AI Images<br/>(e.g., `default.html`) | ✅ Required | Rich visual content | ⚡ Standard |
|
||||
|
||||
**Tip**: Beginners can start with text-only templates for instant zero-barrier experience!
|
||||
|
||||
**Alternative**: If you need AI images but don't want local ComfyUI, use RunningHub cloud service.
|
||||
|
||||
### Q: Which LLMs are supported?
|
||||
|
||||
All OpenAI-compatible LLMs, including:
|
||||
- Qianwen
|
||||
- GPT-4o
|
||||
- DeepSeek
|
||||
- Ollama (local)
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
### Q: How long does first-time usage take?
|
||||
|
||||
Generating a 3-5 scene video takes approximately 2-5 minutes.
|
||||
|
||||
### Q: What if I'm not satisfied with the video?
|
||||
|
||||
Try:
|
||||
1. Change LLM model
|
||||
2. Adjust image dimensions and prompt prefix
|
||||
3. Change TTS workflow
|
||||
4. Try different video templates
|
||||
|
||||
### Q: What are the costs?
|
||||
|
||||
- **Completely Free**: Ollama + Local ComfyUI = $0
|
||||
- **Recommended**: Qianwen + Local ComfyUI ≈ $0.01-0.05/video
|
||||
- **Cloud Solution**: OpenAI + RunningHub (higher cost)
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Q: ComfyUI connection failed
|
||||
|
||||
1. Confirm ComfyUI is running
|
||||
2. Check if URL is correct
|
||||
3. Click "Test Connection" in Web interface
|
||||
|
||||
### Q: LLM API call failed
|
||||
|
||||
1. Check if API Key is correct
|
||||
2. Check network connection
|
||||
3. Review error messages
|
||||
|
||||
---
|
||||
|
||||
## Other Questions
|
||||
|
||||
Have other questions? Check [Troubleshooting](troubleshooting.md) or submit an [Issue](https://github.com/AIDC-AI/Pixelle-Video/issues).
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# 🎬 Video Gallery
|
||||
|
||||
Showcase of videos created with Pixelle-Video. Click on cards to view complete workflows and configuration files.
|
||||
|
||||
---
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- **Reading Habit**
|
||||
|
||||
---
|
||||
|
||||
<video controls width="100%" style="border-radius: 8px;">
|
||||
<source src="https://your-oss-bucket.oss-cn-hangzhou.aliyuncs.com/pixelle-video/reading-habit/video.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
[:octicons-mark-github-16: View Workflows & Config](https://github.com/AIDC-AI/Pixelle-Video/tree/main/docs/gallery/reading-habit)
|
||||
|
||||
- **Work Efficiency**
|
||||
|
||||
---
|
||||
|
||||
<video controls width="100%" style="border-radius: 8px;">
|
||||
<source src="https://your-oss-bucket.oss-cn-hangzhou.aliyuncs.com/pixelle-video/work-efficiency/video.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
[:octicons-mark-github-16: View Workflows & Config](https://github.com/AIDC-AI/Pixelle-Video/tree/main/docs/gallery/work-efficiency)
|
||||
|
||||
- **Healthy Diet**
|
||||
|
||||
---
|
||||
|
||||
<video controls width="100%" style="border-radius: 8px;">
|
||||
<source src="https://your-oss-bucket.oss-cn-hangzhou.aliyuncs.com/pixelle-video/healthy-diet/video.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
[:octicons-mark-github-16: View Workflows & Config](https://github.com/AIDC-AI/Pixelle-Video/tree/main/docs/gallery/healthy-diet)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
!!! tip "How to Use"
|
||||
Click on a case card to jump to GitHub, download workflow files and configuration, and reproduce the video effect with one click.
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# Configuration
|
||||
|
||||
After installation, you need to configure services to use Pixelle-Video.
|
||||
|
||||
---
|
||||
|
||||
## LLM Configuration
|
||||
|
||||
LLM (Large Language Model) is used to generate video scripts.
|
||||
|
||||
### Quick Preset Selection
|
||||
|
||||
1. Select a preset model from the dropdown:
|
||||
- Qianwen (recommended, great value)
|
||||
- GPT-4o
|
||||
- DeepSeek
|
||||
- Ollama (local, completely free)
|
||||
|
||||
2. The system will auto-fill `base_url` and `model`
|
||||
|
||||
3. Click「🔑 Get API Key」to register and obtain credentials
|
||||
|
||||
4. Enter your API Key
|
||||
|
||||
---
|
||||
|
||||
## Image/Video Generation Configuration
|
||||
|
||||
Two options available:
|
||||
|
||||
### Local Deployment
|
||||
|
||||
Using local ComfyUI service:
|
||||
|
||||
1. Install and start ComfyUI
|
||||
2. Enter ComfyUI URL (default `http://127.0.0.1:8188`)
|
||||
3. Click "Test Connection" to verify
|
||||
4. (Optional) Enter ComfyUI API Key (get from [Comfy Platform](https://platform.comfy.org/profile/api-keys))
|
||||
|
||||
### Cloud Deployment (Recommended)
|
||||
|
||||
Using RunningHub cloud service, no local GPU required:
|
||||
|
||||
1. Register for a RunningHub account
|
||||
2. Obtain API Key
|
||||
3. Enter API Key in configuration
|
||||
4. Configure advanced options (optional):
|
||||
- **Concurrent Limit**: Set number of simultaneous tasks (1-10, default 1 for regular members)
|
||||
- **Instance Type**: Choose 24GB or 48GB VRAM machine (48GB for large video generation)
|
||||
|
||||
---
|
||||
|
||||
## Save Configuration
|
||||
|
||||
After filling in all required configuration, click the "Save Configuration" button.
|
||||
|
||||
Configuration will be saved to `config.yaml` file.
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Quick Start](quick-start.md) - Create your first video
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
# Installation
|
||||
|
||||
This page will guide you through installing Pixelle-Video.
|
||||
|
||||
---
|
||||
|
||||
## System Requirements
|
||||
|
||||
### Required
|
||||
|
||||
- **Python**: 3.10 or higher
|
||||
- **Operating System**: Windows, macOS, or Linux
|
||||
- **Package Manager**: uv (recommended) or pip
|
||||
|
||||
### Optional
|
||||
|
||||
- **GPU**: NVIDIA GPU with 6GB+ VRAM recommended for local ComfyUI
|
||||
- **Network**: Stable internet connection for LLM API and image generation services
|
||||
|
||||
---
|
||||
|
||||
## 🪟 Windows All-in-One Package (Recommended for Windows Users)
|
||||
|
||||
**No need to install Python, uv, or ffmpeg - ready to use out of the box!**
|
||||
|
||||
### Download and Install
|
||||
|
||||
1. Visit [GitHub Releases](https://github.com/AIDC-AI/Pixelle-Video/releases/latest) to download the latest version
|
||||
2. Download the latest Windows All-in-One Package and extract it to any directory
|
||||
3. Double-click `start.bat` to launch the Web interface
|
||||
4. Your browser will automatically open `http://localhost:8501`
|
||||
|
||||
!!! success "Installation Complete!"
|
||||
The package includes all dependencies, no need to manually install any environment. On first use, you only need to configure API keys in "⚙️ System Configuration" to get started.
|
||||
|
||||
!!! tip "Next Steps"
|
||||
After installation, check out the [Configuration Guide](configuration.md) to set up LLM and image generation services, then see [Quick Start](quick-start.md) to create your first video.
|
||||
|
||||
---
|
||||
|
||||
## Install from Source (For macOS / Linux Users or Users Who Need Customization)
|
||||
|
||||
### Step 1: Clone the Repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/AIDC-AI/Pixelle-Video.git
|
||||
cd Pixelle-Video
|
||||
```
|
||||
|
||||
### Step 2: Install Dependencies
|
||||
|
||||
!!! tip "Recommended: Use uv"
|
||||
This project uses `uv` as the package manager, which is faster and more reliable than traditional pip.
|
||||
|
||||
#### Using uv (Recommended)
|
||||
|
||||
```bash
|
||||
# Install uv if you haven't already
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
|
||||
# Install project dependencies (uv will create a virtual environment automatically)
|
||||
uv sync
|
||||
```
|
||||
|
||||
#### Using pip
|
||||
|
||||
```bash
|
||||
# Create virtual environment
|
||||
python -m venv venv
|
||||
|
||||
# Activate virtual environment
|
||||
# Windows:
|
||||
venv\Scripts\activate
|
||||
# macOS/Linux:
|
||||
source venv/bin/activate
|
||||
|
||||
# Install dependencies
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verify Installation
|
||||
|
||||
Run the following command to verify the installation:
|
||||
|
||||
```bash
|
||||
# Using uv
|
||||
uv run streamlit run web/app.py
|
||||
|
||||
# Or using pip (activate virtual environment first)
|
||||
streamlit run web/app.py
|
||||
```
|
||||
|
||||
Your browser should automatically open `http://localhost:8501` and display the Pixelle-Video web interface.
|
||||
|
||||
!!! success "Installation Successful!"
|
||||
If you can see the web interface, the installation was successful! Next, check out the [Configuration Guide](configuration.md) to set up your services.
|
||||
|
||||
---
|
||||
|
||||
## Optional: Install ComfyUI (Local Deployment)
|
||||
|
||||
If you want to run image generation locally, you'll need to install ComfyUI:
|
||||
|
||||
### Quick Install
|
||||
|
||||
```bash
|
||||
# Clone ComfyUI
|
||||
git clone https://github.com/comfyanonymous/ComfyUI.git
|
||||
cd ComfyUI
|
||||
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Start ComfyUI
|
||||
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
|
||||
ComfyUI runs on `http://127.0.0.1:8188` by default.
|
||||
|
||||
!!! info "ComfyUI Models"
|
||||
ComfyUI requires downloading model files to work. Please refer to the [ComfyUI documentation](https://github.com/comfyanonymous/ComfyUI) for information on downloading and configuring models.
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Configuration](configuration.md) - Configure LLM and image generation services
|
||||
- [Quick Start](quick-start.md) - Create your first video
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
# Quick Start
|
||||
|
||||
Already installed and configured? Let's create your first video!
|
||||
|
||||
---
|
||||
|
||||
## Start the Web Interface
|
||||
|
||||
### Windows All-in-One Package Users
|
||||
|
||||
If you're using the Windows All-in-One Package, simply:
|
||||
1. Double-click `start.bat`
|
||||
2. Your browser will automatically open `http://localhost:8501`
|
||||
|
||||
### Install from Source Users
|
||||
|
||||
```bash
|
||||
# Using uv
|
||||
uv run streamlit run web/app.py
|
||||
```
|
||||
|
||||
Your browser will automatically open `http://localhost:8501`
|
||||
|
||||
---
|
||||
|
||||
## Create Your First Video
|
||||
|
||||
### Step 1: Check Configuration
|
||||
|
||||
On first use, expand the「⚙️ System Configuration」panel and confirm:
|
||||
|
||||
- **LLM Configuration**: Select an AI model (e.g., Qianwen, GPT) and enter API Key
|
||||
- **Image Configuration**: Configure ComfyUI address or RunningHub API Key
|
||||
|
||||
If not yet configured, see the [Configuration Guide](configuration.md).
|
||||
|
||||
Click "Save Configuration" when done.
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Enter a Topic
|
||||
|
||||
In the left panel's「📝 Content Input」section:
|
||||
|
||||
1. Select「**AI Generate Content**」mode
|
||||
2. Enter a topic in the text box, for example:
|
||||
```
|
||||
Why develop a reading habit
|
||||
```
|
||||
3. (Optional) Set number of scenes, default is 5 frames
|
||||
|
||||
!!! tip "Topic Examples"
|
||||
- Why develop a reading habit
|
||||
- How to improve work efficiency
|
||||
- The importance of healthy eating
|
||||
- The meaning of travel
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Configure Voice and Visuals
|
||||
|
||||
In the middle panel:
|
||||
|
||||
**Voice Settings**
|
||||
- Select TTS workflow (default Edge-TTS works well)
|
||||
- For voice cloning, upload a reference audio file
|
||||
|
||||
**Visual Settings**
|
||||
- Select image generation workflow (default works well)
|
||||
- Set image dimensions (default 1024x1024)
|
||||
- Choose video template (recommend portrait 1080x1920)
|
||||
|
||||
---
|
||||
|
||||
### Step 4: Generate Video
|
||||
|
||||
Click the「🎬 Generate Video」button in the right panel!
|
||||
|
||||
The system will show real-time progress:
|
||||
- Generate script
|
||||
- Generate images (for each scene)
|
||||
- Synthesize voice
|
||||
- Compose video
|
||||
|
||||
!!! info "Generation Time"
|
||||
Generating a 5-scene video takes about 2-5 minutes, depending on: LLM API response speed, image generation speed, TTS workflow type, and network conditions
|
||||
|
||||
---
|
||||
|
||||
### Step 5: Preview Video
|
||||
|
||||
Once complete, the video will automatically play in the right panel!
|
||||
|
||||
You'll see:
|
||||
- 📹 Video preview player
|
||||
- ⏱️ Video duration
|
||||
- 📦 File size
|
||||
- 🎬 Number of scenes
|
||||
- 📐 Video dimensions
|
||||
|
||||
The video file is saved in the `output/` folder.
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
Congratulations! You've successfully created your first video 🎉
|
||||
|
||||
Next, you can:
|
||||
|
||||
- **Adjust Styles** - See the [Custom Visual Style](../tutorials/custom-style.md) tutorial
|
||||
- **Clone Voices** - See the [Voice Cloning with Reference Audio](../tutorials/voice-cloning.md) tutorial
|
||||
- **Use API** - See the [API Usage Guide](../user-guide/api.md)
|
||||
- **Develop Templates** - See the [Template Development Guide](../user-guide/templates.md)
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
# Pixelle-Video 🎬
|
||||
|
||||
<div align="center" markdown="1">
|
||||
|
||||
**AI Video Creator - Generate a short video in 3 minutes**
|
||||
|
||||
[](https://github.com/AIDC-AI/Pixelle-Video/stargazers)
|
||||
[](https://github.com/AIDC-AI/Pixelle-Video/issues)
|
||||
[](https://github.com/AIDC-AI/Pixelle-Video/blob/main/LICENSE)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Overview
|
||||
|
||||
Simply input a **topic**, and Pixelle-Video will automatically:
|
||||
|
||||
- ✍️ Write video scripts
|
||||
- 🎨 Generate AI images
|
||||
- 🗣️ Synthesize voice narration
|
||||
- 🎵 Add background music
|
||||
- 🎬 Create the final video
|
||||
|
||||
**No barriers, no video editing experience required** - turn video creation into a one-line task!
|
||||
|
||||
---
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- ✅ **Fully Automated** - Input a topic, get a complete video in 3 minutes
|
||||
- ✅ **AI-Powered Scripts** - Intelligently create narration based on your topic
|
||||
- ✅ **AI-Generated Images** - Each sentence comes with beautiful AI illustrations
|
||||
- ✅ **AI Voice Synthesis** - Support for Edge-TTS, Index-TTS and more mainstream TTS solutions
|
||||
- ✅ **Background Music** - Add BGM for enhanced atmosphere
|
||||
- ✅ **Visual Styles** - Multiple templates to create unique video styles
|
||||
- ✅ **Flexible Dimensions** - Support for portrait, landscape and more video sizes
|
||||
- ✅ **Multiple AI Models** - Support for GPT, Qianwen, DeepSeek, Ollama, etc.
|
||||
- ✅ **Flexible Composition** - Based on ComfyUI architecture, use preset workflows or customize any capability
|
||||
|
||||
---
|
||||
|
||||
## 🎬 Video Examples
|
||||
|
||||
!!! info "Sample Videos"
|
||||
Coming soon: Video examples will be added here
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
Ready to get started? Just three steps:
|
||||
|
||||
1. **[Install Pixelle-Video](getting-started/installation.md)** - Download and install the project
|
||||
- 🪟 **Windows Users (Recommended)**: Use the [All-in-One Package](https://github.com/AIDC-AI/Pixelle-Video/releases/latest), no Python installation required
|
||||
- 💻 **macOS/Linux Users**: Install from source, see [Installation Guide](getting-started/installation.md)
|
||||
2. **[Configure Services](getting-started/configuration.md)** - Set up LLM and image generation services
|
||||
3. **[Create Your First Video](getting-started/quick-start.md)** - Start creating your first video
|
||||
|
||||
---
|
||||
|
||||
## 💰 Pricing
|
||||
|
||||
!!! success "Completely free to run!"
|
||||
|
||||
- **Completely Free**: Use Ollama (local) + Local ComfyUI = $0
|
||||
- **Recommended**: Use Qianwen LLM (≈$0.01-0.05 per 3-scene video) + Local ComfyUI
|
||||
- **Cloud Solution**: Use OpenAI + RunningHub (higher cost but no local setup required)
|
||||
|
||||
**Recommendation**: If you have a local GPU, go with the completely free solution. Otherwise, we recommend Qianwen for best value.
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Acknowledgments
|
||||
|
||||
Pixelle-Video was inspired by the following excellent open source projects:
|
||||
|
||||
- [Pixelle-MCP](https://github.com/AIDC-AI/Pixelle-MCP) - ComfyUI MCP server
|
||||
- [MoneyPrinterTurbo](https://github.com/harry0703/MoneyPrinterTurbo) - Excellent video generation tool
|
||||
- [NarratoAI](https://github.com/linyqh/NarratoAI) - Video narration automation tool
|
||||
- [MoneyPrinterPlus](https://github.com/ddean2009/MoneyPrinterPlus) - Video creation platform
|
||||
- [ComfyKit](https://github.com/puke3615/ComfyKit) - ComfyUI workflow wrapper library
|
||||
|
||||
Thanks to these projects for their open source spirit! 🙏
|
||||
|
||||
---
|
||||
|
||||
## 📢 Feedback & Support
|
||||
|
||||
- 🐛 **Found a bug**: Submit an [Issue](https://github.com/AIDC-AI/Pixelle-Video/issues)
|
||||
- 💡 **Feature request**: Submit a [Feature Request](https://github.com/AIDC-AI/Pixelle-Video/issues)
|
||||
- ⭐ **Give us a Star**: If this project helps you, please give us a star!
|
||||
|
||||
---
|
||||
|
||||
## 📝 License
|
||||
|
||||
This project is licensed under the Apache License 2.0. See the [LICENSE](https://github.com/AIDC-AI/Pixelle-Video/blob/main/LICENSE) file for details.
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
# API Overview
|
||||
|
||||
Pixelle-Video provides both Python SDK and HTTP REST API.
|
||||
|
||||
---
|
||||
|
||||
## Python SDK
|
||||
|
||||
### PixelleVideoCore
|
||||
|
||||
Main service class providing video generation functionality.
|
||||
|
||||
```python
|
||||
from pixelle_video.service import PixelleVideoCore
|
||||
|
||||
pixelle = PixelleVideoCore()
|
||||
await pixelle.initialize()
|
||||
```
|
||||
|
||||
### generate_video()
|
||||
|
||||
Primary method for generating videos.
|
||||
|
||||
**Parameters**:
|
||||
|
||||
- `text` (str): Topic or complete script
|
||||
- `mode` (str): Generation mode ("generate" or "fixed")
|
||||
- `n_scenes` (int): Number of scenes
|
||||
- `title` (str, optional): Video title
|
||||
- `tts_workflow` (str): TTS workflow
|
||||
- `media_workflow` (str): Media generation workflow (image or video)
|
||||
- `frame_template` (str): Video template
|
||||
- `template_params` (dict, optional): Custom template parameters
|
||||
- `bgm_path` (str, optional): BGM file path
|
||||
- `bgm_volume` (float): BGM volume (0.0-1.0)
|
||||
|
||||
**Returns**: `VideoResult` object
|
||||
|
||||
---
|
||||
|
||||
## HTTP REST API
|
||||
|
||||
Start the API server:
|
||||
|
||||
```bash
|
||||
uv run uvicorn api.app:app --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
### Video Generation - Synchronous
|
||||
|
||||
`POST /api/video/generate/sync`
|
||||
|
||||
Generate video synchronously, waits until completion. Suitable for small videos (< 30 seconds).
|
||||
|
||||
**Request Body**:
|
||||
|
||||
```json
|
||||
{
|
||||
"text": "Why you should develop a reading habit",
|
||||
"mode": "generate",
|
||||
"n_scenes": 5,
|
||||
"frame_template": "1080x1920/image_default.html",
|
||||
"template_params": {
|
||||
"accent_color": "#3498db",
|
||||
"background": "https://example.com/custom-bg.jpg"
|
||||
},
|
||||
"title": "The Power of Reading"
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Success",
|
||||
"video_url": "http://localhost:8000/api/files/xxx/final.mp4",
|
||||
"duration": 45.5,
|
||||
"file_size": 12345678
|
||||
}
|
||||
```
|
||||
|
||||
### Video Generation - Asynchronous
|
||||
|
||||
`POST /api/video/generate/async`
|
||||
|
||||
Generate video asynchronously, returns task ID immediately. Suitable for large videos.
|
||||
|
||||
**Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Task created successfully",
|
||||
"task_id": "abc123"
|
||||
}
|
||||
```
|
||||
|
||||
### Query Task Status
|
||||
|
||||
`GET /api/tasks/{task_id}`
|
||||
|
||||
**Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "abc123",
|
||||
"status": "completed",
|
||||
"result": {
|
||||
"video_url": "http://localhost:8000/api/files/xxx/final.mp4",
|
||||
"duration": 45.5,
|
||||
"file_size": 12345678
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Request Parameters
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `text` | string | Yes | Topic or complete script |
|
||||
| `mode` | string | No | `"generate"` (AI generates) or `"fixed"` (use text as-is) |
|
||||
| `n_scenes` | int | No | Number of scenes (1-20), only used in generate mode |
|
||||
| `title` | string | No | Video title (auto-generated if not provided) |
|
||||
| `frame_template` | string | No | Template path, e.g., `1080x1920/image_default.html` |
|
||||
| `template_params` | object | No | Custom template parameters (colors, backgrounds, etc.) |
|
||||
| `media_workflow` | string | No | Media workflow (image or video generation) |
|
||||
| `tts_workflow` | string | No | TTS workflow |
|
||||
| `ref_audio` | string | No | Reference audio path for voice cloning |
|
||||
| `prompt_prefix` | string | No | Image style prefix |
|
||||
| `bgm_path` | string | No | BGM file path |
|
||||
| `bgm_volume` | float | No | BGM volume (0.0-1.0, default 0.3) |
|
||||
|
||||
---
|
||||
|
||||
## More Information
|
||||
|
||||
API documentation is also available via Swagger UI: `http://localhost:8000/docs`
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
# Config Schema
|
||||
|
||||
Detailed explanation of the `config.yaml` configuration file.
|
||||
|
||||
---
|
||||
|
||||
## Configuration Structure
|
||||
|
||||
```yaml
|
||||
llm:
|
||||
api_key: "your-api-key"
|
||||
base_url: "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||
model: "qwen-plus"
|
||||
|
||||
comfyui:
|
||||
comfyui_url: "http://127.0.0.1:8188"
|
||||
comfyui_api_key: "" # ComfyUI API key (optional)
|
||||
runninghub_api_key: ""
|
||||
runninghub_concurrent_limit: 1 # Concurrent limit (1-10)
|
||||
runninghub_instance_type: "" # Instance type (optional, set to "plus" for 48GB VRAM)
|
||||
|
||||
image:
|
||||
default_workflow: "runninghub/image_flux.json"
|
||||
prompt_prefix: "Minimalist illustration style"
|
||||
|
||||
video:
|
||||
default_workflow: "runninghub/video_wan2.1_fusionx.json"
|
||||
prompt_prefix: "Minimalist illustration style"
|
||||
|
||||
tts:
|
||||
default_workflow: "selfhost/tts_edge.json"
|
||||
|
||||
template:
|
||||
default_template: "1080x1920/image_default.html"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## LLM Configuration
|
||||
|
||||
- `api_key`: API key
|
||||
- `base_url`: API service address (supports any OpenAI-compatible interface)
|
||||
- `model`: Model name
|
||||
|
||||
---
|
||||
|
||||
## ComfyUI Configuration
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
- `comfyui_url`: Local ComfyUI address (default `http://127.0.0.1:8188`)
|
||||
- `comfyui_api_key`: ComfyUI API key (optional, for [Comfy Platform](https://platform.comfy.org/profile/api-keys))
|
||||
|
||||
### RunningHub Cloud Configuration
|
||||
|
||||
- `runninghub_api_key`: RunningHub API key (required for cloud workflows)
|
||||
- `runninghub_concurrent_limit`: Concurrent execution limit (1-10, default 1 for regular members)
|
||||
- `runninghub_instance_type`: Instance type (optional)
|
||||
- Empty or unset: Use 24GB VRAM machine
|
||||
- `"plus"`: Use 48GB VRAM machine (suitable for large video generation)
|
||||
|
||||
### Image Configuration
|
||||
|
||||
- `default_workflow`: Default image generation workflow
|
||||
- `prompt_prefix`: Prompt prefix
|
||||
|
||||
### Video Configuration
|
||||
|
||||
- `default_workflow`: Default video generation workflow
|
||||
- `runninghub/video_wan2.1_fusionx.json`: Cloud workflow (recommended, no local setup required)
|
||||
- `selfhost/video_wan2.1_fusionx.json`: Local workflow (requires local ComfyUI support)
|
||||
- `prompt_prefix`: Video prompt prefix (controls video generation style)
|
||||
|
||||
### TTS Configuration
|
||||
|
||||
- `default_workflow`: Default TTS workflow
|
||||
|
||||
---
|
||||
|
||||
## Template Configuration
|
||||
|
||||
- `default_template`: Default frame template path (e.g., `1080x1920/image_default.html`)
|
||||
|
||||
---
|
||||
|
||||
## More Information
|
||||
|
||||
The configuration file is automatically created on first run.
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
# Troubleshooting
|
||||
|
||||
Having issues? Here are solutions to common problems.
|
||||
|
||||
---
|
||||
|
||||
## Installation Issues
|
||||
|
||||
### Dependency installation failed
|
||||
|
||||
```bash
|
||||
# Clean cache
|
||||
uv cache clean
|
||||
|
||||
# Reinstall
|
||||
uv sync
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration Issues
|
||||
|
||||
### ComfyUI connection failed
|
||||
|
||||
**Possible Causes**:
|
||||
- ComfyUI not running
|
||||
- Incorrect URL configuration
|
||||
- Firewall blocking
|
||||
|
||||
**Solutions**:
|
||||
1. Confirm ComfyUI is running
|
||||
2. Check URL configuration (default `http://127.0.0.1:8188`)
|
||||
3. Test by accessing ComfyUI address in browser
|
||||
4. Check firewall settings
|
||||
|
||||
### LLM API call failed
|
||||
|
||||
**Possible Causes**:
|
||||
- Incorrect API Key
|
||||
- Network issues
|
||||
- Insufficient balance
|
||||
|
||||
**Solutions**:
|
||||
1. Verify API Key is correct
|
||||
2. Check network connection
|
||||
3. Review error message details
|
||||
4. Check account balance
|
||||
|
||||
---
|
||||
|
||||
## Generation Issues
|
||||
|
||||
### Video generation failed
|
||||
|
||||
**Possible Causes**:
|
||||
- Corrupted workflow file
|
||||
- Models not downloaded
|
||||
- Insufficient resources
|
||||
|
||||
**Solutions**:
|
||||
1. Check if workflow file exists
|
||||
2. Confirm ComfyUI has downloaded required models
|
||||
3. Check disk space and memory
|
||||
|
||||
### Image generation failed
|
||||
|
||||
**Solutions**:
|
||||
1. Check if ComfyUI is running properly
|
||||
2. Try manually testing workflow in ComfyUI
|
||||
3. Check workflow configuration
|
||||
|
||||
### TTS generation failed
|
||||
|
||||
**Solutions**:
|
||||
1. Check if TTS workflow is correct
|
||||
2. If using voice cloning, check reference audio format
|
||||
3. Review error logs
|
||||
|
||||
---
|
||||
|
||||
## Performance Issues
|
||||
|
||||
### Slow generation speed
|
||||
|
||||
**Optimization Tips**:
|
||||
1. Use local ComfyUI (faster than cloud)
|
||||
2. Reduce number of scenes
|
||||
3. Use faster LLM (e.g., Qianwen)
|
||||
4. Check network connection
|
||||
|
||||
---
|
||||
|
||||
## Other Issues
|
||||
|
||||
Still having problems?
|
||||
|
||||
1. Check project [GitHub Issues](https://github.com/AIDC-AI/Pixelle-Video/issues)
|
||||
2. Submit a new Issue describing your problem
|
||||
3. Include error logs and configuration details for quick diagnosis
|
||||
|
||||
---
|
||||
|
||||
## View Logs
|
||||
|
||||
Log files are located in project root:
|
||||
- `api_server.log` - API service logs
|
||||
- `test_output.log` - Test logs
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# Custom Visual Style
|
||||
|
||||
Learn how to adjust image generation parameters to create unique visual styles.
|
||||
|
||||
---
|
||||
|
||||
## Adjust Prompt Prefix
|
||||
|
||||
The prompt prefix controls overall visual style:
|
||||
|
||||
```
|
||||
Minimalist black-and-white illustration, clean lines, simple style
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Adjust Image Dimensions
|
||||
|
||||
Different dimensions for different scenarios:
|
||||
|
||||
- **1024x1024**: Square, suitable for Xiaohongshu
|
||||
- **1080x1920**: Portrait, suitable for TikTok, Kuaishou
|
||||
- **1920x1080**: Landscape, suitable for Bilibili, YouTube
|
||||
|
||||
---
|
||||
|
||||
## Preview Effects
|
||||
|
||||
Use the "Preview Style" feature to test different configurations.
|
||||
|
||||
---
|
||||
|
||||
## More Information
|
||||
|
||||
More style customization tips coming soon.
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# Voice Cloning
|
||||
|
||||
Use reference audio to implement voice cloning functionality.
|
||||
|
||||
---
|
||||
|
||||
## Prepare Reference Audio
|
||||
|
||||
1. Prepare a clear audio file (MP3/WAV/FLAC)
|
||||
2. Recommended duration: 10-30 seconds
|
||||
3. Avoid background noise
|
||||
|
||||
---
|
||||
|
||||
## Usage Steps
|
||||
|
||||
1. Select a TTS workflow that supports voice cloning (e.g., Index-TTS) in voice settings
|
||||
2. Upload reference audio file
|
||||
3. Test effects with "Preview Voice"
|
||||
4. Generate video
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Not all TTS workflows support voice cloning
|
||||
- Reference audio quality affects cloning results
|
||||
- Edge-TTS does not support voice cloning
|
||||
|
||||
---
|
||||
|
||||
## More Information
|
||||
|
||||
Detailed voice cloning tutorial coming soon.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# Your First Video
|
||||
|
||||
Step-by-step guide to creating your first video with Pixelle-Video.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Make sure you've completed:
|
||||
|
||||
- ✅ [Installation](../getting-started/installation.md)
|
||||
- ✅ [Configuration](../getting-started/configuration.md)
|
||||
|
||||
---
|
||||
|
||||
## Tutorial Steps
|
||||
|
||||
For detailed steps, see [Quick Start](../getting-started/quick-start.md).
|
||||
|
||||
---
|
||||
|
||||
## Tips
|
||||
|
||||
- Choose an appropriate topic for better results
|
||||
- Start with 3-5 scenes for first generation
|
||||
- Preview voice and image effects before generating
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Having issues? Check out [FAQ](../faq.md) or [Troubleshooting](../troubleshooting.md).
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# API Usage
|
||||
|
||||
Pixelle-Video provides a complete Python API for easy integration into your projects.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
```python
|
||||
from pixelle_video.service import PixelleVideoCore
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
# Initialize
|
||||
pixelle = PixelleVideoCore()
|
||||
await pixelle.initialize()
|
||||
|
||||
# Generate video
|
||||
result = await pixelle.generate_video(
|
||||
text="Why develop a reading habit",
|
||||
mode="generate",
|
||||
n_scenes=5
|
||||
)
|
||||
|
||||
print(f"Video generated: {result.video_path}")
|
||||
|
||||
# Run
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
For detailed API documentation, see [API Overview](../reference/api-overview.md).
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
For more usage examples, check the `examples/` directory in the project.
|
||||
|
||||
@@ -0,0 +1,410 @@
|
||||
# Template Development
|
||||
|
||||
How to create custom video templates.
|
||||
|
||||
---
|
||||
|
||||
## Template Introduction
|
||||
|
||||
Video templates use HTML to define the layout and style of video frames. Pixelle-Video provides multiple preset templates covering different video dimensions and style requirements.
|
||||
|
||||
---
|
||||
|
||||
## Built-in Template Preview
|
||||
|
||||
### Portrait Templates (1080x1920)
|
||||
|
||||
Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- **static_default**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Default static template
|
||||
|
||||
- **static_excerpt**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Text excerpt static template
|
||||
|
||||
- **Blur Card**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Blurred background card style, suitable for graphic content display
|
||||
|
||||
- **Cartoon**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Cartoon style, suitable for light and lively content
|
||||
|
||||
- **Default**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Default template, simple and versatile
|
||||
|
||||
- **Elegant**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Elegant style, suitable for artistic and intellectual content
|
||||
|
||||
- **Fashion Vintage**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Retro fashion style, suitable for nostalgic themes
|
||||
|
||||
- **Life Insights**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Life insight style, suitable for inspirational content
|
||||
|
||||
- **Modern**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Modern minimalist style, suitable for business and tech content
|
||||
|
||||
- **Neon**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Neon style, suitable for fashion and trendy content
|
||||
|
||||
- **Psychology Card**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Psychology card style, suitable for knowledge popularization
|
||||
|
||||
- **Purple**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Purple theme, suitable for dreamy and mysterious styles
|
||||
|
||||
- **Satirical Cartoon**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
80s satirical cartoon style for spiritual tales
|
||||
|
||||
- **Simple Black Background**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Simple black background, suitable for inspirational content
|
||||
|
||||
- **Simple Line Drawing**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Simple line drawing style for cognitive growth content
|
||||
|
||||
- **Book**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Book style, suitable for book lists
|
||||
|
||||
- **Long Text**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Long text style, suitable for inspirational content
|
||||
|
||||
- **Excerpt**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Excerpt style, suitable for quotes and book excerpts
|
||||
|
||||
- **Health Preservation**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Health preserving tips, suitable for wellness explainers.
|
||||
|
||||
- **Life Insights**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Life insights, conveying warmth and strength
|
||||
|
||||
- **Full**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Full screen display, suitable for book lists
|
||||
|
||||
- **Healing**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Healing style, suitable for therapeutic content
|
||||
|
||||
- **Video_Default**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Default dynamic template
|
||||
|
||||
- **Video_Healing**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Healing dynamic template
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
### Landscape Templates (1920x1080)
|
||||
|
||||
Suitable for YouTube, Bilibili, and other video platforms.
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- **Ultrawide Minimal**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Ultrawide minimalist style, suitable for desktop viewing
|
||||
|
||||
- **Wide Darktech**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Dark tech style, suitable for technology and gaming content
|
||||
|
||||
- **Film**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Film style, immersive experience
|
||||
|
||||
- **Full**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Full screen display, suitable for book lists
|
||||
|
||||
- **Book**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Book style, suitable for book lists
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
### Square Templates (1080x1080)
|
||||
|
||||
Suitable for Instagram, WeChat Moments, and other platforms.
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- **Minimal Framed**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
Minimalist framed style, suitable for social media sharing
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## Template Naming Convention
|
||||
|
||||
Templates follow a unified naming convention to distinguish different types:
|
||||
|
||||
- **`static_*.html`**: Static templates
|
||||
- No AI-generated media content required
|
||||
- Pure text style rendering
|
||||
- Suitable for quick generation and low-cost scenarios
|
||||
|
||||
- **`image_*.html`**: Image templates
|
||||
- Uses AI-generated images as background
|
||||
- Invokes ComfyUI image generation workflows
|
||||
- Suitable for content requiring visual illustrations
|
||||
|
||||
- **`video_*.html`**: Video templates
|
||||
- Uses AI-generated videos as background
|
||||
- Invokes ComfyUI video generation workflows
|
||||
- Creates dynamic video content with enhanced expressiveness
|
||||
|
||||
## Template Structure
|
||||
|
||||
Templates are located in the `templates/` directory, grouped by size:
|
||||
|
||||
```
|
||||
templates/
|
||||
├── 1080x1920/ # Portrait
|
||||
│ ├── static_*.html # Static templates
|
||||
│ ├── image_*.html # Image templates
|
||||
│ └── video_*.html # Video templates
|
||||
├── 1920x1080/ # Landscape
|
||||
│ └── image_*.html # Image templates
|
||||
└── 1080x1080/ # Square
|
||||
└── image_*.html # Image templates
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Creating Custom Templates
|
||||
|
||||
### Steps
|
||||
|
||||
1. Copy an existing template file from the `templates/` directory
|
||||
2. Modify HTML and CSS styles
|
||||
3. Save to the corresponding size directory with `.html` extension
|
||||
4. Use the new template name in configuration or Web interface
|
||||
|
||||
### Template Variables
|
||||
|
||||
Templates support the following Jinja2 variables:
|
||||
|
||||
- `{{ title }}` - Video title (optional)
|
||||
- `{{ text }}` - Current scene text content
|
||||
- `{{ image }}` - Current scene image (if any)
|
||||
|
||||
### Example Template
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
width: 1080px;
|
||||
height: 1920px;
|
||||
margin: 0;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: 'Arial', sans-serif;
|
||||
}
|
||||
.content {
|
||||
text-align: center;
|
||||
color: white;
|
||||
padding: 40px;
|
||||
}
|
||||
.text {
|
||||
font-size: 48px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<div class="text">{{ text }}</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Template Development Tips
|
||||
|
||||
### 1. Responsive Sizing
|
||||
|
||||
Ensure the template's `body` size matches the target video dimensions:
|
||||
|
||||
- Portrait: `width: 1080px; height: 1920px;`
|
||||
- Landscape: `width: 1920px; height: 1080px;`
|
||||
- Square: `width: 1080px; height: 1080px;`
|
||||
|
||||
### 2. Text Typography
|
||||
|
||||
- Use appropriate font sizes and line heights for readability
|
||||
- Add shadows or backgrounds to text for better contrast
|
||||
- Control text length to avoid overflow
|
||||
|
||||
### 3. Image Handling
|
||||
|
||||
- Use `object-fit: cover` to ensure image filling
|
||||
- Add gradients or overlay layers to improve text readability
|
||||
- Consider fallback solutions for image loading failures
|
||||
|
||||
### 4. Performance Optimization
|
||||
|
||||
- Avoid overly complex CSS animations
|
||||
- Optimize background image sizes
|
||||
- Use system fonts or web-safe fonts
|
||||
|
||||
---
|
||||
|
||||
## More Information
|
||||
|
||||
For template development questions, feel free to ask in [GitHub Issues](https://github.com/AIDC-AI/Pixelle-Video/issues).
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# Web UI Guide
|
||||
|
||||
Detailed introduction to the Pixelle-Video Web interface features.
|
||||
|
||||
---
|
||||
|
||||
## Interface Layout
|
||||
|
||||
The Web interface uses a three-column layout:
|
||||
|
||||
- **Left Panel**: Content input and audio settings
|
||||
- **Middle Panel**: Voice and visual settings
|
||||
- **Right Panel**: Video generation and preview
|
||||
- **Sidebar**: System configuration and FAQ
|
||||
|
||||
---
|
||||
|
||||
## System Configuration
|
||||
|
||||
First-time use requires configuring LLM and image generation services. See [Configuration Guide](../getting-started/configuration.md).
|
||||
|
||||
---
|
||||
|
||||
## Content Input
|
||||
|
||||
### Generation Mode
|
||||
|
||||
- **AI Generate Content**: Enter a topic, AI creates script automatically
|
||||
- **Fixed Script Content**: Enter complete script directly
|
||||
|
||||
### Fixed Script Split Mode
|
||||
|
||||
When using fixed script mode, you can choose how to split the content:
|
||||
|
||||
- **By Paragraph**: Split by empty lines, each paragraph becomes a scene
|
||||
- **By Line**: Split by line breaks, each line becomes a scene
|
||||
- **By Sentence**: Smart sentence boundary detection, each sentence becomes a scene
|
||||
|
||||
### Background Music
|
||||
|
||||
- Built-in music supported
|
||||
- Custom music files supported
|
||||
|
||||
---
|
||||
|
||||
## Voice Settings
|
||||
|
||||
### TTS Workflow
|
||||
|
||||
- Select TTS workflow
|
||||
- Supports Edge-TTS, Index-TTS, etc.
|
||||
|
||||
### Reference Audio
|
||||
|
||||
- Upload reference audio for voice cloning
|
||||
- Supports MP3/WAV/FLAC formats
|
||||
|
||||
---
|
||||
|
||||
## Visual Settings
|
||||
|
||||
### Image/Video Generation
|
||||
|
||||
- Select media generation workflow (image or video)
|
||||
- Adjust prompt prefix to control style
|
||||
|
||||
### Video Template
|
||||
|
||||
- **Template Preview Gallery**: Visually preview all available templates
|
||||
- Supports portrait (1080x1920) / landscape (1920x1080) / square (1080x1080)
|
||||
- Template types:
|
||||
- `static_*.html`: Static templates (no AI-generated media)
|
||||
- `image_*.html`: Image templates (requires AI-generated images)
|
||||
- `video_*.html`: Video templates (requires AI-generated videos)
|
||||
|
||||
---
|
||||
|
||||
## Generate Video
|
||||
|
||||
After clicking "Generate Video", the system will:
|
||||
|
||||
1. Generate video script
|
||||
2. Generate images/videos for each scene
|
||||
3. Synthesize voice narration
|
||||
4. Compose final video
|
||||
|
||||
Automatically previews when complete.
|
||||
|
||||
---
|
||||
|
||||
## FAQ
|
||||
|
||||
The sidebar includes built-in FAQ for quick reference:
|
||||
|
||||
- Common configuration issues
|
||||
- Generation failure solutions
|
||||
- Performance optimization tips
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# Workflow Customization
|
||||
|
||||
How to customize ComfyUI workflows to achieve specific functionality.
|
||||
|
||||
---
|
||||
|
||||
## Workflow Introduction
|
||||
|
||||
Pixelle-Video is built on the ComfyUI architecture and supports custom workflows.
|
||||
|
||||
---
|
||||
|
||||
## Workflow Types
|
||||
|
||||
### TTS Workflows
|
||||
|
||||
Located in `workflows/selfhost/` or `workflows/runninghub/`
|
||||
|
||||
Used for Text-to-Speech, supporting various TTS engines:
|
||||
- Edge-TTS
|
||||
- Index-TTS (supports voice cloning)
|
||||
- Other ComfyUI-compatible TTS nodes
|
||||
|
||||
### Image Generation Workflows
|
||||
|
||||
Located in `workflows/selfhost/` or `workflows/runninghub/`
|
||||
|
||||
Used for generating static images as video backgrounds:
|
||||
- FLUX series models
|
||||
- Stable Diffusion series models
|
||||
- Other image generation models
|
||||
|
||||
### Video Generation Workflows
|
||||
|
||||
Located in `workflows/selfhost/` or `workflows/runninghub/`
|
||||
|
||||
**New Feature**: Supports AI video generation to create dynamic video content.
|
||||
|
||||
**Preset Workflows**:
|
||||
- `runninghub/video_wan2.1_fusionx.json`: Cloud workflow (recommended)
|
||||
- Based on WAN 2.1 model
|
||||
- No local setup required, accessed via RunningHub API
|
||||
- Supports Text-to-Video generation
|
||||
|
||||
- `selfhost/video_wan2.1_fusionx.json`: Local workflow
|
||||
- Requires local ComfyUI environment
|
||||
- Requires installation of corresponding video generation nodes
|
||||
- Suitable for users with local GPU
|
||||
|
||||
**Use Cases**:
|
||||
- Works with `video_*.html` templates
|
||||
- Automatically generates dynamic video backgrounds based on scripts
|
||||
- Enhances visual expressiveness and viewing experience
|
||||
|
||||
---
|
||||
|
||||
## Custom Workflows
|
||||
|
||||
1. Design your workflow in ComfyUI
|
||||
2. Export as JSON file
|
||||
3. Place in `workflows/` directory
|
||||
4. Select and use in Web interface
|
||||
|
||||
---
|
||||
|
||||
## More Information
|
||||
|
||||
Detailed workflow customization guide coming soon.
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# 🎬 视频示例库 / Video Gallery
|
||||
|
||||
展示使用 Pixelle-Video 制作的各类视频案例,包含完整的制作参数和资源下载。
|
||||
|
||||
Showcase of videos created with Pixelle-Video, including complete production parameters and downloadable resources.
|
||||
|
||||
---
|
||||
|
||||
## 📚 案例列表 / Cases
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-book-open-variant:{ .lg .middle } **阅读习惯养成**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
**时长 Duration**: 45s | **分镜 Scenes**: 5 | **尺寸 Size**: 1080x1920
|
||||
|
||||
一个关于为什么要养成阅读习惯的教育科普视频。
|
||||
|
||||
An educational video about why we should develop reading habits.
|
||||
|
||||
[:octicons-arrow-right-24: 查看详情 View Details](reading-habit/)
|
||||
|
||||
- :material-chart-line:{ .lg .middle } **提高工作效率**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
**时长 Duration**: 30s | **分镜 Scenes**: 3 | **尺寸 Size**: 1920x1080
|
||||
|
||||
关于如何提高工作效率的实用技巧分享。
|
||||
|
||||
Practical tips on improving work efficiency.
|
||||
|
||||
[:octicons-arrow-right-24: 查看详情 View Details](#) *(即将推出 Coming soon)*
|
||||
|
||||
- :material-food-apple:{ .lg .middle } **健康饮食**
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
**时长 Duration**: 60s | **分镜 Scenes**: 6 | **尺寸 Size**: 1080x1080
|
||||
|
||||
健康饮食的重要性和实用建议。
|
||||
|
||||
The importance of healthy eating and practical advice.
|
||||
|
||||
[:octicons-arrow-right-24: 查看详情 View Details](#) *(即将推出 Coming soon)*
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 🎯 如何使用这些案例 / How to Use
|
||||
|
||||
每个案例都包含:/ Each case includes:
|
||||
|
||||
- **📹 成品视频 Video**: OSS 托管的完整视频 / Complete video hosted on OSS
|
||||
- **⚙️ 工作流文件 Workflows**: ComfyUI 工作流 JSON / ComfyUI workflow JSON files
|
||||
- **📝 配置文件 Config**: 完整的生成配置 / Complete generation configuration
|
||||
- **🎨 提示词 Prompts**: 所有使用的提示词 / All prompts used
|
||||
- **📥 一键复现 Reproduce**: 可直接导入使用 / Can be imported directly
|
||||
|
||||
---
|
||||
|
||||
## 💡 贡献你的案例 / Contribute Your Case
|
||||
|
||||
制作了优秀的视频?欢迎分享!/ Created an awesome video? Share it with us!
|
||||
|
||||
查看 [贡献指南](../en/development/contributing.md) 了解如何提交你的案例。
|
||||
|
||||
See [Contributing Guide](../en/development/contributing.md) to learn how to submit your case.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
为什么要养成阅读习惯
|
||||
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 201 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 390 KiB |
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 86 KiB |