Live

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.

Demonstrates Scheduling at scale with multi-provider resilience — exact-time publish via Cloud Tasks, distributed rate-limiting, encrypted token storage, queue-decoupled preparation.
5minOrchestrator interval
3LLM providers
±60sSchedule tolerance
2Buffered posts

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.

AppFlutter 3.8 · iOS + Android · Riverpod 3 · go_router
BackendFirebase — Auth · Firestore · Storage · Cloud Functions v2 on Cloud Run · Cloud Tasks · Cloud Scheduler
AI captionsOpenAI (gpt-5.4 / -mini), xAI (grok-4.20), Google Gemini (3.1 pro / flash)
Image pipeline@napi-rs/canvas (text rendering) + sharp (compression) · TikTok Sans + Noto Color Emoji fonts
TikTokContent Posting API v2 — OAuth, encrypted tokens at rest in Firestore
PaymentsRevenueCat with credit-per-post consumption model
HostingFirebase Hosting (us-central1) · slidefarm.web.app

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
01

Automations

One automation = one image collection + one TikTok account + one schedule + one caption model. Multi-account supported via separate automations.

02

Buffered scheduling

Always keeps 2 posts prepared. If buffer drops below 2, the orchestrator dispatches a Cloud Task to top it up.

03

Multi-provider AI

Pick from 5 models across OpenAI, xAI, Gemini. Retry logic with exponential backoff handles rate limits and rotates between providers.

04

TikTok-native rendering

Captions rendered as multi-block overlays with TikTok Sans + Noto Color Emoji on @napi-rs/canvas, compressed with sharp.

05

Credit system

Daily caps per plan (3 → 50 posts). Credits debit on preparation (not publish). Legacy unlimited grandfathered as credits = -1.

06

Analytics sync

Separate 6-hour cron scrapes TikTok video metrics so creators see view counts in-app.

  1. 01

    Orchestrator fires

    Every 5 min: enqueuePendingAutomations queries active automations, counts prepared/preparing posts.

  2. 02

    Top up buffer

    If buffer < 2: create a Cloud Task targeting processAutomationTask. Hourly task naming (automationId-YYYYMMDDH) prevents duplicates.

  3. 03

    Prepare

    Validate credits → fetch images → generate captions via selected model → render text overlays on @napi-rs/canvas → sharp compress → upload to Storage.

  4. 04

    Schedule publish

    Save post with status="prepared", set scheduledPublishTime to next matching slot (±60 s tolerance), deduct 1 credit.

  5. 05

    Exact-time publish

    Second Cloud Task targets publishSinglePost. Decrypt TikTok tokens → call initializePhotoPost → upload slides → poll fetchPublishStatus.

  6. 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.

Free
  • 3 daily posts
  • 1 active automation
  • 1 credit / month
Paid plans
  • 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.

Crossover to enterprise work

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.