diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..7ea633c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,14 @@ +.git +.github +.gitignore +.ruff_cache +__pycache__ +**/__pycache__ +docs +input +*.md +!README.md +venv +.venv +Dockerfile +.dockerignore diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..3e2abd6 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,39 @@ +name: Docker + +on: + push: + tags: ["v*"] + workflow_dispatch: + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v7 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=semver,pattern={{version}} + type=raw,value=latest + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.gitignore b/.gitignore index 1a70eca..832f01e 100644 --- a/.gitignore +++ b/.gitignore @@ -176,3 +176,4 @@ config.yaml # macOS .DS_Store +.claude/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d9f7a46 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +# CPU-only image. For GPU acceleration install a CUDA/ROCm PyTorch build +# instead (see README) or run natively. +FROM python:3.13-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY main.py . +COPY modules/ modules/ + +# marker-pdf downloads its models on first run; mount a volume at this path +# to cache them across container runs. +ENV HF_HOME=/models +VOLUME /models + +# PDFs are read from /data/input and results written next to them by default. +WORKDIR /data + +ENTRYPOINT ["python", "/app/main.py"] diff --git a/README.md b/README.md index d004cbd..16c6b4a 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,15 @@ Python 3.13 is recommended. `marker-pdf` constrains `Pillow<11.0.0`, and Pillow only ships Python 3.14 wheels from 11.3.0 onward. On Python 3.10–3.13 every dependency installs as a prebuilt wheel. On 3.14, pip has to build Pillow from source instead: this -works, but it is slower and requires a working C toolchain. +works, but it is slower and requires a working C toolchain plus the image +library headers Pillow links against. On Debian/Ubuntu install them first: + +```bash +sudo apt install libjpeg-dev zlib1g-dev libtiff-dev libfreetype6-dev libwebp-dev +``` + +Without these headers the install fails with +`RequiredDependencyException: The headers or library files could not be found for jpeg`. ## 💻 Installation @@ -74,6 +82,30 @@ print(torch.backends.mps.is_available()) # Should return True for Apple Silicon print(torch.version.hip) # Should print ROCm version for AMD ``` +### 🐳 Docker + +A CPU-only image can be built from the included `Dockerfile`: + +```bash +docker build -t pdf2epub . +``` + +Run it with your PDFs mounted at `/data` and a model cache volume (marker-pdf +downloads its models on first run): + +```bash +docker run -it --rm \ + -v "$(pwd)":/data \ + -v pdf2epub-models:/models \ + pdf2epub input.pdf +``` + +`-it` is required for EPUB generation because metadata is prompted +interactively; with `--skip-epub` it can run non-interactively. + +Tagged releases are also published to +`ghcr.io/overcuriousity/pdf2epub` by the Docker workflow. + ## 🚀 Usage ### Basic Usage diff --git a/main.py b/main.py index dbb2ea2..14a0f74 100755 --- a/main.py +++ b/main.py @@ -11,7 +11,7 @@ def main(): if torch.cuda.is_available(): print("CUDA is available. Using GPU for processing.") - elif torch.mps.is_available(): + elif torch.backends.mps.is_available(): print("MPS is available. Using Apple Silicon for processing.") else: print("CUDA is not available. Using CPU for processing.") diff --git a/modules/mark2epub.py b/modules/mark2epub.py index ee09f61..7a289aa 100644 --- a/modules/mark2epub.py +++ b/modules/mark2epub.py @@ -58,7 +58,12 @@ def review_markdown(markdown_path: Path) -> tuple[bool, str]: response = input("\nWould you like to review the markdown file before conversion? (y/n): ").lower() if response in ['y', 'yes']: try: - subprocess.run(['xdg-open' if os.name == 'posix' else 'start', str(markdown_path)], check=True) + if sys.platform == 'darwin': + subprocess.run(['open', str(markdown_path)], check=True) + elif os.name == 'posix': + subprocess.run(['xdg-open', str(markdown_path)], check=True) + else: + os.startfile(str(markdown_path)) while True: proceed = input("\nPress Enter when you're done editing (or 'q' to abort): ").lower() @@ -333,8 +338,8 @@ def get_coverpage_XML(title, authors):
{authors}
+{xml_escape(authors) if authors else ''}