mdengine 0.2.2__tar.gz → 0.4.0__tar.gz

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 (181) hide show
  1. {mdengine-0.2.2 → mdengine-0.4.0}/PKG-INFO +179 -6
  2. {mdengine-0.2.2 → mdengine-0.4.0}/README.md +152 -3
  3. {mdengine-0.2.2 → mdengine-0.4.0}/pyproject.toml +44 -3
  4. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/__init__.py +1 -1
  5. mdengine-0.4.0/src/md_generator/media/__init__.py +21 -0
  6. mdengine-0.4.0/src/md_generator/media/audio/__init__.py +18 -0
  7. mdengine-0.4.0/src/md_generator/media/audio/api/__init__.py +1 -0
  8. mdengine-0.4.0/src/md_generator/media/audio/api/main.py +175 -0
  9. mdengine-0.4.0/src/md_generator/media/audio/api/mcp_server.py +38 -0
  10. mdengine-0.4.0/src/md_generator/media/audio/api/mcp_setup.py +84 -0
  11. mdengine-0.4.0/src/md_generator/media/audio/api/run.py +30 -0
  12. mdengine-0.4.0/src/md_generator/media/audio/api/settings.py +26 -0
  13. mdengine-0.4.0/src/md_generator/media/audio/converter.py +178 -0
  14. mdengine-0.4.0/src/md_generator/media/audio/formatter.py +70 -0
  15. mdengine-0.4.0/src/md_generator/media/audio/service.py +52 -0
  16. mdengine-0.4.0/src/md_generator/media/document_converter.py +239 -0
  17. mdengine-0.4.0/src/md_generator/media/job_store.py +94 -0
  18. mdengine-0.4.0/src/md_generator/media/video/__init__.py +9 -0
  19. mdengine-0.4.0/src/md_generator/media/video/api/__init__.py +1 -0
  20. mdengine-0.4.0/src/md_generator/media/video/api/main.py +174 -0
  21. mdengine-0.4.0/src/md_generator/media/video/api/mcp_server.py +38 -0
  22. mdengine-0.4.0/src/md_generator/media/video/api/mcp_setup.py +84 -0
  23. mdengine-0.4.0/src/md_generator/media/video/api/run.py +30 -0
  24. mdengine-0.4.0/src/md_generator/media/video/api/settings.py +26 -0
  25. mdengine-0.4.0/src/md_generator/media/video/converter.py +111 -0
  26. mdengine-0.4.0/src/md_generator/media/video/formatter.py +81 -0
  27. mdengine-0.4.0/src/md_generator/media/video/service.py +79 -0
  28. mdengine-0.4.0/src/md_generator/media/whisper_language.py +78 -0
  29. mdengine-0.4.0/src/md_generator/media/youtube/__init__.py +34 -0
  30. mdengine-0.4.0/src/md_generator/media/youtube/api/__init__.py +1 -0
  31. mdengine-0.4.0/src/md_generator/media/youtube/api/main.py +140 -0
  32. mdengine-0.4.0/src/md_generator/media/youtube/api/mcp_server.py +31 -0
  33. mdengine-0.4.0/src/md_generator/media/youtube/api/mcp_setup.py +48 -0
  34. mdengine-0.4.0/src/md_generator/media/youtube/api/run.py +30 -0
  35. mdengine-0.4.0/src/md_generator/media/youtube/api/settings.py +24 -0
  36. mdengine-0.4.0/src/md_generator/media/youtube/converter.py +140 -0
  37. mdengine-0.4.0/src/md_generator/media/youtube/formatter.py +58 -0
  38. mdengine-0.4.0/src/md_generator/media/youtube/metadata.py +210 -0
  39. mdengine-0.4.0/src/md_generator/media/youtube/service.py +200 -0
  40. mdengine-0.4.0/src/md_generator/media/youtube/transcript.py +100 -0
  41. mdengine-0.4.0/src/md_generator/playwright/__init__.py +6 -0
  42. mdengine-0.4.0/src/md_generator/playwright/api/__init__.py +1 -0
  43. mdengine-0.4.0/src/md_generator/playwright/api/convert_runner.py +71 -0
  44. mdengine-0.4.0/src/md_generator/playwright/api/jobs.py +92 -0
  45. mdengine-0.4.0/src/md_generator/playwright/api/main.py +295 -0
  46. mdengine-0.4.0/src/md_generator/playwright/api/mcp_server.py +37 -0
  47. mdengine-0.4.0/src/md_generator/playwright/api/mcp_setup.py +58 -0
  48. mdengine-0.4.0/src/md_generator/playwright/api/run.py +29 -0
  49. mdengine-0.4.0/src/md_generator/playwright/api/settings.py +26 -0
  50. mdengine-0.4.0/src/md_generator/playwright/assets.py +119 -0
  51. mdengine-0.4.0/src/md_generator/playwright/chunker.py +53 -0
  52. mdengine-0.4.0/src/md_generator/playwright/cli.py +131 -0
  53. mdengine-0.4.0/src/md_generator/playwright/extractor.py +24 -0
  54. mdengine-0.4.0/src/md_generator/playwright/html_to_md.py +25 -0
  55. mdengine-0.4.0/src/md_generator/playwright/options.py +35 -0
  56. mdengine-0.4.0/src/md_generator/playwright/pipeline.py +65 -0
  57. mdengine-0.4.0/src/md_generator/playwright/playwright_fetcher.py +107 -0
  58. {mdengine-0.2.2 → mdengine-0.4.0}/src/mdengine.egg-info/PKG-INFO +179 -6
  59. {mdengine-0.2.2 → mdengine-0.4.0}/src/mdengine.egg-info/SOURCES.txt +53 -0
  60. mdengine-0.4.0/src/mdengine.egg-info/entry_points.txt +21 -0
  61. {mdengine-0.2.2 → mdengine-0.4.0}/src/mdengine.egg-info/requires.txt +28 -0
  62. mdengine-0.2.2/src/mdengine.egg-info/entry_points.txt +0 -9
  63. {mdengine-0.2.2 → mdengine-0.4.0}/LICENSE +0 -0
  64. {mdengine-0.2.2 → mdengine-0.4.0}/setup.cfg +0 -0
  65. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/archive/__init__.py +0 -0
  66. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/archive/api/__init__.py +0 -0
  67. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/archive/api/convert_runner.py +0 -0
  68. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/archive/api/jobs.py +0 -0
  69. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/archive/api/main.py +0 -0
  70. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/archive/api/mcp_server.py +0 -0
  71. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/archive/api/mcp_setup.py +0 -0
  72. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/archive/api/query_options.py +0 -0
  73. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/archive/api/settings.py +0 -0
  74. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/archive/convert_impl.py +0 -0
  75. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/archive/converter.py +0 -0
  76. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/archive/options.py +0 -0
  77. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/__init__.py +0 -0
  78. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/api/__init__.py +0 -0
  79. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/api/main.py +0 -0
  80. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/api/mcp_server.py +0 -0
  81. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/api/query_options.py +0 -0
  82. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/api/settings.py +0 -0
  83. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/api/staging.py +0 -0
  84. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/api/zip_bundle.py +0 -0
  85. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/backends/__init__.py +0 -0
  86. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/backends/base.py +0 -0
  87. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/backends/easy.py +0 -0
  88. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/backends/paddle.py +0 -0
  89. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/backends/tesseract.py +0 -0
  90. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/convert_impl.py +0 -0
  91. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/converter.py +0 -0
  92. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/emit.py +0 -0
  93. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/io_util.py +0 -0
  94. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/image/utils.py +0 -0
  95. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/pdf/__init__.py +0 -0
  96. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/pdf/api/__init__.py +0 -0
  97. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/pdf/api/main.py +0 -0
  98. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/pdf/api/mcp_server.py +0 -0
  99. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/pdf/api/settings.py +0 -0
  100. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/pdf/api/zip_bundle.py +0 -0
  101. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/pdf/converter.py +0 -0
  102. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/pdf/md_emit.py +0 -0
  103. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/pdf/pdf_extract.py +0 -0
  104. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/pdf/utils.py +0 -0
  105. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/__init__.py +0 -0
  106. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/api/__init__.py +0 -0
  107. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/api/convert_runner.py +0 -0
  108. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/api/jobs.py +0 -0
  109. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/api/main.py +0 -0
  110. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/api/mcp_server.py +0 -0
  111. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/api/mcp_setup.py +0 -0
  112. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/api/query_options.py +0 -0
  113. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/api/settings.py +0 -0
  114. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/convert_impl.py +0 -0
  115. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/converter.py +0 -0
  116. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/embedded_extract.py +0 -0
  117. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/ooxml_media.py +0 -0
  118. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/options.py +0 -0
  119. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/post_assets.py +0 -0
  120. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/text_formatting.py +0 -0
  121. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/vendor_pdf_md/__init__.py +0 -0
  122. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/vendor_pdf_md/convert.py +0 -0
  123. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/vendor_word_md/__init__.py +0 -0
  124. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/vendor_word_md/convert.py +0 -0
  125. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/ppt/zip_deep.py +0 -0
  126. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/text/__init__.py +0 -0
  127. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/text/api/__init__.py +0 -0
  128. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/text/api/convert_runner.py +0 -0
  129. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/text/api/jobs.py +0 -0
  130. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/text/api/main.py +0 -0
  131. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/text/api/mcp_server.py +0 -0
  132. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/text/api/mcp_setup.py +0 -0
  133. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/text/api/query_options.py +0 -0
  134. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/text/api/settings.py +0 -0
  135. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/text/convert_impl.py +0 -0
  136. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/text/converter.py +0 -0
  137. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/text/format_detect.py +0 -0
  138. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/text/md_emit_json.py +0 -0
  139. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/text/md_emit_txt.py +0 -0
  140. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/text/md_emit_xml.py +0 -0
  141. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/text/options.py +0 -0
  142. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/__init__.py +0 -0
  143. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/api/__init__.py +0 -0
  144. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/api/convert_runner.py +0 -0
  145. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/api/jobs.py +0 -0
  146. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/api/main.py +0 -0
  147. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/api/mcp_server.py +0 -0
  148. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/api/mcp_setup.py +0 -0
  149. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/api/query_options.py +0 -0
  150. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/api/settings.py +0 -0
  151. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/assets.py +0 -0
  152. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/convert_impl.py +0 -0
  153. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/converter.py +0 -0
  154. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/crawl.py +0 -0
  155. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/extract.py +0 -0
  156. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/fetch.py +0 -0
  157. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/html_to_md.py +0 -0
  158. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/options.py +0 -0
  159. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/page_convert.py +0 -0
  160. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/post_convert_assets.py +0 -0
  161. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/url/robots_util.py +0 -0
  162. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/word/__init__.py +0 -0
  163. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/word/api/__init__.py +0 -0
  164. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/word/api/convert_util.py +0 -0
  165. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/word/api/jobs.py +0 -0
  166. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/word/api/main.py +0 -0
  167. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/word/api/mcp_server.py +0 -0
  168. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/word/artifact.py +0 -0
  169. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/word/converter.py +0 -0
  170. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/word/settings.py +0 -0
  171. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/xlsx/__init__.py +0 -0
  172. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/xlsx/api/__init__.py +0 -0
  173. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/xlsx/api/app.py +0 -0
  174. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/xlsx/convert_config.py +0 -0
  175. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/xlsx/converter.py +0 -0
  176. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/xlsx/converter_core.py +0 -0
  177. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/xlsx/excel_reader.py +0 -0
  178. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/xlsx/markdown_emitter.py +0 -0
  179. {mdengine-0.2.2 → mdengine-0.4.0}/src/md_generator/xlsx/mcp_server.py +0 -0
  180. {mdengine-0.2.2 → mdengine-0.4.0}/src/mdengine.egg-info/dependency_links.txt +0 -0
  181. {mdengine-0.2.2 → mdengine-0.4.0}/src/mdengine.egg-info/top_level.txt +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdengine
3
- Version: 0.2.2
4
- Summary: Convert PDF, Office, images, text/JSON/XML, ZIP archives, and web URLs to Markdown.
3
+ Version: 0.4.0
4
+ Summary: Convert PDF, Office, images, audio, video, text/JSON/XML, ZIP archives, and web URLs to Markdown.
5
5
  Author: mdengine contributors
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/vishal7090/md-generator
8
8
  Project-URL: Repository, https://github.com/vishal7090/md-generator
9
9
  Project-URL: Issues, https://github.com/vishal7090/md-generator/issues
10
- Keywords: markdown,pdf,docx,pptx,xlsx,ocr,zip,url,html
10
+ Keywords: markdown,pdf,docx,pptx,xlsx,ocr,zip,url,html,youtube,playwright
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3 :: Only
13
13
  Classifier: Topic :: Text Processing :: Markup :: Markdown
@@ -68,6 +68,25 @@ Requires-Dist: Pillow>=10.0.0; extra == "url-full"
68
68
  Requires-Dist: olefile>=0.47; extra == "url-full"
69
69
  Requires-Dist: openpyxl>=3.1.0; extra == "url-full"
70
70
  Requires-Dist: pytesseract>=0.3.10; extra == "url-full"
71
+ Provides-Extra: audio
72
+ Requires-Dist: openai-whisper>=20231117; extra == "audio"
73
+ Requires-Dist: imageio-ffmpeg>=0.5.1; extra == "audio"
74
+ Provides-Extra: video
75
+ Requires-Dist: openai-whisper>=20231117; extra == "video"
76
+ Requires-Dist: imageio-ffmpeg>=0.5.1; extra == "video"
77
+ Provides-Extra: youtube
78
+ Requires-Dist: youtube-transcript-api>=0.6.0; extra == "youtube"
79
+ Requires-Dist: httpx>=0.27.0; extra == "youtube"
80
+ Requires-Dist: beautifulsoup4>=4.12.0; extra == "youtube"
81
+ Requires-Dist: lxml>=5.0.0; extra == "youtube"
82
+ Provides-Extra: playwright
83
+ Requires-Dist: playwright>=1.49.0; extra == "playwright"
84
+ Requires-Dist: httpx>=0.27.0; extra == "playwright"
85
+ Requires-Dist: beautifulsoup4>=4.12.0; extra == "playwright"
86
+ Requires-Dist: lxml>=5.0.0; extra == "playwright"
87
+ Requires-Dist: markdownify>=0.14.0; extra == "playwright"
88
+ Requires-Dist: readability-lxml>=0.8.1; extra == "playwright"
89
+ Requires-Dist: Pillow>=10.0.0; extra == "playwright"
71
90
  Provides-Extra: api
72
91
  Requires-Dist: fastapi>=0.115.0; extra == "api"
73
92
  Requires-Dist: uvicorn[standard]>=0.32.0; extra == "api"
@@ -80,9 +99,13 @@ Requires-Dist: fastmcp>=2.3.0; extra == "mcp"
80
99
  Provides-Extra: dev
81
100
  Requires-Dist: pytest>=8.0.0; extra == "dev"
82
101
  Requires-Dist: httpx>=0.27.0; extra == "dev"
102
+ Requires-Dist: beautifulsoup4>=4.12.0; extra == "dev"
103
+ Requires-Dist: lxml>=5.0.0; extra == "dev"
104
+ Requires-Dist: youtube-transcript-api>=0.6.0; extra == "dev"
83
105
  Requires-Dist: mdengine[api]; extra == "dev"
84
106
  Requires-Dist: mdengine[mcp]; extra == "dev"
85
107
  Requires-Dist: mdengine[url]; extra == "dev"
108
+ Requires-Dist: mdengine[playwright]; extra == "dev"
86
109
  Provides-Extra: all
87
110
  Requires-Dist: pymupdf>=1.24.0; extra == "all"
88
111
  Requires-Dist: pdfplumber>=0.11.0; extra == "all"
@@ -108,18 +131,19 @@ Requires-Dist: fastmcp>=2.3.0; extra == "all"
108
131
  Requires-Dist: beautifulsoup4>=4.12.0; extra == "all"
109
132
  Requires-Dist: lxml_html_clean>=0.4.0; extra == "all"
110
133
  Requires-Dist: readability-lxml>=0.8.1; extra == "all"
134
+ Requires-Dist: youtube-transcript-api>=0.6.0; extra == "all"
111
135
  Dynamic: license-file
112
136
 
113
137
  # mdengine
114
138
 
115
- Single Python distribution for converting **PDF**, **Word (.docx)**, **PowerPoint (.pptx)**, **Excel (.xlsx/.xlsm)**, **images** (OCR), **plain text / JSON / XML**, and **ZIP archives** into **Markdown** (and related assets). Install only the extras you need; everything imports under the **`md_generator`** package.
139
+ Single Python distribution for converting **PDF**, **Word (.docx)**, **PowerPoint (.pptx)**, **Excel (.xlsx/.xlsm)**, **images** (OCR), **plain text / JSON / XML**, **ZIP archives**, and **audio / video** (Whisper transcription → Markdown) into **Markdown** (and related assets). Install only the extras you need; everything imports under the **`md_generator`** package.
116
140
 
117
141
  - **PyPI name:** `mdengine` (import package: `md_generator`)
118
142
  - **Source:** [github.com/vishal7090/md-generator](https://github.com/vishal7090/md-generator)
119
143
  - **Python:** 3.10+
120
144
  - **License:** [MIT](LICENSE)
121
145
 
122
- **Quick links:** [On a new computer](#on-a-new-computer) · [Command-line execution](#command-line-execution) · [Python library](#python-library) · [HTTP API](#http-api-fastapi) · [MCP](#mcp-model-context-protocol) · [Development](#development) · [Code of Conduct](CODE_OF_CONDUCT.md)
146
+ **Quick links:** [On a new computer](#on-a-new-computer) · [Command-line execution](#command-line-execution) · [Python library](#python-library) · [Audio and video](#audio-and-video-to-markdown) · [HTTP API](#http-api-fastapi) · [MCP](#mcp-model-context-protocol) · [Development](#development) · [Code of Conduct](CODE_OF_CONDUCT.md)
123
147
 
124
148
  ---
125
149
 
@@ -186,6 +210,8 @@ pip install "mdengine[all]"
186
210
  | `archive` | ZIP → Markdown layout (Pillow; optional tesseract for inline image OCR) |
187
211
  | `url` | HTTP(S) HTML → Markdown (httpx, readability-lxml, markdownify, BeautifulSoup, lxml) |
188
212
  | `url-full` | `url` plus PDF/Word/PPTX/XLSX/archive stack for **post-converting** downloaded linked files to Markdown |
213
+ | `audio` | **Audio → Markdown** via Whisper (`openai-whisper`); ships `imageio-ffmpeg` for a bundled **ffmpeg** when none is on `PATH` |
214
+ | `video` | **Video → Markdown** (ffmpeg extracts mono 16 kHz WAV, then same Whisper stack as `audio`) |
189
215
  | `api` | FastAPI, uvicorn, httpx, pydantic-settings |
190
216
  | `mcp` | MCP servers (`mcp`, `fastmcp` where used) |
191
217
  | `dev` | pytest + API/MCP test helpers |
@@ -228,6 +254,15 @@ If the shell reports “command not found”, ensure the Python **Scripts** dire
228
254
  | `md-text` | `md_generator.text.converter:main` | `md-text config.xml out.md` |
229
255
  | `md-zip` | `md_generator.archive.converter:main` | `md-zip bundle.zip ./zip-out` |
230
256
  | `md-url` | `md_generator.url.converter:main` | `md-url https://example.com/doc ./web-out --artifact-layout` |
257
+ | `md-audio` | `md_generator.media.audio.converter:main` | `md-audio clip.mp3 transcript.md --model base` |
258
+ | `md-video` | `md_generator.media.video.converter:main` | `md-video clip.mp4 transcript.md --model base` |
259
+ | `md-youtube` | `md_generator.media.youtube.converter:main` | `md-youtube "https://youtu.be/…" out.md --transcript-lang en` |
260
+ | `md-audio-api` | `md_generator.media.audio.api.run:main` | REST + MCP on port **8011** (see [Audio and video to Markdown](#audio-and-video-to-markdown)) |
261
+ | `md-video-api` | `md_generator.media.video.api.run:main` | REST + MCP on port **8012** |
262
+ | `md-youtube-api` | `md_generator.media.youtube.api.run:main` | REST + MCP on port **8013** (JSON `url` body; see same section) |
263
+ | `md-audio-mcp` | `md_generator.media.audio.api.mcp_server:main` | Standalone MCP (`--transport stdio` \| `sse` \| `streamable-http`) |
264
+ | `md-video-mcp` | `md_generator.media.video.api.mcp_server:main` | Same for video |
265
+ | `md-youtube-mcp` | `md_generator.media.youtube.api.mcp_server:main` | Same for YouTube (`youtube_url_to_markdown`) |
231
266
 
232
267
  Every command accepts **`-h` / `--help`** for full flags (artifact layout, OCR, ZIP options, etc.).
233
268
 
@@ -245,6 +280,8 @@ md-image ./photos ocr.md --engines tess --strategy best
245
280
  md-text data.json data.md
246
281
  md-zip archive.zip ./unzipped-md
247
282
  md-url https://example.com/page ./page-bundle --artifact-layout
283
+ md-audio ./voice.mp3 ./voice.md --model tiny
284
+ md-video ./screen.mp4 ./screen.md --model base
248
285
  ```
249
286
 
250
287
  **Windows PowerShell** (same commands; use backslashes for paths if you prefer)
@@ -253,6 +290,8 @@ md-url https://example.com/page ./page-bundle --artifact-layout
253
290
  md-pdf .\manual.pdf .\out\doc.md
254
291
  md-zip .\archive.zip .\zip-out
255
292
  md-url https://example.com/page .\page-bundle --artifact-layout
293
+ md-audio .\voice.mp3 .\voice.md --model tiny
294
+ md-video .\screen.mp4 .\screen.md --model base
256
295
  ```
257
296
 
258
297
  **Windows CMD**
@@ -422,6 +461,124 @@ convert_zip(
422
461
 
423
462
  ---
424
463
 
464
+ ## Audio and video to Markdown
465
+
466
+ Library code lives under **`md_generator.media`**: shared probing in [`document_converter.py`](src/md_generator/media/document_converter.py), **audio** in [`media/audio/`](src/md_generator/media/audio/) (Whisper + ffprobe / ffmpeg metadata), **video** in [`media/video/`](src/md_generator/media/video/) (ffmpeg extracts audio only; transcription always goes through the audio service—no duplicate Whisper path in video).
467
+
468
+ ### System requirements
469
+
470
+ - **ffmpeg** (and **ffprobe** when available) on `PATH` for metadata and for **video** demux. If `ffprobe` is missing or misbehaving, metadata falls back to parsing `ffmpeg -i` stderr.
471
+ - Optional **`FFMPEG`** environment variable: absolute path to an `ffmpeg` executable (see [`resolve_ffmpeg_executable()`](src/md_generator/media/document_converter.py)).
472
+ - **GPU** is optional; Whisper runs on CPU if needed (may log FP16→FP32 on CPU).
473
+
474
+ ### Install
475
+
476
+ ```bash
477
+ pip install "mdengine[audio,api,mcp]" # audio CLI + HTTP + MCP
478
+ pip install "mdengine[video,api,mcp]" # video CLI + HTTP + MCP (same ML stack as audio)
479
+ pip install "mdengine[youtube,api,mcp]" # YouTube URL → Markdown (captions + metadata; optional Whisper fallback)
480
+ ```
481
+
482
+ ### Python library
483
+
484
+ **Audio** — structured result + Markdown:
485
+
486
+ ```python
487
+ from pathlib import Path
488
+ from md_generator.media.audio import AudioToMarkdownService, AudioConverter
489
+
490
+ svc = AudioToMarkdownService(whisper_model="base") # language omitted → Whisper auto-detect; pass e.g. language="en" to force
491
+ text = svc.to_markdown(Path("input.mp3"), title="My title")
492
+ svc.write_markdown(Path("input.mp3"), Path("out/transcript.md"))
493
+
494
+ result = svc.transcribe(Path("input.wav")) # metadata + segments + plain_text
495
+ ```
496
+
497
+ **Video** — extract → transcribe (via audio) → Markdown:
498
+
499
+ ```python
500
+ from pathlib import Path
501
+ from md_generator.media.video import VideoToMarkdownService
502
+
503
+ svc = VideoToMarkdownService(whisper_model="base") # transcription language omitted → auto-detect
504
+ md = svc.to_markdown(Path("input.mp4"), title=None)
505
+ svc.write_markdown(Path("input.mp4"), Path("out/transcript.md"))
506
+ ```
507
+
508
+ **YouTube** — captions API + page metadata (BeautifulSoup / oEmbed); optional **yt-dlp + Whisper** when captions are missing (`mdengine[audio]` and `yt-dlp` on `PATH`, or `MD_YOUTUBE_YTDLP`):
509
+
510
+ ```python
511
+ from md_generator.media.youtube import YouTubeToMarkdownService
512
+
513
+ svc = YouTubeToMarkdownService(whisper_model="base")
514
+ md = svc.to_markdown("https://www.youtube.com/watch?v=VIDEO_ID", transcript_languages=["en"])
515
+ svc.write_markdown("https://youtu.be/VIDEO_ID", Path("out/youtube.md"))
516
+ ```
517
+
518
+ For file-based pipelines, [`YouTubeConverter`](src/md_generator/media/youtube/converter.py) reads a `.url` / `.yturl` / `.youtube` file (or a `.txt` whose first non-comment line is a YouTube URL) and implements [`DocumentConverter`](src/md_generator/media/document_converter.py).
519
+
520
+ Public symbols are also re-exported from [`md_generator.media`](src/md_generator/media/__init__.py) for ffprobe helpers (`ffprobe_json`, `VideoProbeResult`, …).
521
+
522
+ ### REST API (FastAPI)
523
+
524
+ Each service exposes the same job pattern as other converters:
525
+
526
+ | Endpoint | Description |
527
+ |----------|-------------|
528
+ | `POST /convert/sync` | Multipart field **`file`**; returns **Markdown** body. Query: `whisper_model`, `language` (omit or `auto` for detection; pass a code to force), `title`. |
529
+ | `POST /convert/jobs` | Async upload; returns `{ "job_id", "status" }`. |
530
+ | `GET /convert/jobs/{job_id}` | Status JSON. |
531
+ | `GET /convert/jobs/{job_id}/download` | Markdown when `done`; workspace removed after download. |
532
+
533
+ **Audio** defaults: `MD_AUDIO_MAX_UPLOAD_MB=200`, `MD_AUDIO_MAX_SYNC_UPLOAD_MB=40`, `MD_AUDIO_API_PORT=8011`.
534
+ **Video** defaults: `MD_VIDEO_MAX_UPLOAD_MB=500`, `MD_VIDEO_MAX_SYNC_UPLOAD_MB=80`, `MD_VIDEO_API_PORT=8012`.
535
+ **YouTube** uses **JSON** (not multipart): `POST /convert/sync` and `POST /convert/jobs` accept `{"url":"https://www.youtube.com/watch?v=…","title":null,"transcript_languages":["en"],"enable_audio_fallback":true,"whisper_model":"base","language":null}`. Defaults: `MD_YOUTUBE_API_PORT=8013`, `MD_YOUTUBE_JOB_TTL_SECONDS`, `MD_YOUTUBE_CORS_ORIGINS`, `MD_YOUTUBE_TEMP_DIR`.
536
+
537
+ Run with the bundled runners (each call builds the app with **`factory=True`** for a clean MCP session manager):
538
+
539
+ ```bash
540
+ md-audio-api --host 127.0.0.1 --port 8011
541
+ md-video-api --host 127.0.0.1 --port 8012
542
+ md-youtube-api --host 127.0.0.1 --port 8013
543
+ ```
544
+
545
+ Or with Uvicorn directly (the ASGI app is built by **`create_app()`** so each worker gets its own MCP session manager):
546
+
547
+ ```bash
548
+ uvicorn md_generator.media.audio.api.main:create_app --factory --host 127.0.0.1 --port 8011
549
+ uvicorn md_generator.media.video.api.main:create_app --factory --host 127.0.0.1 --port 8012
550
+ uvicorn md_generator.media.youtube.api.main:create_app --factory --host 127.0.0.1 --port 8013
551
+ ```
552
+
553
+ The module also defines **`app = create_app()`** for a single-process target: `uvicorn md_generator.media.audio.api.main:app` (no `--factory`).
554
+
555
+ Swagger is at **`/docs`** when the app is running.
556
+
557
+ ### MCP (stdio, SSE, streamable HTTP)
558
+
559
+ 1. **With FastAPI** — start `md-audio-api`, `md-video-api`, or `md-youtube-api`; mount Streamable HTTP MCP at **`http://<host>:<port>/mcp`** (same host as REST).
560
+ 2. **Standalone** — process speaks MCP only:
561
+
562
+ ```bash
563
+ md-audio-mcp --transport stdio
564
+ md-audio-mcp --transport sse
565
+ md-audio-mcp --transport streamable-http
566
+ md-video-mcp --transport stdio
567
+ md-youtube-mcp --transport stdio
568
+ ```
569
+
570
+ **Audio MCP tools:** `transcribe_audio_path`, `transcribe_audio_base64`.
571
+ **Video MCP tools:** `transcribe_video_path`, `transcribe_video_base64`.
572
+ **YouTube MCP tool:** `youtube_url_to_markdown`.
573
+
574
+ Equivalent modules: `python -m md_generator.media.audio.api.mcp_server`, `python -m md_generator.media.video.api.mcp_server`, `python -m md_generator.media.youtube.api.mcp_server`.
575
+
576
+ ### Thin shims (repo clone)
577
+
578
+ [`audio-to-md/converter.py`](audio-to-md/converter.py), [`video-to-md/converter.py`](video-to-md/converter.py), and [`youtube-to-md/converter.py`](youtube-to-md/converter.py) delegate to the same `main` as `md-audio` / `md-video` / `md-youtube`. Tests and `pytest.ini` live under `audio-to-md/tests/`, `video-to-md/tests/`, and `youtube-to-md/tests/`.
579
+
580
+ ---
581
+
425
582
  ## HTTP API (FastAPI)
426
583
 
427
584
  All format APIs follow a similar pattern:
@@ -447,6 +604,10 @@ Install `mdengine[api]` plus the format extra(s), then run the **`app`** object
447
604
  | Text/JSON/XML | `md_generator.text.api.main:app` | `text`, `api`, `mcp` |
448
605
  | ZIP | `md_generator.archive.api.main:app` | `archive`, `api`, `mcp` (+ extras for nested office/PDF) |
449
606
  | URL / HTML | `md_generator.url.api.main:app` | `url`, `api`, `mcp` |
607
+ | Playwright / SPA | `md_generator.playwright.api.main:app` | `playwright`, `api`, `mcp` |
608
+ | Audio (Whisper) | `md_generator.media.audio.api.main:create_app` (use **`--factory`**) or `…main:app` | `audio`, `api`, `mcp` |
609
+ | Video (Whisper) | `md_generator.media.video.api.main:create_app` (use **`--factory`**) or `…main:app` | `video`, `api`, `mcp` |
610
+ | YouTube | `md_generator.media.youtube.api.main:create_app` (use **`--factory`**) or `…main:app` | `youtube`, `api`, `mcp` |
450
611
 
451
612
  Examples:
452
613
 
@@ -455,6 +616,10 @@ uvicorn md_generator.pdf.api.main:app --host 127.0.0.1 --port 8001
455
616
  uvicorn md_generator.word.api.main:app --host 127.0.0.1 --port 8002
456
617
  uvicorn md_generator.archive.api.main:app --host 127.0.0.1 --port 8010
457
618
  uvicorn md_generator.url.api.main:app --host 127.0.0.1 --port 8011
619
+ uvicorn md_generator.playwright.api.main:app --host 127.0.0.1 --port 8014
620
+ uvicorn md_generator.media.audio.api.main:create_app --factory --host 127.0.0.1 --port 8011
621
+ uvicorn md_generator.media.video.api.main:create_app --factory --host 127.0.0.1 --port 8012
622
+ uvicorn md_generator.media.youtube.api.main:create_app --factory --host 127.0.0.1 --port 8013
458
623
  ```
459
624
 
460
625
  ### MCP over HTTP on the same server
@@ -474,6 +639,10 @@ Prefixes differ per service (often read from a `.env` file next to the process):
474
639
  | Text | `TXT_JSON_XML_TO_MD_` | same pattern |
475
640
  | XLSX | `XLSX_TO_MD_` | `XLSX_TO_MD_TEMP_DIR`, `XLSX_TO_MD_CORS_ORIGINS`, etc. (see `md_generator.xlsx.api.app`) |
476
641
  | URL | `URL_TO_MD_` | `URL_TO_MD_MAX_SYNC_URLS`, `URL_TO_MD_MAX_SYNC_CRAWL_PAGES`, `URL_TO_MD_MAX_JOB_URLS`, `URL_TO_MD_JOB_TTL_SECONDS`, `URL_TO_MD_TEMP_DIR`, `URL_TO_MD_CORS_ORIGINS` |
642
+ | Playwright / SPA | `PLAYWRIGHT_TO_MD_` | `PLAYWRIGHT_TO_MD_MAX_SYNC_URLS`, `PLAYWRIGHT_TO_MD_MAX_JOB_URLS`, `PLAYWRIGHT_TO_MD_JOB_TTL_SECONDS`, `PLAYWRIGHT_TO_MD_TEMP_DIR`, `PLAYWRIGHT_TO_MD_CORS_ORIGINS`, `PLAYWRIGHT_TO_MD_API_HOST`, `PLAYWRIGHT_TO_MD_API_PORT` (default **8014**) |
643
+ | Audio API | `MD_AUDIO_` | `MD_AUDIO_MAX_UPLOAD_MB`, `MD_AUDIO_MAX_SYNC_UPLOAD_MB`, `MD_AUDIO_JOB_TTL_SECONDS`, `MD_AUDIO_TEMP_DIR`, `MD_AUDIO_CORS_ORIGINS`, `MD_AUDIO_API_HOST`, `MD_AUDIO_API_PORT` |
644
+ | Video API | `MD_VIDEO_` | Same pattern as audio with `MD_VIDEO_*` (defaults: larger upload/sync caps, port **8012**) |
645
+ | YouTube API | `MD_YOUTUBE_` | `MD_YOUTUBE_JOB_TTL_SECONDS`, `MD_YOUTUBE_TEMP_DIR`, `MD_YOUTUBE_CORS_ORIGINS`, `MD_YOUTUBE_API_HOST`, `MD_YOUTUBE_API_PORT` (default **8013**); optional `MD_YOUTUBE_YTDLP` path for audio fallback |
477
646
 
478
647
  Exact variable names match the `ApiSettings` / helper functions in each `api/settings` or `api/app` module.
479
648
 
@@ -497,6 +666,10 @@ Two usage patterns:
497
666
  | PPTX | `python -m md_generator.ppt.api.mcp_server` (see module docstring for flags) |
498
667
  | Image | `python -m md_generator.image.api.mcp_server` (see module for CLI) |
499
668
  | URL / HTML | `python -m md_generator.url.api.mcp_server` / `--transport sse` / `--transport streamable-http` |
669
+ | Playwright / SPA | `md-playwright-mcp` or `python -m md_generator.playwright.api.mcp_server` / `--transport sse` / `--transport streamable-http` |
670
+ | Audio | `md-audio-mcp` or `python -m md_generator.media.audio.api.mcp_server` — `--transport stdio` (default), `sse`, `streamable-http` |
671
+ | Video | `md-video-mcp` or `python -m md_generator.media.video.api.mcp_server` — same transports |
672
+ | YouTube | `md-youtube-mcp` or `python -m md_generator.media.youtube.api.mcp_server` — same transports |
500
673
 
501
674
  **Word** and **XLSX** also ship a small runner script in the repo:
502
675
 
@@ -531,7 +704,7 @@ Tests live under each legacy folder’s `tests/` directory (e.g. `pdf-to-md/test
531
704
  |------|------|
532
705
  | `LICENSE` | MIT license text |
533
706
  | `CODE_OF_CONDUCT.md` | [Contributor Covenant](https://www.contributor-covenant.org/) 2.1 |
534
- | `src/md_generator/` | **Library source** (all formats + `api` subpackages) |
707
+ | `src/md_generator/` | **Library source** (all formats + `api` subpackages); **audio/video** under [`media/audio/`](src/md_generator/media/audio/) and [`media/video/`](src/md_generator/media/video/) |
535
708
  | `pyproject.toml` | Packaging, extras, CLI entry points, pytest |
536
709
  | `*-to-md/` | **Docs, tests, fixtures**, thin `converter.py` shims, some `run.py` helpers |
537
710
  | `README.md` | This document |
@@ -1,13 +1,13 @@
1
1
  # mdengine
2
2
 
3
- Single Python distribution for converting **PDF**, **Word (.docx)**, **PowerPoint (.pptx)**, **Excel (.xlsx/.xlsm)**, **images** (OCR), **plain text / JSON / XML**, and **ZIP archives** into **Markdown** (and related assets). Install only the extras you need; everything imports under the **`md_generator`** package.
3
+ Single Python distribution for converting **PDF**, **Word (.docx)**, **PowerPoint (.pptx)**, **Excel (.xlsx/.xlsm)**, **images** (OCR), **plain text / JSON / XML**, **ZIP archives**, and **audio / video** (Whisper transcription → Markdown) into **Markdown** (and related assets). Install only the extras you need; everything imports under the **`md_generator`** package.
4
4
 
5
5
  - **PyPI name:** `mdengine` (import package: `md_generator`)
6
6
  - **Source:** [github.com/vishal7090/md-generator](https://github.com/vishal7090/md-generator)
7
7
  - **Python:** 3.10+
8
8
  - **License:** [MIT](LICENSE)
9
9
 
10
- **Quick links:** [On a new computer](#on-a-new-computer) · [Command-line execution](#command-line-execution) · [Python library](#python-library) · [HTTP API](#http-api-fastapi) · [MCP](#mcp-model-context-protocol) · [Development](#development) · [Code of Conduct](CODE_OF_CONDUCT.md)
10
+ **Quick links:** [On a new computer](#on-a-new-computer) · [Command-line execution](#command-line-execution) · [Python library](#python-library) · [Audio and video](#audio-and-video-to-markdown) · [HTTP API](#http-api-fastapi) · [MCP](#mcp-model-context-protocol) · [Development](#development) · [Code of Conduct](CODE_OF_CONDUCT.md)
11
11
 
12
12
  ---
13
13
 
@@ -74,6 +74,8 @@ pip install "mdengine[all]"
74
74
  | `archive` | ZIP → Markdown layout (Pillow; optional tesseract for inline image OCR) |
75
75
  | `url` | HTTP(S) HTML → Markdown (httpx, readability-lxml, markdownify, BeautifulSoup, lxml) |
76
76
  | `url-full` | `url` plus PDF/Word/PPTX/XLSX/archive stack for **post-converting** downloaded linked files to Markdown |
77
+ | `audio` | **Audio → Markdown** via Whisper (`openai-whisper`); ships `imageio-ffmpeg` for a bundled **ffmpeg** when none is on `PATH` |
78
+ | `video` | **Video → Markdown** (ffmpeg extracts mono 16 kHz WAV, then same Whisper stack as `audio`) |
77
79
  | `api` | FastAPI, uvicorn, httpx, pydantic-settings |
78
80
  | `mcp` | MCP servers (`mcp`, `fastmcp` where used) |
79
81
  | `dev` | pytest + API/MCP test helpers |
@@ -116,6 +118,15 @@ If the shell reports “command not found”, ensure the Python **Scripts** dire
116
118
  | `md-text` | `md_generator.text.converter:main` | `md-text config.xml out.md` |
117
119
  | `md-zip` | `md_generator.archive.converter:main` | `md-zip bundle.zip ./zip-out` |
118
120
  | `md-url` | `md_generator.url.converter:main` | `md-url https://example.com/doc ./web-out --artifact-layout` |
121
+ | `md-audio` | `md_generator.media.audio.converter:main` | `md-audio clip.mp3 transcript.md --model base` |
122
+ | `md-video` | `md_generator.media.video.converter:main` | `md-video clip.mp4 transcript.md --model base` |
123
+ | `md-youtube` | `md_generator.media.youtube.converter:main` | `md-youtube "https://youtu.be/…" out.md --transcript-lang en` |
124
+ | `md-audio-api` | `md_generator.media.audio.api.run:main` | REST + MCP on port **8011** (see [Audio and video to Markdown](#audio-and-video-to-markdown)) |
125
+ | `md-video-api` | `md_generator.media.video.api.run:main` | REST + MCP on port **8012** |
126
+ | `md-youtube-api` | `md_generator.media.youtube.api.run:main` | REST + MCP on port **8013** (JSON `url` body; see same section) |
127
+ | `md-audio-mcp` | `md_generator.media.audio.api.mcp_server:main` | Standalone MCP (`--transport stdio` \| `sse` \| `streamable-http`) |
128
+ | `md-video-mcp` | `md_generator.media.video.api.mcp_server:main` | Same for video |
129
+ | `md-youtube-mcp` | `md_generator.media.youtube.api.mcp_server:main` | Same for YouTube (`youtube_url_to_markdown`) |
119
130
 
120
131
  Every command accepts **`-h` / `--help`** for full flags (artifact layout, OCR, ZIP options, etc.).
121
132
 
@@ -133,6 +144,8 @@ md-image ./photos ocr.md --engines tess --strategy best
133
144
  md-text data.json data.md
134
145
  md-zip archive.zip ./unzipped-md
135
146
  md-url https://example.com/page ./page-bundle --artifact-layout
147
+ md-audio ./voice.mp3 ./voice.md --model tiny
148
+ md-video ./screen.mp4 ./screen.md --model base
136
149
  ```
137
150
 
138
151
  **Windows PowerShell** (same commands; use backslashes for paths if you prefer)
@@ -141,6 +154,8 @@ md-url https://example.com/page ./page-bundle --artifact-layout
141
154
  md-pdf .\manual.pdf .\out\doc.md
142
155
  md-zip .\archive.zip .\zip-out
143
156
  md-url https://example.com/page .\page-bundle --artifact-layout
157
+ md-audio .\voice.mp3 .\voice.md --model tiny
158
+ md-video .\screen.mp4 .\screen.md --model base
144
159
  ```
145
160
 
146
161
  **Windows CMD**
@@ -310,6 +325,124 @@ convert_zip(
310
325
 
311
326
  ---
312
327
 
328
+ ## Audio and video to Markdown
329
+
330
+ Library code lives under **`md_generator.media`**: shared probing in [`document_converter.py`](src/md_generator/media/document_converter.py), **audio** in [`media/audio/`](src/md_generator/media/audio/) (Whisper + ffprobe / ffmpeg metadata), **video** in [`media/video/`](src/md_generator/media/video/) (ffmpeg extracts audio only; transcription always goes through the audio service—no duplicate Whisper path in video).
331
+
332
+ ### System requirements
333
+
334
+ - **ffmpeg** (and **ffprobe** when available) on `PATH` for metadata and for **video** demux. If `ffprobe` is missing or misbehaving, metadata falls back to parsing `ffmpeg -i` stderr.
335
+ - Optional **`FFMPEG`** environment variable: absolute path to an `ffmpeg` executable (see [`resolve_ffmpeg_executable()`](src/md_generator/media/document_converter.py)).
336
+ - **GPU** is optional; Whisper runs on CPU if needed (may log FP16→FP32 on CPU).
337
+
338
+ ### Install
339
+
340
+ ```bash
341
+ pip install "mdengine[audio,api,mcp]" # audio CLI + HTTP + MCP
342
+ pip install "mdengine[video,api,mcp]" # video CLI + HTTP + MCP (same ML stack as audio)
343
+ pip install "mdengine[youtube,api,mcp]" # YouTube URL → Markdown (captions + metadata; optional Whisper fallback)
344
+ ```
345
+
346
+ ### Python library
347
+
348
+ **Audio** — structured result + Markdown:
349
+
350
+ ```python
351
+ from pathlib import Path
352
+ from md_generator.media.audio import AudioToMarkdownService, AudioConverter
353
+
354
+ svc = AudioToMarkdownService(whisper_model="base") # language omitted → Whisper auto-detect; pass e.g. language="en" to force
355
+ text = svc.to_markdown(Path("input.mp3"), title="My title")
356
+ svc.write_markdown(Path("input.mp3"), Path("out/transcript.md"))
357
+
358
+ result = svc.transcribe(Path("input.wav")) # metadata + segments + plain_text
359
+ ```
360
+
361
+ **Video** — extract → transcribe (via audio) → Markdown:
362
+
363
+ ```python
364
+ from pathlib import Path
365
+ from md_generator.media.video import VideoToMarkdownService
366
+
367
+ svc = VideoToMarkdownService(whisper_model="base") # transcription language omitted → auto-detect
368
+ md = svc.to_markdown(Path("input.mp4"), title=None)
369
+ svc.write_markdown(Path("input.mp4"), Path("out/transcript.md"))
370
+ ```
371
+
372
+ **YouTube** — captions API + page metadata (BeautifulSoup / oEmbed); optional **yt-dlp + Whisper** when captions are missing (`mdengine[audio]` and `yt-dlp` on `PATH`, or `MD_YOUTUBE_YTDLP`):
373
+
374
+ ```python
375
+ from md_generator.media.youtube import YouTubeToMarkdownService
376
+
377
+ svc = YouTubeToMarkdownService(whisper_model="base")
378
+ md = svc.to_markdown("https://www.youtube.com/watch?v=VIDEO_ID", transcript_languages=["en"])
379
+ svc.write_markdown("https://youtu.be/VIDEO_ID", Path("out/youtube.md"))
380
+ ```
381
+
382
+ For file-based pipelines, [`YouTubeConverter`](src/md_generator/media/youtube/converter.py) reads a `.url` / `.yturl` / `.youtube` file (or a `.txt` whose first non-comment line is a YouTube URL) and implements [`DocumentConverter`](src/md_generator/media/document_converter.py).
383
+
384
+ Public symbols are also re-exported from [`md_generator.media`](src/md_generator/media/__init__.py) for ffprobe helpers (`ffprobe_json`, `VideoProbeResult`, …).
385
+
386
+ ### REST API (FastAPI)
387
+
388
+ Each service exposes the same job pattern as other converters:
389
+
390
+ | Endpoint | Description |
391
+ |----------|-------------|
392
+ | `POST /convert/sync` | Multipart field **`file`**; returns **Markdown** body. Query: `whisper_model`, `language` (omit or `auto` for detection; pass a code to force), `title`. |
393
+ | `POST /convert/jobs` | Async upload; returns `{ "job_id", "status" }`. |
394
+ | `GET /convert/jobs/{job_id}` | Status JSON. |
395
+ | `GET /convert/jobs/{job_id}/download` | Markdown when `done`; workspace removed after download. |
396
+
397
+ **Audio** defaults: `MD_AUDIO_MAX_UPLOAD_MB=200`, `MD_AUDIO_MAX_SYNC_UPLOAD_MB=40`, `MD_AUDIO_API_PORT=8011`.
398
+ **Video** defaults: `MD_VIDEO_MAX_UPLOAD_MB=500`, `MD_VIDEO_MAX_SYNC_UPLOAD_MB=80`, `MD_VIDEO_API_PORT=8012`.
399
+ **YouTube** uses **JSON** (not multipart): `POST /convert/sync` and `POST /convert/jobs` accept `{"url":"https://www.youtube.com/watch?v=…","title":null,"transcript_languages":["en"],"enable_audio_fallback":true,"whisper_model":"base","language":null}`. Defaults: `MD_YOUTUBE_API_PORT=8013`, `MD_YOUTUBE_JOB_TTL_SECONDS`, `MD_YOUTUBE_CORS_ORIGINS`, `MD_YOUTUBE_TEMP_DIR`.
400
+
401
+ Run with the bundled runners (each call builds the app with **`factory=True`** for a clean MCP session manager):
402
+
403
+ ```bash
404
+ md-audio-api --host 127.0.0.1 --port 8011
405
+ md-video-api --host 127.0.0.1 --port 8012
406
+ md-youtube-api --host 127.0.0.1 --port 8013
407
+ ```
408
+
409
+ Or with Uvicorn directly (the ASGI app is built by **`create_app()`** so each worker gets its own MCP session manager):
410
+
411
+ ```bash
412
+ uvicorn md_generator.media.audio.api.main:create_app --factory --host 127.0.0.1 --port 8011
413
+ uvicorn md_generator.media.video.api.main:create_app --factory --host 127.0.0.1 --port 8012
414
+ uvicorn md_generator.media.youtube.api.main:create_app --factory --host 127.0.0.1 --port 8013
415
+ ```
416
+
417
+ The module also defines **`app = create_app()`** for a single-process target: `uvicorn md_generator.media.audio.api.main:app` (no `--factory`).
418
+
419
+ Swagger is at **`/docs`** when the app is running.
420
+
421
+ ### MCP (stdio, SSE, streamable HTTP)
422
+
423
+ 1. **With FastAPI** — start `md-audio-api`, `md-video-api`, or `md-youtube-api`; mount Streamable HTTP MCP at **`http://<host>:<port>/mcp`** (same host as REST).
424
+ 2. **Standalone** — process speaks MCP only:
425
+
426
+ ```bash
427
+ md-audio-mcp --transport stdio
428
+ md-audio-mcp --transport sse
429
+ md-audio-mcp --transport streamable-http
430
+ md-video-mcp --transport stdio
431
+ md-youtube-mcp --transport stdio
432
+ ```
433
+
434
+ **Audio MCP tools:** `transcribe_audio_path`, `transcribe_audio_base64`.
435
+ **Video MCP tools:** `transcribe_video_path`, `transcribe_video_base64`.
436
+ **YouTube MCP tool:** `youtube_url_to_markdown`.
437
+
438
+ Equivalent modules: `python -m md_generator.media.audio.api.mcp_server`, `python -m md_generator.media.video.api.mcp_server`, `python -m md_generator.media.youtube.api.mcp_server`.
439
+
440
+ ### Thin shims (repo clone)
441
+
442
+ [`audio-to-md/converter.py`](audio-to-md/converter.py), [`video-to-md/converter.py`](video-to-md/converter.py), and [`youtube-to-md/converter.py`](youtube-to-md/converter.py) delegate to the same `main` as `md-audio` / `md-video` / `md-youtube`. Tests and `pytest.ini` live under `audio-to-md/tests/`, `video-to-md/tests/`, and `youtube-to-md/tests/`.
443
+
444
+ ---
445
+
313
446
  ## HTTP API (FastAPI)
314
447
 
315
448
  All format APIs follow a similar pattern:
@@ -335,6 +468,10 @@ Install `mdengine[api]` plus the format extra(s), then run the **`app`** object
335
468
  | Text/JSON/XML | `md_generator.text.api.main:app` | `text`, `api`, `mcp` |
336
469
  | ZIP | `md_generator.archive.api.main:app` | `archive`, `api`, `mcp` (+ extras for nested office/PDF) |
337
470
  | URL / HTML | `md_generator.url.api.main:app` | `url`, `api`, `mcp` |
471
+ | Playwright / SPA | `md_generator.playwright.api.main:app` | `playwright`, `api`, `mcp` |
472
+ | Audio (Whisper) | `md_generator.media.audio.api.main:create_app` (use **`--factory`**) or `…main:app` | `audio`, `api`, `mcp` |
473
+ | Video (Whisper) | `md_generator.media.video.api.main:create_app` (use **`--factory`**) or `…main:app` | `video`, `api`, `mcp` |
474
+ | YouTube | `md_generator.media.youtube.api.main:create_app` (use **`--factory`**) or `…main:app` | `youtube`, `api`, `mcp` |
338
475
 
339
476
  Examples:
340
477
 
@@ -343,6 +480,10 @@ uvicorn md_generator.pdf.api.main:app --host 127.0.0.1 --port 8001
343
480
  uvicorn md_generator.word.api.main:app --host 127.0.0.1 --port 8002
344
481
  uvicorn md_generator.archive.api.main:app --host 127.0.0.1 --port 8010
345
482
  uvicorn md_generator.url.api.main:app --host 127.0.0.1 --port 8011
483
+ uvicorn md_generator.playwright.api.main:app --host 127.0.0.1 --port 8014
484
+ uvicorn md_generator.media.audio.api.main:create_app --factory --host 127.0.0.1 --port 8011
485
+ uvicorn md_generator.media.video.api.main:create_app --factory --host 127.0.0.1 --port 8012
486
+ uvicorn md_generator.media.youtube.api.main:create_app --factory --host 127.0.0.1 --port 8013
346
487
  ```
347
488
 
348
489
  ### MCP over HTTP on the same server
@@ -362,6 +503,10 @@ Prefixes differ per service (often read from a `.env` file next to the process):
362
503
  | Text | `TXT_JSON_XML_TO_MD_` | same pattern |
363
504
  | XLSX | `XLSX_TO_MD_` | `XLSX_TO_MD_TEMP_DIR`, `XLSX_TO_MD_CORS_ORIGINS`, etc. (see `md_generator.xlsx.api.app`) |
364
505
  | URL | `URL_TO_MD_` | `URL_TO_MD_MAX_SYNC_URLS`, `URL_TO_MD_MAX_SYNC_CRAWL_PAGES`, `URL_TO_MD_MAX_JOB_URLS`, `URL_TO_MD_JOB_TTL_SECONDS`, `URL_TO_MD_TEMP_DIR`, `URL_TO_MD_CORS_ORIGINS` |
506
+ | Playwright / SPA | `PLAYWRIGHT_TO_MD_` | `PLAYWRIGHT_TO_MD_MAX_SYNC_URLS`, `PLAYWRIGHT_TO_MD_MAX_JOB_URLS`, `PLAYWRIGHT_TO_MD_JOB_TTL_SECONDS`, `PLAYWRIGHT_TO_MD_TEMP_DIR`, `PLAYWRIGHT_TO_MD_CORS_ORIGINS`, `PLAYWRIGHT_TO_MD_API_HOST`, `PLAYWRIGHT_TO_MD_API_PORT` (default **8014**) |
507
+ | Audio API | `MD_AUDIO_` | `MD_AUDIO_MAX_UPLOAD_MB`, `MD_AUDIO_MAX_SYNC_UPLOAD_MB`, `MD_AUDIO_JOB_TTL_SECONDS`, `MD_AUDIO_TEMP_DIR`, `MD_AUDIO_CORS_ORIGINS`, `MD_AUDIO_API_HOST`, `MD_AUDIO_API_PORT` |
508
+ | Video API | `MD_VIDEO_` | Same pattern as audio with `MD_VIDEO_*` (defaults: larger upload/sync caps, port **8012**) |
509
+ | YouTube API | `MD_YOUTUBE_` | `MD_YOUTUBE_JOB_TTL_SECONDS`, `MD_YOUTUBE_TEMP_DIR`, `MD_YOUTUBE_CORS_ORIGINS`, `MD_YOUTUBE_API_HOST`, `MD_YOUTUBE_API_PORT` (default **8013**); optional `MD_YOUTUBE_YTDLP` path for audio fallback |
365
510
 
366
511
  Exact variable names match the `ApiSettings` / helper functions in each `api/settings` or `api/app` module.
367
512
 
@@ -385,6 +530,10 @@ Two usage patterns:
385
530
  | PPTX | `python -m md_generator.ppt.api.mcp_server` (see module docstring for flags) |
386
531
  | Image | `python -m md_generator.image.api.mcp_server` (see module for CLI) |
387
532
  | URL / HTML | `python -m md_generator.url.api.mcp_server` / `--transport sse` / `--transport streamable-http` |
533
+ | Playwright / SPA | `md-playwright-mcp` or `python -m md_generator.playwright.api.mcp_server` / `--transport sse` / `--transport streamable-http` |
534
+ | Audio | `md-audio-mcp` or `python -m md_generator.media.audio.api.mcp_server` — `--transport stdio` (default), `sse`, `streamable-http` |
535
+ | Video | `md-video-mcp` or `python -m md_generator.media.video.api.mcp_server` — same transports |
536
+ | YouTube | `md-youtube-mcp` or `python -m md_generator.media.youtube.api.mcp_server` — same transports |
388
537
 
389
538
  **Word** and **XLSX** also ship a small runner script in the repo:
390
539
 
@@ -419,7 +568,7 @@ Tests live under each legacy folder’s `tests/` directory (e.g. `pdf-to-md/test
419
568
  |------|------|
420
569
  | `LICENSE` | MIT license text |
421
570
  | `CODE_OF_CONDUCT.md` | [Contributor Covenant](https://www.contributor-covenant.org/) 2.1 |
422
- | `src/md_generator/` | **Library source** (all formats + `api` subpackages) |
571
+ | `src/md_generator/` | **Library source** (all formats + `api` subpackages); **audio/video** under [`media/audio/`](src/md_generator/media/audio/) and [`media/video/`](src/md_generator/media/video/) |
423
572
  | `pyproject.toml` | Packaging, extras, CLI entry points, pytest |
424
573
  | `*-to-md/` | **Docs, tests, fixtures**, thin `converter.py` shims, some `run.py` helpers |
425
574
  | `README.md` | This document |
@@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mdengine"
7
- version = "0.2.2"
8
- description = "Convert PDF, Office, images, text/JSON/XML, ZIP archives, and web URLs to Markdown."
7
+ version = "0.4.0"
8
+ description = "Convert PDF, Office, images, audio, video, text/JSON/XML, ZIP archives, and web URLs to Markdown."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  license = "MIT"
12
12
  license-files = ["LICENSE"]
13
13
  authors = [{ name = "mdengine contributors" }]
14
- keywords = ["markdown", "pdf", "docx", "pptx", "xlsx", "ocr", "zip", "url", "html"]
14
+ keywords = ["markdown", "pdf", "docx", "pptx", "xlsx", "ocr", "zip", "url", "html", "youtube", "playwright"]
15
15
  classifiers = [
16
16
  "Programming Language :: Python :: 3",
17
17
  "Programming Language :: Python :: 3 :: Only",
@@ -72,6 +72,26 @@ url-full = [
72
72
  "openpyxl>=3.1.0",
73
73
  "pytesseract>=0.3.10",
74
74
  ]
75
+ # Whisper + PyTorch; metadata uses ffprobe when available, else ``ffmpeg -i`` (see imageio-ffmpeg).
76
+ audio = ["openai-whisper>=20231117", "imageio-ffmpeg>=0.5.1"]
77
+ # Same ML stack as audio; ships a bundled ``ffmpeg`` binary when the system has none.
78
+ video = ["openai-whisper>=20231117", "imageio-ffmpeg>=0.5.1"]
79
+ youtube = [
80
+ "youtube-transcript-api>=0.6.0",
81
+ "httpx>=0.27.0",
82
+ "beautifulsoup4>=4.12.0",
83
+ "lxml>=5.0.0",
84
+ ]
85
+ # Playwright-rendered SPA → Markdown (install browsers: playwright install chromium).
86
+ playwright = [
87
+ "playwright>=1.49.0",
88
+ "httpx>=0.27.0",
89
+ "beautifulsoup4>=4.12.0",
90
+ "lxml>=5.0.0",
91
+ "markdownify>=0.14.0",
92
+ "readability-lxml>=0.8.1",
93
+ "Pillow>=10.0.0",
94
+ ]
75
95
  api = [
76
96
  "fastapi>=0.115.0",
77
97
  "uvicorn[standard]>=0.32.0",
@@ -83,9 +103,13 @@ mcp = ["mcp>=1.2.0", "fastmcp>=2.3.0"]
83
103
  dev = [
84
104
  "pytest>=8.0.0",
85
105
  "httpx>=0.27.0",
106
+ "beautifulsoup4>=4.12.0",
107
+ "lxml>=5.0.0",
108
+ "youtube-transcript-api>=0.6.0",
86
109
  "mdengine[api]",
87
110
  "mdengine[mcp]",
88
111
  "mdengine[url]",
112
+ "mdengine[playwright]",
89
113
  ]
90
114
  all = [
91
115
  "pymupdf>=1.24.0",
@@ -112,6 +136,7 @@ all = [
112
136
  "beautifulsoup4>=4.12.0",
113
137
  "lxml_html_clean>=0.4.0",
114
138
  "readability-lxml>=0.8.1",
139
+ "youtube-transcript-api>=0.6.0",
115
140
  ]
116
141
 
117
142
  [project.urls]
@@ -128,6 +153,18 @@ md-image = "md_generator.image.converter:main"
128
153
  md-text = "md_generator.text.converter:main"
129
154
  md-zip = "md_generator.archive.converter:main"
130
155
  md-url = "md_generator.url.converter:main"
156
+ md-audio = "md_generator.media.audio.converter:main"
157
+ md-video = "md_generator.media.video.converter:main"
158
+ md-audio-api = "md_generator.media.audio.api.run:main"
159
+ md-video-api = "md_generator.media.video.api.run:main"
160
+ md-audio-mcp = "md_generator.media.audio.api.mcp_server:main"
161
+ md-video-mcp = "md_generator.media.video.api.mcp_server:main"
162
+ md-youtube = "md_generator.media.youtube.converter:main"
163
+ md-youtube-api = "md_generator.media.youtube.api.run:main"
164
+ md-youtube-mcp = "md_generator.media.youtube.api.mcp_server:main"
165
+ md-playwright = "md_generator.playwright.cli:main"
166
+ md-playwright-api = "md_generator.playwright.api.run:main"
167
+ md-playwright-mcp = "md_generator.playwright.api.mcp_server:main"
131
168
 
132
169
  [tool.setuptools.packages.find]
133
170
  where = ["src"]
@@ -143,5 +180,9 @@ testpaths = [
143
180
  "txt-json-xml-to-md/tests",
144
181
  "zip-to-md/tests",
145
182
  "url-to-md/tests",
183
+ "audio-to-md/tests",
184
+ "video-to-md/tests",
185
+ "youtube-to-md/tests",
186
+ "playwright-to-md/tests",
146
187
  ]
147
188
  markers = ["integration: optional dependency or slower checks"]
@@ -1,5 +1,5 @@
1
1
  """md_generator: PDF, Office, image, text, and ZIP to Markdown conversion."""
2
2
 
3
- __version__ = "0.2.2"
3
+ __version__ = "0.4.0"
4
4
 
5
5
  __all__ = ["__version__"]
@@ -0,0 +1,21 @@
1
+ """Media helpers: document probing (ffprobe/ffmpeg) and audio/video to Markdown."""
2
+
3
+ from md_generator.media.document_converter import (
4
+ DocumentConverter,
5
+ MediaToolsError,
6
+ VideoProbeResult,
7
+ ffprobe_json,
8
+ require_ffmpeg_tools,
9
+ resolve_ffmpeg_executable,
10
+ video_probe_from_ffprobe,
11
+ )
12
+
13
+ __all__ = [
14
+ "DocumentConverter",
15
+ "MediaToolsError",
16
+ "VideoProbeResult",
17
+ "ffprobe_json",
18
+ "require_ffmpeg_tools",
19
+ "resolve_ffmpeg_executable",
20
+ "video_probe_from_ffprobe",
21
+ ]