pasteup 0.1.0__py3-none-any.whl

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,481 @@
1
+ Metadata-Version: 2.5
2
+ Name: pasteup
3
+ Version: 0.1.0
4
+ Summary: Turn a folder of clip art into listing images: preview sheets, cover collages and preview videos.
5
+ Project-URL: Source, https://github.com/ftymoshchuk/pasteup
6
+ Project-URL: Issues, https://github.com/ftymoshchuk/pasteup/issues
7
+ Project-URL: Changelog, https://github.com/ftymoshchuk/pasteup/blob/main/CHANGELOG.md
8
+ License-Expression: Apache-2.0 AND OFL-1.1
9
+ License-File: LICENSE
10
+ License-File: src/pasteup/static/fonts/OFL-Caveat.txt
11
+ License-File: src/pasteup/static/fonts/OFL-PatrickHand.txt
12
+ Keywords: clip art,collage,contact sheet,etsy,listing images
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Web Environment
15
+ Classifier: Framework :: FastAPI
16
+ Classifier: Intended Audience :: End Users/Desktop
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Multimedia :: Graphics
24
+ Requires-Python: >=3.11
25
+ Requires-Dist: fastapi~=0.143.2
26
+ Requires-Dist: jinja2~=3.1
27
+ Requires-Dist: mullion~=0.2.1
28
+ Requires-Dist: pillow~=12.3
29
+ Requires-Dist: pydantic~=2.14
30
+ Requires-Dist: uvicorn~=0.54.0
31
+ Provides-Extra: dev
32
+ Requires-Dist: httpx2~=2.13; extra == 'dev'
33
+ Requires-Dist: pytest~=9.1; extra == 'dev'
34
+ Requires-Dist: ruff==0.17.1; extra == 'dev'
35
+ Description-Content-Type: text/markdown
36
+
37
+ # Pasteup
38
+
39
+ Turn a folder of clip art into listing images: preview sheets, a cover collage
40
+ and a short preview video, ready for Etsy and other marketplaces. Pasteup runs
41
+ on your own computer and works in your browser.
42
+
43
+ The name comes from *paste-up*, how clip art was used before desktop
44
+ publishing: cut out by hand and pasted onto a layout board.
45
+
46
+ ## What it does
47
+ - **Preview sheets:** 4x3 grids of your images on a white page, as PNG or JPG
48
+ at a chosen DPI.
49
+ - **Cover collage:** a single page in one of three layouts, for a listing's
50
+ main image.
51
+ - **Preview video:** an MP4 slideshow of the sheets, exactly as long as you ask
52
+ (Etsy's listing videos run 5 to 15 s).
53
+ - **Clean-up:** transparent PNGs land on white instead of black, phone photos
54
+ are turned upright, and near-white backgrounds can be removed.
55
+ - **Batch mode:** process every bundle folder under a parent folder in one go.
56
+
57
+ ## Install
58
+ You need Python 3.11 or newer. Preview videos also need
59
+ [ffmpeg](https://ffmpeg.org/download.html); everything else works without it.
60
+
61
+ Install Pasteup as a command of its own with [pipx](https://pipx.pypa.io) or
62
+ [uv](https://docs.astral.sh/uv/), with no virtualenv to manage:
63
+
64
+ ```bash
65
+ pipx install pasteup
66
+ # or
67
+ uv tool install pasteup
68
+ ```
69
+
70
+ To update it later: `pipx upgrade pasteup` or `uv tool upgrade pasteup`. To try
71
+ it without installing anything: `uvx pasteup`.
72
+
73
+ For changes not released yet, install from GitHub instead:
74
+ `pipx install git+https://github.com/ftymoshchuk/pasteup.git`.
75
+
76
+ ## Run
77
+ ```bash
78
+ pasteup
79
+ ```
80
+ Pasteup starts and opens in your browser. Keep its window open while you use
81
+ it, and press Ctrl+C there to stop it. Running `pasteup` again while it runs
82
+ just opens it again, and if another program has port 8000, Pasteup takes the
83
+ next free port and says which.
84
+
85
+ **To start it with a double-click,** run `pasteup --shortcut` once. It puts a
86
+ launcher on your desktop (`Pasteup.command` on macOS, `Pasteup.bat` on
87
+ Windows) or in your applications menu on Linux. Run it again if you reinstall
88
+ Pasteup somewhere else.
89
+
90
+ `pasteup --help` lists the options, among them `--no-browser` and `--port`;
91
+ `python -m pasteup` works too. The API docs are at `/docs`.
92
+
93
+ Pasteup reads and writes files at the paths you give it and has no login, so
94
+ keep it on `127.0.0.1` (the default). See [SECURITY.md](https://github.com/ftymoshchuk/pasteup/blob/main/SECURITY.md).
95
+
96
+ ### Optional environment variables
97
+ - `PASTEUP_INPUT_FOLDER`: prefills the input-folder field, e.g.
98
+ `PASTEUP_INPUT_FOLDER=~/Pictures/clip-art pasteup`.
99
+ - `PASTEUP_ALLOWED_HOSTS`: comma-separated host names to accept besides
100
+ `localhost`, `127.0.0.1` and `[::1]`. Only for serving the app under another
101
+ name on purpose.
102
+
103
+ ## Development
104
+ ```bash
105
+ git clone https://github.com/ftymoshchuk/pasteup.git
106
+ cd pasteup
107
+ python -m venv .venv
108
+ source .venv/bin/activate
109
+ pip install -e ".[dev]"
110
+ ruff check .
111
+ pytest
112
+ ```
113
+ CI runs the same on Python 3.11, 3.12 and 3.13, against a regular install of
114
+ the package and with ffmpeg present, so the preview-video tests encode real
115
+ files. It also builds the package, checks the README renders on PyPI and
116
+ installs the wheel. Its `ci-passed` job succeeds only when every other job
117
+ does, and is the one check `main` requires.
118
+
119
+ `scripts/protect-repo.sh` applies the repository's branch ruleset and security
120
+ settings through the GitHub CLI; it needs `gh` signed in as a repository admin.
121
+
122
+ ### Releasing
123
+ 1. Bump `__version__` in `src/pasteup/__init__.py` and move the
124
+ `[Unreleased]` entries in `CHANGELOG.md` under a heading for that version.
125
+ 2. Merge that through a pull request.
126
+ 3. Tag the merge commit and push the tag, e.g.
127
+ `git tag v0.2.0 origin/main && git push origin v0.2.0`.
128
+
129
+ The `Release` workflow then builds the package, refuses a tag that does not
130
+ match the built version or a changelog that does not name it, runs the test
131
+ suite against the built wheel, publishes to PyPI by trusted publishing (no
132
+ token is stored anywhere), and installs the result back from PyPI to check it.
133
+ PyPI trusts `release.yml` running in this repository's `pypi` environment.
134
+
135
+ Project layout:
136
+ - `src/pasteup/main.py`: the FastAPI app
137
+ - `src/pasteup/routes/`: the page and the `/api/jobs` endpoints
138
+ - `src/pasteup/services/`: scanning, page composition, collages, export, video
139
+ - `src/pasteup/models/`: request and response schemas
140
+ - `src/pasteup/templates/` and `src/pasteup/static/`: the UI
141
+ - `src/pasteup/cli.py`: the `pasteup` command; `shortcut.py` makes its launchers
142
+ - `tests/`
143
+
144
+ ---
145
+
146
+ ## Expected Supported File Types
147
+ Input scan supports the following image extensions (case-insensitive):
148
+ - `.jpg`
149
+ - `.jpeg`
150
+ - `.png`
151
+ - `.webp`
152
+
153
+ Notes:
154
+ - Folder scan is **non-recursive** (only files directly inside the selected folder).
155
+ - Export output format supports `PNG` and `JPG`.
156
+
157
+ ---
158
+
159
+ ## Default Setting Values
160
+ Default UI values:
161
+ - input folder: empty (set `PASTEUP_INPUT_FOLDER` to prefill it)
162
+ - `page_width`: `3000`
163
+ - `page_height`: `3000`
164
+ - `gap`: `30`
165
+ - `margin`: `60`
166
+ - `format`: `JPG`
167
+ - `page_unit`: `px` (also accepts `in` and `mm`, converted at `dpi`)
168
+ - `dpi`: `300`
169
+ - `background_clean_mode`: `off` (`flatten` or `transparent` to enable)
170
+ - `background_tolerance`: `240`
171
+ - `background_feather`: `1`
172
+ - `include_partial_page`: `false`
173
+
174
+ Behavior defaults:
175
+ - Scan sample size: first `5` files (`sample_files`) in sorted order.
176
+ - Images per page: `12` (fixed 4x3).
177
+ - Export naming: `page_001`, `page_002`, ...
178
+ - Export conflict handling: never overwrite existing `page_###`; continue at next index.
179
+
180
+ ---
181
+
182
+ ## Endpoint Descriptions
183
+ Base URL: `http://127.0.0.1:8000`
184
+
185
+ ### `GET /api/jobs/health`
186
+ Health check endpoint.
187
+
188
+ **Response**
189
+ ```json
190
+ {"status": "ok", "app": "pasteup", "version": "0.1.0"}
191
+ ```
192
+
193
+ ### `POST /api/jobs/scan-folder`
194
+ Validates a folder and scans for supported image files.
195
+
196
+ **Request body**
197
+ ```json
198
+ {
199
+ "folder_path": "/path/to/images"
200
+ }
201
+ ```
202
+
203
+ **Response fields**
204
+ - `valid` (bool): whether folder is usable and contains supported images.
205
+ - `image_count` (int): number of supported files found.
206
+ - `sample_files` (list[str]): up to 5 sorted sample filenames.
207
+ - `estimated_pages` (int): `image_count // 12` (whole pages only).
208
+ - `message` (str): summary or error detail.
209
+
210
+ ### `POST /api/jobs/scan-selected-batch-folders`
211
+ Validates selected batch subfolders under a scanned parent folder and reports which selected folders are ready for generation.
212
+
213
+ **Request body**
214
+ ```json
215
+ {
216
+ "parent_folder_path": "/path/to/parent-folder",
217
+ "selected_subfolders": ["/path/to/parent-folder/bundle-a", "/path/to/parent-folder/bundle-b"]
218
+ }
219
+ ```
220
+
221
+ **Response fields**
222
+ - `parent_folder_path` (str): normalized parent folder path.
223
+ - `selected_folders` (int): selected subfolders submitted for validation.
224
+ - `valid_selected_folders` (int): selected subfolders with eligible top-level images.
225
+ - `invalid_selected_folders` (int): selected subfolders skipped because they are invalid or not ready.
226
+ - `scanned_folders` (list): per-folder scan summaries for valid selected folders, including image counts and samples.
227
+ - `skipped_folders` (list): per-folder scan summaries for selected folders that were skipped or invalid.
228
+ - `message` (str): validation summary.
229
+
230
+ ### `POST /api/jobs/generate`
231
+ Used by the UI in **One bundle** mode. Scans one folder, composes pages with a fixed 4x3 layout, and exports files.
232
+
233
+ **Request body**
234
+ ```json
235
+ {
236
+ "folder_path": "/path/to/images",
237
+ "output_path": "/path/to/output",
238
+ "page_width": 3000,
239
+ "page_height": 4000,
240
+ "gap": 30,
241
+ "margin": 60,
242
+ "format": "PNG",
243
+ "page_unit": "px",
244
+ "dpi": 300,
245
+ "background_clean_mode": "off",
246
+ "background_tolerance": 240,
247
+ "background_feather": 1,
248
+ "include_partial_page": false
249
+ }
250
+ ```
251
+
252
+ **Output quality fields** (shared by `/generate`, `/generate-all-folders`,
253
+ `/generate-cover-collage` and `/generate-cover-collage-batch`)
254
+ - `page_unit` (`px` | `in` | `mm`): unit for `page_width`/`page_height`. Anything
255
+ other than `px` is converted to pixels at `dpi`, so `10 x 8 in` at `300` becomes
256
+ `3000 x 2400 px`.
257
+ - `dpi` (int, 1-1200, default `300`): stamped into the exported file. Without it a
258
+ `3000x4000` page carries no resolution and print software reads it at 72 DPI —
259
+ 41.7 x 55.6 in instead of 10 x 13.3 in.
260
+ - `background_clean_mode` (`off` | `flatten` | `transparent`, default `off`):
261
+ cleans a near-white background off each image before it is placed. Only pixels
262
+ **connected to the image border** are affected, so a white highlight inside the
263
+ subject is left alone.
264
+ - `background_tolerance` (int, 0-255, default `240`): the lowest channel value
265
+ counted as background. Lower catches more; too low starts eating the subject.
266
+ - `background_feather` (int, 0-25, default `1`): softens the mask edge, removing
267
+ the grey halo around anti-aliased cut-outs.
268
+ - `include_partial_page` (bool, default `false`, grid endpoints only): render a
269
+ final page even when it is not completely filled.
270
+ - Pages are capped at 100 megapixels after unit conversion (a 24 x 36 in poster
271
+ at 300 DPI is 78 MP); a larger request is refused with a message saying so.
272
+
273
+ **Preview video fields** (`/generate` and `/generate-all-folders`)
274
+ - `create_video` (bool, default `false`; alias `create_preview_video`): also
275
+ write an MP4 slideshow of the generated pages.
276
+ - `video_duration_seconds` (number, over 0 and at most `120`, default `15`;
277
+ alias `duration_seconds`): the video runs exactly this long. Etsy's listing
278
+ videos max out at 15 s.
279
+ - `video_transition` (`none` | `crossfade` | `flip`, default `none`; alias
280
+ `transition`): transitions last 0.5 s, less when pages are short.
281
+ - `video_filename` (default `preview.mp4`): a plain `.mp4` file name, written
282
+ into the output folder. Paths are refused.
283
+
284
+ The video is encoded at 30 fps and scaled to at most 1920 px on its long side
285
+ (never scaled up). If ffmpeg is missing or fails, the pages are still exported
286
+ and the response message gives ffmpeg's error.
287
+
288
+ **Response fields**
289
+ - `success` (bool)
290
+ - `total_images` (int): images actually placed
291
+ - `pages_generated` (int)
292
+ - `output_files` (list[str]): generated file paths
293
+ - `output_path` (str): normalized output folder path
294
+ - `images_dropped` (int): trailing images left out because they did not fill a page
295
+ - `backgrounds_cleaned` (int): images whose background was cleaned
296
+ - `page_geometry` (str): resolved page size, e.g. `3000x2400px (10.00x8.00in at 300 DPI)`
297
+ - `video_file` (str | null), `video_created` (bool): the preview video, when requested
298
+ - `message` (str): generation summary
299
+
300
+ ### `POST /api/jobs/generate-all-folders`
301
+ Used by the UI in **Many bundles** mode. Iterates bundle folders under a parent folder and writes each folder's generated pages under a created output root.
302
+
303
+ **Request body**
304
+ ```json
305
+ {
306
+ "parent_folder_path": "/path/to/parent-folder",
307
+ "created_output_root_path": "/path/to/created-output-root",
308
+ "page_width": 3000,
309
+ "page_height": 4000,
310
+ "gap": 30,
311
+ "margin": 60,
312
+ "format": "PNG",
313
+ "selected_subfolders": ["/path/to/parent-folder/bundle-a", "/path/to/parent-folder/bundle-b"]
314
+ }
315
+ ```
316
+
317
+ **Response fields**
318
+ - `success` (bool)
319
+ - `created_output_root_path` (str): normalized created output root
320
+ - `processed_folders` (int): selected bundle folders processed (kept for compatibility)
321
+ - `selected_folders` (int): selected bundle folders requested for generation
322
+ - `total_detected_folders` (int): eligible child folders detected under the parent folder
323
+ - `generated_folders` (int): bundle folders that produced pages
324
+ - `bundle_results` (list): per-folder generation summaries
325
+ - `message` (str): batch generation summary
326
+
327
+ ---
328
+
329
+ ## Using the app
330
+ 1. Run `pasteup` and open `http://127.0.0.1:8000/`.
331
+ 2. **Pick your clip art.** Choose **One bundle** (a folder of images) or **Many
332
+ bundles** (a folder with one folder inside per bundle), enter the folder and
333
+ click **Scan folder**. Results go in a `---Created---` folder next to it.
334
+ - One bundle: **Folder check** says how many images it found and how many
335
+ full sheets of 12 they make. If the folder holds only other folders, pick
336
+ one under **Choose folders**.
337
+ - Many bundles: every bundle inside is listed, picked and checked in one go.
338
+ Untick any you want to skip. A bundle that fails its check can be unticked,
339
+ or fixed and checked again with **Check selected bundles**.
340
+ 3. **Set up the page.** Size, units, DPI and format apply to sheets and covers
341
+ alike. **Clean up white backgrounds** can make near-white backgrounds pure
342
+ white or transparent.
343
+ 4. **Make your images.**
344
+ - **Preview sheets:** every image, 12 to a page (`POST /api/jobs/generate`,
345
+ or `POST /api/jobs/generate-all-folders` for many bundles), optionally with a
346
+ part-filled last page and an MP4 preview video.
347
+ - **Cover collage:** one cover in the chosen layout
348
+ (`POST /api/jobs/generate-cover-collage`, or
349
+ `POST /api/jobs/generate-cover-collage-batch`). **Only use certain images**
350
+ limits it to the files you name.
351
+ 5. **Your files** lists what was made and where it was saved; **Copy** puts a
352
+ path on the clipboard.
353
+
354
+ ---
355
+
356
+ ## Known Constraints
357
+ - Layout is fixed to **4 columns x 3 rows** (12 images per page) only.
358
+ - No custom row/column configuration in MVP.
359
+ - Scan is non-recursive.
360
+ - Trailing images that do not fill a complete page are left out by default; the
361
+ response reports how many. Set `include_partial_page: true` to render that final
362
+ page with its unused cells left white.
363
+
364
+ ---
365
+
366
+ ## Acceptance Checklist (MVP Scope)
367
+ - [ ] App runs locally with FastAPI and serves UI.
368
+ - [ ] Folder scan validates path and reports supported image count.
369
+ - [ ] Supported file types: JPG/JPEG/PNG/WEBP.
370
+ - [ ] Estimated pages computed using fixed 12 images/page.
371
+ - [ ] Generation creates fixed 4x3 grids with centered, aspect-preserving fit.
372
+ - [ ] Transparent PNGs composite onto white rather than black.
373
+ - [ ] Exports carry DPI metadata so the page has a real physical size.
374
+ - [ ] Background cleaning only touches near-white areas connected to the border.
375
+ - [ ] Output exports as PNG or JPG.
376
+ - [ ] Exports use sequential naming (`page_###`) and avoid overwriting existing files.
377
+ - [ ] API returns clear success/error messages for scan and generation.
378
+ - [ ] UI supports end-to-end flow: input paths → scan → generate.
379
+
380
+ ---
381
+
382
+ ## Troubleshooting
383
+
384
+ ### Transparent PNGs used to come out with a black background
385
+ Fixed. Transparent areas are composited onto the page's white rather than having
386
+ the alpha channel dropped, which is what left `(0, 0, 0)` showing through. This
387
+ covers RGBA, greyscale+alpha and palette PNGs carrying a `transparency` entry.
388
+
389
+ ### The exported page opens at the wrong physical size
390
+ Check `dpi`. A page with no resolution metadata is read at 72 DPI, so a
391
+ `3000x4000` export measures 41.7 x 55.6 in instead of 10 x 13.3 in. Exports now
392
+ carry `dpi` (default 300); set `page_unit` to `in` or `mm` to give the page size
393
+ in physical units directly.
394
+
395
+ ### Background cleaning did nothing
396
+ The image's border has to read as near-white before anything is cleaned — a
397
+ full-bleed photograph has no background to remove, and is returned untouched
398
+ rather than partially eaten. Lower `background_tolerance` for a greyer
399
+ background, or check the response message, which says which case applied.
400
+
401
+ ### Background cleaning ate part of the subject
402
+ Raise `background_tolerance`. Note that only pixels connected to the border are
403
+ ever cleaned, so an enclosed light area (the inside of a ring, a white label on a
404
+ dark product) is safe regardless of tolerance.
405
+
406
+ ### The preview video was not created
407
+ The pages are still exported; the response message gives the reason in
408
+ ffmpeg's own words. The usual cause is ffmpeg missing from `PATH` (install it,
409
+ then restart the app so it is found).
410
+
411
+ ### Generation fails with "Computed cell size is invalid"
412
+ The margin and gaps leave no room for a 4x3 grid on the page. Reduce `margin`
413
+ or `gap`, or enlarge the page.
414
+
415
+ ### Some images are missing from the output
416
+ Trailing images that do not fill a complete 12-image page are left out by
417
+ default. The response's `images_dropped` says how many; set
418
+ `include_partial_page: true` (in the app, tick **Include a part-filled last
419
+ page**) to render them on a final, partly-filled page.
420
+
421
+ ### Invalid folder path
422
+ Symptoms:
423
+ - Scan response has `valid: false`
424
+ - Message such as:
425
+ - `Invalid folder path.`
426
+ - `Folder does not exist: ...`
427
+ - `Path is not a folder: ...`
428
+
429
+ Fixes:
430
+ - Confirm absolute path is correct.
431
+ - Ensure the path exists on the same machine running the app.
432
+ - Use a directory path, not a file path.
433
+
434
+ ### Empty scan results
435
+ Symptoms:
436
+ - `valid: false`
437
+ - `image_count: 0`
438
+ - Message: `No supported image files found (.jpg, .jpeg, .png, .webp).`
439
+
440
+ Fixes:
441
+ - Confirm files are directly in the selected folder (non-recursive scan).
442
+ - Confirm extensions are supported.
443
+ - Check that files are not hidden/system placeholders.
444
+
445
+ ### Permission errors
446
+ Symptoms:
447
+ - Scan/generate fails when reading input files or writing output files.
448
+ - Some unreadable images are skipped during generation.
449
+
450
+ Fixes:
451
+ - Verify read permission on input folder and files.
452
+ - Verify write permission on output folder.
453
+ - Try a user-owned directory (for example inside your home directory).
454
+
455
+ ---
456
+
457
+ ## How images are loaded
458
+ Opening, background cleaning and page geometry live in
459
+ [mullion](https://github.com/sigrix-io/mullion), a small library Pasteup
460
+ depends on (`mullion~=0.2.1`, from PyPI).
461
+
462
+ `mullion.open_path()` is the one place a source image is opened. It composites
463
+ transparency onto white (a bare `convert("RGB")` drops the alpha and leaves the
464
+ black underneath showing) and applies EXIF orientation, so a phone photo is not
465
+ placed sideways. `mullion.clean_background()` is the border-seeded cleaner
466
+ described above, and `mullion.resolve_page_geometry()` turns `page_unit` and
467
+ `dpi` into the pixel size the exporter stamps. mullion's own suite tests these
468
+ directly; Pasteup's tests check the same claims through its composer and
469
+ exporter, so a bad mullion upgrade still fails here.
470
+
471
+ ---
472
+
473
+ ## License
474
+ Apache License 2.0, see [LICENSE](https://github.com/ftymoshchuk/pasteup/blob/main/LICENSE). Security reports:
475
+ see [SECURITY.md](https://github.com/ftymoshchuk/pasteup/blob/main/SECURITY.md).
476
+
477
+ The handwriting fonts the app ships with, [Caveat](https://fonts.google.com/specimen/Caveat)
478
+ and [Patrick Hand](https://fonts.google.com/specimen/Patrick+Hand), are under the
479
+ SIL Open Font License 1.1; their licence texts are in
480
+ [src/pasteup/static/fonts](https://github.com/ftymoshchuk/pasteup/tree/main/src/pasteup/static/fonts). They are bundled rather
481
+ than fetched, so the app needs no internet connection.
@@ -0,0 +1,32 @@
1
+ pasteup/__init__.py,sha256=wo5TS0sHotNtWUauYbEnnzYLZOxpS7IqwZUERTPYbgU,85
2
+ pasteup/__main__.py,sha256=frJ_0DbX5UBbCsfBPdTcQz4jczFoHlbe2P_BF_Znqjw,68
3
+ pasteup/cli.py,sha256=AujTOmWyOMxer_T84IghDzWHH_LEEzwrG87AONvIWBQ,6095
4
+ pasteup/main.py,sha256=WhuCLmY0jr_6Li1DS8VN1YX2ejc7I8RNiWyVXVIq8WI,1305
5
+ pasteup/shortcut.py,sha256=AqtF1njdD5E34kPECDZRI3r4U9n5iuFGQOUubUHO_3s,3700
6
+ pasteup/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ pasteup/models/schemas.py,sha256=uKMtQAZTdUlYp3iFwNSp3TvYh8LDfWaV7SgkYVs6Lgw,13438
8
+ pasteup/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ pasteup/routes/jobs.py,sha256=quyJ0XsF993cBIsMlwK9bZJOscueVBj6fBfwli-hdcc,25676
10
+ pasteup/routes/ui.py,sha256=FQov9t2ePFD0Ng5Jqif2XlOU6mvpWFO15Gk3MoY9y7Q,1463
11
+ pasteup/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ pasteup/services/cover_collage.py,sha256=i7Qe9LarFgY33346ievx37V4_j2JClbOjBahLJrIxYk,13307
13
+ pasteup/services/exporter.py,sha256=L1Oua83tNa1g3yOyY9UDpLFj3CLbBkXaaydwksTV0HI,3483
14
+ pasteup/services/folder_scanner.py,sha256=Tz6ZNcIIDnG02sa9cxwrzDzjSOn8Wd3W96JAz0AImrw,5131
15
+ pasteup/services/grid_composer.py,sha256=quXAwhQNzn9UCA3il1l2KB5EJxFmPNDTcl9dPTpCYBU,7067
16
+ pasteup/services/image_batcher.py,sha256=Jr0V2ukjDaZRyP4_iVpb_NHkL11WzPX440S__8-lQAE,696
17
+ pasteup/services/video_exporter.py,sha256=NoYlBOcktWqS9AOQNT1kahznZYCwuEkcQeYXtwZ13I0,8526
18
+ pasteup/static/favicon.svg,sha256=_LQUgMo89KgJHpmBt1WfKksjsGENnk-KgzBC6Xgzjxo,466
19
+ pasteup/static/css/styles.css,sha256=b9Zgomwber4VeZSgF41ukdXMHxQV2DppA_dwjtDSNkM,19660
20
+ pasteup/static/fonts/OFL-Caveat.txt,sha256=FjorQA4WkWrTGWKWyUbFJtDvzmuvIqIWQmn_xi-59nE,4384
21
+ pasteup/static/fonts/OFL-PatrickHand.txt,sha256=2_DfQw1ZlxIcxWnb5PkZj3bPOTEUQ48fbICknwbwKdY,4375
22
+ pasteup/static/fonts/caveat-latin-700.woff2,sha256=FfljgJWtXsmBb5PWaAXKGofnHcWna1lr_OHHjUcExAU,51020
23
+ pasteup/static/fonts/patrick-hand-latin-400.woff2,sha256=rFvJAzslcr-E059xUMFjTjftFujb7mMtbQvOrAu_AZk,23944
24
+ pasteup/static/js/app.js,sha256=FM5sUPXNM19lXkWCC6Y8mBKXA0mXgkUqr6RzSgeAE4k,60341
25
+ pasteup/templates/index.html,sha256=ozYrX7289AOadbM4mEXJoa-IoT8WYZtuO3wNYbchD2M,16411
26
+ pasteup-0.1.0.dist-info/METADATA,sha256=yWV_RLCK3ql0Ni360FmDmnk4Y1pv8XbK_3Wwtt1Z2MQ,20047
27
+ pasteup-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
28
+ pasteup-0.1.0.dist-info/entry_points.txt,sha256=CVG7qFMHeWSbpInAcSbJDxXslQIXLjsCI7vIN0-oYOw,45
29
+ pasteup-0.1.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
30
+ pasteup-0.1.0.dist-info/licenses/src/pasteup/static/fonts/OFL-Caveat.txt,sha256=FjorQA4WkWrTGWKWyUbFJtDvzmuvIqIWQmn_xi-59nE,4384
31
+ pasteup-0.1.0.dist-info/licenses/src/pasteup/static/fonts/OFL-PatrickHand.txt,sha256=2_DfQw1ZlxIcxWnb5PkZj3bPOTEUQ48fbICknwbwKdY,4375
32
+ pasteup-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ pasteup = pasteup.cli:main