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. 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 https://github.com/rany2/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: python 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. python 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 https://play.google.com/store/apps/details?id=com.noktastudio.judgeme&referrer=utm source%3Ddevto . I build in public as @fatihbuilds https://x.com/fatihbuilds .