cd /news/ai-tools/i-automated-a-tiktok-campaign-for-my… · home › topics › ai-tools › article
[ARTICLE · art-153201] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=· neutral

I Automated a TikTok Campaign for My App by Editing CapCut's Project Files Directly

A developer built Judge Me, an Android app for voting on "Am I the jerk?"-style dilemmas, and automated a 14-day TikTok and Instagram Reels campaign by directly editing CapCut's draft_content.json project files. The pipeline uses edge-tts word-boundary timestamps to generate word-timed captions, re-aligning bare timed words against the punctuated script to fix captions that bled across sentences, and programmatically assembles hook text, background clips, voiceover, and a vote-screen outro. The stated goal is 500+ Play Store installs in 14 days or a single video exceeding 50k views.

by read5 min views6 publishedOct 19, 2026

I shipped Judge Me, a small Android app where people read "Am I the jerk?"-style dilemmas and vote: you were right, you were wrong, both of you messed up, or it's complicated. The app works. Nobody knows it exists.

So I'm running a 14-day experiment: short vertical videos on TikTok and Instagram Reels, one per day, each telling one dilemma from the app and ending with "go vote." The goal is simple and measurable: 500+ Play Store installs in 14 days, or one video over 50k views. If neither happens, I stop and try something else.

This post is about the boring-but-interesting part: how I made producing a video take ~5 minutes of my time instead of an hour, with no paid tools.

Each video looks like this:

Time What's on screen
0–2 s Big hook text (first sentence of the story)
2 s → end−4 s Background clip + AI voiceover + 2–3 word captions
Last 4 s "Right or wrong? YOU decide." + screenshot of the app's vote screen

Scripts are 100–130 words, first person, short sentences, ending with "So… was I wrong?". I use edge-tts, which is free and — crucially — streams word boundary events with timestamps alongside the audio.

comm = edge_tts.Communicate(text, "en-US-GuyNeural", rate="+12%", boundary="WordBoundary")
async for chunk in comm.stream():
    if chunk["type"] == "audio":
        f.write(chunk["data"])
    elif chunk["type"] == "WordBoundary":
        start = chunk["offset"] // 10_000  # 100ns ticks → ms
        words.append({"text": chunk["text"], "start_ms": start,
                      "end_ms": start + chunk["duration"] // 10_000})

That gives me an MP3 plus a JSON list of every word and when it's spoken. Captions come for free.

My first captions looked like star She launched and reviews So I — chunks bleeding across sentences. The boundary events return bare words, so my "break on punctuation" rule never fired.

Fix: re-align the timed words against the original script tokens (which do have punctuation), then chunk in two passes — split on clause boundaries first, then divide each clause into balanced groups so you never get a lonely one-word chunk:

def chunk_words(words, max_words=3, max_chars=20):
    clauses, cur = [], []
    for w in words:
        cur.append(w)
        if w["text"][-1:] in ".!?,;:…":
            clauses.append(cur); cur = []
    if cur: clauses.append(cur)

    chunks = []
    for cl in clauses:
        k = -(-len(cl) // max_words)          # ceil
        while True:                            # balance sizes, respect char limit
            size, extra = divmod(len(cl), k)
            parts, i = [], 0
            for j in range(k):
                step = size + (j < extra)
                parts.append(cl[i:i+step]); i += step
            if all(len(" ".join(x["text"] for x in p)) <= max_chars for p in parts) or k >= len(cl):
                break
            k += 1
        chunks.extend(parts)
    return chunks

Before: They showed up | late.

After: They showed | up late.

Small thing, but captions are most of what people actually read on mute.

This is the part that made the whole pipeline possible. CapCut desktop stores each project as a folder containing draft_content.json — tracks, segments, materials, text styles, all in plain JSON. (Some versions encrypt it; mine, 9.6 on Windows, didn't. Check yours before building on this.)

The structure, simplified:

tracks[]            → video / audio / text tracks, each with segments[]
segments[]          → material_id + target_timerange {start, duration} in µs + clip transform
materials.texts[]   → text content (as an embedded JSON string!) + style
materials.videos[]  → file paths for clips and images
materials.audios[]  → file paths for audio

My approach:

HOOK_TEXT, SUB_1, VOTE_TEXT, plus the background, audio, and vote-screen image. The one non-obvious detail: the text content lives in a JSON string inside the JSON, with a character range for styling. Change the text without updating the range and the styling breaks.

def set_text(mat, text):
    c = json.loads(mat["content"])
    c["text"] = text
    for st in c["styles"]:
        st["range"] = [0, len(text)]
    mat["content"] = json.dumps(c, ensure_ascii=False)

Then I open CapCut, glance at the timeline, and hit Export. That's my whole manual step.

Once one video worked, I generated 10 separate CapCut projects in one go — copy the template folder, give it a new draft_id, register it in CapCut's root_meta_info.json, render the content. CapCut picked them all up in its project list. Ten videos, ten exports, done in one sitting.

.locked file in the project folder tells you it's open. My first attempt was to have an AI agent with desktop control build the template inside CapCut by clicking. It was slower and flakier than I expected: CapCut's media panel didn't respond to synthetic mouse events at all (couldn't drag a text layer onto the timeline), and the agent's own window kept stealing focus. After a few rounds I switched to "just edit the files," which took less time than the clicking attempts had.

Lesson: if an app has a readable file format, editing the file beats automating the UI almost every time.

Plan said "link in bio." TikTok said no — clickable bio links need 1,000 followers on a personal account, and the business account switch wasn't available to me without verification. Plain-text URLs in the bio aren't clickable.

So the end card now says "Vote free: search 'Judge Me AITA' on Google Play" — I checked that exact search ranks the app first. The downside: I lose UTM attribution on TikTok and have to infer it from Play Console's search-traffic numbers. Instagram still gets a proper tracked link.

The experiment starts this week. I'll post the results — views, installs, what hooks worked, and whether 03:00 scheduling actually mattered — when the 14 days are up, including if it flops.

If you're an indie dev sitting on an app nobody knows about, the takeaway isn't "use CapCut." It's: find the fixed structure in your marketing content, and turn the repetitive part into data.

Judge Me is on Google Play. I build in public as @fatihbuilds.

── more in #ai-tools 4 stories · sorted by recency
── more on @judge me 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/i-automated-a-tiktok…] indexed:0 read:5min 2026-10-19 · —