Skip to content
Back to skills

Tiktok

ASecurity

URL: `https://www.tiktok.com/tiktokstudio/upload?from=upload&lang=en` (always append `&lang=en`)

  • 9 stars
  • 0 votes
  • 0 copies
  • 5 views
  • Added May 27, 2026
developmentjavascriptgojavareact

Works with

  • cli

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned May 27, 2026

npx -y skills add yangchuansheng/browser-harness-rust --skill tiktok --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Tiktok?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Tiktok
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/yangchuansheng-tiktok/badge)](https://www.skillsdirectory.com/skills/yangchuansheng-tiktok)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
# TikTok Studio — Upload Video

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:

1. Find the banner Discard button (y < 300 in the page)
2. CDP `click(x, y)` on it
3. A confirmation modal appears — find the red Discard button (y > 300) and CDP `click(x, y)`
4. Repeat if multiple stale drafts are stacked

## Upload flow

### 1. Attach file

```text
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:

```text
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(700, 50)        # click away to deselect
```

Verify: `js('document.querySelector(\'div[contenteditable="true"][role="combobox"]\').innerText')`

### 3. Schedule

Click the Schedule radio label:
```text
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.

```text
# 1. ScrollIntoView and open the time picker
js("...scrollIntoView the time input...")
click(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.

```text
# 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(x, y)`. After success, page redirects to `/tiktokstudio/content`.

```text
js("...scrollIntoView Schedule button (offsetWidth > 100)...")
click(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 CDP `click(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 (see `interaction-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

Files in this skill

  • skill.md3.8 KB
  • upload.md3.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.