voxflow 1.15.0 → 1.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (453) hide show
  1. package/README.md +34 -0
  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-render.js +282 -0
  383. package/lib/commands/slice-stage.js +264 -0
  384. package/lib/commands/slice.js +346 -0
  385. package/lib/commands/slides/constants.js +108 -0
  386. package/lib/commands/slides/html-renderer.js +338 -0
  387. package/lib/commands/slides/index.js +345 -0
  388. package/lib/commands/slides.js +11 -0
  389. package/lib/commands/story.js +302 -0
  390. package/lib/commands/summarize.js +532 -0
  391. package/lib/commands/synthesize.js +261 -0
  392. package/lib/commands/translate.js +593 -0
  393. package/lib/commands/upgrade.js +249 -0
  394. package/lib/commands/video-translate.js +577 -0
  395. package/lib/commands/voices.js +292 -0
  396. package/lib/core/agent-env.js +104 -0
  397. package/lib/core/args.js +107 -0
  398. package/lib/core/asr-client.js +448 -0
  399. package/lib/core/asr-jobs-client.js +126 -0
  400. package/lib/core/asr-jobs-store.js +105 -0
  401. package/lib/core/asr-r2-upload.js +181 -0
  402. package/lib/core/asr-upload.js +132 -0
  403. package/lib/core/audio-extract.js +150 -0
  404. package/lib/core/audio.js +219 -0
  405. package/lib/core/auth.js +880 -0
  406. package/lib/core/config.js +197 -0
  407. package/lib/core/feedback.js +64 -0
  408. package/lib/core/ffmpeg.js +476 -0
  409. package/lib/core/http.js +188 -0
  410. package/lib/core/image-client.js +55 -0
  411. package/lib/core/intent-params.js +11 -0
  412. package/lib/core/llm-client.js +76 -0
  413. package/lib/core/logger.js +208 -0
  414. package/lib/core/mic-recorder.js +182 -0
  415. package/lib/core/pause-markers.js +94 -0
  416. package/lib/core/podcast-pacing.js +118 -0
  417. package/lib/core/spinner.js +33 -0
  418. package/lib/core/srt.js +394 -0
  419. package/lib/core/telemetry.js +100 -0
  420. package/lib/core/timeline.js +92 -0
  421. package/lib/core/tts-synthesizer.js +70 -0
  422. package/lib/core/update-check.js +185 -0
  423. package/lib/core/url-download.js +148 -0
  424. package/lib/core/whisper-local.js +279 -0
  425. package/lib/internal/deck-validator.js +488 -0
  426. package/lib/internal/slice-themes.json +370 -0
  427. package/lib/stage-core/cloud-render.js +170 -0
  428. package/lib/stage-core/deck-format.js +133 -0
  429. package/lib/stage-core/edit-prompt.js +104 -0
  430. package/lib/stage-core/event-bus.js +31 -0
  431. package/lib/stage-core/port.js +46 -0
  432. package/lib/stage-core/server.js +352 -0
  433. package/lib/stage-core/snapshot-store.js +198 -0
  434. package/lib/stage-core/watcher.js +106 -0
  435. package/lib/stage-ui/slice/template.js +1672 -0
  436. package/package.json +9 -4
  437. package/skills/.claude-plugin/marketplace.json +22 -0
  438. package/skills/.claude-plugin/plugin.json +25 -0
  439. package/skills/LICENSE +21 -0
  440. package/skills/README.md +120 -0
  441. package/skills/hub/SKILL.md +317 -0
  442. package/skills/podcast/SKILL.md +146 -0
  443. package/skills/slice/SKILL.md +205 -0
  444. package/skills/slice/agents/openai.yaml +4 -0
  445. package/skills/slice/references/deck-schema.md +183 -0
  446. package/skills/slice/references/example-decks.md +108 -0
  447. package/skills/slice/references/themes.md +172 -0
  448. package/skills/transcribe/SKILL.md +473 -0
  449. package/skills/video/SKILL.md +261 -0
  450. package/skills/voxflow-slice/SKILL.md +271 -0
  451. package/skills/voxflow-slice/examples/article.md +13 -0
  452. package/skills/voxflow-slice/examples/expected-deck.json +39 -0
  453. package/skills/voxflow-slice/examples/validate.mjs +46 -0
@@ -0,0 +1,577 @@
1
+ /**
2
+ * VoxFlow CLI — Video Translate command
3
+ *
4
+ * End-to-end video translation pipeline:
5
+ * 1. Extract audio from video (FFmpeg)
6
+ * 2. Transcribe audio → SRT (ASR, forced flash mode for fine-grained timestamps)
7
+ * 3. Translate SRT → target language (LLM)
8
+ * 4. TTS dub + burn subtitles + merge back into video (Dub + FFmpeg)
9
+ *
10
+ * Chains existing asr(), translate(), dub() functions programmatically.
11
+ * All intermediate files go in a temp directory — user only sees the final MP4.
12
+ *
13
+ * Cost: ~2-4 quota (1 ASR + 1 translate batch + 1-N TTS per caption)
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const os = require('os');
19
+ const { checkFfmpeg, extractAudio } = require('../core/ffmpeg');
20
+ const { asr } = require('./asr');
21
+ const { translate } = require('./translate');
22
+ const { dub } = require('./dub');
23
+ const { detectLanguage } = require('../core/llm-client');
24
+ const { parseSrt } = require('../core/srt');
25
+ const { API_BASE, VIDEO_TRANSLATE_DEFAULTS } = require('../core/config');
26
+
27
+ // ─── Language → ASR engine mapping ──────────────────────────────────────────
28
+
29
+ /**
30
+ * Map short language codes (used by --from) to Tencent ASR engine types.
31
+ * Falls back to 16k_zh (Chinese) if not found.
32
+ */
33
+ const LANG_TO_ASR_ENGINE = {
34
+ zh: '16k_zh',
35
+ en: '16k_en',
36
+ ja: '16k_ja',
37
+ ko: '16k_ko',
38
+ // Mixed modes
39
+ 'zh-en': '16k_zh_en',
40
+ };
41
+
42
+ function resolveAsrLang(fromCode, explicitAsrLang) {
43
+ // Explicit --asr-lang takes priority
44
+ if (explicitAsrLang) return explicitAsrLang;
45
+ // Map from --from code
46
+ if (fromCode && LANG_TO_ASR_ENGINE[fromCode]) return LANG_TO_ASR_ENGINE[fromCode];
47
+ // Default to Chinese
48
+ return '16k_zh';
49
+ }
50
+
51
+ function looksUntranslated(translatedText, sourceText, targetLang) {
52
+ if (translatedText === sourceText) return true;
53
+ const latinTargets = ['en', 'fr', 'de', 'es', 'pt', 'it', 'vi'];
54
+ if (latinTargets.includes(targetLang)) {
55
+ const cjkChars = (translatedText.match(/[一-鿿぀-ヿ가-힯]/g) || []).length;
56
+ const totalChars = translatedText.replace(/\s/g, '').length;
57
+ if (totalChars > 0 && cjkChars / totalChars > 0.5) return true;
58
+ }
59
+ return false;
60
+ }
61
+
62
+ // ─── Main command ────────────────────────────────────────────────────────────
63
+
64
+ /**
65
+ * Video-translate command entry point (with SIGINT guard).
66
+ *
67
+ * @param {object} opts
68
+ * @param {string} opts.token - JWT auth token
69
+ * @param {string} [opts.api] - Backend base URL
70
+ * @param {string} opts.input - Input video file path
71
+ * @param {string} [opts.from] - Source language code (auto-detect if omitted)
72
+ * @param {string} opts.to - Target language code (required)
73
+ * @param {string} [opts.voice] - TTS voice ID
74
+ * @param {string} [opts.voicesMap] - Voice mapping JSON file path
75
+ * @param {boolean}[opts.realign] - Adjust subtitle timing for target language
76
+ * @param {string} [opts.output] - Output MP4 path
77
+ * @param {boolean}[opts.keepIntermediates] - Keep temp files after completion
78
+ * @param {number} [opts.batchSize] - Translation batch size
79
+ * @param {number} [opts.speed] - TTS speed (0.5-2.0)
80
+ * @param {string} [opts.asrMode] - Override ASR mode: sentence, flash, file, auto
81
+ * @param {string} [opts.asrLang] - Override ASR engine type: 16k_zh, 16k_en, etc.
82
+ * @param {string} [opts.engine] - ASR engine: auto, local, cloud (default: auto)
83
+ * @param {string} [opts.model] - Whisper model for local engine: tiny, base, small, medium, large
84
+ * @returns {Promise<object>} Result with outputPath and stage details
85
+ */
86
+ async function videoTranslate(opts) {
87
+ const sigintHandler = () => {
88
+ console.log('\n\nVideo translation cancelled.');
89
+ process.exit(130);
90
+ };
91
+ process.on('SIGINT', sigintHandler);
92
+
93
+ // Background refresh: video-translate routinely outlives the 1h Supabase
94
+ // JWT (long video → ASR poll → batch translate → per-caption TTS). Refresh
95
+ // proactively at expiresAt-5min so the user doesn't see "Token expired,
96
+ // refreshing..." mid-pipeline.
97
+ const { startBackgroundRefresh } = require('../core/auth');
98
+ const stopRefresh = startBackgroundRefresh({ api: opts.api });
99
+
100
+ try {
101
+ return await _videoTranslate(opts);
102
+ } finally {
103
+ stopRefresh();
104
+ process.removeListener('SIGINT', sigintHandler);
105
+ }
106
+ }
107
+
108
+ async function _videoTranslate(opts) {
109
+ const {
110
+ token,
111
+ api = API_BASE,
112
+ input,
113
+ from,
114
+ to,
115
+ voice,
116
+ voicesMap,
117
+ realign = false,
118
+ output: userOutput,
119
+ keepIntermediates = false,
120
+ batchSize = VIDEO_TRANSLATE_DEFAULTS.batchSize,
121
+ speed = VIDEO_TRANSLATE_DEFAULTS.speed,
122
+ asrMode,
123
+ asrLang,
124
+ engine = 'auto',
125
+ model,
126
+ } = opts;
127
+
128
+ const resolvedInput = path.resolve(input);
129
+ const inputBasename = path.basename(resolvedInput, path.extname(resolvedInput));
130
+
131
+ console.log('\n=== VoxFlow Video Translate ===');
132
+ console.log(`Input: ${path.basename(resolvedInput)}`);
133
+ console.log(`Target: ${to}`);
134
+ console.log('');
135
+
136
+ // Create temp directory for intermediate files
137
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'voxflow-vtranslate-'));
138
+
139
+ let totalQuota = 0;
140
+ const stages = {};
141
+
142
+ try {
143
+ // ─── Stage 1: Check FFmpeg ──────────────────────────────────────────
144
+ process.stdout.write('[1/5] Checking FFmpeg... ');
145
+ const ffmpegInfo = await checkFfmpeg();
146
+ if (!ffmpegInfo.available) {
147
+ throw new Error('FFmpeg is required for video-translate. Install: https://ffmpeg.org/download.html');
148
+ }
149
+ console.log(`OK (${ffmpegInfo.version})`);
150
+
151
+ // ─── Stage 2: Extract audio + ASR ───────────────────────────────────
152
+ process.stdout.write('[2/5] Transcribing audio... ');
153
+ const audioPath = path.join(tmpDir, 'extracted-audio.wav');
154
+ await extractAudio(resolvedInput, audioPath);
155
+
156
+ const asrOutputPath = path.join(tmpDir, 'source.srt');
157
+ const effectiveAsrLang = resolveAsrLang(from, asrLang);
158
+ const asrOpts = {
159
+ token,
160
+ api,
161
+ input: audioPath,
162
+ format: 'srt',
163
+ output: asrOutputPath,
164
+ lang: effectiveAsrLang,
165
+ engine,
166
+ // Force 'flash' mode for video-translate: ensures fine-grained per-sentence
167
+ // timestamps (one caption per sentence) instead of 'sentence' mode which
168
+ // dumps everything into a single giant block.
169
+ mode: asrMode || 'flash',
170
+ };
171
+ if (model) asrOpts.model = model;
172
+
173
+ let asrResult;
174
+ try {
175
+ asrResult = await asr(asrOpts);
176
+ } catch (asrErr) {
177
+ // Flash mode can fail on some Tencent backend issues — fallback to azure
178
+ if (!asrMode && engine !== 'local' && asrErr.message && asrErr.message.includes('500')) {
179
+ console.log(`\n ⚠ Flash ASR failed, falling back to Azure...`);
180
+ asrOpts.engine = 'azure';
181
+ asrOpts.mode = undefined;
182
+ // Map Tencent lang codes to Azure BCP-47
183
+ const azureLangMap = { '16k_zh': 'zh-CN', '16k_en': 'en-US', '16k_ja': 'ja-JP', '16k_ko': 'ko-KR', '16k_zh_en': 'auto' };
184
+ asrOpts.lang = azureLangMap[effectiveAsrLang] || 'auto';
185
+ asrResult = await asr(asrOpts);
186
+ } else {
187
+ throw asrErr;
188
+ }
189
+ }
190
+
191
+ if (asrResult.captionCount === 0) {
192
+ throw new Error('ASR produced no captions. The video may have no audible speech.');
193
+ }
194
+
195
+ stages.asr = {
196
+ mode: asrResult.mode,
197
+ duration: asrResult.duration,
198
+ captionCount: asrResult.captionCount,
199
+ quotaUsed: asrResult.quotaUsed,
200
+ };
201
+ totalQuota += asrResult.quotaUsed;
202
+ console.log(`${asrResult.captionCount} captions (${asrResult.mode} mode)`);
203
+
204
+ // ─── Detect source language if not specified ────────────────────────
205
+ let fromCode = from;
206
+ if (!fromCode) {
207
+ const srtContent = fs.readFileSync(asrOutputPath, 'utf8');
208
+ const captions = parseSrt(srtContent);
209
+ const sample = captions.slice(0, 3).map(c => c.text).join(' ');
210
+ fromCode = await detectLanguage({ apiBase: api, text: sample }) || 'auto';
211
+ }
212
+
213
+ // ─── Stage 3: Translate ─────────────────────────────────────────────
214
+ process.stdout.write(`[3/5] Translating (${fromCode} → ${to})... `);
215
+ const translatedSrtPath = path.join(tmpDir, `translated-${to}.srt`);
216
+ const translateResult = await translate({
217
+ token,
218
+ api,
219
+ srt: asrOutputPath,
220
+ from: fromCode,
221
+ to,
222
+ output: translatedSrtPath,
223
+ realign,
224
+ batchSize,
225
+ });
226
+
227
+ stages.translate = {
228
+ from: translateResult.from,
229
+ to: translateResult.to,
230
+ captionCount: translateResult.captionCount,
231
+ quotaUsed: translateResult.quotaUsed,
232
+ };
233
+ totalQuota += translateResult.quotaUsed;
234
+
235
+ // ─── Translation quality fix: re-translate untranslated captions ────
236
+ const translatedContent = fs.readFileSync(translatedSrtPath, 'utf8');
237
+ const translatedCaptions = parseSrt(translatedContent);
238
+ const sourceCaptions = parseSrt(fs.readFileSync(asrOutputPath, 'utf8'));
239
+ const { formatSrt } = require('../core/srt');
240
+ const { chatCompletion } = require('../core/llm-client');
241
+ const { TRANSLATE_DEFAULTS } = require('../core/config');
242
+
243
+ let fixedCount = 0;
244
+ for (let ci = 0; ci < translatedCaptions.length && ci < sourceCaptions.length; ci++) {
245
+ if (looksUntranslated(translatedCaptions[ci].text, sourceCaptions[ci].text, to)) {
246
+ // This caption was not translated — retry individually (up to 2 attempts)
247
+ const { LANG_MAP } = require('./translate');
248
+ const toLang = LANG_MAP[to] || to;
249
+ const fromLang = LANG_MAP[fromCode] || fromCode;
250
+
251
+ for (let attempt = 0; attempt < 2; attempt++) {
252
+ try {
253
+ if (attempt > 0) await new Promise(r => setTimeout(r, 2000)); // wait 2s before retry
254
+ const singleResult = await chatCompletion({
255
+ apiBase: api,
256
+ token,
257
+ messages: [
258
+ { role: 'system', content: `Translate this subtitle from ${fromLang} to ${toLang}. Return ONLY the translation, nothing else.` },
259
+ { role: 'user', content: sourceCaptions[ci].text },
260
+ ],
261
+ temperature: TRANSLATE_DEFAULTS.temperature,
262
+ maxTokens: TRANSLATE_DEFAULTS.maxTokens,
263
+ });
264
+ const fixed = singleResult.content.trim();
265
+ if (fixed && fixed !== sourceCaptions[ci].text) {
266
+ translatedCaptions[ci].text = fixed;
267
+ fixedCount++;
268
+ break; // success
269
+ }
270
+ totalQuota++;
271
+ } catch {
272
+ // Retry on next iteration or give up
273
+ totalQuota++;
274
+ }
275
+ }
276
+ }
277
+ }
278
+
279
+ // Write the fixed SRT back
280
+ if (fixedCount > 0) {
281
+ const fixedSrt = formatSrt(translatedCaptions);
282
+ fs.writeFileSync(translatedSrtPath, fixedSrt, 'utf8');
283
+ }
284
+
285
+ const finalUntranslated = translatedCaptions.filter((c, i) => i < sourceCaptions.length && looksUntranslated(c.text, sourceCaptions[i].text, to)).length;
286
+ if (fixedCount > 0) {
287
+ console.log(`${translateResult.captionCount} captions translated (${fixedCount} fixed on retry)`);
288
+ } else {
289
+ console.log(`${translateResult.captionCount} captions translated`);
290
+ }
291
+ if (finalUntranslated > 0) {
292
+ console.log(` ⚠ ${finalUntranslated} caption(s) could not be translated`);
293
+ }
294
+
295
+ // ─── Stage 4: Dub + Video merge ─────────────────────────────────────
296
+ process.stdout.write('[4/5] Dubbing and merging video... ');
297
+
298
+ // Determine final output path (use temp for now; subtitle burn-in is stage 5)
299
+ // Pre-build filename — see scripts/check-no-asset-rewrite.js (ncc rewrite trap)
300
+ const defaultFname = inputBasename + '-' + to + '.mp4';
301
+ const outputPath = userOutput
302
+ ? path.resolve(userOutput)
303
+ : path.resolve(path.dirname(resolvedInput), defaultFname);
304
+
305
+ // Dub into a temp file first — subtitle burn-in happens in stage 5
306
+ const dubbedTempPath = path.join(tmpDir, 'dubbed-nosubs.mp4');
307
+
308
+ const dubResult = await dub({
309
+ token,
310
+ api,
311
+ srt: translatedSrtPath,
312
+ voice,
313
+ voicesMap,
314
+ speed,
315
+ speedAuto: true, // Always enable speed-auto for video-translate
316
+ video: resolvedInput,
317
+ output: dubbedTempPath,
318
+ });
319
+
320
+ stages.dub = {
321
+ segmentCount: dubResult.segmentCount,
322
+ duration: dubResult.duration,
323
+ quotaUsed: dubResult.quotaUsed,
324
+ warnings: dubResult.warnings,
325
+ };
326
+ totalQuota += dubResult.quotaUsed;
327
+ console.log(`${dubResult.segmentCount} segments dubbed`);
328
+
329
+ // ─── Stage 5: Burn subtitles into video ─────────────────────────────
330
+ process.stdout.write('[5/5] Burning subtitles into video... ');
331
+
332
+ // Copy subtitle file to a simple path (avoids ffmpeg path parsing issues)
333
+ const subTempPath = path.join(tmpDir, 'subs.srt');
334
+ fs.copyFileSync(translatedSrtPath, subTempPath);
335
+
336
+ let subtitlesBurned = false;
337
+ try {
338
+ const { runCommand } = require('../core/ffmpeg');
339
+ await runCommand('ffmpeg', [
340
+ '-i', dubbedTempPath,
341
+ '-vf', `subtitles=${subTempPath}:force_style='FontSize=18,PrimaryColour=&Hffffff,OutlineColour=&H000000,Outline=2,Shadow=1,Alignment=2,Bold=1'`,
342
+ '-c:a', 'copy',
343
+ '-y',
344
+ outputPath,
345
+ ]);
346
+ subtitlesBurned = true;
347
+ console.log('OK');
348
+ } catch (err) {
349
+ // Subtitle burn-in failed (likely no libass) — fall back to video without subs
350
+ console.log(`skip (${err.message?.includes('subtitles') ? 'libass not available' : 'failed'})`);
351
+ // Move dubbed file as final output
352
+ fs.copyFileSync(dubbedTempPath, outputPath);
353
+ // Save SRT alongside output for external use
354
+ const srtOutputPath = outputPath.replace(/\.[^.]+$/, `.${to}.srt`);
355
+ fs.copyFileSync(translatedSrtPath, srtOutputPath);
356
+ console.log(` → Subtitle file saved separately: ${srtOutputPath}`);
357
+ }
358
+
359
+ // ─── Copy intermediates if requested ────────────────────────────────
360
+ if (keepIntermediates) {
361
+ const intermediateDir = path.resolve(
362
+ path.dirname(outputPath),
363
+ `${inputBasename}-${to}-intermediates`,
364
+ );
365
+ fs.mkdirSync(intermediateDir, { recursive: true });
366
+
367
+ const filesToCopy = [
368
+ ['extracted-audio.wav', audioPath],
369
+ ['source.srt', asrOutputPath],
370
+ [`translated-${to}.srt`, translatedSrtPath],
371
+ ];
372
+
373
+ for (const [name, src] of filesToCopy) {
374
+ if (fs.existsSync(src)) {
375
+ fs.copyFileSync(src, path.join(intermediateDir, name));
376
+ }
377
+ }
378
+
379
+ console.log(`\nIntermediates saved: ${intermediateDir}`);
380
+ }
381
+
382
+ // ─── Always save SRT alongside output (useful for external players) ──
383
+ const srtOutputPath = outputPath.replace(/\.[^.]+$/, `.srt`);
384
+ fs.copyFileSync(translatedSrtPath, srtOutputPath);
385
+
386
+ // ─── Summary ────────────────────────────────────────────────────────
387
+ console.log('\n=== Done ===');
388
+ console.log(`Output: ${outputPath}`);
389
+ console.log(`Subtitle: ${srtOutputPath}`);
390
+ console.log(`Language: ${fromCode} → ${to}`);
391
+ console.log(`Captions: ${translateResult.captionCount}`);
392
+ console.log(`Duration: ${dubResult.duration.toFixed(1)}s`);
393
+ console.log(`Quota: ${totalQuota} used`);
394
+
395
+ if (stages.dub.warnings && stages.dub.warnings.length > 0) {
396
+ console.log(`\nWarnings:`);
397
+ for (const w of stages.dub.warnings) {
398
+ console.log(` - ${w}`);
399
+ }
400
+ }
401
+
402
+ // ─── Auto-open the output video ─────────────────────────────────────
403
+ try {
404
+ const open = require('open');
405
+ await open(outputPath);
406
+ } catch {
407
+ // open() may fail in headless/CI environments — ignore silently
408
+ }
409
+
410
+ return {
411
+ outputPath,
412
+ srtOutputPath,
413
+ from: fromCode,
414
+ to,
415
+ captionCount: translateResult.captionCount,
416
+ quotaUsed: totalQuota,
417
+ stages,
418
+ };
419
+ } finally {
420
+ // Cleanup temp directory
421
+ if (!keepIntermediates) {
422
+ try {
423
+ fs.rmSync(tmpDir, { recursive: true, force: true });
424
+ } catch {
425
+ // Best-effort cleanup
426
+ }
427
+ }
428
+ }
429
+ }
430
+
431
+ // ─── Exports ─────────────────────────────────────────────────────────────────
432
+
433
+ // ─── CLI Handler ────────────────────────────────────────────────────────────
434
+
435
+ async function handle(args) {
436
+ const { parseFlag, parseIntFlag, parseFloatFlag, parseBoolFlag, validateSpeed, runWithRetry } = require('../core/args');
437
+ const { getToken, getTokenInfo } = require('../core/auth');
438
+ const { API_BASE } = require('../core/config');
439
+
440
+ const api = parseFlag(args, '--api') || API_BASE;
441
+ const explicitToken = parseFlag(args, '--token');
442
+
443
+ // Parse and validate before auth
444
+ const input = parseFlag(args, '--input');
445
+ const from = parseFlag(args, '--from');
446
+ const to = parseFlag(args, '--to');
447
+ const voice = parseFlag(args, '--voice');
448
+ const voicesMap = parseFlag(args, '--voices');
449
+ const output = parseFlag(args, '--output', '-o');
450
+ const realign = parseBoolFlag(args, '--realign');
451
+ const keepIntermediates = parseBoolFlag(args, '--keep-intermediates');
452
+ const batchSize = parseIntFlag(args, '--batch-size');
453
+ const speed = parseFloatFlag(args, '--speed');
454
+ const asrMode = parseFlag(args, '--asr-mode');
455
+ const asrLang = parseFlag(args, '--asr-lang');
456
+ const engine = parseFlag(args, '--engine');
457
+ const model = parseFlag(args, '--model');
458
+
459
+ // Validate --input is required
460
+ if (!input) {
461
+ console.error('Error: --input <video-file> is required. Example: voxflow video-translate --input video.mp4 --to en');
462
+ process.exit(1);
463
+ }
464
+
465
+ // Validate --to is required
466
+ if (!to) {
467
+ console.error('Error: --to <lang> is required. Example: voxflow video-translate --input video.mp4 --to en');
468
+ process.exit(1);
469
+ }
470
+
471
+ // Validate language codes
472
+ const validLangs = ['zh', 'en', 'ja', 'ko', 'fr', 'de', 'es', 'pt', 'ru', 'ar', 'th', 'vi', 'it'];
473
+ if (to && !validLangs.includes(to)) {
474
+ console.error(`Error: --to must be one of: ${validLangs.join(', ')} (got: "${to}")`);
475
+ process.exit(1);
476
+ }
477
+ if (from && !validLangs.includes(from) && from !== 'auto') {
478
+ console.error(`Error: --from must be one of: auto, ${validLangs.join(', ')} (got: "${from}")`);
479
+ process.exit(1);
480
+ }
481
+
482
+ // Validate input file exists
483
+ if (input) {
484
+ const fs = require('fs');
485
+ const path = require('path');
486
+ const resolved = path.resolve(input);
487
+ if (!fs.existsSync(resolved)) {
488
+ console.error(`Error: Video file not found: ${resolved}`);
489
+ process.exit(1);
490
+ }
491
+ }
492
+
493
+ // Validate speed
494
+ validateSpeed(args, speed);
495
+
496
+ // Validate batch size
497
+ if (batchSize !== undefined && (isNaN(batchSize) || batchSize < 1 || batchSize > 20)) {
498
+ console.error(`Error: --batch-size must be between 1 and 20 (got: "${parseFlag(args, '--batch-size')}")`);
499
+ process.exit(1);
500
+ }
501
+
502
+ // Validate ASR mode
503
+ const validAsrModes = ['auto', 'sentence', 'flash', 'file'];
504
+ if (asrMode && !validAsrModes.includes(asrMode)) {
505
+ console.error(`Error: --asr-mode must be one of: ${validAsrModes.join(', ')} (got: "${asrMode}")`);
506
+ process.exit(1);
507
+ }
508
+
509
+ // Validate engine
510
+ const validEngines = ['auto', 'local', 'cloud', 'whisper', 'tencent'];
511
+ if (engine && !validEngines.includes(engine)) {
512
+ console.error(`Error: --engine must be one of: ${validEngines.join(', ')} (got: "${engine}")`);
513
+ process.exit(1);
514
+ }
515
+
516
+ // Validate voices map file exists
517
+ if (voicesMap) {
518
+ const fs = require('fs');
519
+ const path = require('path');
520
+ const resolved = path.resolve(voicesMap);
521
+ if (!fs.existsSync(resolved)) {
522
+ console.error(`Error: Voices map file not found: ${resolved}`);
523
+ process.exit(1);
524
+ }
525
+ }
526
+
527
+ // Authenticate after all validation
528
+ let token;
529
+ if (explicitToken) {
530
+ token = explicitToken;
531
+ } else {
532
+ token = await getToken({ api });
533
+ const info = getTokenInfo();
534
+ if (info) {
535
+ console.log(`\x1b[32mLogged in as ${info.email}\x1b[0m`);
536
+ }
537
+ }
538
+
539
+ const opts = {
540
+ token, api, input, from, to, voice, voicesMap, output,
541
+ realign, keepIntermediates, batchSize, speed, asrMode, asrLang,
542
+ engine, model,
543
+ };
544
+
545
+ await runWithRetry(videoTranslate, opts, api, explicitToken);
546
+ }
547
+
548
+ const meta = {
549
+ 'video-translate': {
550
+ usage: '[opts]',
551
+ description: 'Translate entire video: ASR → translate → dub → merge',
552
+ options: [
553
+ `--input <file> Input video file (required)`,
554
+ `--to <lang> Target language code (required)`,
555
+ `--from <lang> Source language code (default: auto-detect)`,
556
+ `--voice <id> TTS voice ID for dubbed audio`,
557
+ `--voices <file> JSON speaker→voiceId map for multi-speaker dubbing`,
558
+ `--realign Adjust subtitle timing for target language length`,
559
+ `--speed <n> TTS speed 0.5-2.0 (default: ${require('../core/config').VIDEO_TRANSLATE_DEFAULTS.speed})`,
560
+ `--batch-size <n> Translation batch size, 1-20 (default: ${require('../core/config').VIDEO_TRANSLATE_DEFAULTS.batchSize})`,
561
+ `--keep-intermediates Keep intermediate files (SRT, audio) for debugging`,
562
+ `--output <path> Output MP4 path (default: <input>-<lang>.mp4)`,
563
+ `--asr-mode <mode> Override ASR mode: auto, sentence, flash, file`,
564
+ `--asr-lang <engine> Override ASR engine: 16k_zh, 16k_en, 16k_ja, 16k_ko, etc.`,
565
+ `--engine <engine> ASR engine: auto, local, cloud (default: auto)`,
566
+ `--model <model> Whisper model for local engine: tiny, base, small, medium, large`,
567
+ ],
568
+ examples: [
569
+ 'voxflow video-translate --input video.mp4 --to en',
570
+ 'voxflow video-translate --input video.mp4 --from zh --to en --realign',
571
+ 'voxflow video-translate --input video.mp4 --to ja --voice v-male-Bk7vD3xP',
572
+ 'voxflow video-translate --input video.mp4 --to en --engine local',
573
+ ],
574
+ },
575
+ };
576
+
577
+ module.exports = { videoTranslate, handle, meta };