Reklamy: copy a competitor's ad with your own product

Give it an ad that works for a competitor. Get a shot-by-shot breakdown and ready Gemini Omni prompts that build the same ad with your product.

Writing, beginner. Published

What it does

You paste or link a competitor's video ad and describe your product: name, price, a photo and the facts that are true. rozbierz.py finds every cut, saves a frame per shot and writes down the voice-over. The agent fills a table: time, the shot's job (hook, problem, demo, proof, offer, CTA), camera, what happens, text on screen and the spoken words. Then it describes the hook in the first 3 seconds, the order of the beats, the pacing, the avatar and the awareness stage. Before writing any prompt it lists what must change: brand name, logo, slogans, real people, reviews and numbers you don't have. You copy the structure, not the brand. Finally you get Gemini Omni Flash prompts, one per clip of up to 10 seconds, with shot timecodes, your product photo as the reference, the text on screen and the voice-over. On request the agent swaps the avatar (who is on screen and how they talk) or the awareness stage (from unaware to ready to buy).

When to use it

When to use it

  • You see an ad in the Meta Ad Library or on TikTok that has been running for weeks and want to test it with your product.
  • You have a product and a photo, but no footage and no UGC creator.
  • You want several versions of one ad: for another customer group or another awareness stage.
  • You want to understand why a competitor's ad works: hook, pacing, order.

When not to use it

  • The ad stands on the face of a famous person or a specific creator. You can copy the structure, not the face or the voice.
  • Your product doesn't have the feature the ad shows as the main one. Pick another ad.
  • You want to upload the competitor's ad and swap the product inside it. In the EU, editing uploaded videos in Omni is blocked, and legally it is a bad idea.

Decision table

SituationWhat the skill does
The ad is 27 seconds longSplits it into 3 blocks of up to 10 seconds at natural cuts, one prompt per block
A famous person is in the adWrites a new fictional character and describes them the same way in every block
The ad shows "40,000 reviews" or starsRemoves them or uses your real numbers; missing ones stay as [TO FILL]
You want to reach people who don't know they have the problemRewrites the hook as a specific scene with the problem, no product talk at the start
You don't have PythonAsks for 6-10 screenshots and the voice-over text and builds the table from them

Template

The method for the agent: a shot-by-shot breakdown, the list of what must change (brand, people, reviews), moving the structure onto your product, avatar and awareness-stage swaps, and Gemini Omni Flash prompts of at most 10 seconds each.

SKILL.mdDownloadSKILL.md
---
name: reklamy
description: Breaks down a competitor's video ad shot by shot (hook, beats, camera, text on screen, voice-over, pacing, offer) and writes ready prompts for Google's Gemini Omni Flash video model that rebuild the same ad, beat for beat, with the user's own product. Can also swap the avatar (who is on screen) and the awareness stage (unaware, problem aware, solution aware, product aware, most aware). Use when the user says "skopiuj tę reklamę", "rozbierz reklamę konkurencji", "zrób mi taką reklamę z moim produktem", "copy this ad", "recreate this competitor ad", or pastes or links an ad video.
author: Dawid Gac
license: MIT
---

# Reklamy: copy a competitor's ad with your own product

Author: Dawid Gac · https://dawidgac.com/pl/skills

You take an ad that already works for someone else and rebuild its structure for the user's product. You copy the structure: timing, shot types, order of beats, pacing, type of hook. You never copy the brand: its name, logo, slogans, people, reviews or claims. Talk to the user in their language (Polish if they write Polish). Write the video prompts themselves in English, because Google says only English is fully tested for Omni; the spoken lines and on-screen text can stay in the user's language.

## 0. Collect the inputs (one message, skip what you already have)

1. The ad: a video file (best) or a link. Links from TikTok, Instagram, Facebook Ad Library or YouTube work if yt-dlp can download them; if not, ask for a screen recording.
2. Their product: name, what it does, price and offer, 1-3 product photos (a clean photo on a plain background works best), and the claims that are TRUE for it (numbers, guarantee, delivery time).
3. Optional: who the ad should speak to (avatar), which awareness stage, format 9:16 or 16:9 (default 9:16).

Never invent a product fact. A missing fact stays a `[TO FILL: ...]` slot in the prompt.

## 1. Break the ad down

With Python 3 and ffmpeg (run the script by its full path inside this skill's folder):

```
python3 <this skill's folder>/rozbierz.py <file or link> --out ad-breakdown --lang <pl|en>
```

It writes `shots.json` (each cut with start, end and length, plus pacing), one frame per shot (`shot-01.jpg` ...), `first.jpg`, `last.jpg`, `audio.wav`, and `transcript.json` when faster-whisper or whisper is installed. Open every frame and read the transcript. Without the script: ask the user for 6-10 screenshots plus the voice-over text, and estimate the timing from the video.

Fill this table, one row per shot. Copy text on screen and spoken words exactly, word for word.

| # | Time | Beat | Camera (shot size, angle, movement) | What happens | Text on screen | Voice-over / dialogue | Sound | Transition |
|---|---|---|---|---|---|---|---|---|

Beat is one of: HOOK, PROBLEM, AGITATE, REVEAL, DEMO, PROOF, BENEFIT, OFFER, CTA.

Then the whole-ad summary:
- **Hook (first 3 s):** what stops the scroll: a question, a bold claim, a visual pattern break, a demo, a result. Say why it works.
- **Beats in order:** e.g. HOOK > PROBLEM > DEMO > PROOF > OFFER > CTA.
- **Pacing:** number of shots, average shot length, cuts per 10 s (from shots.json).
- **Avatar:** who speaks or appears (age range, style, setting), and who the ad speaks to.
- **Awareness stage:** pick one and give the signal: names the product and price = most aware; compares with other solutions = solution aware; starts from a pain = problem aware; starts from a story or a surprise with no product talk = unaware.
- **Format:** UGC selfie, talking head, product demo, testimonial, before and after, street interview, animation.
- **Offer and CTA:** what is promised, what the viewer is told to do.

## 2. Mark what must change (always, before writing any prompt)

List every brand element and its replacement:
- brand name, logo, website address, slogans, product design details that belong to them: replace with the user's own;
- real people: never recreate the face, voice or name of a real person (a celebrity, an influencer, the competitor's creator). Write a new, fictional character. Omni also refuses many uploads of recognisable people;
- reviews, star ratings, review counts, awards, "as seen on": remove, or use the user's own real numbers;
- claims (warranty, "holds 12 cards", certificates): only what is true for the user's product, otherwise `[TO FILL]`;
- music: describe the mood ("upbeat electronic beat"), never name a song.

Tell the user in one line: "I copy the structure, not the brand. Their logo, people, reviews and slogans are replaced."

## 3. Map the structure onto the user's product

Keep the timing, shot sizes, camera moves, beat order, the hook type and the pacing. For each row write what the user's product does in that slot. A demo of their product feature becomes a demo of the user's matching feature; if there is no matching feature, use the user's strongest true benefit and say you swapped it.

## 4. Swaps on request

**Avatar swap.** Change who is on screen and who the words are for. Keep the shot sizes and timing. Rewrite the spoken lines in that person's own words (age, situation, the way they talk). Describe the new character in full once (age, look, clothes, setting) and repeat the same description in every prompt block, so the character stays the same between clips.

**Awareness stage swap.** Rewrite the hook and the first beats; keep the demo and the CTA shape.

| Stage | What the hook does | Beats |
|---|---|---|
| Unaware | Opens on a specific moment (time, place, action) where the problem shows, without naming the product | SCENE > SYMPTOM > REVEAL > DEMO > CTA |
| Problem aware | Names the pain in the viewer's words and shows the symptom | PROBLEM > AGITATE > REVEAL > DEMO > PROOF > CTA |
| Solution aware | Shows why the usual fixes fail and what is different here | OTHER FIXES > WHY THEY FAIL > MECHANISM > DEMO > PROOF > CTA |
| Product aware | Answers the main doubt (price, quality, does it work for me) | DOUBT > PROOF > DEMO > OFFER > CTA |
| Most aware | Leads with the offer | OFFER > DEMO > URGENCY > CTA |

One ad does one job: one avatar, one awareness stage, one angle. Want more stages? Write a separate ad per stage. Suggested name for each version: `Product_Angle_Stage_Format_Hook_Avatar_v1`.

## 5. Write the Gemini Omni Flash prompts

Gemini Omni Flash makes clips of 4, 6, 8 or 10 seconds. Split the ad into blocks of at most 10 s at natural cuts (a 27 s ad = 3 blocks). Each block is one prompt. Rules from Google's prompt guide:
- Use timecodes for the cuts inside a block: `[0-2s] ... [2-4s] ...`. Without them, Omni invents its own cuts.
- Attach the product photo and refer to it as `<IMAGE_REF_0>`; the product must look exactly like the reference. A photo for the first frame is `<FIRST_FRAME>`.
- Put text on screen in quotes, exactly as it should read.
- Describe the sound: `Voice-over (warm male voice, Polish): "..."`, music mood, effects. Add `No other dialogue.` if needed.
- Negatives go into the prompt as plain sentences (`No logos other than the product. No subtitles.`).
- Repeat the character, setting and light description in every block.

Prompt template (one per block):

```
Vertical 9:16 ad, [style: e.g. bright handheld UGC filmed on a phone / clean studio product demo].
Character: [full description, the same in every block].
Product: the product shown in <IMAGE_REF_0>, exactly as in the reference. [colour, size, material].
[0-2s] [shot size, angle, camera move]: [action]. Text on screen: "[text]".
[2-4s] ...
Voice-over ([voice], [language]): "[line]".
Sound design: [music mood], [effects].
No logos or brand names other than "[user's brand]". No subtitles. No other text.
```

Known limits (seen in testing, 2026-10-20): Omni does not reliably write an invented brand name on a product (a prompt saying "NORDA" engraved came out as a different word), and without a product photo the colour can drift between shots. So always attach a real product photo as `<IMAGE_REF_0>`, keep the brand name off the product in the prompt, and add your real logo and price in the edit.

When the result is close but not right, edit in the same conversation with a short instruction, e.g. `Make the wallet black. Keep everything else the same.` Short edit prompts work better than long ones.

## 6. Where to generate (check prices on the day you use it)

- **Google Flow** (labs.google/fx/tools/flow): everyone gets 50 free Flow credits a day; Google's model page says all users can use Gemini Omni, and the 360p draft costs half the credits of 720p. Without a subscription you may not be able to make videos at peak hours (about 14:00-17:00 UTC). Start with 360p drafts, then 720p for the version you keep.
- **Gemini app** (gemini.google.com): Omni needs a paid Google AI plan.
- **Gemini API** (`gemini-omni-1.1-flash`): paid tier only, about $0.10 per second of 720p video.
- In the EU, UK and Switzerland, editing or extending a video you upload is blocked, so you cannot simply upload the competitor's ad and "swap the product". That is why this skill writes the clip from text and your product photo. That is also the clean way legally.
- The same block prompts also work in other video models; only drop the `<IMAGE_REF_0>` tag and attach the photo the way that tool expects.

## 7. What you give the user

1. The breakdown table and the summary (section 1).
2. The change list (section 2).
3. The prompts, one copy-ready block per clip, in order, with the clip length to choose (e.g. "Block 1: 10 s").
4. Any `[TO FILL]` slots in one list.
5. Three lines on how to put it together: generate each block, check that the product looks right, join the clips in order in any editor (CapCut, Premiere, DaVinci), add the user's real logo and price in the edit.

Checklist before you hand it over:
- no brand name, logo, website or slogan of the competitor in any prompt;
- no real person described or named;
- every number and claim is the user's own or marked `[TO FILL]`;
- every block is 10 s or shorter and has timecodes;
- the character description is identical in every block.

Other files

rozbierz.pyA Python script with no extra libraries (needs ffmpeg): finds the cuts, saves one frame per shot, measures the pacing and, if faster-whisper is installed, writes down the voice-over with timestamps. It sends nothing anywhere.
rozbierz.pyDownloadrozbierz.py
#!/usr/bin/env python3
"""rozbierz.py: split an ad video into shots for the reklamy skill.

Usage:
  python3 rozbierz.py <video file or URL> [--out folder] [--threshold 0.3] [--lang pl]

Needs ffmpeg + ffprobe. A URL needs yt-dlp. Speech-to-text runs only if
faster-whisper or openai-whisper is installed; otherwise the agent listens
itself or asks the user to paste the voice-over.

Writes to the out folder: shots.json (cut times, length, pacing), one frame per
shot (shot-01.jpg ...), first.jpg + last.jpg, audio.wav and, when possible,
transcript.json (speech with timestamps). Sends nothing anywhere.
"""
import argparse, json, re, shutil, subprocess, sys
from pathlib import Path


def run(cmd):
    return subprocess.run(cmd, capture_output=True, text=True)


def need(tool):
    if not shutil.which(tool):
        sys.exit(f"rozbierz: {tool} is not installed (https://ffmpeg.org/download.html, https://github.com/yt-dlp/yt-dlp)")


def fetch(url, out):
    need("yt-dlp")
    dst = out / "ad.mp4"
    r = run(["yt-dlp", "-q", "--no-playlist", "-f", "bv*[height<=1080]+ba/b", "--merge-output-format", "mp4", "-o", str(dst), url])
    if r.returncode or not dst.exists():
        sys.exit("rozbierz: download failed. Save the ad as a file yourself (screen recording works) and pass the file.\n" + r.stderr[-400:])
    return dst


def duration(video):
    return float(run(["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "csv=p=0", str(video)]).stdout.strip())


def cuts(video, threshold):
    # scene score per frame; showinfo prints pts_time for every frame the select keeps
    r = run(["ffmpeg", "-nostdin", "-i", str(video), "-filter:v", f"select='gt(scene,{threshold})',showinfo", "-f", "null", "-"])
    return [float(t) for t in re.findall(r"pts_time:([0-9.]+)", r.stderr)]


def frame(video, t, path):
    run(["ffmpeg", "-nostdin", "-v", "error", "-y", "-ss", f"{t:.3f}", "-i", str(video), "-frames:v", "1", "-vf", "scale=-2:540", str(path)])


def transcribe(audio, lang):
    try:
        from faster_whisper import WhisperModel
        segs, _ = WhisperModel("small", compute_type="int8").transcribe(str(audio), language=lang or None)
        return [{"start": round(s.start, 2), "end": round(s.end, 2), "text": s.text.strip()} for s in segs]
    except ImportError:
        pass
    try:
        import whisper
        res = whisper.load_model("small").transcribe(str(audio), language=lang or None)
        return [{"start": round(s["start"], 2), "end": round(s["end"], 2), "text": s["text"].strip()} for s in res["segments"]]
    except ImportError:
        return None


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("src")
    ap.add_argument("--out", default="ad-breakdown")
    ap.add_argument("--threshold", type=float, default=0.3, help="lower = more cuts found (0.2-0.4)")
    ap.add_argument("--lang", default=None, help="speech language, e.g. pl or en (default: auto)")
    a = ap.parse_args()
    need("ffmpeg"); need("ffprobe")
    out = Path(a.out).expanduser(); out.mkdir(parents=True, exist_ok=True)
    video = fetch(a.src, out) if re.match(r"https?://", a.src) else Path(a.src).expanduser()
    if not video.exists():
        sys.exit(f"rozbierz: no such file: {video}")

    total = duration(video)
    bounds = [0.0] + [t for t in cuts(video, a.threshold) if 0.4 < t < total - 0.2] + [total]
    edges = [bounds[0]]
    for t in bounds[1:]:
        if t - edges[-1] >= 0.4:  # a flash or a glitch is not a new shot
            edges.append(t)
    edges[-1] = total
    shots = []
    for i, (s, e) in enumerate(zip(edges, edges[1:]), 1):
        f = out / f"shot-{i:02d}.jpg"
        frame(video, (s + e) / 2, f)
        shots.append({"n": i, "start": round(s, 2), "end": round(e, 2), "length": round(e - s, 2), "frame": f.name})
    frame(video, 0.1, out / "first.jpg")
    frame(video, max(total - 0.2, 0), out / "last.jpg")

    run(["ffmpeg", "-nostdin", "-v", "error", "-y", "-i", str(video), "-vn", "-ac", "1", "-ar", "16000", str(out / "audio.wav")])
    speech = transcribe(out / "audio.wav", a.lang) if (out / "audio.wav").exists() else None
    if speech is not None:
        (out / "transcript.json").write_text(json.dumps(speech, ensure_ascii=False, indent=1))

    report = {
        "video": str(video), "duration": round(total, 2), "shots": shots,
        "pacing": {"shot_count": len(shots), "avg_shot_s": round(total / len(shots), 2),
                   "cuts_per_10s": round((len(shots) - 1) / total * 10, 1)},
        "transcript": "transcript.json" if speech is not None else None,
        "note": None if speech is not None else "no speech-to-text installed (pip install faster-whisper); listen to audio.wav or ask the user for the voice-over",
    }
    (out / "shots.json").write_text(json.dumps(report, ensure_ascii=False, indent=1))
    print(json.dumps(report["pacing"]), f"-> {out}/shots.json, {len(shots)} frames")


if __name__ == "__main__":
    main()

What you need

Install

  1. Claude Code: copy both files to .claude/skills/reklamy/ in a project, or to ~/.claude/skills/reklamy/ for all projects. The skill loads when you write "copy this ad" or type /reklamy.
  2. Codex: paste SKILL.md into AGENTS.md in the repository root or into ~/.codex/AGENTS.md, and keep rozbierz.py next to it.
  3. Cursor: add SKILL.md as a project rule and keep rozbierz.py in the same folder.
  4. Check the script: python3 rozbierz.py ad.mp4 --out breakdown should print the shot count and save shots.json and the frames.

It's working if

  • The table has one row per cut, and the text on screen and the spoken words are copied word for word.
  • Before the prompts there is a list of what must change: brand, logo, people, reviews, numbers.
  • No prompt contains the competitor's brand name, its logo or a real person.
  • Every prompt is 10 seconds or less with shot timecodes, and the character description is identical in every block.
  • Facts about your product that you didn't give are marked [TO FILL], never invented.

Requirements

  • An AI agent that reads markdown instruction files and can see images (Claude Code, Codex, Cursor or another).
  • A Google account with access to Google Flow (50 free credits a day).
  • Optional: Python 3.9+, ffmpeg, faster-whisper and yt-dlp.

Questions

Is this legal?

The skill copies structure: the order of shots, the pacing, the type of hook. Nobody owns that. It does not copy the brand, logo, slogans, people or reviews: it lists each one and replaces it with yours. You must be able to back up your own product claims.

Is Gemini Omni free?

In Google Flow everyone gets 50 free credits a day, and 360p clips cost half the credits of 720p. In the Gemini app Omni needs a paid Google AI plan, and in the API it is paid tier only (about $0.10 per second of 720p video). Check prices on the day you use it.

Why not just upload the competitor's ad to Omni?

In the EU, the UK and Switzerland, editing and extending uploaded videos in Omni is not available. So the skill writes the clip from scratch, from text and your product photo. That is also the cleaner way legally.

Can the prompts be in Polish?

The scene description is in English, because Google says only English is fully tested. The spoken lines and the text on screen can be in any language.

Will Omni write my brand name on the product?

Not reliably. In testing, the name "NORDA" from the prompt came out on the product as a different word, and without a product photo the colour changed between shots. So attach a real product photo, don't ask the model to write the brand name, and add your logo and price in the edit.

Where it fits

This skill takes an ad that already works and gives you its structure with your product. The next step is versions: another customer group, another awareness stage, several hooks on the same ad body. Name each version so the results show which hook and which stage won. The script and SKILL.md are my own work, MIT licensed.

All skills

Sources

Questions about setting these up go in the Discord.Join freeAll my skills are also on Whop, in Operator: with a changelog, a note on what each was tested on, and a weekly call.