Slidefarm
TikTok auto-publisher — upload an image collection, set a schedule, pick a caption model. A 5-minute orchestrator keeps a 2-post buffer of slideshows ready, then publishes each at its exact slot via Cloud Tasks.
Slidefarm is a posting machine. You wire your TikTok account, upload a collection of niche images, configure a posting schedule (day + time + timezone) and pick a caption model. From there a 5-minute Cloud Scheduler job runs the orchestrator, which keeps every active automation topped up with two prepared posts and dispatches a second Cloud Task to publish at the exact scheduled time via the TikTok Content Posting API v2.
Captions are AI-generated across three providers (OpenAI, xAI, Gemini), rendered with TikTok Sans on @napi-rs/canvas, compressed with sharp, and uploaded to Firebase Storage before the publish step.
Two distinct execution paths: a recurring orchestrator (every 5 min) that prepares posts, and an exact-time Cloud Task that publishes them. Decoupling preparation from publishing means slow caption generation never delays a scheduled drop.
lib/
└── features/
├── automations/ Schedule config (day/time/timezone)
├── billing/ Credits + plan tier (Riverpod creditsInfoProvider)
├── collections/ Image management
├── tiktok/ OAuth flow
├── auth/, settings/
functions/src/
├── enqueuePendingAutomations.ts Scheduler · 5-min interval
│ → enqueues Cloud Tasks per automation
├── processAutomationTask.ts Cloud Task target
│ → generates slides, renders captions, schedules publish
├── publishSinglePost.ts Exact-time Cloud Task target
│ → uploads slides to TikTok
├── services/
│ ├── imageService.ts @napi-rs/canvas text rendering
│ ├── textProviderService.ts Multi-provider LLM call with retry
│ ├── tiktokService.ts OAuth · Posting API v2
│ ├── creditService.ts Balance + transaction log
│ └── rateLimiter.ts Token bucket for TikTok ~10 req/min
└── analytics/ 6-hour cron: scrape view counts
Firestore:
users/*/automations Active schedules
users/*/scheduledPosts status: prepared | preparing | posted | failed
Automations
One automation = one image collection + one TikTok account + one schedule + one caption model. Multi-account supported via separate automations.
Buffered scheduling
Always keeps 2 posts prepared. If buffer drops below 2, the orchestrator dispatches a Cloud Task to top it up.
Multi-provider AI
Pick from 5 models across OpenAI, xAI, Gemini. Retry logic with exponential backoff handles rate limits and rotates between providers.
TikTok-native rendering
Captions rendered as multi-block overlays with TikTok Sans + Noto Color Emoji on @napi-rs/canvas, compressed with sharp.
Credit system
Daily caps per plan (3 → 50 posts). Credits debit on preparation (not publish). Legacy unlimited grandfathered as credits = -1.
Analytics sync
Separate 6-hour cron scrapes TikTok video metrics so creators see view counts in-app.
- 01
Orchestrator fires
Every 5 min:
enqueuePendingAutomationsqueries active automations, counts prepared/preparing posts. - 02
Top up buffer
If buffer < 2: create a Cloud Task targeting
processAutomationTask. Hourly task naming (automationId-YYYYMMDDH) prevents duplicates. - 03
Prepare
Validate credits → fetch images → generate captions via selected model → render text overlays on @napi-rs/canvas → sharp compress → upload to Storage.
- 04
Schedule publish
Save post with
status="prepared", setscheduledPublishTimeto next matching slot (±60 s tolerance), deduct 1 credit. - 05
Exact-time publish
Second Cloud Task targets
publishSinglePost. Decrypt TikTok tokens → callinitializePhotoPost→ upload slides → pollfetchPublishStatus. - 06
Confirm + notify
Mark
status="posted"; success notification. Failed publishes flip to"failed"with reason for retry UX.
Cloud Tasks for exact-time publish
Decouples slow caption generation (≤9 min budget) from precise publish timing. Scheduled Cloud Tasks fire to the second; preparation never blocks a drop.
Distributed token-bucket rate limiter
rateLimiter.ts respects TikTok's ~10 req/min limit per app across all users — global throttle, no per-user contention.
Snowflake-safe JSON
TikTok video IDs are 15+ digit Snowflakes. A custom JSON parser preserves them as strings to prevent JS Number precision loss.
Schedule-change invalidation
If a user changes the schedule, prepared posts that no longer match get marked stale. Stale "preparing" posts older than 10 min auto-fail.
Encrypted OAuth tokens
TikTok access + refresh tokens encrypted at rest in Firestore; decrypted only inside the publish function.
Multi-provider resilience
3-attempt retry with exponential backoff on rate limits; can rotate providers per attempt so an outage at one vendor doesn't kill the queue.
Architectural choices that traded simplicity for resilience.
Cloud Tasks for exact-time publish
Second-precise publishing without a per-minute cron polling loop. Cost: harder to reason about across failures — debugging requires tracing both the orchestrator and the Cloud Task target.
Buffered preparation over JIT generation
Keeping 2 prepared posts ahead of schedule absorbs caption-generation outages without missing a drop. Cost: storage overhead and stale-buffer cleanup logic when schedules change.
Multi-provider over single-vendor
OpenAI / xAI / Gemini means no single rate-limit or outage kills the queue. Cost: prompt drift between providers and a larger surface area for caption-quality regressions.
Global token bucket vs per-user
Global throttle respects TikTok's ~10 req/min limit with no per-user contention. Cost: one heavy user could degrade throughput for everyone — would need fairness scheduling at scale.
- 3 daily posts
- 1 active automation
- 1 credit / month
- Starter: 10 daily · 3 automations
- Growth: 20 daily · 10 automations
- Scale: 35 daily · 20 automations
- Unlimited: 50 daily · 50 automations · -1 credits
Stage: Live. Credit transactions logged with type (subscription_grant, post_deduct, renewal_reset) for full auditability. RevenueCat webhooks reconcile subscription changes with Firestore credits.
The patterns here are exactly what serious enterprise integrations need.
Cloud Tasks scheduling is structurally identical to Mendix scheduled events under load, where reliability and observability matter more than convenience. Encrypted-at-rest OAuth tokens are the same primitive enterprise SSO and Azure AD integrations require. Multi-provider failover with retry semantics is the same pattern that keeps SAP/REST integrations running when an upstream endpoint flaps. Custom JSON parsing for Snowflake-style 64-bit IDs is the kind of integer-precision detail that separates a working integration from a broken one in production.