Skip to main content

ttclips — clip reposter for TikTok

Fully local, free pipeline: monitors YouTube channels, detects highlights (whisper

  • Gemini), crops clips to vertical 9:16 with dead-center, word-synced highlight captions, and posts to TikTok through Playwright browser automation (cookie auth, no API approval needed).

How it works

YouTube RSS feeds ─────────▶ monitor.py (queue of new videos)
                                   │
                          yt-dlp + ffmpeg (download, merge + cache reuse)
                                   ▼
                          faster-whisper (transcription + word timestamps)
                                   ▼
                          Gemini (probe → fallback chain) highlight ranking
                                   ▼
                          FFmpeg (crop 9:16 + big centered karaoke captions)
                                   ▼
    2 pipelines:  repost clips        OR        generated original clips
             Gemini highlights  +   Gemini script → edge-tts narration →
             cut_and_crop_clip        stock b-roll + captions
                                      (gradient fallback)
                                   ▼
                          Playwright → TikTok upload (cookies)

8 posts/day schedule (config/posting_window.json):

Time Type
08:00 repost
10:00 repost
12:00 generated
14:00 repost
16:00 generated
18:00 repost
20:00 generated
22:00 repost

Install as a pip package

pip install .            # from a source checkout
ttclips --init           # scaffold config/ + .env.example into the current dir

Once published: pip install ttclips. The ttclips command resolves config/ + data/ from the current working directory, or from $TTCLIPS_HOME when that variable is set — so an installed CLI can run from any empty folder:

mkdir D:\ttclips && cd D:\ttclips
ttclips --init
# edit .env (GEMINI_API_KEY), then:
ttclips --monitor

Running from a source checkout keeps working unchanged simply by cd-ing to the repo root. Related console commands: ttclips-gemini --probe and ttclips-upload (the internal upload child process).

Releasing to PyPI (auto via GitHub Actions)

Pushing a tag vX.Y.Z automatically builds the wheel + sdist, runs twine check, and publishes to PyPI (.github/workflows/publish.yml). A separate validation workflow (.github/workflows/ci.yml) builds + checks the package on every push to main and every PR — no secrets needed.

Release flow:

  1. Bump the version in both pyproject.toml and ttclips/__init__.py (the release workflow refuses to publish a mismatch).

  2. Tag and push:

    git add . ; git commit -m "Release v0.1.0"
    git tag v0.1.0
    git push origin main --tags
    
  3. Watch "Publish to PyPI" on GitHub → Actions. Once green, pip install ttclips works.

First-time publisher setup (trusted publishing — no API token needed):

  • PyPI → Account settings → Publishing → Add a pending publisher with:
    • Owner: kamzysert3 · Repository: ttclips · Workflow name: publish.yml
  • The workflow filename must match exactly — it's part of the OIDC claim.
  • The first successful run creates the ttclips project on PyPI automatically.

Setup (Windows)

  1. Python 3.10+ — verify with python --version.
  2. powershell -ExecutionPolicy Bypass -File scripts\setup.ps1
    • creates .venv, installs deps & Playwright chromium
    • installs ffmpeg via winget if missing and adds its bin dir to your PATH so yt-dlp's video+audio merge finds it
    • copies .env.example → .env
  3. Edit .env → set GEMINI_API_KEY (free at https://aistudio.google.com/apikey) and PEXELS_API_KEY (free at https://www.pexels.com/api/ — powers stock backgrounds behind generated clips; leave blank and generated clips keep the gradient backplate).
  4. TikTok cookies: log into tiktok.com in Chrome → use the "Get cookies.txt LOCALLY" extension → export for tiktok.com → save as data/cookies.txt. Re-export roughly monthly (sessions drift).

Verify

ttclips --validate-session                 # cookies OK?
ttclips --monitor                          # build watch queue
ttclips --once --dry-run                   # full pipeline, no upload
ttclips --once --slot 12:00                # post one slot now
ttclips --once --slot 12:00 --no-upload    # render only
ttclips --post-file "data\clips\video.mp4" # post an already-generated clip
ttclips-gemini --probe                     # ping Gemini model status

Closing flags: --dry-run = skip upload, --no-upload = render but don't post. --post-file <path> (optional --caption "...") uploads a clip that is already generated, skipping the entire download/transcribe/rank/encode pipeline — handy for retrying a stuck upload on a finished clip. On success it records a manual entry in data/posted.json.

Scheduled runs (Windows Task Scheduler)

Task Trigger Action
ttclips-monitor Daily 06:00 refresh the watch queue (--monitor)
ttclips-poster Hourly 07:00→23:00 --once (posts only at configured slot times)

The scheduler posts exactly once per slot per day; run --catchup after the machine was off for a while to fill missed slots for today. scripts/catchup.ps1 wraps it. When registering a task, set its Start in folder to your ttclips project dir (or define TTCLIPS_HOME) so config/ and data/ resolve.

Configuration

  • config/channels.json — channels to monitor. Fields per channel: name, channel_id, enabled, keywords (empty = accept all uploads), max_moments (how many clips to post per video, capped at 5).
  • config/posting_window.json — the 8 daily slots (time, type, and a hint for generated inspiration).
  • config/tiktok.json — browser/headless/delay/retry settings, default hashtags, caption template. Uploads run in an isolated subprocess with retries; waits grow exponentially (retry_wait_initial_sec, retry_wait_backoff, retry_wait_max_sec) after every failure. user_agent must stay a recent desktop Chrome UA (the library default Chrome/58 gets TCP-reset by TikTok's WAF); upload_timeout_sec gives page loads their budget.
  • .env — secrets + models + styling:
    • GEMINI_MODEL + GEMINI_FALLBACK_MODELS (comma list) — tried in order; the runtime probes candidates at startup and promotes the healthiest model
    • GEMINI_RETRY_TIMEOUT — hard cap on retrying a stuck model
    • WHISPER_MODEL / WHISPER_DEVICE, TTS_VOICE, COOKIES_FILE
    • PEXELS_API_KEY — stock-video b-roll search for generated clips (free key)
    • caption knobs: CAPTION_FONT_SIZE (96), CAPTION_ALIGNMENT (5), CAPTION_MARGINV, CAPTION_OUTLINE, CAPTION_SHADOW, CAPTION_WORDS_PER_LINE, HIGHLIGHT_COLOUR (yellow), IDLE_COLOUR (grey)

Captions

Captions are big (96pt), dead-centered, and karaoke-style: the word currently being spoken is bold in the highlight colour while the rest of the line stays grey. Repost clips use real whisper word timestamps; generated clips re-transcribe their TTS narration with faster-whisper so the karaoke is word-synced too, in exactly the same style as reposts.

State

  • data/queue.json — discovered videos awaiting processing
  • data/posted.json — dedup history (never reposts the same moment twice)
  • data/downloads/ — merged mp4s (reused on retry, no re-download)
  • data/clips/ — rendered vertical clips
  • data/broll/ — cached stock backgrounds for generated clips
  • data/logs/ttclips.log — full log

Resilience

  • Gemini 503/429 overload: the probe picks a healthy model at startup and the fallback chain walks through remaining models; retries use jittered backoff bounded by GEMINI_RETRY_TIMEOUT. Older flash models (2.x) return 404 for new free-tier keys — use the ones listed in .env.example.
  • YouTube bot-checks: intermittent "sign in to confirm you're not a bot". The downloader fails loudly; merging is handled by the winget ffmpeg install.
  • Partial failures: a failed Gemini post step doesn't waste a 150 MB download — the cached merged mp4 is reused.
  • B-roll (generated clips): backgrounds come from the free Pexels Videos API (PEXELS_API_KEY), searched with Gemini-chosen keywords, dimmed and cropped to 9:16. If the search, download, or render fails the clip falls back to an animated gradient backplate, so a slot never dies.

Notes & risks

  • Copyright: only repost content you have rights to. Clipping public channels without permission can violate YouTube ToS / creators' rights. Prefer channels that allow embedding/repurposing, or lean on the generated pipeline.
  • TikTok bot detection: keep headless: false; the human-like delays in config/tiktok.json help. If TikTok serves a CAPTCHA the run fails loudly.
  • UI changes: selectors in the TikTok uploader may break; pin versions.
  • Free tier limits: the Gemini free tier handles dozens of calls/day — fine for a few daily clips.

Metadata

Release files for ttclips 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ttclips 1.0.0
File Size Uploaded
ttclips-1.0.0.tar.gz 43.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ttclips 1.0.0
File Interpreter ABI Platform
ttclips-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 88.5 kB

Release files / ttclips-1.0.0.tar.gz

Download URL ttclips-1.0.0.tar.gz
Size 43.0 kB
Tags Source
SHA-256 checksum
How to use checksums
2d36603b029405b49111ce6e26a9485677e4a6205caf1ab23fa0698cef5ac452
BLAKE2b-256 checksum
How to use checksums
10d13c1bdde1280f2e66b1ed028c446da2ed9632758a05c4bececbe0d6ec34b0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / ttclips-1.0.0-py3-none-any.whl

Download URL ttclips-1.0.0-py3-none-any.whl
Size 45.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ceda14dbd5858de3ccc58593d0b09ec3b12dd324ee624b62255207047e0aa173
BLAKE2b-256 checksum
How to use checksums
bab85341b7deb7ad1d255e3dcf80646412c08175c5e96598b929cdc19ec47921
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release history Release notifications | RSS feed

1.0.2

2 release files

1.0.1

2 release files

This release

1.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page