Shorts・縦長カバー

デザイン全体をJSONファイルで記述した1080×1920のカバーです。Python側では読み込んでレンダリングするだけなので、LLMやテンプレートシステムでレイアウトを作る場合にも使える構成です。
実行するにはuvをインストールし、リポジトリをクローンしてください。
git clone https://github.com/sjquant/quickthumb
cd quickthumb
uv run python examples/shorts_cover_agent.py
examples/shorts_cover_agent.pngを保存し、同名ファイルがあれば上書きします。
作り方
- 同じ写真を2回使います。 横長の16:9の切り取りと、スマートフォン内の画面に使います。
"fit": "cover"が各枠の形に合わせて切り取ります。 - スマートフォンは2枚のレイヤーです。 暗い角丸長方形が本体、その上の写真が画面です。両方と画面上の3つのラベルに
"rotation": 3を指定して、一緒に傾けます。 - パスはリポジトリのルート基準です。 例:
assets/images/denise-jans-WIRvXd1PYlg-unsplash.jpg。スクリプトは描画前にルートへ移動するため、どのディレクトリからでも実行できます。 - 背景に少し粒子を加えます。 固定の
seedを持つgrainで、毎回同じ質感を再現します。
仕様
{
"kind": "canvas",
"width": 1080,
"height": 1920,
"layers": [
{
"type": "background",
"color": "#F5F5F7",
"effects": [
{ "type": "grain", "intensity": 0.006, "monochrome": true, "seed": 9 }
]
},
{
"type": "text",
"content": "JSON WORKFLOW / 01",
"font": "Roboto",
"size": 24,
"color": "#6E6E73",
"position": ["8%", "7%"],
"weight": 700,
"letter_spacing": 3
},
{
"type": "shape",
"shape": "rectangle",
"position": ["8%", "10%"],
"width": 908,
"height": 1,
"color": "#1D1D1F26"
},
{
"type": "text",
"content": [
{ "text": "TURN LONG VIDEOS\nINTO ", "color": "#1D1D1F", "weight": 700 },
{ "text": "SHORTS.", "color": "#0066CC", "weight": 700 }
],
"font": "Roboto",
"size": 102,
"position": ["8%", "16%"],
"max_width": "84%",
"line_height": 1.0,
"letter_spacing": -2
},
{
"type": "text",
"content": "JSON in.\nCover out.",
"font": "Roboto",
"size": 42,
"color": "#6E6E73",
"position": ["8%", "43%"],
"line_height": 1.18
},
{
"type": "shape",
"shape": "rectangle",
"position": ["0%", "50%"],
"width": 1080,
"height": 720,
"color": "#0066CC"
},
{
"type": "shape",
"shape": "rectangle",
"position": ["77%", "56%"],
"width": 430,
"height": 850,
"color": "#0A0A0A",
"border_radius": 50,
"rotation": 3,
"align": "center",
"effects": [
{ "type": "stroke", "width": 1, "color": "#D2D2D7" },
{ "type": "shadow", "offset_x": 0, "offset_y": 22, "color": "#00000038", "blur_radius": 32 }
]
},
{
"type": "image",
"path": "assets/images/denise-jans-WIRvXd1PYlg-unsplash.jpg",
"position": ["77%", "56%"],
"width": 376,
"height": 728,
"fit": "cover",
"rotation": 3,
"align": "center",
"border_radius": 34,
"effects": [
{ "type": "filter", "brightness": 0.9, "contrast": 1.04, "saturation": 0.72, "blur": 0 }
]
},
{
"type": "shape",
"shape": "rectangle",
"position": ["8%", "64%"],
"width": 430,
"height": 242,
"color": "#FFFFFF14",
"border_radius": 18,
"effects": [{ "type": "stroke", "width": 2, "color": "#FFFFFF" }]
},
{
"type": "text",
"content": "16:9 SOURCE",
"font": "Roboto",
"size": 24,
"color": "#FFFFFF",
"position": ["8%", "60%"],
"weight": 700,
"letter_spacing": 2
},
{
"type": "image",
"path": "assets/images/denise-jans-WIRvXd1PYlg-unsplash.jpg",
"position": ["8%", "64%"],
"width": 430,
"height": 242,
"fit": "cover",
"border_radius": 18,
"effects": [
{ "type": "filter", "brightness": 0.76, "contrast": 1.08, "saturation": 0.58, "blur": 0 }
]
},
{
"type": "text",
"content": "TO / 9:16 × 3",
"font": "Roboto",
"size": 40,
"color": "#FFFFFF",
"position": ["8%", "80%"],
"weight": 700,
"letter_spacing": 2
},
{
"type": "shape",
"shape": "rectangle",
"position": ["77%", "47%"],
"width": 330,
"height": 2,
"color": "#FFFFFFCC",
"rotation": 3,
"align": "center"
},
{
"type": "shape",
"shape": "rectangle",
"position": ["77%", "58%"],
"width": 330,
"height": 2,
"color": "#FFFFFFCC",
"rotation": 3,
"align": "center"
},
{
"type": "text",
"content": "01 HOOK",
"font": "Roboto",
"size": 18,
"color": "#FFFFFF",
"position": ["65%", "41%"],
"weight": 700,
"letter_spacing": 2,
"rotation": 3,
"effects": [{ "type": "shadow", "offset_x": 0, "offset_y": 2, "color": "#000000CC", "blur_radius": 5 }]
},
{
"type": "text",
"content": "02 CUT",
"font": "Roboto",
"size": 18,
"color": "#FFFFFF",
"position": ["65%", "52%"],
"weight": 700,
"letter_spacing": 2,
"rotation": 3,
"effects": [{ "type": "shadow", "offset_x": 0, "offset_y": 2, "color": "#000000CC", "blur_radius": 5 }]
},
{
"type": "text",
"content": "03 CAPTION",
"font": "Roboto",
"size": 18,
"color": "#FFFFFF",
"position": ["65%", "63%"],
"weight": 700,
"letter_spacing": 2,
"rotation": 3,
"effects": [{ "type": "shadow", "offset_x": 0, "offset_y": 2, "color": "#000000CC", "blur_radius": 5 }]
},
{
"type": "shape",
"shape": "rectangle",
"position": ["8%", "88%"],
"width": 908,
"height": 1,
"color": "#1D1D1F26"
},
{
"type": "text",
"content": [
{ "text": "AGENT EMITS JSON", "color": "#1D1D1F", "weight": 700 },
{ "text": " / QUICKTHUMB RENDERS", "color": "#6E6E73", "weight": 500 }
],
"font": "Roboto",
"size": 28,
"position": ["8%", "91%"],
"max_width": "84%"
}
]
}
スクリプト
"""
JSON-first vertical cover example.
This script simulates the workflow where an AI agent emits a quickthumb JSON spec
and the application renders it with `Canvas.from_json(...)`.
"""
import os
from quickthumb import Canvas
FILE_DIR = os.path.dirname(__file__)
REPO_DIR = os.path.abspath(os.path.join(FILE_DIR, ".."))
SPEC_PATH = os.path.join(FILE_DIR, "shorts_cover_agent.json")
OUTPUT_PATH = os.path.join(FILE_DIR, "shorts_cover_agent.png")
os.environ["QUICKTHUMB_FONT_DIR"] = os.path.join(REPO_DIR, "assets", "fonts")
os.environ["QUICKTHUMB_DEFAULT_FONT"] = "Roboto"
with open(SPEC_PATH, encoding="utf-8") as f:
json_spec = f.read()
previous_cwd = os.getcwd()
try:
# The checked-in JSON spec uses repo-relative asset paths to stay easy to read.
os.chdir(REPO_DIR)
Canvas.from_json(json_spec).render(OUTPUT_PATH)
finally:
os.chdir(previous_cwd)
print(f"✓ JSON-first Shorts cover created: {OUTPUT_PATH}")
print(f" Spec source: {SPEC_PATH}")
Canvas.from_json()は描画前に仕様を検証します。不正な仕様では、問題のフィールドを示すValidationErrorになります。利用できるフィールドはJSON SchemaとAIワークフローを参照してください。