voxflow 1.14.0 → 1.15.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 (454) hide show
  1. package/README.md +69 -2
  2. package/bin/voxflow.js +27 -0
  3. package/dist/index.js +1 -1
  4. package/dist/remotion-bundle/02a2fb2eb80bc7bf.woff2 +0 -0
  5. package/dist/remotion-bundle/052ca5351e5e06ba.woff2 +0 -0
  6. package/dist/remotion-bundle/05853dd28f4019cb.woff2 +0 -0
  7. package/dist/remotion-bundle/072ead3737f7c0d0.woff2 +0 -0
  8. package/dist/remotion-bundle/07d4248613c86a2e.woff2 +0 -0
  9. package/dist/remotion-bundle/0884a5c2d1d2d99b.woff2 +0 -0
  10. package/dist/remotion-bundle/0b0e185b2752095e.woff2 +0 -0
  11. package/dist/remotion-bundle/0e66c11bde067d91.woff2 +0 -0
  12. package/dist/remotion-bundle/0f7794cfba2c5d21.woff2 +0 -0
  13. package/dist/remotion-bundle/0fdbae5a4365783a.woff2 +0 -0
  14. package/dist/remotion-bundle/112.bundle.js +11 -0
  15. package/dist/remotion-bundle/112.bundle.js.map +1 -0
  16. package/dist/remotion-bundle/113.bundle.js +11 -0
  17. package/dist/remotion-bundle/113.bundle.js.map +1 -0
  18. package/dist/remotion-bundle/119cae0c4c16f7ed.woff2 +0 -0
  19. package/dist/remotion-bundle/14725f649fd1e78c.woff2 +0 -0
  20. package/dist/remotion-bundle/14abe9e3f95f7888.woff2 +0 -0
  21. package/dist/remotion-bundle/163.bundle.js +14678 -0
  22. package/dist/remotion-bundle/163.bundle.js.map +1 -0
  23. package/dist/remotion-bundle/1808c54072bf6d14.woff2 +0 -0
  24. package/dist/remotion-bundle/18948bec3e3012fe.woff2 +0 -0
  25. package/dist/remotion-bundle/1a661c60d0fc84fc.woff2 +0 -0
  26. package/dist/remotion-bundle/1af94941e1bc7e1e.woff2 +0 -0
  27. package/dist/remotion-bundle/1bee0219595f606c.woff2 +0 -0
  28. package/dist/remotion-bundle/1bfd5da7ce9d4ec4.woff2 +0 -0
  29. package/dist/remotion-bundle/1c158d56f1884f3f.woff2 +0 -0
  30. package/dist/remotion-bundle/1cf5e88e667610eb.woff2 +0 -0
  31. package/dist/remotion-bundle/1d431bd10f53c481.woff2 +0 -0
  32. package/dist/remotion-bundle/1d701a81a7670db2.woff2 +0 -0
  33. package/dist/remotion-bundle/1da0fecad4240f16.woff2 +0 -0
  34. package/dist/remotion-bundle/1ed14d3d0c5c63fe.woff2 +0 -0
  35. package/dist/remotion-bundle/1edfecf40e586f53.woff2 +0 -0
  36. package/dist/remotion-bundle/1f479711bc34b054.woff +0 -0
  37. package/dist/remotion-bundle/1f86e54a0ff5fcd1.woff2 +0 -0
  38. package/dist/remotion-bundle/2043ea87d9aabd11.woff2 +0 -0
  39. package/dist/remotion-bundle/20563c39ee8a0e40.woff2 +0 -0
  40. package/dist/remotion-bundle/20c231590fd12c44.woff2 +0 -0
  41. package/dist/remotion-bundle/20ce61713f754c07.woff2 +0 -0
  42. package/dist/remotion-bundle/21eb9306fce24bb1.woff2 +0 -0
  43. package/dist/remotion-bundle/244bf71c0cc851af.woff2 +0 -0
  44. package/dist/remotion-bundle/274d4cfc02bffbcb.woff2 +0 -0
  45. package/dist/remotion-bundle/275.bundle.js +21 -0
  46. package/dist/remotion-bundle/275.bundle.js.map +1 -0
  47. package/dist/remotion-bundle/2958f540b39513dc.woff2 +0 -0
  48. package/dist/remotion-bundle/2a168b98fd97722e.woff2 +0 -0
  49. package/dist/remotion-bundle/2d1f6373937ab55f.woff2 +0 -0
  50. package/dist/remotion-bundle/2d213ae47ff6daa9.woff2 +0 -0
  51. package/dist/remotion-bundle/2e4b1f04fcd05047.woff2 +0 -0
  52. package/dist/remotion-bundle/304170d98f4c4563.woff2 +0 -0
  53. package/dist/remotion-bundle/30d02e136e7a5642.woff2 +0 -0
  54. package/dist/remotion-bundle/3135562b52a714cd.woff2 +0 -0
  55. package/dist/remotion-bundle/313713af2c8144e9.woff2 +0 -0
  56. package/dist/remotion-bundle/325fa4108d2285b9.woff2 +0 -0
  57. package/dist/remotion-bundle/338e927ed3345e0c.woff2 +0 -0
  58. package/dist/remotion-bundle/35fc6b190365bc17.woff2 +0 -0
  59. package/dist/remotion-bundle/37a51f1122d4efc5.woff2 +0 -0
  60. package/dist/remotion-bundle/39a4d63e02736f5e.woff2 +0 -0
  61. package/dist/remotion-bundle/3a00e0d62dfc4171.woff2 +0 -0
  62. package/dist/remotion-bundle/3a6955e6561affe1.woff2 +0 -0
  63. package/dist/remotion-bundle/3c573945aef49b89.woff2 +0 -0
  64. package/dist/remotion-bundle/3cdbfbfa23b516a5.woff2 +0 -0
  65. package/dist/remotion-bundle/3e42f85a9e64ca8a.woff2 +0 -0
  66. package/dist/remotion-bundle/3e83eaf1ec859415.woff2 +0 -0
  67. package/dist/remotion-bundle/3f3c8c90de1250ee.woff2 +0 -0
  68. package/dist/remotion-bundle/434.bundle.js +205 -0
  69. package/dist/remotion-bundle/434.bundle.js.map +1 -0
  70. package/dist/remotion-bundle/44ffc6ca4d781692.woff2 +0 -0
  71. package/dist/remotion-bundle/4670d9c4580b09eb.woff2 +0 -0
  72. package/dist/remotion-bundle/479756881b302824.woff2 +0 -0
  73. package/dist/remotion-bundle/481b82134bfa9c82.woff2 +0 -0
  74. package/dist/remotion-bundle/48d27029626f4328.woff2 +0 -0
  75. package/dist/remotion-bundle/49b7b2a30329c511.woff2 +0 -0
  76. package/dist/remotion-bundle/4c8b25a1a9337045.woff2 +0 -0
  77. package/dist/remotion-bundle/4cba14788ca9259b.woff2 +0 -0
  78. package/dist/remotion-bundle/4cd6c589c004a6a7.woff2 +0 -0
  79. package/dist/remotion-bundle/4cd8d79c1021608d.woff2 +0 -0
  80. package/dist/remotion-bundle/4d8fa99b3f00f9f0.woff2 +0 -0
  81. package/dist/remotion-bundle/4e7805a643f86d53.woff2 +0 -0
  82. package/dist/remotion-bundle/4ff91be454542e3f.woff2 +0 -0
  83. package/dist/remotion-bundle/504cbcba1f63591b.woff2 +0 -0
  84. package/dist/remotion-bundle/5202d792e5791d6c.woff2 +0 -0
  85. package/dist/remotion-bundle/534db5ad4770cc1d.woff2 +0 -0
  86. package/dist/remotion-bundle/53b9568eb85f866b.woff2 +0 -0
  87. package/dist/remotion-bundle/543ad386ca171de9.woff2 +0 -0
  88. package/dist/remotion-bundle/54798e55bbf7976e.woff2 +0 -0
  89. package/dist/remotion-bundle/580.bundle.js +11 -0
  90. package/dist/remotion-bundle/580.bundle.js.map +1 -0
  91. package/dist/remotion-bundle/58d174d1193af6d1.woff2 +0 -0
  92. package/dist/remotion-bundle/591d29ff3ff53c80.woff2 +0 -0
  93. package/dist/remotion-bundle/5c28c4f4824383c6.woff2 +0 -0
  94. package/dist/remotion-bundle/5da9740d2ce894c8.woff2 +0 -0
  95. package/dist/remotion-bundle/6197735364642360.woff2 +0 -0
  96. package/dist/remotion-bundle/6265a4335724080f.woff2 +0 -0
  97. package/dist/remotion-bundle/633f5e4f6394daa7.woff2 +0 -0
  98. package/dist/remotion-bundle/637d95ace6a69c49.woff2 +0 -0
  99. package/dist/remotion-bundle/648e04a04dacff8f.woff2 +0 -0
  100. package/dist/remotion-bundle/64a6e83045a008b2.woff2 +0 -0
  101. package/dist/remotion-bundle/651.bundle.js +11 -0
  102. package/dist/remotion-bundle/651.bundle.js.map +1 -0
  103. package/dist/remotion-bundle/65e2a988c070facc.woff2 +0 -0
  104. package/dist/remotion-bundle/66a2f6ce5cc69105.woff2 +0 -0
  105. package/dist/remotion-bundle/690.bundle.js +3479 -0
  106. package/dist/remotion-bundle/690.bundle.js.map +1 -0
  107. package/dist/remotion-bundle/690ff55252ca715d.woff2 +0 -0
  108. package/dist/remotion-bundle/6a01a1cff49314fc.woff2 +0 -0
  109. package/dist/remotion-bundle/6cbc32670982986c.woff2 +0 -0
  110. package/dist/remotion-bundle/6d3cc42ae547f454.woff2 +0 -0
  111. package/dist/remotion-bundle/6d8f4cfa1ddc0830.woff2 +0 -0
  112. package/dist/remotion-bundle/6e4d7c6ae65e2dc3.woff2 +0 -0
  113. package/dist/remotion-bundle/6e86418bbcefb2e8.woff2 +0 -0
  114. package/dist/remotion-bundle/6ee02884b29cf7fb.woff2 +0 -0
  115. package/dist/remotion-bundle/6f436a74c9e3252c.woff2 +0 -0
  116. package/dist/remotion-bundle/78c8022f1657618b.woff2 +0 -0
  117. package/dist/remotion-bundle/7c5444169792bca4.woff2 +0 -0
  118. package/dist/remotion-bundle/7c86bddd9d997212.woff2 +0 -0
  119. package/dist/remotion-bundle/7e1284684767f584.woff2 +0 -0
  120. package/dist/remotion-bundle/7e81c17522d182b2.woff2 +0 -0
  121. package/dist/remotion-bundle/7eb87be198f7858c.woff2 +0 -0
  122. package/dist/remotion-bundle/8060c928f948aab5.woff2 +0 -0
  123. package/dist/remotion-bundle/80bc9dfbea2b35ae.woff2 +0 -0
  124. package/dist/remotion-bundle/811b83f69963bb48.woff2 +0 -0
  125. package/dist/remotion-bundle/813.bundle.js +117511 -0
  126. package/dist/remotion-bundle/813.bundle.js.map +1 -0
  127. package/dist/remotion-bundle/84df492e349f82e9.woff2 +0 -0
  128. package/dist/remotion-bundle/8501bfd73eb36f2b.woff2 +0 -0
  129. package/dist/remotion-bundle/854236a8376093fe.woff2 +0 -0
  130. package/dist/remotion-bundle/8571d74529082753.woff2 +0 -0
  131. package/dist/remotion-bundle/860bf44f8e6f4b5d.woff2 +0 -0
  132. package/dist/remotion-bundle/879.bundle.js +64 -0
  133. package/dist/remotion-bundle/879.bundle.js.map +1 -0
  134. package/dist/remotion-bundle/887dd482f848d56f.woff2 +0 -0
  135. package/dist/remotion-bundle/89b2132e85fbbb5a.woff2 +0 -0
  136. package/dist/remotion-bundle/8ba60d6c306010c2.woff2 +0 -0
  137. package/dist/remotion-bundle/8c7c4dadea897806.woff2 +0 -0
  138. package/dist/remotion-bundle/8c943f9999706f61.woff2 +0 -0
  139. package/dist/remotion-bundle/8f2a718c90575cc9.woff2 +0 -0
  140. package/dist/remotion-bundle/906b6edb3e1772c9.woff2 +0 -0
  141. package/dist/remotion-bundle/930ff9daccdf14eb.woff2 +0 -0
  142. package/dist/remotion-bundle/934db2f1c403c4d0.woff2 +0 -0
  143. package/dist/remotion-bundle/938.bundle.js +451 -0
  144. package/dist/remotion-bundle/938.bundle.js.map +1 -0
  145. package/dist/remotion-bundle/967.bundle.js +4462 -0
  146. package/dist/remotion-bundle/967.bundle.js.map +1 -0
  147. package/dist/remotion-bundle/9684a1093d3c02ce.woff2 +0 -0
  148. package/dist/remotion-bundle/973dcd0faa6116cc.woff2 +0 -0
  149. package/dist/remotion-bundle/9745400694e76cd8.woff2 +0 -0
  150. package/dist/remotion-bundle/999ef957bed3bdca.woff2 +0 -0
  151. package/dist/remotion-bundle/99a3d67c8b0f43e3.woff2 +0 -0
  152. package/dist/remotion-bundle/a0586c3e03127283.woff2 +0 -0
  153. package/dist/remotion-bundle/a0eb654fdae46269.woff2 +0 -0
  154. package/dist/remotion-bundle/a20e35d3b08f7994.woff2 +0 -0
  155. package/dist/remotion-bundle/a2dcaced7c8c25ab.woff2 +0 -0
  156. package/dist/remotion-bundle/a79255a972a2681a.woff2 +0 -0
  157. package/dist/remotion-bundle/a804b352cb9fec1a.woff2 +0 -0
  158. package/dist/remotion-bundle/aae7117164e1eabc.woff2 +0 -0
  159. package/dist/remotion-bundle/affd121385d0442d.woff2 +0 -0
  160. package/dist/remotion-bundle/b19a6083987ee0d7.woff2 +0 -0
  161. package/dist/remotion-bundle/b1b2bd04d8637981.woff2 +0 -0
  162. package/dist/remotion-bundle/b2c07f341486be87.woff2 +0 -0
  163. package/dist/remotion-bundle/b33d8f82e575c4ce.woff2 +0 -0
  164. package/dist/remotion-bundle/b366c0bed35ef491.woff2 +0 -0
  165. package/dist/remotion-bundle/b41e857ec1b85642.woff2 +0 -0
  166. package/dist/remotion-bundle/b420bb34ccf23e7f.woff2 +0 -0
  167. package/dist/remotion-bundle/b4f7bf4efb0c0ccf.woff2 +0 -0
  168. package/dist/remotion-bundle/b60fe5eca03cff93.woff2 +0 -0
  169. package/dist/remotion-bundle/b6bd31a336e64bce.woff2 +0 -0
  170. package/dist/remotion-bundle/b6d2befba3dfefeb.woff2 +0 -0
  171. package/dist/remotion-bundle/b75f39ab06c43bf4.woff2 +0 -0
  172. package/dist/remotion-bundle/b77880e8c413d4fd.woff2 +0 -0
  173. package/dist/remotion-bundle/b7e38ec441e4a77a.woff2 +0 -0
  174. package/dist/remotion-bundle/b83baa383ff0bf2b.woff2 +0 -0
  175. package/dist/remotion-bundle/b9ad7b6c0a11450a.woff2 +0 -0
  176. package/dist/remotion-bundle/baf84486e8ae3aaf.woff2 +0 -0
  177. package/dist/remotion-bundle/bc047b1f6869cffa.woff2 +0 -0
  178. package/dist/remotion-bundle/bf4f3ac6e93f33aa.woff2 +0 -0
  179. package/dist/remotion-bundle/bf6835ffec5897a2.woff2 +0 -0
  180. package/dist/remotion-bundle/bf8885f581eb1724.woff2 +0 -0
  181. package/dist/remotion-bundle/bundle.js +83376 -0
  182. package/dist/remotion-bundle/bundle.js.map +1 -0
  183. package/dist/remotion-bundle/c03f046bccd789d0.woff2 +0 -0
  184. package/dist/remotion-bundle/c0bb1f8962b73bc3.woff2 +0 -0
  185. package/dist/remotion-bundle/c1003f9a7db6e1cf.woff2 +0 -0
  186. package/dist/remotion-bundle/c15d83fb1e199515.woff2 +0 -0
  187. package/dist/remotion-bundle/c28e7e5d310f73ef.woff2 +0 -0
  188. package/dist/remotion-bundle/c2b840274db78aea.woff2 +0 -0
  189. package/dist/remotion-bundle/c3000e3299d4e45f.woff2 +0 -0
  190. package/dist/remotion-bundle/c83ce886e5288510.woff2 +0 -0
  191. package/dist/remotion-bundle/c87a5a64d4ac0918.woff2 +0 -0
  192. package/dist/remotion-bundle/c8a7e0d049e965fa.woff2 +0 -0
  193. package/dist/remotion-bundle/c949a35d3a3b1faf.woff2 +0 -0
  194. package/dist/remotion-bundle/c9618c9b9ac2bc78.woff2 +0 -0
  195. package/dist/remotion-bundle/ca3add3b84152d5b.woff2 +0 -0
  196. package/dist/remotion-bundle/cad9dd036408d707.woff2 +0 -0
  197. package/dist/remotion-bundle/cbb24916619df439.woff2 +0 -0
  198. package/dist/remotion-bundle/cc054f0b5514e177.woff2 +0 -0
  199. package/dist/remotion-bundle/ccc248ed9312bc71.woff2 +0 -0
  200. package/dist/remotion-bundle/cd9d623aa07af925.woff2 +0 -0
  201. package/dist/remotion-bundle/ce2ba7a321bd1247.woff2 +0 -0
  202. package/dist/remotion-bundle/cf72455f79a29b14.woff2 +0 -0
  203. package/dist/remotion-bundle/d267cbfefab452ac.woff2 +0 -0
  204. package/dist/remotion-bundle/d435cff46a64955f.woff +0 -0
  205. package/dist/remotion-bundle/d494d07f67e363f6.woff2 +0 -0
  206. package/dist/remotion-bundle/d7aa0cc1fa47bf38.woff2 +0 -0
  207. package/dist/remotion-bundle/d7c5ca93d885160a.woff2 +0 -0
  208. package/dist/remotion-bundle/d855d3e252db74e2.woff2 +0 -0
  209. package/dist/remotion-bundle/d8f13d47f02f82c2.woff2 +0 -0
  210. package/dist/remotion-bundle/d9567cce2ee11019.woff2 +0 -0
  211. package/dist/remotion-bundle/db8d4456fc75dd86.woff +0 -0
  212. package/dist/remotion-bundle/dc274628378c47ee.woff2 +0 -0
  213. package/dist/remotion-bundle/dc3e06947bb69903.woff2 +0 -0
  214. package/dist/remotion-bundle/dd67040ac3b6d523.woff2 +0 -0
  215. package/dist/remotion-bundle/e0b04bd488f953f4.woff2 +0 -0
  216. package/dist/remotion-bundle/e2a572ff95089370.woff2 +0 -0
  217. package/dist/remotion-bundle/e2e18a86b1c2b0cc.woff2 +0 -0
  218. package/dist/remotion-bundle/e3a78ee2fc9c6931.woff2 +0 -0
  219. package/dist/remotion-bundle/e654c9d547605a9f.woff2 +0 -0
  220. package/dist/remotion-bundle/e67a3a64c129927c.woff2 +0 -0
  221. package/dist/remotion-bundle/e6be28b4203cd6ce.woff2 +0 -0
  222. package/dist/remotion-bundle/e841907ad9b0a191.woff +0 -0
  223. package/dist/remotion-bundle/e889d1541c69fffa.woff2 +0 -0
  224. package/dist/remotion-bundle/e88ef8c76373a9e2.woff2 +0 -0
  225. package/dist/remotion-bundle/e9c72f4bc37defef.woff2 +0 -0
  226. package/dist/remotion-bundle/e9e35f863403a255.woff2 +0 -0
  227. package/dist/remotion-bundle/eb23b37b009375da.woff2 +0 -0
  228. package/dist/remotion-bundle/ee1342b741625721.woff2 +0 -0
  229. package/dist/remotion-bundle/f07da88543a57ec9.woff2 +0 -0
  230. package/dist/remotion-bundle/f522982115306f8a.woff2 +0 -0
  231. package/dist/remotion-bundle/f8449bd864e6d8bc.woff2 +0 -0
  232. package/dist/remotion-bundle/f906dd5bd95ff9ab.woff2 +0 -0
  233. package/dist/remotion-bundle/f9e9e9413e3c38bb.woff2 +0 -0
  234. package/dist/remotion-bundle/fa5a5b16280994a8.woff2 +0 -0
  235. package/dist/remotion-bundle/favicon.ico +0 -0
  236. package/dist/remotion-bundle/fb19c0517725599b.woff2 +0 -0
  237. package/dist/remotion-bundle/fcaf24232f684b9b.woff2 +0 -0
  238. package/dist/remotion-bundle/fe09e084a3eea8cf.woff2 +0 -0
  239. package/dist/remotion-bundle/ff38d5317df7345a.woff2 +0 -0
  240. package/dist/remotion-bundle/ffe7ea1ea08f455a.woff2 +0 -0
  241. package/dist/remotion-bundle/index.html +49 -0
  242. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaomei/communication/0.mp3 +0 -0
  243. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaomei/communication/1.mp3 +0 -0
  244. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaomei/communication/2.mp3 +0 -0
  245. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaomei/communication/3.mp3 +0 -0
  246. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoxin/career/0.mp3 +0 -0
  247. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoxin/career/1.mp3 +0 -0
  248. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoxin/career/2.mp3 +0 -0
  249. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoxin/career/3.mp3 +0 -0
  250. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoyue/parenting/0.mp3 +0 -0
  251. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoyue/parenting/1.mp3 +0 -0
  252. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoyue/parenting/2.mp3 +0 -0
  253. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoyue/parenting/3.mp3 +0 -0
  254. package/dist/remotion-bundle/public/paper-slide/male-kefu-xiaoxu/time-trap/0.mp3 +0 -0
  255. package/dist/remotion-bundle/public/paper-slide/male-kefu-xiaoxu/time-trap/1.mp3 +0 -0
  256. package/dist/remotion-bundle/public/paper-slide/male-kefu-xiaoxu/time-trap/2.mp3 +0 -0
  257. package/dist/remotion-bundle/public/paper-slide/male-kefu-xiaoxu/time-trap/3.mp3 +0 -0
  258. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/cognition/0.mp3 +0 -0
  259. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/cognition/1.mp3 +0 -0
  260. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/cognition/2.mp3 +0 -0
  261. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/cognition/3.mp3 +0 -0
  262. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/growth/0.mp3 +0 -0
  263. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/growth/1.mp3 +0 -0
  264. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/growth/2.mp3 +0 -0
  265. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/growth/3.mp3 +0 -0
  266. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/parenting/0.mp3 +0 -0
  267. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/parenting/1.mp3 +0 -0
  268. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/parenting/2.mp3 +0 -0
  269. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/parenting/3.mp3 +0 -0
  270. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/soothing/0.mp3 +0 -0
  271. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/soothing/1.mp3 +0 -0
  272. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/soothing/2.mp3 +0 -0
  273. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/soothing/3.mp3 +0 -0
  274. package/dist/remotion-bundle/public/paper-slide/v-female-R2s4N9qJ/cognition/0.mp3 +0 -0
  275. package/dist/remotion-bundle/public/paper-slide/v-female-R2s4N9qJ/cognition/1.mp3 +0 -0
  276. package/dist/remotion-bundle/public/paper-slide/v-female-R2s4N9qJ/cognition/2.mp3 +0 -0
  277. package/dist/remotion-bundle/public/paper-slide/v-female-R2s4N9qJ/cognition/3.mp3 +0 -0
  278. package/dist/remotion-bundle/public/paper-slide/v-male-Bk7vD3xP/decision/0.mp3 +0 -0
  279. package/dist/remotion-bundle/public/paper-slide/v-male-Bk7vD3xP/decision/1.mp3 +0 -0
  280. package/dist/remotion-bundle/public/paper-slide/v-male-Bk7vD3xP/decision/2.mp3 +0 -0
  281. package/dist/remotion-bundle/public/paper-slide/v-male-Bk7vD3xP/decision/3.mp3 +0 -0
  282. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/0.mp3 +0 -0
  283. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/1.mp3 +0 -0
  284. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/2.mp3 +0 -0
  285. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/3.mp3 +0 -0
  286. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/4.mp3 +0 -0
  287. package/dist/remotion-bundle/public/paper-slide/v-male-s5NqE0rZ/founder/0.mp3 +0 -0
  288. package/dist/remotion-bundle/public/paper-slide/v-male-s5NqE0rZ/founder/1.mp3 +0 -0
  289. package/dist/remotion-bundle/public/paper-slide/v-male-s5NqE0rZ/founder/2.mp3 +0 -0
  290. package/dist/remotion-bundle/public/paper-slide/v-male-s5NqE0rZ/founder/3.mp3 +0 -0
  291. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/0.mp3 +0 -0
  292. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/1.mp3 +0 -0
  293. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/2.mp3 +0 -0
  294. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/3.mp3 +0 -0
  295. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/4.mp3 +0 -0
  296. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/0.mp3 +0 -0
  297. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/1.mp3 +0 -0
  298. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/2.mp3 +0 -0
  299. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/3.mp3 +0 -0
  300. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/4.mp3 +0 -0
  301. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/0.mp3 +0 -0
  302. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/1.mp3 +0 -0
  303. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/2.mp3 +0 -0
  304. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/3.mp3 +0 -0
  305. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/4.mp3 +0 -0
  306. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/0.mp3 +0 -0
  307. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/1.mp3 +0 -0
  308. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/2.mp3 +0 -0
  309. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/3.mp3 +0 -0
  310. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/4.mp3 +0 -0
  311. package/dist/remotion-bundle/public/paper-slide-experiments/meeting-closure/0.mp3 +0 -0
  312. package/dist/remotion-bundle/public/paper-slide-experiments/meeting-closure/1.mp3 +0 -0
  313. package/dist/remotion-bundle/public/paper-slide-experiments/meeting-closure/2.mp3 +0 -0
  314. package/dist/remotion-bundle/public/paper-slide-experiments/meeting-closure/3.mp3 +0 -0
  315. package/dist/remotion-bundle/public/paper-slide-experiments/product-update/0.mp3 +0 -0
  316. package/dist/remotion-bundle/public/paper-slide-experiments/product-update/1.mp3 +0 -0
  317. package/dist/remotion-bundle/public/paper-slide-experiments/product-update/2.mp3 +0 -0
  318. package/dist/remotion-bundle/public/paper-slide-experiments/product-update/3.mp3 +0 -0
  319. package/dist/remotion-bundle/public/paper-slide-experiments/research-reading/0.mp3 +0 -0
  320. package/dist/remotion-bundle/public/paper-slide-experiments/research-reading/1.mp3 +0 -0
  321. package/dist/remotion-bundle/public/paper-slide-experiments/research-reading/2.mp3 +0 -0
  322. package/dist/remotion-bundle/public/paper-slide-experiments/research-reading/3.mp3 +0 -0
  323. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/0.mp3 +0 -0
  324. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/1.mp3 +0 -0
  325. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/2.mp3 +0 -0
  326. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/3.mp3 +0 -0
  327. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/4.mp3 +0 -0
  328. package/dist/remotion-bundle/public/voiceover/ai-life/card-0.mp3 +0 -0
  329. package/dist/remotion-bundle/public/voiceover/ai-life/card-1.mp3 +0 -0
  330. package/dist/remotion-bundle/public/voiceover/ai-life/card-2.mp3 +0 -0
  331. package/dist/remotion-bundle/public/voiceover/ai-life/card-3.mp3 +0 -0
  332. package/dist/remotion-bundle/public/voiceover/ai-life/card-4.mp3 +0 -0
  333. package/dist/remotion-bundle/public/voiceover/ai-life/card-5.mp3 +0 -0
  334. package/dist/remotion-bundle/public/voiceover/coffee-science/card-0.mp3 +0 -0
  335. package/dist/remotion-bundle/public/voiceover/coffee-science/card-1.mp3 +0 -0
  336. package/dist/remotion-bundle/public/voiceover/coffee-science/card-2.mp3 +0 -0
  337. package/dist/remotion-bundle/public/voiceover/coffee-science/card-3.mp3 +0 -0
  338. package/dist/remotion-bundle/public/voiceover/coffee-science/card-4.mp3 +0 -0
  339. package/dist/remotion-bundle/public/voiceover/coffee-science/card-5.mp3 +0 -0
  340. package/dist/remotion-bundle/public/voiceover/coffee-science/card-6.mp3 +0 -0
  341. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-0.mp3 +0 -0
  342. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-1.mp3 +0 -0
  343. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-2.mp3 +0 -0
  344. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-3.mp3 +0 -0
  345. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-4.mp3 +0 -0
  346. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-5.mp3 +0 -0
  347. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-6.mp3 +0 -0
  348. package/dist/remotion-bundle/public/voiceover/remote-work/card-0.mp3 +0 -0
  349. package/dist/remotion-bundle/public/voiceover/remote-work/card-1.mp3 +0 -0
  350. package/dist/remotion-bundle/public/voiceover/remote-work/card-2.mp3 +0 -0
  351. package/dist/remotion-bundle/public/voiceover/remote-work/card-3.mp3 +0 -0
  352. package/dist/remotion-bundle/public/voiceover/remote-work/card-4.mp3 +0 -0
  353. package/dist/remotion-bundle/public/voiceover/remote-work/card-5.mp3 +0 -0
  354. package/dist/remotion-bundle/source-map-helper.wasm +0 -0
  355. package/lib/cli.js +270 -0
  356. package/lib/commands/_registry.js +48 -0
  357. package/lib/commands/add.js +242 -0
  358. package/lib/commands/asr/azure-transcribe.js +336 -0
  359. package/lib/commands/asr/cloud-transcribe.js +384 -0
  360. package/lib/commands/asr/helpers.js +76 -0
  361. package/lib/commands/asr/index.js +236 -0
  362. package/lib/commands/asr/local-transcribe.js +125 -0
  363. package/lib/commands/asr-jobs.js +257 -0
  364. package/lib/commands/asr.js +11 -0
  365. package/lib/commands/auth-cmds.js +358 -0
  366. package/lib/commands/dub.js +542 -0
  367. package/lib/commands/explain.js +512 -0
  368. package/lib/commands/feedback.js +152 -0
  369. package/lib/commands/image.js +207 -0
  370. package/lib/commands/mcp-key.js +166 -0
  371. package/lib/commands/narrate.js +639 -0
  372. package/lib/commands/picstory-templates.js +276 -0
  373. package/lib/commands/picstory.js +547 -0
  374. package/lib/commands/podcast/dialogue.js +109 -0
  375. package/lib/commands/podcast/generate.js +127 -0
  376. package/lib/commands/podcast/index.js +561 -0
  377. package/lib/commands/podcast/synthesize.js +188 -0
  378. package/lib/commands/podcast.js +11 -0
  379. package/lib/commands/present.js +519 -0
  380. package/lib/commands/publish.js +415 -0
  381. package/lib/commands/skills.js +473 -0
  382. package/lib/commands/slice-preview.js +266 -0
  383. package/lib/commands/slice-render.js +282 -0
  384. package/lib/commands/slice-stage.js +264 -0
  385. package/lib/commands/slice.js +343 -0
  386. package/lib/commands/slides/constants.js +108 -0
  387. package/lib/commands/slides/html-renderer.js +338 -0
  388. package/lib/commands/slides/index.js +345 -0
  389. package/lib/commands/slides.js +11 -0
  390. package/lib/commands/story.js +302 -0
  391. package/lib/commands/summarize.js +532 -0
  392. package/lib/commands/synthesize.js +261 -0
  393. package/lib/commands/translate.js +593 -0
  394. package/lib/commands/upgrade.js +249 -0
  395. package/lib/commands/video-translate.js +577 -0
  396. package/lib/commands/voices.js +292 -0
  397. package/lib/core/agent-env.js +104 -0
  398. package/lib/core/args.js +107 -0
  399. package/lib/core/asr-client.js +448 -0
  400. package/lib/core/asr-jobs-client.js +126 -0
  401. package/lib/core/asr-jobs-store.js +105 -0
  402. package/lib/core/asr-r2-upload.js +181 -0
  403. package/lib/core/asr-upload.js +132 -0
  404. package/lib/core/audio-extract.js +150 -0
  405. package/lib/core/audio.js +219 -0
  406. package/lib/core/auth.js +880 -0
  407. package/lib/core/config.js +197 -0
  408. package/lib/core/feedback.js +64 -0
  409. package/lib/core/ffmpeg.js +476 -0
  410. package/lib/core/http.js +188 -0
  411. package/lib/core/image-client.js +55 -0
  412. package/lib/core/intent-params.js +11 -0
  413. package/lib/core/llm-client.js +76 -0
  414. package/lib/core/logger.js +208 -0
  415. package/lib/core/mic-recorder.js +182 -0
  416. package/lib/core/pause-markers.js +94 -0
  417. package/lib/core/podcast-pacing.js +118 -0
  418. package/lib/core/spinner.js +33 -0
  419. package/lib/core/srt.js +394 -0
  420. package/lib/core/telemetry.js +100 -0
  421. package/lib/core/timeline.js +92 -0
  422. package/lib/core/tts-synthesizer.js +70 -0
  423. package/lib/core/update-check.js +185 -0
  424. package/lib/core/url-download.js +148 -0
  425. package/lib/core/whisper-local.js +279 -0
  426. package/lib/internal/deck-validator.js +488 -0
  427. package/lib/internal/slice-themes.json +370 -0
  428. package/lib/stage-core/cloud-render.js +170 -0
  429. package/lib/stage-core/deck-format.js +133 -0
  430. package/lib/stage-core/edit-prompt.js +104 -0
  431. package/lib/stage-core/event-bus.js +31 -0
  432. package/lib/stage-core/port.js +46 -0
  433. package/lib/stage-core/server.js +352 -0
  434. package/lib/stage-core/snapshot-store.js +198 -0
  435. package/lib/stage-core/watcher.js +106 -0
  436. package/lib/stage-ui/slice/template.js +1672 -0
  437. package/package.json +9 -4
  438. package/skills/.claude-plugin/marketplace.json +22 -0
  439. package/skills/.claude-plugin/plugin.json +25 -0
  440. package/skills/LICENSE +21 -0
  441. package/skills/README.md +120 -0
  442. package/skills/hub/SKILL.md +317 -0
  443. package/skills/podcast/SKILL.md +146 -0
  444. package/skills/slice/SKILL.md +205 -0
  445. package/skills/slice/agents/openai.yaml +4 -0
  446. package/skills/slice/references/deck-schema.md +183 -0
  447. package/skills/slice/references/example-decks.md +108 -0
  448. package/skills/slice/references/themes.md +172 -0
  449. package/skills/transcribe/SKILL.md +473 -0
  450. package/skills/video/SKILL.md +261 -0
  451. package/skills/voxflow-slice/SKILL.md +271 -0
  452. package/skills/voxflow-slice/examples/article.md +13 -0
  453. package/skills/voxflow-slice/examples/expected-deck.json +39 -0
  454. package/skills/voxflow-slice/examples/validate.mjs +46 -0
@@ -0,0 +1,547 @@
1
+ /**
2
+ * VoxFlow CLI — PicStory command
3
+ *
4
+ * Generates a narrated visual story video:
5
+ * 1. LLM script → structured scenes (heading + keyPoints + narration)
6
+ * 2. TTS per scene (sequential)
7
+ * 3. AI image per scene (parallel via /api/image/generate)
8
+ * 4. FFmpeg Ken Burns render + concat → MP4
9
+ *
10
+ * Visual styles: sketchnote, neon_noir, minimal_3d, chalkboard (card),
11
+ * photo, manga_panel, vintage_newspaper (scene)
12
+ * Ratios: portrait (1080×1920), landscape (1920×1080), square (1080×1080)
13
+ */
14
+
15
+ const fs = require('fs');
16
+ const os = require('os');
17
+ const path = require('path');
18
+ const { PICSTORY_DEFAULTS } = require('../core/config');
19
+ const { request, throwApiError, throwNetworkError } = require('../core/http');
20
+ const { buildWav } = require('../core/audio');
21
+ const { BYTES_PER_MS } = require('../core/timeline');
22
+ const { synthesizeTTS } = require('../core/tts-synthesizer');
23
+ const { generateImage } = require('../core/image-client');
24
+ const { runCommand, checkFfmpeg, concatVideos } = require('../core/ffmpeg');
25
+ const { startSpinner } = require('../core/spinner');
26
+ const {
27
+ RATIO_CONFIG, VALID_RATIOS, VALID_STYLES,
28
+ buildSystemPrompt, buildImagePrompt,
29
+ buildSketchnotePrompt, buildPhotoPrompt,
30
+ } = require('./picstory-templates');
31
+
32
+ // ─── Script Model Presets ─────────────────────────────────────────────────────
33
+
34
+ /**
35
+ * Named model presets for LLM script generation.
36
+ * Must stay in sync with PICSTORY_SCRIPT_PRESETS in backend/utils/llm-request.js.
37
+ * Users pick a preset name; the CLI resolves it to {provider, model} for the API call.
38
+ */
39
+ const SCRIPT_MODEL_PRESETS = {
40
+ 'default': null, // use server default (LLM_DEFAULT_PROVIDER + LLM_MODEL)
41
+ 'gemini-flash': { provider: 'openrouter', model: 'google/gemini-3-flash-preview' },
42
+ 'deepseek': { provider: 'deepseek', model: 'deepseek-chat' },
43
+ 'hunyuan': { provider: 'hunyuan', model: 'hunyuan-lite' },
44
+ 'moonshot': { provider: 'moonshot', model: 'moonshot-v1-8k' },
45
+ };
46
+
47
+ const VALID_SCRIPT_MODELS = Object.keys(SCRIPT_MODEL_PRESETS);
48
+
49
+ // ─── LLM Script Generation ────────────────────────────────────────────────────
50
+
51
+ async function generateScript(apiBase, token, topic, { language, sceneCount, style, scriptModel }) {
52
+ const systemPrompt = buildSystemPrompt(style);
53
+ const userPrompt = [
54
+ `Topic: ${topic}`,
55
+ `Language: ${language}`,
56
+ `Number of scenes: ${sceneCount}`,
57
+ `Visual style: ${style}`,
58
+ '',
59
+ `Generate a compelling ${sceneCount}-scene picstory script about the topic above.`,
60
+ 'Each scene should cover a distinct aspect or step.',
61
+ ].join('\n');
62
+
63
+ const preset = scriptModel && scriptModel !== 'default'
64
+ ? SCRIPT_MODEL_PRESETS[scriptModel]
65
+ : null;
66
+
67
+ const body = {
68
+ messages: [
69
+ { role: 'system', content: systemPrompt },
70
+ { role: 'user', content: userPrompt },
71
+ ],
72
+ temperature: 0.7,
73
+ max_tokens: 2000,
74
+ };
75
+ if (preset) {
76
+ body.model = preset.model;
77
+ body.provider = preset.provider;
78
+ }
79
+
80
+ let status, data;
81
+ try {
82
+ ({ status, data } = await request(`${apiBase}/api/llm/chat`, {
83
+ method: 'POST',
84
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
85
+ timeoutMs: 180_000,
86
+ }, body));
87
+ } catch (err) {
88
+ throwNetworkError(err, apiBase);
89
+ }
90
+
91
+ if (status !== 200 || data.code !== 'success') throwApiError(status, data, 'LLM script');
92
+
93
+ let script;
94
+ try {
95
+ const raw = data.content.trim().replace(/^```json\s*/i, '').replace(/```\s*$/, '');
96
+ script = JSON.parse(raw);
97
+ } catch (err) {
98
+ throw new Error(`LLM returned invalid JSON: ${err.message}\nContent: ${data.content.slice(0, 200)}`);
99
+ }
100
+
101
+ if (!script.title || !Array.isArray(script.scenes) || script.scenes.length === 0) {
102
+ throw new Error('LLM script missing required fields: title, scenes[]');
103
+ }
104
+
105
+ return { script, quota: data.quota };
106
+ }
107
+
108
+ // ─── FFmpeg Per-Scene Render ──────────────────────────────────────────────────
109
+
110
+ // Zoom anchor positions — varied per scene for visual interest
111
+ const ZOOM_ANCHORS = [
112
+ { x: `'iw/2-(iw/zoom/2)'`, y: `'ih/2-(ih/zoom/2)'` }, // center
113
+ { x: `'0'`, y: `'0'` }, // top-left
114
+ { x: `'iw-iw/zoom'`, y: `'ih-ih/zoom'` }, // bottom-right
115
+ { x: `'iw-iw/zoom'`, y: `'0'` }, // top-right
116
+ ];
117
+
118
+ /**
119
+ * Render a single scene: image + WAV → MP4 with Ken Burns zoom + fade transitions.
120
+ * Uses output-frame-counter `on` for drift-free linear zoom.
121
+ * fadeSeconds > 0 adds fade-in from black and fade-out to black at scene boundaries,
122
+ * creating smooth transitions when scenes are concatenated.
123
+ */
124
+ async function renderScene({ imgPath, wavPath, outPath, durationMs, ratio, sceneIndex = 0, fadeSeconds = 0.4 }) {
125
+ const { w, h } = RATIO_CONFIG[ratio] || RATIO_CONFIG.portrait;
126
+ const fps = 30;
127
+ const durationSec = durationMs / 1000;
128
+ const frames = Math.max(2, Math.round(durationSec * fps));
129
+
130
+ // Drift-free Ken Burns: z = 1.0 + rate*on, no accumulated floating-point error
131
+ const zoomRate = (0.25 / Math.max(frames - 1, 1)).toFixed(8);
132
+ const zoomExpr = `'min(1.0+${zoomRate}*on,1.25)'`;
133
+ const { x: xExpr, y: yExpr } = ZOOM_ANCHORS[sceneIndex % ZOOM_ANCHORS.length];
134
+
135
+ const fd = fadeSeconds > 0 && durationSec > fadeSeconds * 2.5 ? fadeSeconds : 0;
136
+ const fadeOutStart = (durationSec - fd).toFixed(3);
137
+
138
+ const vfParts = [
139
+ `scale=${w}:${h}:force_original_aspect_ratio=increase`,
140
+ `crop=${w}:${h}`,
141
+ `zoompan=z=${zoomExpr}:x=${xExpr}:y=${yExpr}:d=${frames}:s=${w}x${h}:fps=${fps}`,
142
+ ];
143
+ if (fd > 0) {
144
+ vfParts.push(`fade=t=in:st=0:d=${fd}`, `fade=t=out:st=${fadeOutStart}:d=${fd}`);
145
+ }
146
+ vfParts.push('format=yuv420p');
147
+
148
+ const audioFilterParts = fd > 0
149
+ ? [`afade=t=in:st=0:d=${fd}`, `afade=t=out:st=${fadeOutStart}:d=${fd}`]
150
+ : [];
151
+
152
+ const args = [
153
+ '-loop', '1',
154
+ '-t', String(durationSec + 0.1),
155
+ '-i', path.resolve(imgPath),
156
+ '-i', path.resolve(wavPath),
157
+ '-vf', vfParts.join(','),
158
+ ];
159
+
160
+ if (audioFilterParts.length > 0) {
161
+ args.push('-af', audioFilterParts.join(','));
162
+ }
163
+
164
+ args.push(
165
+ '-c:v', 'libx264', '-preset', 'fast', '-crf', '22',
166
+ '-c:a', 'aac', '-b:a', '128k', '-ar', '24000', '-ac', '1',
167
+ '-shortest',
168
+ '-movflags', '+faststart',
169
+ '-y', path.resolve(outPath),
170
+ );
171
+
172
+ await runCommand('ffmpeg', args);
173
+ }
174
+
175
+ /**
176
+ * Mix background music into a video file.
177
+ * BGM loops to cover the full video duration, fades in at start.
178
+ */
179
+ async function mixBgmIntoVideo(videoPath, bgmPath, outputPath, opts = {}) {
180
+ const volume = opts.volume ?? 0.1;
181
+ const fadeIn = opts.fadeIn ?? 2.0;
182
+ await runCommand('ffmpeg', [
183
+ '-i', path.resolve(videoPath),
184
+ '-stream_loop', '-1',
185
+ '-i', path.resolve(bgmPath),
186
+ '-filter_complex',
187
+ `[1:a]volume=${volume},afade=t=in:st=0:d=${fadeIn}[bgm];[0:a][bgm]amix=inputs=2:duration=first:dropout_transition=2[out]`,
188
+ '-map', '0:v',
189
+ '-map', '[out]',
190
+ '-c:v', 'copy',
191
+ '-c:a', 'aac', '-b:a', '128k',
192
+ '-y', path.resolve(outputPath),
193
+ ]);
194
+ }
195
+
196
+ // ─── Public API ───────────────────────────────────────────────────────────────
197
+
198
+ /**
199
+ * Generate a narrated visual story video.
200
+ */
201
+ async function picstory(opts) {
202
+ const {
203
+ token, api: apiBase,
204
+ topic, text: inputText,
205
+ voice = PICSTORY_DEFAULTS.voice,
206
+ speed = PICSTORY_DEFAULTS.speed,
207
+ style = PICSTORY_DEFAULTS.style,
208
+ ratio = PICSTORY_DEFAULTS.ratio,
209
+ language = PICSTORY_DEFAULTS.language,
210
+ sceneCount = PICSTORY_DEFAULTS.sceneCount,
211
+ quality = PICSTORY_DEFAULTS.quality,
212
+ fadeSeconds = PICSTORY_DEFAULTS.fadeSeconds,
213
+ bgm,
214
+ bgmVolume = PICSTORY_DEFAULTS.bgmVolume,
215
+ imageOnly = false,
216
+ scriptModel = 'default',
217
+ } = opts;
218
+
219
+ const scriptInput = inputText || topic;
220
+ if (!scriptInput) throw new Error('No input provided. Use --topic or --text');
221
+
222
+ const sigintHandler = () => { console.log('\n\nGeneration cancelled.'); process.exit(130); };
223
+ process.on('SIGINT', sigintHandler);
224
+
225
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'voxflow-picstory-'));
226
+
227
+ try {
228
+ // ── Step 1: LLM script ────────────────────────────────────────────────────
229
+ console.log('\n[1/4] Generating script via LLM...');
230
+ const modelLabel = scriptModel && scriptModel !== 'default' ? `, model: ${scriptModel}` : '';
231
+ console.log(` Topic: "${scriptInput.slice(0, 80)}" (${language}, ${sceneCount} scenes, ${style}${modelLabel})`);
232
+
233
+ const spinner1 = startSpinner(' Thinking...');
234
+ let script, llmQuota;
235
+ try {
236
+ ({ script, quota: llmQuota } = await generateScript(
237
+ apiBase, token, scriptInput, { language, sceneCount, style, scriptModel }
238
+ ));
239
+ spinner1.stop(`OK — "${script.title}" (${script.scenes.length} scenes)`);
240
+ } catch (err) {
241
+ spinner1.stop('FAIL');
242
+ throw err;
243
+ }
244
+
245
+ // ── Step 2: TTS narration ─────────────────────────────────────────────────
246
+ console.log(`\n[2/4] Synthesizing narration (${script.scenes.length} scenes)...`);
247
+
248
+ const sceneData = [];
249
+ let lastTtsQuota = llmQuota;
250
+
251
+ for (let i = 0; i < script.scenes.length; i++) {
252
+ const narration = script.scenes[i].narration || '';
253
+ if (!narration.trim()) {
254
+ sceneData.push({ scene: script.scenes[i], pcm: Buffer.alloc(48 * 1000), durationMs: 1000 });
255
+ continue;
256
+ }
257
+ const result = await synthesizeTTS({
258
+ apiBase, token, text: narration, voiceId: voice, speed,
259
+ index: i, total: script.scenes.length,
260
+ });
261
+ const durationMs = Math.round(result.audio.length / BYTES_PER_MS);
262
+ console.log(` OK (${(result.audio.length / 1024).toFixed(0)} KB, ${(durationMs / 1000).toFixed(1)}s)`);
263
+ lastTtsQuota = result.quota;
264
+ sceneData.push({ scene: script.scenes[i], pcm: result.audio, durationMs });
265
+ }
266
+
267
+ // ── Step 3: Image generation (parallel) ──────────────────────────────────
268
+ console.log(`\n[3/4] Generating images (${script.scenes.length} scenes, parallel)...`);
269
+
270
+ let lastImgQuota = lastTtsQuota;
271
+ const imageTasks = script.scenes.map((scene, i) => {
272
+ const prompt = buildImagePrompt(scene, style, ratio);
273
+ return generateImage({ apiBase, token, prompt, ratio, quality, index: i, total: script.scenes.length })
274
+ .then(result => { lastImgQuota = result.quota; return result.imageDataUrl; });
275
+ });
276
+ const imageDataUrls = await Promise.all(imageTasks);
277
+
278
+ // ── Step 4: Write temp files + FFmpeg render ──────────────────────────────
279
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
280
+ const outputPath = opts.output || resolveOutputPath(`picstory-${timestamp}.mp4`, opts.outputDir);
281
+
282
+ if (imageOnly) {
283
+ console.log('\n[4/4] Saving images and audio...');
284
+ const base = outputPath.replace(/\.mp4$/i, '');
285
+ for (let i = 0; i < sceneData.length; i++) {
286
+ const imgPath = `${base}-scene-${i + 1}.jpg`;
287
+ const wavPath = `${base}-scene-${i + 1}.wav`;
288
+ saveImageDataUrl(imageDataUrls[i], imgPath);
289
+ fs.writeFileSync(wavPath, buildWav([sceneData[i].pcm], 0).wav);
290
+ console.log(` Scene ${i + 1}: ${imgPath} + ${wavPath}`);
291
+ }
292
+ const totalMs = sceneData.reduce((s, d) => s + d.durationMs, 0);
293
+ const scriptPath = `${base}-script.json`;
294
+ fs.writeFileSync(scriptPath, JSON.stringify(script, null, 2));
295
+ printSummary({ outputPath: null, imageOnly: true, script, totalMs, sceneCount: sceneData.length, style, ratio, lastQuota: lastImgQuota });
296
+ return { outputPath: null, scriptPath, duration: totalMs };
297
+ }
298
+
299
+ const ffmpegInfo = await checkFfmpeg();
300
+ if (!ffmpegInfo.available) {
301
+ throw new Error(
302
+ 'ffmpeg not found. picstory requires ffmpeg for video rendering.\n' +
303
+ ' Install: brew install ffmpeg (macOS) / sudo apt install ffmpeg (Linux)'
304
+ );
305
+ }
306
+
307
+ console.log('\n[4/4] Rendering video...');
308
+ const sceneMp4Paths = [];
309
+ for (let i = 0; i < sceneData.length; i++) {
310
+ process.stdout.write(` Scene [${i + 1}/${sceneData.length}]...`);
311
+ const imgPath = path.join(tmpDir, `scene-${i}.jpg`);
312
+ const wavPath = path.join(tmpDir, `scene-${i}.wav`);
313
+ const mp4Path = path.join(tmpDir, `scene-${i}.mp4`);
314
+
315
+ saveImageDataUrl(imageDataUrls[i], imgPath);
316
+ fs.writeFileSync(wavPath, buildWav([sceneData[i].pcm], 0).wav);
317
+
318
+ try {
319
+ await renderScene({ imgPath, wavPath, outPath: mp4Path, durationMs: sceneData[i].durationMs, ratio, sceneIndex: i, fadeSeconds });
320
+ console.log(' OK');
321
+ sceneMp4Paths.push(mp4Path);
322
+ } catch (err) {
323
+ console.log(' FAIL');
324
+ throw new Error(`Scene ${i + 1} render failed: ${err.message}`);
325
+ }
326
+ }
327
+
328
+ // Concat all scenes; BGM needs a separate mux pass so use a temp path when BGM is present
329
+ const concatTarget = bgm ? path.join(tmpDir, 'concat.mp4') : path.resolve(outputPath);
330
+ process.stdout.write(' Concatenating scenes...');
331
+ try {
332
+ await concatVideos(sceneMp4Paths, concatTarget);
333
+ console.log(' OK');
334
+ } catch (err) {
335
+ console.log(' FAIL');
336
+ throw new Error(`Concat failed: ${err.message}`);
337
+ }
338
+
339
+ if (bgm) {
340
+ process.stdout.write(' Mixing BGM...');
341
+ try {
342
+ await mixBgmIntoVideo(concatTarget, bgm, path.resolve(outputPath), { volume: bgmVolume });
343
+ console.log(' OK');
344
+ } catch (err) {
345
+ console.log(` WARN (BGM mix failed: ${err.message.slice(0, 80)})`);
346
+ // Fall back to the no-BGM concat output
347
+ fs.copyFileSync(concatTarget, path.resolve(outputPath));
348
+ }
349
+ }
350
+
351
+ const scriptPath = outputPath.replace(/\.mp4$/i, '.json');
352
+ fs.writeFileSync(scriptPath, JSON.stringify(script, null, 2));
353
+ const totalMs = sceneData.reduce((s, d) => s + d.durationMs, 0);
354
+ printSummary({ outputPath, imageOnly: false, script, totalMs, sceneCount: sceneData.length, style, ratio, lastQuota: lastImgQuota, bgm });
355
+
356
+ return { outputPath, scriptPath, duration: totalMs };
357
+ } finally {
358
+ process.removeListener('SIGINT', sigintHandler);
359
+ try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch { /* ignore */ }
360
+ }
361
+ }
362
+
363
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
364
+
365
+ function resolveOutputPath(filename, outputDir) {
366
+ if (!outputDir) return filename;
367
+ fs.mkdirSync(outputDir, { recursive: true });
368
+ return path.join(outputDir, path.basename(filename));
369
+ }
370
+
371
+ function saveImageDataUrl(dataUrl, filePath) {
372
+ const base64 = dataUrl.startsWith('data:') ? dataUrl.slice(dataUrl.indexOf(',') + 1) : dataUrl;
373
+ fs.writeFileSync(filePath, Buffer.from(base64, 'base64'));
374
+ }
375
+
376
+ function printSummary({ outputPath, imageOnly, script, totalMs, sceneCount, style, ratio, lastQuota, bgm }) {
377
+ const totalSec = Math.round(totalMs / 1000);
378
+ const durationStr = totalSec >= 60
379
+ ? `${Math.floor(totalSec / 60)}m${String(totalSec % 60).padStart(2, '0')}s`
380
+ : `${totalSec}s`;
381
+
382
+ console.log('\n=== Output ===');
383
+ if (outputPath) {
384
+ console.log(` Video: ${outputPath} (${(fs.statSync(outputPath).size / (1024 * 1024)).toFixed(1)} MB, ${durationStr})`);
385
+ } else if (imageOnly) {
386
+ console.log(` Mode: images + audio saved (no video render)`);
387
+ console.log(` Duration: ~${durationStr}`);
388
+ }
389
+ console.log(` Title: ${script.title}`);
390
+ console.log(` Scenes: ${sceneCount}`);
391
+ console.log(` Style: ${style} / ${ratio}`);
392
+ if (bgm) console.log(` BGM: ${path.basename(bgm)}`);
393
+ if (lastQuota) console.log(` Quota: ${lastQuota.remaining ?? '?'} remaining`);
394
+ }
395
+
396
+ // ─── CLI Handler ──────────────────────────────────────────────────────────────
397
+
398
+ async function handle(args) {
399
+ const {
400
+ parseFlag, parseIntFlag, parseFloatFlag, parseBoolFlag,
401
+ validateSpeed, runWithRetry,
402
+ } = require('../core/args');
403
+ const { getToken, getTokenInfo } = require('../core/auth');
404
+ const { API_BASE } = require('../core/config');
405
+
406
+ const api = parseFlag(args, '--api') || API_BASE;
407
+ const explicitToken = parseFlag(args, '--token');
408
+ const speed = parseFloatFlag(args, '--speed');
409
+ const output = parseFlag(args, '--output', '-o');
410
+ const scenes = parseIntFlag(args, '--scenes');
411
+
412
+ validateSpeed(args, speed);
413
+
414
+ if (output && !/\.mp4$/i.test(output)) {
415
+ console.error('Error: --output path must end with .mp4');
416
+ process.exit(1);
417
+ }
418
+
419
+ const style = parseFlag(args, '--style');
420
+ if (style && !VALID_STYLES.includes(style)) {
421
+ console.error(`Error: --style must be one of: ${VALID_STYLES.join(', ')} (got: "${style}")`);
422
+ process.exit(1);
423
+ }
424
+
425
+ const ratio = parseFlag(args, '--ratio');
426
+ if (ratio && !VALID_RATIOS.includes(ratio)) {
427
+ console.error(`Error: --ratio must be one of: ${VALID_RATIOS.join(', ')} (got: "${ratio}")`);
428
+ process.exit(1);
429
+ }
430
+
431
+ if (scenes !== undefined && (isNaN(scenes) || scenes < 2 || scenes > 10)) {
432
+ console.error(`Error: --scenes must be between 2 and 10 (got: "${parseFlag(args, '--scenes')}")`);
433
+ process.exit(1);
434
+ }
435
+
436
+ const topic = parseFlag(args, '--topic');
437
+ const text = parseFlag(args, '--text');
438
+ if (!topic && !text) {
439
+ console.error('Error: provide --topic <text> or --text <content>');
440
+ process.exit(1);
441
+ }
442
+
443
+ let token;
444
+ if (explicitToken) {
445
+ token = explicitToken;
446
+ } else {
447
+ token = await getToken({ api });
448
+ const info = getTokenInfo();
449
+ if (info) console.log(`\x1b[32mLogged in as ${info.email}\x1b[0m`);
450
+ }
451
+
452
+ const quality = parseFlag(args, '--quality');
453
+ const VALID_QUALITIES = ['fast', 'hd', 'ultra', 'fast-aiberm', 'hd-aiberm'];
454
+ if (quality && !VALID_QUALITIES.includes(quality)) {
455
+ console.error(`Error: --quality must be one of: ${VALID_QUALITIES.join(', ')}`);
456
+ process.exit(1);
457
+ }
458
+
459
+ const scriptModel = parseFlag(args, '--script-model');
460
+ if (scriptModel && !VALID_SCRIPT_MODELS.includes(scriptModel)) {
461
+ console.error(`Error: --script-model must be one of: ${VALID_SCRIPT_MODELS.join(', ')} (got: "${scriptModel}")`);
462
+ process.exit(1);
463
+ }
464
+
465
+ const outputDir = parseFlag(args, '--output-dir', '-d');
466
+ const bgm = parseFlag(args, '--bgm');
467
+ if (bgm) {
468
+ const bgmResolved = path.resolve(bgm);
469
+ if (!fs.existsSync(bgmResolved)) {
470
+ console.error(`Error: --bgm file not found: ${bgm}`);
471
+ process.exit(1);
472
+ }
473
+ }
474
+ const bgmVolume = parseFloatFlag(args, '--bgm-volume');
475
+ if (bgmVolume !== undefined && (isNaN(bgmVolume) || bgmVolume < 0 || bgmVolume > 1)) {
476
+ console.error('Error: --bgm-volume must be between 0 and 1');
477
+ process.exit(1);
478
+ }
479
+ const fadeSeconds = parseFloatFlag(args, '--fade');
480
+
481
+ const opts = {
482
+ token, api,
483
+ topic: topic || undefined,
484
+ text: text || undefined,
485
+ voice: parseFlag(args, '--voice') || undefined,
486
+ speed, output,
487
+ outputDir: outputDir || undefined,
488
+ style: style || undefined,
489
+ ratio: ratio || undefined,
490
+ language: parseFlag(args, '--language') || undefined,
491
+ sceneCount: scenes,
492
+ quality: quality || undefined,
493
+ fadeSeconds: fadeSeconds !== undefined ? fadeSeconds : undefined,
494
+ bgm: bgm ? path.resolve(bgm) : undefined,
495
+ bgmVolume: bgmVolume !== undefined ? bgmVolume : undefined,
496
+ imageOnly: parseBoolFlag(args, '--image-only'),
497
+ scriptModel: scriptModel || undefined,
498
+ };
499
+
500
+ await runWithRetry(picstory, opts, api, explicitToken);
501
+ }
502
+
503
+ // ─── Metadata ─────────────────────────────────────────────────────────────────
504
+
505
+ const meta = {
506
+ picstory: {
507
+ usage: '<--topic|--text> [opts]',
508
+ description: 'Generate a narrated visual story video (AI images + TTS + MP4)',
509
+ options: [
510
+ `--topic <text> Story topic`,
511
+ `--text <content> Input text content to visualize`,
512
+ `--style <name> Visual style: sketchnote (default), neon_noir, minimal_3d, chalkboard, photo, manga_panel, vintage_newspaper`,
513
+ `--ratio <name> Aspect ratio: portrait (default, 9:16), landscape (16:9), square (1:1)`,
514
+ `--language <code> Script language: zh (default), en, ja, etc.`,
515
+ `--scenes <n> Number of scenes, 2-10 (default: ${PICSTORY_DEFAULTS.sceneCount})`,
516
+ `--quality <tier> Image quality: fast (default), hd, ultra (gpt-5.4-image-2, best quality, ~16× cost), hd-aiberm / fast-aiberm (Aiberm Gemini — strongest Chinese text rendering)`,
517
+ `--voice <id> TTS voice ID`,
518
+ `--speed <n> TTS speed 0.5-2.0 (default: ${PICSTORY_DEFAULTS.speed})`,
519
+ `--script-model <preset> LLM model for script generation: ${VALID_SCRIPT_MODELS.join(', ')} (default: server default)`,
520
+ `--bgm <file> Background music file (.mp3/.wav) to mix under narration`,
521
+ `--bgm-volume <n> BGM volume 0-1 (default: ${PICSTORY_DEFAULTS.bgmVolume})`,
522
+ `--fade <n> Scene fade-in/out duration in seconds (default: ${PICSTORY_DEFAULTS.fadeSeconds}, set 0 to disable)`,
523
+ `--image-only Save images+audio without rendering video`,
524
+ `--output-dir <dir> Directory for all output files (auto-created if needed)`,
525
+ `--output <path> Output file path (overrides --output-dir)`,
526
+ ],
527
+ examples: [
528
+ 'voxflow picstory --topic "AI Agent 入门" --style sketchnote',
529
+ 'voxflow picstory --topic "React hooks explained" --style photo --ratio landscape --language en',
530
+ 'voxflow picstory --topic "量子计算原理" --style minimal_3d --scenes 6',
531
+ 'voxflow picstory --topic "产品增长故事" --style neon_noir --bgm ~/music/lofi.mp3',
532
+ 'voxflow picstory --topic "三体故事" --style manga_panel --ratio portrait',
533
+ 'voxflow picstory --topic "2026 AI趋势" --output-dir ~/my-videos --fade 0.6',
534
+ ],
535
+ },
536
+ };
537
+
538
+ module.exports = {
539
+ picstory, handle, meta,
540
+ buildSketchnotePrompt, buildPhotoPrompt, buildImagePrompt,
541
+ VALID_STYLES, VALID_RATIOS, RATIO_CONFIG,
542
+ SCRIPT_MODEL_PRESETS, VALID_SCRIPT_MODELS,
543
+ _test: {
544
+ buildSketchnotePrompt, buildPhotoPrompt, buildImagePrompt, buildSystemPrompt, saveImageDataUrl,
545
+ ...require('./picstory-templates')._test,
546
+ },
547
+ };
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Podcast command — Dialogue parsing and script loading.
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ // CJK range covers CJK Unified Ideographs + Hiragana/Katakana
11
+ const CJK_REGEX = /[぀-ヿ㐀-䶿一-鿿]/;
12
+
13
+ /**
14
+ * Load and validate a podcast script JSON file.
15
+ *
16
+ * Expected format:
17
+ * {
18
+ * "segments": [
19
+ * { "speaker": "Host", "text": "Welcome to the show" },
20
+ * { "speaker": "Guest", "text": "Thanks for having me" }
21
+ * ],
22
+ * "voiceMapping": { ... }
23
+ * }
24
+ */
25
+ function loadScript(filePath) {
26
+ const resolved = path.resolve(filePath);
27
+ if (!fs.existsSync(resolved)) {
28
+ throw new Error(`Script file not found: ${resolved}`);
29
+ }
30
+
31
+ let raw;
32
+ try {
33
+ raw = JSON.parse(fs.readFileSync(resolved, 'utf8'));
34
+ } catch (err) {
35
+ throw new Error(`Invalid JSON in script file: ${err.message}`);
36
+ }
37
+
38
+ if (!Array.isArray(raw.segments) || raw.segments.length === 0) {
39
+ throw new Error(
40
+ 'Script must contain a non-empty "segments" array.\n' +
41
+ 'Expected: { "segments": [{ "speaker": "Host", "text": "Hello" }, ...] }'
42
+ );
43
+ }
44
+
45
+ for (let i = 0; i < raw.segments.length; i++) {
46
+ const seg = raw.segments[i];
47
+ if (!seg || typeof seg.speaker !== 'string' || !seg.speaker.trim()) {
48
+ throw new Error(`Script segment [${i}] is missing a valid "speaker" field`);
49
+ }
50
+ if (!seg || typeof seg.text !== 'string' || !seg.text.trim()) {
51
+ throw new Error(`Script segment [${i}] is missing a valid "text" field`);
52
+ }
53
+ }
54
+
55
+ return {
56
+ segments: raw.segments.map(s => ({ speaker: s.speaker.trim(), text: s.text.trim() })),
57
+ voiceMapping: raw.voiceMapping || {},
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Parse dialogue text into segments with speaker and text.
63
+ * Format: "SpeakerName:content" or "SpeakerName: content".
64
+ */
65
+ function parseDialogueText(text, opts = {}) {
66
+ const lines = text.split('\n').filter(line => line.trim());
67
+ const segments = [];
68
+ const dialoguePattern = /^([^::]+)[::]\s*(.+)$/;
69
+
70
+ const narratorLabel = opts.narratorLabel
71
+ || (CJK_REGEX.test(text) ? '旁白' : 'Narrator');
72
+
73
+ for (const line of lines) {
74
+ const trimmedLine = line.trim();
75
+ if (!trimmedLine) continue;
76
+
77
+ const match = trimmedLine.match(dialoguePattern);
78
+ if (match) {
79
+ const speaker = match[1].trim();
80
+ const content = match[2].trim();
81
+ if (content) {
82
+ segments.push({ speaker, text: content });
83
+ }
84
+ } else if (trimmedLine.length > 0) {
85
+ segments.push({ speaker: narratorLabel, text: trimmedLine });
86
+ }
87
+ }
88
+
89
+ return segments;
90
+ }
91
+
92
+ /**
93
+ * Convert structured PodcastScript dialogue array to segments.
94
+ */
95
+ function parseStructuredScript(script) {
96
+ if (!script?.dialogue || !Array.isArray(script.dialogue)) return [];
97
+ return script.dialogue.map(turn => {
98
+ const speakerInfo = script.speakers?.[turn.speaker];
99
+ const speakerName = speakerInfo?.name || turn.speaker;
100
+ return {
101
+ speaker: speakerName,
102
+ text: turn.text,
103
+ intent: turn.intent || null,
104
+ pauseAfterMs: Number.isFinite(turn.pauseAfterMs) ? turn.pauseAfterMs : null
105
+ };
106
+ });
107
+ }
108
+
109
+ module.exports = { loadScript, parseDialogueText, parseStructuredScript };