Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I4a6b498153eccd5407510dd541b7f4816a6a6964
193 lines
7.8 KiB
Markdown
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.
|