製作一支帶旁白的影片

一個端到端範例:用 LLM 寫腳本、生成影片片段、合成配音,再把它們合在一起——全程只用一把 API 金鑰。

本教學在同一支腳本裡串起三種模型:語言模型撰寫旁白,影片模型生成畫面,語音模型把旁白唸出來。它很好地說明了「單一 API、單一餘額橫跨各類模型」在實務上的價值。

您需要準備: Python 3.9+、一把 API 金鑰,以及 ffmpeg(若最後想把音訊與影片合流的話)。

本教學會實際提交生成任務並消耗真實額度。影片是最花錢的一環——調整階段請先用較短的時長,若想先算出確切金額,可以參考價格估算

準備工作

pip install requests
export ATLASCLOUD_API_KEY="your-api-key"

步驟 1 —— 共用輔助函式

每個媒體任務都遵循同樣的「提交後輪詢」流程,所以只需要寫一次。

import os, time, requests

API_KEY = os.environ["ATLASCLOUD_API_KEY"]
BASE = "https://api.atlascloud.ai/api/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
TERMINAL = {"completed", "succeeded", "failed", "timeout"}


def submit(endpoint: str, model: str, **params) -> str:
    """提交任務。注意參數是平鋪的,不要包在 input 裡。"""
    r = requests.post(f"{BASE}/model/{endpoint}",
                      headers=HEADERS, json={"model": model, **params}, timeout=60)
    r.raise_for_status()
    return r.json()["data"]["id"]


def wait(prediction_id: str, timeout: int = 900) -> dict:
    """輪詢到終態,間隔逐步放大。"""
    deadline, delay = time.time() + timeout, 2.0
    while time.time() < deadline:
        r = requests.get(f"{BASE}/model/prediction/{prediction_id}",
                         headers=HEADERS, timeout=30)
        r.raise_for_status()
        data = r.json()["data"]
        if data.get("status") in TERMINAL:
            if data["status"] in ("failed", "timeout"):
                raise RuntimeError(f"任務失敗: {data.get('error') or data['status']}")
            return data
        time.sleep(delay)
        delay = min(delay * 1.5, 10.0)
    raise TimeoutError(prediction_id)


def download(url: str, path: str) -> str:
    r = requests.get(url, timeout=300)
    r.raise_for_status()
    with open(path, "wb") as f:
        f.write(r.content)
    return path

步驟 2 —— 用 LLM 撰寫腳本

語言模型使用的是另一個 Base URL,而且是同步的,因此不會經過 submitwait

def write_narration(topic: str) -> str:
    r = requests.post(
        "https://api.atlascloud.ai/v1/chat/completions",
        headers={**HEADERS, "Content-Type": "application/json"},
        json={
            "model": "deepseek-ai/deepseek-v3.2",
            "messages": [
                {"role": "system",
                 "content": "You write narration for short videos. "
                            "Reply with two sentences of spoken narration and nothing else."},
                {"role": "user", "content": f"Topic: {topic}"},
            ],
            "max_tokens": 200,
        },
        timeout=120,
    )
    r.raise_for_status()
    return r.json()["choices"][0]["message"]["content"].strip()


narration = write_narration("how ocean waves shape a coastline")
print(narration)

步驟 3 —— 生成畫面

video_id = submit(
    "generateVideo",
    "alibaba/wan-2.5/text-to-video",
    prompt="slow aerial shot of waves breaking against a rocky coastline at golden hour",
    duration=5,
)
video = wait(video_id)
download(video["outputs"][0], "footage.mp4")

影片生成耗時是以分鐘計,而非秒。在生產環境中,請註冊 Webhook,讓任務完成後回呼您,而不是一直掛著一個輪詢迴圈。

步驟 4 —— 合成配音

步驟 2 產出的旁白在這裡作為輸入。語音、音樂與轉寫共用同一個端點——實際執行哪一種由模型決定。

audio_id = submit(
    "generateAudio",
    "bytedance/seed-audio-1.0",
    text=narration,
    format="mp3",
    sample_rate=44100,
    speech_rate=-5,   # 略慢一點,旁白更好懂
)
audio = wait(audio_id)
download(audio["outputs"][0], "voiceover.mp3")

完整參數(包含音色參考)請參閱音訊模型

步驟 5 —— 合流

ffmpeg -i footage.mp4 -i voiceover.mp3 \
  -c:v copy -c:a aac -shortest narrated.mp4

選用 —— 生成字幕

把配音再送進轉寫模型,就能取得詞層級的時間軸,用來製作字幕。

stt_id = submit(
    "generateAudio",
    "bytedance/seed-asr-2.0",
    audio_url=audio["outputs"][0],   # 注意欄位名稱是 audio_url
    enable_punc=True,
    show_utterances=True,
)
stt = wait(stt_id)

# 轉寫模型的 outputs[0] 是文字本身,不是檔案 URL
print(stt["outputs"][0])
for word in stt.get("stt_result", {}).get("words", [])[:10]:
    print(word["start"], word["end"], word["text"])

轉寫模型接收的是 audio_url,但另一些語音轉文字模型接收的卻是 audio。請查閱該模型的 API 參考——欄位名稱傳錯會直接驗證失敗。

接下來可以做什麼

  • 換成支援參考圖片的影片模型,以您提供的一張靜態畫面來決定整體視覺風格
  • 把輪詢改成 Webhook,讓長時間任務不再阻塞您的程序
  • 平行執行多個片段,再串接成更長的序列
  • 加上錯誤與限流中的重試處理

相關內容

Last updated on

On this page