xiaohongshu
xiaohongshu
manim-video-creator
Manim(Mathematical Animation Engine)を使用してアニメーション動画を作成します。このスキルは、(1) 解説動画やビジュアライゼーションの作成、(2) プレゼンテーション動画、(3) ロゴアニメーション、(4) インフォグラフィック、(5) 教育コンテンツ、(6) 3Dアニメーション、(7) アルゴリズムやデータ構造の可視化に使用します。2D/3Dシーン、LaTeX数式、グラフ、TTSナレーション、BGMなどをサポートします。
Full skill instructions
Manim Community ライブラリを使用して、TTSナレーション・BGM付きのアニメーション動画を作成します。
重要: 動画作成を開始する前に、必ず AskUserQuestion ツールを使用して以下の情報をヒアリングしてください。
AskUserQuestionで以下を確認:
1. 動画の種類
- 解説・教育動画(論文解説、チュートリアル等)
- プレゼンテーション動画
- ロゴアニメーション
- インフォグラフィック・データ可視化
- アルゴリズム・コード可視化
- その他
2. 作成範囲
- Manim動画のみ(音声なし)
- Manim動画 + 台本
- フル版(Manim + TTSナレーション + BGM)
3. ナレーション音声(フル版の場合)
- 日本語女性(ja-JP-NanamiNeural)- 推奨
- 日本語男性(ja-JP-KeitaNeural)
- 英語女性(en-US-JennyNeural)
- 英語男性(en-US-GuyNeural)
4. BGMの種類(フル版の場合)
- 自動生成(アンビエント)- 著作権フリー
- BGMなし
- 外部BGMを後から追加
5. プラットフォーム/アスペクト比
- YouTube(16:9, 1920x1080)- 推奨
- YouTube Shorts/TikTok(9:16, 1080x1920)
- Instagram投稿(1:1, 1080x1080)
- カスタム
動画とナレーションのずれを防ぐため、ナレーション台本を先に作成し、その長さに基づいて動画のタイミングを設計します。
# ステップ1: ナレーション台本を作成し、各セグメントの長さを測定
NARRATIONS = [
"最初のナレーション。", # 測定結果: 3.5秒
"2番目のナレーション。", # 測定結果: 4.2秒
]
# ステップ2: タイミング構成を設計
"""
タイミング構成:
- セクション1: 0.0 - 4.0秒(ナレーション1 + 余白)
- セクション2: 4.0 - 9.0秒(ナレーション2 + 余白)
"""
# ステップ3: シーンに反映
class MyScene(Scene):
"""
タイミング構成(ナレーション同期版):
- セクション1: 0.0 - 4.0秒
- セクション2: 4.0 - 9.0秒
"""
def construct(self):
self.section1() # 4秒
self.section2() # 5秒
def section1(self):
"""セクション1: 0.0 - 4.0秒
ナレーション (0.5秒開始, 3.5秒): 最初のナレーション。
"""
# 0.0-1.5秒: タイトル表示
self.play(Write(title), run_time=1.5)
# 1.5-4.0秒: 待機(ナレーション終了を待つ)
self.wait(2.5)
# 累計: 4.0秒
# 基本式
待機時間 = ナレーション終了時間 - 現在の累計アニメーション時間
# 例: ナレーションが8.5秒で終了、現在のアニメーションが6秒まで進んでいる場合
self.wait(8.5 - 6.0) # = 2.5秒待機
各セクションに以下の情報をコメントで明示してください:
def show_section(self):
"""セクション名: 開始時間 - 終了時間(所要時間)
ナレーション1 (開始秒, 長さ): テキスト...
ナレーション2 (開始秒, 長さ): テキスト...
"""
# タイムスタンプコメント
# 0.0-1.0秒: アニメーション説明
self.play(...)
# 1.0-3.0秒: 待機
self.wait(2)
# 累計: 3.0秒
# uvでプロジェクトを作成
uv init --python 3.12 my-animation
cd my-animation
uv add manim
# 音声処理用(フル版)
uv add edge-tts pydub
# システム依存パッケージのインストール
# macOS
brew install pkg-config cairo pango ffmpeg
brew install --cask mactex # LaTeXサポート用
# Linux (Ubuntu/Debian)
# sudo apt-get install libcairo2-dev libpango1.0-dev ffmpeg texlive-full
# Windows
# 1. MiKTeX をインストール: https://miktex.org/download
# 2. FFmpeg をインストール: https://ffmpeg.org/download.html
# 3. パスを環境変数に追加
# インストール確認
uv run manim checkhealth
from manim import *
# 日本語フォント設定
config.font = "Hiragino Sans" # macOS
# config.font = "Noto Sans CJK JP" # Linux
# config.font = "Yu Gothic" # Windows
# ダークモード背景(推奨)
config.background_color = "#1a1a2e"
# カラーパレット
PRIMARY = "#4fc3f7"
SECONDARY = "#81c784"
ACCENT = "#ffb74d"
HIGHLIGHT = "#f06292"
class MyScene(Scene):
def construct(self):
title = Text("タイトル", font_size=48, color=PRIMARY)
self.play(Write(title))
self.wait(2)
# 低品質プレビュー(高速)- 開発・タイミング確認用
uv run manim -ql scene.py MyScene --disable_caching
# 高品質 - 最終出力用
uv run manim -qh scene.py MyScene --disable_caching
# 4K品質
uv run manim -qk scene.py MyScene
class ExplainerScene(Scene):
"""
タイミング構成:
- タイトル: 0-8秒
- セクション1: 8-25秒
- セクション2: 25-45秒
- まとめ: 45-55秒
- エンディング: 55-65秒
"""
def construct(self):
self.show_title()
self.show_section1()
self.show_section2()
self.show_summary()
self.show_ending()
def show_title(self):
"""タイトル: 0-8秒
ナレーション (0.5秒, 7秒): タイトルの説明...
"""
title = Text("タイトル", font_size=72, color=PRIMARY, weight=BOLD)
subtitle = Text("サブタイトル", font_size=32, color=WHITE)
subtitle.next_to(title, DOWN, buff=0.5)
# 0.0-1.5秒: タイトル
self.play(Write(title), run_time=1.5)
# 1.5-2.5秒: サブタイトル
self.play(FadeIn(subtitle), run_time=1)
# 2.5-7.0秒: 待機
self.wait(4.5)
# 7.0-8.0秒: トランジション
self.play(FadeOut(title), FadeOut(subtitle), run_time=1)
def show_section1(self):
"""セクション1: 8-25秒"""
section_title = Text("セクション1", font_size=42, color=ACCENT)
section_title.to_edge(UP, buff=0.5)
self.play(Write(section_title), run_time=1)
# ... セクションの内容
self.play(*[FadeOut(mob) for mob in self.mobjects], run_time=1)
def show_summary(self):
"""まとめセクション"""
title = Text("まとめ", font_size=42, color=ACCENT)
title.to_edge(UP, buff=0.5)
points = VGroup(
Text("• ポイント1", font_size=26),
Text("• ポイント2", font_size=26),
Text("• ポイント3", font_size=26),
).arrange(DOWN, aligned_edge=LEFT, buff=0.5)
points.next_to(title, DOWN, buff=0.8)
points.shift(LEFT * 2)
self.play(Write(title))
for point in points:
self.play(FadeIn(point, shift=RIGHT * 0.3), run_time=0.8)
self.wait(1.5)
def show_ending(self):
"""エンディング"""
self.play(*[FadeOut(mob) for mob in self.mobjects])
thanks = Text("ご視聴ありがとうございました", font_size=32, color=GRAY)
self.play(Write(thanks))
self.wait(3)
class PresentationScene(Scene):
"""スライド形式のプレゼン動画"""
def construct(self):
self.slide_title("プレゼンタイトル", "発表者名")
self.slide_bullets("概要", ["ポイント1", "ポイント2", "ポイント3"])
self.slide_diagram()
def slide_title(self, title, author):
t = Text(title, font_size=56, color=PRIMARY)
a = Text(author, font_size=28, color=GRAY)
a.next_to(t, DOWN, buff=0.5)
self.play(Write(t), FadeIn(a))
self.wait(2)
self.play(FadeOut(t), FadeOut(a))
def slide_bullets(self, title, bullets):
t = Text(title, font_size=42, color=ACCENT).to_edge(UP)
items = VGroup(*[
Text(f"• {b}", font_size=28) for b in bullets
]).arrange(DOWN, aligned_edge=LEFT, buff=0.5)
items.next_to(t, DOWN, buff=0.8).shift(LEFT * 2)
self.play(Write(t))
for item in items:
self.play(FadeIn(item, shift=RIGHT * 0.5))
self.wait(1)
self.wait(1)
self.play(*[FadeOut(mob) for mob in self.mobjects])
class LogoAnimation(Scene):
def construct(self):
circle = Circle(radius=1.5, color=BLUE, fill_opacity=0.8)
text = Text("LOGO", font_size=48, color=WHITE)
self.play(GrowFromCenter(circle), run_time=1)
self.play(Write(text), run_time=0.8)
self.play(
circle.animate.scale(1.1),
text.animate.scale(1.1),
rate_func=there_and_back,
run_time=0.5
)
self.wait(1)
class CycleFlowScene(Scene):
"""サイクル図(Thought→Action→Observation等)"""
def construct(self):
# ボックス作成
box1 = RoundedRectangle(width=3, height=1.2, corner_radius=0.15,
fill_color=PRIMARY, fill_opacity=0.3,
stroke_color=PRIMARY, stroke_width=2)
box1.shift(UP * 1.5)
label1 = Text("ステップ1", font_size=22, color=PRIMARY)
label1.move_to(box1.get_center())
box2 = RoundedRectangle(width=3, height=1.2, corner_radius=0.15,
fill_color=SECONDARY, fill_opacity=0.3,
stroke_color=SECONDARY, stroke_width=2)
box2.shift(RIGHT * 3 + DOWN * 0.8)
label2 = Text("ステップ2", font_size=22, color=SECONDARY)
label2.move_to(box2.get_center())
box3 = RoundedRectangle(width=3, height=1.2, corner_radius=0.15,
fill_color=ACCENT, fill_opacity=0.3,
stroke_color=ACCENT, stroke_width=2)
box3.shift(LEFT * 3 + DOWN * 0.8)
label3 = Text("ステップ3", font_size=22, color=ACCENT)
label3.move_to(box3.get_center())
# 矢印
arrow1 = Arrow(box1.get_right() + DOWN * 0.2, box2.get_top(), color=WHITE, buff=0.1)
arrow2 = Arrow(box2.get_left(), box3.get_right(), color=WHITE, buff=0.1)
arrow3 = Arrow(box3.get_top() + RIGHT * 0.3, box1.get_left() + DOWN * 0.2, color=WHITE, buff=0.1)
# 順番にアニメーション
self.play(Create(box1), Write(label1), run_time=1)
self.play(Create(arrow1), run_time=0.5)
self.play(Create(box2), Write(label2), run_time=1)
self.play(Create(arrow2), run_time=0.5)
self.play(Create(box3), Write(label3), run_time=1)
self.play(Create(arrow3), run_time=0.5)
self.wait(2)
| 言語 | 音声ID | 性別 | 特徴 |
|---|---|---|---|
| 日本語 | ja-JP-NanamiNeural | 女性 | 明瞭で聞きやすい(推奨) |
| 日本語 | ja-JP-KeitaNeural | 男性 | 落ち着いた声 |
| 英語 | en-US-JennyNeural | 女性 | ナチュラル |
| 英語 | en-US-GuyNeural | 男性 | プロフェッショナル |
| 英語 | en-US-AriaNeural | 女性 | エネルギッシュ |
| 中国語 | zh-CN-XiaoxiaoNeural | 女性 | 標準的 |
| 韓国語 | ko-KR-SunHiNeural | 女性 | 標準的 |
# measure_audio.py
import asyncio
import edge_tts
from pydub import AudioSegment
import os
VOICE = "ja-JP-NanamiNeural" # または選択された音声
NARRATIONS = [
"最初のナレーション。",
"2番目のナレーション。",
]
async def measure_duration(text: str, index: int) -> float:
temp_path = f"temp_{index}.mp3"
communicate = edge_tts.Communicate(text, VOICE, rate="+0%")
await communicate.save(temp_path)
audio = AudioSegment.from_mp3(temp_path)
duration = len(audio) / 1000.0
os.remove(temp_path)
return duration
async def main():
print("ナレーション音声長さ測定:")
print("=" * 50)
total = 0
for i, text in enumerate(NARRATIONS):
duration = await measure_duration(text, i)
total += duration
print(f"{i+1}. [{duration:.2f}秒] {text[:30]}...")
print("=" * 50)
print(f"合計: {total:.2f}秒")
asyncio.run(main())
# generate_audio.py
import asyncio
import edge_tts
from pydub import AudioSegment
import os
VOICE = "ja-JP-NanamiNeural"
# (開始秒, テキスト)
NARRATIONS = [
(0.5, "最初のナレーション。"),
(8.5, "2番目のナレーション。"),
(16.0, "3番目のナレーション。"),
]
async def generate_audio_segment(text: str, output_path: str):
communicate = edge_tts.Communicate(text, VOICE, rate="+0%")
await communicate.save(output_path)
async def main():
audio_dir = "audio_segments"
os.makedirs(audio_dir, exist_ok=True)
# 動画の総時間を指定
video_duration_ms = 120 * 1000
final_audio = AudioSegment.silent(duration=video_duration_ms)
print("ナレーション生成中...")
for i, (start_time, text) in enumerate(NARRATIONS):
segment_path = f"{audio_dir}/segment_{i:02d}.mp3"
print(f" {i+1}/{len(NARRATIONS)}: [{start_time:.1f}秒] {text[:30]}...")
await generate_audio_segment(text, segment_path)
segment = AudioSegment.from_mp3(segment_path)
start_ms = int(start_time * 1000)
final_audio = final_audio.overlay(segment, position=start_ms)
final_audio.export("narration.mp3", format="mp3")
print("完成: narration.mp3")
# クリーンアップ
for i in range(len(NARRATIONS)):
os.remove(f"{audio_dir}/segment_{i:02d}.mp3")
os.rmdir(audio_dir)
asyncio.run(main())
外部ダウンロード不要で、pydubのみでアンビエントBGMを生成できます。
# generate_bgm.py
import math
import struct
import wave
import os
from pydub import AudioSegment
def generate_ambient_chord(frequencies, duration_ms, sample_rate=44100, amplitude=0.15):
"""複数の周波数を合成してアンビエントなコードを生成"""
n_samples = int(sample_rate * duration_ms / 1000)
samples = []
for i in range(n_samples):
t = i / sample_rate
value = 0
for freq in frequencies:
phase_mod = 0.002 * math.sin(2 * math.pi * 0.1 * t)
value += amplitude * math.sin(2 * math.pi * freq * t * (1 + phase_mod))
samples.append(value / len(frequencies))
return samples
def apply_envelope(samples, attack_ms, decay_ms, sustain_level, release_ms, sample_rate=44100):
"""ADSRエンベロープを適用"""
n_samples = len(samples)
attack_samples = int(sample_rate * attack_ms / 1000)
decay_samples = int(sample_rate * decay_ms / 1000)
release_samples = int(sample_rate * release_ms / 1000)
result = []
for i, sample in enumerate(samples):
if i < attack_samples:
envelope = i / attack_samples
elif i < attack_samples + decay_samples:
decay_progress = (i - attack_samples) / decay_samples
envelope = 1.0 - (1.0 - sustain_level) * decay_progress
elif i > n_samples - release_samples:
release_progress = (i - (n_samples - release_samples)) / release_samples
envelope = sustain_level * (1.0 - release_progress)
else:
envelope = sustain_level
result.append(sample * envelope)
return result
def samples_to_wav(samples, filename, sample_rate=44100):
"""サンプルをWAVファイルに書き出し"""
with wave.open(filename, 'w') as wav_file:
wav_file.setnchannels(1)
wav_file.setsampwidth(2)
wav_file.setframerate(sample_rate)
for sample in samples:
sample = max(-1.0, min(1.0, sample))
packed = struct.pack('h', int(sample * 32767))
wav_file.writeframes(packed)
def generate_ambient_bgm(duration_seconds=130, output_path="bgm.mp3"):
"""アンビエントBGMを生成"""
print("アンビエントBGMを生成中...")
sample_rate = 44100
duration_ms = duration_seconds * 1000
# Cメジャー系コード進行
chord_progressions = [
[130.81, 164.81, 196.00], # C E G
[146.83, 174.61, 220.00], # D F A
[164.81, 196.00, 246.94], # E G B
[130.81, 164.81, 196.00], # C E G
]
chord_duration_ms = 8000
all_samples = []
for i in range(int(duration_ms / chord_duration_ms) + 1):
chord = chord_progressions[i % len(chord_progressions)]
samples = generate_ambient_chord(chord, chord_duration_ms, sample_rate, amplitude=0.12)
samples = apply_envelope(samples, 2000, 1000, 0.7, 2000, sample_rate)
all_samples.extend(samples)
all_samples = all_samples[:int(sample_rate * duration_seconds)]
# ベースドローン追加
print(" ベースドローンを追加中...")
drone_freq = 65.41 # C2
for i in range(len(all_samples)):
t = i / sample_rate
drone = 0.08 * math.sin(2 * math.pi * drone_freq * t)
drone += 0.04 * math.sin(2 * math.pi * drone_freq * 1.5 * t)
all_samples[i] += drone
# フェードイン・フェードアウト
print(" フェード処理中...")
fade_in_samples = int(sample_rate * 3)
fade_out_samples = int(sample_rate * 5)
for i in range(fade_in_samples):
all_samples[i] *= i / fade_in_samples
for i in range(fade_out_samples):
idx = len(all_samples) - fade_out_samples + i
all_samples[idx] *= (fade_out_samples - i) / fade_out_samples
# WAVに書き出し
temp_wav = "temp_bgm.wav"
samples_to_wav(all_samples, temp_wav, sample_rate)
# MP3に変換
audio = AudioSegment.from_wav(temp_wav)
audio.export(output_path, format="mp3", bitrate="128k")
os.remove(temp_wav)
print(f"BGM生成完了: {output_path}")
if __name__ == "__main__":
generate_ambient_bgm(130, "bgm.mp3")
# combine_final.py
from pydub import AudioSegment
import subprocess
import os
def combine_audio_and_video():
"""ナレーションとBGMを合成し、動画と結合"""
print("音声を処理中...")
narration = AudioSegment.from_mp3("narration.mp3")
bgm = AudioSegment.from_mp3("bgm.mp3")
# BGMをナレーションの長さに合わせる
if len(bgm) < len(narration):
while len(bgm) < len(narration):
bgm = bgm + bgm
bgm = bgm[:len(narration)]
# BGM音量調整(-18dB推奨)
bgm = bgm - 18
# フェードイン・フェードアウト
bgm = bgm.fade_in(3000).fade_out(4000)
# 合成
combined = narration.overlay(bgm)
combined.export("combined_audio.mp3", format="mp3", bitrate="192k")
# 動画と合成
subprocess.run([
"ffmpeg", "-i", "media/videos/scene/1080p60/MyScene.mp4",
"-i", "combined_audio.mp3",
"-c:v", "copy", "-c:a", "aac", "-b:a", "192k",
"-map", "0:v:0", "-map", "1:a:0",
"-shortest", "-y", "final_output.mp4"
])
os.remove("combined_audio.mp3")
print("完成: final_output.mp4")
if __name__ == "__main__":
combine_audio_and_video()
重要: エンディング動画は自動検索されます。ユーザーにパスを聞く必要はありません。
エンディング動画は以下の優先順位で自動検索されます:
| 優先度 | 場所 | パス |
|---|---|---|
| 1 | プロジェクトディレクトリ | ./endings/{aspect_dir}/ending.mp4 |
| 2 | プラグインディレクトリ | ${CLAUDE_PLUGIN_ROOT}/endings/{aspect_dir}/ending.mp4 |
※ {aspect_dir} は動画のアスペクト比に応じて 16_9、9_16、1_1 のいずれか
プロジェクト固有のエンディング動画がある場合:
./ # 現在のmanimプロジェクトディレクトリ
└── endings/
├── 16_9/
│ └── ending.mp4
├── 9_16/
│ └── ending.mp4
└── 1_1/
└── ending.mp4
プロジェクトにエンディング動画がない場合、共通のエンディング動画を使用:
${CLAUDE_PLUGIN_ROOT}/
└── endings/
├── 16_9/ # YouTube用(1920x1080)
│ └── ending.mp4
├── 9_16/ # Shorts/TikTok用(1080x1920)
│ └── ending.mp4
└── 1_1/ # Instagram用(1080x1080)
└── ending.mp4
CLAUDE_PLUGIN_ROOT はClaude Codeによって自動的に設定される環境変数で、プラグインのルートディレクトリを指します。
# 環境変数の確認
echo $CLAUDE_PLUGIN_ROOT
# 例: ~/.claude/plugins/marketplaces/manim-video-creator/plugins/manim-video-creator
注意:
./endings/ に配置${CLAUDE_PLUGIN_ROOT}/endings/ に配置# concat_ending.py
import subprocess
import os
import sys
def get_video_dimensions(video_path):
"""動画の幅と高さを取得"""
result = subprocess.run([
"ffprobe", "-v", "error",
"-select_streams", "v:0",
"-show_entries", "stream=width,height",
"-of", "csv=p=0",
video_path
], capture_output=True, text=True)
width, height = map(int, result.stdout.strip().split(','))
return width, height
def get_aspect_ratio_dir(width, height):
"""アスペクト比に基づいてディレクトリ名を返す"""
if width > height:
return "16_9"
elif width < height:
return "9_16"
else:
return "1_1"
def find_ending_video(aspect_dir, plugin_root=None):
"""エンディング動画を検索(プロジェクト優先、プラグインフォールバック)
Args:
aspect_dir: アスペクト比ディレクトリ名(16_9, 9_16, 1_1)
plugin_root: プラグインのルートディレクトリ
Returns:
エンディング動画のパス、見つからない場合はNone
"""
# 1. プロジェクトディレクトリを優先
project_ending = os.path.join(".", "endings", aspect_dir, "ending.mp4")
if os.path.exists(project_ending):
print(f"プロジェクトのエンディング動画を使用: {project_ending}")
return project_ending
# 2. プラグインディレクトリをフォールバック
if plugin_root is None:
plugin_root = os.environ.get("CLAUDE_PLUGIN_ROOT", ".")
plugin_ending = os.path.join(plugin_root, "endings", aspect_dir, "ending.mp4")
if os.path.exists(plugin_ending):
print(f"プラグインのエンディング動画を使用: {plugin_ending}")
return plugin_ending
# どちらにも見つからない
print(f"警告: エンディング動画が見つかりませんでした")
print(f" - プロジェクト: {project_ending}")
print(f" - プラグイン: {plugin_ending}")
return None
def concat_with_ending(main_video, plugin_root=None):
"""メイン動画とエンディング動画を結合
Args:
main_video: メイン動画のパス
plugin_root: プラグインのルートディレクトリ(指定しない場合は環境変数から取得)
"""
width, height = get_video_dimensions(main_video)
aspect_dir = get_aspect_ratio_dir(width, height)
# エンディング動画を検索(プロジェクト優先)
ending_path = find_ending_video(aspect_dir, plugin_root)
if ending_path is None:
print("エンディング動画なしで続行します")
return main_video
# 結合リストを作成
with open("concat_list.txt", "w") as f:
f.write(f"file '{os.path.abspath(main_video)}'\n")
f.write(f"file '{os.path.abspath(ending_path)}'\n")
output_path = main_video.replace(".mp4", "_with_ending.mp4")
# 動画を結合(同じコーデックの場合は高速)
subprocess.run([
"ffmpeg", "-f", "concat", "-safe", "0",
"-i", "concat_list.txt",
"-c", "copy", "-y", output_path
])
os.remove("concat_list.txt")
print(f"完成: {output_path}")
return output_path
if __name__ == "__main__":
main_video = sys.argv[1] if len(sys.argv) > 1 else "final_output.mp4"
concat_with_ending(main_video)
ffmpeg -f concat -safe 0 -i concat_list.txt \
-c:v libx264 -preset medium -crf 18 \
-c:a aac -b:a 192k \
-y final_with_ending.mp4
# ダークモード(推奨)
config.background_color = "#1a1a2e"
PRIMARY = "#4fc3f7" # ライトブルー
SECONDARY = "#81c784" # グリーン
ACCENT = "#ffb74d" # オレンジ
HIGHLIGHT = "#f06292" # ピンク
TEXT_COLOR = WHITE
# ライトモード
config.background_color = WHITE
PRIMARY = "#2563eb"
SECONDARY = "#16a34a"
ACCENT = "#f59e0b"
HIGHLIGHT = "#ec4899"
TEXT_COLOR = "#1f2937"
# 日本語フォント(OS別)
config.font = "Hiragino Sans" # macOS
# config.font = "Noto Sans CJK JP" # Linux
# config.font = "Yu Gothic" # Windows
# 推奨フォントサイズ
# メインタイトル: 48-72
# セクションタイトル: 36-42
# 本文: 22-28
# キャプション: 18-22
| プラットフォーム | 解像度 | アスペクト比 | 最大長さ |
|---|---|---|---|
| YouTube | 1920x1080 | 16:9 | 制限なし |
| YouTube Shorts | 1080x1920 | 9:16 | 60秒 |
| TikTok | 1080x1920 | 9:16 | 10分 |
| Instagram Reels | 1080x1920 | 9:16 | 90秒 |
| Instagram 投稿 | 1080x1080 | 1:1 | 60秒 |
| Twitter/X | 1920x1080 | 16:9 | 2分20秒 |
動画作成完了後、以下の注意事項をユーザーに必ず伝えてください:
【動画利用に関する重要な注意事項】
1. BGMについて
- 「自動生成BGM」を使用した場合:
→ このBGMは著作権フリーです。商用・非商用問わず自由に利用できます。
- 外部BGMを使用する場合:
→ 必ず利用規約を確認してください
→ フリーBGMサイトでも「クレジット表記必須」「商用利用不可」などの
条件がある場合があります
→ 推奨フリーBGMサイト:
- DOVA-SYNDROME (https://dova-s.jp/)
- 甘茶の音楽工房 (https://amachamusic.chagasi.com/)
- YouTube Audio Library
2. TTSナレーションについて
- edge-ttsで生成した音声は、Microsoftの利用規約に従います
- 商用利用の場合は、Azure Speech Servicesの有料プランを検討してください
3. コンテンツについて
- 論文解説などの場合、引用元を明記してください
- 他者の著作物を使用する場合は、著作権法に従ってください
4. 推奨クレジット表記例
「アニメーション: Manim Community
BGM: [BGMのソース]
ナレーション: Microsoft Edge TTS」
-ql オプション(低品質)を使用-qh を使用--disable_caching でキャッシュ問題を回避xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience