2026-05-26 22:51:14 +02:00
2026-05-26 22:46:00 +02:00
2026-05-26 22:46:00 +02:00
2026-05-26 22:46:00 +02:00
2026-05-26 22:46:00 +02:00
2026-05-26 22:46:00 +02:00
2026-05-26 22:46:00 +02:00
2026-05-26 22:46:00 +02:00
2026-05-26 22:46:00 +02:00
2026-05-26 22:46:00 +02:00
2026-05-26 22:46:00 +02:00
2026-05-26 22:46:00 +02:00
2026-05-26 22:46:00 +02:00
2026-05-26 22:46:00 +02:00
2026-05-26 22:46:00 +02:00
2026-05-26 22:46:00 +02:00
2026-05-26 22:51:14 +02:00
2026-05-26 22:46:00 +02:00

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

pnpm install

This builds the native deps (better-sqlite3, sharp).

Set up the database

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

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

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:

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

S
Description
No description provided
Readme 598 KiB
Languages
TypeScript 66.3%
HTML 33.3%
CSS 0.4%