🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付

實際的發文範例

這個管線自動生成及自動發佈的 X 的發文如下:
https://x.com/aiteacher37681/status/2026187321477931407?s=20

座位安排指南.gif

生成步驟

  1. 使用 Plan 模式請求 Claude Code 製作「使用 Remotion + Playwright 的〇〇功能PR影片」
  2. 確認生成的台本
  3. 若無問題則請求生成PR影片
  4. 確認生成後的品質
  5. 若有疑慮則提出指摘及修正請求
  6. 若無問題則push → GitHub Actions 讀取台本內容,並附上 PR 影片自動發佈到 X。

人類僅負責台本與影片的品質檢查,其他全部自動化。未來若品質可確保,便希望實現完全自動化,讓新增功能時甚至不需要確認台本。

起因

我正在開發一個面向教師的 AI 校務支援服務。

原本是由自己製作 PR 影片,但隨著 AI 開發成為主流,新功能的增加變得容易。雖然可以製作新功能,但手動製作 PR 影片的速度卻拖慢了進度,實際情況是功能存在卻未被用戶使用或未被目標群體認知

因此最近結合了 X 的 PR 自動發文批次,創建了一個附上 PR 影片後自動發文的系統。具體來說,是使用 Playwright 自動錄製瀏覽器操作,並使用 Remotion 合成品牌統一的推廣影片,然後 push 後由 GitHub Actions 自動發文到 X 的管線。

本文將介紹其架構與實現。

總體架構

┌───────────────────────────────────────────────────────────┐
│  1. Playwright 示範測試                                    │
│     tests/e2e/demo/*.spec.ts                              │
│     自動執行瀏覽器操作 → 錄製 .webm 影片                     │
└──────────────────────┬────────────────────────────────────┘
                       │ public/remotion/videos/*.webm
                       ▼
┌───────────────────────────────────────────────────────────┐
│  2. Remotion 合成                                         │
│     remotion/compositions/*.tsx                            │
│     Hook → Problem → Demo(錄製嵌入) → CTA               │
└──────────────────────┬────────────────────────────────────┘
                       │ npm run remotion:render:all
                       ▼
┌───────────────────────────────────────────────────────────┐
│  3. 輸出 MP4                                           │
│     out/*.mp4 (1920x1080, H.264)                          │
│     用於 SNS 發佈及 LP 刊登的推廣影片                      │
└───────────────────────────────────────────────────────────┘

重點在於,Playwright 錄製的操作影片作為素材,Remotion 則將其製作成品牌化的推廣影片,構成一個兩階段的流程。

步驟 1: 使用 Playwright 自動錄製示範操作

專用於示範的 Playwright 設定

與一般的 E2E 測試不同,我們準備了專門的示範錄製設定檔。

// playwright.demo.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests/e2e/demo',
  fullyParallel: false,
  retries: 0,
  workers: 1,
  timeout: 120000,
  projects: [
    {
      name: 'demo',
      use: {
        ...devices['Desktop Chrome'],
        headless: true,
        storageState: 'playwright/.clerk/premium-user.json',
        video: {
          mode: 'on',
          size: { width: 1920, height: 1080 },
        },
      },
    },
  ],
});

video.mode: 'on'size: { width: 1920, height: 1080 } 是重點。在測試執行過程中,瀏覽器畫面自動儲存為1080p的webm影片。

示範測試的編寫

每個功能都有 tests/e2e/demo/ 目錄下的 spec 檔。與一般的 E2E 測試的不同之處在於,模擬 API 回傳理想數據,以及記錄希望展示的操作步驟作為「台本」

// tests/e2e/demo/report-pc.spec.ts(節選)
test('使用 AI 預測、改寫、生成來創建所見', async ({ page }) => {
  // 隱藏 Next.js 錯誤覆蓋層
  await page.addStyleTag({
    content: `
      [data-nextjs-dialog-overlay],
      [data-nextjs-toast],
      nextjs-portal { display: none !important; }
    `
  });

  // API 模擬:返回理想數據
  await page.route('**/api/users/me', async (route) => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({
        id: 1,
        email: '[email protected]',
        is_premium: true,
        subscription_status: 'premium'
      })
    });
  });

  // AI 預測 API 模擬:返回根據輸入的預測文本
  await page.route('**/api/ai/report-prediction', async (route) => {
    const postData = route.request().postDataJSON();
    let prediction = '意欲積極地參與。';
    if (postData.currentText?.includes('國語的音讀發表會中,')) {
      prediction = '場面的情況要很好地傳達,聲音的大小和語調要加以考量...';
    }
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({ prediction })
    });
  });

  // AI 改寫 API 模擬(600 毫秒延遲顯示加載)
  await page.route('**/api/ai/text-rewrite', async (route) => {
    await new Promise(r => setTimeout(r, 600));
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({ rewrittenText: '...' })
    });
  });

  // ========== 示範操作情境 ==========

  // 步驟 1: 轉到所見創建畫面,開始新建
  await page.goto('http://localhost:8080/reports');
  await page.click('button:has-text("新建所見")');
  // ...選擇班級和學期以進行創建

  // 步驟 2: AI 預測轉換(文字輸入 → Tab 確定)
  await textarea.pressSequentially('國語的音讀發表會中,', { delay: 100 });
  await page.waitForTimeout(2000);
  await page.keyboard.press('Tab'); // 用 Tab 確定 AI 預測

  // 步驟 3: AI 改寫 → 預覽 → 確定
  await page.locator('button:has-text("AI 改寫")').click();
  await page.waitForTimeout(1500);
  await page.locator('button:has-text("確定這個內容")').first().click();

  // 步驟 4: 切換到道德標籤
  await page.locator('[role="tab"]:has-text("道德")').click();

  // 步驟 5: AI 生成 → 輸入提示 → 確定
  await page.locator('button:has-text("AI 生成")').click();
  await promptInput.pressSequentially('有同情心並幫助朋友', { delay: 80 });
  await page.keyboard.press('Enter');

  // 步驟 6: 保存
  await page.locator('button:has-text("更新所見")').click();

  // 將影片保存到指定路徑
  await page.close();
  await page.video()?.saveAs('public/remotion/videos/report-editor.webm');
});

示範測試中的巧思

巧思 原因
使用 pressSequentially 逐字輸入 讓打字的過程看起來更真實
使用 mouse.move 平滑移動滑鼠游標 讓滑鼠的動作顯得更自然
API 模擬中故意設置延遲(600-800 毫秒) 自然表現 AI 處理的加載狀態
隱藏 Next.js 錯誤覆蓋層 避免開發伺服器的覆蓋層干擾畫面
捕捉所有 API 模擬 防止未定義的 API 請求導致畫面崩潰
使用 page.video()?.saveAs() 將錄製直接保存到 Remotion 的素材路徑

步驟 2: 使用 Remotion 完成推廣影片

將 Playwright 錄製的原始瀏覽器操作影片轉換為 Remotion 製作的品牌推廣影片。

影片構成

每部影片由統一的四個部分構成。

┌─────────────────────────────────────────────────┐
│  Hook (0-2秒)                                     │
│  標誌 + 標語(附帶 Spring 動畫)                    │
│  例: 「讓校務更聰明,使用 AI」                      │
├─────────────────────────────────────────────────┤
│  Problem (2-4秒)                                  │
│  問題提示(淡入動畫)                             │
│  例: 「每天忙於事務工作...」                       │
├─────────────────────────────────────────────────┤
│  Solution Demo (4-12秒)                           │
│  ★ 播放录制的操作影片 ★                           │
│  可以調整 playbackRate 播放速度                  │
├─────────────────────────────────────────────────┤
│  CTA (12-15秒)                                    │
│  「立即開始免費使用」+ 按鈕風格 UI + URL          │
└─────────────────────────────────────────────────┘

合成的實現範例

// remotion/compositions/HomeDashboard.tsx
import { AbsoluteFill, staticFile } from 'remotion';
import { HookScene } from '../components/HookScene';
import { CtaScene } from '../components/CtaScene';
import { SceneContainer } from '../components/SceneContainer';
import { AnimatedVideo } from '../components/AnimatedVideo';
import { GradientBackground } from '../components/GradientBackground';
import { theme } from '../styles/theme';

const VIDEO_DASHBOARD = staticFile('remotion/videos/home-dashboard.webm');

export const HomeDashboard: React.FC = () => {
  return (
    <AbsoluteFill>
      {/* Hook: 0-2秒 - 標誌和標語 */}
      <SceneContainer startFrame={0} endFrame={60}>
        <HookScene
          title="讓校務更聰明,使用 AI"
          gradient={theme.gradients.primary}
        />
      </SceneContainer>

      {/* Problem: 2-4秒 - 問題提示 */}
      <SceneContainer startFrame={60} endFrame={120}>
        <GradientBackground colors={['#f9fafb', '#eff6ff']}>
          <TextOverlay
            text="每天忙於事務工作..."
            fontSize={56}
            animation="fadeUp"
            startFrame={60}
          />
        </GradientBackground>
      </SceneContainer>

      {/* Solution Demo: 4-12秒 - 播放 Playwright 錄製 */}
      <SceneContainer startFrame={120} endFrame={360}>
        <GradientBackground colors={['#ffffff', '#f0f4ff']}>
          <AnimatedVideo
            src={VIDEO_DASHBOARD}
            startFrame={120}
            playbackRate={1.2}  // 稍微快一點
          />
        </GradientBackground>
      </SceneContainer>

      {/* CTA: 12-15秒 */}
      <SceneContainer startFrame={360} endFrame={450}>
        <CtaScene
          text="立即開始免費使用"
          gradient={theme.gradients.primary}
        />
      </SceneContainer>
    </AbsoluteFill>
  );
};

可變速度播放(ConferenceEditor 的例子)

在個人面談的示範影片中,依場景調整播放速度,希望慢慢展示的部分放慢速度,等候時間則快轉。

// remotion/compositions/ConferenceEditor.tsx
const VIDEO_SEGMENTS: VideoSegment[] = [
  { duration: 25, speed: 0.8 },   // 顯示表單(慢)
  { duration: 25, speed: 1.0 },   // 下拉選單操作
  { duration: 25, speed: 1.2 },   // 學生加載
  { duration: 100, speed: 1.0 },  // 輸入提示
  { duration: 50, speed: 1.5 },   // 生成按鈕
  { duration: 130, speed: 3.0 },  // AI 生成處理(快轉)
  { duration: 13, speed: 1.0 },   // 顯示結果
  { duration: 25, speed: 2.0 },   // 側邊欄操作(加快)
  { duration: 33, speed: 0.8 },   // textarea 輸入(慢)
  { duration: 83, speed: 1.5 },   // 保存+提醒
];

結合 Remotion 的 SequenceOffthreadVideo,以不同的 playbackRate 應用於每個段落。AI 處理中使用三倍速極速播放,您希望展示的輸入場景則以0.8倍速緩慢播放,這樣的控制得以實現。

可重複使用的組件群

remotion/components/
├── AnimatedVideo.tsx       # 影片嵌入(附加 Spring 動畫淡入)
├── HookScene.tsx           # Hook 畫面(標誌+標題+粒子)
├── CtaScene.tsx            # CTA 畫面(文字+按鈕+URL)
├── SceneContainer.tsx      # 場景區間管理
├── GradientBackground.tsx  # 漸層背景
├── TextOverlay.tsx         # 文本覆蓋
├── TransitionWipe.tsx      # 擺動轉場
├── SparkleEffect.tsx       # 閃爍效果
├── CursorAnimation.tsx     # 游標動畫
├── Logo.tsx                # 顯示標誌
└── AnimatedScreenshot.tsx  # 螢幕截圖動畫

透過這些組件的結合,當製作新功能的示範影片時,只需新增一個組合檔即可。

主題與動畫

品牌顏色與 Spring 動畫的設定集中管理。

// remotion/styles/theme.ts
export const theme = {
  gradients: {
    primary: ['#3b82f6', '#2563eb', '#1d4ed8'],
    hero: ['#eff6ff', '#ffffff', '#ede9fe'],
    dark: ['#1e3a8a', '#1e40af', '#2563eb'],
    ai: ['#8b5cf6', '#3b82f6', '#06b6d4'],
  },
  video: {
    width: 1920,
    height: 1080,
    fps: 30,
    durationInFrames: 450, // 15秒
  },
} as const;

// remotion/styles/animations.ts
export const SPRING_CONFIGS = {
  bouncy: { damping: 12, stiffness: 200, mass: 0.5 },
  smooth: { damping: 20, stiffness: 120, mass: 0.8 },
  gentle: { damping: 30, stiffness: 80, mass: 1 },
  snappy: { damping: 15, stiffness: 300, mass: 0.3, overshootClamping: true },
};

步驟 3: 批次渲染

有一個腳本可一次性將全部9個合成渲染為 MP4。

// remotion/scripts/render-all.ts
import { bundle } from '@remotion/bundler';
import { renderMedia, selectComposition } from '@remotion/renderer';

const COMPOSITIONS = [
  { id: 'HomeDashboard', output: 'home-dashboard.mp4' },
  { id: 'ClassManagement', output: 'class-management.mp4' },
  { id: 'SeatArrangement', output: 'seat-arrangement.mp4' },
  { id: 'ReportEditor', output: 'report-editor.mp4' },
  { id: 'NewsletterEditor', output: 'newsletter-editor.mp4' },
  { id: 'PeGrouping', output: 'pe-grouping.mp4' },
  { id: 'ClassDivision', output: 'class-division.mp4' },
  { id: 'ConferenceEditor', output: 'conference-editor.mp4' },
  { id: 'TextOnlyNewsletter', output: 'text-only-newsletter.mp4' },
];

async function main() {
  // 將 Remotion 專案打包
  const bundled = await bundle({ entryPoint });

  for (const comp of COMPOSITIONS) {
    const composition = await selectComposition({
      serveUrl: bundled,
      id: comp.id,
    });
    await renderMedia({
      composition,
      serveUrl: bundled,
      codec: 'h264',
      outputLocation: `out/${comp.output}`,
    });
  }
}

執行也非常簡單。

# 錄製示範影片(Playwright)
npm run test:demo

# 保存錄製素材
bash scripts/preserve-demo-videos.sh

# 渲染推廣影片(Remotion)
npm run remotion:render:all

# 也可以單獨渲染
npm run remotion:render:report
npm run remotion:render:newsletter

與 X(Twitter)自動發文的連結

整合與另一個儲存庫(auto-post 儲存庫)中構建的X 的自動發文批次。當 push 後,GitHub Actions 會讀取台本內容,並附上 PR 影片自動發文到 X。

┌─────────────┐      ┌──────────────┐      ┌──────────────┐      ┌─────────────┐
│  push 進行  │ ──→ │ GitHub Actions │ ──→ │ 讀取台本的內容  │ ──→ │ 附帶 PR 影片的  │
│              │      │ 觸發作業      │      │                │      │ X 自動發文    │
└─────────────┘      └──────────────┘      └──────────────┘      └─────────────┘

人類只需確認品質並 push,便可完成從創建發文到媒體附加的全自動流程。

實際的成果

遺憾的是,X 的發文的觀看數自體並未有太大的變化。
然而,從發文中附上的連結直接流入網站的人數增加了約1.8~2倍
儘管附加影片的發文並未必直接提升觀看數,卻有效提高了感興趣的人實際造訪服務的機率

總結

對於在個人開發中感到製作示範影片麻煩的人,建議嘗試 Playwright + Remotion 的組合。一旦建立管線,只需請 AI 製作 PR 影片後自動發文到 X的體驗相當舒適。
撰寫測試的同時獲得示範素材,這樣的雙重效果亦非常值得珍惜。
希望大家可以嘗試一下✨

PS:
感謝您閱讀到最後🙇‍♂️
如果您覺得「這篇文章不錯」,也希望能順便了解一下我們的服務網站😆(反向連結能提升SEO,對我們會有幫助!)


原文出處:https://qiita.com/akachiryo/items/2777af8e9912d6bbbbb3


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝20  
565
🥈
我愛JS
💬2  
7
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付