Metadata-Version: 2.5
Name: pixindex
Version: 0.1.0
Summary: Index pictures in a folder or S3 prefix. Query the catalog. Export it.
Author: Waseem Jan
License: MIT
License-File: LICENSE
Requires-Python: >=3.12
Requires-Dist: boto3>=1.34
Requires-Dist: pillow>=10.0
Requires-Dist: typer>=0.12
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# pixindex

pixindex looks through your pictures and writes down the useful facts about each one: size, camera, date, and whether the photo has a GPS location.

You run it in the terminal. It does not start a website, a queue, or a background service.

It works on a folder on your computer, or on pictures in an Amazon S3 bucket.

## What it does today

Point it at a folder. It finds `.jpg`, `.jpeg`, `.png`, and `.webp` files.

For each picture it saves:

- the full path
- file size
- width and height
- camera make and model, if the file has that data
- when the photo was taken, if the file has that data
- GPS coordinates, if the file has them

That list is stored in a single SQLite file on your machine. Think of it as a notebook, not a photo library. pixindex does not move, copy, or change your pictures.

Hidden files (names that start with `.`) and folders that start with `.` are skipped.

## Install

You need Python 3.12 or newer.

On Ubuntu, if `python3.12 -m venv` fails, run `sudo apt install python3.12-venv` first.

```bash
python3.12 -m venv .venv
source .venv/bin/activate
pip install -e .
pixindex --help
```

## Use it

Index a folder and keep the notebook in the current directory:

```bash
pixindex --db ./catalog.sqlite index ./photos
```

Or index one picture:

```bash
pixindex --db ./catalog.sqlite index ./photos/beach.jpg
```

Or an S3 prefix. pixindex uses your normal AWS credentials (`AWS_PROFILE` or `AWS_ACCESS_KEY_ID`). It does not change objects in the bucket.

```bash
pixindex --db ./catalog.sqlite index s3://my-bucket/photos/2024/
```

JPEG files on S3 are read from the start of the object only, enough to get the metadata. PNG and WebP are downloaded in full. The next run skips objects whose ETag has not changed.

When it finishes you will see something like:

```text
Indexed 12, skipped 0, failed 0.
```

- **indexed** — new or changed pictures written to the notebook
- **skipped** — already in the notebook, and the file has not changed
- **failed** — not a readable image; pixindex prints the path and continues

If you press `Ctrl+C`, it stops. Pictures it already saved stay in the notebook. Run the same command again to continue. Unchanged files are skipped.

If you leave out `--db`, the notebook is stored at:

```text
~/.local/share/pixindex/index.sqlite
```

## Run it again

Same folder, same `--db`:

```bash
pixindex --db ./catalog.sqlite index ./photos
```

pixindex only re-reads a file if the size or the last-modified time changed. A large folder is slow the first time. Later runs are mostly a quick check.

## See a summary

Use the same `--db` as when you indexed:

```bash
pixindex --db ./catalog.sqlite stat
```

You get a short report: how many pictures, how much space they take, the date range, how many have GPS, and which cameras showed up.

To summarize only one folder you already indexed:

```bash
pixindex --db ./catalog.sqlite stat --source ./photos
```

If you have not indexed anything yet, stat tells you that and exits.

## Search

Search prints one file path per line. Use the same `--db` as when you indexed.

```bash
pixindex --db ./catalog.sqlite search --camera Nikon --has-gps
pixindex --db ./catalog.sqlite search --after 2024-06-01 --before 2024-08-31
pixindex --db ./catalog.sqlite search --ext jpg --min-size 5mb
pixindex --db ./catalog.sqlite search --source ./photos --no-gps
```

You can combine flags. A picture must match all of them.

- `--camera` — make or model contains this text (`Nikon` matches `Nikon` or `NIKON Z8`)
- `--after` / `--before` — date the photo was taken, `YYYY-MM-DD`. Both ends are included. Pictures with no date are left out.
- `--has-gps` / `--no-gps` — has a location, or does not
- `--ext` — file type, for example `jpg` or `png`. `jpg` also matches `.jpeg`
- `--min-size` — smallest file size, for example `5mb` or `200kb`
- `--source` — only pictures from one folder you already indexed

No matches means no output. That is normal.

If a date or size does not look right, pixindex says so and exits.

## Export

Export writes the same matches as search, as a table. It prints to the terminal. Redirect it to a file if you want to keep it.

```bash
pixindex --db ./catalog.sqlite export csv > inventory.csv
pixindex --db ./catalog.sqlite export json > inventory.json
pixindex --db ./catalog.sqlite export csv --camera Nikon --has-gps > nikon-gps.csv
```

`csv` is for a spreadsheet. `json` is for a script. The columns are path, folder, size, width, height, date taken, camera, and GPS.

Export accepts the same filters as search. An empty result is still a valid file: CSV has only the header row, JSON is `[]`.

Anything other than `csv` or `json` is an error.

`--source` also works with an S3 prefix you already indexed:

```bash
pixindex --db ./catalog.sqlite search --source s3://my-bucket/photos/2024
pixindex --db ./catalog.sqlite stat --source s3://my-bucket/photos/2024
```

If AWS credentials are missing, or the bucket cannot be listed, pixindex says so and exits. A single unreadable object is one error line; the rest continue.

## Tests

```bash
source .venv/bin/activate
pip install -e ".[dev]"
pytest
```
