twi-scraper 0.1.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.
@@ -0,0 +1,8 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ *.epub
5
+ *.part
6
+ /out/
7
+ .pytest_cache/
8
+ dist/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Johann
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,155 @@
1
+ Metadata-Version: 2.5
2
+ Name: twi-scraper
3
+ Version: 0.1.0
4
+ Summary: Download The Wandering Inn web serial as clean, screen-reader-friendly Markdown or EPUB.
5
+ Project-URL: Homepage, https://github.com/johanntan/twi-scraper
6
+ Project-URL: Source, https://github.com/johanntan/twi-scraper
7
+ Project-URL: Issues, https://github.com/johanntan/twi-scraper/issues
8
+ Author-email: Johann <johann@jtan.me>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: accessibility,epub,markdown,screen reader,wandering inn,web serial
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: End Users/Desktop
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Text Processing :: Markup :: Markdown
21
+ Classifier: Topic :: Utilities
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: beautifulsoup4>=4.12
24
+ Requires-Dist: lxml>=5.0
25
+ Requires-Dist: requests>=2.31
26
+ Description-Content-Type: text/markdown
27
+
28
+ # twi
29
+
30
+ Download [The Wandering Inn](https://wanderinginn.com) as clean, screen-reader-friendly
31
+ Markdown or EPUB files.
32
+
33
+ - Single chapters → Markdown, with the author's notes kept under an "Author's Note" heading.
34
+ - Whole volumes → EPUB, with the author's notes and announcements removed.
35
+ - Gallery images, fan-art credits and Previous/Next links are always removed.
36
+ - The author's dash separators (`——`, `—-`) become a real separator: `***` in Markdown,
37
+ `<hr/>` in EPUB.
38
+ - Links between chapters stay inside the book. For example, Solstice Pt. 3's "Changed." link
39
+ jumps to Pt. 4 in the EPUB instead of opening the website. Links to chapters that aren't in
40
+ the file become plain text.
41
+ - Tales of Innworld and other image-only or announcement posts are left out of volumes.
42
+ - Blacked-out text (`███` in 7.25, 7.50) reads as "[redacted]" instead of "full block full
43
+ block". Where the page hides the original words, they go in a footnote: a pop-up link in the
44
+ EPUB, a `[^1]` footnote at the end of the chapter in Markdown.
45
+ - "Fancy font" letters become plain letters a screen reader can say. This covers small capitals
46
+ for gods' voices (`Dᴇᴀᴛʜ ʜᴀs ᴄᴏᴍᴇ` → "Death has come"), superscript fine print, maths-font
47
+ digits, and upside-down text (10.56 F). In the EPUB, small caps and superscripts still look
48
+ the same.
49
+
50
+ ## Install
51
+
52
+ The package is called `twi-scraper`; the command it installs is `twi`.
53
+
54
+ ```sh
55
+ uv tool install twi-scraper # with uv
56
+ pipx install twi-scraper # or with pipx
57
+ uv tool upgrade twi-scraper # later, to update
58
+ ```
59
+
60
+ From a checkout of this repository, `uv run twi --help` works without installing.
61
+
62
+ ## Usage
63
+
64
+ ```sh
65
+ twi list # volumes, the Volume 1 archive, and Griefman
66
+ twi list 7 # chapters in Volume 7
67
+
68
+ twi chapter "10.78 RV" # one chapter as Markdown -> "10.78 RV.md"
69
+ twi chapter 8.31 "solstice pt 3" latest
70
+ twi https://wanderinginn.com/2023/11/28/9-68/ # any chapter by URL
71
+
72
+ twi volume 7 # "The Wandering Inn - Volume 7.epub"
73
+ twi volume 7 --format md # a folder of numbered Markdown files
74
+ twi volume 7 --format md --single-file
75
+ twi volume archive # the original 2016 Volume 1
76
+
77
+ twi book griefman # Griefman as one EPUB, split by its own chapters
78
+ twi book griefman --format md # ...or as one Markdown file
79
+ ```
80
+
81
+ Chapter titles are matched loosely: `solstice pt 3` finds "Solstice (Pt. 3)". If a title matches
82
+ several chapters, use the full title or the URL.
83
+
84
+ ### Options
85
+
86
+ | Option | Effect |
87
+ |---|---|
88
+ | `--format md` / `--format epub` | Change the output format. |
89
+ | `-o DIR`, `--output DIR` | Where to write files (default: the current folder). |
90
+ | `--single-file` | With `--format md`, write a volume as one file instead of a folder. |
91
+ | `--keep-notes` / `--no-notes` | Keep or remove author's notes and announcements. By default, `chapter` keeps them; `volume` and `book` remove them. |
92
+ | `--password PW` | A password to try on locked chapters. Can be given more than once. |
93
+ | `--skip-locked` | Skip locked chapters without asking. |
94
+ | `--trigger-free` | Where a chapter has a trigger-free version (9.68), download that instead. |
95
+ | `--include-extras` | Keep Tales of Innworld and other non-story posts in volumes. |
96
+ | `--refresh` | Download pages again instead of using the cache. |
97
+
98
+ ## Locked chapters
99
+
100
+ Some chapters are Patreon-locked, including the newest chapter and many rewritten Volume 1–2
101
+ chapters, which have a password field on the site. When `twi` meets one, it:
102
+
103
+ 1. tries the passwords you gave with `--password` or the `TWI_PASSWORD` environment variable,
104
+ then any password you've already typed in this run;
105
+ 2. otherwise asks: `"10.79 RVP" is Patreon-locked (Jar of Acid tier). Password (blank to skip):`.
106
+ A wrong password asks again. A blank answer skips that chapter and every other chapter
107
+ behind the same lock for the rest of the run, so you aren't asked 50 times.
108
+
109
+ Skipped chapters are listed at the end. With `--skip-locked`, or when not run in a terminal,
110
+ locked chapters are skipped without asking.
111
+
112
+ **Wrong passwords get you blocked.** The site's firewall blocks a connection from locked pages
113
+ after wrong password attempts, with a "WP Remote Firewall: Blocked because of Malicious
114
+ Activities" page. Other pages keep working. To avoid this, `twi` remembers which password works
115
+ for each Patreon tier and never sends a password again for a tier where it already failed. If
116
+ you do get blocked, `twi` stops and says so. Wait a while before trying again. Everything
117
+ already downloaded is cached, so the rerun is quick.
118
+
119
+ Some extra pages, such as 9.68's trigger-free version, use a password printed in the chapter
120
+ itself. `twi` reads that password and uses it automatically.
121
+
122
+ ## Progress messages
123
+
124
+ Progress goes to standard error as plain lines (`[3/95] 7.02`), with no progress bars or
125
+ colour. Each line also says what was removed from a chapter, so nothing disappears silently.
126
+
127
+ ## Cache
128
+
129
+ Unlocked chapter pages are cached in `~/.cache/twi`. The table of contents is refreshed every
130
+ hour. Locked pages are never cached. Delete the folder, or pass `--refresh`, to download
131
+ everything again.
132
+
133
+ ## Publishing a new version
134
+
135
+ Releases go to PyPI automatically through GitHub Actions (`.github/workflows/publish.yml`),
136
+ using PyPI trusted publishing, so no API token is stored anywhere.
137
+
138
+ One-time setup on pypi.org (account → Publishing → "Add a new pending publisher"):
139
+
140
+ | Field | Value |
141
+ |---|---|
142
+ | PyPI project name | `twi-scraper` |
143
+ | Owner | `johanntan` |
144
+ | Repository name | `twi-scraper` |
145
+ | Workflow name | `publish.yml` |
146
+ | Environment name | `pypi` |
147
+
148
+ Then, for each release:
149
+
150
+ 1. Bump `version` in `pyproject.toml` and commit.
151
+ 2. Create a GitHub release whose tag is that version, e.g. `v0.1.0`:
152
+ `gh release create v0.1.0 --generate-notes`.
153
+
154
+ The workflow checks that the tag matches the version, runs the tests, builds, and uploads.
155
+ PyPI never accepts the same version twice, so a mistake means bumping to the next version.
@@ -0,0 +1,128 @@
1
+ # twi
2
+
3
+ Download [The Wandering Inn](https://wanderinginn.com) as clean, screen-reader-friendly
4
+ Markdown or EPUB files.
5
+
6
+ - Single chapters → Markdown, with the author's notes kept under an "Author's Note" heading.
7
+ - Whole volumes → EPUB, with the author's notes and announcements removed.
8
+ - Gallery images, fan-art credits and Previous/Next links are always removed.
9
+ - The author's dash separators (`——`, `—-`) become a real separator: `***` in Markdown,
10
+ `<hr/>` in EPUB.
11
+ - Links between chapters stay inside the book. For example, Solstice Pt. 3's "Changed." link
12
+ jumps to Pt. 4 in the EPUB instead of opening the website. Links to chapters that aren't in
13
+ the file become plain text.
14
+ - Tales of Innworld and other image-only or announcement posts are left out of volumes.
15
+ - Blacked-out text (`███` in 7.25, 7.50) reads as "[redacted]" instead of "full block full
16
+ block". Where the page hides the original words, they go in a footnote: a pop-up link in the
17
+ EPUB, a `[^1]` footnote at the end of the chapter in Markdown.
18
+ - "Fancy font" letters become plain letters a screen reader can say. This covers small capitals
19
+ for gods' voices (`Dᴇᴀᴛʜ ʜᴀs ᴄᴏᴍᴇ` → "Death has come"), superscript fine print, maths-font
20
+ digits, and upside-down text (10.56 F). In the EPUB, small caps and superscripts still look
21
+ the same.
22
+
23
+ ## Install
24
+
25
+ The package is called `twi-scraper`; the command it installs is `twi`.
26
+
27
+ ```sh
28
+ uv tool install twi-scraper # with uv
29
+ pipx install twi-scraper # or with pipx
30
+ uv tool upgrade twi-scraper # later, to update
31
+ ```
32
+
33
+ From a checkout of this repository, `uv run twi --help` works without installing.
34
+
35
+ ## Usage
36
+
37
+ ```sh
38
+ twi list # volumes, the Volume 1 archive, and Griefman
39
+ twi list 7 # chapters in Volume 7
40
+
41
+ twi chapter "10.78 RV" # one chapter as Markdown -> "10.78 RV.md"
42
+ twi chapter 8.31 "solstice pt 3" latest
43
+ twi https://wanderinginn.com/2023/11/28/9-68/ # any chapter by URL
44
+
45
+ twi volume 7 # "The Wandering Inn - Volume 7.epub"
46
+ twi volume 7 --format md # a folder of numbered Markdown files
47
+ twi volume 7 --format md --single-file
48
+ twi volume archive # the original 2016 Volume 1
49
+
50
+ twi book griefman # Griefman as one EPUB, split by its own chapters
51
+ twi book griefman --format md # ...or as one Markdown file
52
+ ```
53
+
54
+ Chapter titles are matched loosely: `solstice pt 3` finds "Solstice (Pt. 3)". If a title matches
55
+ several chapters, use the full title or the URL.
56
+
57
+ ### Options
58
+
59
+ | Option | Effect |
60
+ |---|---|
61
+ | `--format md` / `--format epub` | Change the output format. |
62
+ | `-o DIR`, `--output DIR` | Where to write files (default: the current folder). |
63
+ | `--single-file` | With `--format md`, write a volume as one file instead of a folder. |
64
+ | `--keep-notes` / `--no-notes` | Keep or remove author's notes and announcements. By default, `chapter` keeps them; `volume` and `book` remove them. |
65
+ | `--password PW` | A password to try on locked chapters. Can be given more than once. |
66
+ | `--skip-locked` | Skip locked chapters without asking. |
67
+ | `--trigger-free` | Where a chapter has a trigger-free version (9.68), download that instead. |
68
+ | `--include-extras` | Keep Tales of Innworld and other non-story posts in volumes. |
69
+ | `--refresh` | Download pages again instead of using the cache. |
70
+
71
+ ## Locked chapters
72
+
73
+ Some chapters are Patreon-locked, including the newest chapter and many rewritten Volume 1–2
74
+ chapters, which have a password field on the site. When `twi` meets one, it:
75
+
76
+ 1. tries the passwords you gave with `--password` or the `TWI_PASSWORD` environment variable,
77
+ then any password you've already typed in this run;
78
+ 2. otherwise asks: `"10.79 RVP" is Patreon-locked (Jar of Acid tier). Password (blank to skip):`.
79
+ A wrong password asks again. A blank answer skips that chapter and every other chapter
80
+ behind the same lock for the rest of the run, so you aren't asked 50 times.
81
+
82
+ Skipped chapters are listed at the end. With `--skip-locked`, or when not run in a terminal,
83
+ locked chapters are skipped without asking.
84
+
85
+ **Wrong passwords get you blocked.** The site's firewall blocks a connection from locked pages
86
+ after wrong password attempts, with a "WP Remote Firewall: Blocked because of Malicious
87
+ Activities" page. Other pages keep working. To avoid this, `twi` remembers which password works
88
+ for each Patreon tier and never sends a password again for a tier where it already failed. If
89
+ you do get blocked, `twi` stops and says so. Wait a while before trying again. Everything
90
+ already downloaded is cached, so the rerun is quick.
91
+
92
+ Some extra pages, such as 9.68's trigger-free version, use a password printed in the chapter
93
+ itself. `twi` reads that password and uses it automatically.
94
+
95
+ ## Progress messages
96
+
97
+ Progress goes to standard error as plain lines (`[3/95] 7.02`), with no progress bars or
98
+ colour. Each line also says what was removed from a chapter, so nothing disappears silently.
99
+
100
+ ## Cache
101
+
102
+ Unlocked chapter pages are cached in `~/.cache/twi`. The table of contents is refreshed every
103
+ hour. Locked pages are never cached. Delete the folder, or pass `--refresh`, to download
104
+ everything again.
105
+
106
+ ## Publishing a new version
107
+
108
+ Releases go to PyPI automatically through GitHub Actions (`.github/workflows/publish.yml`),
109
+ using PyPI trusted publishing, so no API token is stored anywhere.
110
+
111
+ One-time setup on pypi.org (account → Publishing → "Add a new pending publisher"):
112
+
113
+ | Field | Value |
114
+ |---|---|
115
+ | PyPI project name | `twi-scraper` |
116
+ | Owner | `johanntan` |
117
+ | Repository name | `twi-scraper` |
118
+ | Workflow name | `publish.yml` |
119
+ | Environment name | `pypi` |
120
+
121
+ Then, for each release:
122
+
123
+ 1. Bump `version` in `pyproject.toml` and commit.
124
+ 2. Create a GitHub release whose tag is that version, e.g. `v0.1.0`:
125
+ `gh release create v0.1.0 --generate-notes`.
126
+
127
+ The workflow checks that the tag matches the version, runs the tests, builds, and uploads.
128
+ PyPI never accepts the same version twice, so a mistake means bumping to the next version.
@@ -0,0 +1,51 @@
1
+ [project]
2
+ name = "twi-scraper"
3
+ version = "0.1.0"
4
+ description = "Download The Wandering Inn web serial as clean, screen-reader-friendly Markdown or EPUB."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ authors = [{ name = "Johann", email = "johann@jtan.me" }]
9
+ requires-python = ">=3.11"
10
+ keywords = ["wandering inn", "web serial", "epub", "markdown", "accessibility", "screen reader"]
11
+ classifiers = [
12
+ "Development Status :: 4 - Beta",
13
+ "Environment :: Console",
14
+ "Intended Audience :: End Users/Desktop",
15
+ "Operating System :: OS Independent",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Topic :: Text Processing :: Markup :: Markdown",
21
+ "Topic :: Utilities",
22
+ ]
23
+ dependencies = [
24
+ "beautifulsoup4>=4.12",
25
+ "lxml>=5.0",
26
+ "requests>=2.31",
27
+ ]
28
+
29
+ [project.urls]
30
+ Homepage = "https://github.com/johanntan/twi-scraper"
31
+ Source = "https://github.com/johanntan/twi-scraper"
32
+ Issues = "https://github.com/johanntan/twi-scraper/issues"
33
+
34
+ [project.scripts]
35
+ twi = "twi.cli:main"
36
+
37
+ [dependency-groups]
38
+ dev = ["pytest>=8"]
39
+
40
+ [build-system]
41
+ requires = ["hatchling"]
42
+ build-backend = "hatchling.build"
43
+
44
+ [tool.hatch.build.targets.wheel]
45
+ packages = ["src/twi"]
46
+
47
+ [tool.hatch.build.targets.sdist]
48
+ exclude = ["CLAUDE.md", ".github", "uv.lock", "out"]
49
+
50
+ [tool.pytest.ini_options]
51
+ testpaths = ["tests"]
@@ -0,0 +1,3 @@
1
+ """Download The Wandering Inn as clean Markdown or EPUB."""
2
+
3
+ __version__ = "0.1.0"