free-short-video 7.0.6 → 7.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +44 -8
  2. package/core/api/agnes_chat.py +28 -14
  3. package/core/api/agnes_image.py +50 -25
  4. package/core/api/agnes_video.py +191 -183
  5. package/core/api/key_manager.py +79 -0
  6. package/core/api/rate_limiter.py +37 -14
  7. package/core/api/retry_policy.py +124 -0
  8. package/core/config.py +28 -35
  9. package/core/i18n_backend.py +19 -0
  10. package/core/pipelines/anchor_video.py +1 -1
  11. package/core/pipelines/creative/pipeline.py +1 -1
  12. package/core/pipelines/creative/steps_frames.py +16 -5
  13. package/core/pipelines/manuscript_video.py +1 -1
  14. package/core/pipelines/poetry_video.py +1 -1
  15. package/core/pipelines/simple_video.py +1 -1
  16. package/core/screenwriter/scenes.py +2 -1
  17. package/package.json +1 -1
  18. package/static/assets/ar-uqdQsD8d.js +1 -0
  19. package/static/assets/bn-Cs0c8ay0.js +1 -0
  20. package/static/assets/de-C03R7qVE.js +1 -0
  21. package/static/assets/es-LJ2OkYIx.js +1 -0
  22. package/static/assets/fa-DySEd8zE.js +1 -0
  23. package/static/assets/fr-BTafm0bE.js +1 -0
  24. package/static/assets/hi-Bk8MY-fF.js +1 -0
  25. package/static/assets/id-DZGFWLC-.js +1 -0
  26. package/static/assets/index-C1tAWGVu.js +46 -0
  27. package/static/assets/index-X0RXgNf5.css +1 -0
  28. package/static/assets/it-DEnLooJL.js +1 -0
  29. package/static/assets/ja-D7VxE6bk.js +1 -0
  30. package/static/assets/ko-nc2euVdS.js +1 -0
  31. package/static/assets/ms-Jz3t8mz-.js +1 -0
  32. package/static/assets/nl-D4aafNI8.js +1 -0
  33. package/static/assets/pt-CBY3pDPr.js +1 -0
  34. package/static/assets/ru-B3WZA3F-.js +1 -0
  35. package/static/assets/th-BSUvfnqU.js +1 -0
  36. package/static/assets/tl-DSeqPP4N.js +1 -0
  37. package/static/assets/tr-jcUJ-jsZ.js +1 -0
  38. package/static/assets/ur-cYxmbnCI.js +1 -0
  39. package/static/assets/vi-Dh-Jp2tU.js +1 -0
  40. package/static/index.html +2 -2
  41. package/web/routes/config_routes.py +36 -13
  42. package/web/routes/task_creation_routes.py +80 -18
  43. package/static/assets/ar-DDU7VnXA.js +0 -1
  44. package/static/assets/bn-nY2VgxNf.js +0 -1
  45. package/static/assets/de-t0gEvqFh.js +0 -1
  46. package/static/assets/es-BrDX0bsl.js +0 -1
  47. package/static/assets/fa-DC45iYzo.js +0 -1
  48. package/static/assets/fr-jNdU3xMA.js +0 -1
  49. package/static/assets/hi-DU2jFclD.js +0 -1
  50. package/static/assets/id-xVm_LLjT.js +0 -1
  51. package/static/assets/index-B_DQr-bu.css +0 -1
  52. package/static/assets/index-D06v2INE.js +0 -45
  53. package/static/assets/it-CNEcqCqv.js +0 -1
  54. package/static/assets/ja-BMrQ7ES8.js +0 -1
  55. package/static/assets/ko-DLcdCpqD.js +0 -1
  56. package/static/assets/ms-lz3IP0pE.js +0 -1
  57. package/static/assets/nl-JdaJ6OFu.js +0 -1
  58. package/static/assets/pt-BAJ6NELp.js +0 -1
  59. package/static/assets/ru-DUk7xiI4.js +0 -1
  60. package/static/assets/th-DBGXh4cH.js +0 -1
  61. package/static/assets/tl-cWM0TcNr.js +0 -1
  62. package/static/assets/tr-DY7yRLf-.js +0 -1
  63. package/static/assets/ur-CBrqZI5F.js +0 -1
  64. package/static/assets/vi-BWMCwlNI.js +0 -1
package/README.md CHANGED
@@ -1,20 +1,22 @@
1
1
  ---
2
2
 
3
- # What's New in v7.0.6
3
+ # What's New in v7.1.1
4
4
 
5
5
  ## What's New
6
6
 
7
7
  ### Features & Improvements
8
8
 
9
- * **Style preset picker shows what is active.** The preset dropdown was rebuilt as a native-select-style control: the trigger displays the currently selected preset name, the list marks the active entry with a checkmark, and a one-line hint under the field explains what picking a preset does. The trigger label, placeholder and hint are localized in all 22 interface languages.
9
+ - **`agnes-video-2.5-flash` is the default video model.** With the legacy Video 2.0 channel retired upstream, the model lineup now covers the 2.5 series only, and every video task is submitted through the 2.5 standard protocol (`mode` / `seconds` / `size` / `aspect_ratio`). The per-model guidance shown in the create-task form reflects the available models.
10
+ - **Configurable configuration directory for npm deployments.** A new `AGNES_CONFIG_DIR` environment variable lets `npx free-short-video` users keep `config.json` (their saved keys and settings) in a stable location such as `~/.agnes_config`, instead of inside the ephemeral npm cache that is lost on `npm cache clean` or a cache-directory change.
11
+ - **Image uploads are validated on arrival.** Non-whitelisted file types are rejected with a readable message instead of being silently rewritten, and uploaded bytes are checked to be a decodable image. A corrupt file now fails immediately with a clear, scene-scoped message rather than surfacing as a low-level error during generation.
12
+ - **Feedback panel and diagnostic report fully localized.** The remaining feedback-panel and diagnostic-report labels that still appeared in English on non-English interfaces are now translated across all 22 interface languages.
10
13
 
11
14
  ### Bug Fixes
12
15
 
13
- * **No more Chinese visual style inside non-Chinese tasks.** The hardcoded Chinese style default was removed from the Creative and Poetry task forms, from the shared legacy task-creation endpoint, and from the Creative and Poetry task models. Leaving the style field empty now pre-fills it on page load with the language-neutral default preset (an English cinematic prompt), so the task, the generated scene prompts and the diagnostic report all stay in the language the user is working in.
14
-
15
- ---
16
-
17
- Existing projects do not need to be recreated. Reopening a Creative or Poetry form after upgrading shows the preset name in the style field, and you can pick any other preset — or clear the field — as before.
16
+ - **Custom end-frame images keep their scene mapping.** Uploaded end-frame images are now saved to their own scene index, so an empty slot no longer shifts later images onto the wrong scenes.
17
+ - **Chat calls retry once on an empty response** instead of aborting the screenwriting stage with a `None` or `KeyError`.
18
+ - **Poetry scene prompts use a language-neutral default style.** When no style is specified, the default hint is no longer a hardcoded Chinese phrase, so non-Chinese runs stay in their own language.
19
+ - **API-key authentication diagnostics no longer write the configured site domain into the log line.**
18
20
 
19
21
  ---
20
22
 
@@ -102,12 +104,45 @@ schema.org structured data for SEO/GEO indexing. GitHub does not execute this sc
102
104
 
103
105
  > **🖥️ Try it now — no install needed:** Visit [video.lichuanyang.top](https://video.lichuanyang.top) and experience **Simple Video** mode directly in your browser. Just enter a prompt and generate a free AI video instantly.
104
106
 
105
- ## 🚀 Two Ways to Use — Both Completely Free
107
+ ## 🚀 Three Ways to Use — All Completely Free
106
108
 
107
109
  | Project | Run Where | Features | Link |
108
110
  |---------|-----------|----------|------|
109
111
  | **[Agnes Video Generator](https://github.com/lcy362/agnes-video-generator)** (this project) | **Download & run locally** | **More powerful** — TTS narration, auto subtitles, digital anchor, image-to-video, keyframes animation, manuscript-to-video, checkpoint resume & more | [GitHub](https://github.com/lcy362/agnes-video-generator) |
110
112
  | **[FreeShortVideoStudio](https://github.com/lcy362/free-short-video-studio)** | **Fully online, in the browser** | Lightweight, zero install — no setup at all, **features under active construction** | [video.lichuanyang.top/studio](https://video.lichuanyang.top/studio) · [GitHub](https://github.com/lcy362/free-short-video-studio) |
113
+ | **[Agnes Video Generator](https://github.com/lcy362/agnes-video-generator)** — **agent-ready** | **Driven by an AI coding agent** | **Least effort** — ships [`AGENTS.md`](AGENTS.md) plus a companion skill set (usage / troubleshoot / contrib): the agent installs the project, generates the video and debugs failures itself | [Agent guide](docs/public/agent-operations.md) · [Flint](https://github.com/lcy362/flint) |
114
+
115
+ ## 🤖 Or Let an AI Agent Drive It
116
+
117
+ This project ships an [`AGENTS.md`](AGENTS.md) written for AI coding assistants, so an agent can install it, generate videos and debug failures after reading one document. Clone the repo, then tell your agent:
118
+
119
+ > "Read the AGENTS.md in this project, install dependencies, configure the API key `<your-key>`, and start the server."
120
+
121
+ Then ask it to prove the setup with `"Run the deployment verification checks."` — it executes the four-layer checklist (connectivity → static analysis → endpoint tests → features) and reports each result.
122
+
123
+ **Want your agent to skip the learning step?** Install the companion skill set — three ready-made skills (**usage**: deploy, pick a mode, submit and watch a task, edit artifacts at Manual Mode checkpoints; **troubleshoot**: find out why a run failed; **contrib**: change the code, get the PR through CI) plus small scripts for task control, deployment verification, evidence collection, media checks and the local CI gate:
124
+
125
+ **[lcy362/agnes-video-generator-skills](https://github.com/lcy362/agnes-video-generator-skills)**
126
+
127
+ Clone the repository first:
128
+
129
+ ```bash
130
+ git clone https://github.com/lcy362/agnes-video-generator-skills.git
131
+ ```
132
+
133
+ **Recommended — let [flint](https://github.com/lcy362/flint) install and distribute it.** flint is a local-first, open-source skills manager that keeps your skills in one place and deploys them into any agent or project directory by symlink or copy. It works on local sources only — it never clones anything for you — so you register the directory you just cloned, and the three skills appear in front of every agent you use:
134
+
135
+ ```bash
136
+ npx flint-skills-hub # Sources → add the cloned directory (flint reads its skills/ subdirectory)
137
+ ```
138
+
139
+ **Or copy it in by hand** — no flint required, but you repeat it for every project and every new agent:
140
+
141
+ ```bash
142
+ cp -R agnes-video-generator-skills/skills/* <project>/.agents/skills/ # any agent that reads .agents/skills
143
+ ```
144
+
145
+ Full guide: **[Let an AI Agent Operate This Project](docs/public/agent-operations.md)**.
111
146
 
112
147
  ## ⭐ Support & Contribute
113
148
 
@@ -193,6 +228,7 @@ Full walkthrough: [Getting Started → Configure API Key](docs/public/getting-st
193
228
 
194
229
  - **[Features](docs/public/features.md)** — Creation modes, the completely free AI model chain, AI narration & smart subtitles, flexible creative controls, production-grade reliability, and the multilingual Web UI.
195
230
  - **[Getting Started](docs/public/getting-started.md)** — Install and deploy in 4 ways: Manual (`start.sh`), Docker, npm (`npx free-short-video`), or AI-Agent assisted.
231
+ - **[AI Agent Operations](docs/public/agent-operations.md)** — Three levels of agent-driven operation: hand your agent `AGENTS.md`, install the companion skill set, or distribute it to every agent with flint.
196
232
  - **[Usage Guide](docs/public/usage.md)** — Configure your API key, pick a video mode, resume from checkpoints, the three chaining modes, and logs & output layout.
197
233
  - **[Architecture](docs/public/architecture.md)** — Project structure and tech stack.
198
234
  - **[API Reference](docs/public/api.md)** — Full REST endpoint list (progress via polling, no WebSocket).
@@ -191,20 +191,34 @@ class AgnesChatAPI:
191
191
  from core.config import get_settings
192
192
 
193
193
  timeout_retry_limit = 1
194
- data = self._request_with_retry(
195
- {
196
- "model": self.model,
197
- "messages": [
198
- {"role": "system", "content": system_prompt},
199
- {"role": "user", "content": user_prompt},
200
- ],
201
- "temperature": 0.7,
202
- "max_tokens": max_tokens,
203
- },
204
- timeout=get_settings().agnes_chat_timeout,
205
- timeout_retry_limit=timeout_retry_limit,
206
- )
207
- return data["choices"][0]["message"]["content"]
194
+ payload = {
195
+ "model": self.model,
196
+ "messages": [
197
+ {"role": "system", "content": system_prompt},
198
+ {"role": "user", "content": user_prompt},
199
+ ],
200
+ "temperature": 0.7,
201
+ "max_tokens": max_tokens,
202
+ }
203
+ # 空响应守卫:缺失 / 空正文时重试一次,避免把 ``None`` 或 KeyError 直接
204
+ # 抛给上层(原实现为裸 ``data["choices"][0]["message"]["content"]``)。
205
+ content = ""
206
+ for attempt in range(2):
207
+ data = self._request_with_retry(
208
+ payload,
209
+ timeout=get_settings().agnes_chat_timeout,
210
+ timeout_retry_limit=timeout_retry_limit,
211
+ )
212
+ try:
213
+ content = data["choices"][0]["message"]["content"] or ""
214
+ except (KeyError, IndexError, TypeError):
215
+ content = ""
216
+ if isinstance(content, str) and content.strip():
217
+ return content
218
+ content = ""
219
+ if attempt == 0:
220
+ logger.warning("[AgnesChat] Empty chat response; retrying once...")
221
+ raise RuntimeError("Agnes Chat returned empty content after retry")
208
222
 
209
223
  def chat_json(self, system_prompt: str, user_prompt: str, max_tokens: int = 4096) -> dict:
210
224
  """Chat 调用并解析 JSON 响应(健壮版)。
@@ -12,6 +12,7 @@ import requests
12
12
  from core.api.error_collector import collect_error, collect_error_from_exception
13
13
  from core.api.key_manager import get_key_ring
14
14
  from core.api.rate_limiter import get_rate_limiter
15
+ from core.api.retry_policy import BusyTracker, busy_delay, fault_delay, is_busy_signal
15
16
  from core.config import get_base_url_for_key
16
17
  from utils.image import download_image
17
18
  from utils.image_normalizer import normalize_reference_path
@@ -21,6 +22,9 @@ logger = logging.getLogger(__name__)
21
22
  # 图像生成读超时基数(秒):timeout = 基数 * (attempt + 1),首次 120s 逐步放宽
22
23
  _READ_TIMEOUT_BASE_SECONDS = 120
23
24
 
25
+ # v7.2 忙轨次数封顶:等效旧的 300s 预算 ÷ 20s 间隔基数
26
+ _BUSY_MAX_ATTEMPTS = 15
27
+
24
28
 
25
29
  class ImageOutput:
26
30
  def __init__(self, fmt: str, ext: str, data: str):
@@ -159,6 +163,8 @@ class AgnesImageAPI:
159
163
  rotations = 0
160
164
  ring = get_key_ring()
161
165
  max_rotations = len(ring) * max_retries
166
+ # 忙轨(429 / 所有 503):首跳贴限流 + 固定间隔 + 次数封顶
167
+ busy: BusyTracker | None = None
162
168
  while attempt < max_retries:
163
169
  try:
164
170
  # 全局限速:在发起 HTTP 请求前获取令牌(2.3 异步原生)
@@ -174,9 +180,12 @@ class AgnesImageAPI:
174
180
  timeout=(30, read_timeout),
175
181
  )
176
182
 
177
- # 429 限流:多 Key 换 Key 立即重试;否则指数退避
178
- if resp.status_code == 429:
179
- if ring.has_multiple() and rotations < max_rotations:
183
+ # ── 忙轨(429 / 所有 503)────────────────────────────────
184
+ # 多 Key 先换 Key 立即重试;换无可换则首跳贴限流(交给共享令牌桶)、
185
+ # 之后固定间隔重试(不逐级加长),由次数封顶兜底。
186
+ if is_busy_signal(resp.status_code):
187
+ if (resp.status_code == 429 and ring.has_multiple()
188
+ and rotations < max_rotations):
180
189
  rotations += 1
181
190
  ring.rotate()
182
191
  logger.warning(
@@ -184,40 +193,56 @@ class AgnesImageAPI:
184
193
  f"(image, rotation {rotations})"
185
194
  )
186
195
  continue
187
- if attempt < max_retries - 1:
188
- delay = retry_base_delay * (attempt + 1)
189
- logger.warning(
190
- f"[AgnesImage] 429 rate limit, "
191
- f"retry {attempt + 1}/{max_retries} in {delay:.0f}s..."
196
+ if busy is None:
197
+ from core.config import get_settings
198
+ busy = BusyTracker(
199
+ get_settings().agnes_busy_retry_attempts
200
+ or _BUSY_MAX_ATTEMPTS
192
201
  )
202
+ error_type = (
203
+ "RateLimit429" if resp.status_code == 429
204
+ else f"Busy{resp.status_code}"
205
+ )
206
+ if busy.exhausted():
207
+ # 次数用尽:交给调用方按原语义抛错(记录最终失败)
193
208
  collect_error(
194
209
  "image", "generate_single_image",
195
210
  prompt=prompt,
196
- error_type="RateLimit429",
197
- error_message="HTTP 429: rate limited",
198
- status_code=429,
211
+ error_type=error_type,
212
+ error_message=(
213
+ f"HTTP {resp.status_code}: busy retries exhausted"
214
+ ),
215
+ status_code=resp.status_code,
199
216
  response_body=resp.text,
200
- retry_count=attempt + 1,
217
+ retry_count=busy.retries,
201
218
  )
202
- await asyncio.sleep(delay)
203
- attempt += 1
204
- continue
205
- # 退避耗尽:记录最终失败并抛出
219
+ resp.raise_for_status()
220
+ break
221
+ busy.retries += 1
222
+ # 第 1 次重试 0s(贴着限流),之后固定基数
223
+ delay = busy_delay(retry_base_delay, 0.0, busy.retries - 1)
224
+ logger.warning(
225
+ f"[AgnesImage] {resp.status_code} busy, "
226
+ f"retry #{busy.retries}/{busy.max_attempts} in {delay:.0f}s..."
227
+ )
206
228
  collect_error(
207
229
  "image", "generate_single_image",
208
230
  prompt=prompt,
209
- error_type="RateLimit429",
210
- error_message="HTTP 429: retries exhausted",
211
- status_code=429,
231
+ error_type=error_type,
232
+ error_message=(
233
+ "HTTP 429: rate limited" if resp.status_code == 429
234
+ else f"HTTP {resp.status_code}: server busy"
235
+ ),
236
+ status_code=resp.status_code,
212
237
  response_body=resp.text,
213
- retry_count=max_retries,
238
+ retry_count=busy.retries,
214
239
  )
215
- resp.raise_for_status()
216
- break
240
+ await asyncio.sleep(delay)
241
+ continue
217
242
 
218
- # 5xx 服务端错误:退避重试
243
+ # 故障轨(非忙 5xx):退避逐级加长 + max_retries 次数封顶
219
244
  if resp.status_code >= 500 and attempt < max_retries - 1:
220
- delay = retry_base_delay * (attempt + 1)
245
+ delay = fault_delay(retry_base_delay, attempt)
221
246
  logger.warning(
222
247
  f"[AgnesImage] {resp.status_code} server error, "
223
248
  f"retry {attempt + 1}/{max_retries} in {delay:.0f}s..."
@@ -247,7 +272,7 @@ class AgnesImageAPI:
247
272
  exc=e, prompt=prompt, retry_count=attempt + 1,
248
273
  )
249
274
  if attempt < max_retries - 1:
250
- delay = retry_base_delay * (attempt + 1)
275
+ delay = fault_delay(retry_base_delay, attempt)
251
276
  logger.warning(
252
277
  f"[AgnesImage] {type(e).__name__}, "
253
278
  f"retry {attempt + 1}/{max_retries} in {delay:.0f}s..."