Domain skill
tiktok
Markdown synced from browser-harness domain skills.
- Host
- tiktok
- Files
- 1
Agent prompt
Use this skill
Copy this prompt into your coding agent to make it enable browser-harness domain skills and read this exact domain folder before automating.
Set up https://github.com/browser-use/browser-harness for me if it is not already installed. If setup is needed, read `install.md` first to install and connect it to my real browser. Then read `SKILL.md` for normal usage and always read `helpers.py` because that is where the browser-harness functions are. Enable domain skills if they are not already enabled by setting `BH_DOMAIN_SKILLS=1` for browser-harness. Use the `tiktok` domain skill from `agent-workspace/domain-skills/tiktok/`. Read every markdown file for this domain before inventing an approach: - agent-workspace/domain-skills/tiktok/upload.md Use those domain-skill notes to complete my task for `tiktok` in my real browser. When you open a setup, verification, or task tab, activate it so I can see the active browser tab.
Skill contents
What the agent will read
Upload Video
upload.md
- URL: https://www.tiktok.com/tiktokstudio/upload?from=upload&lang=en (always append &lang=en)
- Logged into TikTok in the Chrome profile browser-harness is attached to
- Video file on local disk (mp4, <50MB)
- TikTok shows "A video you were editing wasn't saved" if a previous upload was abandoned. Dismiss it:
Show full markdown
URL: https://www.tiktok.com/tiktokstudio/upload?from=upload&lang=en (always append &lang=en)
Prerequisites
- Logged into TikTok in the Chrome profile browser-harness is attached to
- Video file on local disk (mp4, <50MB)
Stale draft banner
TikTok shows "A video you were editing wasn't saved" if a previous upload was abandoned. Dismiss it:
- Find the banner Discard button (y < 300 in the page)
- CDP
click_at_xy(x, y)on it - A confirmation modal appears — find the red Discard button (y > 300) and CDP
click_at_xy(x, y) - Repeat if multiple stale drafts are stacked
Upload flow
1. Attach file
upload_file('input[type="file"]', "/path/to/video.mp4")
wait(12) # processing takes ~10s for 5-10MB
2. Caption
TikTok pre-fills caption with the filename. Clear it first:
js("document.querySelector('div[contenteditable=\"true\"][role=\"combobox\"]').focus()")
press_key("End")
for _ in range(25): press_key("Backspace") # clear filename
type_text("your caption here #hashtag1 #hashtag2")
press_key("Escape") # dismiss hashtag suggestions
click_at_xy(700, 50) # click away to deselect
Verify: js('document.querySelector(\'div[contenteditable="true"][role="combobox"]\').innerText')
3. Schedule
Click the Schedule radio label:
js("(()=>{var l=document.querySelectorAll('label');for(var i=0;i<l.length;i++){if(l[i].textContent.trim()==='Schedule'){l[i].click();break}}})()")
Time picker — uses a scroll-wheel list, NOT a native select. Each scroll(dy=32) steps +1 unit, dy=-32 steps -1 unit.
# 1. ScrollIntoView and open the time picker
js("...scrollIntoView the time input...")
click_at_xy(time_input_x, time_input_y)
# 2. Read default time, calculate difference
default_hour, default_min = 13, 5 # from input value
target_hour, target_min = 20, 25
# 3. Scroll hour column (left, x ≈ 349)
for _ in range(target_hour - default_hour):
scroll(349, dropdown_y, dy=32) # +1 hour per step
# 4. Scroll minute column (right, x ≈ 437)
for _ in range((target_min - default_min) // 5):
scroll(437, dropdown_y, dy=32) # +5 min per step
# 5. Close and verify
press_key("Escape")
Date picker — click the date input, then click the target day number span.
4. AI-generated content disclosure
Under "Show more" section. Toggle is [aria-checked] inside the "AI-generated content" parent.
# Expand settings
js("...click 'Show more' span...")
# ScrollIntoView the toggle
js("...scrollIntoView 'ai-generated content' span...")
# Read state and click if false
# A "Turn on" confirmation dialog may appear — click it
5. Submit
Scroll the Schedule button into view, then CDP click_at_xy(x, y). After success, page redirects to /tiktokstudio/content.
js("...scrollIntoView Schedule button (offsetWidth > 100)...")
click_at_xy(button_x, button_y)
wait(6)
assert "content" in page_info()["url"]
Gotchas
- JS
.click()doesn't work on TikTok's time picker items — must use CDPclick_at_xy(x, y) - Time picker uses virtual scroll —
scroll(x, y, dy=32)changes value, NOT regular DOM scroll - Caption contenteditable appends on type — always clear with End + Backspace first, never set innerHTML (breaks React state)
- beforeunload dialog blocks navigation if upload is in progress — use
cdp("Page.handleJavaScriptDialog", accept=True)to dismiss (seeinteraction-skills/dialogs.md) - Schedule button text is "Schedule" only after the Schedule radio is selected (otherwise "Post")
- "Show more" section expands the page and pushes the time picker off-viewport — collapse it before adjusting time, expand after
- Unicode narrow no-break space (char 8239) appears between time and AM/PM in scheduled post listings — use
.indexOf('12:30')not exact string match