コンテンツにスキップ
quickthumb
すべてのドキュメント

Shorts・縦長カバー

切り取った写真を表示する傾いたスマートフォンと「Turn long videos into 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ワークフローを参照してください。