pinakes/README.md
NotAShelf 6a73d11c4b
initial commit
Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I4a6b498153eccd5407510dd541b7f4816a6a6964
2026-01-31 15:20:30 +03:00

193 lines
7.8 KiB
Markdown

# Pinakes
A media cataloging and library management system written in Rust. Pinakes
indexes files across configured directories, extracts metadata from audio,
video, document, and text files, and provides full-text search with tagging,
collections, and audit logging. It supports both SQLite and PostgreSQL backends.
## Building
```sh
# Build all compilable crates
cargo build -p pinakes-core -p pinakes-server -p pinakes-tui
# The Dioxus UI requires GTK3 and libsoup system libraries:
# On Debian/Ubuntu: apt install libgtk-3-dev libsoup-3.0-dev libwebkit2gtk-4.1-dev
# On Fedora: dnf install gtk3-devel libsoup3-devel webkit2gtk4.1-devel
# On Nix: Use the dev shell, everything is provided :)
cargo build -p pinakes-ui
```
## Configuration
Copy the example config and edit it:
```sh
cp pinakes.toml.example pinakes.toml
```
Key settings:
- `storage.backend` -- `"sqlite"` or `"postgres"`
- `storage.sqlite.path` -- Path to the SQLite database file
- `storage.postgres.*` -- PostgreSQL connection parameters
- `directories.roots` -- Directories to scan for media files
- `scanning.watch` -- Enable filesystem watching for automatic imports
- `scanning.ignore_patterns` -- Patterns to skip during scanning (e.g., `".*"`,
`"node_modules"`)
- `server.host` / `server.port` -- Server bind address
## Running
### Server
```sh
cargo run -p pinakes-server -- pinakes.toml
# or
cargo run -p pinakes-server -- --config pinakes.toml
```
The server starts on the configured host:port (default `127.0.0.1:3000`).
### TUI
```sh
cargo run -p pinakes-tui
# or with a custom server URL:
cargo run -p pinakes-tui -- --server http://localhost:3000
```
Keybindings:
<!-- markdownlint-disable MD013-->
| Key | Action |
| --------------------- | -------------------------------------------------------- |
| `q` / `Ctrl-C` | Quit |
| `j` / `k` | Navigate down / up |
| `Enter` | Select / confirm |
| `Esc` | Back |
| `/` | Search |
| `i` | Import file |
| `o` | Open file |
| `d` | Delete (media in library, tag/collection in their views) |
| `t` | Tags view |
| `c` | Collections view |
| `a` | Audit log view |
| `s` | Trigger scan |
| `r` | Refresh current view |
| `n` | Create new tag (in tags view) |
| `+` | Tag selected media (in detail view) |
| `-` | Untag selected media (in detail view) |
| `Tab` / `Shift-Tab` | Next / previous tab |
| `PageUp` / `PageDown` | Paginate |
<!-- markdownlint-enable MD013-->
### Desktop/Web UI
```sh
cargo run -p pinakes-ui
```
Set `PINAKES_SERVER_URL` to point at the server if it is not on
`localhost:3000`.
## API
All endpoints are under `/api/v1`.
### Media
| Method | Path | Description |
| -------- | -------------------- | ------------------------------------- |
| `POST` | `/media/import` | Import a file (`{"path": "..."}`) |
| `GET` | `/media` | List media (query: `offset`, `limit`) |
| `GET` | `/media/{id}` | Get media item |
| `PATCH` | `/media/{id}` | Update metadata |
| `DELETE` | `/media/{id}` | Delete media item |
| `GET` | `/media/{id}/stream` | Stream file content |
| `POST` | `/media/{id}/open` | Open with system viewer |
### Search
| Method | Path | Description |
| ------ | --------------- | ---------------------------------------------- |
| `GET` | `/search?q=...` | Search (query: `q`, `sort`, `offset`, `limit`) |
Search syntax: `term`, `"exact phrase"`, `field:value`, `type:pdf`, `tag:music`,
`prefix*`, `fuzzy~`, `-excluded`, `a b` (AND), `a OR b`, `(grouped)`.
### Tags
<!-- markdownlint-disable MD013-->
| Method | Path | Description |
| -------- | --------------------------- | ------------------------------------------------ |
| `POST` | `/tags` | Create tag (`{"name": "...", "parent_id": ...}`) |
| `GET` | `/tags` | List all tags |
| `GET` | `/tags/{id}` | Get tag |
| `DELETE` | `/tags/{id}` | Delete tag |
| `POST` | `/media/{id}/tags` | Tag media (`{"tag_id": "..."}`) |
| `GET` | `/media/{id}/tags` | List media's tags |
| `DELETE` | `/media/{id}/tags/{tag_id}` | Untag media |
<!-- markdownlint-enable MD013-->
### Collections
| Method | Path | Description |
| -------- | ---------------------------------- | ----------------- |
| `POST` | `/collections` | Create collection |
| `GET` | `/collections` | List collections |
| `GET` | `/collections/{id}` | Get collection |
| `DELETE` | `/collections/{id}` | Delete collection |
| `POST` | `/collections/{id}/members` | Add member |
| `GET` | `/collections/{id}/members` | List members |
| `DELETE` | `/collections/{cid}/members/{mid}` | Remove member |
Virtual collections (kind `"virtual"`) evaluate their `filter_query` as a search
query when listing members, returning results dynamically.
### Audit & Scanning
<!-- markdownlint-disable MD013-->
| Method | Path | Description |
| ------ | -------- | ----------------------------------------------------------------------------- |
| `GET` | `/audit` | List audit log (query: `offset`, `limit`) |
| `POST` | `/scan` | Trigger directory scan (`{"path": "/..."}` or `{"path": null}` for all roots) |
<!-- markdownlint-enable MD013-->
## Testing
```sh
# Unit and integration tests for the core library (SQLite in-memory)
cargo test -p pinakes-core
# API integration tests for the server
cargo test -p pinakes-server
```
## Supported Media Types
| Category | Formats |
| -------- | ------------------------------- |
| Audio | MP3, FLAC, OGG, WAV, AAC, Opus |
| Video | MP4, MKV, AVI, WebM |
| Document | PDF, EPUB, DjVu |
| Text | Markdown, Plain text |
| Image | JPEG, PNG, GIF, WebP, SVG, AVIF |
Metadata extraction uses lofty (audio, MP4), matroska (MKV), lopdf (PDF), epub
(EPUB), and gray_matter (Markdown frontmatter).
## Storage Backends
**SQLite** (default) -- Single-file database with WAL mode and FTS5 full-text
search. Bundled SQLite guarantees FTS5 availability.
**PostgreSQL** -- Native async with connection pooling (deadpool-postgres). Uses
tsvector with weighted columns for full-text search and pg_trgm for fuzzy
matching. Requires the `pg_trgm` extension.