commit e01d249c8e1bfaf5ba5aee61397b5338671a7716 Author: Moritz Ruth Date: Sat Apr 19 20:36:38 2025 +0200 Initial commit diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..90f212f --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +DATABASE_URL=sqlite:./run/database.sqlite \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e310c9d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +.idea/ +/run/ +*.env \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6d6b6ed --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +# [WIP] minna-pima + +> Picture manager intended for usage with Minna. + +**`minna-pima`** integrates with [`minna-caos`](https://git.moritzruth.de/minna/minna-caos) to provide picture management features. + +Similarly to the latter, it is intended as a companion to a single application (“the app”) that has full authority. + + +## Features + +- automatic conversion of unwanted file formats + +- [ThumbHash](https://evanw.github.io/thumbhash/) generation + +- on-demand resizing and format conversion (without caching, see below) + +- similarity search for finding duplicates + +- CLI for administrative tasks + + +## Notes + +### Pictures are not cached + +`minna-pima` does not cache the pictures it generates on-demand. +This is by design: Caching is hard and better left to tools designated for this purpose. +It is therefore recommended to use a caching reverse-proxy in front of **minna-pima**. + + + + +### Example picture upload flow + +1. The _client_ uploads a PNG file to _`minna-caos`_ in cooperation with the _app_. +2. Because _`minna-caos`_ detected that the file is a PNG image, the _app_ decides to use `minna-pima`. +3. The _app_ sends a `GET /objects/ORIGINAL_UPLOAD_HASH/similar` request to _`minna-pima`_: + 1. _`minna-pima`_ fetches the object’s content and media type from _`minna-caos`_. + 2. _`minna-pima`_ computes a perceptual hash of the picture and uses it to search its database for similar pictures. + 3. It finds two matching pictures and returns the following result: + ```json + { + "similar_picture_ids": ["abc123", "def456"] + } + ``` + +4. The _app_ notifies the _client_ that there are similar pictures. +5. The _user_ decides that… + 1. the first picture is unrelated, and + 2. the second picture is a lower-quality version of the uploaded picture and should be replaced by the higher-quality version. +6. The _client_ notifies the _app_ of the user’s decision. +7. The _app_ creates a new upload through _`minna-caos`_. It’s ID is `CONVERTED_UPLOAD_ID`. +8. The _app_ sends a `POST /pictures/` request to _`minna_pima`_ with the following payload: + ```json + { + "hash": "ORIGINAL_UPLOAD_HASH", + "upload_id": "CONVERTED_UPLOAD_ID", + "replaces": ["def456"] + } + ``` + 1. _`minna-pima`_ fetches the content and the media type of the object from _`minna-caos`_. + 2. _`minna-pima`_ generates a ThumbHash and converts the picture to AVIF because PNG is not in the list of allowed source formats. + 3. _`minna-pima`_ uploads the picture to _`minna-caos`_ using `PATCH /uploads/CONVERTED_UPLOAD_ID`. + +9. The _app_ waits for the `CONVERTED_UPLOAD_ID` upload to finish. The new picture’s ID is also `CONVERTED_UPLOAD_ID`. + + A 100×100 WebP version can be fetched from `/pictures/CONVERTED_UPLOAD_ID/100x100.webp`. \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..1cbd329 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +max_width = 160 \ No newline at end of file