mdengine 0.2.2__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.
- {mdengine-0.2.2/src/mdengine.egg-info → mdengine-0.3.0}/PKG-INFO +131 -5
- {mdengine-0.2.2 → mdengine-0.3.0}/README.md +123 -3
- {mdengine-0.2.2 → mdengine-0.3.0}/pyproject.toml +14 -2
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/__init__.py +1 -1
- mdengine-0.3.0/src/md_generator/media/__init__.py +21 -0
- mdengine-0.3.0/src/md_generator/media/audio/__init__.py +18 -0
- mdengine-0.3.0/src/md_generator/media/audio/api/__init__.py +1 -0
- mdengine-0.3.0/src/md_generator/media/audio/api/main.py +175 -0
- mdengine-0.3.0/src/md_generator/media/audio/api/mcp_server.py +38 -0
- mdengine-0.3.0/src/md_generator/media/audio/api/mcp_setup.py +84 -0
- mdengine-0.3.0/src/md_generator/media/audio/api/run.py +30 -0
- mdengine-0.3.0/src/md_generator/media/audio/api/settings.py +26 -0
- mdengine-0.3.0/src/md_generator/media/audio/converter.py +178 -0
- mdengine-0.3.0/src/md_generator/media/audio/formatter.py +70 -0
- mdengine-0.3.0/src/md_generator/media/audio/service.py +52 -0
- mdengine-0.3.0/src/md_generator/media/document_converter.py +239 -0
- mdengine-0.3.0/src/md_generator/media/job_store.py +94 -0
- mdengine-0.3.0/src/md_generator/media/video/__init__.py +9 -0
- mdengine-0.3.0/src/md_generator/media/video/api/__init__.py +1 -0
- mdengine-0.3.0/src/md_generator/media/video/api/main.py +174 -0
- mdengine-0.3.0/src/md_generator/media/video/api/mcp_server.py +38 -0
- mdengine-0.3.0/src/md_generator/media/video/api/mcp_setup.py +84 -0
- mdengine-0.3.0/src/md_generator/media/video/api/run.py +30 -0
- mdengine-0.3.0/src/md_generator/media/video/api/settings.py +26 -0
- mdengine-0.3.0/src/md_generator/media/video/converter.py +111 -0
- mdengine-0.3.0/src/md_generator/media/video/formatter.py +81 -0
- mdengine-0.3.0/src/md_generator/media/video/service.py +79 -0
- mdengine-0.3.0/src/md_generator/media/whisper_language.py +78 -0
- {mdengine-0.2.2 → mdengine-0.3.0/src/mdengine.egg-info}/PKG-INFO +131 -5
- {mdengine-0.2.2 → mdengine-0.3.0}/src/mdengine.egg-info/SOURCES.txt +24 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/mdengine.egg-info/entry_points.txt +6 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/mdengine.egg-info/requires.txt +8 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/LICENSE +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/setup.cfg +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/archive/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/archive/api/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/archive/api/convert_runner.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/archive/api/jobs.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/archive/api/main.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/archive/api/mcp_server.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/archive/api/mcp_setup.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/archive/api/query_options.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/archive/api/settings.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/archive/convert_impl.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/archive/converter.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/archive/options.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/api/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/api/main.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/api/mcp_server.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/api/query_options.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/api/settings.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/api/staging.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/api/zip_bundle.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/backends/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/backends/base.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/backends/easy.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/backends/paddle.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/backends/tesseract.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/convert_impl.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/converter.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/emit.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/io_util.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/image/utils.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/pdf/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/pdf/api/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/pdf/api/main.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/pdf/api/mcp_server.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/pdf/api/settings.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/pdf/api/zip_bundle.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/pdf/converter.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/pdf/md_emit.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/pdf/pdf_extract.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/pdf/utils.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/api/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/api/convert_runner.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/api/jobs.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/api/main.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/api/mcp_server.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/api/mcp_setup.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/api/query_options.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/api/settings.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/convert_impl.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/converter.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/embedded_extract.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/ooxml_media.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/options.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/post_assets.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/text_formatting.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/vendor_pdf_md/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/vendor_pdf_md/convert.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/vendor_word_md/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/vendor_word_md/convert.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/ppt/zip_deep.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/text/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/text/api/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/text/api/convert_runner.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/text/api/jobs.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/text/api/main.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/text/api/mcp_server.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/text/api/mcp_setup.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/text/api/query_options.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/text/api/settings.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/text/convert_impl.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/text/converter.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/text/format_detect.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/text/md_emit_json.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/text/md_emit_txt.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/text/md_emit_xml.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/text/options.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/api/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/api/convert_runner.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/api/jobs.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/api/main.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/api/mcp_server.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/api/mcp_setup.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/api/query_options.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/api/settings.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/assets.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/convert_impl.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/converter.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/crawl.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/extract.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/fetch.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/html_to_md.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/options.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/page_convert.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/post_convert_assets.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/url/robots_util.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/word/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/word/api/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/word/api/convert_util.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/word/api/jobs.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/word/api/main.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/word/api/mcp_server.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/word/artifact.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/word/converter.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/word/settings.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/xlsx/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/xlsx/api/__init__.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/xlsx/api/app.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/xlsx/convert_config.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/xlsx/converter.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/xlsx/converter_core.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/xlsx/excel_reader.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/xlsx/markdown_emitter.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/md_generator/xlsx/mcp_server.py +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/mdengine.egg-info/dependency_links.txt +0 -0
- {mdengine-0.2.2 → mdengine-0.3.0}/src/mdengine.egg-info/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mdengine
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Convert PDF, Office, images, text/JSON/XML, ZIP archives, and web URLs 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
|
|
@@ -68,6 +68,12 @@ 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"
|
|
71
77
|
Provides-Extra: api
|
|
72
78
|
Requires-Dist: fastapi>=0.115.0; extra == "api"
|
|
73
79
|
Requires-Dist: uvicorn[standard]>=0.32.0; extra == "api"
|
|
@@ -112,14 +118,14 @@ Dynamic: license-file
|
|
|
112
118
|
|
|
113
119
|
# mdengine
|
|
114
120
|
|
|
115
|
-
Single Python distribution for converting **PDF**, **Word (.docx)**, **PowerPoint (.pptx)**, **Excel (.xlsx/.xlsm)**, **images** (OCR), **plain text / JSON / XML**,
|
|
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.
|
|
116
122
|
|
|
117
123
|
- **PyPI name:** `mdengine` (import package: `md_generator`)
|
|
118
124
|
- **Source:** [github.com/vishal7090/md-generator](https://github.com/vishal7090/md-generator)
|
|
119
125
|
- **Python:** 3.10+
|
|
120
126
|
- **License:** [MIT](LICENSE)
|
|
121
127
|
|
|
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)
|
|
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)
|
|
123
129
|
|
|
124
130
|
---
|
|
125
131
|
|
|
@@ -186,6 +192,8 @@ pip install "mdengine[all]"
|
|
|
186
192
|
| `archive` | ZIP → Markdown layout (Pillow; optional tesseract for inline image OCR) |
|
|
187
193
|
| `url` | HTTP(S) HTML → Markdown (httpx, readability-lxml, markdownify, BeautifulSoup, lxml) |
|
|
188
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`) |
|
|
189
197
|
| `api` | FastAPI, uvicorn, httpx, pydantic-settings |
|
|
190
198
|
| `mcp` | MCP servers (`mcp`, `fastmcp` where used) |
|
|
191
199
|
| `dev` | pytest + API/MCP test helpers |
|
|
@@ -228,6 +236,12 @@ If the shell reports “command not found”, ensure the Python **Scripts** dire
|
|
|
228
236
|
| `md-text` | `md_generator.text.converter:main` | `md-text config.xml out.md` |
|
|
229
237
|
| `md-zip` | `md_generator.archive.converter:main` | `md-zip bundle.zip ./zip-out` |
|
|
230
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 |
|
|
231
245
|
|
|
232
246
|
Every command accepts **`-h` / `--help`** for full flags (artifact layout, OCR, ZIP options, etc.).
|
|
233
247
|
|
|
@@ -245,6 +259,8 @@ md-image ./photos ocr.md --engines tess --strategy best
|
|
|
245
259
|
md-text data.json data.md
|
|
246
260
|
md-zip archive.zip ./unzipped-md
|
|
247
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
|
|
248
264
|
```
|
|
249
265
|
|
|
250
266
|
**Windows PowerShell** (same commands; use backslashes for paths if you prefer)
|
|
@@ -253,6 +269,8 @@ md-url https://example.com/page ./page-bundle --artifact-layout
|
|
|
253
269
|
md-pdf .\manual.pdf .\out\doc.md
|
|
254
270
|
md-zip .\archive.zip .\zip-out
|
|
255
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
|
|
256
274
|
```
|
|
257
275
|
|
|
258
276
|
**Windows CMD**
|
|
@@ -422,6 +440,106 @@ convert_zip(
|
|
|
422
440
|
|
|
423
441
|
---
|
|
424
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
|
+
|
|
425
543
|
## HTTP API (FastAPI)
|
|
426
544
|
|
|
427
545
|
All format APIs follow a similar pattern:
|
|
@@ -447,6 +565,8 @@ Install `mdengine[api]` plus the format extra(s), then run the **`app`** object
|
|
|
447
565
|
| Text/JSON/XML | `md_generator.text.api.main:app` | `text`, `api`, `mcp` |
|
|
448
566
|
| ZIP | `md_generator.archive.api.main:app` | `archive`, `api`, `mcp` (+ extras for nested office/PDF) |
|
|
449
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` |
|
|
450
570
|
|
|
451
571
|
Examples:
|
|
452
572
|
|
|
@@ -455,6 +575,8 @@ uvicorn md_generator.pdf.api.main:app --host 127.0.0.1 --port 8001
|
|
|
455
575
|
uvicorn md_generator.word.api.main:app --host 127.0.0.1 --port 8002
|
|
456
576
|
uvicorn md_generator.archive.api.main:app --host 127.0.0.1 --port 8010
|
|
457
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
|
|
458
580
|
```
|
|
459
581
|
|
|
460
582
|
### MCP over HTTP on the same server
|
|
@@ -474,6 +596,8 @@ Prefixes differ per service (often read from a `.env` file next to the process):
|
|
|
474
596
|
| Text | `TXT_JSON_XML_TO_MD_` | same pattern |
|
|
475
597
|
| XLSX | `XLSX_TO_MD_` | `XLSX_TO_MD_TEMP_DIR`, `XLSX_TO_MD_CORS_ORIGINS`, etc. (see `md_generator.xlsx.api.app`) |
|
|
476
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**) |
|
|
477
601
|
|
|
478
602
|
Exact variable names match the `ApiSettings` / helper functions in each `api/settings` or `api/app` module.
|
|
479
603
|
|
|
@@ -497,6 +621,8 @@ Two usage patterns:
|
|
|
497
621
|
| PPTX | `python -m md_generator.ppt.api.mcp_server` (see module docstring for flags) |
|
|
498
622
|
| Image | `python -m md_generator.image.api.mcp_server` (see module for CLI) |
|
|
499
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 |
|
|
500
626
|
|
|
501
627
|
**Word** and **XLSX** also ship a small runner script in the repo:
|
|
502
628
|
|
|
@@ -531,7 +657,7 @@ Tests live under each legacy folder’s `tests/` directory (e.g. `pdf-to-md/test
|
|
|
531
657
|
|------|------|
|
|
532
658
|
| `LICENSE` | MIT license text |
|
|
533
659
|
| `CODE_OF_CONDUCT.md` | [Contributor Covenant](https://www.contributor-covenant.org/) 2.1 |
|
|
534
|
-
| `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/) |
|
|
535
661
|
| `pyproject.toml` | Packaging, extras, CLI entry points, pytest |
|
|
536
662
|
| `*-to-md/` | **Docs, tests, fixtures**, thin `converter.py` shims, some `run.py` helpers |
|
|
537
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**,
|
|
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,12 @@ 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-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 |
|
|
119
127
|
|
|
120
128
|
Every command accepts **`-h` / `--help`** for full flags (artifact layout, OCR, ZIP options, etc.).
|
|
121
129
|
|
|
@@ -133,6 +141,8 @@ md-image ./photos ocr.md --engines tess --strategy best
|
|
|
133
141
|
md-text data.json data.md
|
|
134
142
|
md-zip archive.zip ./unzipped-md
|
|
135
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
|
|
136
146
|
```
|
|
137
147
|
|
|
138
148
|
**Windows PowerShell** (same commands; use backslashes for paths if you prefer)
|
|
@@ -141,6 +151,8 @@ md-url https://example.com/page ./page-bundle --artifact-layout
|
|
|
141
151
|
md-pdf .\manual.pdf .\out\doc.md
|
|
142
152
|
md-zip .\archive.zip .\zip-out
|
|
143
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
|
|
144
156
|
```
|
|
145
157
|
|
|
146
158
|
**Windows CMD**
|
|
@@ -310,6 +322,106 @@ convert_zip(
|
|
|
310
322
|
|
|
311
323
|
---
|
|
312
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
|
+
|
|
313
425
|
## HTTP API (FastAPI)
|
|
314
426
|
|
|
315
427
|
All format APIs follow a similar pattern:
|
|
@@ -335,6 +447,8 @@ Install `mdengine[api]` plus the format extra(s), then run the **`app`** object
|
|
|
335
447
|
| Text/JSON/XML | `md_generator.text.api.main:app` | `text`, `api`, `mcp` |
|
|
336
448
|
| ZIP | `md_generator.archive.api.main:app` | `archive`, `api`, `mcp` (+ extras for nested office/PDF) |
|
|
337
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` |
|
|
338
452
|
|
|
339
453
|
Examples:
|
|
340
454
|
|
|
@@ -343,6 +457,8 @@ uvicorn md_generator.pdf.api.main:app --host 127.0.0.1 --port 8001
|
|
|
343
457
|
uvicorn md_generator.word.api.main:app --host 127.0.0.1 --port 8002
|
|
344
458
|
uvicorn md_generator.archive.api.main:app --host 127.0.0.1 --port 8010
|
|
345
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
|
|
346
462
|
```
|
|
347
463
|
|
|
348
464
|
### MCP over HTTP on the same server
|
|
@@ -362,6 +478,8 @@ Prefixes differ per service (often read from a `.env` file next to the process):
|
|
|
362
478
|
| Text | `TXT_JSON_XML_TO_MD_` | same pattern |
|
|
363
479
|
| XLSX | `XLSX_TO_MD_` | `XLSX_TO_MD_TEMP_DIR`, `XLSX_TO_MD_CORS_ORIGINS`, etc. (see `md_generator.xlsx.api.app`) |
|
|
364
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**) |
|
|
365
483
|
|
|
366
484
|
Exact variable names match the `ApiSettings` / helper functions in each `api/settings` or `api/app` module.
|
|
367
485
|
|
|
@@ -385,6 +503,8 @@ Two usage patterns:
|
|
|
385
503
|
| PPTX | `python -m md_generator.ppt.api.mcp_server` (see module docstring for flags) |
|
|
386
504
|
| Image | `python -m md_generator.image.api.mcp_server` (see module for CLI) |
|
|
387
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 |
|
|
388
508
|
|
|
389
509
|
**Word** and **XLSX** also ship a small runner script in the repo:
|
|
390
510
|
|
|
@@ -419,7 +539,7 @@ Tests live under each legacy folder’s `tests/` directory (e.g. `pdf-to-md/test
|
|
|
419
539
|
|------|------|
|
|
420
540
|
| `LICENSE` | MIT license text |
|
|
421
541
|
| `CODE_OF_CONDUCT.md` | [Contributor Covenant](https://www.contributor-covenant.org/) 2.1 |
|
|
422
|
-
| `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/) |
|
|
423
543
|
| `pyproject.toml` | Packaging, extras, CLI entry points, pytest |
|
|
424
544
|
| `*-to-md/` | **Docs, tests, fixtures**, thin `converter.py` shims, some `run.py` helpers |
|
|
425
545
|
| `README.md` | This document |
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mdengine"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "Convert PDF, Office, images, text/JSON/XML, ZIP archives, and web URLs 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"
|
|
@@ -72,6 +72,10 @@ 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"]
|
|
75
79
|
api = [
|
|
76
80
|
"fastapi>=0.115.0",
|
|
77
81
|
"uvicorn[standard]>=0.32.0",
|
|
@@ -128,6 +132,12 @@ md-image = "md_generator.image.converter:main"
|
|
|
128
132
|
md-text = "md_generator.text.converter:main"
|
|
129
133
|
md-zip = "md_generator.archive.converter:main"
|
|
130
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"
|
|
131
141
|
|
|
132
142
|
[tool.setuptools.packages.find]
|
|
133
143
|
where = ["src"]
|
|
@@ -143,5 +153,7 @@ testpaths = [
|
|
|
143
153
|
"txt-json-xml-to-md/tests",
|
|
144
154
|
"zip-to-md/tests",
|
|
145
155
|
"url-to-md/tests",
|
|
156
|
+
"audio-to-md/tests",
|
|
157
|
+
"video-to-md/tests",
|
|
146
158
|
]
|
|
147
159
|
markers = ["integration: optional dependency or slower checks"]
|
|
@@ -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
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from md_generator.media.audio.converter import (
|
|
2
|
+
AudioConverter,
|
|
3
|
+
MediaMetadata,
|
|
4
|
+
TranscriptSegment,
|
|
5
|
+
TranscriptionResult,
|
|
6
|
+
)
|
|
7
|
+
from md_generator.media.audio.formatter import AudioMarkdownFormatter, format_timestamped_transcript
|
|
8
|
+
from md_generator.media.audio.service import AudioToMarkdownService
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"AudioConverter",
|
|
12
|
+
"AudioMarkdownFormatter",
|
|
13
|
+
"AudioToMarkdownService",
|
|
14
|
+
"MediaMetadata",
|
|
15
|
+
"TranscriptSegment",
|
|
16
|
+
"TranscriptionResult",
|
|
17
|
+
"format_timestamped_transcript",
|
|
18
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""REST + MCP (Streamable HTTP under ``/mcp``) for audio-to-Markdown."""
|