motion-reels 是做什麼的?
以程式碼繪製的垂直 Reels 動態圖形。每個 Reel 由一個或多個區塊(block)組成。區塊是一個小型的 JavaScript 檔案,負責在一個「世界」中放置標題、圖畫、照片和材質樣本,並指定每個元素出現的時間。每個時間點來自文字時間軸:可以是節拍表(無語音)或語音錄製的文字時間。引擎在全新的瀏覽器頁面上逐幀渲染,檢查佈局,並與語音和/或音樂合成最終結果。
擁有頁面的人決定外觀、文字內容以及發布內容。Claude 絕不發布任何內容。
設定(一次性)
- Python 3.10+ 以及 ffmpeg/ffprobe 需在 PATH 環境變數中。然後執行儲存庫中的
setup.ps1(Windows)或setup.sh(macOS/Linux),或執行pip install -r requirements.txt+python -m playwright install chromium(或設定環境變數MG_CHROME指向現有 Chrome 瀏覽器)。 - 僅語音:
pip install -r requirements-voice.txt(faster-whisper;首次執行會下載語音模型)。 - 測試:執行
python engine/selftest.py必須顯示SELFTEST OK(檢查通過範例,負面測試如預期失敗,阿拉伯語字幕正確分割,且一次簡短渲染加上故障掃描與合成結果達到 -14 LUFS)。使用--quick可跳過渲染步驟。
引擎檔案(全部在 engine/ 目錄下):render.py(檢查/分鏡/靜態圖片/影片)、beats.py、words.py、frames.py、faultscan.py、assemble.py、storyboard_sheet.py、make_grain.py、templates/(殼層、reel.js、reel.css、字型)。
每個 Reel 一個資料夾
<reel 名稱>/
brief.md 該 Reel 的目的、一個核心想法、行動呼籲、可用的照片
beats.txt 螢幕上顯示的文字,一行一個節拍(或者,當有語音時,使用 script.txt + take.wav)
words.json 時間軸(由 beats.py 或 words.py 產生)
photos/ 業主的照片(僅限他們擁有使用權限的照片)
A.js, B.js ... 區塊
build/ 檢查結果、分鏡靜態圖片、區塊影片、render.json 檔案、連絡表
<Reel>_REV01_<日期>.mp4 最終交付檔案;後續修正檔命名為 _REV02,不會覆蓋
閘門(不得跳過或重新排序;每個 ⛔ 標記處暫停,等待業主確認)
- 簡報。 一個 Reel 只有一個核心想法(房間改造、材質故事、格局變更、小技巧)。索取照片和帳號資訊。說明每張照片的內容。在前 2 秒內抓住觀眾注意力。
- 節拍表(
beats.txt):螢幕上顯示的確切文字,依序排列,一行一個節拍。每個節拍保持簡短(2–7 個字)。將清單中的每個項目放在獨立的一行,讓每個項目都有足夠時間呈現。 ⛔ 業主確認文字。 確認後若需修改文字,意味著需要重新調整區塊的時間軸(以及重新錄製語音,如果有語音的話):在進行任何修改前必須先告知業主。 - 時間軸。
- 無語音:執行
python engine/beats.py beats.txt words.json(預設節奏為每秒 2.6 個字 + 每個節拍後停留 0.7 秒;若在行尾加上[+1.5]則表示更長的停留;[pause 2]表示停留且不顯示新內容)。 - 語音(任何 TTS 或錄音,每個區塊一個完整錄音檔,不得編輯):執行
python engine/words.py take.wav words.json,然後將轉錄文字與腳本逐字比對。畫面配合錄音檔,無論如何不能反過來。
- 無語音:執行
- ⛔ 分鏡靜態圖片。 建立區塊佈局(僅需位置即可),執行
check,然後執行render.py A.js words.json storyboard build/sb和storyboard_sheet.py build/storyboard.png build/sb。自行審查連絡表(檢查清單如下),然後發送給業主。在業主確認佈局之前,不得製作任何動畫。 - 製作動畫。 根據時間軸中命名該元素的詞彙來安排每個元素的出現時機(規則如下)。每次修改後都執行
check。 - 渲染。 每個區塊執行
render.py A.js words.json video build/A.mp4 --workers 4(發送給業主的任何東西絕不使用--draft)。每個區塊的build/A.mp4.render.json必須顯示"capture": "fresh"且與其他區塊使用相同瀏覽器;如果瀏覽器變更,則所有區塊都必須重新渲染。 - 故障掃描。 對每個區塊以及最終的 Reel 執行
faultscan.py build/A.mp4 --sheet build/A_contact.png。檢查所有被標記的時間點。黑色條帶或單幀閃爍都是故障:必須停止並修復。照片內容較暗是正常的。 - 合成。 執行
assemble.py blocks.json <Reel>_REV01_<日期>.mp4 [--music bed.mp3]。它會顯示測量到的響度;必須達到目標值(預設 -14 LUFS),且真實峰值必須低於或等於 -1.5 dBTP。 - ⛔ 審查。 發送 Reel 及其連絡表。僅修正圖片的問題只需重新渲染修改過的區塊,並以 _REV 下一版的名稱交付。業主負責發布。
動畫規則(原因:營造平靜、高級的感受;沒有抖動,易於閱讀)
- 文字永不移動。 文字淡入(0.28 秒)後便保持靜止。文字沒有滑入、縮放、彈跳或漂移效果。
- 標題:
hl(el, text, cue(...))會根據時間軸中對應的詞彙,讓每個詞逐個淡入。當標題是語音的 paraphrasing 時,使用{stream:true}:文字從提示詞開始,從左到右間隔 0.1 秒出現。 - 鏡頭: 當畫面停留時保持完全靜止;僅在切換畫面之間滑動(0.8 秒,ease-in-out)。在 9:16 的比例下,將畫面在「世界」中垂直堆疊(畫面 2 在 y 1920,畫面 3 在 y 3840),然後向下滑動:
SHOTS=[[t,540,960,1],[t2,540,2880,1],...]。 - 照片可以緩慢推近:
push:[1,1.04]= 恆定速度,不得超過 +6%(MOTION.PUSH_MAX;引擎會拒絕)。承載文字的照片不得推近。 - 圖畫在被命名時繪製,先繪製參考線:牆壁在窗戶和門之前,地板線在從它開始測量的高度之前。
at('#walls', cue('Walls'), null, 'draw', 0.9);每個元素需 0.6–1.0 秒。 - 尺寸使用同一比例。 如果平面圖顯示兩個尺寸,兩者使用相同比例。
- 標示文字中提及的項目;每個數字都需帶有單位(如 "3.2 m",而不是裸露的 "3.2")。
- 任何地方都不使用彈跳、超調、彈性或彈簧緩動函數。 所有時間設定來自
reel.js中的MOTION;在那裡修改,絕不能作為區塊中的字面值。 - 提供給
at()的裸虛線 SVG 元素會在提示詞之前顯示:請將其包裹在一個<g>標籤中。
時間設定代幣(reel.js 中的 MOTION)
| 代幣 | 值 | 用途 |
|---|---|---|
| WORD_FADE / WORD_LEAD | 0.28 秒 / 0.08 秒 | 文字淡入;在提示詞之前稍微開始 |
| STREAM | 0.10 秒 | 串流模式標題文字間距 |
| REVEAL / EXIT | 0.5 秒 / 0.4 秒 | 區塊、標籤、材質樣本、照片的進入/離開 |
draw (at(...,'draw',dur)) | 0.6–1.0 秒 | 每個繪製的元素 |
| CAM_SLIDE / CAM_LEAD | 0.8 秒 / 0.35 秒 | 鏡頭滑動;在畫面的提示詞之前開始 |
| PUSH_MAX | 0.06 | 照片推進限制 |
| WIPE | 1.2 秒 | 前後擦除效果 |
| 緩動函數:揭示效果使用 ease-out cubic,滑動、繪製和擦拭效果使用 ease-in-out cubic。 |
佈局與檢查
- 安全區域(
reel.js中的SAFE):文字、標籤和字幕必須保持在 1080x1920 幀的 x 65–1015,y 269–1248 範圍內(頂部 14%,底部 35%,側邊 6%)。這些數字是 Reels 廣泛使用的第三方指導原則,並非 Meta 針對原生貼文發布的規則:請根據已發布 Reel 的手機截圖重新測量,如果應用程式有變,則設定safeZone({...})。照片和圖畫可以超出此範圍,但文字不行。 render.py <block> words.json check build/<block>.check.json在每次渲染前必須執行。它會每隔 0.25 秒取樣一次,並檢測不同元素(文字、圖畫、標籤、材質樣本、照片、字幕)之間是否發生持續 0.5 秒或以上的重疊,以及當鏡頭靜止時文字是否超出安全區域。如果檢查失敗,必須修正佈局,不得渲染。需透過負面測試證明新規則(重新引入一個已知故障;檢查必須失敗)。- 屬於照片的文字需放在照片元素內部(
parent: photoDiv),並在其後加上scrim(),或者將照片設為背景(bg:true)。否則檢查會報告文字疊在照片上,這正是你想要的。 - 檢查無法發現被幀邊緣裁切的內容:這需要你的自行審查。
建立區塊(reel.js)
boot(()=>{ // 每個區塊以 boot(...) 開始
handle('@yourstudio'); // 安全區域頂部的小型帳號識別
$('world').insertAdjacentHTML('beforeend', `<div id="h1" class="abs serif" style="left:90px;top:360px;width:900px;font-size:104px"></div>`);
hl('h1', 'A small room, *re-planned.*', cue('A small')); // *...* = 強調斜體;~word = 不在時間軸中
at('#walls', cue('Walls'), null, 'draw', 0.9); // fx: 'fade' | 'pop' | 'wipe' | 'draw'
photo({src:rel('photos/living.jpg'), x:0, y:1920, w:1080, h:1350, a:cue('Light'), push:[1,1.04]});
beforeAfter({before:rel('photos/before.jpg'), after:rel('photos/after.jpg'), x:90, y:2440, w:900, h:900, a:cue('Before'), t:cue('after')});
swatch({name:'Oak', note:'floor', color:'#B08A62', x:90, y:4480, size:270, a:cue('Oak')}); // 或 src: rel('photos/oak.jpg')
clip({dir:rel('clips/walk'), n:240, x:0, y:5760, w:1080, h:1920, a:cue('Walk'), t0:cue('Walk')}); // 來自 frames.py 的幀
captions(1120, 3); // 從時間軸嵌入字幕(有語音的 Reel)
SHOTS=[[0,540,960,1],[cue('Light'),540,2880,1]];
});
rel(path) 會解析區塊旁邊的檔案。cue('words', after) 若時間軸中沒有這些詞彙則會報錯,因此拼寫錯誤會大聲失敗。完整範例:examples/demo/demo.js。
阿拉伯語及其他從右到左的 Reel
- 在
boot()中首先呼叫rtl()。標題使用 Noto Naskh Arabic,文字使用 IBM Plex Sans Arabic(OFL);拉丁文字則回退到預設字型。阿拉伯語中不使用斜體、字母間距或大寫(字母必須連接);強調詞僅改變顏色。 - 文字框保持
left+width;文字對齊右邊緣。從右到左排列清單(第一個命名的 = 最右邊)。 - 提示詞忽略變音符號、塔特維爾、alef/yaa/taa-marbuta 變體,並轉換阿拉伯-印度數字;無字母的提示詞會報錯。ة 和 ه 視為一個字母,因此請提示一個更長的短語,其中兩個詞僅在此不同。數字詞僅匹配英文:在阿拉伯語中,提示數字或轉錄的詞(
words.py --lang ar選擇多語言模型)。 - 字幕在 ، ; ؟ 和拉丁標點符號處斷行,且不顯示這些符號。
- 範例:
examples/arabic/。
外觀
主題僅存在於 engine/templates/reel.css(CSS 變數顏色、字型)和字型檔案中。預設「溫暖簡約」:紙色 #F4EFE8,墨水色 #2B2622,強調色 #9C6B4E,Cormorant Garamond 標題 + Jost 文字(均為 SIL OFL 1.1)。
若要更改外觀:編輯變數,或將 OFL 字型檔案(附帶授權檔案)添加到 engine/fonts/,並在 fonts.css 和 window.FONTS(reel.js)中列出它們。首先向業主索取 3–5 張其網格截圖並比對。
自行審查(Claude,在發送任何連絡表或渲染結果之前)
- 每個元素在其提示詞處開始;參考線先繪製。
- 文字落地後保持靜止;鏡頭靜止時保持靜止;滑動結束時沒有跳動。
- 沒有任何內容被幀邊緣裁切;所有文字都在安全區域內;沒有重要內容位於底部 35% 以下。
- 尺寸使用同一比例;每個數字都有單位;拼寫與已簽署的節拍表一致。
- 每個區塊的檢查 PASS;
render.json顯示capture: fresh,所有區塊使用相同瀏覽器。 - 故障掃描無誤,或每個標記的時間點都經過檢查和解釋。
assemble.py列印的響度和真實峰值符合目標。
權利與隱私
- 僅使用業主的照片,或他們擁有使用權限的照片。客戶家中:未經同意不得顯示臉部、地址或文件。
- 音樂:僅使用業主擁有授權的曲目,或發布時在應用程式中添加的平台自有音樂庫。
- 不宣稱業主未提出的內容(價格、時間表、獎項)。
疑難排解
cue not found: "...":words.json中沒有按順序找到這些詞彙;修正節拍表或提示詞。ERR reqfail:file:///...jpg:照片或剪輯路徑錯誤;使用rel()並檢查檔名。- 先前繪製的幽靈副本或黑色條帶:你使用了
--draft渲染。刪除--draft重新渲染。 - 渲染緩慢:在具有相應核心數量的機器上使用
--workers 4(最多 8);檢查和分鏡很快,影片渲染則不然。