Spaces:
Runtime error
Runtime error
|
Download docs/BLOG2VIDEO.md from firebird-technologies/NoteStack-Test: direct link, hf CLI and curl.
- Browser
- Download file 7.33 kB
-
https://huggingface.co/spaces/firebird-technologies/NoteStack-Test/resolve/1aa4a1663ff037b16e33cea96515f0268a23545d/docs/BLOG2VIDEO.md
- Command line
-
hf download hf://spaces/firebird-technologies/NoteStack-Test@1aa4a1663ff037b16e33cea96515f0268a23545d/docs/BLOG2VIDEO.md
-
curl -L -o BLOG2VIDEO.md https://huggingface.co/spaces/firebird-technologies/NoteStack-Test/resolve/1aa4a1663ff037b16e33cea96515f0268a23545d/docs/BLOG2VIDEO.md
7.33 kB
| # blog2video integration | |
| Videos are made and edited by blog2video. Notestack is an ordinary blog2video API customer: we own one paid | |
| blog2video account (Pro, topped up with per-video credits) and the backend calls its public `/api/v1` API with that | |
| account's key. Every video is charged to our account; we charge our workspaces from our own allowance. | |
| ``` | |
| Browser βββΊ /api/videos/* (backend/app/routers/videos.py) ββ(Bearer b2v_live_β¦)βββΊ blog2video /api/v1 | |
| ββ checks the session and workspace | |
| ββ reserves / refunds the workspace's allowance (subscriptions) | |
| ββ records which video belongs to which workspace (b2v_videos) | |
| ``` | |
| The browser never talks to blog2video, and the key never reaches the browser or the logs. | |
| ## Settings | |
| Backend `.env` only (never a `VITE_` variable): | |
| | Env var | Value | | |
| |---|---| | |
| | `B2V_API_BASE_URL` | blog2video API host, no trailing slash (`B2V_API_URL` is still read as a fallback) | | |
| | `B2V_API_KEY` | `b2v_live_β¦`, from blog2video β Account β API keys on our Pro account | | |
| | `B2V_TIMEOUT_SECONDS` | create timeout (default 30); every other call uses 10s | | |
| | `B2V_CAPACITY_ALERT_BELOW` | log an `ALERT` when our account has fewer videos left (default 20) | | |
| A revoked or rotated key answers 401; a blog2video account that dropped to Free answers 403 to everything. Both | |
| log an `ALERT` line and users see "temporarily unavailable". | |
| ## Ownership | |
| Everything made with the key (videos, custom templates, custom voices, styles) belongs to one blog2video account, | |
| and blog2video's ownership checks stop at the account. Notestack is the only place each workspace's things are kept | |
| apart (`backend/app/services/b2v_access.py`): | |
| | Table | What | Used as | | |
| |---|---|---| | |
| | `b2v_videos` | videos (also the allowance ledger) | project id; `created_via` v1 or upload | | |
| | `b2v_templates` | custom templates (`ready` once generated) | `template: "custom_<id>"` | | |
| | `b2v_custom_voices` | designed and cloned voices | `custom_voice_id: "<voice_id>"` | | |
| | `user_saved_voices` | "My voices" (kept only here) | the wizard's voice list | | |
| | `b2v_styles` | custom video styles | `video_style: "custom:<id>"` | | |
| | `usage_counters` | per-workspace counters (below) | | | |
| Every route that takes one of these ids checks it is the workspace's (404 otherwise), and every create or edit body | |
| has its `template`, `video_style` and `custom_voice_id` checked before it is forwarded. Lists come from these tables. | |
| Account-wide endpoints (`POST /api/projects`, `/api/projects/bulk`, `GET /api/projects`, `/api/voices/saved*`, | |
| built-in style overrides, style pin/selection, "Your Style", script preferences) are refused in | |
| `blog2video.DENIED` before anything is sent. | |
| ## Routes | |
| | Notestack | blog2video | | |
| |---|---| | |
| | `POST /api/videos` (link, text, a post) | `POST /api/v1/videos` with `Idempotency-Key: <workspace>:<uuid>` | | |
| | `POST /api/videos/upload` (β€5 files) | `POST /api/projects/upload` then `/generate` | | |
| | `POST /api/videos/batch` (β€10 links) | one `POST /api/v1/videos` per link | | |
| | `GET /api/videos/{id}/status` | `/api/v1/videos/{id}/status`, or `/api/projects/{id}/status` for uploads | | |
| | `/api/videos/{id}/script*` | `/api/projects/{id}/script-review/*` | | |
| | `/api/videos/{id}/p/<path>` | `/api/projects/{id}/<path>`, allowlisted in `routers/video_edit.py` | | |
| | `/api/video-voices/*` | `/api/voices/design-from-*`, `/api/voices/custom*`, `/api/voices/clone`, `/api/voice/preview` | | |
| | `/api/video-templates/*` | `/api/custom-templates/*` (editor paths allowlisted) | | |
| | `/api/video-styles/*` | `/api/video-styles/ai-draft`, `/api/video-styles/custom*` | | |
| The live preview link comes from `POST /api/embed/token/{id}` and is safe for the browser. | |
| ## Premium and per-workspace limits | |
| blog2video checks every limit against our Pro account, so every workspace passes its checks. Notestack divides them: | |
| each plan row has `video_premium` (β : detailed lengths, paid and custom voices, advanced voice tuning, voice samples, | |
| AI script rewrites, AI chat, avatars, designer templates, the source download) and `video_limits`: | |
| | Metric | Period | Free / Writer / Studio | Counts | | |
| |---|---|---|---| | |
| | `ai_edits` | month | 20 / 300 / 1000 | scene regenerate/add, AI image, stock clip, AI chat, AI rewrites, template AI edits, avatars (10 per scene) | | |
| | `templates` | lifetime | 0 / 2 / 5 | custom templates made (blog2video slots are never given back) | | |
| | `template_ai_daily` | day | 0 / 3 / 5 | theme from a document or prompt, code generation | | |
| | `voice_designs_daily` | day | 0 / 5 / 10 | voice designs | | |
| | `voice_samples_daily` | day | 0 / 20 / 40 | voice samples | | |
| | `custom_voices` | total | 0 / 3 / 10 | kept or cloned voices (a delete frees one) | | |
| Edit a plan row to change them. A counter is given back when blog2video refuses the call. A template switch also | |
| uses a video. | |
| ## Quota | |
| One row per workspace in `subscriptions`, enforced only by us (`backend/app/services/video_quota.py`): | |
| | Column | Meaning | | |
| |---|---| | |
| | `video_plan` | effective plan id (`free`, `writer`, `studio`) | | |
| | `video_limit` | videos per period, from the plan | | |
| | `videos_used` | videos made this period | | |
| | `videos_period_start` | start of the current period | | |
| - **Reserve** before calling blog2video: `videos_used + 1` only while below `video_limit`, in one `UPDATE`. At the | |
| limit we answer 402 `plan_limit` and blog2video is never called. | |
| - **Refund exactly once** (conditional `UPDATE b2v_videos SET quota_state = 'refunded' WHERE quota_state = 'charged'`) | |
| when the create is refused (402, 422, key/plan problems), gets no answer after 3 tries with the same | |
| `Idempotency-Key`, or generation fails (status `failed`/`error`, or 404). | |
| - The worker's `video_refund_sweep` (every 10 min) checks charged, unfinished videos older than 30 minutes, for users | |
| who closed the tab. `video_capacity_check` (hourly) reads `GET /api/v1/me` and alerts when we run low. | |
| - Resets: Stripe `invoice.paid` with `billing_reason` `subscription_cycle` or `subscription_create`; otherwise the | |
| hourly `video_period_reset` resets any period older than a month (free, billing disabled, annual plans). | |
| A 402 `quota_exceeded` from blog2video means **our** account is out of videos, not the user: they are refunded and | |
| see "temporarily unavailable"; buy credits. Our users' limits added up can exceed the account, so watch the alert. | |
| ## Videos made before the switch | |
| Videos made through the old partner integration belonged to a blog2video service account that is now deactivated; | |
| the key gets 404 for them. They have no `b2v_videos` row, so the API treats them as legacy: `GET` returns the stored | |
| `video_url`/`preview_url` with `legacy: true`, and edits answer 410. blog2video can move them to our account on | |
| request if we ever need them editable. | |
| ## Clean-up after the switch | |
| The partner-token code and settings (`B2V_PARTNER_SLUG`, `B2V_PARTNER_SECRET`) are gone. blog2video used to connect | |
| to our database, so drop that access: | |
| ```sql | |
| REVOKE ALL ON public.subscriptions FROM b2v_partner; | |
| DROP VIEW IF EXISTS b2v_quota; | |
| DROP ROLE IF EXISTS b2v_partner; | |
| ``` | |
| and remove blog2video's server IP from the database firewall. | |
| ## Local development | |
| Set `B2V_API_BASE_URL` and a test key. Unit tests (`backend/tests/test_videos.py`) use a fake blog2video. | |