# Pinakes Pinakes, named after the first known library cataloging system designed to be the last library cataloging system you will ever need. Pinakes indexes files across configured directories, extracts metadata from audio, video, document and text files, and provides full-text search with tagging, collections, roles, audit logging and more. It supports both SQlite (for easy bootstrapping) and PostgreSQL (production deployments) as available database backends. ## Building ```bash # 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 # Alternatively, while app deps are in PATH, you may simply build the entire # workspace. $ cargo build --workspace ``` ## Configuration Pinakes runs with its own built-in configuration file out of the box. While using the default configuration, you will not be able to edit the configuration but it will provide the minimum required configuration values to get you going with Pinakes. If you are more interested in fully configuring Pinales, you must create your own configuration. You may copy the example config and edit it to your needs: ```bash # Copy the sample config $ cp pinakes.example.toml 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 To use Pinakes, you will need the server to be running. The GUI on its own will work, but it will not be functional without the server. ```sh # Start the server first $ 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`). In a production scenario you are encouraged to reverse proxy the service, and prefer SSL. ### TUI The Pinakes TUI can be used to manage your collections from the comfort of your terminal. While the server is running you may connect to it using the `--server` flag. ```bash # Using defaults $ cargo run -p pinakes-tui # or with a custom server URL: $ cargo run -p pinakes-tui -- --server http://localhost:3000 ``` #### Keybindings The TUI component of Pinakes is designed to be keyboard-centric, as it is designed for the terminal. The keybindings are as follows: | 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 | ### Desktop/Web UI Pinakes features a fully fledged Desktop and Web UI powered by Dioxus. Those two components are meant as a GUI frontend for the Pinakes server, and are interchangeable in terms of usage. ```bash # Build the UI $ cargo run -p pinakes-ui ``` > [!TIP] > By default Pinakes GUI will assume the server to be running on localhost and > bound to port 3000. Set `PINAKES_SERVER_URL` to point at the server if it is > not on `localhost:3000`. ## API There exists a comprehensive UI for the server component that you may query directly from the `/api/v1` endpoint. All other 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 | 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 | ### 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 | Method | Path | Description | | ------ | -------- | ----------------------------------------------------------------------------- | | `GET` | `/audit` | List audit log (query: `offset`, `limit`) | | `POST` | `/scan` | Trigger directory scan (`{"path": "/..."}` or `{"path": null}` for all roots) | ## 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 Two storage backends are supported. For convenience, SQLite is the default backend out of the box but for production deployments you may choose to prefer PostgreSQL. ### **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.