KangKlip is a GPU-first short‑clip generator. Paste a long‑form video URL and get 1–5 vertical clips (30/45/60s) with titles and download links. The system is designed for speed and reproducible artifacts: jobs are isolated, outputs are stored under a fixed R2 prefix, and manifests keep every stage auditable.
Next update: we are integrating an AI video creator for text‑to‑video and image‑to‑video workflows, supporting outputs up to 1 minute.
- Demo Video
- Quick Start
- Services
- Why It Stands Out
- Architecture at a Glance
- End-to-End Workflow
- API Surface
- Web3 Credits (Solana)
- Pricing & Credit Policy
- Authentication & Tokens
- Frontend Routes
- Storage Layout (R2)
- Job State Lifecycle
- Backend
- Worker
- Demo Workflow (Local)
- Production Notes
- Troubleshooting
- Definition of Done
- Frontend
# 1) Configure env
cp .env.example .env
# fill in required variables
# 2) Start Redis (if not already running)
redis-server --daemonize yes
# 3) Run backend
cd backend
npm install
npm run dev
# 4) Run frontend
cd ../frontend
npm install
NEXT_PUBLIC_API_BASE=http://localhost:8000 npm run devbackend/Express + TypeScript orchestratorworker/GPU pipeline jobfrontend/Next.js UI
- Script‑driven, not linear. The pipeline selects highlight segments from a transcript rather than trimming the first N minutes.
- Isolated job model. Every job is stateless, runs on exactly one GPU, and writes to a fixed R2 prefix.
- Speed‑first architecture. Faster‑whisper ASR, LLM selection via API with heuristic fallback, and FFmpeg render tuned for GPU nodes.
- Clean artifact trail. Manifest + transcript + chunks + EDL are uploaded alongside clips for auditability.
User → Frontend → Backend (Express) → Nosana GPU Job → R2
↑ ↓
Redis state Callback status
- User submits a URL, duration (30/45/60), count (1–5), and language.
- Backend validates input, generates
kk_<ULID>, stores job state in Redis, and submits a Nosana run. - Worker pipeline executes:
- Download video → extract audio
- ASR transcript (faster‑whisper on GPU)
- Chunking (≈5 minute windows)
- LLM selection (external API; heuristic fallback)
- FFmpeg render to 9:16
- Upload artifacts + clips to R2
- Worker callback marks job
SUCCEEDEDorFAILED. - Frontend polls status and lists clip metadata (locked state + preview/download endpoints).
POST /api/jobs→ create a job, returns{ job_id, job_token, status }GET /api/jobs/{job_id}→ status + stage + progress + errorGET /api/jobs/{job_id}/results→ clip metadata (locked flag + endpoints)POST /api/callback/nosana→ worker completion/failure (requiresx-callback-token)POST /api/auth/challenge→ wallet challengePOST /api/auth/verify→ wallet signature verificationGET /api/credits/balance→ credits for authenticated walletPOST /api/credits/topup/usdc/intent→ build pay_usdc instructionPOST /api/credits/topup/usdc/confirm→ confirm a topup txPOST /api/jobs/{job_id}/clips/{clip_file}/unlock→ consume 1 creditGET /api/jobs/{job_id}/clips/{clip_file}/preview→ short-lived preview URLGET /api/jobs/{job_id}/clips/{clip_file}/download→ signed download URL
Notes:
POST /api/jobsnow returnsjob_token. Store it temporarily (in memory or localStorage) and send it asx-job-tokenon results/clip requests.- Authenticated credit endpoints require
x-auth-token(obtained via challenge/verify).
KangKlip uses a simple on-chain credit system on Solana. Credits live in a UserCredit PDA and do not expire.
How it works
- Wallet auth: call
/api/auth/challenge, sign the message, then/api/auth/verifyreturns anauth_token. - Top up:
/api/credits/topup/usdc/intentreturns apay_usdcinstruction payload for the on-chain program. The client signs and submits the transaction, then calls/api/credits/topup/usdc/confirmwith the signature. - Unlock:
/api/jobs/{job_id}/clips/{clip_file}/unlockconsumes 1 credit on-chain via the backend spender key. - No expiry: credits are stored on-chain and do not have an expiration.
Program
- Anchor program:
programs/kangklip_credits - Key accounts:
ConfigPDA (authority + USDC mint) andUserCreditPDA (user credits)
Transaction details
- The backend builds and submits
consume_credittransactions using theSPENDER_KEYPAIR. - Unlock requests include
unlock_request_idand are treated as idempotent (safe to retry). - Preview URLs are short-lived; download URLs are longer-lived and require an unlocked clip.
Required backend env vars (web3)
SOLANA_RPC_URLUSDC_MINTTREASURY_ADDRESSCREDITS_PROGRAM_IDSPENDER_KEYPAIR
- Generate clips: 2 credits per job (covers transcript + processing).
- Download clip: 1 credit per clip download (enforced by
/clips/{clip_file}/unlock). - Credits never expire.
Enforcement note: generation fees are a product policy. The current backend only consumes credits on unlock. If you want to enforce generation fees, gate /api/jobs behind wallet auth and consume credits before submitting the Nosana job.
- Job token (
job_token): returned byPOST /api/jobsand required asx-job-tokenfor results, preview, download, and unlock. - Auth token (
auth_token): returned by/api/auth/verifyand required asx-auth-tokenfor credit balance, topup, and unlock. - Nonce TTL: auth challenges expire in ~5 minutes.
- Auth token TTL: ~24 hours.
Frontend stores job_token in localStorage (per job). Auth tokens are kept in memory only.
/→ landing (hero + CTA + FAQ)/generate-clips→ builder form to create jobs/jobs/{job_id}→ job progress, previews, unlock, download/topup→ credit topup flow/pricing→ pricing details
jobs/{job_id}/
manifest.json
transcript.json
chunks.json
edl.json
meta.json
video_stats.json
face_log.json
clip_01.mp4
clip_02.mp4
QUEUED→ created and awaiting GPURUNNING→ worker stages (DOWNLOAD, TRANSCRIPT, CHUNK, SELECT, RENDER, UPLOAD)SUCCEEDED→ artifacts uploaded, results availableFAILED→ error captured in job state
Run locally:
cd backend
npm install
npm run devRequired environment variables (no placeholders):
NOSANA_API_BASENOSANA_API_KEYNOSANA_WORKER_IMAGENOSANA_MARKETREDIS_URLR2_ENDPOINTR2_BUCKETR2_ACCESS_KEY_IDR2_SECRET_ACCESS_KEYCALLBACK_BASE_URLCALLBACK_TOKENLLM_API_BASELLM_MODEL_NAMESOLANA_RPC_URLUSDC_MINTTREASURY_ADDRESSCREDITS_PROGRAM_IDSPENDER_KEYPAIR
Optional:
CORS_ORIGINS(comma-separated list of allowed frontend origins)
Local dev note: if your frontend runs on a non-3000 port, add it to CORS_ORIGINS.
Run in container with GPU and bundled deps. The entrypoint is worker/main.py.
Face detection uses OpenCV DNN on CPU (no CUDA required).
Build image:
cd worker
docker build \
-t kangklip-worker:latest .Required environment variables:
JOB_IDVIDEO_URLCLIP_COUNTMIN_CLIP_SECONDSMAX_CLIP_SECONDSOUTPUT_LANGUAGER2_ENDPOINTR2_BUCKETR2_ACCESS_KEY_IDR2_SECRET_ACCESS_KEYR2_PREFIXCALLBACK_URLCALLBACK_TOKEN
Optional overrides:
LLM_API_KEYLLM_TIMEOUT_SECONDSRENDER_RESOLUTION(default1080x1920)RENDER_MAX_FPS(e.g.30, used for download selection and render fps)RENDER_CRF(e.g.18)RENDER_PRESET(e.g.medium)CAPTION_FONT(defaultRoboto)CAPTION_FONT_SIZE(default scales from 68 @ 1080x1920)CAPTION_MAX_CHARS(default scales from 22 @ 1080x1920)CAPTION_MAX_LINES(default3)CAPTION_MARGIN_H/CAPTION_MARGIN_V
- Start Redis.
- Run the backend (
npm run dev). - Run the frontend (
NEXT_PUBLIC_API_BASE=http://localhost:8000 npm run dev). - Submit a job and watch progress in
/jobs/{job_id}.
- Callback auth uses
CALLBACK_TOKEN; keep it secret and rotate if exposed. - The worker image is expected to include all model weights and dependencies (no runtime installs).
- NVENC/NVDEC requires host NVIDIA driver capabilities; GPU nodes may be compute-only.
- Job stuck in QUEUED: check Nosana deployment status and whether
start_erroris set in/api/jobs/{id}. - Job RUNNING but no results: verify callback URL/token is correct and reachable from Nosana.
- No dashboard logs: ensure worker writes to stdout/stderr (see
worker/main.pylogging). - ASR failed or slow: verify GPU availability and that
faster-whisperloads on the node. - Face detection not working: check
face_log.jsonforffprobe_failedor missing model files. - Blurry clips: try lowering
RENDER_MAX_FPS, settingRENDER_CRFto 18–20, or reducingRENDER_RESOLUTION.
- URL → clips generated successfully
- Artifacts stored in R2
- Backend reports correct state
- Frontend can download outputs
- No GPU OOM on the target GPU
Run locally:
cd frontend
npm install
NEXT_PUBLIC_API_BASE=http://localhost:8000 npm run dev