> ## 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.

# Caption-Based Content Extraction

> Pull Vimeo transcripts and repurpose them into blog posts, social media, and email copy using Mavera Chat and Generate

### Scenario

Your Vimeo videos contain hours of spoken content locked inside video files. This job pulls text tracks (captions/transcripts) via `GET /videos/{id}/texttracks`, downloads the transcript text, then sends it to Mavera Chat to extract key messages, claims, and CTAs. Finally, it uses Generate to repurpose the extracted content into blog post drafts, social media posts, and email copy. The result is a content repurposing pipeline that turns every video into 5+ written assets — without a human watching the footage.

### Architecture

```mermaid theme={"dark"}
flowchart LR
    A["Vimeo GET /videos/{id}/texttracks"] --> B[Download transcript] --> C["POST /mave/chat (extract key messages)"] --> D["POST /generate (blog, social, email)"] --> E[Repurposed content library]
```

### Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, requests, time

  VM = os.environ["VIMEO_ACCESS_TOKEN"]
  MV = os.environ["MAVERA_API_KEY"]
  VM_BASE = "https://api.vimeo.com"
  MV_BASE = "https://app.mavera.io/api/v1"
  VM_H = {"Authorization": f"Bearer {VM}", "Accept": "application/vnd.vimeo.*+json;version=3.4"}
  MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}

  VIDEO_IDS = ["123456789", "987654321", "456789012"]

  all_repurposed = []

  for video_id in VIDEO_IDS:
      # 1. Fetch text tracks
      tracks_resp = requests.get(
          f"{VM_BASE}/videos/{video_id}/texttracks", headers=VM_H
      ).json()

      tracks = tracks_resp.get("data", [])
      if not tracks:
          print(f"No captions for video {video_id} — skipping")
          continue

      caption_track = next(
          (t for t in tracks if t.get("type") == "captions" and t.get("language") == "en"),
          tracks[0]
      )

      # 2. Download the transcript file
      transcript_url = caption_track.get("link")
      if not transcript_url:
          print(f"No download link for video {video_id} — skipping")
          continue

      transcript_raw = requests.get(transcript_url).text

      # 3. Clean VTT/SRT formatting to plain text
      lines = []
      for line in transcript_raw.split("\n"):
          line = line.strip()
          if not line or line.startswith("WEBVTT") or "-->" in line or line.isdigit():
              continue
          lines.append(line)
      transcript = " ".join(lines)

      # 4. Get video metadata for context
      video_meta = requests.get(f"{VM_BASE}/videos/{video_id}", headers=VM_H, params={
          "fields": "name,description,duration",
      }).json()
      video_name = video_meta.get("name", f"Video {video_id}")

      print(f"Processing: \"{video_name}\" ({len(transcript)} chars transcript)")

      # 5. Extract key messages via Mave Chat
      extraction = requests.post(f"{MV_BASE}/mave/chat", headers=MV_H, json={
          "message": f"""Extract structured content from this video transcript.

  VIDEO: "{video_name}"
  TRANSCRIPT:
  {transcript[:8000]}

  Extract:
  1. **Key Messages** (3-5 core points the speaker makes)
  2. **Claims & Statistics** (any data points, percentages, or factual claims)
  3. **CTAs** (calls to action — explicit or implied)
  4. **Quotable Lines** (5-7 sentences that work standalone as social posts)
  5. **Topic Tags** (10 keywords for SEO/categorization)""",
      }).json()

      extracted = extraction.get("content", "")

      # 6. Generate repurposed content
      repurpose = requests.post(f"{MV_BASE}/generate", headers=MV_H, json={
          "prompt": f"""Using this extracted content from a video, generate repurposed written assets.

  SOURCE VIDEO: "{video_name}"
  EXTRACTED CONTENT:
  {extracted[:4000]}

  Generate these assets:

  1. **Blog Post Outline** — 800-word post structure with H2s, key points per section, and SEO title
  2. **LinkedIn Post** — 150-word thought leadership post with hook, insight, CTA
  3. **Twitter/X Thread** — 5-tweet thread with a hook tweet and numbered insights
  4. **Email Newsletter Block** — 100-word summary for a weekly newsletter with subject line
  5. **Instagram Caption** — 80 words max with relevant hashtags""",
      }).json()

      all_repurposed.append({
          "video": video_name,
          "video_id": video_id,
          "transcript_length": len(transcript),
          "extracted": extracted[:1000],
          "repurposed": repurpose.get("content", "")[:2000],
      })
      time.sleep(1)

  # 7. Output
  print("\nCAPTION-BASED CONTENT EXTRACTION")
  print("=" * 60)
  for item in all_repurposed:
      print(f"\n{'─' * 60}")
      print(f"VIDEO: {item['video']} ({item['transcript_length']:,} chars)")
      print(f"\nEXTRACTED CONTENT:")
      print(item["extracted"][:600])
      print(f"\nREPURPOSED ASSETS:")
      print(item["repurposed"][:1200])
  ```

  ```javascript JavaScript theme={"dark"}
  const VM = process.env.VIMEO_ACCESS_TOKEN;
  const MV = process.env.MAVERA_API_KEY;
  const VM_BASE = "https://api.vimeo.com";
  const MV_BASE = "https://app.mavera.io/api/v1";
  const VM_H = { Authorization: `Bearer ${VM}`, Accept: "application/vnd.vimeo.*+json;version=3.4" };
  const MV_H = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };

  const VIDEO_IDS = ["123456789", "987654321", "456789012"];
  const allRepurposed = [];

  for (const videoId of VIDEO_IDS) {
    // 1. Fetch text tracks
    const tracksResp = await fetch(
      `${VM_BASE}/videos/${videoId}/texttracks`, { headers: VM_H }
    ).then(r => r.json());

    const tracks = tracksResp.data || [];
    if (!tracks.length) { console.log(`No captions for ${videoId}`); continue; }

    const captionTrack = tracks.find(t => t.type === "captions" && t.language === "en") || tracks[0];

    // 2. Download transcript
    if (!captionTrack.link) { console.log(`No link for ${videoId}`); continue; }
    const transcriptRaw = await fetch(captionTrack.link).then(r => r.text());

    // 3. Clean VTT/SRT to plain text
    const transcript = transcriptRaw.split("\n")
      .map(l => l.trim())
      .filter(l => l && !l.startsWith("WEBVTT") && !l.includes("-->") && !/^\d+$/.test(l))
      .join(" ");

    // 4. Video metadata
    const videoMeta = await fetch(
      `${VM_BASE}/videos/${videoId}?fields=name,description,duration`, { headers: VM_H }
    ).then(r => r.json());
    const videoName = videoMeta.name || `Video ${videoId}`;

    console.log(`Processing: "${videoName}" (${transcript.length} chars)`);

    // 5. Extract key messages
    const extraction = await fetch(`${MV_BASE}/mave/chat`, {
      method: "POST", headers: MV_H,
      body: JSON.stringify({
        message: `Extract from transcript.\n\nVIDEO: "${videoName}"\nTRANSCRIPT:\n${transcript.slice(0, 8000)}\n\nExtract:\n1. Key Messages (3-5)\n2. Claims & Statistics\n3. CTAs\n4. Quotable Lines (5-7)\n5. Topic Tags (10 keywords)`,
      }),
    }).then(r => r.json());

    const extracted = extraction.content || "";

    // 6. Generate repurposed content
    const repurpose = await fetch(`${MV_BASE}/generate`, {
      method: "POST", headers: MV_H,
      body: JSON.stringify({
        prompt: `From this extracted video content, generate:\n\nSOURCE: "${videoName}"\nCONTENT:\n${extracted.slice(0, 4000)}\n\n1. Blog Post Outline (800-word structure with H2s, SEO title)\n2. LinkedIn Post (150 words, hook+insight+CTA)\n3. Twitter/X Thread (5 tweets, hook + numbered insights)\n4. Email Newsletter Block (100 words + subject line)\n5. Instagram Caption (80 words + hashtags)`,
      }),
    }).then(r => r.json());

    allRepurposed.push({
      video: videoName, videoId,
      transcriptLength: transcript.length,
      extracted: extracted.slice(0, 1000),
      repurposed: (repurpose.content || "").slice(0, 2000),
    });
    await new Promise(r => setTimeout(r, 1000));
  }

  // 7. Output
  console.log("\nCAPTION-BASED CONTENT EXTRACTION");
  console.log("=".repeat(60));
  for (const item of allRepurposed) {
    console.log(`\n${"─".repeat(60)}`);
    console.log(`VIDEO: ${item.video} (${item.transcriptLength.toLocaleString()} chars)`);
    console.log(`\nEXTRACTED:\n${item.extracted.slice(0, 600)}`);
    console.log(`\nREPURPOSED:\n${item.repurposed.slice(0, 1200)}`);
  }
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
Processing: "Q1 Product Launch Keynote" (12,480 chars transcript)
Processing: "Customer Success Webinar — Acme Corp" (8,200 chars transcript)
No captions for video 456789012 — skipping

CAPTION-BASED CONTENT EXTRACTION
============================================================

──────────────────────────────────────────────────────────────
VIDEO: Q1 Product Launch Keynote (12,480 chars)

EXTRACTED CONTENT:
## Key Messages
1. The new dashboard reduces reporting time from 4 hours to 15 minutes
2. AI-powered anomaly detection catches issues 3 days before manual review
3. Integration with existing tools requires zero code changes

## Claims & Statistics
- "4 hours to 15 minutes" (93% time reduction)
- "3 days earlier detection"
- "200+ enterprise customers in beta"
- "99.7% accuracy in anomaly detection"

## CTAs
- "Sign up for the beta at example.com/beta"
- "Book a demo with our team" (implied, slide shown at 4:32)

## Quotable Lines
- "The best dashboard is one you never have to open."
- "We didn't build another analytics tool. We built the one that tells you when to look."

REPURPOSED ASSETS:

### Blog Post Outline
Title: "How AI Anomaly Detection Catches Problems 3 Days Before You Do"
H2: The Hidden Cost of Manual Reporting (problem framing)
H2: From 4 Hours to 15 Minutes (the transformation)
H2: How Anomaly Detection Actually Works (technical credibility)
H2: What 200+ Beta Customers Discovered (social proof)
H2: Getting Started (CTA)

### LinkedIn Post
"Your team spends 4 hours building a report that's outdated by the time
it's finished. We cut that to 15 minutes — and added anomaly detection
that catches issues 3 days before manual review ever would. 200+ beta
customers are already seeing results. Here's what they found →"

### Twitter/X Thread
1/ Your reporting workflow is broken. Here's why — and what 200+
   companies are doing instead. 🧵
2/ Problem: Manual reporting takes 4 hours. By the time it's done,
   the data is stale and the damage is done.
3/ Solution: AI anomaly detection catches issues 3 days before you'd
   find them manually. 99.7% accuracy.
4/ Result: 93% reduction in reporting time. Teams spend those hours
   on strategy instead of spreadsheets.
5/ Want to see it? Beta is open → example.com/beta
```

### Error Handling

<AccordionGroup>
  <Accordion title="No text tracks available">Many Vimeo videos lack captions. If `texttracks` returns empty, use Vimeo's auto-captioning feature (available on paid plans) or upload an SRT file first. Alternatively, send the video directly to Mavera for transcript extraction via Video Analysis.</Accordion>
  <Accordion title="VTT format parsing">Vimeo text tracks can be in VTT, SRT, or DFXP format. The code strips VTT headers, timestamps, and sequence numbers. For DFXP (XML-based), use an XML parser to extract the text nodes.</Accordion>
  <Accordion title="Transcript length limits">Very long videos (60+ minutes) produce transcripts exceeding Mavera Chat's context window. The code truncates to 8,000 characters. For longer content, split the transcript into 5-minute chunks and extract from each separately.</Accordion>
</AccordionGroup>

***

## What's Next

<CardGroup cols={2}>
  <Card title="Vimeo Integration" icon="circle-play" href="/integrations/vimeo">
    Back to Vimeo integration overview
  </Card>

  <Card title="Webinar Intelligence" icon="graduation-cap" href="/integrations/vimeo/webinar-intelligence">
    Track engagement across webinar series
  </Card>

  <Card title="Mave Agent" icon="brain" href="/api-reference/mave">
    Full reference for POST /api/v1/mave/chat
  </Card>

  <Card title="Generate API" icon="wand-magic-sparkles" href="/api-reference/generate">
    Full reference for POST /api/v1/generate
  </Card>
</CardGroup>
