Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.mavera.io/llms.txt

Use this file to discover all available pages before exploring further.

Scenario

The first 5 seconds of a YouTube pre-roll ad determine everything — viewers either watch or hit Skip. This job takes your pre-roll ad candidates, runs Video Analysis focused on the opening 5 seconds (hook score, visual complexity, text legibility, emotional trigger), then puts the results through a Focus Group asking the core question: “Would you skip this ad?” The result is a skip-or-watch prediction with specific improvement recommendations from each persona before you commit media budget.

Architecture

Code

import os, requests, time

MV = os.environ["MAVERA_API_KEY"]
MV_BASE = "https://app.mavera.io/api/v1"
MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

PRE_ROLL_CANDIDATES = [
    {"path": "ads/preroll_a_product_demo.mp4", "label": "Product Demo — Fast Cuts"},
    {"path": "ads/preroll_b_testimonial.mp4", "label": "Customer Testimonial — Face First"},
    {"path": "ads/preroll_c_problem_hook.mp4", "label": "Problem Hook — Text Overlay"},
    {"path": "ads/preroll_d_humor.mp4", "label": "Humor — Unexpected Opening"},
]

# 1. Upload and analyze each candidate
analyses = []
for candidate in PRE_ROLL_CANDIDATES:
    upload = requests.post(f"{MV_BASE}/assets",
        headers={"Authorization": f"Bearer {MV}"},
        files={"file": (
            candidate["label"] + ".mp4",
            open(candidate["path"], "rb"),
            "video/mp4",
        )},
    ).json()

    analysis = requests.post(f"{MV_BASE}/video-analysis", headers=MV_H, json={
        "asset_id": upload["id"],
        "analysis_types": [
            "hook_score", "visual_complexity", "text_legibility",
            "emotional_arc", "cognitive_load", "pacing",
        ],
        "options": {"focus_window": "0-5s"},
        "metadata": {"label": candidate["label"], "format": "pre-roll"},
    }).json()

    analyses.append({"id": analysis["id"], "label": candidate["label"]})
    time.sleep(0.5)

# 2. Poll all analyses
results = []
for a in analyses:
    for _ in range(30):
        time.sleep(3)
        status = requests.get(
            f"{MV_BASE}/video-analysis/{a['id']}", headers=MV_H
        ).json()
        if status.get("status") == "completed":
            break
    r = status.get("results", {})
    results.append({
        "label": a["label"],
        "hook_score": r.get("hook_score", {}).get("score", 0),
        "hook_assessment": r.get("hook_score", {}).get("assessment", ""),
        "visual_complexity": r.get("visual_complexity", {}).get("score", 0),
        "text_legibility": r.get("text_legibility", {}).get("score", 0),
        "emotional_trigger": r.get("emotional_arc", {}).get("peak_emotion", "none"),
        "cognitive_load": r.get("cognitive_load", {}).get("average", 0),
    })

# 3. Create Focus Group personas
persona_ids = []
for name, desc in [
    ("Impatient Scroller", "25-year-old who skips every ad. Watches YouTube on mobile during commute. Only stops for humor or shock value. Hates sales pitches."),
    ("Intentional Viewer", "35-year-old who watches ads if they're relevant. Desktop viewer, often in research mode. Will watch 15s if the topic matches their search."),
    ("Background Listener", "28-year-old who plays YouTube in the background while working. Audio matters more than visual. Will skip if the first sound is a jingle."),
]:
    p = requests.post(f"{MV_BASE}/personas", headers=MV_H, json={
        "name": name, "description": desc,
    }).json()
    persona_ids.append(p["id"])
    time.sleep(0.3)

# 4. Focus Group — "Would you skip?"
result_block = "\n\n".join(
    f"AD {i+1}: \"{r['label']}\"\n"
    f"  Hook Score: {r['hook_score']}/100 — {r['hook_assessment']}\n"
    f"  Visual Complexity: {r['visual_complexity']}/10 | Text Legibility: {r['text_legibility']}/10\n"
    f"  Emotional Trigger: {r['emotional_trigger']} | Cognitive Load: {r['cognitive_load']}/10"
    for i, r in enumerate(results)
)

fg = requests.post(f"{MV_BASE}/focus-groups", headers=MV_H, json={
    "name": "Pre-Roll Skip Test",
    "persona_ids": persona_ids,
    "questions": [
        f"You're watching a YouTube video and these pre-roll ads play. For each, answer: Would you SKIP or WATCH? Why?\n\n{result_block}",
        "Which ad's first 2 seconds grabbed you? What specifically hooked you?",
        "Rewrite the opening line of the weakest ad to make you NOT skip.",
        "Rate each ad 1-10 on 'Would I click through to the website?'",
    ],
    "responses_per_persona": 2,
}).json()

for _ in range(20):
    time.sleep(5)
    data = requests.get(f"{MV_BASE}/focus-groups/{fg['id']}", headers=MV_H).json()
    if data.get("status") == "completed":
        break

print("PRE-ROLL AD SKIP TEST")
print("=" * 60)
for r in results:
    skip_predict = "WATCH" if r["hook_score"] > 70 else "LIKELY SKIP" if r["hook_score"] > 45 else "SKIP"
    print(f"  {r['label']:<45} Hook:{r['hook_score']:>3}/100  → {skip_predict}")

print("\nFOCUS GROUP RESPONSES:")
for resp in data.get("responses", []):
    print(f"\n[{resp.get('persona_id', '?')[:20]}] {resp.get('question', '')[:60]}...")
    print(f"  → {resp.get('answer', '')[:300]}")

Example Output

PRE-ROLL AD SKIP TEST
============================================================
  Product Demo — Fast Cuts                      Hook: 52/100  → LIKELY SKIP
  Customer Testimonial — Face First             Hook: 78/100  → WATCH
  Problem Hook — Text Overlay                   Hook: 85/100  → WATCH
  Humor — Unexpected Opening                    Hook: 91/100  → WATCH

FOCUS GROUP RESPONSES:

[Impatient Scroller] Pre-roll ads below. SKIP or WATCH each?...
  → AD 1: SKIP — opens with a product shot, my finger is already on Skip.
    AD 2: WATCH — face in first frame creates social obligation to listen.
    AD 3: WATCH — text overlay "You're wasting $400/month" hits a nerve.
    AD 4: WATCH — unexpected visual makes me curious. I'll give it 5 more seconds.

[Intentional Viewer] Rate each 1-10: 'Would I click through?'...
  → AD 1: 4/10 — generic product demo. AD 2: 7/10 — real person builds trust.
    AD 3: 9/10 — problem framing matches my search intent. AD 4: 6/10 — funny
    but unclear what the product does.

[Background Listener] Rewrite the weakest ad's opener...
  → AD 1 original: [product spinning on white background, corporate music]
    Rewrite: Open with a voice saying "Stop. You're about to skip this, but
    you're also about to waste $400 this month." — audio-first grabs background
    listeners like me.

Error Handling

The focus_window: "0-5s" option restricts analysis to the opening seconds. If unsupported, the full video is analyzed — results are still valid but include post-skip content.
YouTube pre-roll ads are 6s (bumper), 15s, or 30s. Ensure uploaded files match these lengths. Longer files are analyzed in full, diluting the skip-window focus.
The “Impatient Scroller” persona skews negative — expected. Weight their feedback for skip prediction, but weight “Intentional Viewer” for click-through prediction.

YouTube Integration

Focus Groups API