mdengine 0.1.0__tar.gz → 0.3.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 (152) hide show
  1. {mdengine-0.1.0 → mdengine-0.3.0}/PKG-INFO +171 -9
  2. {mdengine-0.1.0 → mdengine-0.3.0}/README.md +136 -6
  3. {mdengine-0.1.0 → mdengine-0.3.0}/pyproject.toml +52 -4
  4. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/__init__.py +1 -1
  5. mdengine-0.3.0/src/md_generator/media/__init__.py +21 -0
  6. mdengine-0.3.0/src/md_generator/media/audio/__init__.py +18 -0
  7. mdengine-0.3.0/src/md_generator/media/audio/api/__init__.py +1 -0
  8. mdengine-0.3.0/src/md_generator/media/audio/api/main.py +175 -0
  9. mdengine-0.3.0/src/md_generator/media/audio/api/mcp_server.py +38 -0
  10. mdengine-0.3.0/src/md_generator/media/audio/api/mcp_setup.py +84 -0
  11. mdengine-0.3.0/src/md_generator/media/audio/api/run.py +30 -0
  12. mdengine-0.3.0/src/md_generator/media/audio/api/settings.py +26 -0
  13. mdengine-0.3.0/src/md_generator/media/audio/converter.py +178 -0
  14. mdengine-0.3.0/src/md_generator/media/audio/formatter.py +70 -0
  15. mdengine-0.3.0/src/md_generator/media/audio/service.py +52 -0
  16. mdengine-0.3.0/src/md_generator/media/document_converter.py +239 -0
  17. mdengine-0.3.0/src/md_generator/media/job_store.py +94 -0
  18. mdengine-0.3.0/src/md_generator/media/video/__init__.py +9 -0
  19. mdengine-0.3.0/src/md_generator/media/video/api/__init__.py +1 -0
  20. mdengine-0.3.0/src/md_generator/media/video/api/main.py +174 -0
  21. mdengine-0.3.0/src/md_generator/media/video/api/mcp_server.py +38 -0
  22. mdengine-0.3.0/src/md_generator/media/video/api/mcp_setup.py +84 -0
  23. mdengine-0.3.0/src/md_generator/media/video/api/run.py +30 -0
  24. mdengine-0.3.0/src/md_generator/media/video/api/settings.py +26 -0
  25. mdengine-0.3.0/src/md_generator/media/video/converter.py +111 -0
  26. mdengine-0.3.0/src/md_generator/media/video/formatter.py +81 -0
  27. mdengine-0.3.0/src/md_generator/media/video/service.py +79 -0
  28. mdengine-0.3.0/src/md_generator/media/whisper_language.py +78 -0
  29. mdengine-0.3.0/src/md_generator/url/__init__.py +24 -0
  30. mdengine-0.3.0/src/md_generator/url/api/__init__.py +1 -0
  31. mdengine-0.3.0/src/md_generator/url/api/convert_runner.py +34 -0
  32. mdengine-0.3.0/src/md_generator/url/api/jobs.py +92 -0
  33. mdengine-0.3.0/src/md_generator/url/api/main.py +377 -0
  34. mdengine-0.3.0/src/md_generator/url/api/mcp_server.py +37 -0
  35. mdengine-0.3.0/src/md_generator/url/api/mcp_setup.py +54 -0
  36. mdengine-0.3.0/src/md_generator/url/api/query_options.py +61 -0
  37. mdengine-0.3.0/src/md_generator/url/api/settings.py +25 -0
  38. mdengine-0.3.0/src/md_generator/url/assets.py +174 -0
  39. mdengine-0.3.0/src/md_generator/url/convert_impl.py +128 -0
  40. mdengine-0.3.0/src/md_generator/url/converter.py +200 -0
  41. mdengine-0.3.0/src/md_generator/url/crawl.py +223 -0
  42. mdengine-0.3.0/src/md_generator/url/extract.py +70 -0
  43. mdengine-0.3.0/src/md_generator/url/fetch.py +129 -0
  44. mdengine-0.3.0/src/md_generator/url/html_to_md.py +64 -0
  45. mdengine-0.3.0/src/md_generator/url/options.py +66 -0
  46. mdengine-0.3.0/src/md_generator/url/page_convert.py +102 -0
  47. mdengine-0.3.0/src/md_generator/url/post_convert_assets.py +387 -0
  48. mdengine-0.3.0/src/md_generator/url/robots_util.py +56 -0
  49. {mdengine-0.1.0 → mdengine-0.3.0}/src/mdengine.egg-info/PKG-INFO +171 -9
  50. {mdengine-0.1.0 → mdengine-0.3.0}/src/mdengine.egg-info/SOURCES.txt +44 -0
  51. mdengine-0.3.0/src/mdengine.egg-info/entry_points.txt +15 -0
  52. {mdengine-0.1.0 → mdengine-0.3.0}/src/mdengine.egg-info/requires.txt +36 -0
  53. mdengine-0.1.0/src/mdengine.egg-info/entry_points.txt +0 -8
  54. {mdengine-0.1.0 → mdengine-0.3.0}/LICENSE +0 -0
  55. {mdengine-0.1.0 → mdengine-0.3.0}/setup.cfg +0 -0
  56. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/__init__.py +0 -0
  57. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/api/__init__.py +0 -0
  58. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/api/convert_runner.py +0 -0
  59. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/api/jobs.py +0 -0
  60. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/api/main.py +0 -0
  61. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/api/mcp_server.py +0 -0
  62. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/api/mcp_setup.py +0 -0
  63. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/api/query_options.py +0 -0
  64. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/api/settings.py +0 -0
  65. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/convert_impl.py +0 -0
  66. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/converter.py +0 -0
  67. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/options.py +0 -0
  68. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/__init__.py +0 -0
  69. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/api/__init__.py +0 -0
  70. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/api/main.py +0 -0
  71. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/api/mcp_server.py +0 -0
  72. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/api/query_options.py +0 -0
  73. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/api/settings.py +0 -0
  74. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/api/staging.py +0 -0
  75. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/api/zip_bundle.py +0 -0
  76. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/backends/__init__.py +0 -0
  77. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/backends/base.py +0 -0
  78. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/backends/easy.py +0 -0
  79. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/backends/paddle.py +0 -0
  80. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/backends/tesseract.py +0 -0
  81. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/convert_impl.py +0 -0
  82. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/converter.py +0 -0
  83. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/emit.py +0 -0
  84. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/io_util.py +0 -0
  85. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/utils.py +0 -0
  86. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/__init__.py +0 -0
  87. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/api/__init__.py +0 -0
  88. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/api/main.py +0 -0
  89. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/api/mcp_server.py +0 -0
  90. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/api/settings.py +0 -0
  91. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/api/zip_bundle.py +0 -0
  92. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/converter.py +0 -0
  93. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/md_emit.py +0 -0
  94. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/pdf_extract.py +0 -0
  95. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/utils.py +0 -0
  96. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/__init__.py +0 -0
  97. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/api/__init__.py +0 -0
  98. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/api/convert_runner.py +0 -0
  99. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/api/jobs.py +0 -0
  100. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/api/main.py +0 -0
  101. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/api/mcp_server.py +0 -0
  102. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/api/mcp_setup.py +0 -0
  103. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/api/query_options.py +0 -0
  104. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/api/settings.py +0 -0
  105. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/convert_impl.py +0 -0
  106. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/converter.py +0 -0
  107. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/embedded_extract.py +0 -0
  108. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/ooxml_media.py +0 -0
  109. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/options.py +0 -0
  110. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/post_assets.py +0 -0
  111. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/text_formatting.py +0 -0
  112. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/vendor_pdf_md/__init__.py +0 -0
  113. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/vendor_pdf_md/convert.py +0 -0
  114. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/vendor_word_md/__init__.py +0 -0
  115. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/vendor_word_md/convert.py +0 -0
  116. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/zip_deep.py +0 -0
  117. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/__init__.py +0 -0
  118. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/api/__init__.py +0 -0
  119. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/api/convert_runner.py +0 -0
  120. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/api/jobs.py +0 -0
  121. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/api/main.py +0 -0
  122. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/api/mcp_server.py +0 -0
  123. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/api/mcp_setup.py +0 -0
  124. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/api/query_options.py +0 -0
  125. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/api/settings.py +0 -0
  126. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/convert_impl.py +0 -0
  127. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/converter.py +0 -0
  128. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/format_detect.py +0 -0
  129. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/md_emit_json.py +0 -0
  130. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/md_emit_txt.py +0 -0
  131. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/md_emit_xml.py +0 -0
  132. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/options.py +0 -0
  133. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/__init__.py +0 -0
  134. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/api/__init__.py +0 -0
  135. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/api/convert_util.py +0 -0
  136. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/api/jobs.py +0 -0
  137. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/api/main.py +0 -0
  138. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/api/mcp_server.py +0 -0
  139. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/artifact.py +0 -0
  140. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/converter.py +0 -0
  141. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/settings.py +0 -0
  142. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/__init__.py +0 -0
  143. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/api/__init__.py +0 -0
  144. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/api/app.py +0 -0
  145. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/convert_config.py +0 -0
  146. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/converter.py +0 -0
  147. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/converter_core.py +0 -0
  148. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/excel_reader.py +0 -0
  149. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/markdown_emitter.py +0 -0
  150. {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/mcp_server.py +0 -0
  151. {mdengine-0.1.0 → mdengine-0.3.0}/src/mdengine.egg-info/dependency_links.txt +0 -0
  152. {mdengine-0.1.0 → mdengine-0.3.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.1.0
4
- Summary: Convert PDF, Office, images, text/JSON/XML, and ZIP archives to Markdown.
3
+ Version: 0.3.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
10
+ Keywords: markdown,pdf,docx,pptx,xlsx,ocr,zip,url,html
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3 :: Only
13
13
  Classifier: Topic :: Text Processing :: Markup :: Markdown
@@ -46,6 +46,34 @@ Provides-Extra: text
46
46
  Provides-Extra: archive
47
47
  Requires-Dist: Pillow>=10.0.0; extra == "archive"
48
48
  Requires-Dist: pytesseract>=0.3.10; extra == "archive"
49
+ Provides-Extra: url
50
+ Requires-Dist: httpx>=0.27.0; extra == "url"
51
+ Requires-Dist: beautifulsoup4>=4.12.0; extra == "url"
52
+ Requires-Dist: lxml>=5.0.0; extra == "url"
53
+ Requires-Dist: lxml_html_clean>=0.4.0; extra == "url"
54
+ Requires-Dist: markdownify>=0.14.0; extra == "url"
55
+ Requires-Dist: readability-lxml>=0.8.1; extra == "url"
56
+ Provides-Extra: url-full
57
+ Requires-Dist: httpx>=0.27.0; extra == "url-full"
58
+ Requires-Dist: beautifulsoup4>=4.12.0; extra == "url-full"
59
+ Requires-Dist: lxml>=5.0.0; extra == "url-full"
60
+ Requires-Dist: lxml_html_clean>=0.4.0; extra == "url-full"
61
+ Requires-Dist: markdownify>=0.14.0; extra == "url-full"
62
+ Requires-Dist: readability-lxml>=0.8.1; extra == "url-full"
63
+ Requires-Dist: pymupdf>=1.24.0; extra == "url-full"
64
+ Requires-Dist: pdfplumber>=0.11.0; extra == "url-full"
65
+ Requires-Dist: mammoth>=1.8.0; extra == "url-full"
66
+ Requires-Dist: python-pptx>=1.0.0; extra == "url-full"
67
+ Requires-Dist: Pillow>=10.0.0; extra == "url-full"
68
+ Requires-Dist: olefile>=0.47; extra == "url-full"
69
+ Requires-Dist: openpyxl>=3.1.0; extra == "url-full"
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"
49
77
  Provides-Extra: api
50
78
  Requires-Dist: fastapi>=0.115.0; extra == "api"
51
79
  Requires-Dist: uvicorn[standard]>=0.32.0; extra == "api"
@@ -60,6 +88,7 @@ Requires-Dist: pytest>=8.0.0; extra == "dev"
60
88
  Requires-Dist: httpx>=0.27.0; extra == "dev"
61
89
  Requires-Dist: mdengine[api]; extra == "dev"
62
90
  Requires-Dist: mdengine[mcp]; extra == "dev"
91
+ Requires-Dist: mdengine[url]; extra == "dev"
63
92
  Provides-Extra: all
64
93
  Requires-Dist: pymupdf>=1.24.0; extra == "all"
65
94
  Requires-Dist: pdfplumber>=0.11.0; extra == "all"
@@ -82,18 +111,21 @@ Requires-Dist: httpx>=0.27.0; extra == "all"
82
111
  Requires-Dist: pydantic-settings>=2.0.0; extra == "all"
83
112
  Requires-Dist: mcp>=1.2.0; extra == "all"
84
113
  Requires-Dist: fastmcp>=2.3.0; extra == "all"
114
+ Requires-Dist: beautifulsoup4>=4.12.0; extra == "all"
115
+ Requires-Dist: lxml_html_clean>=0.4.0; extra == "all"
116
+ Requires-Dist: readability-lxml>=0.8.1; extra == "all"
85
117
  Dynamic: license-file
86
118
 
87
119
  # mdengine
88
120
 
89
- 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.
121
+ 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.
90
122
 
91
123
  - **PyPI name:** `mdengine` (import package: `md_generator`)
92
124
  - **Source:** [github.com/vishal7090/md-generator](https://github.com/vishal7090/md-generator)
93
125
  - **Python:** 3.10+
94
126
  - **License:** [MIT](LICENSE)
95
127
 
96
- **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)
128
+ **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)
97
129
 
98
130
  ---
99
131
 
@@ -158,6 +190,10 @@ pip install "mdengine[all]"
158
190
  | `image-ocr` | Heavy OCR backends (pytesseract, paddle, easyocr, …) |
159
191
  | `text` | TXT / JSON / XML converter (stdlib-oriented; marker extra) |
160
192
  | `archive` | ZIP → Markdown layout (Pillow; optional tesseract for inline image OCR) |
193
+ | `url` | HTTP(S) HTML → Markdown (httpx, readability-lxml, markdownify, BeautifulSoup, lxml) |
194
+ | `url-full` | `url` plus PDF/Word/PPTX/XLSX/archive stack for **post-converting** downloaded linked files to Markdown |
195
+ | `audio` | **Audio → Markdown** via Whisper (`openai-whisper`); ships `imageio-ffmpeg` for a bundled **ffmpeg** when none is on `PATH` |
196
+ | `video` | **Video → Markdown** (ffmpeg extracts mono 16 kHz WAV, then same Whisper stack as `audio`) |
161
197
  | `api` | FastAPI, uvicorn, httpx, pydantic-settings |
162
198
  | `mcp` | MCP servers (`mcp`, `fastmcp` where used) |
163
199
  | `dev` | pytest + API/MCP test helpers |
@@ -199,6 +235,13 @@ If the shell reports “command not found”, ensure the Python **Scripts** dire
199
235
  | `md-image` | `md_generator.image.converter:main` | `md-image ./scans page.md` |
200
236
  | `md-text` | `md_generator.text.converter:main` | `md-text config.xml out.md` |
201
237
  | `md-zip` | `md_generator.archive.converter:main` | `md-zip bundle.zip ./zip-out` |
238
+ | `md-url` | `md_generator.url.converter:main` | `md-url https://example.com/doc ./web-out --artifact-layout` |
239
+ | `md-audio` | `md_generator.media.audio.converter:main` | `md-audio clip.mp3 transcript.md --model base` |
240
+ | `md-video` | `md_generator.media.video.converter:main` | `md-video clip.mp4 transcript.md --model base` |
241
+ | `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)) |
242
+ | `md-video-api` | `md_generator.media.video.api.run:main` | REST + MCP on port **8012** |
243
+ | `md-audio-mcp` | `md_generator.media.audio.api.mcp_server:main` | Standalone MCP (`--transport stdio` \| `sse` \| `streamable-http`) |
244
+ | `md-video-mcp` | `md_generator.media.video.api.mcp_server:main` | Same for video |
202
245
 
203
246
  Every command accepts **`-h` / `--help`** for full flags (artifact layout, OCR, ZIP options, etc.).
204
247
 
@@ -215,6 +258,9 @@ md-xlsx -i export.csv -o ./csv-out
215
258
  md-image ./photos ocr.md --engines tess --strategy best
216
259
  md-text data.json data.md
217
260
  md-zip archive.zip ./unzipped-md
261
+ md-url https://example.com/page ./page-bundle --artifact-layout
262
+ md-audio ./voice.mp3 ./voice.md --model tiny
263
+ md-video ./screen.mp4 ./screen.md --model base
218
264
  ```
219
265
 
220
266
  **Windows PowerShell** (same commands; use backslashes for paths if you prefer)
@@ -222,6 +268,9 @@ md-zip archive.zip ./unzipped-md
222
268
  ```powershell
223
269
  md-pdf .\manual.pdf .\out\doc.md
224
270
  md-zip .\archive.zip .\zip-out
271
+ md-url https://example.com/page .\page-bundle --artifact-layout
272
+ md-audio .\voice.mp3 .\voice.md --model tiny
273
+ md-video .\screen.mp4 .\screen.md --model base
225
274
  ```
226
275
 
227
276
  **Windows CMD**
@@ -229,11 +278,12 @@ md-zip .\archive.zip .\zip-out
229
278
  ```cmd
230
279
  md-pdf manual.pdf out\doc.md
231
280
  md-zip archive.zip zip-out
281
+ md-url https://example.com/page page-bundle --artifact-layout
232
282
  ```
233
283
 
234
284
  ### 5. Run without `pip install` (repo clone + `PYTHONPATH`)
235
285
 
236
- The folders `pdf-to-md/`, `word-to-md/`, … contain a thin `converter.py` that calls the same code as `md-pdf`, `md-word`, etc. From the **repository root**, point Python at `src` so `md_generator` imports, then run the shim:
286
+ The folders `pdf-to-md/`, `word-to-md/`, `url-to-md/`, … contain a thin `converter.py` that calls the same code as `md-pdf`, `md-word`, etc. From the **repository root**, point Python at `src` so `md_generator` imports, then run the shim:
237
287
 
238
288
  **PowerShell**
239
289
 
@@ -390,16 +440,116 @@ convert_zip(
390
440
 
391
441
  ---
392
442
 
443
+ ## Audio and video to Markdown
444
+
445
+ 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).
446
+
447
+ ### System requirements
448
+
449
+ - **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.
450
+ - Optional **`FFMPEG`** environment variable: absolute path to an `ffmpeg` executable (see [`resolve_ffmpeg_executable()`](src/md_generator/media/document_converter.py)).
451
+ - **GPU** is optional; Whisper runs on CPU if needed (may log FP16→FP32 on CPU).
452
+
453
+ ### Install
454
+
455
+ ```bash
456
+ pip install "mdengine[audio,api,mcp]" # audio CLI + HTTP + MCP
457
+ pip install "mdengine[video,api,mcp]" # video CLI + HTTP + MCP (same ML stack as audio)
458
+ ```
459
+
460
+ ### Python library
461
+
462
+ **Audio** — structured result + Markdown:
463
+
464
+ ```python
465
+ from pathlib import Path
466
+ from md_generator.media.audio import AudioToMarkdownService, AudioConverter
467
+
468
+ svc = AudioToMarkdownService(whisper_model="base") # language omitted → Whisper auto-detect; pass e.g. language="en" to force
469
+ text = svc.to_markdown(Path("input.mp3"), title="My title")
470
+ svc.write_markdown(Path("input.mp3"), Path("out/transcript.md"))
471
+
472
+ result = svc.transcribe(Path("input.wav")) # metadata + segments + plain_text
473
+ ```
474
+
475
+ **Video** — extract → transcribe (via audio) → Markdown:
476
+
477
+ ```python
478
+ from pathlib import Path
479
+ from md_generator.media.video import VideoToMarkdownService
480
+
481
+ svc = VideoToMarkdownService(whisper_model="base") # transcription language omitted → auto-detect
482
+ md = svc.to_markdown(Path("input.mp4"), title=None)
483
+ svc.write_markdown(Path("input.mp4"), Path("out/transcript.md"))
484
+ ```
485
+
486
+ Public symbols are also re-exported from [`md_generator.media`](src/md_generator/media/__init__.py) for ffprobe helpers (`ffprobe_json`, `VideoProbeResult`, …).
487
+
488
+ ### REST API (FastAPI)
489
+
490
+ Each service exposes the same job pattern as other converters:
491
+
492
+ | Endpoint | Description |
493
+ |----------|-------------|
494
+ | `POST /convert/sync` | Multipart field **`file`**; returns **Markdown** body. Query: `whisper_model`, `language` (omit or `auto` for detection; pass a code to force), `title`. |
495
+ | `POST /convert/jobs` | Async upload; returns `{ "job_id", "status" }`. |
496
+ | `GET /convert/jobs/{job_id}` | Status JSON. |
497
+ | `GET /convert/jobs/{job_id}/download` | Markdown when `done`; workspace removed after download. |
498
+
499
+ **Audio** defaults: `MD_AUDIO_MAX_UPLOAD_MB=200`, `MD_AUDIO_MAX_SYNC_UPLOAD_MB=40`, `MD_AUDIO_API_PORT=8011`.
500
+ **Video** defaults: `MD_VIDEO_MAX_UPLOAD_MB=500`, `MD_VIDEO_MAX_SYNC_UPLOAD_MB=80`, `MD_VIDEO_API_PORT=8012`.
501
+
502
+ Run with the bundled runners (each call builds the app with **`factory=True`** for a clean MCP session manager):
503
+
504
+ ```bash
505
+ md-audio-api --host 127.0.0.1 --port 8011
506
+ md-video-api --host 127.0.0.1 --port 8012
507
+ ```
508
+
509
+ Or with Uvicorn directly (the ASGI app is built by **`create_app()`** so each worker gets its own MCP session manager):
510
+
511
+ ```bash
512
+ uvicorn md_generator.media.audio.api.main:create_app --factory --host 127.0.0.1 --port 8011
513
+ uvicorn md_generator.media.video.api.main:create_app --factory --host 127.0.0.1 --port 8012
514
+ ```
515
+
516
+ The module also defines **`app = create_app()`** for a single-process target: `uvicorn md_generator.media.audio.api.main:app` (no `--factory`).
517
+
518
+ Swagger is at **`/docs`** when the app is running.
519
+
520
+ ### MCP (stdio, SSE, streamable HTTP)
521
+
522
+ 1. **With FastAPI** — start `md-audio-api` or `md-video-api`; mount Streamable HTTP MCP at **`http://<host>:<port>/mcp`** (same host as REST).
523
+ 2. **Standalone** — process speaks MCP only:
524
+
525
+ ```bash
526
+ md-audio-mcp --transport stdio
527
+ md-audio-mcp --transport sse
528
+ md-audio-mcp --transport streamable-http
529
+ md-video-mcp --transport stdio
530
+ ```
531
+
532
+ **Audio MCP tools:** `transcribe_audio_path`, `transcribe_audio_base64`.
533
+ **Video MCP tools:** `transcribe_video_path`, `transcribe_video_base64`.
534
+
535
+ Equivalent modules: `python -m md_generator.media.audio.api.mcp_server`, `python -m md_generator.media.video.api.mcp_server`.
536
+
537
+ ### Thin shims (repo clone)
538
+
539
+ [`audio-to-md/converter.py`](audio-to-md/converter.py) and [`video-to-md/converter.py`](video-to-md/converter.py) delegate to the same `main` as `md-audio` / `md-video`. Tests and `pytest.ini` live under `audio-to-md/tests/` and `video-to-md/tests/`.
540
+
541
+ ---
542
+
393
543
  ## HTTP API (FastAPI)
394
544
 
395
545
  All format APIs follow a similar pattern:
396
546
 
397
- - **`POST /convert/sync`** — upload a file; response is often a **ZIP** (artifact bundle) for larger formats.
547
+ - **`POST /convert/sync`** — upload a file (most converters) **or** send JSON (`url-to-md`); response is often a **ZIP** (artifact bundle) for larger formats.
398
548
  - **`POST /convert/jobs`** — async job; returns `job_id`.
399
549
  - **`GET /convert/jobs/{job_id}`** — status.
400
550
  - **`GET /convert/jobs/{job_id}/download`** — download result when ready.
401
551
 
402
- Upload field name is **`file`** (multipart form). Use `httpx` or `curl -F "file=@path/to/file"`.
552
+ Upload field name is **`file`** (multipart form) for file-based converters. Use `httpx` or `curl -F "file=@path/to/file"`. **URL** conversion uses a **JSON** body (`url` or `urls`); see [url-to-md/README.md](url-to-md/README.md).
403
553
 
404
554
  ### Run with Uvicorn
405
555
 
@@ -414,6 +564,9 @@ Install `mdengine[api]` plus the format extra(s), then run the **`app`** object
414
564
  | Image | `md_generator.image.api.main:app` | `image`, `api`, `mcp` |
415
565
  | Text/JSON/XML | `md_generator.text.api.main:app` | `text`, `api`, `mcp` |
416
566
  | ZIP | `md_generator.archive.api.main:app` | `archive`, `api`, `mcp` (+ extras for nested office/PDF) |
567
+ | URL / HTML | `md_generator.url.api.main:app` | `url`, `api`, `mcp` |
568
+ | Audio (Whisper) | `md_generator.media.audio.api.main:create_app` (use **`--factory`**) or `…main:app` | `audio`, `api`, `mcp` |
569
+ | Video (Whisper) | `md_generator.media.video.api.main:create_app` (use **`--factory`**) or `…main:app` | `video`, `api`, `mcp` |
417
570
 
418
571
  Examples:
419
572
 
@@ -421,6 +574,9 @@ Examples:
421
574
  uvicorn md_generator.pdf.api.main:app --host 127.0.0.1 --port 8001
422
575
  uvicorn md_generator.word.api.main:app --host 127.0.0.1 --port 8002
423
576
  uvicorn md_generator.archive.api.main:app --host 127.0.0.1 --port 8010
577
+ uvicorn md_generator.url.api.main:app --host 127.0.0.1 --port 8011
578
+ uvicorn md_generator.media.audio.api.main:create_app --factory --host 127.0.0.1 --port 8011
579
+ uvicorn md_generator.media.video.api.main:create_app --factory --host 127.0.0.1 --port 8012
424
580
  ```
425
581
 
426
582
  ### MCP over HTTP on the same server
@@ -439,6 +595,9 @@ Prefixes differ per service (often read from a `.env` file next to the process):
439
595
  | PPTX | `PPT_TO_MD_` | `PPT_TO_MD_MAX_UPLOAD_MB`, … |
440
596
  | Text | `TXT_JSON_XML_TO_MD_` | same pattern |
441
597
  | XLSX | `XLSX_TO_MD_` | `XLSX_TO_MD_TEMP_DIR`, `XLSX_TO_MD_CORS_ORIGINS`, etc. (see `md_generator.xlsx.api.app`) |
598
+ | 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` |
599
+ | 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` |
600
+ | Video API | `MD_VIDEO_` | Same pattern as audio with `MD_VIDEO_*` (defaults: larger upload/sync caps, port **8012**) |
442
601
 
443
602
  Exact variable names match the `ApiSettings` / helper functions in each `api/settings` or `api/app` module.
444
603
 
@@ -461,6 +620,9 @@ Two usage patterns:
461
620
  | PDF (FastMCP) | `python -m md_generator.pdf.api.mcp_server` / `--transport stdio` / `sse` / `streamable-http` |
462
621
  | PPTX | `python -m md_generator.ppt.api.mcp_server` (see module docstring for flags) |
463
622
  | Image | `python -m md_generator.image.api.mcp_server` (see module for CLI) |
623
+ | URL / HTML | `python -m md_generator.url.api.mcp_server` / `--transport sse` / `--transport streamable-http` |
624
+ | Audio | `md-audio-mcp` or `python -m md_generator.media.audio.api.mcp_server` — `--transport stdio` (default), `sse`, `streamable-http` |
625
+ | Video | `md-video-mcp` or `python -m md_generator.media.video.api.mcp_server` — same transports |
464
626
 
465
627
  **Word** and **XLSX** also ship a small runner script in the repo:
466
628
 
@@ -495,7 +657,7 @@ Tests live under each legacy folder’s `tests/` directory (e.g. `pdf-to-md/test
495
657
  |------|------|
496
658
  | `LICENSE` | MIT license text |
497
659
  | `CODE_OF_CONDUCT.md` | [Contributor Covenant](https://www.contributor-covenant.org/) 2.1 |
498
- | `src/md_generator/` | **Library source** (all formats + `api` subpackages) |
660
+ | `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/) |
499
661
  | `pyproject.toml` | Packaging, extras, CLI entry points, pytest |
500
662
  | `*-to-md/` | **Docs, tests, fixtures**, thin `converter.py` shims, some `run.py` helpers |
501
663
  | `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
 
@@ -72,6 +72,10 @@ pip install "mdengine[all]"
72
72
  | `image-ocr` | Heavy OCR backends (pytesseract, paddle, easyocr, …) |
73
73
  | `text` | TXT / JSON / XML converter (stdlib-oriented; marker extra) |
74
74
  | `archive` | ZIP → Markdown layout (Pillow; optional tesseract for inline image OCR) |
75
+ | `url` | HTTP(S) HTML → Markdown (httpx, readability-lxml, markdownify, BeautifulSoup, lxml) |
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`) |
75
79
  | `api` | FastAPI, uvicorn, httpx, pydantic-settings |
76
80
  | `mcp` | MCP servers (`mcp`, `fastmcp` where used) |
77
81
  | `dev` | pytest + API/MCP test helpers |
@@ -113,6 +117,13 @@ If the shell reports “command not found”, ensure the Python **Scripts** dire
113
117
  | `md-image` | `md_generator.image.converter:main` | `md-image ./scans page.md` |
114
118
  | `md-text` | `md_generator.text.converter:main` | `md-text config.xml out.md` |
115
119
  | `md-zip` | `md_generator.archive.converter:main` | `md-zip bundle.zip ./zip-out` |
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-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)) |
124
+ | `md-video-api` | `md_generator.media.video.api.run:main` | REST + MCP on port **8012** |
125
+ | `md-audio-mcp` | `md_generator.media.audio.api.mcp_server:main` | Standalone MCP (`--transport stdio` \| `sse` \| `streamable-http`) |
126
+ | `md-video-mcp` | `md_generator.media.video.api.mcp_server:main` | Same for video |
116
127
 
117
128
  Every command accepts **`-h` / `--help`** for full flags (artifact layout, OCR, ZIP options, etc.).
118
129
 
@@ -129,6 +140,9 @@ md-xlsx -i export.csv -o ./csv-out
129
140
  md-image ./photos ocr.md --engines tess --strategy best
130
141
  md-text data.json data.md
131
142
  md-zip archive.zip ./unzipped-md
143
+ md-url https://example.com/page ./page-bundle --artifact-layout
144
+ md-audio ./voice.mp3 ./voice.md --model tiny
145
+ md-video ./screen.mp4 ./screen.md --model base
132
146
  ```
133
147
 
134
148
  **Windows PowerShell** (same commands; use backslashes for paths if you prefer)
@@ -136,6 +150,9 @@ md-zip archive.zip ./unzipped-md
136
150
  ```powershell
137
151
  md-pdf .\manual.pdf .\out\doc.md
138
152
  md-zip .\archive.zip .\zip-out
153
+ md-url https://example.com/page .\page-bundle --artifact-layout
154
+ md-audio .\voice.mp3 .\voice.md --model tiny
155
+ md-video .\screen.mp4 .\screen.md --model base
139
156
  ```
140
157
 
141
158
  **Windows CMD**
@@ -143,11 +160,12 @@ md-zip .\archive.zip .\zip-out
143
160
  ```cmd
144
161
  md-pdf manual.pdf out\doc.md
145
162
  md-zip archive.zip zip-out
163
+ md-url https://example.com/page page-bundle --artifact-layout
146
164
  ```
147
165
 
148
166
  ### 5. Run without `pip install` (repo clone + `PYTHONPATH`)
149
167
 
150
- The folders `pdf-to-md/`, `word-to-md/`, … contain a thin `converter.py` that calls the same code as `md-pdf`, `md-word`, etc. From the **repository root**, point Python at `src` so `md_generator` imports, then run the shim:
168
+ The folders `pdf-to-md/`, `word-to-md/`, `url-to-md/`, … contain a thin `converter.py` that calls the same code as `md-pdf`, `md-word`, etc. From the **repository root**, point Python at `src` so `md_generator` imports, then run the shim:
151
169
 
152
170
  **PowerShell**
153
171
 
@@ -304,16 +322,116 @@ convert_zip(
304
322
 
305
323
  ---
306
324
 
325
+ ## Audio and video to Markdown
326
+
327
+ 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).
328
+
329
+ ### System requirements
330
+
331
+ - **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.
332
+ - Optional **`FFMPEG`** environment variable: absolute path to an `ffmpeg` executable (see [`resolve_ffmpeg_executable()`](src/md_generator/media/document_converter.py)).
333
+ - **GPU** is optional; Whisper runs on CPU if needed (may log FP16→FP32 on CPU).
334
+
335
+ ### Install
336
+
337
+ ```bash
338
+ pip install "mdengine[audio,api,mcp]" # audio CLI + HTTP + MCP
339
+ pip install "mdengine[video,api,mcp]" # video CLI + HTTP + MCP (same ML stack as audio)
340
+ ```
341
+
342
+ ### Python library
343
+
344
+ **Audio** — structured result + Markdown:
345
+
346
+ ```python
347
+ from pathlib import Path
348
+ from md_generator.media.audio import AudioToMarkdownService, AudioConverter
349
+
350
+ svc = AudioToMarkdownService(whisper_model="base") # language omitted → Whisper auto-detect; pass e.g. language="en" to force
351
+ text = svc.to_markdown(Path("input.mp3"), title="My title")
352
+ svc.write_markdown(Path("input.mp3"), Path("out/transcript.md"))
353
+
354
+ result = svc.transcribe(Path("input.wav")) # metadata + segments + plain_text
355
+ ```
356
+
357
+ **Video** — extract → transcribe (via audio) → Markdown:
358
+
359
+ ```python
360
+ from pathlib import Path
361
+ from md_generator.media.video import VideoToMarkdownService
362
+
363
+ svc = VideoToMarkdownService(whisper_model="base") # transcription language omitted → auto-detect
364
+ md = svc.to_markdown(Path("input.mp4"), title=None)
365
+ svc.write_markdown(Path("input.mp4"), Path("out/transcript.md"))
366
+ ```
367
+
368
+ Public symbols are also re-exported from [`md_generator.media`](src/md_generator/media/__init__.py) for ffprobe helpers (`ffprobe_json`, `VideoProbeResult`, …).
369
+
370
+ ### REST API (FastAPI)
371
+
372
+ Each service exposes the same job pattern as other converters:
373
+
374
+ | Endpoint | Description |
375
+ |----------|-------------|
376
+ | `POST /convert/sync` | Multipart field **`file`**; returns **Markdown** body. Query: `whisper_model`, `language` (omit or `auto` for detection; pass a code to force), `title`. |
377
+ | `POST /convert/jobs` | Async upload; returns `{ "job_id", "status" }`. |
378
+ | `GET /convert/jobs/{job_id}` | Status JSON. |
379
+ | `GET /convert/jobs/{job_id}/download` | Markdown when `done`; workspace removed after download. |
380
+
381
+ **Audio** defaults: `MD_AUDIO_MAX_UPLOAD_MB=200`, `MD_AUDIO_MAX_SYNC_UPLOAD_MB=40`, `MD_AUDIO_API_PORT=8011`.
382
+ **Video** defaults: `MD_VIDEO_MAX_UPLOAD_MB=500`, `MD_VIDEO_MAX_SYNC_UPLOAD_MB=80`, `MD_VIDEO_API_PORT=8012`.
383
+
384
+ Run with the bundled runners (each call builds the app with **`factory=True`** for a clean MCP session manager):
385
+
386
+ ```bash
387
+ md-audio-api --host 127.0.0.1 --port 8011
388
+ md-video-api --host 127.0.0.1 --port 8012
389
+ ```
390
+
391
+ Or with Uvicorn directly (the ASGI app is built by **`create_app()`** so each worker gets its own MCP session manager):
392
+
393
+ ```bash
394
+ uvicorn md_generator.media.audio.api.main:create_app --factory --host 127.0.0.1 --port 8011
395
+ uvicorn md_generator.media.video.api.main:create_app --factory --host 127.0.0.1 --port 8012
396
+ ```
397
+
398
+ The module also defines **`app = create_app()`** for a single-process target: `uvicorn md_generator.media.audio.api.main:app` (no `--factory`).
399
+
400
+ Swagger is at **`/docs`** when the app is running.
401
+
402
+ ### MCP (stdio, SSE, streamable HTTP)
403
+
404
+ 1. **With FastAPI** — start `md-audio-api` or `md-video-api`; mount Streamable HTTP MCP at **`http://<host>:<port>/mcp`** (same host as REST).
405
+ 2. **Standalone** — process speaks MCP only:
406
+
407
+ ```bash
408
+ md-audio-mcp --transport stdio
409
+ md-audio-mcp --transport sse
410
+ md-audio-mcp --transport streamable-http
411
+ md-video-mcp --transport stdio
412
+ ```
413
+
414
+ **Audio MCP tools:** `transcribe_audio_path`, `transcribe_audio_base64`.
415
+ **Video MCP tools:** `transcribe_video_path`, `transcribe_video_base64`.
416
+
417
+ Equivalent modules: `python -m md_generator.media.audio.api.mcp_server`, `python -m md_generator.media.video.api.mcp_server`.
418
+
419
+ ### Thin shims (repo clone)
420
+
421
+ [`audio-to-md/converter.py`](audio-to-md/converter.py) and [`video-to-md/converter.py`](video-to-md/converter.py) delegate to the same `main` as `md-audio` / `md-video`. Tests and `pytest.ini` live under `audio-to-md/tests/` and `video-to-md/tests/`.
422
+
423
+ ---
424
+
307
425
  ## HTTP API (FastAPI)
308
426
 
309
427
  All format APIs follow a similar pattern:
310
428
 
311
- - **`POST /convert/sync`** — upload a file; response is often a **ZIP** (artifact bundle) for larger formats.
429
+ - **`POST /convert/sync`** — upload a file (most converters) **or** send JSON (`url-to-md`); response is often a **ZIP** (artifact bundle) for larger formats.
312
430
  - **`POST /convert/jobs`** — async job; returns `job_id`.
313
431
  - **`GET /convert/jobs/{job_id}`** — status.
314
432
  - **`GET /convert/jobs/{job_id}/download`** — download result when ready.
315
433
 
316
- Upload field name is **`file`** (multipart form). Use `httpx` or `curl -F "file=@path/to/file"`.
434
+ Upload field name is **`file`** (multipart form) for file-based converters. Use `httpx` or `curl -F "file=@path/to/file"`. **URL** conversion uses a **JSON** body (`url` or `urls`); see [url-to-md/README.md](url-to-md/README.md).
317
435
 
318
436
  ### Run with Uvicorn
319
437
 
@@ -328,6 +446,9 @@ Install `mdengine[api]` plus the format extra(s), then run the **`app`** object
328
446
  | Image | `md_generator.image.api.main:app` | `image`, `api`, `mcp` |
329
447
  | Text/JSON/XML | `md_generator.text.api.main:app` | `text`, `api`, `mcp` |
330
448
  | ZIP | `md_generator.archive.api.main:app` | `archive`, `api`, `mcp` (+ extras for nested office/PDF) |
449
+ | URL / HTML | `md_generator.url.api.main:app` | `url`, `api`, `mcp` |
450
+ | Audio (Whisper) | `md_generator.media.audio.api.main:create_app` (use **`--factory`**) or `…main:app` | `audio`, `api`, `mcp` |
451
+ | Video (Whisper) | `md_generator.media.video.api.main:create_app` (use **`--factory`**) or `…main:app` | `video`, `api`, `mcp` |
331
452
 
332
453
  Examples:
333
454
 
@@ -335,6 +456,9 @@ Examples:
335
456
  uvicorn md_generator.pdf.api.main:app --host 127.0.0.1 --port 8001
336
457
  uvicorn md_generator.word.api.main:app --host 127.0.0.1 --port 8002
337
458
  uvicorn md_generator.archive.api.main:app --host 127.0.0.1 --port 8010
459
+ uvicorn md_generator.url.api.main:app --host 127.0.0.1 --port 8011
460
+ uvicorn md_generator.media.audio.api.main:create_app --factory --host 127.0.0.1 --port 8011
461
+ uvicorn md_generator.media.video.api.main:create_app --factory --host 127.0.0.1 --port 8012
338
462
  ```
339
463
 
340
464
  ### MCP over HTTP on the same server
@@ -353,6 +477,9 @@ Prefixes differ per service (often read from a `.env` file next to the process):
353
477
  | PPTX | `PPT_TO_MD_` | `PPT_TO_MD_MAX_UPLOAD_MB`, … |
354
478
  | Text | `TXT_JSON_XML_TO_MD_` | same pattern |
355
479
  | XLSX | `XLSX_TO_MD_` | `XLSX_TO_MD_TEMP_DIR`, `XLSX_TO_MD_CORS_ORIGINS`, etc. (see `md_generator.xlsx.api.app`) |
480
+ | 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` |
481
+ | 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` |
482
+ | Video API | `MD_VIDEO_` | Same pattern as audio with `MD_VIDEO_*` (defaults: larger upload/sync caps, port **8012**) |
356
483
 
357
484
  Exact variable names match the `ApiSettings` / helper functions in each `api/settings` or `api/app` module.
358
485
 
@@ -375,6 +502,9 @@ Two usage patterns:
375
502
  | PDF (FastMCP) | `python -m md_generator.pdf.api.mcp_server` / `--transport stdio` / `sse` / `streamable-http` |
376
503
  | PPTX | `python -m md_generator.ppt.api.mcp_server` (see module docstring for flags) |
377
504
  | Image | `python -m md_generator.image.api.mcp_server` (see module for CLI) |
505
+ | URL / HTML | `python -m md_generator.url.api.mcp_server` / `--transport sse` / `--transport streamable-http` |
506
+ | Audio | `md-audio-mcp` or `python -m md_generator.media.audio.api.mcp_server` — `--transport stdio` (default), `sse`, `streamable-http` |
507
+ | Video | `md-video-mcp` or `python -m md_generator.media.video.api.mcp_server` — same transports |
378
508
 
379
509
  **Word** and **XLSX** also ship a small runner script in the repo:
380
510
 
@@ -409,7 +539,7 @@ Tests live under each legacy folder’s `tests/` directory (e.g. `pdf-to-md/test
409
539
  |------|------|
410
540
  | `LICENSE` | MIT license text |
411
541
  | `CODE_OF_CONDUCT.md` | [Contributor Covenant](https://www.contributor-covenant.org/) 2.1 |
412
- | `src/md_generator/` | **Library source** (all formats + `api` subpackages) |
542
+ | `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/) |
413
543
  | `pyproject.toml` | Packaging, extras, CLI entry points, pytest |
414
544
  | `*-to-md/` | **Docs, tests, fixtures**, thin `converter.py` shims, some `run.py` helpers |
415
545
  | `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.1.0"
8
- description = "Convert PDF, Office, images, text/JSON/XML, and ZIP archives to Markdown."
7
+ version = "0.3.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"]
14
+ keywords = ["markdown", "pdf", "docx", "pptx", "xlsx", "ocr", "zip", "url", "html"]
15
15
  classifiers = [
16
16
  "Programming Language :: Python :: 3",
17
17
  "Programming Language :: Python :: 3 :: Only",
@@ -47,6 +47,35 @@ image-ocr = [
47
47
  ]
48
48
  text = []
49
49
  archive = ["Pillow>=10.0.0", "pytesseract>=0.3.10"]
50
+ url = [
51
+ "httpx>=0.27.0",
52
+ "beautifulsoup4>=4.12.0",
53
+ "lxml>=5.0.0",
54
+ "lxml_html_clean>=0.4.0",
55
+ "markdownify>=0.14.0",
56
+ "readability-lxml>=0.8.1",
57
+ ]
58
+ # Convenience extra for URL fetch + post-convert of downloaded office/PDF/ZIP/etc.
59
+ url-full = [
60
+ "httpx>=0.27.0",
61
+ "beautifulsoup4>=4.12.0",
62
+ "lxml>=5.0.0",
63
+ "lxml_html_clean>=0.4.0",
64
+ "markdownify>=0.14.0",
65
+ "readability-lxml>=0.8.1",
66
+ "pymupdf>=1.24.0",
67
+ "pdfplumber>=0.11.0",
68
+ "mammoth>=1.8.0",
69
+ "python-pptx>=1.0.0",
70
+ "Pillow>=10.0.0",
71
+ "olefile>=0.47",
72
+ "openpyxl>=3.1.0",
73
+ "pytesseract>=0.3.10",
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"]
50
79
  api = [
51
80
  "fastapi>=0.115.0",
52
81
  "uvicorn[standard]>=0.32.0",
@@ -55,7 +84,13 @@ api = [
55
84
  "pydantic-settings>=2.0.0",
56
85
  ]
57
86
  mcp = ["mcp>=1.2.0", "fastmcp>=2.3.0"]
58
- dev = ["pytest>=8.0.0", "httpx>=0.27.0", "mdengine[api]", "mdengine[mcp]"]
87
+ dev = [
88
+ "pytest>=8.0.0",
89
+ "httpx>=0.27.0",
90
+ "mdengine[api]",
91
+ "mdengine[mcp]",
92
+ "mdengine[url]",
93
+ ]
59
94
  all = [
60
95
  "pymupdf>=1.24.0",
61
96
  "pdfplumber>=0.11.0",
@@ -78,6 +113,9 @@ all = [
78
113
  "pydantic-settings>=2.0.0",
79
114
  "mcp>=1.2.0",
80
115
  "fastmcp>=2.3.0",
116
+ "beautifulsoup4>=4.12.0",
117
+ "lxml_html_clean>=0.4.0",
118
+ "readability-lxml>=0.8.1",
81
119
  ]
82
120
 
83
121
  [project.urls]
@@ -93,6 +131,13 @@ md-xlsx = "md_generator.xlsx.converter:main"
93
131
  md-image = "md_generator.image.converter:main"
94
132
  md-text = "md_generator.text.converter:main"
95
133
  md-zip = "md_generator.archive.converter:main"
134
+ md-url = "md_generator.url.converter:main"
135
+ md-audio = "md_generator.media.audio.converter:main"
136
+ md-video = "md_generator.media.video.converter:main"
137
+ md-audio-api = "md_generator.media.audio.api.run:main"
138
+ md-video-api = "md_generator.media.video.api.run:main"
139
+ md-audio-mcp = "md_generator.media.audio.api.mcp_server:main"
140
+ md-video-mcp = "md_generator.media.video.api.mcp_server:main"
96
141
 
97
142
  [tool.setuptools.packages.find]
98
143
  where = ["src"]
@@ -107,5 +152,8 @@ testpaths = [
107
152
  "image-to-md/tests",
108
153
  "txt-json-xml-to-md/tests",
109
154
  "zip-to-md/tests",
155
+ "url-to-md/tests",
156
+ "audio-to-md/tests",
157
+ "video-to-md/tests",
110
158
  ]
111
159
  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.1.0"
3
+ __version__ = "0.3.0"
4
4
 
5
5
  __all__ = ["__version__"]