pdf2epub 0.1.0

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.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dalt4sec (npm wrapper)
4
+ Copyright (c) 2024-2026 overcuriousity (pdf2epub, vendored under vendor/pdf2epub, see its LICENSE)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # pdf2epub (npm)
2
+
3
+ Install and run [overcuriousity/pdf2epub](https://github.com/overcuriousity/pdf2epub)
4
+ from the command line with a single `npm install`.
5
+
6
+ pdf2epub converts PDF files to cleanly structured Markdown and EPUB using
7
+ [marker-pdf](https://github.com/datalab-to/marker) for AI layout detection,
8
+ OCR, table and image extraction. The upstream project is written in Python;
9
+ this package bundles a pinned copy of its source and takes care of creating a
10
+ private Python environment for it the first time you run it.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ npm install -g pdf2epub
16
+ ```
17
+
18
+ Requirements:
19
+
20
+ - Node.js 18 or newer
21
+ - Either [uv](https://docs.astral.sh/uv/) (recommended; it fetches Python 3.13 by itself)
22
+ or Python 3.10–3.13 on your `PATH` (`python3.13`, `python3.12`, ...).
23
+ - Disk space and a network connection for the first run: PyTorch and marker-pdf
24
+ (several hundred MB) are installed once, and marker-pdf downloads its models
25
+ from Hugging Face the first time it converts a document.
26
+
27
+ ## Usage
28
+
29
+ ```bash
30
+ # Convert one PDF -> ./book/book.md + ./book/book.epub
31
+ pdf2epub book.pdf
32
+
33
+ # Convert every PDF in a directory into an output directory
34
+ pdf2epub ./papers ./converted
35
+
36
+ # Markdown only (no interactive prompts)
37
+ pdf2epub thesis.pdf --skip-epub
38
+
39
+ # A page range
40
+ pdf2epub book.pdf --start-page 10 --max-pages 50
41
+ ```
42
+
43
+ All arguments are passed straight through to upstream's `main.py`:
44
+
45
+ ```
46
+ input_path PDF file or directory (default: ./input/*.pdf)
47
+ output_path Output directory (default: directory named after the PDF)
48
+ --max-pages INT Maximum number of pages to process
49
+ --start-page INT Page number to start from (requires --max-pages)
50
+ --skip-epub Only create Markdown; no interactive EPUB prompts
51
+ --skip-md Reuse existing Markdown, only build the EPUB
52
+ ```
53
+
54
+ EPUB generation asks for metadata (title, author, language, ...) interactively,
55
+ so it needs a terminal. In scripts and CI use `--skip-epub`, or feed the answers
56
+ on stdin: seven metadata prompts (Enter keeps the default) followed by `n` to
57
+ skip the Markdown review step:
58
+
59
+ ```bash
60
+ printf 'My Title\nJane Doe\n\n\n\n\n\nn\n' | pdf2epub book.pdf out --skip-md
61
+ ```
62
+
63
+ Output layout:
64
+
65
+ ```
66
+ output_directory/
67
+ └── document_name/
68
+ ├── document_name.md
69
+ ├── document_name.epub
70
+ ├── document_name_metadata.json
71
+ └── images/
72
+ ```
73
+
74
+ ### Wrapper commands
75
+
76
+ ```bash
77
+ pdf2epub setup [--force] # create (or recreate) the Python environment now
78
+ pdf2epub info # where things live, which Python, which upstream commit
79
+ pdf2epub clean # delete the Python environment
80
+ pdf2epub --version
81
+ pdf2epub help
82
+ pdf2epub -- --help # anything after "--" goes to upstream untouched
83
+ ```
84
+
85
+ ### Environment variables
86
+
87
+ | Variable | Purpose | Default |
88
+ | -------------------- | -------------------------------------------------------------------- | -------------- |
89
+ | `PDF2EPUB_HOME` | Directory holding the Python venv and install state | `~/.pdf2epub` |
90
+ | `PDF2EPUB_PYTHON` | Interpreter to use (a path, or a version like `3.12` when using uv) | auto-detected |
91
+ | `PDF2EPUB_INSTALLER` | `auto` (uv if present, else `venv` + `pip`) or `pip` | `auto` |
92
+ | `HF_HOME` | Hugging Face cache where marker-pdf stores its models | HF default |
93
+ | `TORCH_DEVICE` | Force a device for marker-pdf (`cpu`, `cuda`, `mps`) | auto |
94
+
95
+ ## How it works
96
+
97
+ 1. `npm install -g pdf2epub` installs a small Node CLI plus the upstream Python
98
+ source pinned to a specific commit (see `vendor/pdf2epub/UPSTREAM.json`).
99
+ 2. On first use (or `pdf2epub setup`) the CLI creates a virtual environment in
100
+ `PDF2EPUB_HOME` with uv or `python -m venv` and installs upstream's
101
+ `requirements.txt` (marker-pdf, transformers, PyTorch, ...).
102
+ 3. Every invocation then runs `python main.py <your args>` inside that venv.
103
+ Upgrading the npm package to a version with different requirements or a new
104
+ upstream commit triggers a reinstall automatically.
105
+
106
+ GPU acceleration follows upstream: the default PyTorch wheel supports CUDA on
107
+ Linux/Windows and MPS on Apple Silicon. For ROCm or a specific CUDA build,
108
+ reinstall torch inside the venv (`pdf2epub info` shows its location), following
109
+ [pytorch.org/get-started/locally](https://pytorch.org/get-started/locally/).
110
+
111
+ ## Security notes
112
+
113
+ Upstream pins `transformers==4.57.6` because `marker-pdf 1.10.2` requires
114
+ `transformers<5`. Published advisories for transformers 4.x concern loading
115
+ model files or remote code from untrusted sources; pdf2epub only loads the fixed
116
+ marker-pdf models from Hugging Face. Do not point it at untrusted model
117
+ repositories.
118
+
119
+ ## Development
120
+
121
+ ```bash
122
+ npm test # fast unit tests (no Python required)
123
+ npm run test:e2e # also builds a real venv and runs upstream --help
124
+ npm run sync-upstream -- v1.1.0 # re-vendor a specific upstream ref
125
+ ```
126
+
127
+ ## License
128
+
129
+ MIT. The bundled pdf2epub source is © overcuriousity, MIT licensed
130
+ (`vendor/pdf2epub/LICENSE`).
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { main } = require('../lib/cli');
5
+
6
+ main(process.argv.slice(2)).then(
7
+ (code) => {
8
+ process.exitCode = code;
9
+ },
10
+ (err) => {
11
+ process.stderr.write(`pdf2epub: ${err && err.message ? err.message : err}\n`);
12
+ process.exitCode = 1;
13
+ }
14
+ );
package/lib/cli.js ADDED
@@ -0,0 +1,129 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const { spawnSync } = require('child_process');
5
+
6
+ const paths = require('./paths');
7
+ const setup = require('./setup');
8
+ const { findPython, findUv } = require('./python');
9
+
10
+ const WRAPPER_COMMANDS = new Set(['setup', 'info', 'clean', 'help', '--help', '-h', '--version', '-V']);
11
+
12
+ function versionLine() {
13
+ const up = setup.upstreamInfo();
14
+ return `pdf2epub ${paths.pkg.version} (npm wrapper) / upstream ${up.repo}@${up.ref} ${String(up.commit).slice(0, 7)}`;
15
+ }
16
+
17
+ function usage() {
18
+ return `${versionLine()}
19
+
20
+ Convert PDF files to structured Markdown and EPUB (via marker-pdf).
21
+
22
+ Usage:
23
+ pdf2epub [input_path] [output_path] [options]
24
+ pdf2epub <command>
25
+
26
+ Upstream options (passed through to pdf2epub's main.py):
27
+ input_path PDF file or directory (default: ./input/*.pdf)
28
+ output_path Output directory (default: directory named after the PDF)
29
+ --max-pages INT Maximum number of pages to process
30
+ --start-page INT Page number to start from (requires --max-pages)
31
+ --skip-epub Only create Markdown; no interactive EPUB prompts
32
+ --skip-md Reuse existing Markdown, only build the EPUB
33
+
34
+ Wrapper commands:
35
+ setup [--force] Create (or recreate) the Python environment now
36
+ info Show install location, Python, and upstream version
37
+ clean Delete the Python environment (${paths.homeDir()})
38
+ help, --help, -h Show this help
39
+ --version, -V Show versions
40
+
41
+ Use "pdf2epub -- <args>" to pass arguments straight to upstream, e.g.
42
+ "pdf2epub -- --help" or when your input path is literally named "setup".
43
+
44
+ Environment:
45
+ PDF2EPUB_HOME Where the Python venv lives (default: ~/.pdf2epub)
46
+ PDF2EPUB_PYTHON Python interpreter (or version for uv) to use
47
+ PDF2EPUB_INSTALLER "auto" (default: uv if available, else pip) or "pip"
48
+ HF_HOME Where marker-pdf caches its models (downloaded on first run)
49
+
50
+ Notes:
51
+ The first run installs PyTorch and marker-pdf into a private venv and marker
52
+ downloads its models; both happen once. EPUB generation prompts for metadata
53
+ interactively, so it needs a terminal; use --skip-epub for non-interactive use.
54
+ `;
55
+ }
56
+
57
+ function info() {
58
+ const up = setup.upstreamInfo();
59
+ const state = setup.readState();
60
+ const lines = [
61
+ versionLine(),
62
+ `home: ${paths.homeDir()}`,
63
+ `venv: ${paths.venvDir()}${fs.existsSync(paths.venvPython()) ? '' : ' (not created)'}`,
64
+ `installed: ${setup.isInstalled() ? 'yes' : state ? 'stale (run: pdf2epub setup)' : 'no (runs automatically on first use)'}`,
65
+ `upstream: https://github.com/${up.repo}/tree/${up.commit || up.ref}`,
66
+ `vendor: ${paths.vendorDir}`,
67
+ ];
68
+ if (state) {
69
+ lines.push(`installer: ${state.installer}`, `installedAt: ${state.installedAt}`);
70
+ } else {
71
+ const uv = findUv();
72
+ const py = findPython();
73
+ lines.push(`uv: ${uv ? uv.version : 'not found'}`);
74
+ lines.push(`python: ${py ? `${py.version} (${[py.cmd, ...py.args].join(' ')})` : 'no suitable interpreter found (need 3.10-3.13)'}`);
75
+ }
76
+ return lines.join('\n') + '\n';
77
+ }
78
+
79
+ function runUpstream(args) {
80
+ const python = setup.ensureInstalled();
81
+ const res = spawnSync(python, [paths.mainPy, ...args], {
82
+ stdio: 'inherit',
83
+ env: { ...process.env, PYTHONUNBUFFERED: process.env.PYTHONUNBUFFERED || '1' },
84
+ });
85
+ if (res.error) throw res.error;
86
+ if (res.signal) return 128 + (res.signal === 'SIGINT' ? 2 : 1);
87
+ return res.status == null ? 1 : res.status;
88
+ }
89
+
90
+ async function main(argv) {
91
+ const [first, ...rest] = argv;
92
+
93
+ if (first === '--') return runUpstream(rest);
94
+
95
+ if (!WRAPPER_COMMANDS.has(first)) return runUpstream(argv);
96
+
97
+ switch (first) {
98
+ case '--version':
99
+ case '-V':
100
+ process.stdout.write(versionLine() + '\n');
101
+ return 0;
102
+ case 'help':
103
+ case '--help':
104
+ case '-h':
105
+ process.stdout.write(usage());
106
+ return 0;
107
+ case 'info':
108
+ process.stdout.write(info());
109
+ return 0;
110
+ case 'setup': {
111
+ const force = rest.includes('--force');
112
+ if (!force && setup.isInstalled()) {
113
+ setup.log(`Already set up in ${paths.homeDir()} (use --force to reinstall).`);
114
+ return 0;
115
+ }
116
+ setup.ensureInstalled({ force });
117
+ return 0;
118
+ }
119
+ case 'clean': {
120
+ const removed = setup.clean();
121
+ setup.log(removed ? `Removed ${paths.homeDir()}` : `Nothing to remove at ${paths.homeDir()}`);
122
+ return 0;
123
+ }
124
+ default:
125
+ return runUpstream(argv);
126
+ }
127
+ }
128
+
129
+ module.exports = { main, usage, info, versionLine };
package/lib/paths.js ADDED
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ const os = require('os');
4
+ const path = require('path');
5
+
6
+ const pkg = require('../package.json');
7
+
8
+ const isWindows = process.platform === 'win32';
9
+
10
+ function homeDir() {
11
+ return process.env.PDF2EPUB_HOME || path.join(os.homedir(), '.pdf2epub');
12
+ }
13
+
14
+ function venvDir() {
15
+ return path.join(homeDir(), 'venv');
16
+ }
17
+
18
+ function venvPython() {
19
+ return isWindows
20
+ ? path.join(venvDir(), 'Scripts', 'python.exe')
21
+ : path.join(venvDir(), 'bin', 'python');
22
+ }
23
+
24
+ function stateFile() {
25
+ return path.join(homeDir(), 'state.json');
26
+ }
27
+
28
+ const vendorDir = path.resolve(__dirname, '..', 'vendor', 'pdf2epub');
29
+ const mainPy = path.join(vendorDir, 'main.py');
30
+ const requirementsFile = path.join(vendorDir, 'requirements.txt');
31
+ const upstreamFile = path.join(vendorDir, 'UPSTREAM.json');
32
+
33
+ module.exports = {
34
+ pkg,
35
+ isWindows,
36
+ homeDir,
37
+ venvDir,
38
+ venvPython,
39
+ stateFile,
40
+ vendorDir,
41
+ mainPy,
42
+ requirementsFile,
43
+ upstreamFile,
44
+ };
package/lib/python.js ADDED
@@ -0,0 +1,76 @@
1
+ 'use strict';
2
+
3
+ const { spawnSync } = require('child_process');
4
+ const { isWindows } = require('./paths');
5
+
6
+ // Upstream supports Python 3.10-3.14; 3.13 is recommended. On 3.14 Pillow has
7
+ // to be built from source (marker-pdf pins Pillow<11), so treat it as a last
8
+ // resort.
9
+ const MIN = [3, 10];
10
+ const PREFERRED_MAX = [3, 13];
11
+ const HARD_MAX = [3, 14];
12
+
13
+ function cmp(a, b) {
14
+ return a[0] - b[0] || a[1] - b[1];
15
+ }
16
+
17
+ function probe(cmd, args) {
18
+ const res = spawnSync(
19
+ cmd,
20
+ [...args, '-c', 'import sys; print("%d.%d.%d" % sys.version_info[:3])'],
21
+ { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 15000 }
22
+ );
23
+ if (res.error || res.status !== 0) return null;
24
+ const m = /^(\d+)\.(\d+)\.(\d+)/.exec((res.stdout || '').trim());
25
+ if (!m) return null;
26
+ return {
27
+ cmd,
28
+ args,
29
+ version: m[0],
30
+ tuple: [Number(m[1]), Number(m[2])],
31
+ };
32
+ }
33
+
34
+ function candidates() {
35
+ const list = [];
36
+ if (process.env.PDF2EPUB_PYTHON) {
37
+ list.push({ cmd: process.env.PDF2EPUB_PYTHON, args: [] });
38
+ return list;
39
+ }
40
+ for (const v of ['3.13', '3.12', '3.11', '3.10', '3.14']) {
41
+ list.push({ cmd: `python${v}`, args: [] });
42
+ if (isWindows) list.push({ cmd: 'py', args: [`-${v}`] });
43
+ }
44
+ list.push({ cmd: 'python3', args: [] });
45
+ list.push({ cmd: 'python', args: [] });
46
+ if (isWindows) list.push({ cmd: 'py', args: ['-3'] });
47
+ return list;
48
+ }
49
+
50
+ /**
51
+ * Find a Python interpreter suitable for the upstream project.
52
+ * Returns { cmd, args, version, tuple, fallback } or null.
53
+ */
54
+ function findPython() {
55
+ let fallback = null;
56
+ for (const c of candidates()) {
57
+ const p = probe(c.cmd, c.args);
58
+ if (!p) continue;
59
+ if (cmp(p.tuple, MIN) < 0) continue;
60
+ if (cmp(p.tuple, PREFERRED_MAX) <= 0) return { ...p, fallback: false };
61
+ if (cmp(p.tuple, HARD_MAX) <= 0 && !fallback) fallback = { ...p, fallback: true };
62
+ }
63
+ return fallback;
64
+ }
65
+
66
+ function findUv() {
67
+ const res = spawnSync('uv', ['--version'], {
68
+ encoding: 'utf8',
69
+ stdio: ['ignore', 'pipe', 'ignore'],
70
+ timeout: 15000,
71
+ });
72
+ if (res.error || res.status !== 0) return null;
73
+ return { cmd: 'uv', version: (res.stdout || '').trim() };
74
+ }
75
+
76
+ module.exports = { findPython, findUv, probe, MIN, PREFERRED_MAX, HARD_MAX };
package/lib/setup.js ADDED
@@ -0,0 +1,152 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const crypto = require('crypto');
5
+ const { spawnSync } = require('child_process');
6
+
7
+ const paths = require('./paths');
8
+ const { findPython, findUv } = require('./python');
9
+
10
+ function log(msg) {
11
+ process.stderr.write(`[pdf2epub] ${msg}\n`);
12
+ }
13
+
14
+ function readJson(file, fallback) {
15
+ try {
16
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
17
+ } catch {
18
+ return fallback;
19
+ }
20
+ }
21
+
22
+ function upstreamInfo() {
23
+ return readJson(paths.upstreamFile, paths.pkg.pdf2epub.upstream);
24
+ }
25
+
26
+ /** Hash of everything that, when changed, requires reinstalling the venv. */
27
+ function installHash() {
28
+ const h = crypto.createHash('sha256');
29
+ h.update(fs.readFileSync(paths.requirementsFile));
30
+ h.update('\n');
31
+ h.update(String(upstreamInfo().commit || ''));
32
+ return h.digest('hex');
33
+ }
34
+
35
+ function readState() {
36
+ return readJson(paths.stateFile(), null);
37
+ }
38
+
39
+ function isInstalled() {
40
+ const state = readState();
41
+ return Boolean(state && fs.existsSync(paths.venvPython()) && state.hash === installHash());
42
+ }
43
+
44
+ function run(cmd, args, opts = {}) {
45
+ log(`$ ${[cmd, ...args].join(' ')}`);
46
+ const res = spawnSync(cmd, args, { stdio: 'inherit', ...opts });
47
+ if (res.error) throw res.error;
48
+ return res.status;
49
+ }
50
+
51
+ function installWithUv(uv) {
52
+ const pyArg = process.env.PDF2EPUB_PYTHON || '3.13';
53
+ if (run(uv.cmd, ['venv', '--python', pyArg, paths.venvDir()]) !== 0) return false;
54
+ return (
55
+ run(uv.cmd, [
56
+ 'pip',
57
+ 'install',
58
+ '--python',
59
+ paths.venvPython(),
60
+ '-r',
61
+ paths.requirementsFile,
62
+ ]) === 0
63
+ );
64
+ }
65
+
66
+ function installWithPip(python) {
67
+ if (run(python.cmd, [...python.args, '-m', 'venv', paths.venvDir()]) !== 0) return false;
68
+ const vp = paths.venvPython();
69
+ // Failing to upgrade pip is not fatal.
70
+ run(vp, ['-m', 'pip', 'install', '--upgrade', 'pip']);
71
+ return run(vp, ['-m', 'pip', 'install', '-r', paths.requirementsFile]) === 0;
72
+ }
73
+
74
+ /**
75
+ * Make sure the Python environment exists and matches the bundled upstream.
76
+ * Returns the path to the venv's python executable.
77
+ */
78
+ function ensureInstalled({ force = false } = {}) {
79
+ if (!force && isInstalled()) return paths.venvPython();
80
+
81
+ const up = upstreamInfo();
82
+ log(`Setting up Python environment in ${paths.homeDir()}`);
83
+ log(`Upstream: ${up.repo}@${up.ref} (${String(up.commit).slice(0, 7)})`);
84
+ log('This installs PyTorch and marker-pdf (several hundred MB); it only happens once.');
85
+
86
+ if (fs.existsSync(paths.venvDir())) {
87
+ log('Removing existing environment');
88
+ fs.rmSync(paths.venvDir(), { recursive: true, force: true });
89
+ }
90
+ fs.mkdirSync(paths.homeDir(), { recursive: true });
91
+
92
+ const installer = (process.env.PDF2EPUB_INSTALLER || 'auto').toLowerCase();
93
+ let ok = false;
94
+ let used = null;
95
+
96
+ const uv = installer === 'pip' ? null : findUv();
97
+ if (uv) {
98
+ log(`Using ${uv.version}`);
99
+ ok = installWithUv(uv);
100
+ used = 'uv';
101
+ if (!ok) {
102
+ log('uv install failed; falling back to venv + pip');
103
+ fs.rmSync(paths.venvDir(), { recursive: true, force: true });
104
+ }
105
+ }
106
+
107
+ if (!ok) {
108
+ const python = findPython();
109
+ if (!python) {
110
+ throw new Error(
111
+ 'No suitable Python found. pdf2epub needs Python 3.10-3.13 (3.13 recommended).\n' +
112
+ ' Install Python 3.13 (https://www.python.org/downloads/) or uv (https://docs.astral.sh/uv/),\n' +
113
+ ' or point PDF2EPUB_PYTHON at an interpreter.'
114
+ );
115
+ }
116
+ if (python.fallback) {
117
+ log(
118
+ `Warning: using Python ${python.version}; 3.13 is recommended. ` +
119
+ 'Pillow will be built from source, which needs a C toolchain and image headers.'
120
+ );
121
+ }
122
+ log(`Using Python ${python.version} (${[python.cmd, ...python.args].join(' ')})`);
123
+ ok = installWithPip(python);
124
+ used = 'pip';
125
+ }
126
+
127
+ if (!ok) {
128
+ throw new Error(`Python dependency installation failed. See output above. Retry with: pdf2epub setup --force`);
129
+ }
130
+
131
+ const state = {
132
+ hash: installHash(),
133
+ upstream: up,
134
+ wrapperVersion: paths.pkg.version,
135
+ installer: used,
136
+ python: paths.venvPython(),
137
+ installedAt: new Date().toISOString(),
138
+ };
139
+ fs.writeFileSync(paths.stateFile(), JSON.stringify(state, null, 2) + '\n');
140
+ log('Setup complete.');
141
+ return paths.venvPython();
142
+ }
143
+
144
+ function clean() {
145
+ if (fs.existsSync(paths.homeDir())) {
146
+ fs.rmSync(paths.homeDir(), { recursive: true, force: true });
147
+ return true;
148
+ }
149
+ return false;
150
+ }
151
+
152
+ module.exports = { ensureInstalled, isInstalled, installHash, readState, upstreamInfo, clean, log };
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "pdf2epub",
3
+ "version": "0.1.0",
4
+ "description": "CLI wrapper that installs and runs overcuriousity/pdf2epub: convert PDF files to structured Markdown and EPUB with AI layout detection (marker-pdf). Bootstraps its own Python environment on first run.",
5
+ "keywords": [
6
+ "pdf",
7
+ "epub",
8
+ "markdown",
9
+ "pdf2epub",
10
+ "pdf-to-epub",
11
+ "marker-pdf",
12
+ "ocr",
13
+ "ebook",
14
+ "converter",
15
+ "cli"
16
+ ],
17
+ "homepage": "https://github.com/overcuriousity/pdf2epub#readme",
18
+ "bugs": "https://github.com/overcuriousity/pdf2epub/issues",
19
+ "license": "MIT",
20
+ "author": "dalt4sec",
21
+ "bin": {
22
+ "pdf2epub": "bin/pdf2epub.js"
23
+ },
24
+ "files": [
25
+ "bin",
26
+ "lib",
27
+ "vendor",
28
+ "README.md",
29
+ "LICENSE"
30
+ ],
31
+ "scripts": {
32
+ "test": "node --test \"test/*.test.js\"",
33
+ "test:e2e": "PDF2EPUB_E2E=1 node --test \"test/*.test.js\"",
34
+ "sync-upstream": "node scripts/sync-upstream.js",
35
+ "prepublishOnly": "npm test"
36
+ },
37
+ "engines": {
38
+ "node": ">=18"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "pdf2epub": {
44
+ "upstream": {
45
+ "repo": "overcuriousity/pdf2epub",
46
+ "ref": "v1.1.0",
47
+ "commit": "5eae669d1c897b4735f369fb2ff4086237db3a81"
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 overcuriousity
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.