schenesort 2.1.1__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,2 @@
1
+ export VIRTUAL_ENV=.venv
2
+ layout python
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_call: # Allow this workflow to be called by other workflows
8
+
9
+ jobs:
10
+ checks:
11
+ name: Code quality checks
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - name: Check out code
15
+ uses: actions/checkout@v4
16
+
17
+ - name: Set up Python
18
+ uses: actions/setup-python@v5
19
+ with:
20
+ python-version: '3.13'
21
+
22
+ - name: Install uv
23
+ uses: astral-sh/setup-uv@v4
24
+ with:
25
+ enable-cache: true
26
+
27
+ - name: Install dependencies
28
+ run: uv sync
29
+
30
+ - name: Run pre-commit checks
31
+ run: uv run pre-commit run --all-files
@@ -0,0 +1,38 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ jobs:
9
+ ci:
10
+ name: Run CI checks
11
+ uses: ./.github/workflows/ci.yml
12
+
13
+ build-and-publish:
14
+ name: Build and publish Python distribution to PyPI
15
+ runs-on: ubuntu-latest
16
+ needs: ci # Only publish if CI passes
17
+ permissions:
18
+ id-token: write # Required for trusted publishing to PyPI
19
+
20
+ steps:
21
+ - name: Check out code
22
+ uses: actions/checkout@v4
23
+
24
+ - name: Set up Python
25
+ uses: actions/setup-python@v5
26
+ with:
27
+ python-version: '3.13'
28
+
29
+ - name: Install uv
30
+ uses: astral-sh/setup-uv@v4
31
+ with:
32
+ enable-cache: true
33
+
34
+ - name: Build package
35
+ run: uv build
36
+
37
+ - name: Publish to PyPI
38
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,25 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.so
5
+ .Python
6
+ build/
7
+ develop-eggs/
8
+ dist/
9
+ downloads/
10
+ eggs/
11
+ .eggs/
12
+ lib/
13
+ lib64/
14
+ parts/
15
+ sdist/
16
+ var/
17
+ wheels/
18
+ *.egg-info/
19
+ .installed.cfg
20
+ *.egg
21
+ .venv/
22
+ venv/
23
+ ENV/
24
+ .pytest_cache/
25
+ .ruff_cache/
@@ -0,0 +1,17 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.14.14
4
+ hooks:
5
+ - id: ruff-check
6
+ args: [--fix]
7
+ - id: ruff-format
8
+
9
+ - repo: local
10
+ hooks:
11
+ - id: pytest
12
+ name: pytest
13
+ entry: uv run --frozen pytest tests/ -q
14
+ language: system
15
+ types: [python]
16
+ pass_filenames: false
17
+ always_run: true
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,53 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ Schenesort is a CLI tool for managing wallpaper collections. It sanitises filenames (lowercase, spaces to underscores), validates image file extensions against actual content, indexes metadata into SQLite for fast querying, provides a terminal UI browser, and can rename images based on AI-generated descriptions using Ollama.
8
+
9
+ ## Commands
10
+
11
+ ```bash
12
+ # Run the CLI
13
+ uv run schenesort --help
14
+ uv run schenesort sanitise <path> --dry-run
15
+ uv run schenesort validate <path> --fix
16
+ uv run schenesort info <path>
17
+ uv run schenesort describe <path> --dry-run -m llava
18
+ uv run schenesort browse <path>
19
+ uv run schenesort index <path>
20
+ uv run schenesort get --mood peaceful --screen 4K
21
+ uv run schenesort stats
22
+ uv run schenesort cleanup <path> --dry-run
23
+ uv run schenesort config --create
24
+
25
+ # Testing
26
+ uv run pytest tests/ -v # Run all tests
27
+ uv run pytest tests/test_sanitise.py -v # Run specific test file
28
+ uv run pytest -k "test_sanitise_dry" # Run tests matching pattern
29
+
30
+ # Linting
31
+ uv run ruff check src/ tests/ # Check for issues
32
+ uv run ruff check src/ tests/ --fix # Auto-fix issues
33
+ uv run ruff format src/ tests/ # Format code
34
+
35
+ # Pre-commit
36
+ uv run pre-commit run --all-files # Run all hooks manually
37
+ ```
38
+
39
+ ## Architecture
40
+
41
+ - `src/schenesort/cli.py` - All CLI commands using Typer. Contains `sanitise`, `validate`, `info`, `browse`, `index`, `get`, `stats`, `cleanup`, `config`, `describe` commands plus helper functions `sanitise_filename()`, `get_actual_image_type()`, `validate_extension()`, `get_image_dimensions()`, and `describe_image()`.
42
+ - `src/schenesort/config.py` - Configuration file handling. Loads settings from `~/.config/schenesort/config.toml`.
43
+ - `src/schenesort/xmp.py` - XMP sidecar file handling. Contains `ImageMetadata` dataclass, `read_xmp()`, `write_xmp()`, and `get_recommended_screen()`.
44
+ - `src/schenesort/db.py` - SQLite database for collection indexing. Contains `WallpaperDB` class with query and stats methods.
45
+ - `src/schenesort/tui/` - Terminal UI browser using Textual.
46
+ - `app.py` - `WallpaperBrowser` main application
47
+ - `widgets/image_preview.py` - Image display widget using textual-image
48
+ - `widgets/metadata_panel.py` - Metadata display panel
49
+ - `schenesort.yazi/` - Yazi file manager plugin for previewing metadata
50
+ - `tests/test_sanitise.py` - Unit tests for `sanitise_filename()` function
51
+ - `tests/test_cli.py` - Integration tests for CLI commands using `typer.testing.CliRunner`
52
+
53
+ Image type detection uses the `filetype` library to read file headers (Python 3.13 removed `imghdr`). AI image description uses the `ollama` library to communicate with a local Ollama instance running a vision model (default: llava).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Thys Meintjes
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,318 @@
1
+ Metadata-Version: 2.4
2
+ Name: schenesort
3
+ Version: 2.1.1
4
+ Summary: Wallpaper collection management CLI tool
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.13
7
+ Requires-Dist: defusedxml>=0.7.1
8
+ Requires-Dist: filetype>=1.2.0
9
+ Requires-Dist: ollama>=0.6.1
10
+ Requires-Dist: textual-image>=0.8.5
11
+ Requires-Dist: textual>=0.95.0
12
+ Requires-Dist: typer>=0.21.1
13
+ Description-Content-Type: text/markdown
14
+
15
+ # Schenesort v2.1.1
16
+
17
+ A CLI tool for managing wallpaper collections with AI-powered metadata, terminal UI browsing, and fast SQLite-based querying.
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ uv sync
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```bash
28
+ # Generate AI metadata for images
29
+ schenesort metadata generate ~/wallpapers -r
30
+
31
+ # Browse with TUI
32
+ schenesort browse ~/wallpapers
33
+
34
+ # Index the collection
35
+ schenesort index ~/wallpapers
36
+
37
+ # Query wallpapers
38
+ schenesort get --mood peaceful --screen 4K
39
+ schenesort get -1 -p | xargs feh # random wallpaper
40
+ ```
41
+
42
+ ## Commands
43
+
44
+ | Command | Description |
45
+ |------------------------------|-----------------------------------------------------|
46
+ | `browse` | Terminal UI browser with image preview and metadata |
47
+ | `index` | Build SQLite index for fast querying |
48
+ | `get` | Query wallpapers by metadata attributes |
49
+ | `stats` | Show collection statistics from index |
50
+ | `config` | Show or create configuration file |
51
+ | `sanitise` | Rename files to Unix-friendly format |
52
+ | `validate` | Check image extensions match file content |
53
+ | `cleanup` | Delete orphaned XMP sidecars |
54
+ | `info` | Show collection file statistics |
55
+ | `describe` | AI-rename images based on content (Ollama) |
56
+ | `models` | List available Ollama models |
57
+ | `metadata show` | Display XMP sidecar metadata |
58
+ | `metadata set` | Manually set metadata fields |
59
+ | `metadata generate` | Generate metadata with AI (Ollama) |
60
+ | `metadata update-dimensions` | Add image dimensions to existing sidecars |
61
+ | `metadata embed` | Embed sidecar data into image files |
62
+
63
+ ## Terminal UI Browser
64
+
65
+ Browse your wallpaper collection with image preview and metadata display:
66
+
67
+ ```bash
68
+ schenesort browse ~/wallpapers
69
+ schenesort browse ~/wallpapers -r # recursive
70
+ ```
71
+
72
+ **Keyboard shortcuts:**
73
+ | Key | Action |
74
+ |--------------|----------------|
75
+ | `j` / `Down` | Next image |
76
+ | `k` / `Up` | Previous image |
77
+ | `g` / `Home` | First image |
78
+ | `G` / `End` | Last image |
79
+ | `+` / `-` | Zoom in/out |
80
+ | `q` | Quit |
81
+
82
+ The TUI uses textual-image for rendering, which auto-detects terminal graphics support (Sixel, iTerm2, Kitty).
83
+
84
+ ## Collection Indexing and Querying
85
+
86
+ Build a SQLite index for fast querying across your entire collection:
87
+
88
+ ```bash
89
+ # Build/update the index
90
+ schenesort index ~/wallpapers
91
+ schenesort index ~/wallpapers --rebuild # rebuild from scratch
92
+ schenesort index ~/wallpapers --prune # remove deleted files
93
+
94
+ # Query wallpapers
95
+ schenesort get --tag cyberpunk
96
+ schenesort get --mood peaceful --style photography
97
+ schenesort get --screen 4K --subject landscape
98
+ schenesort get --color blue --time sunset
99
+ schenesort get --min-width 3840
100
+ schenesort get -q "mountain" # text search
101
+
102
+ # Random selection
103
+ schenesort get --random -n 10 # 10 random wallpapers
104
+ schenesort get -1 # single random wallpaper
105
+ schenesort get -1 --mood dramatic # random with filter
106
+
107
+ # For scripting (paths only)
108
+ schenesort get -1 -p # just the path
109
+ feh $(schenesort get -1 -p) # set random wallpaper
110
+ hyprctl hyprpaper wallpaper "eDP-1,$(schenesort get -1 -p)"
111
+
112
+ # View collection stats
113
+ schenesort stats
114
+ ```
115
+
116
+ The database is stored at `$XDG_DATA_HOME/schenesort/index.db` (default: `~/.local/share/schenesort/index.db`).
117
+
118
+ ## Metadata Management
119
+
120
+ Store metadata in XMP sidecar files (`.xmp`) alongside images without modifying the original files.
121
+
122
+ ### Metadata Fields
123
+
124
+ | Field | Description |
125
+ |----------------------|----------------------------------------------------------|
126
+ | `description` | Short description (used for filenames) |
127
+ | `scene` | Detailed scene description |
128
+ | `tags` | Keywords/tags |
129
+ | `mood` | Visual mood (peaceful, dramatic, mysterious, etc.) |
130
+ | `style` | Art style (photography, digital art, illustration, etc.) |
131
+ | `colors` | Dominant colors |
132
+ | `time_of_day` | Time depicted (day, night, sunset, etc.) |
133
+ | `subject` | Primary subject (landscape, urban, nature, etc.) |
134
+ | `width` / `height` | Image dimensions in pixels |
135
+ | `recommended_screen` | Best screen size (4K, 1440p, 1080p, etc.) |
136
+ | `source` | Source URL or info |
137
+ | `ai_model` | Model used for metadata generation |
138
+
139
+ ### Generate Metadata with AI
140
+
141
+ ```bash
142
+ # Preview what would be generated
143
+ schenesort metadata generate ~/wallpapers --dry-run
144
+
145
+ # Generate metadata and rename files
146
+ schenesort metadata generate ~/wallpapers -m llava
147
+
148
+ # Generate without renaming
149
+ schenesort metadata generate ~/wallpapers --no-rename
150
+
151
+ # Overwrite existing metadata
152
+ schenesort metadata generate ~/wallpapers --overwrite
153
+
154
+ # Use remote Ollama server
155
+ schenesort metadata generate ~/wallpapers --host http://server:11434
156
+ ```
157
+
158
+ ### Update Dimensions Only
159
+
160
+ Add dimensions to existing sidecars without re-running AI inference:
161
+
162
+ ```bash
163
+ schenesort metadata update-dimensions ~/wallpapers -r
164
+ ```
165
+
166
+ ### Manual Metadata
167
+
168
+ ```bash
169
+ # Show metadata
170
+ schenesort metadata show image.jpg
171
+
172
+ # Set fields manually
173
+ schenesort metadata set image.jpg -d "Mountain sunset landscape"
174
+ schenesort metadata set image.jpg -t "nature,sunset,mountains"
175
+ schenesort metadata set image.jpg -a "peaceful" # add tag
176
+ schenesort metadata set image.jpg -s "https://unsplash.com/..."
177
+ ```
178
+
179
+ ### Embed into Image Files
180
+
181
+ Write metadata directly into images (requires `exiftool`):
182
+
183
+ ```bash
184
+ schenesort metadata embed ~/wallpapers -r
185
+ ```
186
+
187
+ ## Filename Sanitation
188
+
189
+ The `sanitise` command makes filenames Unix-friendly:
190
+
191
+ ```bash
192
+ schenesort sanitise ~/wallpapers --dry-run
193
+ schenesort sanitise ~/wallpapers -r
194
+ ```
195
+
196
+ | Rule | Example |
197
+ |-----------------------------|-------------------------------------|
198
+ | Lowercase | `HelloWorld.JPG` → `helloworld.jpg` |
199
+ | Spaces → underscore | `my file.jpg` → `my_file.jpg` |
200
+ | Remove punctuation | `file(1)!.jpg` → `file1.jpg` |
201
+ | Collapse underscores | `a___b.jpg` → `a_b.jpg` |
202
+ | Strip leading/trailing `_-` | `_file_.jpg` → `file.jpg` |
203
+
204
+ ## Cleanup Orphaned Sidecars
205
+
206
+ Delete XMP sidecar files that have no corresponding image:
207
+
208
+ ```bash
209
+ schenesort cleanup ~/wallpapers --dry-run
210
+ schenesort cleanup ~/wallpapers -r
211
+ ```
212
+
213
+ ## Configuration
214
+
215
+ Schenesort follows XDG Base Directory spec:
216
+ - Config: `$XDG_CONFIG_HOME/schenesort/config.toml` (default: `~/.config/schenesort/config.toml`)
217
+ - Data: `$XDG_DATA_HOME/schenesort/index.db` (default: `~/.local/share/schenesort/index.db`)
218
+
219
+ ```bash
220
+ # Show current config
221
+ schenesort config
222
+
223
+ # Create default config file
224
+ schenesort config --create
225
+ ```
226
+
227
+ Config file format:
228
+
229
+ ```toml
230
+ [ollama]
231
+ # Ollama server URL (leave empty for localhost:11434)
232
+ host = "http://server:11434"
233
+
234
+ # Default vision model
235
+ model = "llava:13b"
236
+
237
+ [paths]
238
+ # Default wallpaper collection path
239
+ wallpaper = "~/wallpapers"
240
+ ```
241
+
242
+ Command-line options override config file settings.
243
+
244
+ ## Ollama Setup (Arch Linux)
245
+
246
+ ```bash
247
+ # Install
248
+ yay -S ollama ollama-cuda # for NVIDIA GPU
249
+
250
+ # Start service
251
+ sudo systemctl enable --now ollama
252
+
253
+ # Pull a vision model
254
+ ollama pull llava # ~4GB
255
+ ollama pull llava:13b # ~8GB, better quality
256
+
257
+ # List available models
258
+ schenesort models
259
+ ```
260
+
261
+ ## Yazi Plugin
262
+
263
+ A [Yazi](https://yazi-rs.github.io/) previewer plugin that displays XMP metadata alongside image previews.
264
+
265
+ ### Installation
266
+
267
+ ```bash
268
+ # Copy plugin
269
+ mkdir -p ~/.config/yazi/plugins/schenesort.yazi
270
+ cp schenesort.yazi/main.lua ~/.config/yazi/plugins/schenesort.yazi/
271
+
272
+ # Install exiftool config for custom namespace
273
+ mkdir -p ~/.config/ExifTool
274
+ cp schenesort.yazi/schenesort.config ~/.config/ExifTool/
275
+
276
+ # Install exiftool
277
+ sudo pacman -S perl-image-exiftool # Arch
278
+ ```
279
+
280
+ Add to `~/.config/yazi/yazi.toml`:
281
+
282
+ ```toml
283
+ [plugin]
284
+ prepend_previewers = [
285
+ { mime = "image/*", run = "schenesort" },
286
+ ]
287
+ ```
288
+
289
+ See [schenesort.yazi/README.md](schenesort.yazi/README.md) for details.
290
+
291
+ ## XMP Sidecar Format
292
+
293
+ ```
294
+ ~/wallpapers/
295
+ ├── mountain_sunset.jpg
296
+ ├── mountain_sunset.jpg.xmp ← metadata stored here
297
+ ├── cyberpunk_city.png
298
+ └── cyberpunk_city.png.xmp
299
+ ```
300
+
301
+ Metadata is stored in standard XMP format, compatible with digiKam, darktable, and Lightroom.
302
+
303
+ ## Screen Size Recommendations
304
+
305
+ Images are tagged with recommended screen sizes based on resolution:
306
+
307
+ | Screen | Resolution |
308
+ |--------|------------|
309
+ | 8K | 7680x4320 |
310
+ | 5K | 5120x2880 |
311
+ | 4K | 3840x2160 |
312
+ | Ultrawide 4K | 5120x2160 |
313
+ | Ultrawide 1440p | 3440x1440 |
314
+ | 1440p | 2560x1440 |
315
+ | 1080p | 1920x1080 |
316
+ | 720p | 1280x720 |
317
+
318
+ An image is recommended for a screen size if it can cover the screen without upscaling.