Manage course video publishing workflows by editing metadata, generating descriptions
翻譯準備中
AI 摘要 · README
媒體TypeScript · ShellGitHub @mattpocock自行安裝營運者未確認
試用過了嗎? 點一下就好,不需要登入。
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
安裝型專案無需部署網域也可驗證。個人儲存庫可透過 GitHub 登入驗證,組織儲存庫可使用上方的儲存庫驗證檔案。
正在載入完整 README。 以下為已儲存的摘錄。
A tool for managing course video publishing workflows — editing metadata, generating descriptions, creating thumbnails, and posting to social platforms.
A Turborepo monorepo over the pnpm workspace:
packages/core has no filesystem access, no child_process and no git
coupling, so it can be deployed as well as run locally. pnpm lint:boundaries
enforces that — anything that needs a machine is injected from apps/local
(see packages/core/services/diagram-thumbnail-store.ts for the shape).檢視完整 README ↗A tool for managing course video publishing workflows — editing metadata, generating descriptions, creating thumbnails, and posting to social platforms.
A Turborepo monorepo over the pnpm workspace:
| Directory | What it is |
|---|---|
apps/local | The application as it runs on the author's machine: the React Router app, the Video Editor, the Diagram Playground, the Publish flow, ffmpeg, the recorder service, and the cvm CLI |
packages/core | The domain database — the Drizzle schema, the DrizzleService and every db-* operations service. Every piece of SQL in the repo lives here |
packages/overlay-renderer | The standalone Remotion renderer for every overlay content-kind — subtitles, the CTA, and Definition Cards — with its own toolchain |
packages/core has no filesystem access, no child_process and no git
coupling, so it can be deployed as well as run locally. pnpm lint:boundaries
enforces that — anything that needs a machine is injected from apps/local
(see packages/core/services/diagram-thumbnail-store.ts for the shape).
.env lives at the workspace root: one file for the whole monorepo, which is
also where cvm looks for it (apps/local/app/cli/env.ts).
Run these from the workspace root; Turborepo fans them out and re-runs only what changed.
| Script | Description |
|---|---|
pnpm typecheck | Typecheck every package |
pnpm test | Run every suite once |
pnpm test:watch | Run every suite in watch mode |
pnpm lint:boundaries | Enforce the package boundaries |
pnpm dev | Start the local application |
pnpm build | Build the local application |
Each of these filters out @cvm/overlay-renderer: it ships its own
toolchain (Remotion, and a Chromium download) and has never been part of the
application's checks. Run it with pnpm --filter @cvm/overlay-renderer.
Vercel gets one project per deployable directory, each with its own Root
Directory, and relies on Vercel's built-in unaffected-project skipping to
decide what to deploy. There is deliberately no Ignored Build Step:
turbo-ignore is deprecated, and native skipping does not consume a concurrent
build slot. If a custom step is ever needed it is turbo query affected.
Only main deploys. apps/remote/vercel.json sets
git.deploymentEnabled to { "**": false, "main": true }, so a push to any
other branch builds nothing: no Preview Deployment for an open PR. Production
still deploys on a merge to main, because a branch that matches two rules
deploys if either one is true. The pattern is ** and not *, as * does
not match a slash and a feat/thing branch would still deploy. This is the
supported per-branch control — Vercel's dashboard has no equivalent, and the
repo takes no Ignored Build Step.
Schema changes are managed with drizzle-kit generate / migrate (versioned SQL files), not push. The schema, the migrations and the drizzle config all live in packages/core.
packages/core/db/schema.ts.pnpm db:generate — creates a new numbered .sql file under packages/core/db/migrations/.pnpm db:migrate — run by hand, against DIRECT_DATABASE_URL — before or as part of deploying apps/remote. Applying migrations used to be the deploy's job exclusively; it moved to a manual step because that ran on every Vercel build, previews included, and could land an unmerged migration on the production schema. See apps/remote/README.md and ADR 0026.Against any non-local host, pnpm db:migrate (and drizzle-kit push) refuses unless you are on a clean main at exactly origin/main — an unmerged branch once migrated production out of order. Merge first, then migrate from the main checkout. Local databases are never checked. See the ADR 0026 addendum.
Migrations are additive-only: no dropped or renamed columns without a two-step release. A cvm invocation may be in flight while a deploy lands, and it is the additive rule — not the version gate — that keeps that from breaking. The version gate refuses the box's next command, naming both migration counts and telling it to pull (packages/core/rpc/schema-version.ts).
If the database was originally created via drizzle-kit push and has never run migrations:
pnpm db:baseline
This registers the 0000 baseline migration as already-applied so the next pnpm db:migrate won't replay the initial CREATE TABLE statements.
| Script | Description |
|---|---|
pnpm db:generate | Generate a new migration from schema changes |
pnpm db:migrate | Apply pending migrations by hand (deploy no longer does this) |
pnpm db:baseline | Mark the 0000 baseline as applied (one-time setup) |
pnpm db:studio | Open Drizzle Studio |
The app uses a Dropbox → Zapier → Buffer pipeline to post videos to social media. When you click "Post to Buffer" in the app, it:
dropbox filestatus to poll sync status)| Variable | Description |
|---|---|
ZAPIER_BUFFER_WEBHOOK_URL | The webhook URL generated by your Zapier Zap (see below) |
AI_HERO_BASE_URL | Base URL for the AI Hero instance (e.g. https://www.aihero.dev). Required for AI Hero posting integration. |
ZAPIER_BUFFER_WEBHOOK_URL environment variable in your appThe webhook receives a JSON payload with this shape:
{
"caption": "Your post caption text",
"dropboxFilePath": "/full/path/to/buffer-posts/video.mp4"
}
dropboxFilePath value from the webhook payloadcaption value from the webhook payload