agent-recipes 0.0.5__py3-none-any.whl

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 (270) hide show
  1. agent_recipes/__init__.py +27 -0
  2. agent_recipes/recipe_runtime/__init__.py +28 -0
  3. agent_recipes/recipe_runtime/core.py +385 -0
  4. agent_recipes/templates/ai-ab-hook-tester/recipe.yaml +45 -0
  5. agent_recipes/templates/ai-ab-hook-tester/tools.py +169 -0
  6. agent_recipes/templates/ai-angle-generator/recipe.yaml +49 -0
  7. agent_recipes/templates/ai-angle-generator/tools.py +182 -0
  8. agent_recipes/templates/ai-api-doc-generator/README.md +59 -0
  9. agent_recipes/templates/ai-api-doc-generator/TEMPLATE.yaml +29 -0
  10. agent_recipes/templates/ai-api-tester/README.md +60 -0
  11. agent_recipes/templates/ai-api-tester/TEMPLATE.yaml +29 -0
  12. agent_recipes/templates/ai-audio-enhancer/README.md +59 -0
  13. agent_recipes/templates/ai-audio-enhancer/TEMPLATE.yaml +28 -0
  14. agent_recipes/templates/ai-audio-normalizer/README.md +13 -0
  15. agent_recipes/templates/ai-audio-normalizer/TEMPLATE.yaml +44 -0
  16. agent_recipes/templates/ai-audio-splitter/README.md +14 -0
  17. agent_recipes/templates/ai-audio-splitter/TEMPLATE.yaml +47 -0
  18. agent_recipes/templates/ai-background-music-generator/README.md +59 -0
  19. agent_recipes/templates/ai-background-music-generator/TEMPLATE.yaml +28 -0
  20. agent_recipes/templates/ai-background-remover/README.md +60 -0
  21. agent_recipes/templates/ai-background-remover/TEMPLATE.yaml +27 -0
  22. agent_recipes/templates/ai-barcode-scanner/README.md +60 -0
  23. agent_recipes/templates/ai-barcode-scanner/TEMPLATE.yaml +26 -0
  24. agent_recipes/templates/ai-blog-generator/README.md +59 -0
  25. agent_recipes/templates/ai-blog-generator/TEMPLATE.yaml +28 -0
  26. agent_recipes/templates/ai-brief-generator/recipe.yaml +52 -0
  27. agent_recipes/templates/ai-brief-generator/tools.py +231 -0
  28. agent_recipes/templates/ai-broll-builder/recipe.yaml +47 -0
  29. agent_recipes/templates/ai-broll-builder/tools.py +204 -0
  30. agent_recipes/templates/ai-calendar-scheduler/README.md +60 -0
  31. agent_recipes/templates/ai-calendar-scheduler/TEMPLATE.yaml +29 -0
  32. agent_recipes/templates/ai-changelog-generator/README.md +14 -0
  33. agent_recipes/templates/ai-changelog-generator/TEMPLATE.yaml +46 -0
  34. agent_recipes/templates/ai-chart-generator/README.md +61 -0
  35. agent_recipes/templates/ai-chart-generator/TEMPLATE.yaml +32 -0
  36. agent_recipes/templates/ai-code-documenter/README.md +12 -0
  37. agent_recipes/templates/ai-code-documenter/TEMPLATE.yaml +37 -0
  38. agent_recipes/templates/ai-code-refactorer/README.md +59 -0
  39. agent_recipes/templates/ai-code-refactorer/TEMPLATE.yaml +28 -0
  40. agent_recipes/templates/ai-code-reviewer/README.md +59 -0
  41. agent_recipes/templates/ai-code-reviewer/TEMPLATE.yaml +31 -0
  42. agent_recipes/templates/ai-color-palette-extractor/README.md +60 -0
  43. agent_recipes/templates/ai-color-palette-extractor/TEMPLATE.yaml +27 -0
  44. agent_recipes/templates/ai-comment-miner/recipe.yaml +40 -0
  45. agent_recipes/templates/ai-comment-miner/tools.py +141 -0
  46. agent_recipes/templates/ai-commit-message-generator/README.md +59 -0
  47. agent_recipes/templates/ai-commit-message-generator/TEMPLATE.yaml +31 -0
  48. agent_recipes/templates/ai-content-calendar/recipe.yaml +43 -0
  49. agent_recipes/templates/ai-content-calendar/tools.py +170 -0
  50. agent_recipes/templates/ai-context-enricher/recipe.yaml +48 -0
  51. agent_recipes/templates/ai-context-enricher/tools.py +258 -0
  52. agent_recipes/templates/ai-contract-analyzer/README.md +60 -0
  53. agent_recipes/templates/ai-contract-analyzer/TEMPLATE.yaml +34 -0
  54. agent_recipes/templates/ai-csv-cleaner/README.md +13 -0
  55. agent_recipes/templates/ai-csv-cleaner/TEMPLATE.yaml +45 -0
  56. agent_recipes/templates/ai-cta-generator/recipe.yaml +54 -0
  57. agent_recipes/templates/ai-cta-generator/tools.py +174 -0
  58. agent_recipes/templates/ai-daily-news-show/recipe.yaml +103 -0
  59. agent_recipes/templates/ai-daily-news-show/tools.py +308 -0
  60. agent_recipes/templates/ai-data-anonymizer/README.md +60 -0
  61. agent_recipes/templates/ai-data-anonymizer/TEMPLATE.yaml +31 -0
  62. agent_recipes/templates/ai-data-profiler/README.md +14 -0
  63. agent_recipes/templates/ai-data-profiler/TEMPLATE.yaml +42 -0
  64. agent_recipes/templates/ai-dependency-auditor/README.md +12 -0
  65. agent_recipes/templates/ai-dependency-auditor/TEMPLATE.yaml +37 -0
  66. agent_recipes/templates/ai-doc-translator/README.md +12 -0
  67. agent_recipes/templates/ai-doc-translator/TEMPLATE.yaml +41 -0
  68. agent_recipes/templates/ai-duplicate-finder/README.md +59 -0
  69. agent_recipes/templates/ai-duplicate-finder/TEMPLATE.yaml +28 -0
  70. agent_recipes/templates/ai-ebook-converter/README.md +60 -0
  71. agent_recipes/templates/ai-ebook-converter/TEMPLATE.yaml +27 -0
  72. agent_recipes/templates/ai-email-parser/README.md +59 -0
  73. agent_recipes/templates/ai-email-parser/TEMPLATE.yaml +29 -0
  74. agent_recipes/templates/ai-etl-pipeline/README.md +60 -0
  75. agent_recipes/templates/ai-etl-pipeline/TEMPLATE.yaml +30 -0
  76. agent_recipes/templates/ai-excel-formula-generator/README.md +59 -0
  77. agent_recipes/templates/ai-excel-formula-generator/TEMPLATE.yaml +28 -0
  78. agent_recipes/templates/ai-face-blur/README.md +60 -0
  79. agent_recipes/templates/ai-face-blur/TEMPLATE.yaml +28 -0
  80. agent_recipes/templates/ai-fact-checker/recipe.yaml +52 -0
  81. agent_recipes/templates/ai-fact-checker/tools.py +279 -0
  82. agent_recipes/templates/ai-faq-generator/README.md +59 -0
  83. agent_recipes/templates/ai-faq-generator/TEMPLATE.yaml +28 -0
  84. agent_recipes/templates/ai-file-organizer/README.md +59 -0
  85. agent_recipes/templates/ai-file-organizer/TEMPLATE.yaml +29 -0
  86. agent_recipes/templates/ai-folder-packager/README.md +15 -0
  87. agent_recipes/templates/ai-folder-packager/TEMPLATE.yaml +48 -0
  88. agent_recipes/templates/ai-form-filler/README.md +60 -0
  89. agent_recipes/templates/ai-form-filler/TEMPLATE.yaml +30 -0
  90. agent_recipes/templates/ai-hashtag-optimizer/recipe.yaml +45 -0
  91. agent_recipes/templates/ai-hashtag-optimizer/tools.py +134 -0
  92. agent_recipes/templates/ai-hook-generator/recipe.yaml +50 -0
  93. agent_recipes/templates/ai-hook-generator/tools.py +177 -0
  94. agent_recipes/templates/ai-image-captioner/README.md +59 -0
  95. agent_recipes/templates/ai-image-captioner/TEMPLATE.yaml +28 -0
  96. agent_recipes/templates/ai-image-cataloger/README.md +13 -0
  97. agent_recipes/templates/ai-image-cataloger/TEMPLATE.yaml +39 -0
  98. agent_recipes/templates/ai-image-optimizer/README.md +13 -0
  99. agent_recipes/templates/ai-image-optimizer/TEMPLATE.yaml +43 -0
  100. agent_recipes/templates/ai-image-resizer/README.md +12 -0
  101. agent_recipes/templates/ai-image-resizer/TEMPLATE.yaml +39 -0
  102. agent_recipes/templates/ai-image-tagger/README.md +59 -0
  103. agent_recipes/templates/ai-image-tagger/TEMPLATE.yaml +28 -0
  104. agent_recipes/templates/ai-image-upscaler/README.md +60 -0
  105. agent_recipes/templates/ai-image-upscaler/TEMPLATE.yaml +27 -0
  106. agent_recipes/templates/ai-invoice-processor/README.md +60 -0
  107. agent_recipes/templates/ai-invoice-processor/TEMPLATE.yaml +34 -0
  108. agent_recipes/templates/ai-json-to-csv/README.md +12 -0
  109. agent_recipes/templates/ai-json-to-csv/TEMPLATE.yaml +36 -0
  110. agent_recipes/templates/ai-log-analyzer/README.md +59 -0
  111. agent_recipes/templates/ai-log-analyzer/TEMPLATE.yaml +28 -0
  112. agent_recipes/templates/ai-markdown-to-pdf/README.md +12 -0
  113. agent_recipes/templates/ai-markdown-to-pdf/TEMPLATE.yaml +40 -0
  114. agent_recipes/templates/ai-meeting-summarizer/README.md +59 -0
  115. agent_recipes/templates/ai-meeting-summarizer/TEMPLATE.yaml +32 -0
  116. agent_recipes/templates/ai-meta-tag-generator/README.md +59 -0
  117. agent_recipes/templates/ai-meta-tag-generator/TEMPLATE.yaml +28 -0
  118. agent_recipes/templates/ai-news-capture-pack/recipe.yaml +42 -0
  119. agent_recipes/templates/ai-news-capture-pack/tools.py +150 -0
  120. agent_recipes/templates/ai-news-crawler/recipe.yaml +99 -0
  121. agent_recipes/templates/ai-news-crawler/tools.py +417 -0
  122. agent_recipes/templates/ai-news-deduper/recipe.yaml +47 -0
  123. agent_recipes/templates/ai-news-deduper/tools.py +235 -0
  124. agent_recipes/templates/ai-newsletter-generator/README.md +59 -0
  125. agent_recipes/templates/ai-newsletter-generator/TEMPLATE.yaml +28 -0
  126. agent_recipes/templates/ai-note-summarizer/README.md +59 -0
  127. agent_recipes/templates/ai-note-summarizer/TEMPLATE.yaml +28 -0
  128. agent_recipes/templates/ai-pdf-summarizer/README.md +12 -0
  129. agent_recipes/templates/ai-pdf-summarizer/TEMPLATE.yaml +40 -0
  130. agent_recipes/templates/ai-pdf-to-markdown/README.md +19 -0
  131. agent_recipes/templates/ai-pdf-to-markdown/TEMPLATE.yaml +63 -0
  132. agent_recipes/templates/ai-performance-analyzer/recipe.yaml +45 -0
  133. agent_recipes/templates/ai-performance-analyzer/tools.py +159 -0
  134. agent_recipes/templates/ai-podcast-cleaner/README.md +117 -0
  135. agent_recipes/templates/ai-podcast-cleaner/TEMPLATE.yaml +117 -0
  136. agent_recipes/templates/ai-podcast-cleaner/agents.yaml +59 -0
  137. agent_recipes/templates/ai-podcast-cleaner/workflow.yaml +77 -0
  138. agent_recipes/templates/ai-podcast-transcriber/README.md +59 -0
  139. agent_recipes/templates/ai-podcast-transcriber/TEMPLATE.yaml +32 -0
  140. agent_recipes/templates/ai-post-copy-generator/recipe.yaml +41 -0
  141. agent_recipes/templates/ai-post-copy-generator/tools.py +105 -0
  142. agent_recipes/templates/ai-product-description-generator/README.md +59 -0
  143. agent_recipes/templates/ai-product-description-generator/TEMPLATE.yaml +28 -0
  144. agent_recipes/templates/ai-publisher-pack/recipe.yaml +44 -0
  145. agent_recipes/templates/ai-publisher-pack/tools.py +252 -0
  146. agent_recipes/templates/ai-qr-code-generator/README.md +60 -0
  147. agent_recipes/templates/ai-qr-code-generator/TEMPLATE.yaml +26 -0
  148. agent_recipes/templates/ai-regex-generator/README.md +59 -0
  149. agent_recipes/templates/ai-regex-generator/TEMPLATE.yaml +28 -0
  150. agent_recipes/templates/ai-repo-readme/README.md +13 -0
  151. agent_recipes/templates/ai-repo-readme/TEMPLATE.yaml +42 -0
  152. agent_recipes/templates/ai-report-generator/README.md +61 -0
  153. agent_recipes/templates/ai-report-generator/TEMPLATE.yaml +32 -0
  154. agent_recipes/templates/ai-resume-parser/README.md +60 -0
  155. agent_recipes/templates/ai-resume-parser/TEMPLATE.yaml +33 -0
  156. agent_recipes/templates/ai-rss-aggregator/README.md +60 -0
  157. agent_recipes/templates/ai-rss-aggregator/TEMPLATE.yaml +30 -0
  158. agent_recipes/templates/ai-schema-generator/README.md +12 -0
  159. agent_recipes/templates/ai-schema-generator/TEMPLATE.yaml +34 -0
  160. agent_recipes/templates/ai-screen-recorder/recipe.yaml +43 -0
  161. agent_recipes/templates/ai-screen-recorder/tools.py +184 -0
  162. agent_recipes/templates/ai-screenshot-capture/recipe.yaml +45 -0
  163. agent_recipes/templates/ai-screenshot-capture/tools.py +231 -0
  164. agent_recipes/templates/ai-screenshot-ocr/README.md +12 -0
  165. agent_recipes/templates/ai-screenshot-ocr/TEMPLATE.yaml +37 -0
  166. agent_recipes/templates/ai-script-writer/recipe.yaml +58 -0
  167. agent_recipes/templates/ai-script-writer/tools.py +297 -0
  168. agent_recipes/templates/ai-sentiment-analyzer/README.md +59 -0
  169. agent_recipes/templates/ai-sentiment-analyzer/TEMPLATE.yaml +28 -0
  170. agent_recipes/templates/ai-seo-optimizer/README.md +59 -0
  171. agent_recipes/templates/ai-seo-optimizer/TEMPLATE.yaml +28 -0
  172. agent_recipes/templates/ai-signal-ranker/recipe.yaml +54 -0
  173. agent_recipes/templates/ai-signal-ranker/tools.py +256 -0
  174. agent_recipes/templates/ai-sitemap-generator/README.md +59 -0
  175. agent_recipes/templates/ai-sitemap-generator/TEMPLATE.yaml +26 -0
  176. agent_recipes/templates/ai-sitemap-scraper/README.md +13 -0
  177. agent_recipes/templates/ai-sitemap-scraper/TEMPLATE.yaml +41 -0
  178. agent_recipes/templates/ai-slide-generator/README.md +60 -0
  179. agent_recipes/templates/ai-slide-generator/TEMPLATE.yaml +29 -0
  180. agent_recipes/templates/ai-slide-to-notes/README.md +12 -0
  181. agent_recipes/templates/ai-slide-to-notes/TEMPLATE.yaml +37 -0
  182. agent_recipes/templates/ai-social-media-generator/README.md +59 -0
  183. agent_recipes/templates/ai-social-media-generator/TEMPLATE.yaml +28 -0
  184. agent_recipes/templates/ai-sql-generator/README.md +59 -0
  185. agent_recipes/templates/ai-sql-generator/TEMPLATE.yaml +28 -0
  186. agent_recipes/templates/ai-subtitle-generator/README.md +59 -0
  187. agent_recipes/templates/ai-subtitle-generator/TEMPLATE.yaml +31 -0
  188. agent_recipes/templates/ai-test-generator/README.md +59 -0
  189. agent_recipes/templates/ai-test-generator/TEMPLATE.yaml +28 -0
  190. agent_recipes/templates/ai-translation-batch/README.md +59 -0
  191. agent_recipes/templates/ai-translation-batch/TEMPLATE.yaml +28 -0
  192. agent_recipes/templates/ai-url-to-markdown/README.md +14 -0
  193. agent_recipes/templates/ai-url-to-markdown/TEMPLATE.yaml +44 -0
  194. agent_recipes/templates/ai-video-chapter-generator/README.md +59 -0
  195. agent_recipes/templates/ai-video-chapter-generator/TEMPLATE.yaml +32 -0
  196. agent_recipes/templates/ai-video-compressor/README.md +59 -0
  197. agent_recipes/templates/ai-video-compressor/TEMPLATE.yaml +28 -0
  198. agent_recipes/templates/ai-video-editor/README.md +254 -0
  199. agent_recipes/templates/ai-video-editor/TEMPLATE.yaml +139 -0
  200. agent_recipes/templates/ai-video-editor/agents.yaml +36 -0
  201. agent_recipes/templates/ai-video-editor/requirements.txt +8 -0
  202. agent_recipes/templates/ai-video-editor/scripts/run.sh +10 -0
  203. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__init__.py +45 -0
  204. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__main__.py +8 -0
  205. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/__init__.cpython-312.pyc +0 -0
  206. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/cli.cpython-312.pyc +0 -0
  207. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/config.cpython-312.pyc +0 -0
  208. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/ffmpeg_probe.cpython-312.pyc +0 -0
  209. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/heuristics.cpython-312.pyc +0 -0
  210. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/llm_plan.cpython-312.pyc +0 -0
  211. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/models.cpython-312.pyc +0 -0
  212. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/pipeline.cpython-312.pyc +0 -0
  213. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/render.cpython-312.pyc +0 -0
  214. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/timeline.cpython-312.pyc +0 -0
  215. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/transcribe.cpython-312.pyc +0 -0
  216. agent_recipes/templates/ai-video-editor/src/ai_video_editor/__pycache__/utils.cpython-312.pyc +0 -0
  217. agent_recipes/templates/ai-video-editor/src/ai_video_editor/cli.py +343 -0
  218. agent_recipes/templates/ai-video-editor/src/ai_video_editor/config.py +102 -0
  219. agent_recipes/templates/ai-video-editor/src/ai_video_editor/ffmpeg_probe.py +92 -0
  220. agent_recipes/templates/ai-video-editor/src/ai_video_editor/heuristics.py +119 -0
  221. agent_recipes/templates/ai-video-editor/src/ai_video_editor/llm_plan.py +277 -0
  222. agent_recipes/templates/ai-video-editor/src/ai_video_editor/models.py +343 -0
  223. agent_recipes/templates/ai-video-editor/src/ai_video_editor/pipeline.py +287 -0
  224. agent_recipes/templates/ai-video-editor/src/ai_video_editor/render.py +274 -0
  225. agent_recipes/templates/ai-video-editor/src/ai_video_editor/timeline.py +278 -0
  226. agent_recipes/templates/ai-video-editor/src/ai_video_editor/transcribe.py +233 -0
  227. agent_recipes/templates/ai-video-editor/src/ai_video_editor/utils.py +222 -0
  228. agent_recipes/templates/ai-video-editor/src/input.mov +0 -0
  229. agent_recipes/templates/ai-video-editor/src/out.mp4 +0 -0
  230. agent_recipes/templates/ai-video-editor/tests/test_heuristics.py +130 -0
  231. agent_recipes/templates/ai-video-editor/tests/test_models.py +152 -0
  232. agent_recipes/templates/ai-video-editor/tests/test_timeline.py +105 -0
  233. agent_recipes/templates/ai-video-editor/workflow.yaml +51 -0
  234. agent_recipes/templates/ai-video-highlight-extractor/README.md +60 -0
  235. agent_recipes/templates/ai-video-highlight-extractor/TEMPLATE.yaml +33 -0
  236. agent_recipes/templates/ai-video-merger/recipe.yaml +40 -0
  237. agent_recipes/templates/ai-video-merger/tools.py +172 -0
  238. agent_recipes/templates/ai-video-thumbnails/README.md +16 -0
  239. agent_recipes/templates/ai-video-thumbnails/TEMPLATE.yaml +53 -0
  240. agent_recipes/templates/ai-video-to-gif/README.md +14 -0
  241. agent_recipes/templates/ai-video-to-gif/TEMPLATE.yaml +64 -0
  242. agent_recipes/templates/ai-voice-cloner/README.md +59 -0
  243. agent_recipes/templates/ai-voice-cloner/TEMPLATE.yaml +31 -0
  244. agent_recipes/templates/ai-voiceover-generator/recipe.yaml +41 -0
  245. agent_recipes/templates/ai-voiceover-generator/tools.py +194 -0
  246. agent_recipes/templates/ai-watermark-adder/README.md +59 -0
  247. agent_recipes/templates/ai-watermark-adder/TEMPLATE.yaml +26 -0
  248. agent_recipes/templates/ai-watermark-remover/README.md +60 -0
  249. agent_recipes/templates/ai-watermark-remover/TEMPLATE.yaml +32 -0
  250. agent_recipes/templates/data-transformer/README.md +75 -0
  251. agent_recipes/templates/data-transformer/TEMPLATE.yaml +63 -0
  252. agent_recipes/templates/data-transformer/agents.yaml +70 -0
  253. agent_recipes/templates/data-transformer/workflow.yaml +92 -0
  254. agent_recipes/templates/shorts-generator/README.md +61 -0
  255. agent_recipes/templates/shorts-generator/TEMPLATE.yaml +65 -0
  256. agent_recipes/templates/shorts-generator/agents.yaml +66 -0
  257. agent_recipes/templates/shorts-generator/workflow.yaml +86 -0
  258. agent_recipes/templates/transcript-generator/README.md +103 -0
  259. agent_recipes/templates/transcript-generator/TEMPLATE.yaml +57 -0
  260. agent_recipes/templates/transcript-generator/agents.yaml +62 -0
  261. agent_recipes/templates/transcript-generator/workflow.yaml +82 -0
  262. agent_recipes/templates/video-editor/README.md +70 -0
  263. agent_recipes/templates/video-editor/TEMPLATE.yaml +55 -0
  264. agent_recipes/templates/video-editor/agents.yaml +68 -0
  265. agent_recipes/templates/video-editor/workflow.yaml +92 -0
  266. agent_recipes-0.0.5.dist-info/METADATA +145 -0
  267. agent_recipes-0.0.5.dist-info/RECORD +269 -0
  268. agent_recipes-0.0.5.dist-info/WHEEL +5 -0
  269. agent_recipes-0.0.5.dist-info/top_level.txt +1 -0
  270. /236/326/177nE/243/231/214/232/265/322m/201/253/353/022C/372/321/266/b/225^=/272/017t/262/3337/310@/315wb/341pB/277z/216/330/314/004/265B/213/375/236/203/026/373/307/354z41/347#/374q/262/22589/032/276 /277/244Vh/322/017/004/224/215/004/367/377/375/335/n +0 -0
@@ -0,0 +1,343 @@
1
+ """
2
+ CLI for AI Video Editor.
3
+
4
+ Provides command-line interface for:
5
+ - edit: Edit video with AI analysis
6
+ - probe: Extract video metadata
7
+ - transcript: Generate transcript with timestamps
8
+ - doctor: Check dependencies
9
+ """
10
+
11
+ import argparse
12
+ import json
13
+ import os
14
+ import sys
15
+ from pathlib import Path
16
+
17
+
18
+ def main():
19
+ """Main CLI entry point."""
20
+ parser = argparse.ArgumentParser(
21
+ prog="ai_video_editor",
22
+ description="AI-powered video editor - removes fillers, repetitions, tangents, and silences"
23
+ )
24
+
25
+ subparsers = parser.add_subparsers(dest="command", help="Commands")
26
+
27
+ # Edit command
28
+ edit_parser = subparsers.add_parser("edit", help="Edit video with AI analysis")
29
+ edit_parser.add_argument("input", help="Input video file")
30
+ edit_parser.add_argument("-o", "--output", help="Output video file")
31
+ edit_parser.add_argument("-p", "--preset", default="podcast",
32
+ choices=["podcast", "meeting", "course", "clean"],
33
+ help="Edit preset (default: podcast)")
34
+ edit_parser.add_argument("--remove-fillers", action="store_true",
35
+ help="Remove filler words")
36
+ edit_parser.add_argument("--remove-repetitions", action="store_true",
37
+ help="Remove repeated phrases")
38
+ edit_parser.add_argument("--remove-tangents", action="store_true",
39
+ help="Remove off-topic content")
40
+ edit_parser.add_argument("--remove-silence", action="store_true",
41
+ help="Remove long silences")
42
+ edit_parser.add_argument("--auto-crop", default="off",
43
+ choices=["off", "center", "face"],
44
+ help="Cropping mode (default: off)")
45
+ edit_parser.add_argument("--target-length", help="Target duration (e.g., 6m, 90s)")
46
+ edit_parser.add_argument("--captions", default="srt",
47
+ choices=["off", "srt", "burn"],
48
+ help="Caption mode (default: srt)")
49
+ edit_parser.add_argument("--provider", default="auto",
50
+ choices=["openai", "local", "auto"],
51
+ help="Transcription provider (default: auto)")
52
+ edit_parser.add_argument("--no-llm", action="store_true",
53
+ help="Use simple pattern matching instead of LLM")
54
+ edit_parser.add_argument("--force", action="store_true",
55
+ help="Overwrite output if exists")
56
+ edit_parser.add_argument("--json-report", help="Save JSON report to path")
57
+ edit_parser.add_argument("-v", "--verbose", action="store_true",
58
+ help="Enable verbose output")
59
+
60
+ # Probe command
61
+ probe_parser = subparsers.add_parser("probe", help="Extract video metadata")
62
+ probe_parser.add_argument("input", help="Input video file")
63
+ probe_parser.add_argument("--json", action="store_true",
64
+ help="Output as JSON")
65
+
66
+ # Transcript command
67
+ transcript_parser = subparsers.add_parser("transcript", help="Generate transcript")
68
+ transcript_parser.add_argument("input", help="Input video/audio file")
69
+ transcript_parser.add_argument("-o", "--output", help="Output file path")
70
+ transcript_parser.add_argument("--format", default="srt",
71
+ choices=["srt", "txt", "json"],
72
+ help="Output format (default: srt)")
73
+ transcript_parser.add_argument("--provider", default="auto",
74
+ choices=["openai", "local", "auto"],
75
+ help="Transcription provider (default: auto)")
76
+ transcript_parser.add_argument("--language", default="en",
77
+ help="Language code (default: en)")
78
+
79
+ # Doctor command
80
+ doctor_parser = subparsers.add_parser("doctor", help="Check dependencies")
81
+
82
+ # Help command
83
+ help_parser = subparsers.add_parser("help", help="Show help")
84
+
85
+ args = parser.parse_args()
86
+
87
+ if args.command is None or args.command == "help":
88
+ _print_help()
89
+ return 0
90
+
91
+ try:
92
+ if args.command == "edit":
93
+ return cmd_edit(args)
94
+ elif args.command == "probe":
95
+ return cmd_probe(args)
96
+ elif args.command == "transcript":
97
+ return cmd_transcript(args)
98
+ elif args.command == "doctor":
99
+ return cmd_doctor(args)
100
+ else:
101
+ parser.print_help()
102
+ return 1
103
+ except KeyboardInterrupt:
104
+ print("\nOperation cancelled.")
105
+ return 130
106
+ except Exception as e:
107
+ print(f"Error: {e}", file=sys.stderr)
108
+ if os.environ.get("DEBUG"):
109
+ import traceback
110
+ traceback.print_exc()
111
+ return 1
112
+
113
+
114
+ def _print_help():
115
+ """Print help message."""
116
+ print("""
117
+ AI Video Editor - Self-contained recipe for PraisonAI
118
+
119
+ Usage:
120
+ python -m ai_video_editor.cli <command> [options]
121
+
122
+ Commands:
123
+ edit <input> Edit video with AI analysis
124
+ probe <input> Extract video metadata
125
+ transcript <input> Generate transcript with timestamps
126
+ doctor Check dependencies
127
+ help Show this help
128
+
129
+ Edit Options:
130
+ --output, -o PATH Output video path
131
+ --preset PRESET Edit preset (podcast, meeting, course, clean)
132
+ --remove-fillers Remove filler words (um, uh, like, etc.)
133
+ --remove-repetitions Remove repeated phrases
134
+ --remove-tangents Remove off-topic content
135
+ --auto-crop MODE Crop mode (off, center, face)
136
+ --target-length TIME Target duration (e.g., 6m, 90s)
137
+ --captions MODE Caption mode (off, srt, burn)
138
+ --provider PROVIDER Transcription provider (openai, local, auto)
139
+ --no-llm Use simple pattern matching instead of LLM
140
+ --force Overwrite output if exists
141
+ --json-report PATH Save JSON report to path
142
+ --verbose, -v Enable verbose output
143
+
144
+ Presets:
145
+ podcast Remove fillers, repetitions, long silences
146
+ meeting Remove fillers, repetitions, tangents, silences
147
+ course Remove fillers, repetitions, short silences
148
+ clean Aggressive removal of all detected issues
149
+
150
+ Examples:
151
+ python -m ai_video_editor.cli edit input.mp4 --preset podcast --output out.mp4
152
+ python -m ai_video_editor.cli edit input.mp4 --remove-fillers --remove-tangents
153
+ python -m ai_video_editor.cli probe input.mp4
154
+ python -m ai_video_editor.cli transcript input.mp4 --output transcript.srt
155
+ python -m ai_video_editor.cli doctor
156
+
157
+ Environment Variables:
158
+ OPENAI_API_KEY Required for transcription and LLM analysis
159
+ WHISPER_MODEL Local whisper model size (default: small)
160
+ DEBUG Enable debug output
161
+ """)
162
+
163
+
164
+ def cmd_edit(args):
165
+ """Handle edit command."""
166
+ from .pipeline import edit
167
+
168
+ # Determine overrides from flags
169
+ remove_fillers = True if args.remove_fillers else None
170
+ remove_repetitions = True if args.remove_repetitions else None
171
+ remove_tangents = True if args.remove_tangents else None
172
+ remove_silence = True if args.remove_silence else None
173
+
174
+ result = edit(
175
+ input_path=args.input,
176
+ output_path=args.output,
177
+ preset=args.preset,
178
+ remove_fillers=remove_fillers,
179
+ remove_repetitions=remove_repetitions,
180
+ remove_tangents=remove_tangents,
181
+ remove_silence=remove_silence,
182
+ auto_crop=args.auto_crop,
183
+ target_length=args.target_length,
184
+ captions=args.captions,
185
+ provider=args.provider,
186
+ use_llm=not args.no_llm,
187
+ force=args.force,
188
+ verbose=args.verbose
189
+ )
190
+
191
+ # Print summary
192
+ print(f"\n✓ Edit complete!")
193
+ print(f" Output: {result.output_path}")
194
+ print(f" Original: {_format_duration(result.original_duration)}")
195
+ print(f" Final: {_format_duration(result.final_duration)}")
196
+ print(f" Saved: {_format_duration(result.time_saved)} ({result.time_saved/result.original_duration*100:.1f}%)")
197
+ print(f"\nArtifacts:")
198
+ print(f" Transcript: {result.transcript_path}")
199
+ print(f" Captions: {result.srt_path}")
200
+ print(f" Edit Plan: {result.report_path}")
201
+ print(f" EDL: {result.edl_path}")
202
+
203
+ if args.json_report:
204
+ result.to_json(args.json_report)
205
+ print(f" JSON Report: {args.json_report}")
206
+
207
+ return 0
208
+
209
+
210
+ def cmd_probe(args):
211
+ """Handle probe command."""
212
+ from .pipeline import probe
213
+
214
+ result = probe(args.input)
215
+
216
+ if args.json:
217
+ print(json.dumps(result.to_dict(), indent=2))
218
+ else:
219
+ print(f"File: {result.path}")
220
+ print(f"Duration: {_format_duration(result.duration)}")
221
+ print(f"Resolution: {result.width}x{result.height}")
222
+ print(f"FPS: {result.fps}")
223
+ print(f"Video Codec: {result.codec}")
224
+ if result.audio_codec:
225
+ print(f"Audio Codec: {result.audio_codec}")
226
+ print(f"Audio Channels: {result.audio_channels}")
227
+ print(f"Sample Rate: {result.audio_sample_rate} Hz")
228
+ print(f"File Size: {result.file_size / 1024 / 1024:.2f} MB")
229
+
230
+ return 0
231
+
232
+
233
+ def cmd_transcript(args):
234
+ """Handle transcript command."""
235
+ from .pipeline import transcript
236
+
237
+ result = transcript(
238
+ input_path=args.input,
239
+ provider=args.provider,
240
+ language=args.language
241
+ )
242
+
243
+ output_path = args.output
244
+ if output_path is None:
245
+ stem = Path(args.input).stem
246
+ if args.format == "srt":
247
+ output_path = f"{stem}.srt"
248
+ elif args.format == "txt":
249
+ output_path = f"{stem}.txt"
250
+ else:
251
+ output_path = f"{stem}_transcript.json"
252
+
253
+ if args.format == "srt":
254
+ result.to_srt(output_path)
255
+ print(f"✓ SRT saved to: {output_path}")
256
+ elif args.format == "txt":
257
+ Path(output_path).write_text(result.text)
258
+ print(f"✓ Transcript saved to: {output_path}")
259
+ else:
260
+ Path(output_path).write_text(json.dumps(result.to_dict(), indent=2))
261
+ print(f"✓ JSON saved to: {output_path}")
262
+
263
+ print(f" Words: {len(result.words)}")
264
+ print(f" Duration: {_format_duration(result.duration)}")
265
+ print(f" Provider: {result.provider}")
266
+
267
+ return 0
268
+
269
+
270
+ def cmd_doctor(args):
271
+ """Handle doctor command."""
272
+ from .utils import check_ffmpeg, check_ffprobe
273
+
274
+ print("AI Video Editor - Dependency Check\n")
275
+
276
+ all_ok = True
277
+
278
+ # Check FFmpeg
279
+ ffmpeg_ok, ffmpeg_msg = check_ffmpeg()
280
+ if ffmpeg_ok:
281
+ print(f"✓ FFmpeg: {ffmpeg_msg}")
282
+ else:
283
+ print(f"✗ FFmpeg: {ffmpeg_msg}")
284
+ all_ok = False
285
+
286
+ # Check FFprobe
287
+ ffprobe_ok, ffprobe_msg = check_ffprobe()
288
+ if ffprobe_ok:
289
+ print(f"✓ FFprobe: {ffprobe_msg}")
290
+ else:
291
+ print(f"✗ FFprobe: {ffprobe_msg}")
292
+ all_ok = False
293
+
294
+ # Check OpenAI API key
295
+ api_key = os.environ.get("OPENAI_API_KEY")
296
+ if api_key:
297
+ print(f"✓ OPENAI_API_KEY: Set ({len(api_key)} chars)")
298
+ else:
299
+ print("✗ OPENAI_API_KEY: Not set")
300
+ all_ok = False
301
+
302
+ # Check OpenAI package
303
+ try:
304
+ import openai
305
+ print(f"✓ openai package: {openai.__version__}")
306
+ except ImportError:
307
+ print("✗ openai package: Not installed (pip install openai)")
308
+ all_ok = False
309
+
310
+ # Check faster-whisper (optional)
311
+ try:
312
+ import faster_whisper
313
+ print(f"✓ faster-whisper: Available (optional)")
314
+ except ImportError:
315
+ print("○ faster-whisper: Not installed (optional, for local transcription)")
316
+
317
+ print()
318
+ if all_ok:
319
+ print("All required dependencies are available!")
320
+ return 0
321
+ else:
322
+ print("Some dependencies are missing. Please install them to use all features.")
323
+ return 1
324
+
325
+
326
+ def _format_duration(seconds: float) -> str:
327
+ """Format duration in human-readable format."""
328
+ hours = int(seconds // 3600)
329
+ minutes = int((seconds % 3600) // 60)
330
+ secs = int(seconds % 60)
331
+
332
+ parts = []
333
+ if hours > 0:
334
+ parts.append(f"{hours}h")
335
+ if minutes > 0 or hours > 0:
336
+ parts.append(f"{minutes}m")
337
+ parts.append(f"{secs}s")
338
+
339
+ return " ".join(parts)
340
+
341
+
342
+ if __name__ == "__main__":
343
+ sys.exit(main())
@@ -0,0 +1,102 @@
1
+ """
2
+ Configuration and presets for AI Video Editor.
3
+ """
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Any, Dict, List, Optional
7
+
8
+
9
+ # Preset configurations
10
+ PRESETS = {
11
+ "podcast": {
12
+ "remove_fillers": True,
13
+ "remove_repetitions": True,
14
+ "remove_tangents": False,
15
+ "remove_silence": True,
16
+ "silence_threshold_ms": 700,
17
+ "min_segment_length": 1.2,
18
+ "padding_ms": 120,
19
+ "filler_words": ["um", "uh", "like", "you know", "sort of", "kind of", "basically", "actually", "literally", "right"],
20
+ },
21
+ "meeting": {
22
+ "remove_fillers": True,
23
+ "remove_repetitions": True,
24
+ "remove_tangents": True,
25
+ "remove_silence": True,
26
+ "silence_threshold_ms": 1000,
27
+ "min_segment_length": 1.5,
28
+ "padding_ms": 150,
29
+ "filler_words": ["um", "uh", "like", "you know"],
30
+ },
31
+ "course": {
32
+ "remove_fillers": True,
33
+ "remove_repetitions": True,
34
+ "remove_tangents": False,
35
+ "remove_silence": True,
36
+ "silence_threshold_ms": 500,
37
+ "min_segment_length": 1.0,
38
+ "padding_ms": 100,
39
+ "filler_words": ["um", "uh"],
40
+ },
41
+ "clean": {
42
+ "remove_fillers": True,
43
+ "remove_repetitions": True,
44
+ "remove_tangents": True,
45
+ "remove_silence": True,
46
+ "silence_threshold_ms": 600,
47
+ "min_segment_length": 1.0,
48
+ "padding_ms": 100,
49
+ "filler_words": ["um", "uh", "like", "you know", "sort of", "kind of"],
50
+ },
51
+ }
52
+
53
+
54
+ @dataclass
55
+ class EditConfig:
56
+ """Configuration for video editing."""
57
+ preset: str = "podcast"
58
+ remove_fillers: bool = True
59
+ remove_repetitions: bool = True
60
+ remove_tangents: bool = False
61
+ remove_silence: bool = True
62
+ silence_threshold_ms: int = 700
63
+ min_segment_length: float = 1.2
64
+ padding_ms: int = 120
65
+ filler_words: List[str] = field(default_factory=lambda: ["um", "uh", "like", "you know"])
66
+ target_length: Optional[float] = None # seconds
67
+ auto_crop: str = "off" # off, center, face
68
+ captions: str = "srt" # off, srt, burn
69
+ provider: str = "auto" # openai, local, auto
70
+ use_llm: bool = True
71
+ verbose: bool = False
72
+
73
+ @classmethod
74
+ def from_preset(cls, preset: str, **overrides) -> "EditConfig":
75
+ """Create config from preset with optional overrides."""
76
+ if preset not in PRESETS:
77
+ raise ValueError(f"Unknown preset: {preset}. Available: {list(PRESETS.keys())}")
78
+
79
+ config_dict = PRESETS[preset].copy()
80
+ config_dict["preset"] = preset
81
+ config_dict.update(overrides)
82
+
83
+ return cls(**{k: v for k, v in config_dict.items() if k in cls.__dataclass_fields__})
84
+
85
+ def to_dict(self) -> Dict[str, Any]:
86
+ return {
87
+ "preset": self.preset,
88
+ "remove_fillers": self.remove_fillers,
89
+ "remove_repetitions": self.remove_repetitions,
90
+ "remove_tangents": self.remove_tangents,
91
+ "remove_silence": self.remove_silence,
92
+ "silence_threshold_ms": self.silence_threshold_ms,
93
+ "min_segment_length": self.min_segment_length,
94
+ "padding_ms": self.padding_ms,
95
+ "filler_words": self.filler_words,
96
+ "target_length": self.target_length,
97
+ "auto_crop": self.auto_crop,
98
+ "captions": self.captions,
99
+ "provider": self.provider,
100
+ "use_llm": self.use_llm,
101
+ "verbose": self.verbose,
102
+ }
@@ -0,0 +1,92 @@
1
+ """
2
+ Video probe functionality using ffprobe.
3
+ """
4
+
5
+ import json
6
+ import os
7
+ import subprocess
8
+ from pathlib import Path
9
+
10
+ from .models import VideoProbeResult
11
+ from .utils import check_ffprobe
12
+
13
+
14
+ def probe(input_path: str) -> VideoProbeResult:
15
+ """
16
+ Probe a video file to extract metadata.
17
+
18
+ Args:
19
+ input_path: Path to video file
20
+
21
+ Returns:
22
+ VideoProbeResult with video metadata
23
+ """
24
+ if not os.path.exists(input_path):
25
+ raise FileNotFoundError(f"Video file not found: {input_path}")
26
+
27
+ available, msg = check_ffprobe()
28
+ if not available:
29
+ raise RuntimeError(f"ffprobe is required: {msg}")
30
+
31
+ cmd = [
32
+ "ffprobe",
33
+ "-v", "quiet",
34
+ "-print_format", "json",
35
+ "-show_format",
36
+ "-show_streams",
37
+ input_path
38
+ ]
39
+
40
+ try:
41
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
42
+ except subprocess.TimeoutExpired:
43
+ raise RuntimeError("ffprobe timed out")
44
+
45
+ if result.returncode != 0:
46
+ raise RuntimeError(f"ffprobe failed: {result.stderr}")
47
+
48
+ try:
49
+ data = json.loads(result.stdout)
50
+ except json.JSONDecodeError as e:
51
+ raise RuntimeError(f"Failed to parse ffprobe output: {e}")
52
+
53
+ video_stream = None
54
+ audio_stream = None
55
+
56
+ for stream in data.get("streams", []):
57
+ if stream.get("codec_type") == "video" and video_stream is None:
58
+ video_stream = stream
59
+ elif stream.get("codec_type") == "audio" and audio_stream is None:
60
+ audio_stream = stream
61
+
62
+ if video_stream is None:
63
+ raise RuntimeError("No video stream found in file")
64
+
65
+ format_info = data.get("format", {})
66
+
67
+ fps = 30.0
68
+ if "r_frame_rate" in video_stream:
69
+ fps_parts = video_stream["r_frame_rate"].split("/")
70
+ if len(fps_parts) == 2 and int(fps_parts[1]) != 0:
71
+ fps = int(fps_parts[0]) / int(fps_parts[1])
72
+ elif len(fps_parts) == 1:
73
+ fps = float(fps_parts[0])
74
+ elif "avg_frame_rate" in video_stream:
75
+ fps_parts = video_stream["avg_frame_rate"].split("/")
76
+ if len(fps_parts) == 2 and int(fps_parts[1]) != 0:
77
+ fps = int(fps_parts[0]) / int(fps_parts[1])
78
+
79
+ return VideoProbeResult(
80
+ path=str(Path(input_path).absolute()),
81
+ duration=float(format_info.get("duration", 0)),
82
+ width=int(video_stream.get("width", 0)),
83
+ height=int(video_stream.get("height", 0)),
84
+ fps=round(fps, 3),
85
+ codec=video_stream.get("codec_name", "unknown"),
86
+ audio_codec=audio_stream.get("codec_name", "") if audio_stream else "",
87
+ audio_channels=int(audio_stream.get("channels", 0)) if audio_stream else 0,
88
+ audio_sample_rate=int(audio_stream.get("sample_rate", 0)) if audio_stream else 0,
89
+ bitrate=int(format_info.get("bit_rate", 0)),
90
+ file_size=int(format_info.get("size", 0)),
91
+ format_name=format_info.get("format_name", "")
92
+ )
@@ -0,0 +1,119 @@
1
+ """
2
+ Heuristic-based content analysis (non-LLM).
3
+
4
+ Provides fast detection of:
5
+ - Filler words
6
+ - Repetitions
7
+ - Long silences
8
+ """
9
+
10
+ from typing import List
11
+
12
+ from .models import Segment, SegmentCategory, TranscriptResult
13
+
14
+
15
+ def detect_fillers(
16
+ transcript: TranscriptResult,
17
+ filler_words: List[str]
18
+ ) -> List[Segment]:
19
+ """
20
+ Detect filler words in transcript using simple pattern matching.
21
+
22
+ Args:
23
+ transcript: Transcript with word timestamps
24
+ filler_words: List of filler words to detect
25
+
26
+ Returns:
27
+ List of segments containing fillers
28
+ """
29
+ filler_segments = []
30
+ filler_set = set(w.lower().strip() for w in filler_words)
31
+
32
+ for word in transcript.words:
33
+ word_text = word.text.lower().strip().strip(".,!?")
34
+ if word_text in filler_set:
35
+ filler_segments.append(Segment(
36
+ start=word.start,
37
+ end=word.end,
38
+ category=SegmentCategory.FILLER,
39
+ reason=f"Filler word: {word.text}",
40
+ text=word.text
41
+ ))
42
+
43
+ return filler_segments
44
+
45
+
46
+ def detect_repetitions(
47
+ transcript: TranscriptResult,
48
+ window_size: int = 5,
49
+ min_repeat_words: int = 3
50
+ ) -> List[Segment]:
51
+ """
52
+ Detect repeated phrases in transcript.
53
+
54
+ Looks for patterns where speaker restarts or repeats themselves.
55
+
56
+ Args:
57
+ transcript: Transcript with word timestamps
58
+ window_size: Number of words to look ahead for repetition
59
+ min_repeat_words: Minimum words to consider a repetition
60
+
61
+ Returns:
62
+ List of segments containing repetitions
63
+ """
64
+ repetition_segments = []
65
+ words = transcript.words
66
+
67
+ i = 0
68
+ while i < len(words) - min_repeat_words:
69
+ current_phrase = [w.text.lower().strip(".,!?") for w in words[i:i+min_repeat_words]]
70
+
71
+ for j in range(i + 1, min(i + window_size + 1, len(words) - min_repeat_words + 1)):
72
+ next_phrase = [w.text.lower().strip(".,!?") for w in words[j:j+min_repeat_words]]
73
+
74
+ if current_phrase == next_phrase:
75
+ repetition_segments.append(Segment(
76
+ start=words[i].start,
77
+ end=words[j-1].end if j > i else words[i+min_repeat_words-1].end,
78
+ category=SegmentCategory.REPEAT,
79
+ reason=f"Repeated phrase: {' '.join(current_phrase)}",
80
+ text=" ".join(w.text for w in words[i:j])
81
+ ))
82
+ i = j + min_repeat_words - 1
83
+ break
84
+
85
+ i += 1
86
+
87
+ return repetition_segments
88
+
89
+
90
+ def detect_silence(
91
+ transcript: TranscriptResult,
92
+ threshold_ms: float = 700
93
+ ) -> List[Segment]:
94
+ """
95
+ Detect long silences between words.
96
+
97
+ Args:
98
+ transcript: Transcript with word timestamps
99
+ threshold_ms: Minimum silence duration in milliseconds
100
+
101
+ Returns:
102
+ List of segments containing silences
103
+ """
104
+ silence_segments = []
105
+ threshold_s = threshold_ms / 1000.0
106
+
107
+ words = transcript.words
108
+ for i in range(len(words) - 1):
109
+ gap = words[i + 1].start - words[i].end
110
+ if gap >= threshold_s:
111
+ silence_segments.append(Segment(
112
+ start=words[i].end,
113
+ end=words[i + 1].start,
114
+ category=SegmentCategory.SILENCE,
115
+ reason=f"Silence: {gap:.2f}s",
116
+ text=""
117
+ ))
118
+
119
+ return silence_segments