Files
pinkudex/README.md
T
2026-05-26 22:51:14 +02:00

132 lines
5.1 KiB
Markdown

# Pinkudex
A personal, self-hosted JAV cover-art library and video player. Pinkudex indexes a local folder of cover images, links them to their video files, and gives you a fast, keyboard-friendly UI to browse, tag, rate, and play.
It runs entirely on your own machine — no accounts, no cloud, no telemetry. The web UI is for your eyes; the API is locked to `localhost` (with an opt-in for trusted private/Tailscale networks).
## Features
- **Cover grid** — virtualized masonry/grid of cover thumbnails (`react-virtuoso`), with portrait and landscape views.
- **Metadata** — code, title, studio, series, actresses, labels, tags, genres, ratings, and watch state, all stored in a local SQLite database.
- **Inline video playback** — click the play button on any card with a linked video; native `<video>` for MP4, `hls.js` for segmented playback (`/api/video-hls`).
- **Subtitles** — picks up sidecar `.srt`/`.vtt`/`.ass`/`.ssa` files, or transcodes embedded streams via ffmpeg on demand. Optional WhisperJAV integration for AI transcription.
- **Search & filters** — by code, actress, studio, series, label, tag, genre, rating, watched/unwatched, VIP, favorite, owned.
- **Collections & categories** — group covers with custom covers of their own.
- **Bulk actions** — multi-select with a context menu (move, tag, rate, delete).
- **Backup / restore** — export and import the library as a zipped bundle.
- **Native file/folder pickers** — calls out to the OS dialog (PowerShell on Windows, `osascript` on macOS, `zenity` on Linux) so you can point Pinkudex at files outside the indexed roots.
## Stack
- **Next.js 16** (App Router, webpack dev mode)
- **React 19**
- **TypeScript**
- **Tailwind CSS v4**
- **SQLite** via `better-sqlite3` + **Drizzle ORM**
- **sharp** for thumbnail generation
- **hls.js** for adaptive video playback
- **lucide-react** for icons
## Project layout
```
app/ Next.js App Router (pages + server actions + /api routes)
api/ Local-only HTTP endpoints (video, thumbs, jobs, backup, ...)
actions/ Server actions for mutations
components/ Client + server components, grouped by domain
lib/
api/ Shared API helpers (localOnly gate, asset serving)
db/ Drizzle schema, client, queries
video/ Video probing, subtitle access, HLS helpers
whisperjav/ WhisperJAV job runner and adapters
ingest/ Library scanning and indexing
data/ Runtime state (SQLite db, thumb cache, job state, portraits)
library/ Your cover image library (configurable)
drizzle.config.ts
```
## Getting started
### Prerequisites
- **Node.js 20+**
- **pnpm**
- **ffmpeg** in `PATH` (required for HLS streaming and embedded subtitle extraction)
- A folder of JAV cover images to index
### Install
```bash
pnpm install
```
This builds the native deps (`better-sqlite3`, `sharp`).
### Set up the database
```bash
pnpm db:generate # generate migrations from schema (when schema changes)
```
The SQLite file lives at `data/library.db` and is created on first run.
### Run the dev server
```bash
pnpm dev
```
Open <http://localhost:3001>.
> Pinkudex's dev script uses webpack rather than Turbopack. Long Turbopack sessions trigger an `AsyncHook` leak that crashes the server, so `pnpm dev` is pinned to `next dev --webpack`.
### Production
```bash
pnpm build
pnpm start
```
## Configuration
Environment variables — set in `.env.local` (not committed):
| Variable | Default | Purpose |
|---|---|---|
| `PINKUDEX_TRUSTED_LAN` | unset | Set to `1` to allow API access from RFC1918 + CGNAT/Tailscale (`100.64/10`) IPs in addition to localhost. |
| `PINKUDEX_TRUSTED_HOSTNAMES` | unset | Comma-separated list of trusted bare hostnames (e.g. `pinkudex.tailnet.ts.net`). Only honored when `PINKUDEX_TRUSTED_LAN=1`. |
The library root is `./library/`; thumbnail cache lives in `./data/thumbs/`.
### Accessing Pinkudex from another device
Pinkudex's filesystem-touching API endpoints (`/api/video-*`, `/api/whisperjav-*`, etc.) are restricted to localhost by default. To browse from a phone or another machine over Tailscale or a private LAN:
```env
PINKUDEX_TRUSTED_LAN=1
```
Restart the dev server. Requests from `10.x.x.x`, `172.16-31.x.x`, `192.168.x.x`, `100.64-127.x.x`, and IPv6 ULA/link-local addresses will be accepted.
**Do not expose port 3001 to the public internet.** There is no authentication — the local-IP gate is the only access control.
## Scripts
| Command | What it does |
|---|---|
| `pnpm dev` | Webpack dev server on port 3001 |
| `pnpm dev:turbo` | Turbopack dev server (use sparingly — known leak on long runs) |
| `pnpm build` | Production build |
| `pnpm start` | Production server on port 3001 |
| `pnpm lint` | ESLint |
| `pnpm db:generate` | Generate Drizzle migrations from `lib/db/schema.ts` |
| `pnpm db:studio` | Open Drizzle Studio against `data/library.db` |
## Optional: WhisperJAV integration
Pinkudex can drive a separate [WhisperJAV](https://github.com/) CLI to transcribe videos that lack subtitles. Jobs are tracked in `data/whisperjav-jobs/`. The integration is opt-in and surfaces in the per-cover detail view.
## License
Private. Not distributed.