cue-ai 0.9.2 → 0.9.3

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 (278) hide show
  1. package/CHANGELOG.md +4 -3
  2. package/README.md +148 -170
  3. package/bin/cue-learnings +30 -4
  4. package/bin/cue-review-progress +0 -0
  5. package/bin/cue-review-watch +0 -0
  6. package/dist/cue.js +4328 -3108
  7. package/package.json +1 -1
  8. package/plugins/cue/commands/cue-switch.md +1 -1
  9. package/plugins/cue/commands/cue.md +1 -1
  10. package/profiles/backend/profile.yaml +4 -0
  11. package/profiles/browser/profile.yaml +4 -0
  12. package/profiles/career/profile.yaml +2 -13
  13. package/profiles/commerce/profile.yaml +0 -2
  14. package/profiles/coolify/profile.yaml +0 -1
  15. package/profiles/core/profile.yaml +78 -11
  16. package/profiles/dash-merge-test/profile.yaml +6 -1
  17. package/profiles/designer/profile.yaml +9 -1
  18. package/profiles/dropshipping/profile.yaml +69 -0
  19. package/profiles/frontend/profile.yaml +4 -0
  20. package/profiles/google-ads/profile.yaml +34 -0
  21. package/profiles/google-analytics/profile.yaml +34 -0
  22. package/profiles/google-drive/profile.yaml +34 -0
  23. package/profiles/gstack/profile.yaml +117 -29
  24. package/profiles/marketing/profile.yaml +0 -1
  25. package/profiles/media/README.md +70 -0
  26. package/profiles/media/profile.yaml +104 -0
  27. package/profiles/nano-banana/profile.yaml +52 -0
  28. package/profiles/ops/profile.yaml +1 -2
  29. package/profiles/secops/profile.yaml +3 -0
  30. package/profiles/skill-writer/profile.yaml +15 -0
  31. package/profiles/video/profile.yaml +3 -0
  32. package/profiles/web-frontend-base/profile.yaml +6 -0
  33. package/profiles/webshop/profile.yaml +0 -1
  34. package/profiles/webshop-google/profile.yaml +1 -0
  35. package/profiles/x-growth-bot/profile.yaml +2 -0
  36. package/resources/icons/generate-icons.py +2 -128
  37. package/resources/mcps/configs/claude.sanitized.json +88 -20
  38. package/resources/mcps/configs/claude_runtime.sanitized.json +40 -1
  39. package/resources/mcps/configs/codex.sanitized.json +29 -0
  40. package/resources/skills/skills/career/job-hunter/LICENSE +21 -0
  41. package/resources/skills/skills/career/job-hunter/README.md +323 -0
  42. package/resources/skills/skills/career/job-hunter/SKILL.md +91 -0
  43. package/resources/skills/skills/career/job-hunter/agents/README.md +96 -0
  44. package/resources/skills/skills/career/job-hunter/agents/apply-assessment-prep.md +195 -0
  45. package/resources/skills/skills/career/job-hunter/agents/apply-ats-scan.md +155 -0
  46. package/resources/skills/skills/career/job-hunter/agents/apply-bias-audit.md +224 -0
  47. package/resources/skills/skills/career/job-hunter/agents/apply-cover-letter.md +69 -0
  48. package/resources/skills/skills/career/job-hunter/agents/apply-decode-jd.md +117 -0
  49. package/resources/skills/skills/career/job-hunter/agents/apply-fit-score.md +183 -0
  50. package/resources/skills/skills/career/job-hunter/agents/apply-linkedin-audit.md +74 -0
  51. package/resources/skills/skills/career/job-hunter/agents/apply-linkedin-scrape.md +255 -0
  52. package/resources/skills/skills/career/job-hunter/agents/apply-portfolio-brief.md +123 -0
  53. package/resources/skills/skills/career/job-hunter/agents/apply-reality-check.md +164 -0
  54. package/resources/skills/skills/career/job-hunter/agents/apply-reference-prep.md +150 -0
  55. package/resources/skills/skills/career/job-hunter/agents/apply-rejection-analysis.md +172 -0
  56. package/resources/skills/skills/career/job-hunter/agents/apply-resume.md +70 -0
  57. package/resources/skills/skills/career/job-hunter/agents/apply-skills-gap-filler.md +109 -0
  58. package/resources/skills/skills/career/job-hunter/agents/career-internal.md +94 -0
  59. package/resources/skills/skills/career/job-hunter/agents/career-linkedin-content.md +173 -0
  60. package/resources/skills/skills/career/job-hunter/agents/career-linkedin-scanner.md +262 -0
  61. package/resources/skills/skills/career/job-hunter/agents/career-network-message.md +108 -0
  62. package/resources/skills/skills/career/job-hunter/agents/career-promote.md +102 -0
  63. package/resources/skills/skills/career/job-hunter/agents/career-review.md +71 -0
  64. package/resources/skills/skills/career/job-hunter/agents/interview-debrief.md +117 -0
  65. package/resources/skills/skills/career/job-hunter/agents/interview-mock.md +171 -0
  66. package/resources/skills/skills/career/job-hunter/agents/interview-panel-decoder.md +152 -0
  67. package/resources/skills/skills/career/job-hunter/agents/interview-prep.md +184 -0
  68. package/resources/skills/skills/career/job-hunter/agents/interview-question-bank.md +133 -0
  69. package/resources/skills/skills/career/job-hunter/agents/interview-research.md +148 -0
  70. package/resources/skills/skills/career/job-hunter/agents/offer-compare.md +117 -0
  71. package/resources/skills/skills/career/job-hunter/agents/offer-counteroffer.md +144 -0
  72. package/resources/skills/skills/career/job-hunter/agents/offer-deadline-manager.md +148 -0
  73. package/resources/skills/skills/career/job-hunter/agents/offer-negotiate.md +126 -0
  74. package/resources/skills/skills/career/job-hunter/agents/offer-schedule.md +99 -0
  75. package/resources/skills/skills/career/job-hunter/agents/offer-thankyou.md +80 -0
  76. package/resources/skills/skills/career/job-hunter/agents/search-company-research.md +146 -0
  77. package/resources/skills/skills/career/job-hunter/agents/search-follow-up.md +129 -0
  78. package/resources/skills/skills/career/job-hunter/agents/search-ghost-job-detector.md +152 -0
  79. package/resources/skills/skills/career/job-hunter/agents/search-inbox-scan.md +193 -0
  80. package/resources/skills/skills/career/job-hunter/agents/search-interview-scorecard.md +164 -0
  81. package/resources/skills/skills/career/job-hunter/agents/search-jobs.md +149 -0
  82. package/resources/skills/skills/career/job-hunter/agents/search-momentum-check.md +194 -0
  83. package/resources/skills/skills/career/job-hunter/agents/search-outreach.md +85 -0
  84. package/resources/skills/skills/career/job-hunter/agents/search-referral-finder.md +124 -0
  85. package/resources/skills/skills/career/job-hunter/agents/search-salary.md +96 -0
  86. package/resources/skills/skills/career/job-hunter/agents/search-send-email.md +109 -0
  87. package/resources/skills/skills/career/job-hunter/agents/search-tracker-update.md +127 -0
  88. package/resources/skills/skills/career/job-hunter/inputs/README.md +26 -0
  89. package/resources/skills/skills/career/job-hunter/inputs/apply-linkedin-url.txt +8 -0
  90. package/resources/skills/skills/career/job-hunter/inputs/interview-context.md +24 -0
  91. package/resources/skills/skills/career/job-hunter/inputs/job-description.md +20 -0
  92. package/resources/skills/skills/career/job-hunter/inputs/job-search-criteria.md +36 -0
  93. package/resources/skills/skills/career/job-hunter/inputs/my-linkedin.md +24 -0
  94. package/resources/skills/skills/career/job-hunter/inputs/my-resume.md +28 -0
  95. package/resources/skills/skills/career/job-hunter/inputs/search-outreach-target.md +24 -0
  96. package/resources/skills/skills/career/job-hunter/rules/README.md +37 -0
  97. package/resources/skills/skills/career/job-hunter/rules/writing-rules.md +81 -0
  98. package/resources/skills/skills/design/banana/SKILL.md +375 -0
  99. package/resources/skills/skills/design/banana/references/cost-tracking.md +47 -0
  100. package/resources/skills/skills/design/banana/references/gemini-models.md +236 -0
  101. package/resources/skills/skills/design/banana/references/mcp-tools.md +145 -0
  102. package/resources/skills/skills/design/banana/references/post-processing.md +192 -0
  103. package/resources/skills/skills/design/banana/references/presets.md +69 -0
  104. package/resources/skills/skills/design/banana/references/prompt-engineering.md +481 -0
  105. package/resources/skills/skills/design/banana/scripts/batch.py +97 -0
  106. package/resources/skills/skills/design/banana/scripts/cost_tracker.py +191 -0
  107. package/resources/skills/skills/design/banana/scripts/edit.py +159 -0
  108. package/resources/skills/skills/design/banana/scripts/generate.py +168 -0
  109. package/resources/skills/skills/design/banana/scripts/presets.py +154 -0
  110. package/resources/skills/skills/design/banana/scripts/setup_mcp.py +151 -0
  111. package/resources/skills/skills/design/banana/scripts/validate_setup.py +133 -0
  112. package/resources/skills/skills/gstack/ship/SKILL.md +13 -0
  113. package/resources/skills/skills/media/3d-logo-animation/SKILL.md +59 -0
  114. package/resources/skills/skills/media/action-figure-generator/SKILL.md +48 -0
  115. package/resources/skills/skills/media/ad-creative/SKILL.md +79 -0
  116. package/resources/skills/skills/media/ai-clipping/SKILL.md +194 -0
  117. package/resources/skills/skills/media/ai-clipping/scripts/run-ai-clipping.sh +200 -0
  118. package/resources/skills/skills/media/ai-fight-scene/SKILL.md +132 -0
  119. package/resources/skills/skills/media/amazon-product-listing/SKILL.md +68 -0
  120. package/resources/skills/skills/media/animal-video-generator/SKILL.md +59 -0
  121. package/resources/skills/skills/media/award-ceremony-video/SKILL.md +87 -0
  122. package/resources/skills/skills/media/blog-header/SKILL.md +61 -0
  123. package/resources/skills/skills/media/brand-kit/SKILL.md +72 -0
  124. package/resources/skills/skills/media/brochures/SKILL.md +65 -0
  125. package/resources/skills/skills/media/cartoon-dance-animation/SKILL.md +62 -0
  126. package/resources/skills/skills/media/character-story-video/SKILL.md +84 -0
  127. package/resources/skills/skills/media/chibi-collage-effect/SKILL.md +63 -0
  128. package/resources/skills/skills/media/cinema-director/SKILL.md +93 -0
  129. package/resources/skills/skills/media/cinema-director/scripts/generate-film.sh +78 -0
  130. package/resources/skills/skills/media/color-analysis-board/SKILL.md +71 -0
  131. package/resources/skills/skills/media/core-edit/SKILL.md +48 -0
  132. package/resources/skills/skills/media/core-edit/edit-image.sh +54 -0
  133. package/resources/skills/skills/media/core-edit/enhance-image.sh +191 -0
  134. package/resources/skills/skills/media/core-edit/lipsync.sh +144 -0
  135. package/resources/skills/skills/media/core-edit/video-effects.sh +193 -0
  136. package/resources/skills/skills/media/core-media/SKILL.md +49 -0
  137. package/resources/skills/skills/media/core-media/create-music.sh +169 -0
  138. package/resources/skills/skills/media/core-media/generate-image.sh +161 -0
  139. package/resources/skills/skills/media/core-media/generate-video.sh +137 -0
  140. package/resources/skills/skills/media/core-media/image-to-video.sh +228 -0
  141. package/resources/skills/skills/media/core-media/schema_data.json +18708 -0
  142. package/resources/skills/skills/media/core-media/upload.sh +41 -0
  143. package/resources/skills/skills/media/core-platform/SKILL.md +41 -0
  144. package/resources/skills/skills/media/core-platform/check-result.sh +37 -0
  145. package/resources/skills/skills/media/core-platform/setup.sh +31 -0
  146. package/resources/skills/skills/media/couple-grid-creator/SKILL.md +47 -0
  147. package/resources/skills/skills/media/design-guide/SKILL.md +73 -0
  148. package/resources/skills/skills/media/drone-style-video/SKILL.md +61 -0
  149. package/resources/skills/skills/media/fashion-try-on/SKILL.md +61 -0
  150. package/resources/skills/skills/media/floor-plan-rendering/SKILL.md +56 -0
  151. package/resources/skills/skills/media/freeze-effect-video/SKILL.md +100 -0
  152. package/resources/skills/skills/media/giant-product-showcase/SKILL.md +61 -0
  153. package/resources/skills/skills/media/instagram-post/SKILL.md +58 -0
  154. package/resources/skills/skills/media/interior-design/SKILL.md +61 -0
  155. package/resources/skills/skills/media/interior-design-visualizer/SKILL.md +57 -0
  156. package/resources/skills/skills/media/jewelry-product-video/SKILL.md +61 -0
  157. package/resources/skills/skills/media/kdenlive/SKILL.md +106 -0
  158. package/resources/skills/skills/media/kdenlive/scripts/assemble.sh +57 -0
  159. package/resources/skills/skills/media/kdenlive/scripts/common.sh +30 -0
  160. package/resources/skills/skills/media/kdenlive/scripts/inspect.sh +19 -0
  161. package/resources/skills/skills/media/kdenlive/scripts/reframe.sh +22 -0
  162. package/resources/skills/skills/media/kdenlive/scripts/render.sh +16 -0
  163. package/resources/skills/skills/media/kdenlive/scripts/title-card.sh +25 -0
  164. package/resources/skills/skills/media/keyboard-art-maker/SKILL.md +44 -0
  165. package/resources/skills/skills/media/logo-branding/SKILL.md +70 -0
  166. package/resources/skills/skills/media/logo-creator/SKILL.md +80 -0
  167. package/resources/skills/skills/media/logo-creator/scripts/create-logo.sh +38 -0
  168. package/resources/skills/skills/media/logo-generator/SKILL.md +56 -0
  169. package/resources/skills/skills/media/multi-angle-reshoot/SKILL.md +70 -0
  170. package/resources/skills/skills/media/multi-angle-shots/SKILL.md +73 -0
  171. package/resources/skills/skills/media/music-video/SKILL.md +61 -0
  172. package/resources/skills/skills/media/nano-banana/SKILL.md +80 -0
  173. package/resources/skills/skills/media/nano-banana/scripts/generate-nano-art.sh +54 -0
  174. package/resources/skills/skills/media/one-shot-video/SKILL.md +56 -0
  175. package/resources/skills/skills/media/photo-pack-generator/SKILL.md +205 -0
  176. package/resources/skills/skills/media/photo-pack-generator/scripts/generate-pack.sh +241 -0
  177. package/resources/skills/skills/media/product-ad-cinematic/SKILL.md +78 -0
  178. package/resources/skills/skills/media/product-campaign/SKILL.md +76 -0
  179. package/resources/skills/skills/media/product-showcase-video/SKILL.md +60 -0
  180. package/resources/skills/skills/media/product-video-ad-maker/SKILL.md +59 -0
  181. package/resources/skills/skills/media/rednote-cover/SKILL.md +57 -0
  182. package/resources/skills/skills/media/seedance-2/SKILL.md +632 -0
  183. package/resources/skills/skills/media/seedance-2/scripts/generate-seedance.sh +701 -0
  184. package/resources/skills/skills/media/selfie-with-celebrities/SKILL.md +64 -0
  185. package/resources/skills/skills/media/social-media-video/SKILL.md +277 -0
  186. package/resources/skills/skills/media/social-media-video/scripts/run-social-video.sh +316 -0
  187. package/resources/skills/skills/media/social-pack/SKILL.md +58 -0
  188. package/resources/skills/skills/media/storyboard/SKILL.md +57 -0
  189. package/resources/skills/skills/media/storyboard-to-cooking-video/SKILL.md +143 -0
  190. package/resources/skills/skills/media/talking-baby-video/SKILL.md +57 -0
  191. package/resources/skills/skills/media/ugc-ads-workflow/SKILL.md +70 -0
  192. package/resources/skills/skills/media/ugc-lifestyle-try-on/SKILL.md +65 -0
  193. package/resources/skills/skills/media/ugc-video-factory/SKILL.md +134 -0
  194. package/resources/skills/skills/media/ui-design/SKILL.md +81 -0
  195. package/resources/skills/skills/media/ui-design/scripts/generate-mockup.sh +49 -0
  196. package/resources/skills/skills/media/url-to-design/SKILL.md +61 -0
  197. package/resources/skills/skills/media/workflow/SKILL.md +197 -0
  198. package/resources/skills/skills/media/workflow/scripts/discover-workflow.sh +18 -0
  199. package/resources/skills/skills/media/workflow/scripts/generate-workflow.sh +33 -0
  200. package/resources/skills/skills/media/workflow/scripts/interactive-run.sh +16 -0
  201. package/resources/skills/skills/media/workflow/scripts/list-workflows.sh +20 -0
  202. package/resources/skills/skills/media/workflow/scripts/run-workflow.sh +34 -0
  203. package/resources/skills/skills/media/youtube-shorts/SKILL.md +173 -0
  204. package/resources/skills/skills/media/youtube-shorts/scripts/run-youtube-shorts.sh +141 -0
  205. package/resources/skills/skills/media/youtube-thumbnail/SKILL.md +66 -0
  206. package/resources/skills/skills/meta/cue-developer/references/architecture.md +2 -2
  207. package/resources/skills/skills/meta/cue-usage/SKILL.md +1 -1
  208. package/resources/skills/skills/meta/profile-fit-monitor/SKILL.md +2 -2
  209. package/resources/skills/skills/meta/profile-optimizer/SKILL.md +1 -1
  210. package/resources/skills/skills/meta/profile-suggest/SKILL.md +7 -7
  211. package/resources/skills/skills/meta/profile-summon/SKILL.md +159 -0
  212. package/resources/skills/skills/meta/profile-summon/evals/evals.json +53 -0
  213. package/resources/skills/skills/meta/save-profile/SKILL.md +1 -1
  214. package/resources/skills/skills/meta/skill-reviewer/SKILL.md +3 -0
  215. package/resources/skills/skills/meta/skill-reviewer/references/tdd-for-skills.md +55 -0
  216. package/resources/skills/skills/research/find-skills/SKILL.md +1 -1
  217. package/resources/skills/skills/review/code-review-deep/SKILL.md +20 -0
  218. package/resources/skills/skills/security/trivy-scan/SKILL.md +139 -0
  219. package/resources/skills/skills/security/trivy-scan/scripts/ensure-trivy.sh +21 -0
  220. package/resources/skills/skills/tools/ccusage/SKILL.md +142 -0
  221. package/src/commands/_index.ts +8 -0
  222. package/src/commands/ai.ts +2 -2
  223. package/src/commands/auto-detect.test.ts +74 -0
  224. package/src/commands/auto-detect.ts +9 -7
  225. package/src/commands/cli.test.ts +20 -4
  226. package/src/commands/cli.ts +36 -20
  227. package/src/commands/create-profile.ts +2 -2
  228. package/src/commands/debug.ts +2 -2
  229. package/src/commands/discover.ts +14 -4
  230. package/src/commands/export-docker.ts +1 -1
  231. package/src/commands/features-batch1.test.ts +1 -1
  232. package/src/commands/gates.ts +1 -1
  233. package/src/commands/import-profile.ts +1 -1
  234. package/src/commands/init.ts +15 -11
  235. package/src/commands/install.test.ts +192 -0
  236. package/src/commands/install.ts +610 -0
  237. package/src/commands/launch-handoff.e2e.test.ts +33 -1
  238. package/src/commands/launch.e2e.test.ts +15 -10
  239. package/src/commands/launch.ts +73 -116
  240. package/src/commands/materialize.ts +2 -2
  241. package/src/commands/prune.ts +1 -1
  242. package/src/commands/security-audit.ts +1 -1
  243. package/src/commands/shell.ts +7 -7
  244. package/src/commands/skill-report.ts +1 -1
  245. package/src/commands/skills.ts +3 -3
  246. package/src/commands/snapshot.ts +2 -2
  247. package/src/commands/summon.test.ts +116 -0
  248. package/src/commands/summon.ts +338 -0
  249. package/src/commands/trigger-gaps.ts +1 -1
  250. package/src/commands/use.ts +47 -3
  251. package/src/commands/watch-live.ts +5 -5
  252. package/src/commands/watch.ts +8 -8
  253. package/src/index.ts +2 -0
  254. package/src/lib/active-sessions.test.ts +3 -3
  255. package/src/lib/active-sessions.ts +4 -4
  256. package/src/lib/auto-detect.test.ts +172 -8
  257. package/src/lib/auto-detect.ts +191 -136
  258. package/src/lib/codex-persona-parity.test.ts +58 -0
  259. package/src/lib/companion-detect.test.ts +43 -1
  260. package/src/lib/companion-detect.ts +35 -0
  261. package/src/lib/credentials-sync.test.ts +121 -1
  262. package/src/lib/credentials-sync.ts +95 -1
  263. package/src/lib/cwd-resolver.test.ts +8 -8
  264. package/src/lib/cwd-resolver.ts +2 -2
  265. package/src/lib/dashboard-merge.test.ts +9 -4
  266. package/src/lib/dashboard-server.ts +1 -1
  267. package/src/lib/picker.test.ts +1 -1
  268. package/src/lib/picker.ts +5 -5
  269. package/src/lib/profile-merge.test.ts +8 -0
  270. package/src/lib/profile-names.test.ts +3 -3
  271. package/src/lib/runtime-install.ts +166 -0
  272. package/src/lib/runtime-materializer.test.ts +137 -0
  273. package/src/lib/runtime-materializer.ts +105 -2
  274. package/src/lib/skill-router.test.ts +38 -0
  275. package/src/lib/skill-router.ts +65 -4
  276. package/profiles/eu-tender-research/README.md +0 -48
  277. package/profiles/eu-tender-research/logo.png +0 -0
  278. package/profiles/eu-tender-research/profile.yaml +0 -108
@@ -0,0 +1,701 @@
1
+ #!/bin/bash
2
+ # Expert Skill: Seedance 2 Cinema Expert
3
+ # Translates creative intent into 'Director-Level' technical directives for Seedance 2.0.
4
+ #
5
+ # Modes: t2v | i2v | extend | first-last | omni | omni-train | character | video-edit | watermark-remove
6
+ # Tiers: chinese (default) | global | vip
7
+ # Options: --fast for fast-queue variants (global/vip only)
8
+ #
9
+ # Requires: bash 3.2+, curl, jq, python3
10
+
11
+ set -euo pipefail
12
+
13
+ SUBJECT=""
14
+ INTENT="cinematic"
15
+ ASPECT="16:9"
16
+ DURATION=5
17
+ QUALITY="basic"
18
+ AUDIO_FLAG=""
19
+ VIEW=false
20
+ MODE="t2v"
21
+ TIER="chinese"
22
+ FAST=false
23
+ IMAGE_URLS=()
24
+ IMAGE_FILES=()
25
+ VIDEO_URLS=()
26
+ VIDEO_FILES=()
27
+ AUDIO_URLS=()
28
+ AUDIO_FILES=()
29
+ EXTEND_REQUEST_ID=""
30
+ CHARACTER_NAME=""
31
+ CHARACTER_DESC=""
32
+ REMOVE_WATERMARK=false
33
+ PRO_WATERMARK=false
34
+ ASYNC=false
35
+ JSON_ONLY=false
36
+ MAX_WAIT=600
37
+ POLL_INTERVAL=5
38
+
39
+ MUAPI_BASE="https://api.muapi.ai/api/v1"
40
+
41
+ # Load .env if present (suppress errors, disable nounset temporarily)
42
+ if [ -f ".env" ]; then
43
+ set +u; source .env 2>/dev/null || true; set -u
44
+ fi
45
+
46
+ # ============================================================
47
+ # Argument parsing
48
+ # ============================================================
49
+ while [[ $# -gt 0 ]]; do
50
+ case $1 in
51
+ --mode) MODE="$2"; shift 2 ;;
52
+ --tier) TIER="$2"; shift 2 ;;
53
+ --fast) FAST=true; shift ;;
54
+ --subject) SUBJECT="$2"; shift 2 ;;
55
+ --intent) INTENT="$2"; shift 2 ;;
56
+ --aspect) ASPECT="$2"; shift 2 ;;
57
+ --duration) DURATION="$2"; shift 2 ;;
58
+ --quality) QUALITY="$2"; shift 2 ;;
59
+ --no-audio) AUDIO_FLAG="--no-audio"; shift ;;
60
+ --view) VIEW=true; shift ;;
61
+ --image|--image-url) IMAGE_URLS+=("$2"); shift 2 ;;
62
+ --file|--image-file) IMAGE_FILES+=("$2"); shift 2 ;;
63
+ --video-url) VIDEO_URLS+=("$2"); shift 2 ;;
64
+ --video-file) VIDEO_FILES+=("$2"); shift 2 ;;
65
+ --audio-url) AUDIO_URLS+=("$2"); shift 2 ;;
66
+ --audio-file) AUDIO_FILES+=("$2"); shift 2 ;;
67
+ --request-id) EXTEND_REQUEST_ID="$2"; shift 2 ;;
68
+ --character-name) CHARACTER_NAME="$2"; shift 2 ;;
69
+ --character-desc) CHARACTER_DESC="$2"; shift 2 ;;
70
+ --remove-watermark) REMOVE_WATERMARK=true; shift ;;
71
+ --pro) PRO_WATERMARK=true; shift ;;
72
+ --async) ASYNC=true; shift ;;
73
+ --json) JSON_ONLY=true; shift ;;
74
+ --help|-h)
75
+ cat <<'HELP'
76
+ Seedance 2 Cinema Expert
77
+ Usage: bash generate-seedance.sh --mode MODE [options]
78
+
79
+ MODES
80
+ t2v Text-to-Video
81
+ i2v Image-to-Video (1–9 images)
82
+ extend Extend an existing Seedance 2.0 video (Chinese tier only)
83
+ first-last First & Last Frame interpolation (Global/VIP; 1–2 images)
84
+ omni Omni Reference — images + audio + optional @character refs
85
+ omni-train Train a custom Omni Reference character (one reference image)
86
+ character Build a character sheet from 1–3 images
87
+ video-edit Edit a video with prompt + optional reference images (Chinese tier)
88
+ watermark-remove Remove watermark from a Seedance 2.0 video
89
+
90
+ TIERS (default: chinese)
91
+ chinese seedance-v2.0-* endpoints — lower cost, low censorship
92
+ global seedance-2-* endpoints — 21:9/1:1 aspect ratios, flexible 4–15s duration
93
+ vip seedance-2-vip-* endpoints — fast queue + low censorship
94
+
95
+ OPTIONS
96
+ --fast Use fast-queue variant (global/vip tiers only)
97
+ --subject TEXT Scene description / prompt (required for most modes)
98
+ --intent TYPE reveal|tense|epic|narrative|product|educational|comedy|fpv|drone|flythrough (default: cinematic)
99
+ --aspect RATIO 16:9|9:16|4:3|3:4 — global/vip also support: 21:9|1:1 (default: 16:9)
100
+ --duration N Chinese: 5|10|15 s — global/vip: any integer 4–15 s (default: 5)
101
+ --quality Q basic|high (Chinese tier only; default: basic)
102
+ --view Download and open the output file (macOS)
103
+ --async Return request_id immediately without polling
104
+ --json Raw JSON output only
105
+
106
+ INPUTS
107
+ --image URL Image URL (repeatable, up to 9)
108
+ --file PATH Local image file — auto-uploaded (repeatable)
109
+ --video-url URL Reference video URL (repeatable, up to 3; Chinese i2v/omni only)
110
+ --video-file PATH Local video file — auto-uploaded (repeatable)
111
+ --audio-url URL Reference audio URL (repeatable, up to 3)
112
+ --audio-file PATH Local audio file — auto-uploaded (repeatable)
113
+
114
+ MODE-SPECIFIC OPTIONS
115
+ extend: --request-id ID request_id of the original Seedance 2.0 video
116
+ character: --character-name NAME optional character label
117
+ --subject TEXT optional outfit/costume description
118
+ omni-train: --character-name NAME character name for @omni-character ref
119
+ --character-desc TEXT optional character description
120
+ video-edit: --video-url/--video-file source video (1 max, 10MB, 15s)
121
+ --remove-watermark strip watermark from edited output
122
+ watermark-remove: --video-url/--video-file Seedance 2.0 video URL
123
+ --pro use Pro remover (100MB limit)
124
+
125
+ EXAMPLES
126
+ # Chinese T2V (default)
127
+ bash generate-seedance.sh --subject "hidden Andes temple at dawn" --intent epic --view
128
+
129
+ # Global T2V — 21:9, 12s, fast queue
130
+ bash generate-seedance.sh --tier global --fast --subject "neon cyberpunk alley" --aspect "21:9" --duration 12
131
+
132
+ # VIP I2V
133
+ bash generate-seedance.sh --mode i2v --tier vip --fast --file hero.jpg --subject "hero strides forward"
134
+
135
+ # First & Last Frame (global)
136
+ bash generate-seedance.sh --mode first-last --tier global --file start.jpg --file end.jpg \
137
+ --subject "smooth cinematic transition"
138
+
139
+ # Omni Reference (global, with character ref)
140
+ bash generate-seedance.sh --mode omni --tier global --file portrait.jpg \
141
+ --subject "@image1 walks through a neon-lit city at night"
142
+
143
+ # Train Omni Reference character
144
+ bash generate-seedance.sh --mode omni-train --file portrait.jpg \
145
+ --character-name "Alex" --character-desc "A brave explorer"
146
+
147
+ # Chinese character sheet
148
+ bash generate-seedance.sh --mode character --file ref1.jpg --file ref2.jpg \
149
+ --character-name "Hero" --subject "red leather jacket"
150
+
151
+ # Video Edit
152
+ bash generate-seedance.sh --mode video-edit \
153
+ --video-url "https://example.com/input.mp4" --file replacement.jpg \
154
+ --subject "Replace the runner with @image1, preserve exact motion and camera"
155
+
156
+ # Watermark Remove (Pro)
157
+ bash generate-seedance.sh --mode watermark-remove --video-url "https://example.com/v.mp4" --pro
158
+ HELP
159
+ exit 0 ;;
160
+ *)
161
+ echo "Warning: Unknown argument '$1' — skipping." >&2
162
+ shift ;;
163
+ esac
164
+ done
165
+
166
+ # ============================================================
167
+ # Validate API key
168
+ # ============================================================
169
+ if [ -z "${MUAPI_KEY:-}" ]; then
170
+ echo "Error: MUAPI_KEY is not set. Export it or add to .env" >&2
171
+ exit 1
172
+ fi
173
+
174
+ HEADERS=(-H "x-api-key: $MUAPI_KEY" -H "Content-Type: application/json")
175
+
176
+ # ============================================================
177
+ # Director's Cinematography Grammar
178
+ # ============================================================
179
+ case "$INTENT" in
180
+ reveal)
181
+ MOVEMENT="Slow crane up and tilt down, wide establishing shot."
182
+ LIGHTING="Volumetric god rays, golden hour atmosphere, warm bloom."
183
+ OPTICS="Deep focus, anamorphic widescreen, ultra-high clarity."
184
+ ;;
185
+ tense)
186
+ MOVEMENT="Handheld jittery movement, dutch angle close-up, unstable framing."
187
+ LIGHTING="Low key, harsh shadows, flickering magenta neon, split lighting."
188
+ OPTICS="Shallow depth of field, anamorphic lens flare, slight motion blur."
189
+ ;;
190
+ epic)
191
+ MOVEMENT="Dolly in with circular orbit, low hero angle, sweeping arc."
192
+ LIGHTING="Dramatic rim lighting, high contrast cinematic grade, specular highlights."
193
+ OPTICS="Anamorphic 35mm, sharp focus on subject, chromatic aberration edges."
194
+ ;;
195
+ narrative)
196
+ MOVEMENT="Smooth tracking shot following subject, natural Steadicam motion."
197
+ LIGHTING="Natural soft light, blue hour tones, practical light sources."
198
+ OPTICS="Standard 50mm, realistic bokeh, minimal distortion."
199
+ ;;
200
+ product)
201
+ MOVEMENT="Static camera with slow macro orbit, precision product reveal."
202
+ LIGHTING="Perfect even exposure, specular highlights on product surface."
203
+ OPTICS="Macro lens, zero distortion, commercial grade clarity."
204
+ ;;
205
+ educational)
206
+ MOVEMENT="Slow push in, clinical camera movements, no handheld jitter."
207
+ LIGHTING="Even neutral lighting, high clarity, no dramatic shadows."
208
+ OPTICS="Standard lens, deep focus, semi-transparent CGI visualization."
209
+ ;;
210
+ comedy)
211
+ MOVEMENT="Reactive handheld, punchy cuts, exaggerated zooms."
212
+ LIGHTING="Bright even lighting, warm comedic tone, no harsh shadows."
213
+ OPTICS="Slight wide angle, expressive framing, snappy focus pulls."
214
+ ;;
215
+ fpv)
216
+ MOVEMENT="Immersive first-person POV, continuous forward motion at eye level, slight natural stabilization with GoPro-style wide angle. No cuts throughout."
217
+ LIGHTING="Natural ambient light matching environment, subtle lens flare, peripheral depth-of-field blur."
218
+ OPTICS="Ultra-wide fisheye-adjacent lens, slight barrel distortion, high motion clarity, natural vignette edges."
219
+ ;;
220
+ drone)
221
+ MOVEMENT="Cinematic aerial drone shot, smooth gimbal-stabilized descent from high altitude, sweeping lateral arc resolving into establishing medium shot. DJI Inspire aesthetic."
222
+ LIGHTING="Golden hour atmosphere, long directional shadows across terrain, volumetric haze on horizon."
223
+ OPTICS="Wide-angle aerial lens, deep focus across entire frame, high altitude clarity, natural atmospheric haze."
224
+ ;;
225
+ flythrough)
226
+ MOVEMENT="Continuous ground-level architectural dolly-through, seamless forward movement through connected spaces. One-take no-cut trajectory."
227
+ LIGHTING="Practical interior lighting with natural window light spill, soft even exposure transitions between zones."
228
+ OPTICS="Wide-angle rectilinear lens, deep focus, minimal distortion, smooth exposure transitions."
229
+ ;;
230
+ *)
231
+ MOVEMENT="Smooth cinematic pan, balanced stable framing."
232
+ LIGHTING="Natural studio lighting, balanced highlights and shadows."
233
+ OPTICS="Standard cinematic lens, high-fidelity optics."
234
+ ;;
235
+ esac
236
+
237
+ # ============================================================
238
+ # Helper: upload a local file — prints CDN URL to stdout, errors to stderr
239
+ # Returns 1 on failure (caller should handle with || exit 1)
240
+ # ============================================================
241
+ upload_file() {
242
+ local FPATH="$1"
243
+ if [ ! -f "$FPATH" ]; then
244
+ echo "Error: File not found: $FPATH" >&2
245
+ return 1
246
+ fi
247
+ [ "$JSON_ONLY" = false ] && echo "Uploading $(basename "$FPATH")..." >&2
248
+ local RESP URL
249
+ RESP=$(curl -s -X POST "${MUAPI_BASE}/upload_file" \
250
+ -H "x-api-key: $MUAPI_KEY" \
251
+ -F "file=@${FPATH}")
252
+ URL=$(echo "$RESP" | jq -r '.url // empty')
253
+ if [ -z "$URL" ]; then
254
+ echo "Error: Upload failed for $(basename "$FPATH"): $(echo "$RESP" | jq -r '.error // .detail // "unknown"')" >&2
255
+ return 1
256
+ fi
257
+ echo "$URL"
258
+ }
259
+
260
+ # ============================================================
261
+ # Helper: build a JSON array string from positional args
262
+ # Usage: build_json_array "${MY_ARRAY[@]}" — empty array safe
263
+ # ============================================================
264
+ build_json_array() {
265
+ local JSON="[" i=0 elem
266
+ for elem in "$@"; do
267
+ [ "$i" -gt 0 ] && JSON="${JSON},"
268
+ JSON="${JSON}\"${elem}\""
269
+ i=$(( i + 1 ))
270
+ done
271
+ echo "${JSON}]"
272
+ }
273
+
274
+ # ============================================================
275
+ # Helper: JSON-encode a string (handles quotes, backslashes, newlines)
276
+ # Usage: echo "text" | json_string
277
+ # ============================================================
278
+ json_string() {
279
+ python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip()))'
280
+ }
281
+
282
+ # ============================================================
283
+ # Helper: poll predictions/<id>/result until completed or failed
284
+ # Prints final JSON to stdout; status lines go to stderr.
285
+ # ============================================================
286
+ poll_result() {
287
+ local REQ_ID="$1"
288
+ local ELAPSED=0 LAST_STATUS="" RESULT STATUS URL EXT OUTPUT_DIR TEMP_FILE
289
+
290
+ while [ "$ELAPSED" -lt "$MAX_WAIT" ]; do
291
+ sleep "$POLL_INTERVAL"
292
+ ELAPSED=$(( ELAPSED + POLL_INTERVAL ))
293
+
294
+ RESULT=$(curl -s -X GET "${MUAPI_BASE}/predictions/${REQ_ID}/result" \
295
+ -H "x-api-key: $MUAPI_KEY" \
296
+ -H "Content-Type: application/json")
297
+
298
+ STATUS=$(echo "$RESULT" | jq -r '.status // "unknown"')
299
+
300
+ if [ "$STATUS" != "$LAST_STATUS" ] && [ "$JSON_ONLY" = false ]; then
301
+ echo "Status: $STATUS (${ELAPSED}s elapsed)" >&2
302
+ LAST_STATUS="$STATUS"
303
+ fi
304
+
305
+ if [ "$STATUS" = "completed" ]; then
306
+ URL=$(echo "$RESULT" | jq -r '.outputs[0] // empty')
307
+ [ "$JSON_ONLY" = false ] && echo "Done! Output: $URL" >&2
308
+ if [ "$VIEW" = true ] && [ -n "$URL" ]; then
309
+ EXT="${URL##*.}"
310
+ # Detect non-extension fragment (query strings, etc.) → default mp4
311
+ case "$EXT" in
312
+ mp4|mov|webm|jpg|jpeg|png|gif|webp) ;;
313
+ *) EXT="mp4" ;;
314
+ esac
315
+ OUTPUT_DIR="${PWD}/media_outputs"
316
+ mkdir -p "$OUTPUT_DIR"
317
+ TEMP_FILE="$OUTPUT_DIR/muapi_$(date +%s).$EXT"
318
+ [ "$JSON_ONLY" = false ] && echo "Downloading → $TEMP_FILE" >&2
319
+ curl -s -o "$TEMP_FILE" "$URL"
320
+ [[ "$OSTYPE" == "darwin"* ]] && open "$TEMP_FILE"
321
+ fi
322
+ echo "$RESULT"
323
+ return 0
324
+
325
+ elif [ "$STATUS" = "failed" ]; then
326
+ echo "Error: Job failed — $(echo "$RESULT" | jq -r '.output.error // .error // "unknown"')" >&2
327
+ echo "$RESULT"
328
+ return 1
329
+ fi
330
+ done
331
+
332
+ echo "Error: Timeout after ${MAX_WAIT}s for request_id=$REQ_ID" >&2
333
+ return 1
334
+ }
335
+
336
+ # ============================================================
337
+ # Helper: submit JSON payload to endpoint, then poll (or return async)
338
+ # Usage: submit_and_poll ENDPOINT PAYLOAD [LABEL]
339
+ # ============================================================
340
+ submit_and_poll() {
341
+ local ENDPOINT="$1"
342
+ local PAYLOAD="$2"
343
+ local LABEL="${3:-$1}"
344
+ local SUBMIT API_ERROR REQUEST_ID
345
+
346
+ [ "$JSON_ONLY" = false ] && echo "Submitting to $LABEL..." >&2
347
+
348
+ SUBMIT=$(curl -s -X POST "${MUAPI_BASE}/${ENDPOINT}" "${HEADERS[@]}" -d "$PAYLOAD")
349
+
350
+ API_ERROR=$(echo "$SUBMIT" | jq -r '.error // .detail // empty')
351
+ if [ -n "$API_ERROR" ]; then
352
+ echo "Error: $API_ERROR" >&2
353
+ echo "$SUBMIT" >&2
354
+ exit 1
355
+ fi
356
+
357
+ REQUEST_ID=$(echo "$SUBMIT" | jq -r '.request_id // empty')
358
+ if [ -z "$REQUEST_ID" ]; then
359
+ echo "Error: No request_id returned from $ENDPOINT" >&2
360
+ echo "$SUBMIT" >&2
361
+ exit 1
362
+ fi
363
+
364
+ [ "$JSON_ONLY" = false ] && echo "Request ID: $REQUEST_ID" >&2
365
+
366
+ if [ "$ASYNC" = true ]; then
367
+ echo "$SUBMIT"
368
+ exit 0
369
+ fi
370
+
371
+ [ "$JSON_ONLY" = false ] && echo "Waiting for completion..." >&2
372
+ poll_result "$REQUEST_ID"
373
+ }
374
+
375
+ # ============================================================
376
+ # MODE: t2v — Text-to-Video
377
+ #
378
+ # Chinese: seedance-v2.0-t2v (16:9/9:16/4:3/3:4, quality, duration 5|10|15)
379
+ # Global: seedance-2-text-to-video{-fast} (+ 21:9/1:1, any 4–15s, no quality)
380
+ # VIP: seedance-2-vip-text-to-video{-fast}
381
+ # ============================================================
382
+ if [ "$MODE" = "t2v" ]; then
383
+ [ -z "$SUBJECT" ] && { echo "Error: --subject is required for t2v." >&2; exit 1; }
384
+
385
+ DIRECTOR_PROMPT="[SCENE] $SUBJECT. [LIGHTING] $LIGHTING [ACTION] Fluid continuous motion. [CAMERA] $MOVEMENT [STYLE] $OPTICS High-fidelity production grade, 24fps. Maintain high character consistency, zero flicker."
386
+
387
+ if [ "$TIER" = "chinese" ]; then
388
+ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
389
+ CORE_SCRIPT="$SCRIPT_DIR/../../core-media/generate-video.sh"
390
+ [ ! -f "$CORE_SCRIPT" ] && { echo "Error: Core script not found at $CORE_SCRIPT" >&2; exit 1; }
391
+
392
+ VIEW_FLAG=""; [ "$VIEW" = true ] && VIEW_FLAG="--view"
393
+ ASYNC_FLAG=""; [ "$ASYNC" = true ] && ASYNC_FLAG="--async"
394
+ JSON_FLAG=""; [ "$JSON_ONLY" = true ] && JSON_FLAG="--json"
395
+
396
+ # shellcheck disable=SC2086
397
+ bash "$CORE_SCRIPT" \
398
+ --prompt "$DIRECTOR_PROMPT" \
399
+ --model "seedance-v2.0-t2v" \
400
+ --aspect-ratio "$ASPECT" \
401
+ --duration "$DURATION" \
402
+ $AUDIO_FLAG $VIEW_FLAG $ASYNC_FLAG $JSON_FLAG
403
+ else
404
+ PROMPT_JSON=$(echo "$DIRECTOR_PROMPT" | json_string)
405
+ [ "$TIER" = "vip" ] && ENDPOINT="seedance-2-vip-text-to-video" || ENDPOINT="seedance-2-text-to-video"
406
+ [ "$FAST" = true ] && ENDPOINT="${ENDPOINT}-fast"
407
+ PAYLOAD="{\"prompt\": $PROMPT_JSON, \"aspect_ratio\": \"$ASPECT\", \"duration\": $DURATION}"
408
+ submit_and_poll "$ENDPOINT" "$PAYLOAD"
409
+ fi
410
+
411
+ # ============================================================
412
+ # MODE: i2v — Image-to-Video
413
+ #
414
+ # Chinese: seedance-v2.0-i2v (images_list + videos_list + audios_list, quality)
415
+ # Global: seedance-2-image-to-video{-fast} (1 img=first frame, 2-9=omni ref; no quality)
416
+ # VIP: seedance-2-vip-image-to-video{-fast}
417
+ # ============================================================
418
+ elif [ "$MODE" = "i2v" ]; then
419
+ for FPATH in "${IMAGE_FILES[@]+"${IMAGE_FILES[@]}"}"; do
420
+ URL=$(upload_file "$FPATH") || exit 1; IMAGE_URLS+=("$URL")
421
+ done
422
+ for FPATH in "${VIDEO_FILES[@]+"${VIDEO_FILES[@]}"}"; do
423
+ URL=$(upload_file "$FPATH") || exit 1; VIDEO_URLS+=("$URL")
424
+ done
425
+ for FPATH in "${AUDIO_FILES[@]+"${AUDIO_FILES[@]}"}"; do
426
+ URL=$(upload_file "$FPATH") || exit 1; AUDIO_URLS+=("$URL")
427
+ done
428
+
429
+ [ ${#IMAGE_URLS[@]} -eq 0 ] && { echo "Error: At least one --image or --file is required for i2v." >&2; exit 1; }
430
+ [ ${#IMAGE_URLS[@]} -gt 9 ] && { echo "Error: Maximum 9 images allowed." >&2; exit 1; }
431
+
432
+ if [ -n "$SUBJECT" ]; then
433
+ DIRECTOR_PROMPT="[ACTION] $SUBJECT. [CAMERA] $MOVEMENT [STYLE] $OPTICS Fluid continuous motion. Maintain high character consistency, zero flicker."
434
+ else
435
+ DIRECTOR_PROMPT="[CAMERA] $MOVEMENT [STYLE] $OPTICS Fluid continuous motion. Animate the provided image with cinematic realism."
436
+ fi
437
+ PROMPT_JSON=$(echo "$DIRECTOR_PROMPT" | json_string)
438
+ IMAGES_JSON=$(build_json_array "${IMAGE_URLS[@]}")
439
+
440
+ if [ "$TIER" = "chinese" ]; then
441
+ [ ${#VIDEO_URLS[@]} -gt 3 ] && { echo "Error: Maximum 3 reference videos allowed." >&2; exit 1; }
442
+ [ ${#AUDIO_URLS[@]} -gt 3 ] && { echo "Error: Maximum 3 reference audio files allowed." >&2; exit 1; }
443
+
444
+ PAYLOAD="{\"prompt\": $PROMPT_JSON, \"images_list\": $IMAGES_JSON, \"aspect_ratio\": \"$ASPECT\", \"duration\": $DURATION, \"quality\": \"$QUALITY\""
445
+ [ ${#VIDEO_URLS[@]} -gt 0 ] && PAYLOAD="${PAYLOAD}, \"videos_list\": $(build_json_array "${VIDEO_URLS[@]}")"
446
+ [ ${#AUDIO_URLS[@]} -gt 0 ] && PAYLOAD="${PAYLOAD}, \"audios_list\": $(build_json_array "${AUDIO_URLS[@]}")"
447
+ PAYLOAD="${PAYLOAD}}"
448
+ submit_and_poll "seedance-v2.0-i2v" "$PAYLOAD" "seedance-v2.0-i2v (${#IMAGE_URLS[@]} image(s))"
449
+ else
450
+ [ "$TIER" = "vip" ] && ENDPOINT="seedance-2-vip-image-to-video" || ENDPOINT="seedance-2-image-to-video"
451
+ [ "$FAST" = true ] && ENDPOINT="${ENDPOINT}-fast"
452
+ PAYLOAD="{\"prompt\": $PROMPT_JSON, \"images_list\": $IMAGES_JSON, \"duration\": $DURATION}"
453
+ submit_and_poll "$ENDPOINT" "$PAYLOAD" "$ENDPOINT (${#IMAGE_URLS[@]} image(s))"
454
+ fi
455
+
456
+ # ============================================================
457
+ # MODE: extend — Extend a Seedance 2.0 video (Chinese tier)
458
+ # Endpoint: seedance-v2.0-extend
459
+ # ============================================================
460
+ elif [ "$MODE" = "extend" ]; then
461
+ [ -z "$EXTEND_REQUEST_ID" ] && { echo "Error: --request-id is required for extend." >&2; exit 1; }
462
+
463
+ if [ -n "$SUBJECT" ]; then
464
+ EXT_PROMPT="[CONTINUATION] $SUBJECT. [CAMERA] $MOVEMENT [STYLE] $OPTICS Seamless continuation of previous scene."
465
+ PROMPT_JSON=$(echo "$EXT_PROMPT" | json_string)
466
+ PAYLOAD="{\"request_id\": \"$EXTEND_REQUEST_ID\", \"prompt\": $PROMPT_JSON, \"duration\": $DURATION, \"quality\": \"$QUALITY\"}"
467
+ else
468
+ PAYLOAD="{\"request_id\": \"$EXTEND_REQUEST_ID\", \"duration\": $DURATION, \"quality\": \"$QUALITY\"}"
469
+ fi
470
+
471
+ submit_and_poll "seedance-v2.0-extend" "$PAYLOAD"
472
+
473
+ # ============================================================
474
+ # MODE: first-last — First & Last Frame interpolation (Global/VIP only)
475
+ #
476
+ # Global: seedance-2-first-last-frame{-fast} (1 img=anchor, 2 imgs=first+last)
477
+ # VIP: seedance-2-vip-first-last-frame{-fast}
478
+ # Note: Aspect ratio is inferred from the reference image.
479
+ # ============================================================
480
+ elif [ "$MODE" = "first-last" ]; then
481
+ [ "$TIER" = "chinese" ] && { echo "Error: --mode first-last requires --tier global or --tier vip." >&2; exit 1; }
482
+
483
+ for FPATH in "${IMAGE_FILES[@]+"${IMAGE_FILES[@]}"}"; do
484
+ URL=$(upload_file "$FPATH") || exit 1; IMAGE_URLS+=("$URL")
485
+ done
486
+
487
+ [ ${#IMAGE_URLS[@]} -eq 0 ] && { echo "Error: At least 1 --image or --file required for first-last." >&2; exit 1; }
488
+ [ ${#IMAGE_URLS[@]} -gt 2 ] && { echo "Error: first-last accepts 1 (anchor) or 2 (first+last) images." >&2; exit 1; }
489
+
490
+ IMAGES_JSON=$(build_json_array "${IMAGE_URLS[@]}")
491
+ PROMPT_JSON=$(echo "$SUBJECT" | json_string)
492
+
493
+ [ "$TIER" = "vip" ] && ENDPOINT="seedance-2-vip-first-last-frame" || ENDPOINT="seedance-2-first-last-frame"
494
+ [ "$FAST" = true ] && ENDPOINT="${ENDPOINT}-fast"
495
+
496
+ PAYLOAD="{\"prompt\": $PROMPT_JSON, \"images_list\": $IMAGES_JSON, \"duration\": $DURATION}"
497
+ submit_and_poll "$ENDPOINT" "$PAYLOAD" "$ENDPOINT (${#IMAGE_URLS[@]} frame(s))"
498
+
499
+ # ============================================================
500
+ # MODE: omni — Omni Reference (full multimodal)
501
+ #
502
+ # Chinese: seedance-2.0-omni-reference
503
+ # params: images_list, video_files, audio_files, aspect_ratio, duration, quality
504
+ # tags: @image1…@image9, @video1…@video3, @audio1…@audio3
505
+ # also: @character:<request_id>, @omni-character:<character_id>
506
+ #
507
+ # Global: seedance-2-omni-reference-no-video{-fast}
508
+ # params: images_list, audio_files, aspect_ratio, duration (no video, no quality)
509
+ #
510
+ # VIP: seedance-2-vip-omni-reference{-fast}
511
+ # params: images_list, audio_files, aspect_ratio, duration (no video, no quality)
512
+ # ============================================================
513
+ elif [ "$MODE" = "omni" ]; then
514
+ for FPATH in "${IMAGE_FILES[@]+"${IMAGE_FILES[@]}"}"; do
515
+ URL=$(upload_file "$FPATH") || exit 1; IMAGE_URLS+=("$URL")
516
+ done
517
+ for FPATH in "${VIDEO_FILES[@]+"${VIDEO_FILES[@]}"}"; do
518
+ URL=$(upload_file "$FPATH") || exit 1; VIDEO_URLS+=("$URL")
519
+ done
520
+ for FPATH in "${AUDIO_FILES[@]+"${AUDIO_FILES[@]}"}"; do
521
+ URL=$(upload_file "$FPATH") || exit 1; AUDIO_URLS+=("$URL")
522
+ done
523
+
524
+ [ -z "$SUBJECT" ] && { echo "Error: --subject is required for omni." >&2; exit 1; }
525
+ [ ${#IMAGE_URLS[@]} -gt 9 ] && { echo "Error: Maximum 9 images allowed." >&2; exit 1; }
526
+ [ ${#VIDEO_URLS[@]} -gt 3 ] && { echo "Error: Maximum 3 reference videos allowed." >&2; exit 1; }
527
+ [ ${#AUDIO_URLS[@]} -gt 3 ] && { echo "Error: Maximum 3 reference audio files allowed." >&2; exit 1; }
528
+
529
+ PROMPT_JSON=$(echo "$SUBJECT" | json_string)
530
+
531
+ if [ "$TIER" = "chinese" ]; then
532
+ PAYLOAD="{\"prompt\": $PROMPT_JSON, \"aspect_ratio\": \"$ASPECT\", \"duration\": $DURATION, \"quality\": \"$QUALITY\""
533
+ [ ${#IMAGE_URLS[@]} -gt 0 ] && PAYLOAD="${PAYLOAD}, \"images_list\": $(build_json_array "${IMAGE_URLS[@]}")"
534
+ [ ${#VIDEO_URLS[@]} -gt 0 ] && PAYLOAD="${PAYLOAD}, \"video_files\": $(build_json_array "${VIDEO_URLS[@]}")"
535
+ [ ${#AUDIO_URLS[@]} -gt 0 ] && PAYLOAD="${PAYLOAD}, \"audio_files\": $(build_json_array "${AUDIO_URLS[@]}")"
536
+ PAYLOAD="${PAYLOAD}}"
537
+ submit_and_poll "seedance-2.0-omni-reference" "$PAYLOAD" "seedance-v2.0-omni-reference"
538
+ else
539
+ if [ ${#VIDEO_URLS[@]} -gt 0 ]; then
540
+ echo "Warning: $TIER omni does not support video references — ignoring --video-url/--video-file." >&2
541
+ fi
542
+ [ "$TIER" = "vip" ] && ENDPOINT="seedance-2-vip-omni-reference" || ENDPOINT="seedance-2-omni-reference-no-video"
543
+ [ "$FAST" = true ] && ENDPOINT="${ENDPOINT}-fast"
544
+ PAYLOAD="{\"prompt\": $PROMPT_JSON, \"aspect_ratio\": \"$ASPECT\", \"duration\": $DURATION"
545
+ [ ${#IMAGE_URLS[@]} -gt 0 ] && PAYLOAD="${PAYLOAD}, \"images_list\": $(build_json_array "${IMAGE_URLS[@]}")"
546
+ [ ${#AUDIO_URLS[@]} -gt 0 ] && PAYLOAD="${PAYLOAD}, \"audio_files\": $(build_json_array "${AUDIO_URLS[@]}")"
547
+ PAYLOAD="${PAYLOAD}}"
548
+ submit_and_poll "$ENDPOINT" "$PAYLOAD"
549
+ fi
550
+
551
+ # ============================================================
552
+ # MODE: omni-train — Train a custom Omni Reference character
553
+ # Endpoint: seedance-2-omni-reference-train
554
+ # Params: image_url (single portrait), character_name, description
555
+ # Output: character_id — reference as @omni-character:<character_id>
556
+ # ============================================================
557
+ elif [ "$MODE" = "omni-train" ]; then
558
+ for FPATH in "${IMAGE_FILES[@]+"${IMAGE_FILES[@]}"}"; do
559
+ URL=$(upload_file "$FPATH") || exit 1; IMAGE_URLS+=("$URL")
560
+ done
561
+
562
+ [ ${#IMAGE_URLS[@]} -eq 0 ] && { echo "Error: Exactly 1 --image or --file required for omni-train." >&2; exit 1; }
563
+ [ ${#IMAGE_URLS[@]} -gt 1 ] && { echo "Error: omni-train accepts only 1 reference image." >&2; exit 1; }
564
+
565
+ PAYLOAD="{\"image_url\": \"${IMAGE_URLS[0]}\""
566
+ if [ -n "$CHARACTER_NAME" ]; then
567
+ NAME_JSON=$(echo "$CHARACTER_NAME" | json_string)
568
+ PAYLOAD="${PAYLOAD}, \"character_name\": $NAME_JSON"
569
+ fi
570
+ if [ -n "$CHARACTER_DESC" ]; then
571
+ DESC_JSON=$(echo "$CHARACTER_DESC" | json_string)
572
+ PAYLOAD="${PAYLOAD}, \"description\": $DESC_JSON"
573
+ fi
574
+ PAYLOAD="${PAYLOAD}}"
575
+
576
+ [ "$JSON_ONLY" = false ] && echo "Submitting omni-reference-train..." >&2
577
+ SUBMIT=$(curl -s -X POST "${MUAPI_BASE}/seedance-2-omni-reference-train" "${HEADERS[@]}" -d "$PAYLOAD")
578
+
579
+ API_ERROR=$(echo "$SUBMIT" | jq -r '.error // .detail // empty')
580
+ if [ -n "$API_ERROR" ]; then
581
+ echo "Error: $API_ERROR" >&2; echo "$SUBMIT" >&2; exit 1
582
+ fi
583
+
584
+ REQUEST_ID=$(echo "$SUBMIT" | jq -r '.request_id // empty')
585
+ if [ -z "$REQUEST_ID" ]; then
586
+ echo "Error: No request_id in response" >&2; echo "$SUBMIT" >&2; exit 1
587
+ fi
588
+
589
+ [ "$JSON_ONLY" = false ] && echo "Request ID: $REQUEST_ID" >&2
590
+ [ "$JSON_ONLY" = false ] && echo "Training started — use @omni-character:<character_id> once complete." >&2
591
+
592
+ if [ "$ASYNC" = true ]; then echo "$SUBMIT"; exit 0; fi
593
+
594
+ [ "$JSON_ONLY" = false ] && echo "Waiting for training to complete..." >&2
595
+ RESULT=$(poll_result "$REQUEST_ID")
596
+ echo "$RESULT"
597
+ CHAR_ID=$(echo "$RESULT" | jq -r '.outputs[0] // empty')
598
+ if [ "$JSON_ONLY" = false ] && [ -n "$CHAR_ID" ]; then
599
+ echo "Character trained! Reference as: @omni-character:$CHAR_ID" >&2
600
+ fi
601
+
602
+ # ============================================================
603
+ # MODE: character — Build a reusable character sheet from 1–3 images
604
+ # Endpoint: seedance-2-character
605
+ # Params: images_list (1–3), prompt (outfit description), character_name
606
+ # Output: request_id — reference as @character:<request_id>
607
+ # ============================================================
608
+ elif [ "$MODE" = "character" ]; then
609
+ for FPATH in "${IMAGE_FILES[@]+"${IMAGE_FILES[@]}"}"; do
610
+ URL=$(upload_file "$FPATH") || exit 1; IMAGE_URLS+=("$URL")
611
+ done
612
+
613
+ [ ${#IMAGE_URLS[@]} -eq 0 ] && { echo "Error: At least 1 --image or --file required for character." >&2; exit 1; }
614
+ [ ${#IMAGE_URLS[@]} -gt 3 ] && { echo "Error: Maximum 3 reference images for character training." >&2; exit 1; }
615
+
616
+ IMAGES_JSON=$(build_json_array "${IMAGE_URLS[@]}")
617
+ PAYLOAD="{\"images_list\": $IMAGES_JSON"
618
+ if [ -n "$SUBJECT" ]; then
619
+ OUTFIT_JSON=$(echo "$SUBJECT" | json_string)
620
+ PAYLOAD="${PAYLOAD}, \"prompt\": $OUTFIT_JSON"
621
+ fi
622
+ if [ -n "$CHARACTER_NAME" ]; then
623
+ NAME_JSON=$(echo "$CHARACTER_NAME" | json_string)
624
+ PAYLOAD="${PAYLOAD}, \"character_name\": $NAME_JSON"
625
+ fi
626
+ PAYLOAD="${PAYLOAD}}"
627
+
628
+ [ "$JSON_ONLY" = false ] && echo "Submitting character training (${#IMAGE_URLS[@]} image(s))..." >&2
629
+ SUBMIT=$(curl -s -X POST "${MUAPI_BASE}/seedance-2-character" "${HEADERS[@]}" -d "$PAYLOAD")
630
+
631
+ API_ERROR=$(echo "$SUBMIT" | jq -r '.error // .detail // empty')
632
+ if [ -n "$API_ERROR" ]; then
633
+ echo "Error: $API_ERROR" >&2; echo "$SUBMIT" >&2; exit 1
634
+ fi
635
+
636
+ REQUEST_ID=$(echo "$SUBMIT" | jq -r '.request_id // empty')
637
+ if [ -z "$REQUEST_ID" ]; then
638
+ echo "Error: No request_id in response" >&2; echo "$SUBMIT" >&2; exit 1
639
+ fi
640
+
641
+ [ "$JSON_ONLY" = false ] && echo "Request ID: $REQUEST_ID" >&2
642
+ [ "$JSON_ONLY" = false ] && echo "Use in prompts as: @character:$REQUEST_ID" >&2
643
+
644
+ if [ "$ASYNC" = true ]; then echo "$SUBMIT"; exit 0; fi
645
+
646
+ [ "$JSON_ONLY" = false ] && echo "Waiting for character sheet to complete..." >&2
647
+ poll_result "$REQUEST_ID"
648
+
649
+ # ============================================================
650
+ # MODE: video-edit — Edit a video with a prompt + optional reference images
651
+ # Endpoint: seedance-v2.0-video-edit
652
+ # Params: prompt, video_urls (1 max, 10MB/15s), images_list (up to 9),
653
+ # aspect_ratio, quality, remove_watermark
654
+ # ============================================================
655
+ elif [ "$MODE" = "video-edit" ]; then
656
+ for FPATH in "${IMAGE_FILES[@]+"${IMAGE_FILES[@]}"}"; do
657
+ URL=$(upload_file "$FPATH") || exit 1; IMAGE_URLS+=("$URL")
658
+ done
659
+ for FPATH in "${VIDEO_FILES[@]+"${VIDEO_FILES[@]}"}"; do
660
+ URL=$(upload_file "$FPATH") || exit 1; VIDEO_URLS+=("$URL")
661
+ done
662
+
663
+ [ -z "$SUBJECT" ] && { echo "Error: --subject (edit prompt) is required for video-edit." >&2; exit 1; }
664
+ [ ${#VIDEO_URLS[@]} -eq 0 ] && { echo "Error: --video-url or --video-file is required for video-edit." >&2; exit 1; }
665
+ [ ${#VIDEO_URLS[@]} -gt 1 ] && { echo "Error: video-edit supports 1 source video only (max 10MB, 15s)." >&2; exit 1; }
666
+ [ ${#IMAGE_URLS[@]} -gt 9 ] && { echo "Error: Maximum 9 reference images allowed." >&2; exit 1; }
667
+
668
+ PROMPT_JSON=$(echo "$SUBJECT" | json_string)
669
+ PAYLOAD="{\"prompt\": $PROMPT_JSON, \"video_urls\": [\"${VIDEO_URLS[0]}\"], \"aspect_ratio\": \"$ASPECT\", \"quality\": \"$QUALITY\""
670
+ [ ${#IMAGE_URLS[@]} -gt 0 ] && PAYLOAD="${PAYLOAD}, \"images_list\": $(build_json_array "${IMAGE_URLS[@]}")"
671
+ [ "$REMOVE_WATERMARK" = true ] && PAYLOAD="${PAYLOAD}, \"remove_watermark\": true"
672
+ PAYLOAD="${PAYLOAD}}"
673
+
674
+ submit_and_poll "seedance-v2.0-video-edit" "$PAYLOAD"
675
+
676
+ # ============================================================
677
+ # MODE: watermark-remove — Strip watermark from a Seedance 2.0 video
678
+ #
679
+ # Basic: seedance-2.0-watermark-remover
680
+ # Pro: seedance-2-video-watermark-remover-pro (--pro flag; up to 100MB)
681
+ # ============================================================
682
+ elif [ "$MODE" = "watermark-remove" ]; then
683
+ for FPATH in "${VIDEO_FILES[@]+"${VIDEO_FILES[@]}"}"; do
684
+ URL=$(upload_file "$FPATH") || exit 1; VIDEO_URLS+=("$URL")
685
+ done
686
+
687
+ [ ${#VIDEO_URLS[@]} -eq 0 ] && { echo "Error: --video-url or --video-file is required for watermark-remove." >&2; exit 1; }
688
+
689
+ PAYLOAD="{\"video_url\": \"${VIDEO_URLS[0]}\"}"
690
+
691
+ if [ "$PRO_WATERMARK" = true ]; then
692
+ submit_and_poll "seedance-2-video-watermark-remover-pro" "$PAYLOAD"
693
+ else
694
+ submit_and_poll "seedance-2.0-watermark-remover" "$PAYLOAD"
695
+ fi
696
+
697
+ else
698
+ echo "Error: Unknown mode '$MODE'." >&2
699
+ echo "Valid: t2v | i2v | extend | first-last | omni | omni-train | character | video-edit | watermark-remove" >&2
700
+ exit 1
701
+ fi