A future-proof media management suite, designed to be your last.
  • Rust 96.2%
  • SCSS 3.6%
Find a file
NotAShelf 06c1898040
chore: format Cargo manifest with Taplo
Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: If49e784ce4c704538529a41a237d6a7c6a6a6964
2026-01-31 15:20:32 +03:00
crates initial commit 2026-01-31 15:20:30 +03:00
migrations db: add migrations 2026-01-31 15:20:31 +03:00
nix initial commit 2026-01-31 15:20:30 +03:00
.envrc initial commit 2026-01-31 15:20:30 +03:00
Cargo.lock initial commit 2026-01-31 15:20:30 +03:00
Cargo.toml chore: format Cargo manifest with Taplo 2026-01-31 15:20:32 +03:00
flake.lock initial commit 2026-01-31 15:20:30 +03:00
flake.nix initial commit 2026-01-31 15:20:30 +03:00
pinakes.toml.example initial commit 2026-01-31 15:20:30 +03:00
README.md initial commit 2026-01-31 15:20:30 +03:00

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

# 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:

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

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

cargo run -p pinakes-tui
# or with a custom server URL:
cargo run -p pinakes-tui -- --server http://localhost:3000

Keybindings:

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

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
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

# 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.