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,287 @@
1
+ """
2
+ Main pipeline orchestrator for AI Video Editor.
3
+
4
+ Provides the high-level edit(), probe(), and transcript() functions.
5
+ """
6
+
7
+ import os
8
+ from pathlib import Path
9
+ from typing import Optional
10
+
11
+ from .config import PRESETS
12
+ from .models import TranscriptResult, VideoEditResult, VideoProbeResult
13
+ from .ffmpeg_probe import probe as probe_video
14
+ from .transcribe import transcript as transcribe_video
15
+ from .llm_plan import analyze_content, create_simple_edit_plan
16
+ from .timeline import optimize_timeline, calculate_final_duration
17
+ from .render import render as render_video
18
+ from .utils import (
19
+ check_ffmpeg,
20
+ create_workdir,
21
+ file_hash,
22
+ format_duration,
23
+ generate_edl,
24
+ parse_duration,
25
+ save_config_snapshot,
26
+ )
27
+
28
+
29
+ def probe(input_path: str) -> VideoProbeResult:
30
+ """
31
+ Probe a video file to extract metadata.
32
+
33
+ Args:
34
+ input_path: Path to video file
35
+
36
+ Returns:
37
+ VideoProbeResult with video metadata
38
+ """
39
+ return probe_video(input_path)
40
+
41
+
42
+ def transcript(
43
+ input_path: str,
44
+ provider: str = "auto",
45
+ language: str = "en",
46
+ workdir: str = None
47
+ ) -> TranscriptResult:
48
+ """
49
+ Transcribe audio/video with word-level timestamps.
50
+
51
+ Args:
52
+ input_path: Path to audio or video file
53
+ provider: Transcription provider (openai, local, auto)
54
+ language: Language code
55
+ workdir: Working directory for temp files
56
+
57
+ Returns:
58
+ TranscriptResult with text and word timestamps
59
+ """
60
+ return transcribe_video(input_path, provider=provider, language=language, workdir=workdir)
61
+
62
+
63
+ def edit(
64
+ input_path: str,
65
+ output_path: Optional[str] = None,
66
+ preset: str = "podcast",
67
+ workdir: Optional[str] = None,
68
+ remove_fillers: Optional[bool] = None,
69
+ remove_repetitions: Optional[bool] = None,
70
+ remove_tangents: Optional[bool] = None,
71
+ remove_silence: Optional[bool] = None,
72
+ auto_crop: str = "off",
73
+ target_length: Optional[str] = None,
74
+ captions: str = "srt",
75
+ provider: str = "auto",
76
+ use_llm: bool = True,
77
+ force: bool = False,
78
+ verbose: bool = False
79
+ ) -> VideoEditResult:
80
+ """
81
+ Edit a video using AI-powered analysis.
82
+
83
+ Args:
84
+ input_path: Path to input video file
85
+ output_path: Path for output video (default: input_edited.mp4)
86
+ preset: Edit preset (podcast, meeting, course, clean)
87
+ workdir: Working directory for temp files
88
+ remove_fillers: Remove filler words (overrides preset)
89
+ remove_repetitions: Remove repeated phrases (overrides preset)
90
+ remove_tangents: Remove off-topic content (overrides preset)
91
+ remove_silence: Remove long silences (overrides preset)
92
+ auto_crop: Cropping mode (off, center, face)
93
+ target_length: Target output duration (e.g., "6m", "90s")
94
+ captions: Caption mode (off, srt, burn)
95
+ provider: Transcription provider (openai, local, auto)
96
+ use_llm: Use LLM for content analysis (False = simple pattern matching)
97
+ force: Overwrite output if exists
98
+ verbose: Print progress messages
99
+
100
+ Returns:
101
+ VideoEditResult with paths to output files and metadata
102
+ """
103
+ def _log(message: str):
104
+ if verbose:
105
+ print(f"[AI Video Editor] {message}")
106
+
107
+ # Validate input
108
+ if not os.path.exists(input_path):
109
+ raise FileNotFoundError(f"Input video not found: {input_path}")
110
+
111
+ # Check ffmpeg
112
+ available, msg = check_ffmpeg()
113
+ if not available:
114
+ raise RuntimeError(f"FFmpeg is required: {msg}")
115
+
116
+ _log(f"Starting edit of: {input_path}")
117
+
118
+ # Set up paths
119
+ input_path = str(Path(input_path).absolute())
120
+
121
+ if output_path is None:
122
+ input_stem = Path(input_path).stem
123
+ output_path = str(Path(input_path).parent / f"{input_stem}_edited.mp4")
124
+
125
+ output_path = str(Path(output_path).absolute())
126
+
127
+ # Create working directory
128
+ if workdir is None:
129
+ work_path = create_workdir(input_path)
130
+ else:
131
+ work_path = Path(workdir)
132
+ work_path.mkdir(parents=True, exist_ok=True)
133
+
134
+ _log(f"Working directory: {work_path}")
135
+
136
+ # Build configuration from preset + overrides
137
+ config = _build_config(
138
+ preset=preset,
139
+ remove_fillers=remove_fillers,
140
+ remove_repetitions=remove_repetitions,
141
+ remove_tangents=remove_tangents,
142
+ remove_silence=remove_silence,
143
+ target_length=target_length
144
+ )
145
+
146
+ # Save config snapshot
147
+ config_snapshot = {
148
+ "input_path": input_path,
149
+ "input_hash": file_hash(input_path),
150
+ "preset": preset,
151
+ "config": config,
152
+ "auto_crop": auto_crop,
153
+ "captions": captions,
154
+ "provider": provider,
155
+ "use_llm": use_llm
156
+ }
157
+ save_config_snapshot(config_snapshot, work_path)
158
+
159
+ # Step 1: Probe video
160
+ _log("Probing video...")
161
+ video_probe = probe_video(input_path)
162
+ _log(f"Duration: {format_duration(video_probe.duration)}, "
163
+ f"Resolution: {video_probe.width}x{video_probe.height}")
164
+
165
+ # Step 2: Transcribe
166
+ _log("Transcribing audio...")
167
+ transcript_result = transcribe_video(
168
+ input_path,
169
+ provider=provider,
170
+ workdir=str(work_path)
171
+ )
172
+ _log(f"Transcribed {len(transcript_result.words)} words")
173
+
174
+ # Save transcript
175
+ transcript_path = work_path / "output" / "transcript.txt"
176
+ transcript_path.parent.mkdir(exist_ok=True)
177
+ transcript_path.write_text(transcript_result.text)
178
+
179
+ srt_path = work_path / "output" / "captions.srt"
180
+ transcript_result.to_srt(srt_path)
181
+
182
+ # Step 3: Analyze content and create edit plan
183
+ _log("Analyzing content...")
184
+ if use_llm:
185
+ edit_plan = analyze_content(transcript_result, config)
186
+ else:
187
+ edit_plan = create_simple_edit_plan(transcript_result, config)
188
+
189
+ _log(f"Found {len(edit_plan.segments_to_remove)} segments to remove")
190
+
191
+ # Step 4: Optimize timeline
192
+ _log("Optimizing timeline...")
193
+ edit_plan = optimize_timeline(edit_plan, transcript_result, config)
194
+
195
+ # Step 5: Render
196
+ _log("Rendering video...")
197
+ render_video(
198
+ input_path=input_path,
199
+ output_path=output_path,
200
+ edit_plan=edit_plan,
201
+ probe=video_probe,
202
+ workdir=work_path,
203
+ crop_mode=auto_crop,
204
+ normalize_audio=True,
205
+ burn_captions=(captions == "burn"),
206
+ srt_path=str(srt_path) if captions == "burn" else None,
207
+ force=force
208
+ )
209
+
210
+ # Step 6: Generate reports
211
+ _log("Generating reports...")
212
+
213
+ # EDL file
214
+ edl_path = work_path / "output" / "edit_decision_list.edl"
215
+ generate_edl(
216
+ segments_to_keep=[(s.start, s.end) for s in edit_plan.segments_to_keep],
217
+ segments_to_remove=[(s.start, s.end, s.reason) for s in edit_plan.segments_to_remove],
218
+ output_path=str(edl_path),
219
+ fps=video_probe.fps
220
+ )
221
+
222
+ # Edit plan JSON
223
+ plan_path = work_path / "output" / "edit_plan.json"
224
+ edit_plan.to_json(plan_path)
225
+
226
+ # Calculate final duration
227
+ final_duration = calculate_final_duration(edit_plan)
228
+ time_saved = video_probe.duration - final_duration
229
+
230
+ # Build result
231
+ result = VideoEditResult(
232
+ output_path=output_path,
233
+ report_path=str(plan_path),
234
+ transcript_path=str(transcript_path),
235
+ srt_path=str(srt_path),
236
+ edl_path=str(edl_path),
237
+ original_duration=video_probe.duration,
238
+ final_duration=final_duration,
239
+ time_saved=time_saved,
240
+ edit_plan=edit_plan,
241
+ probe=video_probe,
242
+ transcript=transcript_result,
243
+ workdir=str(work_path),
244
+ config_snapshot=config_snapshot
245
+ )
246
+
247
+ # Save full report
248
+ report_path = work_path / "output" / "report.json"
249
+ result.to_json(report_path)
250
+
251
+ _log("Edit complete!")
252
+ _log(f"Original: {format_duration(video_probe.duration)}")
253
+ _log(f"Final: {format_duration(final_duration)}")
254
+ _log(f"Saved: {format_duration(time_saved)} ({(time_saved/video_probe.duration)*100:.1f}%)")
255
+ _log(f"Output: {output_path}")
256
+
257
+ return result
258
+
259
+
260
+ def _build_config(
261
+ preset: str,
262
+ remove_fillers: Optional[bool],
263
+ remove_repetitions: Optional[bool],
264
+ remove_tangents: Optional[bool],
265
+ remove_silence: Optional[bool],
266
+ target_length: Optional[str]
267
+ ) -> dict:
268
+ """Build configuration from preset and overrides."""
269
+
270
+ if preset not in PRESETS:
271
+ raise ValueError(f"Unknown preset: {preset}. Available: {list(PRESETS.keys())}")
272
+
273
+ config = PRESETS[preset].copy()
274
+
275
+ if remove_fillers is not None:
276
+ config["remove_fillers"] = remove_fillers
277
+ if remove_repetitions is not None:
278
+ config["remove_repetitions"] = remove_repetitions
279
+ if remove_tangents is not None:
280
+ config["remove_tangents"] = remove_tangents
281
+ if remove_silence is not None:
282
+ config["remove_silence"] = remove_silence
283
+
284
+ if target_length:
285
+ config["target_length"] = parse_duration(target_length)
286
+
287
+ return config
@@ -0,0 +1,274 @@
1
+ """
2
+ Video rendering using FFmpeg.
3
+
4
+ Handles:
5
+ - Segment concatenation
6
+ - Cropping and scaling
7
+ - Audio normalization
8
+ - Caption burning
9
+ """
10
+
11
+ import os
12
+ import subprocess
13
+ from pathlib import Path
14
+ from typing import List, Literal, Tuple
15
+
16
+ from .models import EditPlan, VideoProbeResult
17
+ from .timeline import get_keep_intervals
18
+ from .utils import ensure_ffmpeg
19
+
20
+
21
+ def render(
22
+ input_path: str,
23
+ output_path: str,
24
+ edit_plan: EditPlan,
25
+ probe: VideoProbeResult,
26
+ workdir: Path,
27
+ crop_mode: Literal["off", "center", "face"] = "off",
28
+ normalize_audio: bool = False,
29
+ burn_captions: bool = False,
30
+ srt_path: str = None,
31
+ force: bool = False
32
+ ) -> str:
33
+ """
34
+ Render edited video.
35
+
36
+ Args:
37
+ input_path: Path to input video
38
+ output_path: Path for output video
39
+ edit_plan: Edit plan with segments to keep
40
+ probe: Video probe result
41
+ workdir: Working directory
42
+ crop_mode: Cropping mode (off, center, face)
43
+ normalize_audio: Normalize audio loudness
44
+ burn_captions: Burn captions into video
45
+ srt_path: Path to SRT file for captions
46
+ force: Overwrite output if exists
47
+
48
+ Returns:
49
+ Path to rendered video
50
+ """
51
+ ensure_ffmpeg()
52
+
53
+ if os.path.exists(output_path) and not force:
54
+ raise FileExistsError(
55
+ f"Output file already exists: {output_path}. "
56
+ "Use force=True to overwrite."
57
+ )
58
+
59
+ intervals = get_keep_intervals(edit_plan)
60
+
61
+ if not intervals:
62
+ raise ValueError("No segments to keep in edit plan")
63
+
64
+ Path(output_path).parent.mkdir(parents=True, exist_ok=True)
65
+
66
+ if len(intervals) <= 10:
67
+ return _render_filter_complex(
68
+ input_path, output_path, intervals, probe,
69
+ crop_mode, normalize_audio, burn_captions, srt_path
70
+ )
71
+ else:
72
+ return _render_concat_demuxer(
73
+ input_path, output_path, intervals, probe, workdir,
74
+ crop_mode, normalize_audio, burn_captions, srt_path
75
+ )
76
+
77
+
78
+ def _render_filter_complex(
79
+ input_path: str,
80
+ output_path: str,
81
+ intervals: List[Tuple[float, float]],
82
+ probe: VideoProbeResult,
83
+ crop_mode: str,
84
+ normalize_audio: bool,
85
+ burn_captions: bool,
86
+ srt_path: str
87
+ ) -> str:
88
+ """Render using filter_complex (efficient for few segments)."""
89
+
90
+ filter_parts = []
91
+ n = len(intervals)
92
+
93
+ for i, (start, end) in enumerate(intervals):
94
+ duration = end - start
95
+ filter_parts.append(
96
+ f"[0:v]trim=start={start}:duration={duration},setpts=PTS-STARTPTS[v{i}]"
97
+ )
98
+ filter_parts.append(
99
+ f"[0:a]atrim=start={start}:duration={duration},asetpts=PTS-STARTPTS[a{i}]"
100
+ )
101
+
102
+ video_inputs = "".join(f"[v{i}]" for i in range(n))
103
+ filter_parts.append(f"{video_inputs}concat=n={n}:v=1:a=0[vconcat]")
104
+
105
+ audio_inputs = "".join(f"[a{i}]" for i in range(n))
106
+ filter_parts.append(f"{audio_inputs}concat=n={n}:v=0:a=1[aconcat]")
107
+
108
+ video_out = "[vconcat]"
109
+ if crop_mode == "center":
110
+ target_ratio = 9 / 16
111
+ current_ratio = probe.width / probe.height
112
+
113
+ if current_ratio > target_ratio:
114
+ new_width = int(probe.height * target_ratio)
115
+ crop_x = (probe.width - new_width) // 2
116
+ filter_parts.append(f"[vconcat]crop={new_width}:{probe.height}:{crop_x}:0[vcrop]")
117
+ video_out = "[vcrop]"
118
+
119
+ audio_out = "[aconcat]"
120
+ if normalize_audio:
121
+ filter_parts.append("[aconcat]loudnorm=I=-16:TP=-1.5:LRA=11[anorm]")
122
+ audio_out = "[anorm]"
123
+
124
+ filter_complex = ";".join(filter_parts)
125
+
126
+ cmd = [
127
+ "ffmpeg", "-y",
128
+ "-i", input_path,
129
+ ]
130
+
131
+ if burn_captions and srt_path and os.path.exists(srt_path):
132
+ filter_complex += f";{video_out}subtitles='{srt_path}'[vfinal]"
133
+ video_out = "[vfinal]"
134
+
135
+ cmd.extend([
136
+ "-filter_complex", filter_complex,
137
+ "-map", video_out,
138
+ "-map", audio_out,
139
+ "-c:v", "libx264",
140
+ "-preset", "medium",
141
+ "-crf", "23",
142
+ "-c:a", "aac",
143
+ "-b:a", "192k",
144
+ output_path
145
+ ])
146
+
147
+ result = subprocess.run(cmd, capture_output=True, text=True)
148
+
149
+ if result.returncode != 0:
150
+ raise RuntimeError(f"FFmpeg render failed: {result.stderr}")
151
+
152
+ return output_path
153
+
154
+
155
+ def _render_concat_demuxer(
156
+ input_path: str,
157
+ output_path: str,
158
+ intervals: List[Tuple[float, float]],
159
+ probe: VideoProbeResult,
160
+ workdir: Path,
161
+ crop_mode: str,
162
+ normalize_audio: bool,
163
+ burn_captions: bool,
164
+ srt_path: str
165
+ ) -> str:
166
+ """Render using concat demuxer (efficient for many segments)."""
167
+
168
+ segments_dir = workdir / "segments"
169
+ segments_dir.mkdir(exist_ok=True)
170
+
171
+ segment_files = []
172
+
173
+ for i, (start, end) in enumerate(intervals):
174
+ seg_path = segments_dir / f"seg_{i:04d}.mp4"
175
+ duration = end - start
176
+
177
+ cmd = [
178
+ "ffmpeg", "-y",
179
+ "-ss", str(start),
180
+ "-i", input_path,
181
+ "-t", str(duration),
182
+ "-c", "copy",
183
+ "-avoid_negative_ts", "make_zero",
184
+ str(seg_path)
185
+ ]
186
+
187
+ result = subprocess.run(cmd, capture_output=True, text=True)
188
+ if result.returncode != 0:
189
+ raise RuntimeError(f"Failed to extract segment {i}: {result.stderr}")
190
+
191
+ segment_files.append(seg_path)
192
+
193
+ concat_file = workdir / "concat.txt"
194
+ with open(concat_file, "w") as f:
195
+ for seg_path in segment_files:
196
+ f.write(f"file '{seg_path}'\n")
197
+
198
+ temp_output = workdir / "temp_concat.mp4"
199
+
200
+ cmd = [
201
+ "ffmpeg", "-y",
202
+ "-f", "concat",
203
+ "-safe", "0",
204
+ "-i", str(concat_file),
205
+ "-c", "copy",
206
+ str(temp_output)
207
+ ]
208
+
209
+ result = subprocess.run(cmd, capture_output=True, text=True)
210
+ if result.returncode != 0:
211
+ raise RuntimeError(f"Failed to concatenate segments: {result.stderr}")
212
+
213
+ if crop_mode != "off" or normalize_audio or burn_captions:
214
+ return _apply_post_processing(
215
+ str(temp_output), output_path, probe,
216
+ crop_mode, normalize_audio, burn_captions, srt_path
217
+ )
218
+ else:
219
+ import shutil
220
+ shutil.move(str(temp_output), output_path)
221
+ return output_path
222
+
223
+
224
+ def _apply_post_processing(
225
+ input_path: str,
226
+ output_path: str,
227
+ probe: VideoProbeResult,
228
+ crop_mode: str,
229
+ normalize_audio: bool,
230
+ burn_captions: bool,
231
+ srt_path: str
232
+ ) -> str:
233
+ """Apply post-processing filters."""
234
+
235
+ filters_v = []
236
+ filters_a = []
237
+
238
+ if crop_mode == "center":
239
+ target_ratio = 9 / 16
240
+ current_ratio = probe.width / probe.height
241
+
242
+ if current_ratio > target_ratio:
243
+ new_width = int(probe.height * target_ratio)
244
+ crop_x = (probe.width - new_width) // 2
245
+ filters_v.append(f"crop={new_width}:{probe.height}:{crop_x}:0")
246
+
247
+ if burn_captions and srt_path and os.path.exists(srt_path):
248
+ filters_v.append(f"subtitles='{srt_path}'")
249
+
250
+ if normalize_audio:
251
+ filters_a.append("loudnorm=I=-16:TP=-1.5:LRA=11")
252
+
253
+ cmd = ["ffmpeg", "-y", "-i", input_path]
254
+
255
+ if filters_v:
256
+ cmd.extend(["-vf", ",".join(filters_v)])
257
+
258
+ if filters_a:
259
+ cmd.extend(["-af", ",".join(filters_a)])
260
+
261
+ cmd.extend([
262
+ "-c:v", "libx264",
263
+ "-preset", "medium",
264
+ "-crf", "23",
265
+ "-c:a", "aac",
266
+ "-b:a", "192k",
267
+ output_path
268
+ ])
269
+
270
+ result = subprocess.run(cmd, capture_output=True, text=True)
271
+ if result.returncode != 0:
272
+ raise RuntimeError(f"Post-processing failed: {result.stderr}")
273
+
274
+ return output_path