diff --git a/.gitignore b/.gitignore index aa4abd38..2c651be8 100644 --- a/.gitignore +++ b/.gitignore @@ -165,3 +165,7 @@ cython_debug/ src/.DS_Store .DS_Store .cursorrules + +# MarkItDown Batch Converter - User files +uploads/ +outputs/ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index f9ba8cf6..00000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,9 +0,0 @@ -# Microsoft Open Source Code of Conduct - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). - -Resources: - -- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) -- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) -- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns diff --git a/README.md b/README.md deleted file mode 100644 index 652afc05..00000000 --- a/README.md +++ /dev/null @@ -1,248 +0,0 @@ -# MarkItDown - -[![PyPI](https://img.shields.io/pypi/v/markitdown.svg)](https://pypi.org/project/markitdown/) -![PyPI - Downloads](https://img.shields.io/pypi/dd/markitdown) -[![Built by AutoGen Team](https://img.shields.io/badge/Built%20by-AutoGen%20Team-blue)](https://github.com/microsoft/autogen) - -> [!TIP] -> MarkItDown now offers an MCP (Model Context Protocol) server for integration with LLM applications like Claude Desktop. See [markitdown-mcp](https://github.com/microsoft/markitdown/tree/main/packages/markitdown-mcp) for more information. - -> [!IMPORTANT] -> Breaking changes between 0.0.1 to 0.1.0: -> * Dependencies are now organized into optional feature-groups (further details below). Use `pip install 'markitdown[all]'` to have backward-compatible behavior. -> * convert\_stream() now requires a binary file-like object (e.g., a file opened in binary mode, or an io.BytesIO object). This is a breaking change from the previous version, where it previously also accepted text file-like objects, like io.StringIO. -> * The DocumentConverter class interface has changed to read from file-like streams rather than file paths. *No temporary files are created anymore*. If you are the maintainer of a plugin, or custom DocumentConverter, you likely need to update your code. Otherwise, if only using the MarkItDown class or CLI (as in these examples), you should not need to change anything. - -MarkItDown is a lightweight Python utility for converting various files to Markdown for use with LLMs and related text analysis pipelines. To this end, it is most comparable to [textract](https://github.com/deanmalmgren/textract), but with a focus on preserving important document structure and content as Markdown (including: headings, lists, tables, links, etc.) While the output is often reasonably presentable and human-friendly, it is meant to be consumed by text analysis tools -- and may not be the best option for high-fidelity document conversions for human consumption. - -MarkItDown currently supports the conversion from: - -- PDF -- PowerPoint -- Word -- Excel -- Images (EXIF metadata and OCR) -- Audio (EXIF metadata and speech transcription) -- HTML -- Text-based formats (CSV, JSON, XML) -- ZIP files (iterates over contents) -- Youtube URLs -- EPubs -- ... and more! - -## Why Markdown? - -Markdown is extremely close to plain text, with minimal markup or formatting, but still -provides a way to represent important document structure. Mainstream LLMs, such as -OpenAI's GPT-4o, natively "_speak_" Markdown, and often incorporate Markdown into their -responses unprompted. This suggests that they have been trained on vast amounts of -Markdown-formatted text, and understand it well. As a side benefit, Markdown conventions -are also highly token-efficient. - -## Prerequisites -MarkItDown requires Python 3.10 or higher. It is recommended to use a virtual environment to avoid dependency conflicts. - -With the standard Python installation, you can create and activate a virtual environment using the following commands: - -```bash -python -m venv .venv -source .venv/bin/activate -``` - -If using `uv`, you can create a virtual environment with: - -```bash -uv venv --python=3.12 .venv -source .venv/bin/activate -# NOTE: Be sure to use 'uv pip install' rather than just 'pip install' to install packages in this virtual environment -``` - -If you are using Anaconda, you can create a virtual environment with: - -```bash -conda create -n markitdown python=3.12 -conda activate markitdown -``` - -## Installation - -To install MarkItDown, use pip: `pip install 'markitdown[all]'`. Alternatively, you can install it from the source: - -```bash -git clone git@github.com:microsoft/markitdown.git -cd markitdown -pip install -e 'packages/markitdown[all]' -``` - -## Usage - -### Command-Line - -```bash -markitdown path-to-file.pdf > document.md -``` - -Or use `-o` to specify the output file: - -```bash -markitdown path-to-file.pdf -o document.md -``` - -You can also pipe content: - -```bash -cat path-to-file.pdf | markitdown -``` - -### Optional Dependencies -MarkItDown has optional dependencies for activating various file formats. Earlier in this document, we installed all optional dependencies with the `[all]` option. However, you can also install them individually for more control. For example: - -```bash -pip install 'markitdown[pdf, docx, pptx]' -``` - -will install only the dependencies for PDF, DOCX, and PPTX files. - -At the moment, the following optional dependencies are available: - -* `[all]` Installs all optional dependencies -* `[pptx]` Installs dependencies for PowerPoint files -* `[docx]` Installs dependencies for Word files -* `[xlsx]` Installs dependencies for Excel files -* `[xls]` Installs dependencies for older Excel files -* `[pdf]` Installs dependencies for PDF files -* `[outlook]` Installs dependencies for Outlook messages -* `[az-doc-intel]` Installs dependencies for Azure Document Intelligence -* `[audio-transcription]` Installs dependencies for audio transcription of wav and mp3 files -* `[youtube-transcription]` Installs dependencies for fetching YouTube video transcription - -### Plugins - -MarkItDown also supports 3rd-party plugins. Plugins are disabled by default. To list installed plugins: - -```bash -markitdown --list-plugins -``` - -To enable plugins use: - -```bash -markitdown --use-plugins path-to-file.pdf -``` - -To find available plugins, search GitHub for the hashtag `#markitdown-plugin`. To develop a plugin, see `packages/markitdown-sample-plugin`. - -### Azure Document Intelligence - -To use Microsoft Document Intelligence for conversion: - -```bash -markitdown path-to-file.pdf -o document.md -d -e "" -``` - -More information about how to set up an Azure Document Intelligence Resource can be found [here](https://learn.microsoft.com/en-us/azure/ai-services/document-intelligence/how-to-guides/create-document-intelligence-resource?view=doc-intel-4.0.0) - -### Python API - -Basic usage in Python: - -```python -from markitdown import MarkItDown - -md = MarkItDown(enable_plugins=False) # Set to True to enable plugins -result = md.convert("test.xlsx") -print(result.text_content) -``` - -Document Intelligence conversion in Python: - -```python -from markitdown import MarkItDown - -md = MarkItDown(docintel_endpoint="") -result = md.convert("test.pdf") -print(result.text_content) -``` - -To use Large Language Models for image descriptions (currently only for pptx and image files), provide `llm_client` and `llm_model`: - -```python -from markitdown import MarkItDown -from openai import OpenAI - -client = OpenAI() -md = MarkItDown(llm_client=client, llm_model="gpt-4o", llm_prompt="optional custom prompt") -result = md.convert("example.jpg") -print(result.text_content) -``` - -### Docker - -```sh -docker build -t markitdown:latest . -docker run --rm -i markitdown:latest < ~/your-file.pdf > output.md -``` - -## Contributing - -This project welcomes contributions and suggestions. Most contributions require you to agree to a -Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us -the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. - -When you submit a pull request, a CLA bot will automatically determine whether you need to provide -a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions -provided by the bot. You will only need to do this once across all repos using our CLA. - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or -contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. - -### How to Contribute - -You can help by looking at issues or helping review PRs. Any issue or PR is welcome, but we have also marked some as 'open for contribution' and 'open for reviewing' to help facilitate community contributions. These are of course just suggestions and you are welcome to contribute in any way you like. - -
- -| | All | Especially Needs Help from Community | -| ---------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | -| **Issues** | [All Issues](https://github.com/microsoft/markitdown/issues) | [Issues open for contribution](https://github.com/microsoft/markitdown/issues?q=is%3Aissue+is%3Aopen+label%3A%22open+for+contribution%22) | -| **PRs** | [All PRs](https://github.com/microsoft/markitdown/pulls) | [PRs open for reviewing](https://github.com/microsoft/markitdown/pulls?q=is%3Apr+is%3Aopen+label%3A%22open+for+reviewing%22) | - -
- -### Running Tests and Checks - -- Navigate to the MarkItDown package: - - ```sh - cd packages/markitdown - ``` - -- Install `hatch` in your environment and run tests: - - ```sh - pip install hatch # Other ways of installing hatch: https://hatch.pypa.io/dev/install/ - hatch shell - hatch test - ``` - - (Alternative) Use the Devcontainer which has all the dependencies installed: - - ```sh - # Reopen the project in Devcontainer and run: - hatch test - ``` - -- Run pre-commit checks before submitting a PR: `pre-commit run --all-files` - -### Contributing 3rd-party Plugins - -You can also contribute by creating and sharing 3rd party plugins. See `packages/markitdown-sample-plugin` for more details. - -## Trademarks - -This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft -trademarks or logos is subject to and must follow -[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). -Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. -Any use of third-party trademarks or logos are subject to those third-party's policies. diff --git a/SECURITY.md b/SECURITY.md index b3c89efc..6b906d43 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,41 +1,41 @@ - - -## Security - -Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin). - -If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below. - -## Reporting Security Issues - -**Please do not report security vulnerabilities through public GitHub issues.** - -Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report). - -If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp). - -You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). - -Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: - - * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) - * Full paths of source file(s) related to the manifestation of the issue - * The location of the affected source code (tag/branch/commit or direct URL) - * Any special configuration required to reproduce the issue - * Step-by-step instructions to reproduce the issue - * Proof-of-concept or exploit code (if possible) - * Impact of the issue, including how an attacker might exploit the issue - -This information will help us triage your report more quickly. - -If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs. - -## Preferred Languages - -We prefer all communications to be in English. - -## Policy - -Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). - - + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). + + diff --git a/SUPPORT.md b/SUPPORT.md deleted file mode 100644 index 291d4d43..00000000 --- a/SUPPORT.md +++ /dev/null @@ -1,25 +0,0 @@ -# TODO: The maintainer of this repo has not yet edited this file - -**REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project? - -- **No CSS support:** Fill out this template with information about how to file issues and get help. -- **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work with/help you to determine next steps. -- **Not sure?** Fill out an intake as though the answer were "Yes". CSS will help you decide. - -*Then remove this first heading from this SUPPORT.MD file before publishing your repo.* - -# Support - -## How to file issues and get help - -This project uses GitHub Issues to track bugs and feature requests. Please search the existing -issues before filing new issues to avoid duplicates. For new issues, file your bug or -feature request as a new Issue. - -For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE -FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER -CHANNEL. WHERE WILL YOU HELP PEOPLE?**. - -## Microsoft Support Policy - -Support for this **PROJECT or PRODUCT** is limited to the resources listed above. diff --git a/code.html b/code.html new file mode 100644 index 00000000..76f20445 --- /dev/null +++ b/code.html @@ -0,0 +1,553 @@ + + + + + +MarkItDown Batch Converter + + + + + + + + +
+
+ +
+
+
+
+

MarkItDown

+

Batch Converter

+
+
+ +
+upload_file +

Drag and drop files here to add them to the queue.

+
+
+ +
+
+

Conversion Queue

+ +
+
+
+ +
+inbox +

Your queue is empty

+

Add files to get started.

+
+
+
+ + +
+
+
+ + + + \ No newline at end of file diff --git a/requirements-server.txt b/requirements-server.txt new file mode 100644 index 00000000..506fe92c --- /dev/null +++ b/requirements-server.txt @@ -0,0 +1,4 @@ +flask>=3.0.0 +flask-cors>=4.0.0 +werkzeug>=3.0.0 +markitdown[all]>=0.1.0 diff --git a/screen.png b/screen.png new file mode 100644 index 00000000..e3583443 Binary files /dev/null and b/screen.png differ diff --git a/server.py b/server.py new file mode 100644 index 00000000..df41a8e1 --- /dev/null +++ b/server.py @@ -0,0 +1,367 @@ +""" +Flask backend API for MarkItDown Batch Converter +Handles file uploads, conversions, and serves the frontend +""" +import os +import sys +import uuid +import json +from pathlib import Path +from flask import Flask, request, jsonify, send_from_directory, Response +from flask_cors import CORS +from werkzeug.utils import secure_filename +import threading +import time +import queue + +# Add the markitdown package to the path +sys.path.insert(0, str(Path(__file__).parent / "packages" / "markitdown" / "src")) + +from markitdown import MarkItDown + +app = Flask(__name__) +CORS(app) + +# Configuration +UPLOAD_FOLDER = Path('uploads') +OUTPUT_FOLDER = Path('outputs') +UPLOAD_FOLDER.mkdir(exist_ok=True) +OUTPUT_FOLDER.mkdir(exist_ok=True) + +app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER +app.config['OUTPUT_FOLDER'] = OUTPUT_FOLDER +app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 # 100MB max file size + +# Store conversion jobs in memory (in production, use a database) +conversion_jobs = {} +job_lock = threading.Lock() + +# SSE notification system +notification_queues = {} +notification_lock = threading.Lock() + +# Initialize MarkItDown +markitdown = MarkItDown() + + +class ConversionJob: + def __init__(self, file_id, filename, filesize): + self.file_id = file_id + self.filename = filename + self.filesize = filesize + self.status = 'waiting' # waiting, converting, completed, failed + self.progress = 0 + self.error = None + self.output_file = None + self.created_at = time.time() + + +def broadcast_notification(event_type, data): + """Send notification to all connected SSE clients""" + with notification_lock: + message = f"event: {event_type}\ndata: {json.dumps(data)}\n\n" + # Send to all connected clients + for client_id, client_queue in list(notification_queues.items()): + try: + client_queue.put_nowait(message) + except queue.Full: + # Remove disconnected clients + del notification_queues[client_id] + + +def notify_status_change(file_id, status, **kwargs): + """Notify clients about status changes""" + with job_lock: + if file_id in conversion_jobs: + job = conversion_jobs[file_id] + data = { + 'file_id': file_id, + 'filename': job.filename, + 'status': status, + 'progress': job.progress, + 'error': job.error, + 'output_file': job.output_file, + **kwargs + } + broadcast_notification('status_update', data) + + +def convert_file_async(file_id, input_path, output_path): + """Convert file in background thread""" + try: + with job_lock: + conversion_jobs[file_id].status = 'converting' + conversion_jobs[file_id].progress = 10 + + # Notify conversion started + notify_status_change(file_id, 'converting', progress=10) + + # Perform conversion + result = markitdown.convert(input_path) + + with job_lock: + conversion_jobs[file_id].progress = 80 + + # Notify progress + notify_status_change(file_id, 'converting', progress=80) + + # Write output + with open(output_path, 'w', encoding='utf-8') as f: + f.write(result.text_content) + + with job_lock: + conversion_jobs[file_id].status = 'completed' + conversion_jobs[file_id].progress = 100 + conversion_jobs[file_id].output_file = output_path.name + + # Notify completion immediately + notify_status_change(file_id, 'completed', progress=100) + + except Exception as e: + with job_lock: + conversion_jobs[file_id].status = 'failed' + conversion_jobs[file_id].error = str(e) + + # Notify failure immediately + notify_status_change(file_id, 'failed', error=str(e)) + + +@app.route('/') +def index(): + """Serve the frontend HTML""" + return send_from_directory('.', 'code.html') + + +@app.route('/api/events') +def events(): + """Server-Sent Events endpoint for real-time notifications""" + def event_stream(): + # Create a unique client ID and queue + client_id = str(uuid.uuid4()) + client_queue = queue.Queue(maxsize=100) + + with notification_lock: + notification_queues[client_id] = client_queue + + try: + # Send initial connection confirmation + yield f"event: connected\ndata: {json.dumps({'client_id': client_id})}\n\n" + + # Send current status of all jobs + with job_lock: + for job in conversion_jobs.values(): + data = { + 'file_id': job.file_id, + 'filename': job.filename, + 'status': job.status, + 'progress': job.progress, + 'error': job.error, + 'output_file': job.output_file + } + yield f"event: status_update\ndata: {json.dumps(data)}\n\n" + + # Keep connection alive and send notifications + while True: + try: + # Wait for notification with timeout + message = client_queue.get(timeout=30) + yield message + except queue.Empty: + # Send heartbeat to keep connection alive + yield f"event: heartbeat\ndata: {json.dumps({'timestamp': time.time()})}\n\n" + + except GeneratorExit: + # Client disconnected + pass + finally: + # Clean up client queue + with notification_lock: + if client_id in notification_queues: + del notification_queues[client_id] + + return Response(event_stream(), mimetype='text/event-stream', + headers={'Cache-Control': 'no-cache', + 'Connection': 'keep-alive', + 'Access-Control-Allow-Origin': '*'}) + + +@app.route('/api/upload', methods=['POST']) +def upload_files(): + """Handle file uploads""" + if 'files' not in request.files: + return jsonify({'error': 'No files provided'}), 400 + + files = request.files.getlist('files') + uploaded_files = [] + + for file in files: + if file.filename == '': + continue + + # Generate unique ID + file_id = str(uuid.uuid4()) + + # Secure filename + original_filename = secure_filename(file.filename) + filename = f"{file_id}_{original_filename}" + filepath = app.config['UPLOAD_FOLDER'] / filename + + # Save file + file.save(filepath) + + # Get file size + filesize = filepath.stat().st_size + + # Create job entry + with job_lock: + conversion_jobs[file_id] = ConversionJob( + file_id=file_id, + filename=original_filename, + filesize=filesize + ) + + uploaded_files.append({ + 'file_id': file_id, + 'filename': original_filename, + 'filesize': filesize, + 'status': 'waiting' + }) + + return jsonify({'files': uploaded_files}) + + +@app.route('/api/convert', methods=['POST']) +def convert_files(): + """Start conversion for all waiting files""" + data = request.json + file_ids = data.get('file_ids', []) + + if not file_ids: + # Convert all waiting files + with job_lock: + file_ids = [ + job.file_id for job in conversion_jobs.values() + if job.status == 'waiting' + ] + + # Start conversion threads + for file_id in file_ids: + with job_lock: + if file_id not in conversion_jobs: + continue + + job = conversion_jobs[file_id] + if job.status != 'waiting': + continue + + # Find input file + input_files = list(app.config['UPLOAD_FOLDER'].glob(f"{file_id}_*")) + if not input_files: + continue + + input_path = input_files[0] + output_path = app.config['OUTPUT_FOLDER'] / f"{file_id}_{Path(job.filename).stem}.md" + + # Start conversion in background thread + thread = threading.Thread( + target=convert_file_async, + args=(file_id, input_path, output_path) + ) + thread.daemon = True + thread.start() + + return jsonify({'message': f'Started conversion for {len(file_ids)} files'}) + + +@app.route('/api/status', methods=['GET']) +def get_status(): + """Get status of all conversion jobs""" + with job_lock: + jobs_list = [] + for job in conversion_jobs.values(): + jobs_list.append({ + 'file_id': job.file_id, + 'filename': job.filename, + 'filesize': job.filesize, + 'status': job.status, + 'progress': job.progress, + 'error': job.error, + 'output_file': job.output_file + }) + + return jsonify({'jobs': jobs_list}) + + +@app.route('/api/delete/', methods=['DELETE']) +def delete_file(file_id): + """Delete a file from the queue""" + with job_lock: + if file_id not in conversion_jobs: + return jsonify({'error': 'File not found'}), 404 + + job = conversion_jobs[file_id] + + # Delete input file + input_files = list(app.config['UPLOAD_FOLDER'].glob(f"{file_id}_*")) + for f in input_files: + f.unlink(missing_ok=True) + + # Delete output file if exists + if job.output_file: + output_path = app.config['OUTPUT_FOLDER'] / job.output_file + output_path.unlink(missing_ok=True) + + # Remove from jobs + del conversion_jobs[file_id] + + return jsonify({'message': 'File deleted successfully'}) + + +@app.route('/api/download/', methods=['GET']) +def download_file(file_id): + """Download converted markdown file""" + with job_lock: + if file_id not in conversion_jobs: + return jsonify({'error': 'File not found'}), 404 + + job = conversion_jobs[file_id] + if job.status != 'completed' or not job.output_file: + return jsonify({'error': 'Conversion not completed'}), 400 + + return send_from_directory( + app.config['OUTPUT_FOLDER'], + job.output_file, + as_attachment=True + ) + + +@app.route('/api/clear', methods=['POST']) +def clear_completed(): + """Clear all completed conversions""" + with job_lock: + completed_ids = [ + job.file_id for job in conversion_jobs.values() + if job.status == 'completed' + ] + + for file_id in completed_ids: + job = conversion_jobs[file_id] + + # Delete files + input_files = list(app.config['UPLOAD_FOLDER'].glob(f"{file_id}_*")) + for f in input_files: + f.unlink(missing_ok=True) + + if job.output_file: + output_path = app.config['OUTPUT_FOLDER'] / job.output_file + output_path.unlink(missing_ok=True) + + del conversion_jobs[file_id] + + return jsonify({'message': f'Cleared {len(completed_ids)} completed jobs'}) + + +if __name__ == '__main__': + print("Starting MarkItDown Batch Converter Server...") + print("Open http://localhost:5000 in your browser") + app.run(debug=True, port=5000, threaded=True) diff --git a/start_converter.bat b/start_converter.bat new file mode 100644 index 00000000..db625921 --- /dev/null +++ b/start_converter.bat @@ -0,0 +1,40 @@ +@echo off +echo ======================================== +echo MarkItDown Batch Converter +echo ======================================== +echo. + +REM Check if virtual environment exists +if not exist ".venv\Scripts\activate.bat" ( + echo Creating virtual environment... + python -m venv .venv + echo. +) + +REM Activate virtual environment +echo Activating virtual environment... +call .venv\Scripts\activate.bat +echo. + +REM Check if requirements are installed +pip show flask >nul 2>&1 +if errorlevel 1 ( + echo Installing dependencies... + pip install -r requirements-server.txt + echo. +) + +REM Start the server +echo Starting MarkItDown Batch Converter Server... +echo. +echo ======================================== +echo Server will be available at: +echo http://localhost:5000 +echo ======================================== +echo. +echo Press Ctrl+C to stop the server +echo. + +python server.py + +pause