six2one 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.
six2one-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,317 @@
1
+ Metadata-Version: 2.4
2
+ Name: six2one
3
+ Version: 0.1.0
4
+ Summary: Manifest-backed e621/e926 dataset fetching from tiny numeric commands.
5
+ Keywords: e621,e926,dataset,downloader,cli,manifest
6
+ Author: Nolla Fox
7
+ Author-email: nollafox.dev@gmail.com
8
+ Requires-Python: >=3.10
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: End Users/Desktop
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Internet :: WWW/HTTP
19
+ Classifier: Topic :: Multimedia :: Graphics
20
+ Requires-Dist: aiofiles (>=25.1.0,<26.0.0)
21
+ Requires-Dist: aiohttp (>=3.13.5,<4.0.0)
22
+ Requires-Dist: tqdm (>=4.67.3,<5.0.0)
23
+ Project-URL: Homepage, https://github.com/nollafox/six2one
24
+ Project-URL: Issues, https://github.com/nollafox/six2one/issues
25
+ Project-URL: Repository, https://github.com/nollafox/six2one
26
+ Description-Content-Type: text/markdown
27
+
28
+ # six2one
29
+
30
+ <p align="center">
31
+ <img src="https://github.com/nollafox/six2one/raw/main/docs/banner.png" alt="six2one banner" style="border-radius: 16px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12); max-width: 100%; height: auto;">
32
+ </p>
33
+
34
+
35
+ <p align="center">
36
+ <img src="https://img.shields.io/badge/Python-3.10%2B-1E90FF" alt="Python 3.10+">
37
+ <a href="https://github.com/nollafox/six2one/actions/workflows/test.yml">
38
+ <img src="https://img.shields.io/github/actions/workflow/status/nollafox/six2one/test.yml?branch=main&label=tests&color=2E8B57" alt="Test status">
39
+ </a>
40
+ <img src="https://img.shields.io/badge/CLI-621%20%7C%20926-4169E1" alt="621 and 926 CLI">
41
+ <img src="https://img.shields.io/badge/sites-e621%20%7C%20e926-8A2BE2" alt="e621 and e926">
42
+ <img src="https://img.shields.io/badge/author-Nolla%20Fox-2E8B57" alt="Author: Nolla Fox">
43
+ </p>
44
+
45
+ <p align="center">
46
+ <a href="#quick-start">Quick Start</a> •
47
+ <a href="#how-queries-compile">Queries</a> •
48
+ <a href="#the-manifest">Manifest</a> •
49
+ <a href="#commands">Commands</a>
50
+ </p>
51
+
52
+ **six2one** is a small command-line fetcher for e621 and e926. Pass it the same tags you'd type into the site search; it compiles them into a query, downloads matching posts in API-friendly pages, and writes each image alongside a caption file generated from its tags and the raw post JSON. Behind that, a `manifest.json` records everything as it goes so you can resume, dedupe, repair, inspect, and prune without managing state yourself.
53
+
54
+ The result is a CLI that stays pleasant for one-off searches and trustworthy for long-running collections: reference folders for a particular artist, archives of a saved search, the occasional small dataset.
55
+
56
+ ## Quick Start
57
+
58
+ Install six2one from PyPI:
59
+
60
+ ```bash
61
+ $ python -m pip install six2one
62
+ ```
63
+
64
+ That installs the `621` and `926` commands onto your `PATH`.
65
+
66
+ The following command searches e621 for `fox solo rating:s` and begins to download the posts returned:
67
+
68
+ ```bash
69
+ $ 621 fox solo --safe
70
+ ```
71
+
72
+ This will download into `output/fox-solo-safe/` with the following layout:
73
+
74
+ ```
75
+ output/fox-solo-safe/
76
+ images/
77
+ captions/
78
+ posts/
79
+ manifest.json
80
+ ```
81
+
82
+ Alternatively, for the safe-only sister site, pass `--site e926`, or use the `926` binary, which applies that site default automatically:
83
+
84
+ ```bash
85
+ $ 926 fox solo --safe
86
+ ```
87
+
88
+ You may also use `--dry-run` to see the compiled query before downloading anything:
89
+
90
+ ```
91
+ $ 621 fox solo --safe --dry-run
92
+ Compiled query: fox solo rating:s
93
+ ```
94
+
95
+ For an isolated CLI install, use `pipx install six2one`. For an editable install from a local clone, use `python -m pip install --user -e .`. If `621` installs but your shell cannot find it, run `python -m site --user-base`; the binary lives under that path's `bin/` directory, or `Scripts\` on Windows.
96
+
97
+ ## How Queries Compile
98
+
99
+ Tags pass through to e621 unchanged. Artist tags, OR terms, exclusions, and rating get appended in a fixed order, and `--dry-run` shows exactly what comes out:
100
+
101
+ ```bash
102
+ $ 621 fox solo \
103
+ --author some_artist \
104
+ --any cat,dog \
105
+ --exclude chicken,watermark,comic \
106
+ --safe \
107
+ --dry-run
108
+
109
+ # Compiled query: fox solo some_artist ~cat ~dog -chicken -watermark -comic rating:s
110
+ ```
111
+
112
+ That compiled string is what you'd type into the e621 search bar, which is also why six2one's parser stays thin. The flags are conveniences for patterns six2one already knows; everything else, including wildcards (`cat*`), single-dash negation (`-comic`), and grouped OR syntax (`( ~cat ~dog )`), passes through to e621's native [search syntax](https://e621.net/help/cheatsheet). Unknown long options stay CLI errors rather than being silently forwarded, but anything that works in the search bar works here:
113
+
114
+ ```bash
115
+ $ 621 "( ~cat ~tiger ~leopard ) ( ~dog ~wolf )" --safe
116
+ $ 621 fox african_wild_dog -chicken
117
+ ```
118
+
119
+ To fetch more than the 320-post default, raise `--limit` or use `--all`. The downloader continues in API-sized pages until it reaches the requested count or the query is exhausted:
120
+
121
+ ```bash
122
+ $ 621 fox solo --safe --limit 1000
123
+ $ 621 dragon solo --explicit --all --resume
124
+ ```
125
+
126
+ ## The Manifest
127
+
128
+ Every output folder gets a `manifest.json` that records what's been downloaded, keyed by numeric post ID, and which queries produced it, keyed by compiled query, site, and image size. That record keeps the folder understandable across sessions: you can stop a fetch, come back later, and continue without reasoning from whatever happens to be on disk.
129
+
130
+ The same record also controls how six2one handles folders it has already touched. By default, it refuses to fetch into a folder with an existing manifest until you choose the intended mode:
131
+
132
+ ```bash
133
+ $ 621 fox solo --safe --resume # continue the same query
134
+ $ 621 fox --any cat,dog --merge # add a different query to the same folder
135
+ $ 621 fox solo --safe --force-new # start over without deleting files
136
+ ```
137
+
138
+ The continuation rules follow from those three modes:
139
+
140
+ | Situation | Behavior |
141
+ |---|---|
142
+ | Same query, same output, same size and site, `--resume` | Continue from manifest state. |
143
+ | Same query with a higher limit | Continue until the new limit. |
144
+ | Same query with a lower limit | Keep existing files; nothing is deleted. |
145
+ | Different query in the same output | Fail unless `--merge` or `--force-new` is used. |
146
+ | Manifest-listed files are missing | Redownload or regenerate them. |
147
+ | Files exist that aren't in the manifest | Ignore them unless `--adopt-existing` is used. |
148
+ | Target path collision | Fail unless adoption is explicitly valid. |
149
+
150
+ Six2one checks the manifest to decide what work still needs doing. If a post is already recorded and its files exist, it skips it; if something should be there but is missing, the next fetch can redownload or regenerate it. You do not edit the manifest directly.
151
+
152
+ ## What's in the Output Folder
153
+
154
+ The layout is stable:
155
+
156
+ ```
157
+ images/ downloaded media
158
+ captions/ text caption per post, generated from its tags
159
+ posts/ raw post JSON record per post
160
+ manifest.json
161
+ ```
162
+
163
+ Captions and post JSON are siblings of each image. Those companion files make the folder useful after the download is over: grep for tags, re-derive metadata, rebuild a downstream index, or use `show` to merge the manifest entry, caption text, raw post JSON, and local file paths into one view of a post.
164
+
165
+ The `--size` flag controls which image variant to fetch, mapping directly to e621 and e926 post fields:
166
+
167
+ | Size | e621/e926 post field |
168
+ |---|---|
169
+ | `preview` | `post["preview"]` |
170
+ | `sample` | `post["sample"]` (default) |
171
+ | `original` | `post["file"]` |
172
+
173
+ If the chosen URL is missing for a post, six2one records a warning that `--strict` promotes to an error.
174
+
175
+ ## Commands
176
+
177
+ ```
178
+ usage: 621 [fetch] [TAGS ...] [options]
179
+ 621 show POST_ID... [options]
180
+ 621 prune [output_dir]
181
+ ```
182
+
183
+ `fetch` is the default subcommand, so `621 fox solo --safe` and `621 fetch fox solo --safe` are equivalent.
184
+
185
+ ### Login
186
+
187
+ ```bash
188
+ $ 621 login nollafox YOUR_E621_API_KEY
189
+ $ 621 logout
190
+ ```
191
+
192
+ `login` writes `.six2one-login.json` next to `pyproject.toml`, and `logout` removes it. The file is gitignored by default. When present, those credentials are used for HTTP Basic auth and a username-specific `User-Agent` on API requests.
193
+
194
+ That login flow is one part of being a well-behaved e621 client. The other part is pacing: six2one uses an instance-owned rate limiter capped at 2 requests per second, and it pages through search results in chunks of at most 320 posts per API call, which is the e621 post-search maximum. Even `--all` fetches use that same pacing and pagination.
195
+
196
+ For the safe-only sister site, pass `--site e926` to any fetch or show command, or use the `926` binary, which applies that site default automatically:
197
+
198
+ ```bash
199
+ $ 621 fox solo --site e926 --all
200
+ $ 926 fox solo --all
201
+ $ 926 show 6394158 --fetch # fetch from e926 if available there
202
+ ```
203
+
204
+ ### Fetch
205
+
206
+ ```bash
207
+ $ 621 [fetch] TAGS... [options]
208
+ ```
209
+
210
+ | Option | Meaning |
211
+ |---|---|
212
+ | `TAGS` | e621 tag query terms. Supports `-tag`, `~tag`, wildcards, and grouped OR syntax. |
213
+ | `-o, --out DIR` | Output directory. Default: `./output/<query-slug>`. |
214
+ | `-n, --limit N` | Number of posts to fetch. Default: `320`. |
215
+ | `--all` | Fetch until the query is exhausted. |
216
+ | `--safe` / `--questionable` / `--explicit` | Shortcuts for `--rating`. |
217
+ | `--rating RATING` | One of `safe`, `questionable`, `explicit`, `s`, `q`, or `e`. |
218
+ | `--author NAME`, `--artist NAME`, `--by NAME` | Add an artist tag. Repeatable. |
219
+ | `--any TAG` | Add OR terms as `~TAG`. Repeatable and comma-separated. |
220
+ | `-x, --exclude TAG` | Exclude tags as `-TAG`. Repeatable and comma-separated. |
221
+ | `--site SITE` | `e621` or `e926`. Default: `e621` for `621`, `e926` for `926`. |
222
+ | `--size MODE` | `preview`, `sample`, or `original`. Default: `sample`. |
223
+ | `--resume` | Resume from an existing manifest with the same query and output. |
224
+ | `--merge` | Merge a different query into an existing manifest. |
225
+ | `--force-new` | Replace manifest state for a fresh run while leaving media files alone. |
226
+ | `--adopt-existing` | Adopt colliding files only when matching post metadata already exists. |
227
+ | `--dry-run` | Print the compiled query and exit. |
228
+ | `--validate-tags` | Check concrete and wildcard tags against the tag API before fetching. |
229
+ | `--strict` | Treat warnings, including missing file URLs, as errors. |
230
+
231
+ Hidden compatibility aliases still work: `--continue` for `--resume`, `--or` for `--any`, and `--file` for `--size`.
232
+
233
+ ### Show metadata
234
+
235
+ ```bash
236
+ $ 621 show 6394158
237
+ ```
238
+
239
+ `show` searches recursively under `./output` for six2one `manifest.json` files and returns a merged JSON view for matching posts. The `metadata` command is an alias. Each result combines the manifest post entry, `posts/<id>.json` when present, `captions/<id>.txt` when present, and filesystem-derived paths, existence flags, and file sizes.
240
+
241
+ By default, `show` reads only what is already on disk. Use `--root` to search a narrower folder, `--all` to show every manifest entry under that root, and `--fetch` to fetch remote post JSON only when an ID is missing locally:
242
+
243
+ ```bash
244
+ $ 621 show --all --root output/fox-solo-safe
245
+
246
+ # fetch from e926 if available there
247
+ $ 926 show 6394158 --fetch
248
+ ```
249
+
250
+ The merged object is filterable with dotted paths. Repeated filters and comma-separated filters are both accepted:
251
+
252
+ ```bash
253
+ $ 621 show 6394158 \
254
+ --filter local.image.absolute_path \
255
+ --filter caption.text \
256
+ --filter post.file.url
257
+
258
+ $ 621 show 6394158 -f local.image.absolute_path,caption.text,post.file.url
259
+ ```
260
+
261
+ Filtered keys flatten to their unique leaf names, so `caption.text` becomes `text`. If leaf names collide, six2one keeps enough path context to avoid overwriting values. Without filters, the full merged object is printed.
262
+
263
+ ```bash
264
+ $ 621 show 6394158 -f local.image.absolute_path,caption.text,post.file.url --pretty
265
+
266
+ # {
267
+ # "results": [
268
+ # {
269
+ # "absolute_path": "/Users/nollafox/output/fox-solo-safe/images/000006394158.jpg",
270
+ # "text": "anthro, ... english_text, hi_res, signature",
271
+ # "url": "https://static1.e621.net/data/51/b5/51b5a2f0925e153c2890e37836024f77.jpg"
272
+ # }
273
+ # ]
274
+ # }
275
+ ```
276
+ That makes it easy to export a local dataset index for scripts, audits, or LoRA training prep.
277
+
278
+
279
+
280
+ ### Prune
281
+
282
+ ```bash
283
+ $ 621 prune [output_dir]
284
+ ```
285
+
286
+ Prune scans the output folder for incomplete sibling sets. If an image is deleted by hand but its caption or post JSON remains, `621 prune` removes the orphaned companions and updates the manifest so a later `fetch --resume` can repair the set cleanly. If the output directory does not exist yet, prune creates it and exits.
287
+
288
+ ## Development
289
+
290
+ Install with Poetry:
291
+
292
+ ```bash
293
+ $ poetry install
294
+ ```
295
+
296
+ Run the CLI, the tests, and the compile check from inside the Poetry environment:
297
+
298
+ ```bash
299
+ $ poetry run 621 --help
300
+ $ poetry run 926 fox solo --dry-run
301
+ $ poetry run 621 show --help
302
+ $ poetry run python -m unittest discover -s tests
303
+ $ poetry run python -m compileall -q six2one
304
+ ```
305
+
306
+ <br>
307
+
308
+ ***
309
+
310
+ <p align="center">
311
+ <strong>six2one</strong> - manifest-backed e621 and e926 fetching.
312
+ </p>
313
+
314
+ <p align="center">
315
+ Crafted by <strong>Nolla Fox</strong>
316
+ </p>
317
+
@@ -0,0 +1,289 @@
1
+ # six2one
2
+
3
+ <p align="center">
4
+ <img src="https://github.com/nollafox/six2one/raw/main/docs/banner.png" alt="six2one banner" style="border-radius: 16px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12); max-width: 100%; height: auto;">
5
+ </p>
6
+
7
+
8
+ <p align="center">
9
+ <img src="https://img.shields.io/badge/Python-3.10%2B-1E90FF" alt="Python 3.10+">
10
+ <a href="https://github.com/nollafox/six2one/actions/workflows/test.yml">
11
+ <img src="https://img.shields.io/github/actions/workflow/status/nollafox/six2one/test.yml?branch=main&label=tests&color=2E8B57" alt="Test status">
12
+ </a>
13
+ <img src="https://img.shields.io/badge/CLI-621%20%7C%20926-4169E1" alt="621 and 926 CLI">
14
+ <img src="https://img.shields.io/badge/sites-e621%20%7C%20e926-8A2BE2" alt="e621 and e926">
15
+ <img src="https://img.shields.io/badge/author-Nolla%20Fox-2E8B57" alt="Author: Nolla Fox">
16
+ </p>
17
+
18
+ <p align="center">
19
+ <a href="#quick-start">Quick Start</a> •
20
+ <a href="#how-queries-compile">Queries</a> •
21
+ <a href="#the-manifest">Manifest</a> •
22
+ <a href="#commands">Commands</a>
23
+ </p>
24
+
25
+ **six2one** is a small command-line fetcher for e621 and e926. Pass it the same tags you'd type into the site search; it compiles them into a query, downloads matching posts in API-friendly pages, and writes each image alongside a caption file generated from its tags and the raw post JSON. Behind that, a `manifest.json` records everything as it goes so you can resume, dedupe, repair, inspect, and prune without managing state yourself.
26
+
27
+ The result is a CLI that stays pleasant for one-off searches and trustworthy for long-running collections: reference folders for a particular artist, archives of a saved search, the occasional small dataset.
28
+
29
+ ## Quick Start
30
+
31
+ Install six2one from PyPI:
32
+
33
+ ```bash
34
+ $ python -m pip install six2one
35
+ ```
36
+
37
+ That installs the `621` and `926` commands onto your `PATH`.
38
+
39
+ The following command searches e621 for `fox solo rating:s` and begins to download the posts returned:
40
+
41
+ ```bash
42
+ $ 621 fox solo --safe
43
+ ```
44
+
45
+ This will download into `output/fox-solo-safe/` with the following layout:
46
+
47
+ ```
48
+ output/fox-solo-safe/
49
+ images/
50
+ captions/
51
+ posts/
52
+ manifest.json
53
+ ```
54
+
55
+ Alternatively, for the safe-only sister site, pass `--site e926`, or use the `926` binary, which applies that site default automatically:
56
+
57
+ ```bash
58
+ $ 926 fox solo --safe
59
+ ```
60
+
61
+ You may also use `--dry-run` to see the compiled query before downloading anything:
62
+
63
+ ```
64
+ $ 621 fox solo --safe --dry-run
65
+ Compiled query: fox solo rating:s
66
+ ```
67
+
68
+ For an isolated CLI install, use `pipx install six2one`. For an editable install from a local clone, use `python -m pip install --user -e .`. If `621` installs but your shell cannot find it, run `python -m site --user-base`; the binary lives under that path's `bin/` directory, or `Scripts\` on Windows.
69
+
70
+ ## How Queries Compile
71
+
72
+ Tags pass through to e621 unchanged. Artist tags, OR terms, exclusions, and rating get appended in a fixed order, and `--dry-run` shows exactly what comes out:
73
+
74
+ ```bash
75
+ $ 621 fox solo \
76
+ --author some_artist \
77
+ --any cat,dog \
78
+ --exclude chicken,watermark,comic \
79
+ --safe \
80
+ --dry-run
81
+
82
+ # Compiled query: fox solo some_artist ~cat ~dog -chicken -watermark -comic rating:s
83
+ ```
84
+
85
+ That compiled string is what you'd type into the e621 search bar, which is also why six2one's parser stays thin. The flags are conveniences for patterns six2one already knows; everything else, including wildcards (`cat*`), single-dash negation (`-comic`), and grouped OR syntax (`( ~cat ~dog )`), passes through to e621's native [search syntax](https://e621.net/help/cheatsheet). Unknown long options stay CLI errors rather than being silently forwarded, but anything that works in the search bar works here:
86
+
87
+ ```bash
88
+ $ 621 "( ~cat ~tiger ~leopard ) ( ~dog ~wolf )" --safe
89
+ $ 621 fox african_wild_dog -chicken
90
+ ```
91
+
92
+ To fetch more than the 320-post default, raise `--limit` or use `--all`. The downloader continues in API-sized pages until it reaches the requested count or the query is exhausted:
93
+
94
+ ```bash
95
+ $ 621 fox solo --safe --limit 1000
96
+ $ 621 dragon solo --explicit --all --resume
97
+ ```
98
+
99
+ ## The Manifest
100
+
101
+ Every output folder gets a `manifest.json` that records what's been downloaded, keyed by numeric post ID, and which queries produced it, keyed by compiled query, site, and image size. That record keeps the folder understandable across sessions: you can stop a fetch, come back later, and continue without reasoning from whatever happens to be on disk.
102
+
103
+ The same record also controls how six2one handles folders it has already touched. By default, it refuses to fetch into a folder with an existing manifest until you choose the intended mode:
104
+
105
+ ```bash
106
+ $ 621 fox solo --safe --resume # continue the same query
107
+ $ 621 fox --any cat,dog --merge # add a different query to the same folder
108
+ $ 621 fox solo --safe --force-new # start over without deleting files
109
+ ```
110
+
111
+ The continuation rules follow from those three modes:
112
+
113
+ | Situation | Behavior |
114
+ |---|---|
115
+ | Same query, same output, same size and site, `--resume` | Continue from manifest state. |
116
+ | Same query with a higher limit | Continue until the new limit. |
117
+ | Same query with a lower limit | Keep existing files; nothing is deleted. |
118
+ | Different query in the same output | Fail unless `--merge` or `--force-new` is used. |
119
+ | Manifest-listed files are missing | Redownload or regenerate them. |
120
+ | Files exist that aren't in the manifest | Ignore them unless `--adopt-existing` is used. |
121
+ | Target path collision | Fail unless adoption is explicitly valid. |
122
+
123
+ Six2one checks the manifest to decide what work still needs doing. If a post is already recorded and its files exist, it skips it; if something should be there but is missing, the next fetch can redownload or regenerate it. You do not edit the manifest directly.
124
+
125
+ ## What's in the Output Folder
126
+
127
+ The layout is stable:
128
+
129
+ ```
130
+ images/ downloaded media
131
+ captions/ text caption per post, generated from its tags
132
+ posts/ raw post JSON record per post
133
+ manifest.json
134
+ ```
135
+
136
+ Captions and post JSON are siblings of each image. Those companion files make the folder useful after the download is over: grep for tags, re-derive metadata, rebuild a downstream index, or use `show` to merge the manifest entry, caption text, raw post JSON, and local file paths into one view of a post.
137
+
138
+ The `--size` flag controls which image variant to fetch, mapping directly to e621 and e926 post fields:
139
+
140
+ | Size | e621/e926 post field |
141
+ |---|---|
142
+ | `preview` | `post["preview"]` |
143
+ | `sample` | `post["sample"]` (default) |
144
+ | `original` | `post["file"]` |
145
+
146
+ If the chosen URL is missing for a post, six2one records a warning that `--strict` promotes to an error.
147
+
148
+ ## Commands
149
+
150
+ ```
151
+ usage: 621 [fetch] [TAGS ...] [options]
152
+ 621 show POST_ID... [options]
153
+ 621 prune [output_dir]
154
+ ```
155
+
156
+ `fetch` is the default subcommand, so `621 fox solo --safe` and `621 fetch fox solo --safe` are equivalent.
157
+
158
+ ### Login
159
+
160
+ ```bash
161
+ $ 621 login nollafox YOUR_E621_API_KEY
162
+ $ 621 logout
163
+ ```
164
+
165
+ `login` writes `.six2one-login.json` next to `pyproject.toml`, and `logout` removes it. The file is gitignored by default. When present, those credentials are used for HTTP Basic auth and a username-specific `User-Agent` on API requests.
166
+
167
+ That login flow is one part of being a well-behaved e621 client. The other part is pacing: six2one uses an instance-owned rate limiter capped at 2 requests per second, and it pages through search results in chunks of at most 320 posts per API call, which is the e621 post-search maximum. Even `--all` fetches use that same pacing and pagination.
168
+
169
+ For the safe-only sister site, pass `--site e926` to any fetch or show command, or use the `926` binary, which applies that site default automatically:
170
+
171
+ ```bash
172
+ $ 621 fox solo --site e926 --all
173
+ $ 926 fox solo --all
174
+ $ 926 show 6394158 --fetch # fetch from e926 if available there
175
+ ```
176
+
177
+ ### Fetch
178
+
179
+ ```bash
180
+ $ 621 [fetch] TAGS... [options]
181
+ ```
182
+
183
+ | Option | Meaning |
184
+ |---|---|
185
+ | `TAGS` | e621 tag query terms. Supports `-tag`, `~tag`, wildcards, and grouped OR syntax. |
186
+ | `-o, --out DIR` | Output directory. Default: `./output/<query-slug>`. |
187
+ | `-n, --limit N` | Number of posts to fetch. Default: `320`. |
188
+ | `--all` | Fetch until the query is exhausted. |
189
+ | `--safe` / `--questionable` / `--explicit` | Shortcuts for `--rating`. |
190
+ | `--rating RATING` | One of `safe`, `questionable`, `explicit`, `s`, `q`, or `e`. |
191
+ | `--author NAME`, `--artist NAME`, `--by NAME` | Add an artist tag. Repeatable. |
192
+ | `--any TAG` | Add OR terms as `~TAG`. Repeatable and comma-separated. |
193
+ | `-x, --exclude TAG` | Exclude tags as `-TAG`. Repeatable and comma-separated. |
194
+ | `--site SITE` | `e621` or `e926`. Default: `e621` for `621`, `e926` for `926`. |
195
+ | `--size MODE` | `preview`, `sample`, or `original`. Default: `sample`. |
196
+ | `--resume` | Resume from an existing manifest with the same query and output. |
197
+ | `--merge` | Merge a different query into an existing manifest. |
198
+ | `--force-new` | Replace manifest state for a fresh run while leaving media files alone. |
199
+ | `--adopt-existing` | Adopt colliding files only when matching post metadata already exists. |
200
+ | `--dry-run` | Print the compiled query and exit. |
201
+ | `--validate-tags` | Check concrete and wildcard tags against the tag API before fetching. |
202
+ | `--strict` | Treat warnings, including missing file URLs, as errors. |
203
+
204
+ Hidden compatibility aliases still work: `--continue` for `--resume`, `--or` for `--any`, and `--file` for `--size`.
205
+
206
+ ### Show metadata
207
+
208
+ ```bash
209
+ $ 621 show 6394158
210
+ ```
211
+
212
+ `show` searches recursively under `./output` for six2one `manifest.json` files and returns a merged JSON view for matching posts. The `metadata` command is an alias. Each result combines the manifest post entry, `posts/<id>.json` when present, `captions/<id>.txt` when present, and filesystem-derived paths, existence flags, and file sizes.
213
+
214
+ By default, `show` reads only what is already on disk. Use `--root` to search a narrower folder, `--all` to show every manifest entry under that root, and `--fetch` to fetch remote post JSON only when an ID is missing locally:
215
+
216
+ ```bash
217
+ $ 621 show --all --root output/fox-solo-safe
218
+
219
+ # fetch from e926 if available there
220
+ $ 926 show 6394158 --fetch
221
+ ```
222
+
223
+ The merged object is filterable with dotted paths. Repeated filters and comma-separated filters are both accepted:
224
+
225
+ ```bash
226
+ $ 621 show 6394158 \
227
+ --filter local.image.absolute_path \
228
+ --filter caption.text \
229
+ --filter post.file.url
230
+
231
+ $ 621 show 6394158 -f local.image.absolute_path,caption.text,post.file.url
232
+ ```
233
+
234
+ Filtered keys flatten to their unique leaf names, so `caption.text` becomes `text`. If leaf names collide, six2one keeps enough path context to avoid overwriting values. Without filters, the full merged object is printed.
235
+
236
+ ```bash
237
+ $ 621 show 6394158 -f local.image.absolute_path,caption.text,post.file.url --pretty
238
+
239
+ # {
240
+ # "results": [
241
+ # {
242
+ # "absolute_path": "/Users/nollafox/output/fox-solo-safe/images/000006394158.jpg",
243
+ # "text": "anthro, ... english_text, hi_res, signature",
244
+ # "url": "https://static1.e621.net/data/51/b5/51b5a2f0925e153c2890e37836024f77.jpg"
245
+ # }
246
+ # ]
247
+ # }
248
+ ```
249
+ That makes it easy to export a local dataset index for scripts, audits, or LoRA training prep.
250
+
251
+
252
+
253
+ ### Prune
254
+
255
+ ```bash
256
+ $ 621 prune [output_dir]
257
+ ```
258
+
259
+ Prune scans the output folder for incomplete sibling sets. If an image is deleted by hand but its caption or post JSON remains, `621 prune` removes the orphaned companions and updates the manifest so a later `fetch --resume` can repair the set cleanly. If the output directory does not exist yet, prune creates it and exits.
260
+
261
+ ## Development
262
+
263
+ Install with Poetry:
264
+
265
+ ```bash
266
+ $ poetry install
267
+ ```
268
+
269
+ Run the CLI, the tests, and the compile check from inside the Poetry environment:
270
+
271
+ ```bash
272
+ $ poetry run 621 --help
273
+ $ poetry run 926 fox solo --dry-run
274
+ $ poetry run 621 show --help
275
+ $ poetry run python -m unittest discover -s tests
276
+ $ poetry run python -m compileall -q six2one
277
+ ```
278
+
279
+ <br>
280
+
281
+ ***
282
+
283
+ <p align="center">
284
+ <strong>six2one</strong> - manifest-backed e621 and e926 fetching.
285
+ </p>
286
+
287
+ <p align="center">
288
+ Crafted by <strong>Nolla Fox</strong>
289
+ </p>
@@ -0,0 +1,48 @@
1
+ [project]
2
+ name = "six2one"
3
+ version = "0.1.0"
4
+ description = "Manifest-backed e621/e926 dataset fetching from tiny numeric commands."
5
+ authors = [
6
+ {name = "Nolla Fox",email = "nollafox.dev@gmail.com"}
7
+ ]
8
+ readme = "README.md"
9
+ requires-python = ">=3.10"
10
+ dependencies = [
11
+ "aiohttp>=3.13.5,<4.0.0",
12
+ "aiofiles>=25.1.0,<26.0.0",
13
+ "tqdm>=4.67.3,<5.0.0"
14
+ ]
15
+ keywords = ["e621", "e926", "dataset", "downloader", "cli", "manifest"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Environment :: Console",
19
+ "Intended Audience :: End Users/Desktop",
20
+ "Intended Audience :: Developers",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Topic :: Internet :: WWW/HTTP",
27
+ "Topic :: Multimedia :: Graphics",
28
+ ]
29
+
30
+
31
+ [project.scripts]
32
+ 621 = "six2one.cli:sync_main"
33
+ 926 = "six2one.cli:sync_main"
34
+
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/nollafox/six2one"
38
+ Repository = "https://github.com/nollafox/six2one"
39
+ Issues = "https://github.com/nollafox/six2one/issues"
40
+
41
+
42
+ [tool.poetry]
43
+ packages = [{include = "six2one"}]
44
+
45
+
46
+ [build-system]
47
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
48
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1,3 @@
1
+ """Manifest-backed e621/e926 dataset fetching."""
2
+
3
+ __version__ = "0.1.0"