free-short-video 7.0.0 → 7.0.2

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 (73) hide show
  1. package/.env.example +8 -4
  2. package/README.md +7 -40
  3. package/core/api/agnes_video.py +13 -23
  4. package/core/config.py +60 -3
  5. package/core/i18n_backend.py +643 -0
  6. package/core/pipelines/anchor_video.py +16 -13
  7. package/core/pipelines/creative/steps_audio.py +9 -7
  8. package/core/pipelines/creative/steps_frames.py +16 -4
  9. package/core/pipelines/creative/steps_script.py +44 -21
  10. package/core/pipelines/creative/steps_video.py +14 -14
  11. package/core/pipelines/manuscript_video.py +30 -13
  12. package/core/pipelines/multi_scene.py +17 -11
  13. package/core/pipelines/poetry_video.py +9 -5
  14. package/core/pipelines/simple_video.py +7 -7
  15. package/core/screenwriter/__init__.py +16 -4
  16. package/models/task.py +2 -2
  17. package/package.json +1 -1
  18. package/static/assets/ar-CRbirOE3.js +1 -0
  19. package/static/assets/bn-DGXpxXqo.js +1 -0
  20. package/static/assets/de-CW_QJIGx.js +1 -0
  21. package/static/assets/es-DG3zPtKB.js +1 -0
  22. package/static/assets/fa-KkBv8EdH.js +1 -0
  23. package/static/assets/fr-zdohXdVm.js +1 -0
  24. package/static/assets/hi-g1ky89XQ.js +1 -0
  25. package/static/assets/id-C21bK9DP.js +1 -0
  26. package/static/assets/index-D9sLMHUd.css +1 -0
  27. package/static/assets/index-DNreiP_P.js +45 -0
  28. package/static/assets/it-C5Xbey_b.js +1 -0
  29. package/static/assets/ja-NciKY87B.js +1 -0
  30. package/static/assets/ko-DJ8XhzBo.js +1 -0
  31. package/static/assets/ms-DcwfkMJG.js +1 -0
  32. package/static/assets/nl-CnpYVEZB.js +1 -0
  33. package/static/assets/pt-DAP0auKP.js +1 -0
  34. package/static/assets/ru-CzKYIeBy.js +1 -0
  35. package/static/assets/th-Q_FQzUg5.js +1 -0
  36. package/static/assets/tl-DXi6nq4V.js +1 -0
  37. package/static/assets/tr-BOCBaAw1.js +1 -0
  38. package/static/assets/ur-K0JNi0pO.js +1 -0
  39. package/static/assets/vi-DR30o5T0.js +1 -0
  40. package/static/index.html +2 -2
  41. package/web/helpers.py +39 -9
  42. package/web/routes/config_routes.py +37 -26
  43. package/web/routes/gallery_routes.py +4 -3
  44. package/web/routes/image_routes.py +6 -3
  45. package/web/routes/preset_routes.py +4 -3
  46. package/web/routes/preview_routes.py +38 -11
  47. package/web/routes/task_creation_routes.py +91 -24
  48. package/web/routes/utility_routes.py +8 -7
  49. package/web/routes/video_routes.py +4 -4
  50. package/web/routes/voice_routes.py +3 -2
  51. package/web/routes/workspace_routes.py +7 -6
  52. package/static/assets/ar-DVEg_H6f.js +0 -1
  53. package/static/assets/bn-BiFSnXzB.js +0 -1
  54. package/static/assets/de-uhexDoxx.js +0 -1
  55. package/static/assets/es-DUPqgRup.js +0 -1
  56. package/static/assets/fa-CNvLTccz.js +0 -1
  57. package/static/assets/fr-BagKMN93.js +0 -1
  58. package/static/assets/hi-DeWOhJ1q.js +0 -1
  59. package/static/assets/id-nGPSnGvK.js +0 -1
  60. package/static/assets/index-CJDvYGjk.css +0 -1
  61. package/static/assets/index-CfGG_wN-.js +0 -45
  62. package/static/assets/it-BJGGcHvC.js +0 -1
  63. package/static/assets/ja-cR8f9YPd.js +0 -1
  64. package/static/assets/ko-C0xGw09S.js +0 -1
  65. package/static/assets/ms-Cgs1dWE8.js +0 -1
  66. package/static/assets/nl-Cwg9KBwg.js +0 -1
  67. package/static/assets/pt-B9Hhu0CQ.js +0 -1
  68. package/static/assets/ru-CafubBrt.js +0 -1
  69. package/static/assets/th-2Muse1NU.js +0 -1
  70. package/static/assets/tl-DGVTGEV4.js +0 -1
  71. package/static/assets/tr-BPcMq5fF.js +0 -1
  72. package/static/assets/ur-BTthCgqj.js +0 -1
  73. package/static/assets/vi-Bi-nuXdz.js +0 -1
package/.env.example CHANGED
@@ -36,11 +36,15 @@ HOST=0.0.0.0
36
36
  PORT=8765
37
37
 
38
38
  # 注:Agnes API 走哪个站点(国际站 / 国内站)**不通过环境变量配置**
39
- # (AGNES_BASE_URL 已废弃,代码中不再读取)。请在 Web 设置页的 API Key 面板
40
- # 选择域名,或用「自动探测域名」逐 Key 补全匹配域名(每个 Key 可绑定各自域名)。
39
+ # (AGNES_BASE_URL 已废弃,代码中不再读取)。请在 Web 设置页的 API Key 面板选择全局默认域名。
40
+ # ⚠️ 重要:通过本文件 / 环境变量写入的 Key 属于 env 来源,**无法逐个绑定域名,也不参与
41
+ # Web 页的「自动探测域名」**——它们一律使用你在 Web 页设定的「全局默认域名」。因此:
42
+ # - 单站点 Key:把全局默认域名设成与该 Key 站点一致即可;
43
+ # - 混用不同站点的 Key:请改到 Web 配置页逐个添加(config 来源),再逐 Key 绑定域名
44
+ # 或点「自动探测域名」,才能各走各的站点(env Key 做不到这点)。
45
+ # 跨站使用 Key(如国际站 Key 打到国内站专属域名)会得到 HTTP 401 / 无效的令牌。
41
46
  # 可选域名:com = apihub.agnes-ai.com(国际站)、cn = api.agnes-ai.cn(国内站,
42
- # 需国内站专属 Key)、cn_bak = apihub.agnes-ai.cn(备用,国际站 Key 亦可用)。
43
- # 跨站使用 Key 会得到 HTTP 401 / 无效的令牌。
47
+ # 需国内站专属 Key)、cn_bak = apihub.agnes-ai.cn(备用,国内与国际 Key 均可用)。
44
48
 
45
49
 
46
50
  # ── 4. 限速(可选)─────────────────────────────────────────────────
package/README.md CHANGED
@@ -1,56 +1,23 @@
1
1
  ---
2
2
 
3
- # What's New in v7.0.0
3
+ # What's New in v7.0.2
4
4
 
5
5
  ## What's New
6
6
 
7
7
  ### Features & Improvements
8
8
 
9
- - **Custom text model providers (OpenAI-compatible / Anthropic-compatible).** Configure any
10
- LLM endpoint with `base URL + API key` and two supported wire protocols
11
- (`openai-completions`, `anthropic-messages`). Once selected, every text call — screenplay
12
- breakdown, scene planning, poetry segmentation, image-prompt rewriting — is routed to your
13
- provider automatically. The built-in Agnes provider is always available, cannot be deleted,
14
- and keeps the previous behaviour when no custom provider is selected.
15
- - **Provider management panel.** A dedicated settings section lists all text providers, lets
16
- you add, edit and delete them, and keeps the Agnes key/domain editing inside the provider
17
- modal. Keys are masked in the UI, and the provider's model list is a single editable list:
18
- fetch it from the endpoint or type model ids by hand. Model selection is a two-level
19
- provider → model picker that merges the models of all providers.
20
- - **Interface-language aware backend messages.** Task progress messages, failure panels, HTTP
21
- error details and the diagnostics report are now localized according to the UI language
22
- instead of being hard-coded Chinese. The UI language travels with every request
23
- (`X-Agnes-UI-Lang`, falling back to `Accept-Language`), is snapshotted into each task at
24
- creation time, and therefore does not drift if the user switches language later. Chinese and
25
- English are covered; the remaining languages fall back to the existing behaviour.
26
- - **Clearer network failure diagnosis.** TLS handshake resets (`connection reset`) are now
27
- attributed to local-connection problems, and the diagnostics report shows the interface
28
- language of the reporting user, making failure reports easier to triage.
29
-
30
- ### Refactoring & Optimizations
31
-
32
- - **Decoupled text-generation layer.** A new protocol-client layer
33
- (`core/api/providers/` → base / OpenAI / Anthropic) plus a provider factory replaces the two
34
- hard-coded Agnes chat client constructions, so pipelines and the screenwriter are untouched
35
- while the underlying model source becomes pluggable. Custom providers reuse the existing
36
- shared rate limiter and exponential-backoff retry policy, and failures are recorded in the
37
- same error log as before.
38
- - **Backend i18n runtime.** A new translation runtime plus request-scoped language middleware
39
- gives the FastAPI side the same localization guarantees the frontend already had, with a
40
- safe fallback chain that never turns a missing translation into a request failure.
9
+ * **Clearer key/domain setup to prevent `401`.** The API Key panel now states, per source, that keys added in the web UI can be bound to their own domain (and are covered by *Auto-detect domains*), while `.env` / environment-variable keys always follow the single **global default domain** and cannot be bound individually. Each `.env` key now shows a neutral "follows global domain" badge with the resolved endpoint. A new **multi-key** note explains that a single mismatched or expired key makes tasks intermittently fail with `401` whenever that key is picked. Getting Started and the FAQ gained a "match the key to the right site" section, and `.env.example` now spells out the `.env`-key behavior — so single-site users set the domain and mixed-site users move keys into the web config page.
10
+ * **True 16:9 landscape.** Landscape (16:9) video now renders at the correct aspect ratio, and the portrait / large-landscape resolution option labels were fixed so the listed size matches what is actually generated.
11
+ * **Refreshed language switcher.** The native browser language dropdown was replaced with a site-styled language switcher, consistent with the rest of the UI across all 22 locales.
41
12
 
42
13
  ### Bug Fixes
43
14
 
44
- - Model pulling for an already saved provider now reuses its stored credentials instead of
45
- falling back to the Agnes key.
46
- - The built-in Agnes provider no longer appears twice in the provider picker and is now
47
- labelled as text-only.
48
- - Non-Chinese interfaces no longer receive hard-coded Chinese network-diagnosis and task
49
- messages (issue #64).
15
+ * Fixed the config page recommending "bind a matching domain for each key" on `.env`-sourced keys, which have no domain dropdown and cannot be bound — the misleading hint that repeatedly sent users down the wrong path when chasing `401` errors. The *Auto-detect domains* tooltip now correctly states it only probes web-UI keys.
16
+ * Corrected the Docker/compose image reference to the real flat GHCR namespace, so documented pull commands actually resolve.
50
17
 
51
18
  ---
52
19
 
53
- No action is required after upgrading.
20
+ No action is required after upgrading. If you were hitting `401`, re-check your key against its site in the API Key panel or run *Auto-detect domains*.
54
21
 
55
22
  ---
56
23
 
@@ -15,10 +15,10 @@ from core.api.error_collector import collect_error, collect_error_from_exception
15
15
  from core.api.key_manager import get_key_ring
16
16
  from core.api.rate_limiter import get_rate_limiter, get_video_submit_limiter
17
17
  from core.config import (
18
- VIDEO_ASPECT_RATIOS,
19
18
  get_agnes_api_root,
20
19
  get_base_url_for_key,
21
20
  is_v25_video_model,
21
+ width_height_to_aspect_ratio,
22
22
  )
23
23
  from utils.image_normalizer import normalize_reference_path
24
24
  from utils.video import download_video
@@ -271,7 +271,7 @@ class AgnesVideoAPI:
271
271
  return 961 # 480p tier
272
272
 
273
273
  def _get_frame_config(self, duration: Optional[int] = None,
274
- width: int = 1152, height: int = 768) -> tuple:
274
+ width: int = 1280, height: int = 720) -> tuple:
275
275
  d = duration or self.default_duration
276
276
  max_nf = self._get_max_frames(width, height)
277
277
  if d in DURATION_PRESETS:
@@ -563,8 +563,8 @@ class AgnesVideoAPI:
563
563
  prompt: str,
564
564
  reference_image_paths: List[str] = [],
565
565
  duration: Optional[int] = None,
566
- width: int = 1152,
567
- height: int = 768,
566
+ width: int = 1280,
567
+ height: int = 720,
568
568
  seed: Optional[int] = None,
569
569
  negative_prompt: Optional[str] = None,
570
570
  progress_callback=None,
@@ -587,8 +587,8 @@ class AgnesVideoAPI:
587
587
  prompt: str,
588
588
  reference_image_paths: List[str] = [],
589
589
  duration: Optional[int] = None,
590
- width: int = 1152,
591
- height: int = 768,
590
+ width: int = 1280,
591
+ height: int = 720,
592
592
  seed: Optional[int] = None,
593
593
  negative_prompt: Optional[str] = None,
594
594
  **kwargs,
@@ -649,31 +649,21 @@ class AgnesVideoAPI:
649
649
 
650
650
  @staticmethod
651
651
  def _width_height_to_aspect_ratio(width: int, height: int) -> str:
652
- """将像素宽高映射到最接近的 2.5 系列 aspect_ratio 枚举。
652
+ """将像素宽高映射到 2.5 系列 aspect_ratio 枚举(委托 ``core.config``)。
653
653
 
654
- 找不到精确比例时取误差最小的档位(默认 16:9)。
654
+ 映射规则已上提到 ``core.config.width_height_to_aspect_ratio``,供回归
655
+ 校验脚本共用(保证「提交」与「校验」同一套规则);此方法保留为类内
656
+ 调用入口,行为与上提前完全一致。
655
657
  """
656
- if not width or not height:
657
- return "16:9"
658
- ratio = width / height
659
- best = "16:9"
660
- best_dist = float("inf")
661
- for ar in VIDEO_ASPECT_RATIOS:
662
- w, h = ar.split(":")
663
- target = int(w) / int(h)
664
- dist = abs(ratio - target)
665
- if dist < best_dist:
666
- best_dist = dist
667
- best = ar
668
- return best
658
+ return width_height_to_aspect_ratio(width, height)
669
659
 
670
660
  async def _submit_video_v25(
671
661
  self,
672
662
  prompt: str,
673
663
  reference_image_paths: List[str],
674
664
  duration: Optional[int] = None,
675
- width: int = 1152,
676
- height: int = 768,
665
+ width: int = 1280,
666
+ height: int = 720,
677
667
  seed: Optional[int] = None,
678
668
  **kwargs,
679
669
  ) -> str:
package/core/config.py CHANGED
@@ -18,7 +18,7 @@ CONFIG_FILE = os.path.join(CONFIG_DIR, "config.json")
18
18
  # ═══════════════════════════════════════════════════
19
19
  # 应用版本号(v6.1 新增:发版时同步更新,见 docs/dev/release_process.md)
20
20
  # ═══════════════════════════════════════════════════
21
- APP_VERSION = "7.0.0"
21
+ APP_VERSION = "7.0.2"
22
22
 
23
23
  # 未配置 API Key 时的统一报错文案(含免费获取与在线体验兜底,全站路由共用)
24
24
  #
@@ -903,7 +903,7 @@ def set_watermark_config(enabled: bool = None, language: str = None):
903
903
 
904
904
  VIDEO_RESOLUTION_PRESETS = {
905
905
  "portrait": {"width": 768, "height": 1152, "label": "竖屏 9:16"},
906
- "landscape": {"width": 1152, "height": 768, "label": "横屏 16:9"},
906
+ "landscape": {"width": 1280, "height": 720, "label": "横屏 16:9"},
907
907
  "square": {"width": 1024, "height": 1024, "label": "方形 1:1"},
908
908
  }
909
909
 
@@ -1085,6 +1085,63 @@ def is_v25_video_model(model: str) -> bool:
1085
1085
  # 支持的画幅比例(2.5 系列 aspect_ratio 枚举)
1086
1086
  VIDEO_ASPECT_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"]
1087
1087
 
1088
+ # ── 项目标准像素档 → aspect_ratio 显式映射(语义优先于数值最近邻)──
1089
+ # 背景:2.5 系列只接受上面 6 档比例枚举,而非标准比例走「数值最近邻」会归错档:
1090
+ # · 1152x768(3:2, 1.500)距 4:3(1.333) 0.167 < 距 16:9(1.778) 0.278 → 误判 4:3
1091
+ # · 768x1152(2:3, 0.667)距 3:4(0.750) 0.083 < 距 9:16(0.563) 0.104 → 误判 3:4
1092
+ # 改度量方式(比值 / 对数距离)救不了这两个值——2:3 在几何上确实更贴近 3:4。
1093
+ # 但它们在前端就是「竖屏 9:16」「横屏 16:9」两个语义明确的惯例像素档,故显式查表,
1094
+ # 不让数值距离决定。表内其余档位与最近邻结果一致,仅作显式化,不改变既有行为。
1095
+ STANDARD_PIXEL_TO_RATIO = {
1096
+ # 竖屏
1097
+ (768, 1152): "9:16",
1098
+ (720, 1280): "9:16",
1099
+ (768, 1344): "9:16",
1100
+ (1024, 1792): "9:16",
1101
+ # 横屏
1102
+ (1152, 768): "16:9",
1103
+ (1280, 720): "16:9",
1104
+ (1344, 768): "16:9",
1105
+ (1792, 1024): "16:9",
1106
+ # 方形与超宽
1107
+ (1024, 1024): "1:1",
1108
+ (960, 720): "4:3",
1109
+ (720, 960): "3:4",
1110
+ (1680, 720): "21:9",
1111
+ }
1112
+
1113
+
1114
+ def width_height_to_aspect_ratio(width: int, height: int) -> str:
1115
+ """将像素宽高映射到 2.5 系列 aspect_ratio 枚举。
1116
+
1117
+ 映射顺序:
1118
+
1119
+ 1. 项目标准像素档显式查表(``STANDARD_PIXEL_TO_RATIO``)——语义优先,避免
1120
+ ``768x1152``(竖屏 9:16)/ ``1152x768``(横屏 16:9)这类项目惯例档被
1121
+ 数值最近邻误判成 ``3:4`` / ``4:3``;
1122
+ 2. 未命中时取误差最小的档位(默认 ``16:9``)。
1123
+
1124
+ 供 API 客户端(提交时推导 aspect_ratio)与回归校验脚本(校验时推导期望
1125
+ 比例)共用,保证「提交」与「校验」使用同一套映射规则。
1126
+ """
1127
+ if not width or not height:
1128
+ return "16:9"
1129
+ exact = STANDARD_PIXEL_TO_RATIO.get((width, height))
1130
+ if exact:
1131
+ return exact
1132
+ ratio = width / height
1133
+ best = "16:9"
1134
+ best_dist = float("inf")
1135
+ for ar in VIDEO_ASPECT_RATIOS:
1136
+ w, h = ar.split(":")
1137
+ target = int(w) / int(h)
1138
+ dist = abs(ratio - target)
1139
+ if dist < best_dist:
1140
+ best_dist = dist
1141
+ best = ar
1142
+ return best
1143
+
1144
+
1088
1145
  # 2.5 系列时长档位(seconds 字符串 "4"–"12")
1089
1146
  VIDEO_25_DURATIONS = [4, 5, 6, 8, 10, 12]
1090
1147
 
@@ -1104,7 +1161,7 @@ VIDEO_MODEL_CAPABILITIES = {
1104
1161
  "type": "pixels",
1105
1162
  "options": [
1106
1163
  {"value": "768x1152", "label": {"zh": "竖屏 768x1152", "en": "Portrait 768x1152"}},
1107
- {"value": "1152x768", "label": {"zh": "横屏 1152x768", "en": "Landscape 1152x768"}},
1164
+ {"value": "1280x720", "label": {"zh": "横屏 1280x720", "en": "Landscape 1280x720"}},
1108
1165
  {"value": "1024x1024", "label": {"zh": "方形 1024x1024", "en": "Square 1024x1024"}},
1109
1166
  ],
1110
1167
  },