initial commit
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I4a6b498153eccd5407510dd541b7f4816a6a6964
This commit is contained in:
commit
6a73d11c4b
124 changed files with 34856 additions and 0 deletions
193
README.md
Normal file
193
README.md
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
# 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue