MyGo 云音乐
用 Go 与 MyGo 写成的第三方网易云音乐桌面客户端。原生界面,没有 WebView;边下边播,无损与 Hi-Res,逐字歌词与「律动」,刘海播放器。
Translation pending
Turn long videos into viral short-form reels with AI.
MediaPython · TypeScriptGitHub @itsmenikeshraj99OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
Upload a long video, get back 9:16 vertical reels ready for TikTok / Reels / Shorts —
auto-detected scenes, AI-picked highlights, captioned, reframed, and stored.
A three-tier application that turns long-form video into short, shareable reels using
Google Gemini for planning + review and ffmpeg + OpenCV for the heavy media work.Read the full README ↗Upload a long video, get back 9:16 vertical reels ready for TikTok / Reels / Shorts — auto-detected scenes, AI-picked highlights, captioned, reframed, and stored.
A three-tier application that turns long-form video into short, shareable reels using Google Gemini for planning + review and ffmpeg + OpenCV for the heavy media work.
┌──────────────┐ auth'd REST ┌──────────────┐ X-Worker-Secret ┌──────────────┐
│ Next.js 16 │ ────────────────▶ │ FastAPI API │ ──────────────────▶ │ FastAPI Work │
│ (frontend) │ │ (backend) │ │ (worker) │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
│ Supabase Auth (JWT) │ Supabase Service Role │
└──────────────────────────────────┼────────────────────────────────────┘
▼
┌──────────────────────┐
│ Supabase (Postgres │
│ + Auth + Storage) │
└──────────────────────┘
│
▼
┌──────────────────────┐
│ Google Gemini API │
│ (transcribe / plan │
│ / review captions) │
└──────────────────────┘
aresample=async=1 to fix any A/V driftlocalStorage (reelgen-theme), Sun/Moon toggle in the nav. Light mode is fully styled; dark mode is the default and visually unchanged.| Where | Asset | Source |
|---|---|---|
| Browser tab (favicon) | src/app/icon.png (32×32, 4 KB) | Next.js auto-detected |
| iOS home screen | src/app/apple-icon.png (192×192, 35 KB) | Next.js auto-detected |
| Social share preview (OG / Twitter) | src/app/opengraph-image.png (1200×630, 254 KB) | Next.js auto-detected |
| AppShell nav (32×32 icon + "Reel Generator" text) | public/android-chrome-192x192.png | referenced in src/components/AppShell.tsx |
| Landing-page hero (180×180) | public/logo-1024x1024.png | referenced in src/app/page.tsx |
All 7 logo variants live under frontend/public/ and frontend/src/app/. The
high-res master is public/reel_generator_logo.png (2396×2582, palette mode
— keep as source of truth; the others are sized/optimized derivatives).
To rebrand, drop a new public/reel_generator_logo.png, then re-derive
logo-1024x1024.png + android-chrome-192x192.png + apple-touch-icon.png
favicon-40x40.png + og-image.png (and copy the favicon/apple/og into
src/app/). No code changes needed — the references are by filename.The system has three independently deployable services, all driven by a Postgres
jobs row that the worker reads as a state machine.
| Stage | What happens | Where |
|---|---|---|
VALIDATING | ffprobe codec / duration / size; reject junk | worker |
TRANSCRIBING_PLANNING | Gemini Call #1 — transcribe + propose 3–5 candidate edit plans | worker |
REVIEWING | Gemini Call #2 — review each candidate, re-roll on feedback | worker |
RENDERING | scene-detect → reframe → stitch → upload to Supabase Storage | worker |
READY | Terminal state; frontend shows the gallery | — |
The pipeline is resumable: if the worker crashes mid-run, the next invocation
picks up at current_stage instead of redoing the whole thing. Each stage is
wrapped in a bounded retry (MAX_RETRY_ATTEMPTS, default 3) with exponential
backoff. After exhaustion, the job is marked PERMANENTLY_FAILED so the UI can
distinguish it from a transient crash.
The backend is a small, low-latency HTTP API (FastAPI) that:
videos rowsThe worker is a long-running, CPU/GPU-heavy process that:
X-Worker-Secret) so the
backend — and only the backend — can submit workThis split means the worker can be moved to a GPU box, scaled horizontally, or taken offline for maintenance without ever touching the user-facing API.
:root (light) and
[data-theme="dark"] (dark). Tailwind colors (bg, surface, surface-2,
border, text, text-muted, text-subtle) reference those variables,
so swapping themes is a single attribute flip on <html>.<head> script reads
localStorage.getItem('reelgen-theme') and sets data-theme before first
paint, so there's no flash of dark/light on reload.reelgen-theme ∈
{"light","dark"}, default dark); useTheme() reads + writes it..
├── backend/ FastAPI API (auth, signed URLs, job handoff)
│ ├── app/
│ │ ├── routers/ videos.py, reels.py
│ │ ├── services/ auth.py, supabase.py, storage.py, tasks.py
│ │ ├── models/ pydantic schemas
│ │ └── main.py
│ └── requirements.txt
├── worker/ FastAPI worker (pipeline runner)
│ ├── worker/
│ │ ├── stages/ validate, transcribe_plan, review, reframe, stitch, caption
│ │ ├── gemini/ client + prompts + schemas
│ │ ├── services/ supabase.py, storage.py
│ │ ├── pipeline.py state machine
│ │ └── main.py HTTP entry point
│ └── requirements.txt
├── frontend/ Next.js 16 (App Router) + Tailwind + Supabase SSR
│ ├── public/ logo + favicon + OG variants (see Branding above)
│ ├── src/
│ │ ├── app/ auth, upload, upload/status, upload/gallery, dashboard,
│ │ │ icon.png, apple-icon.png, opengraph-image.png
│ │ ├── components/ AppShell, AuthModal, Toast, Skeleton, EmptyState,
│ │ │ ProgressBar, UploadDropzone, ReelCard, VideoPreview,
│ │ │ ShareMenu, ThemeToggle
│ │ ├── hooks/ useTheme
│ │ └── lib/ api.ts (typed client), supabase.ts, cn.ts
│ ├── next.config.js CSP / X-Frame-Options / referrer-policy
│ └── package.json
├── db/
│ └── schema.sql Supabase tables, indexes, RLS policies — paste in SQL Editor
├── .env.example files (one per service, never commit .env)
└── README.md
Prereqs: Python 3.11+, Node 20+, ffmpeg + ffprobe on
PATH, a Supabase project, a Google Gemini API key.
git clone https://github.com/itsmenikeshraj99/Reel_Generator_V2.0.git
cd Reel_Generator_V2.0
SUPABASE_URLservice_role key (⚠️ backend/worker only — never expose to the browser) → SUPABASE_KEYanon key (frontend-safe) → NEXT_PUBLIC_SUPABASE_ANON_KEYSUPABASE_JWT_SECRETdb/schema.sql. It's idempotent
(create … if not exists, create index … if not exists).reels-videos (public read OR
use the signed-URL flow described in db/schema.sql comments).cp backend/.env.example backend/.env
cp worker/.env.example worker/.env
cp frontend/.env.example frontend/.env.local
Fill them in with the values from step 2. Generate a fresh
WORKER_SHARED_SECRET — both backend and worker must use the same value:
python -c "import secrets; print(secrets.token_urlsafe(48))"
Three terminals, one per service.
Backend (port 8000)
cd backend
python -m venv venv && source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000
Worker (port 8001)
cd worker
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
# ffmpeg + ffprobe must be on PATH (worker re-encodes)
python -m worker.worker.main
Frontend (port 3000)
cd frontend
npm install
npm run dev
Open http://localhost:3000, sign up, and upload a short MP4 (≤ 500 MB by default;
raise MAX_VIDEO_SIZE_MB in backend/.env if you need more headroom).
Grab the access token from your browser's devtools:
// In the browser console, after signing in:
(await supabase.auth.getSession()).data.session.access_token;
Then:
TOKEN="paste-token-here"
# 0) List the caller's videos (for the dashboard)
curl http://localhost:8000/api/videos \
-H "Authorization: Bearer $TOKEN"
# 1) Get a signed upload URL
curl -X POST http://localhost:8000/api/videos/upload-url \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"filename":"test.mp4"}'
# 2) Upload the file to the signed URL (or use the frontend, which does this for you)
# 3) Kick off processing
curl -X POST http://localhost:8000/api/videos/$VIDEO_ID/process \
-H "Authorization: Bearer $TOKEN"
# 4) Poll status
curl http://localhost:8000/api/videos/$VIDEO_ID/status \
-H "Authorization: Bearer $TOKEN"
# 5) Once READY, list reels
curl http://localhost:8000/api/reels/$VIDEO_ID \
-H "Authorization: Bearer $TOKEN"
Unauthenticated calls return 401 — that's the point.
backend/.env)| Var | What |
|---|---|
SUPABASE_URL | Project URL |
SUPABASE_KEY | service_role key |
SUPABASE_JWT_SECRET | For verifying user JWTs |
STORAGE_BUCKET | Default reels-videos |
GEMINI_API_KEY | Google AI Studio |
GEMINI_MODEL | gemini-2.5-flash (or another supported model) |
MAX_VIDEO_SIZE_MB | Upload size cap, default 500 |
FRONTEND_ORIGIN | CORS allow-list, e.g. http://localhost:3000 |
WORKER_URL | Where to send processing jobs |
WORKER_SHARED_SECRET | Must match worker's secret |
worker/.env)| Var | What |
|---|---|
SUPABASE_URL | Same as backend |
SUPABASE_KEY | service_role key (worker has no user context) |
SUPABASE_JWT_SECRET | Not strictly required for the worker, kept for parity |
STORAGE_BUCKET | reels-videos |
GEMINI_API_KEY | Same key |
GEMINI_MODEL | Same model |
WORKER_SHARED_SECRET | Must match backend's |
WORKER_HOST | Default 127.0.0.1; only set 0.0.0.0 behind a trusted proxy |
WORKER_PORT | Default 8001 |
MAX_RETRY_ATTEMPTS | Per-stage retry count, default 3 |
frontend/.env.local)| Var | What |
|---|---|
NEXT_PUBLIC_SUPABASE_URL | Project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY | anon key (safe to expose) |
NEXT_PUBLIC_API_URL | Backend base, e.g. http://localhost:8000/api |
Three layers, each independently necessary:
Authorization: Bearer …. The backend verifies it with auth.get_user(token)
before touching any user data. The frontend also has middleware on
/upload/* that bounces unauthenticated users back to the landing page.require_worker_secret dependency on
/process that compares X-Worker-Secret to WORKER_SHARED_SECRET using a
constant-time compare. The worker binds to 127.0.0.1 by default; flip to
0.0.0.0 only behind a reverse proxy that already authenticates the caller.service_role key bypasses RLS, so the
backend explicitly filters every query by current_user.id. The frontend
uses the anon key, which respects RLS — the user can only see their own
rows.Plus:
*.X-Frame-Options: DENY, X-Content-Type-Options: nosniff,
Referrer-Policy: strict-origin-when-cross-origin, and
Permissions-Policy: camera=(), microphone=(), geolocation=() are set in
frontend/next.config.js..env.example files document the contract;
real .env files are git-ignored.backend/.env and worker/.env. Rotate
the moment you suspect exposure (e.g. pushed to a public repo).# Backend
cd backend
.\venv\Scripts\activate
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000
# Open http://localhost:8000/docs
# Worker (separate terminal)
cd worker
.\venv\Scripts\activate
pip install -r requirements.txt
python -m worker.worker.main
# Frontend (separate terminal)
cd frontend
npm install
npm run dev
# Open http://localhost:3000
End-to-end:
| Symptom | Likely cause |
|---|---|
401 on every backend call | SUPABASE_JWT_SECRET mismatch, or anon key in .env.local |
401 on /api/videos/.../process from backend | WORKER_SHARED_SECRET mismatch |
Worker stuck at VALIDATING | ffprobe not on PATH |
RuntimeError: ffmpeg failed (rc=…): Error reinitializing filters! | Filter graph bug — reframe/stitch, see logs |
Pipeline runs but edit_plans are all rejected | Reviewer too strict; cap-reached fallback accepts the best by hook_score and the pipeline still completes |
UnicodeEncodeError from worker on Windows | Run with PYTHONIOENCODING=utf-8 and python -X utf8 |
Frontend build fails on AlertCircle not defined | Older import — make sure you have the latest frontend/src/app/upload/gallery/page.tsx |
transcripts.words is null, captions skipped | Expected for some videos — captions require overlapping word timings; warning, not error |
The project ships with everything needed to deploy the backend and
worker to Railway and the frontend to
Vercel (free tier, no card required). Earlier
versions targeted Google Cloud Run; the Dockerfiles are unchanged, so
swapping back is a config-only migration — see
deploy/SETUP.md.
Cost: $0–5/mo for a demo / portfolio deployment (1–5 videos/day). The Railway $5 trial credit usually covers this exactly.
# 1. Read the step-by-step guide
open deploy/SETUP.md
| File | What |
|---|---|
deploy/SETUP.md | From zero Railway/Vercel accounts → first deploy |
deploy/SMOKE_TEST.md | End-to-end test of the deployed stack |
deploy/env.prod.example | Env vars template (paste into Railway/Vercel) |
vercel.json | Vercel config (auto-deploys from main) |
PRs welcome. Please:
.env files or any secrets.requirements.txt and package.json pinned..env.example.MIT — see LICENSE.
UI-only polish. No backend, worker, or schema changes.
AuthModal logo
Sparkles icon header with the app logo
(/android-chrome-192x192.png) wrapped in a primary → secondary
gradient ring. Glass inner surface so the logo pops on both light
and dark themes. Drop the now-unused Sparkles import.Dashboard per-card delete
DashboardCard is now stateful and owns its delete modal +
isDeleting state. Trash button: top-right of the 9:16 area
(top-3 right-12, just left of the always-visible reel-count
badge at top-3 right-3 so the two don't overlap), dark glass
pill, revealed on hover (group-hover:opacity-100).<button>, not inside the <Link>) with
stopPropagation so clicking it doesn't navigate to the status
page. Reachable via keyboard (focus:opacity-100).AlertTriangle icon, Cancel + red "Yes, Delete"
buttons. Shows the filename being deleted. Fires a "Video
deleted ✓" toast on success.api.deleteVideo endpoint as the gallery (tier-1
instant kill: original upload + every reel + videos row).DashboardClient now exposes a handleDeleted callback
that drops the row from local state and decrements total. No
refetch — the DELETE response is the source of truth.reel_generator_logo.png (lightning-bolt + "REEL GENERATOR" wordmark) and 6 sized variants under frontend/public/ and frontend/src/app/icon.png (32×32 favicon), apple-icon.png (192×192 iOS), and opengraph-image.png (1200×630 OG) — no metadata.icons config needed for themmetadata.icons added in layout.tsx as a defensive fallbackUI-only refactor (no backend or worker changes). Adds day/night theming, a name field in sign-up, and consolidates the dashboard around videos.
Loading bug fix
api.request() now wraps every fetch in an AbortController with an
8s timeout; stuck requests surface a "Request timed out" error instead
of an infinite skeleton.videos array before the redirect
so the error state is visible during the bounce (no flash of stale
skeleton).Theme system (PR 2-3)
globals.css defines semantic CSS variables on :root (light) and
[data-theme="dark"] — --bg, --surface, --surface-2,
--border, --text, --text-muted, --text-subtle, --primary,
--secondary, --gradient-bg.tailwind.config.js adds token-backed color names so future classes
are theme-aware by default.layout.tsx ships an inline <head> script that reads
localStorage['reelgen-theme'] and stamps data-theme on <html>
before first paint (no flash of dark/light on reload).useTheme() (src/hooks/) is a client hook reading/writing the
preference; <ThemeToggle> is a Sun/Moon button wired into
AppShell (desktop nav + mobile drawer).dark; both themes are fully styled.AuthModal redesign (PR 4)
name (only on sign-up; sign-in keeps
email + password). The name is passed to Supabase as
data: { full_name } and lands in user_metadata.user_metadata.full_name → falls back to
user_metadata.name → email local-part in the user menu.Dashboard consolidation (PR 5)
Gallery "Generate New Reels" (PR 6)
isGenerating flag prevents double-submit; backend's 409
guard on /videos/{id}/process is the safety net.Semantic token swap (PR 7)
text-white, text-gray-300/400/500, bg-white/5/10,
border-white/10/20, bg-dark) → token-backed classes
(text-text, text-text-muted, text-text-subtle, bg-black/5/10,
border-border, bg-bg).text-white on
gradient), color-coded status badges (*-500/15 + *-300),
thumbnail overlay badges (bg-black/60 backdrop-blur), and the
VideoPreview Play icon (text-white/90 on dark glass).The user-facing rebuild. No backend semantics changed (one new endpoint
plus a signed_upload_url on the existing POST /api/videos/upload-url).
Backend
GET /api/videos?limit&offset — list the caller's videos with reel
counts and latest job stage for the dashboard.POST /api/videos/upload-url now returns signed_upload_url +
upload_token so the client can XHR.PUT the file with real progress
events.VideoListItem, VideoListResponse,
SignedUploadInfo.Frontend — new components
AppShell — top nav (sticky, backdrop-blur) + mobile drawer
(<768px); auth via supabase.auth.onAuthStateChange.Toast + useToast — 3-variant notification system (success /
error / info), 4s/6s TTL, z-100, manual close.Skeleton — text/rect/circle placeholders for loading states.EmptyState — icon + body + CTA.UploadDropzone — 4-state drag-drop (idle / drag-over / selected /
error).ProgressBar — value (0-100) + indeterminate modes; accessible.ReelCard + VideoPreview — extracted from the gallery: 9:16 frame
with gradient + Play icon, video cross-fades in on hover.ShareMenu — popover with Copy Link / Twitter / LinkedIn / WhatsApp;
uses native <details> so no outside-click handler needed.Frontend — new pages
/dashboard — list the user's videos with status badges, reel counts,
and "Xh left" countdowns. Server-component shell + client
DashboardClient + error boundary.Frontend — refactored pages
/ split into landing (logged-out) / bounce-to-dashboard (logged-in)./upload wrapped in AppShell; switches to XHR PUT upload with a
live progress bar./upload/status wrapped in AppShell; fires a toast on READY,
adds an "Open Dashboard" link./upload/gallery wrapped in AppShell; uses ReelCard + ShareMenu;
delete-confirm modal z-index lowered from 50 → 40 so toasts sit above
it.not-found page wrapped in AppShell with dual "Go home" /
"Browse dashboard" CTAs.AuthModal redirects new sign-ups to /auth/callback?next=/dashboard.Other
toast-in, pulse-stage, gradient-shift.See the commit history for the full changelog of the auth + pipeline + deploy work (Phases 1–10).
Built with ❤️ by itsmenikeshraj99