@kenkaiiii/gg-editor 0.7.0 → 0.7.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 (446) hide show
  1. package/dist/cli.js +64 -1
  2. package/dist/cli.js.map +1 -1
  3. package/dist/core/audio-mix.d.ts.map +1 -1
  4. package/dist/core/audio-mix.js +8 -1
  5. package/dist/core/audio-mix.js.map +1 -1
  6. package/dist/core/audio-mix.test.js +1 -1
  7. package/dist/core/audio-mix.test.js.map +1 -1
  8. package/dist/core/auth/api-keys.d.ts +1 -1
  9. package/dist/core/auth/api-keys.d.ts.map +1 -1
  10. package/dist/core/auth/api-keys.js +2 -1
  11. package/dist/core/auth/api-keys.js.map +1 -1
  12. package/dist/core/auth/login.d.ts.map +1 -1
  13. package/dist/core/auth/login.js.map +1 -1
  14. package/dist/core/beats.d.ts +59 -0
  15. package/dist/core/beats.d.ts.map +1 -0
  16. package/dist/core/beats.js +122 -0
  17. package/dist/core/beats.js.map +1 -0
  18. package/dist/core/beats.test.d.ts +2 -0
  19. package/dist/core/beats.test.d.ts.map +1 -0
  20. package/dist/core/beats.test.js +86 -0
  21. package/dist/core/beats.test.js.map +1 -0
  22. package/dist/core/brand-kit.d.ts +80 -0
  23. package/dist/core/brand-kit.d.ts.map +1 -0
  24. package/dist/core/brand-kit.js +96 -0
  25. package/dist/core/brand-kit.js.map +1 -0
  26. package/dist/core/brand-kit.test.d.ts +2 -0
  27. package/dist/core/brand-kit.test.d.ts.map +1 -0
  28. package/dist/core/brand-kit.test.js +76 -0
  29. package/dist/core/brand-kit.test.js.map +1 -0
  30. package/dist/core/bundled-sfx.d.ts +64 -0
  31. package/dist/core/bundled-sfx.d.ts.map +1 -0
  32. package/dist/core/bundled-sfx.js +218 -0
  33. package/dist/core/bundled-sfx.js.map +1 -0
  34. package/dist/core/bundled-sfx.test.d.ts +2 -0
  35. package/dist/core/bundled-sfx.test.d.ts.map +1 -0
  36. package/dist/core/bundled-sfx.test.js +81 -0
  37. package/dist/core/bundled-sfx.test.js.map +1 -0
  38. package/dist/core/child-abort.d.ts +57 -0
  39. package/dist/core/child-abort.d.ts.map +1 -0
  40. package/dist/core/child-abort.js +95 -0
  41. package/dist/core/child-abort.js.map +1 -0
  42. package/dist/core/child-abort.test.d.ts +2 -0
  43. package/dist/core/child-abort.test.d.ts.map +1 -0
  44. package/dist/core/child-abort.test.js +88 -0
  45. package/dist/core/child-abort.test.js.map +1 -0
  46. package/dist/core/clip-scoring.d.ts +44 -0
  47. package/dist/core/clip-scoring.d.ts.map +1 -0
  48. package/dist/core/clip-scoring.js +165 -0
  49. package/dist/core/clip-scoring.js.map +1 -0
  50. package/dist/core/clip-scoring.test.d.ts +2 -0
  51. package/dist/core/clip-scoring.test.d.ts.map +1 -0
  52. package/dist/core/clip-scoring.test.js +113 -0
  53. package/dist/core/clip-scoring.test.js.map +1 -0
  54. package/dist/core/emoji-captions.d.ts +45 -0
  55. package/dist/core/emoji-captions.d.ts.map +1 -0
  56. package/dist/core/emoji-captions.js +121 -0
  57. package/dist/core/emoji-captions.js.map +1 -0
  58. package/dist/core/face-reframe.d.ts +91 -0
  59. package/dist/core/face-reframe.d.ts.map +1 -0
  60. package/dist/core/face-reframe.js +141 -0
  61. package/dist/core/face-reframe.js.map +1 -0
  62. package/dist/core/face-reframe.test.d.ts +2 -0
  63. package/dist/core/face-reframe.test.d.ts.map +1 -0
  64. package/dist/core/face-reframe.test.js +171 -0
  65. package/dist/core/face-reframe.test.js.map +1 -0
  66. package/dist/core/filler-words.d.ts +57 -9
  67. package/dist/core/filler-words.d.ts.map +1 -1
  68. package/dist/core/filler-words.js +61 -9
  69. package/dist/core/filler-words.js.map +1 -1
  70. package/dist/core/filler-words.test.js +91 -17
  71. package/dist/core/filler-words.test.js.map +1 -1
  72. package/dist/core/hook-rewrite.d.ts +48 -0
  73. package/dist/core/hook-rewrite.d.ts.map +1 -0
  74. package/dist/core/hook-rewrite.js +151 -0
  75. package/dist/core/hook-rewrite.js.map +1 -0
  76. package/dist/core/hook-rewrite.test.d.ts +2 -0
  77. package/dist/core/hook-rewrite.test.d.ts.map +1 -0
  78. package/dist/core/hook-rewrite.test.js +58 -0
  79. package/dist/core/hook-rewrite.test.js.map +1 -0
  80. package/dist/core/hosts/lazy.d.ts.map +1 -1
  81. package/dist/core/hosts/lazy.js +2 -0
  82. package/dist/core/hosts/lazy.js.map +1 -1
  83. package/dist/core/hosts/premiere/adapter.d.ts +1 -0
  84. package/dist/core/hosts/premiere/adapter.d.ts.map +1 -1
  85. package/dist/core/hosts/premiere/adapter.js.map +1 -1
  86. package/dist/core/hosts/premiere/bridge-source.d.ts.map +1 -1
  87. package/dist/core/hosts/premiere/bridge-source.js +6 -3
  88. package/dist/core/hosts/premiere/bridge-source.js.map +1 -1
  89. package/dist/core/hosts/resolve/adapter.d.ts +1 -0
  90. package/dist/core/hosts/resolve/adapter.d.ts.map +1 -1
  91. package/dist/core/hosts/resolve/adapter.js.map +1 -1
  92. package/dist/core/hosts/resolve/bridge-source.d.ts.map +1 -1
  93. package/dist/core/hosts/resolve/bridge-source.js +31 -4
  94. package/dist/core/hosts/resolve/bridge-source.js.map +1 -1
  95. package/dist/core/hosts/resolve/bridge.d.ts +2 -19
  96. package/dist/core/hosts/resolve/bridge.d.ts.map +1 -1
  97. package/dist/core/hosts/resolve/bridge.js +70 -41
  98. package/dist/core/hosts/resolve/bridge.js.map +1 -1
  99. package/dist/core/hosts/resolve/bridge.test.js +130 -0
  100. package/dist/core/hosts/resolve/bridge.test.js.map +1 -1
  101. package/dist/core/hosts/types.d.ts +6 -0
  102. package/dist/core/hosts/types.d.ts.map +1 -1
  103. package/dist/core/hosts/types.js.map +1 -1
  104. package/dist/core/logger.d.ts +32 -0
  105. package/dist/core/logger.d.ts.map +1 -0
  106. package/dist/core/logger.js +188 -0
  107. package/dist/core/logger.js.map +1 -0
  108. package/dist/core/loop-match.d.ts +57 -0
  109. package/dist/core/loop-match.d.ts.map +1 -0
  110. package/dist/core/loop-match.js +91 -0
  111. package/dist/core/loop-match.js.map +1 -0
  112. package/dist/core/media/ffmpeg.d.ts.map +1 -1
  113. package/dist/core/media/ffmpeg.js +14 -3
  114. package/dist/core/media/ffmpeg.js.map +1 -1
  115. package/dist/core/multi-format.d.ts +67 -0
  116. package/dist/core/multi-format.d.ts.map +1 -0
  117. package/dist/core/multi-format.js +127 -0
  118. package/dist/core/multi-format.js.map +1 -0
  119. package/dist/core/multi-format.test.d.ts +2 -0
  120. package/dist/core/multi-format.test.d.ts.map +1 -0
  121. package/dist/core/multi-format.test.js +151 -0
  122. package/dist/core/multi-format.test.js.map +1 -0
  123. package/dist/core/python/beats.py +61 -0
  124. package/dist/core/python/face_reframe.py +163 -0
  125. package/dist/core/python/sidecar-path.d.ts +13 -0
  126. package/dist/core/python/sidecar-path.d.ts.map +1 -0
  127. package/dist/core/python/sidecar-path.js +24 -0
  128. package/dist/core/python/sidecar-path.js.map +1 -0
  129. package/dist/core/python.d.ts +57 -0
  130. package/dist/core/python.d.ts.map +1 -0
  131. package/dist/core/python.js +107 -0
  132. package/dist/core/python.js.map +1 -0
  133. package/dist/core/python.test.d.ts +2 -0
  134. package/dist/core/python.test.d.ts.map +1 -0
  135. package/dist/core/python.test.js +129 -0
  136. package/dist/core/python.test.js.map +1 -0
  137. package/dist/core/retention-structure.d.ts +81 -0
  138. package/dist/core/retention-structure.d.ts.map +1 -0
  139. package/dist/core/retention-structure.js +206 -0
  140. package/dist/core/retention-structure.js.map +1 -0
  141. package/dist/core/retention-structure.test.d.ts +2 -0
  142. package/dist/core/retention-structure.test.d.ts.map +1 -0
  143. package/dist/core/retention-structure.test.js +88 -0
  144. package/dist/core/retention-structure.test.js.map +1 -0
  145. package/dist/core/review.d.ts +17 -0
  146. package/dist/core/review.d.ts.map +1 -1
  147. package/dist/core/review.js +20 -24
  148. package/dist/core/review.js.map +1 -1
  149. package/dist/core/safe-paths.d.ts +11 -0
  150. package/dist/core/safe-paths.d.ts.map +1 -1
  151. package/dist/core/safe-paths.js +26 -10
  152. package/dist/core/safe-paths.js.map +1 -1
  153. package/dist/core/safe-paths.test.js +16 -0
  154. package/dist/core/safe-paths.test.js.map +1 -1
  155. package/dist/core/skills-loader.d.ts +48 -2
  156. package/dist/core/skills-loader.d.ts.map +1 -1
  157. package/dist/core/skills-loader.js +97 -19
  158. package/dist/core/skills-loader.js.map +1 -1
  159. package/dist/core/skills-loader.test.js +63 -1
  160. package/dist/core/skills-loader.test.js.map +1 -1
  161. package/dist/core/srt.d.ts +42 -7
  162. package/dist/core/srt.d.ts.map +1 -1
  163. package/dist/core/srt.js +101 -32
  164. package/dist/core/srt.js.map +1 -1
  165. package/dist/core/srt.test.js +54 -1
  166. package/dist/core/srt.test.js.map +1 -1
  167. package/dist/core/thumbnail-compose.d.ts +58 -0
  168. package/dist/core/thumbnail-compose.d.ts.map +1 -0
  169. package/dist/core/thumbnail-compose.js +101 -0
  170. package/dist/core/thumbnail-compose.js.map +1 -0
  171. package/dist/core/thumbnail-promise.d.ts +46 -0
  172. package/dist/core/thumbnail-promise.d.ts.map +1 -0
  173. package/dist/core/thumbnail-promise.js +133 -0
  174. package/dist/core/thumbnail-promise.js.map +1 -0
  175. package/dist/core/thumbnail-promise.test.d.ts +2 -0
  176. package/dist/core/thumbnail-promise.test.d.ts.map +1 -0
  177. package/dist/core/thumbnail-promise.test.js +52 -0
  178. package/dist/core/thumbnail-promise.test.js.map +1 -0
  179. package/dist/core/viral-moments.d.ts +70 -0
  180. package/dist/core/viral-moments.d.ts.map +1 -0
  181. package/dist/core/viral-moments.js +192 -0
  182. package/dist/core/viral-moments.js.map +1 -0
  183. package/dist/core/viral-moments.test.d.ts +2 -0
  184. package/dist/core/viral-moments.test.d.ts.map +1 -0
  185. package/dist/core/viral-moments.test.js +153 -0
  186. package/dist/core/viral-moments.test.js.map +1 -0
  187. package/dist/core/whisper.d.ts +16 -0
  188. package/dist/core/whisper.d.ts.map +1 -1
  189. package/dist/core/whisper.js +72 -5
  190. package/dist/core/whisper.js.map +1 -1
  191. package/dist/core/whisper.test.js +111 -1
  192. package/dist/core/whisper.test.js.map +1 -1
  193. package/dist/core/youtube-metadata.d.ts +44 -0
  194. package/dist/core/youtube-metadata.d.ts.map +1 -0
  195. package/dist/core/youtube-metadata.js +168 -0
  196. package/dist/core/youtube-metadata.js.map +1 -0
  197. package/dist/core/youtube-metadata.test.d.ts +2 -0
  198. package/dist/core/youtube-metadata.test.d.ts.map +1 -0
  199. package/dist/core/youtube-metadata.test.js +132 -0
  200. package/dist/core/youtube-metadata.test.js.map +1 -0
  201. package/dist/prompt-commands.d.ts +24 -0
  202. package/dist/prompt-commands.d.ts.map +1 -0
  203. package/dist/prompt-commands.js +243 -0
  204. package/dist/prompt-commands.js.map +1 -0
  205. package/dist/prompt-commands.test.d.ts +2 -0
  206. package/dist/prompt-commands.test.d.ts.map +1 -0
  207. package/dist/prompt-commands.test.js +46 -0
  208. package/dist/prompt-commands.test.js.map +1 -0
  209. package/dist/skills.d.ts +6 -6
  210. package/dist/skills.d.ts.map +1 -1
  211. package/dist/skills.js +1426 -445
  212. package/dist/skills.js.map +1 -1
  213. package/dist/system-prompt.d.ts.map +1 -1
  214. package/dist/system-prompt.js +108 -0
  215. package/dist/system-prompt.js.map +1 -1
  216. package/dist/tools/add-fades.d.ts.map +1 -1
  217. package/dist/tools/add-fades.js +2 -1
  218. package/dist/tools/add-fades.js.map +1 -1
  219. package/dist/tools/add-sfx-at-cuts.d.ts.map +1 -1
  220. package/dist/tools/add-sfx-at-cuts.js +36 -11
  221. package/dist/tools/add-sfx-at-cuts.js.map +1 -1
  222. package/dist/tools/add-sfx-to-timeline.d.ts +34 -0
  223. package/dist/tools/add-sfx-to-timeline.d.ts.map +1 -0
  224. package/dist/tools/add-sfx-to-timeline.js +169 -0
  225. package/dist/tools/add-sfx-to-timeline.js.map +1 -0
  226. package/dist/tools/add-sfx-to-timeline.test.d.ts +2 -0
  227. package/dist/tools/add-sfx-to-timeline.test.d.ts.map +1 -0
  228. package/dist/tools/add-sfx-to-timeline.test.js +181 -0
  229. package/dist/tools/add-sfx-to-timeline.test.js.map +1 -0
  230. package/dist/tools/audit-first-frame.d.ts +36 -0
  231. package/dist/tools/audit-first-frame.d.ts.map +1 -0
  232. package/dist/tools/audit-first-frame.js +181 -0
  233. package/dist/tools/audit-first-frame.js.map +1 -0
  234. package/dist/tools/audit-retention-structure.d.ts +20 -0
  235. package/dist/tools/audit-retention-structure.d.ts.map +1 -0
  236. package/dist/tools/audit-retention-structure.js +95 -0
  237. package/dist/tools/audit-retention-structure.js.map +1 -0
  238. package/dist/tools/audit-retention-structure.test.d.ts +2 -0
  239. package/dist/tools/audit-retention-structure.test.d.ts.map +1 -0
  240. package/dist/tools/audit-retention-structure.test.js +93 -0
  241. package/dist/tools/audit-retention-structure.test.js.map +1 -0
  242. package/dist/tools/bleep-words.d.ts +59 -0
  243. package/dist/tools/bleep-words.d.ts.map +1 -0
  244. package/dist/tools/bleep-words.js +211 -0
  245. package/dist/tools/bleep-words.js.map +1 -0
  246. package/dist/tools/bleep-words.test.d.ts +2 -0
  247. package/dist/tools/bleep-words.test.d.ts.map +1 -0
  248. package/dist/tools/bleep-words.test.js +96 -0
  249. package/dist/tools/bleep-words.test.js.map +1 -0
  250. package/dist/tools/burn-subtitles.d.ts.map +1 -1
  251. package/dist/tools/burn-subtitles.js +10 -5
  252. package/dist/tools/burn-subtitles.js.map +1 -1
  253. package/dist/tools/clean-audio.d.ts.map +1 -1
  254. package/dist/tools/clean-audio.js +2 -1
  255. package/dist/tools/clean-audio.js.map +1 -1
  256. package/dist/tools/cluster-takes.js +2 -1
  257. package/dist/tools/cluster-takes.js.map +1 -1
  258. package/dist/tools/compose-thumbnail-variants.d.ts +70 -0
  259. package/dist/tools/compose-thumbnail-variants.d.ts.map +1 -0
  260. package/dist/tools/compose-thumbnail-variants.js +274 -0
  261. package/dist/tools/compose-thumbnail-variants.js.map +1 -0
  262. package/dist/tools/compose-thumbnail.d.ts +6 -13
  263. package/dist/tools/compose-thumbnail.d.ts.map +1 -1
  264. package/dist/tools/compose-thumbnail.js +44 -81
  265. package/dist/tools/compose-thumbnail.js.map +1 -1
  266. package/dist/tools/concat-videos.d.ts.map +1 -1
  267. package/dist/tools/concat-videos.js +12 -5
  268. package/dist/tools/concat-videos.js.map +1 -1
  269. package/dist/tools/concat-videos.test.d.ts +2 -0
  270. package/dist/tools/concat-videos.test.d.ts.map +1 -0
  271. package/dist/tools/concat-videos.test.js +103 -0
  272. package/dist/tools/concat-videos.test.js.map +1 -0
  273. package/dist/tools/crossfade-videos.d.ts.map +1 -1
  274. package/dist/tools/crossfade-videos.js +2 -1
  275. package/dist/tools/crossfade-videos.js.map +1 -1
  276. package/dist/tools/cut-filler-words.d.ts.map +1 -1
  277. package/dist/tools/cut-filler-words.js +24 -8
  278. package/dist/tools/cut-filler-words.js.map +1 -1
  279. package/dist/tools/detect-speaker-changes.js +2 -1
  280. package/dist/tools/detect-speaker-changes.js.map +1 -1
  281. package/dist/tools/extract-audio.d.ts.map +1 -1
  282. package/dist/tools/extract-audio.js +13 -7
  283. package/dist/tools/extract-audio.js.map +1 -1
  284. package/dist/tools/face-reframe.d.ts +30 -0
  285. package/dist/tools/face-reframe.d.ts.map +1 -0
  286. package/dist/tools/face-reframe.js +143 -0
  287. package/dist/tools/face-reframe.js.map +1 -0
  288. package/dist/tools/face-reframe.test.d.ts +2 -0
  289. package/dist/tools/face-reframe.test.d.ts.map +1 -0
  290. package/dist/tools/face-reframe.test.js +139 -0
  291. package/dist/tools/face-reframe.test.js.map +1 -0
  292. package/dist/tools/find-viral-moments.d.ts +23 -0
  293. package/dist/tools/find-viral-moments.d.ts.map +1 -0
  294. package/dist/tools/find-viral-moments.js +176 -0
  295. package/dist/tools/find-viral-moments.js.map +1 -0
  296. package/dist/tools/find-viral-moments.test.d.ts +2 -0
  297. package/dist/tools/find-viral-moments.test.d.ts.map +1 -0
  298. package/dist/tools/find-viral-moments.test.js +144 -0
  299. package/dist/tools/find-viral-moments.test.js.map +1 -0
  300. package/dist/tools/generate-gif.d.ts.map +1 -1
  301. package/dist/tools/generate-gif.js +47 -40
  302. package/dist/tools/generate-gif.js.map +1 -1
  303. package/dist/tools/generate-gif.test.d.ts +2 -0
  304. package/dist/tools/generate-gif.test.d.ts.map +1 -0
  305. package/dist/tools/generate-gif.test.js +115 -0
  306. package/dist/tools/generate-gif.test.js.map +1 -0
  307. package/dist/tools/generate-outro.d.ts +18 -0
  308. package/dist/tools/generate-outro.d.ts.map +1 -0
  309. package/dist/tools/generate-outro.js +175 -0
  310. package/dist/tools/generate-outro.js.map +1 -0
  311. package/dist/tools/generate-youtube-metadata.d.ts +23 -0
  312. package/dist/tools/generate-youtube-metadata.d.ts.map +1 -0
  313. package/dist/tools/generate-youtube-metadata.js +103 -0
  314. package/dist/tools/generate-youtube-metadata.js.map +1 -0
  315. package/dist/tools/generate-youtube-metadata.test.d.ts +2 -0
  316. package/dist/tools/generate-youtube-metadata.test.d.ts.map +1 -0
  317. package/dist/tools/generate-youtube-metadata.test.js +118 -0
  318. package/dist/tools/generate-youtube-metadata.test.js.map +1 -0
  319. package/dist/tools/index.d.ts +14 -0
  320. package/dist/tools/index.d.ts.map +1 -1
  321. package/dist/tools/index.js +130 -1
  322. package/dist/tools/index.js.map +1 -1
  323. package/dist/tools/index.test.js +27 -1
  324. package/dist/tools/index.test.js.map +1 -1
  325. package/dist/tools/ken-burns.d.ts.map +1 -1
  326. package/dist/tools/ken-burns.js +2 -1
  327. package/dist/tools/ken-burns.js.map +1 -1
  328. package/dist/tools/loop-match-short.d.ts +22 -0
  329. package/dist/tools/loop-match-short.d.ts.map +1 -0
  330. package/dist/tools/loop-match-short.js +107 -0
  331. package/dist/tools/loop-match-short.js.map +1 -0
  332. package/dist/tools/mix-audio.d.ts.map +1 -1
  333. package/dist/tools/mix-audio.js +2 -1
  334. package/dist/tools/mix-audio.js.map +1 -1
  335. package/dist/tools/normalize-loudness.d.ts.map +1 -1
  336. package/dist/tools/normalize-loudness.js +2 -1
  337. package/dist/tools/normalize-loudness.js.map +1 -1
  338. package/dist/tools/path-traversal.test.d.ts +15 -0
  339. package/dist/tools/path-traversal.test.d.ts.map +1 -0
  340. package/dist/tools/path-traversal.test.js +223 -0
  341. package/dist/tools/path-traversal.test.js.map +1 -0
  342. package/dist/tools/pick-best-takes.js +2 -1
  343. package/dist/tools/pick-best-takes.js.map +1 -1
  344. package/dist/tools/punch-in.d.ts.map +1 -1
  345. package/dist/tools/punch-in.js +2 -1
  346. package/dist/tools/punch-in.js.map +1 -1
  347. package/dist/tools/read-transcript.js +2 -1
  348. package/dist/tools/read-transcript.js.map +1 -1
  349. package/dist/tools/render-multi-format.d.ts +35 -0
  350. package/dist/tools/render-multi-format.d.ts.map +1 -0
  351. package/dist/tools/render-multi-format.js +206 -0
  352. package/dist/tools/render-multi-format.js.map +1 -0
  353. package/dist/tools/render-multi-format.test.d.ts +2 -0
  354. package/dist/tools/render-multi-format.test.d.ts.map +1 -0
  355. package/dist/tools/render-multi-format.test.js +312 -0
  356. package/dist/tools/render-multi-format.test.js.map +1 -0
  357. package/dist/tools/render.d.ts.map +1 -1
  358. package/dist/tools/render.js +2 -2
  359. package/dist/tools/render.js.map +1 -1
  360. package/dist/tools/rewrite-hook.d.ts +32 -0
  361. package/dist/tools/rewrite-hook.d.ts.map +1 -0
  362. package/dist/tools/rewrite-hook.js +65 -0
  363. package/dist/tools/rewrite-hook.js.map +1 -0
  364. package/dist/tools/score-clip.d.ts +30 -0
  365. package/dist/tools/score-clip.d.ts.map +1 -0
  366. package/dist/tools/score-clip.js +109 -0
  367. package/dist/tools/score-clip.js.map +1 -0
  368. package/dist/tools/score-clip.test.d.ts +2 -0
  369. package/dist/tools/score-clip.test.d.ts.map +1 -0
  370. package/dist/tools/score-clip.test.js +110 -0
  371. package/dist/tools/score-clip.test.js.map +1 -0
  372. package/dist/tools/search-tools.d.ts +34 -0
  373. package/dist/tools/search-tools.d.ts.map +1 -0
  374. package/dist/tools/search-tools.js +86 -0
  375. package/dist/tools/search-tools.js.map +1 -0
  376. package/dist/tools/search-tools.test.d.ts +2 -0
  377. package/dist/tools/search-tools.test.d.ts.map +1 -0
  378. package/dist/tools/search-tools.test.js +60 -0
  379. package/dist/tools/search-tools.test.js.map +1 -0
  380. package/dist/tools/snap-cuts-to-beats.d.ts +18 -0
  381. package/dist/tools/snap-cuts-to-beats.d.ts.map +1 -0
  382. package/dist/tools/snap-cuts-to-beats.js +110 -0
  383. package/dist/tools/snap-cuts-to-beats.js.map +1 -0
  384. package/dist/tools/snap-cuts-to-beats.test.d.ts +2 -0
  385. package/dist/tools/snap-cuts-to-beats.test.d.ts.map +1 -0
  386. package/dist/tools/snap-cuts-to-beats.test.js +99 -0
  387. package/dist/tools/snap-cuts-to-beats.test.js.map +1 -0
  388. package/dist/tools/speed-ramp.d.ts.map +1 -1
  389. package/dist/tools/speed-ramp.js +2 -1
  390. package/dist/tools/speed-ramp.js.map +1 -1
  391. package/dist/tools/stabilize-video.d.ts.map +1 -1
  392. package/dist/tools/stabilize-video.js +2 -1
  393. package/dist/tools/stabilize-video.js.map +1 -1
  394. package/dist/tools/suggest-broll.d.ts +34 -0
  395. package/dist/tools/suggest-broll.d.ts.map +1 -0
  396. package/dist/tools/suggest-broll.js +367 -0
  397. package/dist/tools/suggest-broll.js.map +1 -0
  398. package/dist/tools/suggest-broll.test.d.ts +2 -0
  399. package/dist/tools/suggest-broll.test.d.ts.map +1 -0
  400. package/dist/tools/suggest-broll.test.js +217 -0
  401. package/dist/tools/suggest-broll.test.js.map +1 -0
  402. package/dist/tools/text-based-cut.d.ts +33 -0
  403. package/dist/tools/text-based-cut.d.ts.map +1 -0
  404. package/dist/tools/text-based-cut.js +172 -0
  405. package/dist/tools/text-based-cut.js.map +1 -0
  406. package/dist/tools/text-based-cut.test.d.ts +2 -0
  407. package/dist/tools/text-based-cut.test.d.ts.map +1 -0
  408. package/dist/tools/text-based-cut.test.js +32 -0
  409. package/dist/tools/text-based-cut.test.js.map +1 -0
  410. package/dist/tools/transcribe.d.ts +1 -1
  411. package/dist/tools/transition-videos.d.ts +1 -1
  412. package/dist/tools/transition-videos.d.ts.map +1 -1
  413. package/dist/tools/transition-videos.js +2 -1
  414. package/dist/tools/transition-videos.js.map +1 -1
  415. package/dist/tools/trim-dead-air.d.ts +59 -0
  416. package/dist/tools/trim-dead-air.d.ts.map +1 -0
  417. package/dist/tools/trim-dead-air.js +215 -0
  418. package/dist/tools/trim-dead-air.js.map +1 -0
  419. package/dist/tools/trim-dead-air.test.d.ts +2 -0
  420. package/dist/tools/trim-dead-air.test.d.ts.map +1 -0
  421. package/dist/tools/trim-dead-air.test.js +75 -0
  422. package/dist/tools/trim-dead-air.test.js.map +1 -0
  423. package/dist/tools/verify-thumbnail-promise.d.ts +33 -0
  424. package/dist/tools/verify-thumbnail-promise.d.ts.map +1 -0
  425. package/dist/tools/verify-thumbnail-promise.js +112 -0
  426. package/dist/tools/verify-thumbnail-promise.js.map +1 -0
  427. package/dist/tools/verify-thumbnail-promise.test.d.ts +2 -0
  428. package/dist/tools/verify-thumbnail-promise.test.d.ts.map +1 -0
  429. package/dist/tools/verify-thumbnail-promise.test.js +38 -0
  430. package/dist/tools/verify-thumbnail-promise.test.js.map +1 -0
  431. package/dist/tools/write-keyword-captions.d.ts +7 -0
  432. package/dist/tools/write-keyword-captions.d.ts.map +1 -1
  433. package/dist/tools/write-keyword-captions.js +35 -4
  434. package/dist/tools/write-keyword-captions.js.map +1 -1
  435. package/dist/ui/App.d.ts.map +1 -1
  436. package/dist/ui/App.js +75 -11
  437. package/dist/ui/App.js.map +1 -1
  438. package/dist/ui/tool-formatters.d.ts +30 -0
  439. package/dist/ui/tool-formatters.d.ts.map +1 -0
  440. package/dist/ui/tool-formatters.js +461 -0
  441. package/dist/ui/tool-formatters.js.map +1 -0
  442. package/dist/ui/tool-formatters.test.d.ts +2 -0
  443. package/dist/ui/tool-formatters.test.d.ts.map +1 -0
  444. package/dist/ui/tool-formatters.test.js +143 -0
  445. package/dist/ui/tool-formatters.test.js.map +1 -0
  446. package/package.json +10 -9
@@ -0,0 +1,70 @@
1
+ import { z } from "zod";
2
+ import type { AgentTool } from "@kenkaiiii/gg-agent";
3
+ import { type ExtractedFrame } from "../core/frames.js";
4
+ import { type ShotScore } from "../core/vision.js";
5
+ declare const ComposeThumbnailVariantsParams: z.ZodObject<{
6
+ input: z.ZodString;
7
+ outputDir: z.ZodString;
8
+ text: z.ZodString;
9
+ count: z.ZodOptional<z.ZodNumber>;
10
+ intervalSec: z.ZodOptional<z.ZodNumber>;
11
+ minSpacingSec: z.ZodOptional<z.ZodNumber>;
12
+ strategy: z.ZodOptional<z.ZodEnum<{
13
+ label: "label";
14
+ expression: "expression";
15
+ mixed: "mixed";
16
+ }>>;
17
+ model: z.ZodOptional<z.ZodString>;
18
+ detail: z.ZodOptional<z.ZodEnum<{
19
+ low: "low";
20
+ high: "high";
21
+ }>>;
22
+ fontFile: z.ZodOptional<z.ZodString>;
23
+ fontSize: z.ZodOptional<z.ZodNumber>;
24
+ fontColor: z.ZodOptional<z.ZodString>;
25
+ outlineColor: z.ZodOptional<z.ZodString>;
26
+ position: z.ZodOptional<z.ZodEnum<{
27
+ top: "top";
28
+ center: "center";
29
+ bottom: "bottom";
30
+ }>>;
31
+ width: z.ZodOptional<z.ZodNumber>;
32
+ }, z.core.$strip>;
33
+ interface VariantPick {
34
+ frame: ExtractedFrame;
35
+ score: ShotScore;
36
+ }
37
+ /**
38
+ * compose_thumbnail_variants — generate N thumbnails for A/B testing.
39
+ *
40
+ * Samples frames at `intervalSec`, scores via vision rubric, picks the
41
+ * top-N spaced ≥ minSpacingSec apart, burns the headline. YouTube
42
+ * native A/B is now standard — variants beat single guesses.
43
+ *
44
+ * Strategies:
45
+ * - "mixed" (default): 3 distinct frames, same label.
46
+ * - "expression": same as mixed — picked separately for face-driven
47
+ * content where the agent wants to emphasise expression variation.
48
+ * - "label": ONE best frame, 3 LLM-generated label variations.
49
+ * Use when the source only has one usable visual.
50
+ */
51
+ export declare function createComposeThumbnailVariantsTool(cwd: string): AgentTool<typeof ComposeThumbnailVariantsParams>;
52
+ /**
53
+ * Greedy top-N selection with minimum spacing. Sort by score desc, walk
54
+ * the list, accept a candidate only if it's ≥ minSpacingSec away from
55
+ * every already-accepted pick. Stable — ties preserve input order.
56
+ *
57
+ * Pure — exported for testing.
58
+ */
59
+ export declare function pickTopSpaced(paired: VariantPick[], count: number, minSpacingSec: number): VariantPick[];
60
+ /**
61
+ * Ask the LLM for N label variations. Pure-ish (network call) — exported
62
+ * for testing under a mocked fetch.
63
+ */
64
+ export declare function generateLabelVariations(headline: string, n: number, opts?: {
65
+ apiKey?: string;
66
+ model?: string;
67
+ signal?: AbortSignal;
68
+ }): Promise<string[]>;
69
+ export {};
70
+ //# sourceMappingURL=compose-thumbnail-variants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose-thumbnail-variants.d.ts","sourceRoot":"","sources":["../../src/tools/compose-thumbnail-variants.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAIrD,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAG3E,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAGhE,QAAA,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgClC,CAAC;AAEH,UAAU,WAAW;IACnB,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,SAAS,CAAC;CAClB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kCAAkC,CAChD,GAAG,EAAE,MAAM,GACV,SAAS,CAAC,OAAO,8BAA8B,CAAC,CA6JlD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,WAAW,EAAE,EACrB,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,GACpB,WAAW,EAAE,CAaf;AASD;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,CAAC,EAAE,MAAM,EACT,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,GACnE,OAAO,CAAC,MAAM,EAAE,CAAC,CAyCnB"}
@@ -0,0 +1,274 @@
1
+ import { resolve as resolvePath, join } from "node:path";
2
+ import { z } from "zod";
3
+ import { resolveApiKey } from "../core/auth/api-keys.js";
4
+ import { loadBrandKit } from "../core/brand-kit.js";
5
+ import { compact, err } from "../core/format.js";
6
+ import { extractAtInterval } from "../core/frames.js";
7
+ import { checkFfmpeg, probeMedia } from "../core/media/ffmpeg.js";
8
+ import { composeThumbnailFrame } from "../core/thumbnail-compose.js";
9
+ import { scoreFrames } from "../core/vision.js";
10
+ import { pickBrandFont } from "./compose-thumbnail.js";
11
+ const ComposeThumbnailVariantsParams = z.object({
12
+ input: z.string().describe("Source video (relative resolves to cwd)."),
13
+ outputDir: z.string().describe("Directory for the variant images. Created if missing."),
14
+ text: z.string().min(1).describe("Headline burned on each variant."),
15
+ count: z.number().int().min(1).max(5).optional().describe("How many variants. Default 3."),
16
+ intervalSec: z
17
+ .number()
18
+ .positive()
19
+ .optional()
20
+ .describe("Sampling interval. Default = totalSec/40 (≈40 candidates over the video)."),
21
+ minSpacingSec: z
22
+ .number()
23
+ .min(0)
24
+ .optional()
25
+ .describe("Minimum seconds between picked variants. Default 10."),
26
+ strategy: z
27
+ .enum(["expression", "label", "mixed"])
28
+ .optional()
29
+ .describe("Variant generation strategy. 'mixed' (default) = 3 distinct frames + same label. " +
30
+ "'expression' = 3 best frames, all same label (vary expression only — face-driven " +
31
+ "content). 'label' = ONE best frame + 3 LLM-generated label variations (use when the " +
32
+ "source has only one usable face / product / screen)."),
33
+ model: z.string().optional().describe("OpenAI model. Default gpt-4o-mini."),
34
+ detail: z.enum(["low", "high"]).optional().describe("Vision detail. Default low."),
35
+ fontFile: z.string().optional(),
36
+ fontSize: z.number().int().positive().optional(),
37
+ fontColor: z.string().optional(),
38
+ outlineColor: z.string().optional(),
39
+ position: z.enum(["top", "center", "bottom"]).optional(),
40
+ width: z.number().int().positive().optional(),
41
+ });
42
+ /**
43
+ * compose_thumbnail_variants — generate N thumbnails for A/B testing.
44
+ *
45
+ * Samples frames at `intervalSec`, scores via vision rubric, picks the
46
+ * top-N spaced ≥ minSpacingSec apart, burns the headline. YouTube
47
+ * native A/B is now standard — variants beat single guesses.
48
+ *
49
+ * Strategies:
50
+ * - "mixed" (default): 3 distinct frames, same label.
51
+ * - "expression": same as mixed — picked separately for face-driven
52
+ * content where the agent wants to emphasise expression variation.
53
+ * - "label": ONE best frame, 3 LLM-generated label variations.
54
+ * Use when the source only has one usable visual.
55
+ */
56
+ export function createComposeThumbnailVariantsTool(cwd) {
57
+ return {
58
+ name: "compose_thumbnail_variants",
59
+ description: "Generate N thumbnail variants for A/B testing. Samples frames across the video, scores " +
60
+ "each via vision LLM, picks top-N distinct frames (≥minSpacingSec apart), burns the " +
61
+ "headline. `strategy` controls what varies: 'expression' (default for face-driven " +
62
+ "content) picks 3 frames with the SAME label; 'label' picks ONE frame + generates 3 " +
63
+ "distinct labels via LLM (use when only one usable face / product / screen exists); " +
64
+ "'mixed' = current behaviour (3 frames + same label). Output ordered best-first. " +
65
+ "Auto-applies brand kit (fonts.heading, colors.primary). YouTube native A/B is now " +
66
+ "standard — ship variants, not a single guess. Requires OPENAI_API_KEY + ffmpeg. Pair " +
67
+ "with `generate_youtube_metadata` before publish.",
68
+ parameters: ComposeThumbnailVariantsParams,
69
+ async execute(args, ctx) {
70
+ try {
71
+ if (!checkFfmpeg())
72
+ return err("ffmpeg not on PATH", "install ffmpeg");
73
+ if (!process.env.OPENAI_API_KEY) {
74
+ return err("OPENAI_API_KEY not set", "export OPENAI_API_KEY=...");
75
+ }
76
+ const inAbs = resolvePath(cwd, args.input);
77
+ const outDir = resolvePath(cwd, args.outputDir);
78
+ const probe = probeMedia(inAbs);
79
+ if (!probe)
80
+ return err(`probe failed for ${inAbs}`, "verify file exists and is media");
81
+ if (probe.durationSec <= 0)
82
+ return err("source has zero duration");
83
+ const count = Math.min(5, Math.max(1, args.count ?? 3));
84
+ const minSpacing = Math.max(0, args.minSpacingSec ?? 10);
85
+ const interval = args.intervalSec && args.intervalSec > 0
86
+ ? args.intervalSec
87
+ : Math.max(1, probe.durationSec / 40);
88
+ const strategy = args.strategy ?? "mixed";
89
+ // Brand-kit fallback: only fill in fontFile / outlineColor when the
90
+ // caller didn't pass an explicit value.
91
+ const brand = loadBrandKit(cwd);
92
+ const brandFont = pickBrandFont(cwd, brand?.fonts?.heading);
93
+ const fontFile = args.fontFile ?? brandFont;
94
+ const outlineColor = args.outlineColor ?? brand?.colors?.primary;
95
+ const brandKitLoaded = brand !== null;
96
+ // Sample frames across the whole video.
97
+ const frames = await extractAtInterval(inAbs, interval, probe.durationSec, {
98
+ maxWidth: 1280,
99
+ signal: ctx.signal,
100
+ });
101
+ if (frames.length === 0)
102
+ return err("no frames extracted", "lower intervalSec");
103
+ // Score each frame.
104
+ const scores = await scoreFrames(frames, {
105
+ model: args.model,
106
+ detail: args.detail,
107
+ signal: ctx.signal,
108
+ });
109
+ // Pair scores with their source frames + pick top-N respecting
110
+ // minSpacingSec.
111
+ const paired = scores.map((s, i) => ({ frame: frames[i], score: s }));
112
+ // ── label strategy: 1 frame, 3 LLM-generated labels ────────────
113
+ if (strategy === "label") {
114
+ const top = paired.sort((a, b) => b.score.score - a.score.score)[0];
115
+ if (!top)
116
+ return err("no variants survived spacing constraint", "lower minSpacingSec");
117
+ const labels = await generateLabelVariations(args.text, count, {
118
+ model: args.model,
119
+ signal: ctx.signal,
120
+ });
121
+ const variants = [];
122
+ for (let i = 0; i < labels.length; i++) {
123
+ const outPath = join(outDir, `variant-${i + 1}.jpg`);
124
+ await composeThumbnailFrame({
125
+ input: inAbs,
126
+ output: outPath,
127
+ atSec: top.frame.atSec,
128
+ text: labels[i],
129
+ fontFile,
130
+ fontSize: args.fontSize,
131
+ fontColor: args.fontColor,
132
+ outlineColor,
133
+ position: args.position,
134
+ width: args.width,
135
+ signal: ctx.signal,
136
+ }, cwd);
137
+ variants.push({
138
+ path: outPath,
139
+ atSec: +top.frame.atSec.toFixed(2),
140
+ score: top.score.score,
141
+ why: top.score.why,
142
+ label: labels[i],
143
+ });
144
+ }
145
+ return compact({
146
+ variants,
147
+ count: variants.length,
148
+ totalScored: paired.length,
149
+ strategy,
150
+ brandKitLoaded,
151
+ });
152
+ }
153
+ // ── mixed / expression strategies: N frames, same label ────────
154
+ const picks = pickTopSpaced(paired, count, minSpacing);
155
+ if (picks.length === 0) {
156
+ return err("no variants survived spacing constraint", "lower minSpacingSec");
157
+ }
158
+ // Burn the headline on each pick.
159
+ const variants = [];
160
+ for (let i = 0; i < picks.length; i++) {
161
+ const p = picks[i];
162
+ const outPath = join(outDir, `variant-${i + 1}.jpg`);
163
+ await composeThumbnailFrame({
164
+ input: inAbs,
165
+ output: outPath,
166
+ atSec: p.frame.atSec,
167
+ text: args.text,
168
+ fontFile,
169
+ fontSize: args.fontSize,
170
+ fontColor: args.fontColor,
171
+ outlineColor,
172
+ position: args.position,
173
+ width: args.width,
174
+ signal: ctx.signal,
175
+ }, cwd);
176
+ variants.push({
177
+ path: outPath,
178
+ atSec: +p.frame.atSec.toFixed(2),
179
+ score: p.score.score,
180
+ why: p.score.why,
181
+ });
182
+ }
183
+ return compact({
184
+ variants,
185
+ count: variants.length,
186
+ totalScored: paired.length,
187
+ strategy,
188
+ brandKitLoaded,
189
+ });
190
+ }
191
+ catch (e) {
192
+ return err(e.message);
193
+ }
194
+ },
195
+ };
196
+ }
197
+ /**
198
+ * Greedy top-N selection with minimum spacing. Sort by score desc, walk
199
+ * the list, accept a candidate only if it's ≥ minSpacingSec away from
200
+ * every already-accepted pick. Stable — ties preserve input order.
201
+ *
202
+ * Pure — exported for testing.
203
+ */
204
+ export function pickTopSpaced(paired, count, minSpacingSec) {
205
+ const sorted = [...paired].sort((a, b) => {
206
+ if (b.score.score !== a.score.score)
207
+ return b.score.score - a.score.score;
208
+ return a.frame.atSec - b.frame.atSec;
209
+ });
210
+ const kept = [];
211
+ for (const p of sorted) {
212
+ if (kept.length >= count)
213
+ break;
214
+ const tooClose = kept.some((k) => Math.abs(k.frame.atSec - p.frame.atSec) < minSpacingSec);
215
+ if (tooClose)
216
+ continue;
217
+ kept.push(p);
218
+ }
219
+ return kept;
220
+ }
221
+ const LABEL_SYSTEM = `You distill a YouTube headline into thumbnail labels. Given an input headline, output exactly N distinct 2-4-word labels. Each label should attack the same headline from a DIFFERENT angle (e.g. one curiosity-driven, one outcome-driven, one number-driven). Labels render large on a 100x56 mobile-feed thumbnail — no punctuation, no quotes, ALL CAPS preferred when it fits.
222
+
223
+ Output JSON with this EXACT shape:
224
+ { "labels": ["...", "...", "..."] }
225
+
226
+ No prose, no extra keys.`;
227
+ /**
228
+ * Ask the LLM for N label variations. Pure-ish (network call) — exported
229
+ * for testing under a mocked fetch.
230
+ */
231
+ export async function generateLabelVariations(headline, n, opts = {}) {
232
+ const apiKey = opts.apiKey ?? resolveApiKey("OPENAI_API_KEY", "openai");
233
+ if (!apiKey)
234
+ throw new Error("OPENAI_API_KEY required for compose_thumbnail_variants.");
235
+ const model = opts.model ?? "gpt-4o-mini";
236
+ const body = {
237
+ model,
238
+ messages: [
239
+ { role: "system", content: LABEL_SYSTEM },
240
+ { role: "user", content: `HEADLINE:\n${headline}\n\nN: ${n}` },
241
+ ],
242
+ response_format: { type: "json_object" },
243
+ temperature: 0,
244
+ };
245
+ const res = await fetch("https://api.openai.com/v1/chat/completions", {
246
+ method: "POST",
247
+ headers: {
248
+ Authorization: `Bearer ${apiKey}`,
249
+ "Content-Type": "application/json",
250
+ },
251
+ body: JSON.stringify(body),
252
+ signal: opts.signal,
253
+ });
254
+ if (!res.ok) {
255
+ const text = await res.text().catch(() => "");
256
+ throw new Error(`OpenAI HTTP ${res.status}: ${text.slice(0, 200)}`);
257
+ }
258
+ const data = (await res.json());
259
+ const content = data.choices[0]?.message?.content;
260
+ if (!content)
261
+ throw new Error("compose_thumbnail_variants: empty model response");
262
+ const parsed = JSON.parse(content);
263
+ const arr = Array.isArray(parsed.labels) ? parsed.labels : [];
264
+ const labels = arr
265
+ .map((v) => String(v ?? "").trim())
266
+ .filter((v) => v.length > 0)
267
+ .slice(0, n);
268
+ // Pad with the headline itself if the model under-delivers, so caller
269
+ // always gets N entries.
270
+ while (labels.length < n)
271
+ labels.push(headline);
272
+ return labels;
273
+ }
274
+ //# sourceMappingURL=compose-thumbnail-variants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose-thumbnail-variants.js","sourceRoot":"","sources":["../../src/tools/compose-thumbnail-variants.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import type { AgentTool } from "@kenkaiiii/gg-agent";
3
+ import { escapeDrawtextValue, pickFont } from "../core/thumbnail-compose.js";
3
4
  declare const ComposeThumbnailParams: z.ZodObject<{
4
5
  input: z.ZodString;
5
6
  output: z.ZodString;
@@ -17,19 +18,11 @@ declare const ComposeThumbnailParams: z.ZodObject<{
17
18
  width: z.ZodOptional<z.ZodNumber>;
18
19
  }, z.core.$strip>;
19
20
  export declare function createComposeThumbnailTool(cwd: string): AgentTool<typeof ComposeThumbnailParams>;
21
+ export { escapeDrawtextValue, pickFont };
20
22
  /**
21
- * Escape a string for ffmpeg drawtext's `text='...'` parameter.
22
- *
23
- * Rules (verified against real-world implementations GVCLab/CutClaw,
24
- * g0ldyy/comet, ehendrix23/tesla_dashcam):
25
- * 1. `\\` first → `\\\\`
26
- * 2. `:` → `\\:`
27
- * 3. `'` → `\\'`
28
- * 4. `%` → `\\%` (because `%{...}` is used for runtime expansions like timecode)
29
- * 5. newlines → literal `\\n` (drawtext renders `\\n` as a line break)
30
- *
31
- * Backslash MUST come first so subsequent escapes don't double-escape it.
23
+ * Resolve a brand-kit `fonts.heading` value to a readable absolute path.
24
+ * Returns undefined when the kit doesn't define one or the file is missing.
25
+ * Pure-ish (touches the filesystem)exported for testing.
32
26
  */
33
- export declare function escapeDrawtextValue(text: string): string;
34
- export {};
27
+ export declare function pickBrandFont(cwd: string, heading: string | undefined): string | undefined;
35
28
  //# sourceMappingURL=compose-thumbnail.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compose-thumbnail.d.ts","sourceRoot":"","sources":["../../src/tools/compose-thumbnail.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAKrD,QAAA,MAAM,sBAAsB;;;;;;;;;;;;;;;iBAyB1B,CAAC;AAUH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC,OAAO,sBAAsB,CAAC,CA0EhG;AAiBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOxD"}
1
+ {"version":3,"file":"compose-thumbnail.d.ts","sourceRoot":"","sources":["../../src/tools/compose-thumbnail.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAKrD,OAAO,EAAyB,mBAAmB,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAEpG,QAAA,MAAM,sBAAsB;;;;;;;;;;;;;;;iBAyB1B,CAAC;AAEH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC,OAAO,sBAAsB,CAAC,CAqDhG;AAGD,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,CAAC;AAEzC;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAQ1F"}
@@ -1,9 +1,11 @@
1
- import { existsSync, mkdirSync } from "node:fs";
2
- import { dirname, resolve as resolvePath } from "node:path";
1
+ import { existsSync } from "node:fs";
2
+ import { resolve as resolvePath } from "node:path";
3
3
  import { z } from "zod";
4
+ import { loadBrandKit } from "../core/brand-kit.js";
4
5
  import { compact, err } from "../core/format.js";
5
- import { checkFfmpeg, runFfmpeg } from "../core/media/ffmpeg.js";
6
+ import { checkFfmpeg } from "../core/media/ffmpeg.js";
6
7
  import { safeResolveOutputPath } from "../core/safe-paths.js";
8
+ import { composeThumbnailFrame, escapeDrawtextValue, pickFont } from "../core/thumbnail-compose.js";
7
9
  const ComposeThumbnailParams = z.object({
8
10
  input: z.string().describe("Source video."),
9
11
  output: z.string().describe("Thumbnail .jpg or .png."),
@@ -28,13 +30,6 @@ const ComposeThumbnailParams = z.object({
28
30
  .optional()
29
31
  .describe("Optional output width (height auto). Common: 1280 for YouTube."),
30
32
  });
31
- const SYSTEM_FONT_GUESSES = [
32
- "/System/Library/Fonts/Helvetica.ttc",
33
- "/System/Library/Fonts/Supplemental/Arial.ttf",
34
- "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
35
- "/usr/share/fonts/TTF/DejaVuSans-Bold.ttf",
36
- "C:\\Windows\\Fonts\\Arial.ttf",
37
- ];
38
33
  export function createComposeThumbnailTool(cwd) {
39
34
  return {
40
35
  name: "compose_thumbnail",
@@ -49,47 +44,33 @@ export function createComposeThumbnailTool(cwd) {
49
44
  try {
50
45
  const inAbs = resolvePath(cwd, args.input);
51
46
  const resolved = safeResolveOutputPath(cwd, args.output);
52
- const outAbs = resolved.path;
53
- const fontFile = args.fontFile ? resolvePath(cwd, args.fontFile) : pickFont();
54
- if (!fontFile) {
55
- return err("no system font found", "pass fontFile=<path-to-.ttf> (try ~/Library/Fonts on macOS or /usr/share/fonts on Linux)");
56
- }
57
- const fontSize = args.fontSize ?? 96;
58
- const fontColor = args.fontColor ?? "white";
59
- const outlineColor = args.outlineColor === "" ? "" : (args.outlineColor ?? "black");
60
- const pos = args.position ?? "bottom";
61
- const yExpr = pos === "top" ? "h*0.08" : pos === "center" ? "(h-text_h)/2" : "h-text_h-h*0.08";
62
- const safeText = escapeDrawtextValue(args.text);
63
- const drawText = `drawtext=fontfile=${escapeFilterPath(fontFile)}:` +
64
- `text='${safeText}':` +
65
- `fontsize=${fontSize}:` +
66
- `fontcolor=${fontColor}:` +
67
- `x=(w-text_w)/2:` +
68
- `y=${yExpr}` +
69
- (outlineColor ? `:bordercolor=${outlineColor}:borderw=4` : "");
70
- const vf = args.width ? `scale=${args.width}:-1,${drawText}` : drawText;
71
- mkdirSync(dirname(outAbs), { recursive: true });
72
- const r = await runFfmpeg([
73
- "-ss",
74
- String(args.atSec),
75
- "-i",
76
- inAbs,
77
- "-frames:v",
78
- "1",
79
- "-vf",
80
- vf,
81
- "-q:v",
82
- "2",
83
- outAbs,
84
- ], { signal: ctx.signal });
85
- if (r.code !== 0) {
86
- return err(`ffmpeg drawtext exited ${r.code}`, "verify atSec is in range and ffmpeg has --enable-libfreetype");
87
- }
47
+ // Brand-kit fallback: when fontFile / outlineColor aren't passed, fall
48
+ // back to the kit so a single tool call can pick up channel defaults.
49
+ const brand = loadBrandKit(cwd);
50
+ const brandFont = pickBrandFont(cwd, brand?.fonts?.heading);
51
+ const brandOutline = brand?.colors?.primary;
52
+ const fontFile = args.fontFile ?? brandFont;
53
+ const outlineColor = args.outlineColor ?? brandOutline;
54
+ const brandKitLoaded = brand !== null;
55
+ const r = await composeThumbnailFrame({
56
+ input: inAbs,
57
+ output: resolved.path,
58
+ atSec: args.atSec,
59
+ text: args.text,
60
+ fontFile,
61
+ fontSize: args.fontSize,
62
+ fontColor: args.fontColor,
63
+ outlineColor,
64
+ position: args.position,
65
+ width: args.width,
66
+ signal: ctx.signal,
67
+ }, cwd);
88
68
  return compact({
89
69
  ok: true,
90
- path: outAbs,
70
+ path: r.path,
91
71
  atSec: args.atSec,
92
- fontFile,
72
+ fontFile: r.fontFile,
73
+ brandKitLoaded,
93
74
  ...(resolved.redirected ? { redirected: true, reason: resolved.reason } : {}),
94
75
  });
95
76
  }
@@ -99,40 +80,22 @@ export function createComposeThumbnailTool(cwd) {
99
80
  },
100
81
  };
101
82
  }
102
- function pickFont() {
103
- for (const p of SYSTEM_FONT_GUESSES) {
104
- try {
105
- if (existsSync(p))
106
- return p;
107
- }
108
- catch {
109
- /* ignore */
110
- }
111
- }
112
- return undefined;
113
- }
114
- function escapeFilterPath(p) {
115
- return p.replace(/\\/g, "\\\\").replace(/:/g, "\\:").replace(/'/g, "\\'");
116
- }
83
+ // Re-exports for backwards compatibility — older tests import these from here.
84
+ export { escapeDrawtextValue, pickFont };
117
85
  /**
118
- * Escape a string for ffmpeg drawtext's `text='...'` parameter.
119
- *
120
- * Rules (verified against real-world implementations GVCLab/CutClaw,
121
- * g0ldyy/comet, ehendrix23/tesla_dashcam):
122
- * 1. `\\` first → `\\\\`
123
- * 2. `:` → `\\:`
124
- * 3. `'` → `\\'`
125
- * 4. `%` → `\\%` (because `%{...}` is used for runtime expansions like timecode)
126
- * 5. newlines → literal `\\n` (drawtext renders `\\n` as a line break)
127
- *
128
- * Backslash MUST come first so subsequent escapes don't double-escape it.
86
+ * Resolve a brand-kit `fonts.heading` value to a readable absolute path.
87
+ * Returns undefined when the kit doesn't define one or the file is missing.
88
+ * Pure-ish (touches the filesystem)exported for testing.
129
89
  */
130
- export function escapeDrawtextValue(text) {
131
- return text
132
- .replace(/\\/g, "\\\\")
133
- .replace(/:/g, "\\:")
134
- .replace(/'/g, "\\'")
135
- .replace(/%/g, "\\%")
136
- .replace(/\r\n?|\n/g, "\\n");
90
+ export function pickBrandFont(cwd, heading) {
91
+ if (!heading)
92
+ return undefined;
93
+ const abs = resolvePath(cwd, heading);
94
+ try {
95
+ return existsSync(abs) ? abs : undefined;
96
+ }
97
+ catch {
98
+ return undefined;
99
+ }
137
100
  }
138
101
  //# sourceMappingURL=compose-thumbnail.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compose-thumbnail.js","sourceRoot":"","sources":["../../src/tools/compose-thumbnail.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compose-thumbnail.js","sourceRoot":"","sources":["../../src/tools/compose-thumbnail.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"concat-videos.d.ts","sourceRoot":"","sources":["../../src/tools/concat-videos.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAE3D;AACD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAKrD,QAAA,MAAM,kBAAkB;;;;;;iBAatB,CAAC;AAEH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC,OAAO,kBAAkB,CAAC,CAuExF"}
1
+ {"version":3,"file":"concat-videos.d.ts","sourceRoot":"","sources":["../../src/tools/concat-videos.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAE3D;AACD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAKrD,QAAA,MAAM,kBAAkB;;;;;;iBAatB,CAAC;AAEH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC,OAAO,kBAAkB,CAAC,CA6ExF"}
@@ -1,4 +1,4 @@
1
- import { mkdirSync, writeFileSync } from "node:fs";
1
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { dirname, resolve as resolvePath } from "node:path";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
@@ -46,14 +46,21 @@ export function createConcatVideosTool(cwd) {
46
46
  mkdirSync(dirname(outAbs), { recursive: true });
47
47
  if (lossless) {
48
48
  // concat demuxer: build a list file `file 'path'\n` and feed it.
49
+ // The list file is a temp scratch artifact — clean it up no matter
50
+ // how this branch exits (success, ffmpeg non-zero, or thrown error).
49
51
  const listPath = join(tmpdir(), `gg-concat-${Date.now()}.txt`);
50
52
  const body = buildConcatListBody(absInputs);
51
53
  writeFileSync(listPath, body, "utf8");
52
- const r = await runFfmpeg(["-f", "concat", "-safe", "0", "-i", listPath, "-c", "copy", outAbs], { signal: ctx.signal });
53
- if (r.code !== 0) {
54
- return err(`ffmpeg concat-demuxer exited ${r.code}`, "sources may differ in codec/res/fps; retry with lossless=false");
54
+ try {
55
+ const r = await runFfmpeg(["-f", "concat", "-safe", "0", "-i", listPath, "-c", "copy", outAbs], { signal: ctx.signal });
56
+ if (r.code !== 0) {
57
+ return err(`ffmpeg concat-demuxer exited ${r.code}`, "sources may differ in codec/res/fps; retry with lossless=false");
58
+ }
59
+ return compact({ ok: true, path: outAbs, mode: "lossless" });
60
+ }
61
+ finally {
62
+ rmSync(listPath, { force: true });
55
63
  }
56
- return compact({ ok: true, path: outAbs, mode: "lossless" });
57
64
  }
58
65
  // Filter-based concat (re-encode).
59
66
  const args = [];