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.
- {mdengine-0.1.0 → mdengine-0.3.0}/PKG-INFO +171 -9
- {mdengine-0.1.0 → mdengine-0.3.0}/README.md +136 -6
- {mdengine-0.1.0 → mdengine-0.3.0}/pyproject.toml +52 -4
- {mdengine-0.1.0 → 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.3.0/src/md_generator/url/__init__.py +24 -0
- mdengine-0.3.0/src/md_generator/url/api/__init__.py +1 -0
- mdengine-0.3.0/src/md_generator/url/api/convert_runner.py +34 -0
- mdengine-0.3.0/src/md_generator/url/api/jobs.py +92 -0
- mdengine-0.3.0/src/md_generator/url/api/main.py +377 -0
- mdengine-0.3.0/src/md_generator/url/api/mcp_server.py +37 -0
- mdengine-0.3.0/src/md_generator/url/api/mcp_setup.py +54 -0
- mdengine-0.3.0/src/md_generator/url/api/query_options.py +61 -0
- mdengine-0.3.0/src/md_generator/url/api/settings.py +25 -0
- mdengine-0.3.0/src/md_generator/url/assets.py +174 -0
- mdengine-0.3.0/src/md_generator/url/convert_impl.py +128 -0
- mdengine-0.3.0/src/md_generator/url/converter.py +200 -0
- mdengine-0.3.0/src/md_generator/url/crawl.py +223 -0
- mdengine-0.3.0/src/md_generator/url/extract.py +70 -0
- mdengine-0.3.0/src/md_generator/url/fetch.py +129 -0
- mdengine-0.3.0/src/md_generator/url/html_to_md.py +64 -0
- mdengine-0.3.0/src/md_generator/url/options.py +66 -0
- mdengine-0.3.0/src/md_generator/url/page_convert.py +102 -0
- mdengine-0.3.0/src/md_generator/url/post_convert_assets.py +387 -0
- mdengine-0.3.0/src/md_generator/url/robots_util.py +56 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/mdengine.egg-info/PKG-INFO +171 -9
- {mdengine-0.1.0 → mdengine-0.3.0}/src/mdengine.egg-info/SOURCES.txt +44 -0
- mdengine-0.3.0/src/mdengine.egg-info/entry_points.txt +15 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/mdengine.egg-info/requires.txt +36 -0
- mdengine-0.1.0/src/mdengine.egg-info/entry_points.txt +0 -8
- {mdengine-0.1.0 → mdengine-0.3.0}/LICENSE +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/setup.cfg +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/__init__.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/api/__init__.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/api/convert_runner.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/api/jobs.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/api/main.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/api/mcp_server.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/api/mcp_setup.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/api/query_options.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/api/settings.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/convert_impl.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/converter.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/archive/options.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/__init__.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/api/__init__.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/api/main.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/api/mcp_server.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/api/query_options.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/api/settings.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/api/staging.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/api/zip_bundle.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/backends/__init__.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/backends/base.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/backends/easy.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/backends/paddle.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/backends/tesseract.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/convert_impl.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/converter.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/emit.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/io_util.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/image/utils.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/__init__.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/api/__init__.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/api/main.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/api/mcp_server.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/api/settings.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/api/zip_bundle.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/converter.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/md_emit.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/pdf_extract.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/pdf/utils.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/__init__.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/api/__init__.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/api/convert_runner.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/api/jobs.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/api/main.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/api/mcp_server.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/api/mcp_setup.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/api/query_options.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/api/settings.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/convert_impl.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/converter.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/embedded_extract.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/ooxml_media.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/options.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/post_assets.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/text_formatting.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/vendor_pdf_md/__init__.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/vendor_pdf_md/convert.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/vendor_word_md/__init__.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/vendor_word_md/convert.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/ppt/zip_deep.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/__init__.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/api/__init__.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/api/convert_runner.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/api/jobs.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/api/main.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/api/mcp_server.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/api/mcp_setup.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/api/query_options.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/api/settings.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/convert_impl.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/converter.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/format_detect.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/md_emit_json.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/md_emit_txt.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/md_emit_xml.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/text/options.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/__init__.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/api/__init__.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/api/convert_util.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/api/jobs.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/api/main.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/api/mcp_server.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/artifact.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/converter.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/word/settings.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/__init__.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/api/__init__.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/api/app.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/convert_config.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/converter.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/converter_core.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/excel_reader.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/markdown_emitter.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/md_generator/xlsx/mcp_server.py +0 -0
- {mdengine-0.1.0 → mdengine-0.3.0}/src/mdengine.egg-info/dependency_links.txt +0 -0
- {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.
|
|
4
|
-
Summary: Convert PDF, Office, images, text/JSON/XML,
|
|
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**,
|
|
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**,
|
|
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.
|
|
8
|
-
description = "Convert PDF, Office, images, text/JSON/XML,
|
|
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 = [
|
|
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"]
|