# MediaLore A self-hosted web UI for browsing media libraries on a NAS or local filesystem. Configure folders as typed libraries — currently supporting **Games** and **Mixed Media** library types. ## Features - **Games library** — displays a grid of game cover art scanned from folders. Each game folder is expected to contain a `.zip` archive and optional artwork (`cover.*`, `widecover.*`). Clicking a game opens a detail modal with a download link for the zip. - **Mixed media library** — a folder-navigable browser that mirrors the directory structure on disk. Videos open in an inline player (with full seek support via HTTP range requests). Images open in a lightbox. Other files are opened in a new tab. - **Configurable libraries** — libraries are registered in `libraries.json` at the project root; no database required. - **Path-jailed file serving** — all file access is verified to stay within the configured library root before being served. ## Project Structure ``` MediaLoreWeb/ ├── libraries.json # Library configuration ├── data/ # Example media (not committed to production) ├── src/ │ ├── app/ │ │ ├── layout.tsx │ │ ├── page.tsx # Home — library cards │ │ ├── library/[id]/page.tsx # Library view (games or mixed) │ │ └── api/ │ │ ├── libraries/route.ts # GET /api/libraries │ │ ├── games/route.ts # GET /api/games?libraryId= │ │ ├── browse/route.ts # GET /api/browse?libraryId=&path= │ │ └── file/route.ts # GET /api/file?libraryId=&path= │ ├── components/ │ │ ├── LibraryCard.tsx │ │ ├── games/ │ │ │ ├── GamesView.tsx │ │ │ └── GameDetailModal.tsx │ │ └── mixed/ │ │ ├── MixedView.tsx │ │ ├── VideoPlayerModal.tsx │ │ └── ImageLightbox.tsx │ ├── lib/ │ │ ├── libraries.ts # Config parsing and path resolution │ │ ├── games.ts # Games library scanner │ │ └── files.ts # Mixed library directory scanner │ └── types/ │ └── index.ts ``` ## Developer Setup **Requirements:** Node.js 18+ ```bash # 1. Install dependencies npm install # 2. Start the development server npm run dev ``` Open [http://localhost:3000](http://localhost:3000). Other available commands: ```bash npm run build # Production build npm run start # Start production server (run build first) npm run lint # Run ESLint ``` ## Library Configuration Libraries are defined in `libraries.json` at the project root: ```json [ { "id": "games", "name": "Games", "path": "./data/Games", "type": "games" }, { "id": "various", "name": "Various Media", "path": "./data/Various Media", "type": "mixed" } ] ``` | Field | Description | |--------|-------------| | `id` | URL-safe unique identifier used in routes (`/library/`) | | `name` | Display name shown in the UI | | `path` | Absolute or project-relative path to the library root folder | | `type` | `"games"` or `"mixed"` | Paths can point anywhere on the filesystem — they do not need to be inside the project directory. Restart the dev server after editing `libraries.json`. ## Library Folder Conventions ### Games (`"type": "games"`) Each game is a subdirectory containing: ``` Games/ └── My Game Title/ ├── My Game Title.zip # Required — the downloadable archive ├── cover.png # Optional — portrait cover art (case-insensitive) └── widecover.jpg # Optional — landscape/hero cover art (case-insensitive) ``` - The `.zip` filename can be anything; the first `.zip` found in the folder is used. - Cover art filenames are matched case-insensitively against `cover.*` and `widecover.*`. Any image extension is accepted. ### Mixed Media (`"type": "mixed"`) No specific structure is required. The UI mirrors the directory tree exactly as it exists on disk. Supported media types: - **Video:** `.mp4`, `.mov`, `.mkv`, `.avi`, `.webm`, `.m4v` - **Image:** `.jpg`, `.jpeg`, `.png`, `.gif`, `.webp`, `.bmp`, `.tiff` ## API Reference All API routes are server-side. File paths are never exposed in client-side state — only opaque `/api/file?...` URLs are sent to the browser. | Route | Method | Description | |-------|--------|-------------| | `/api/libraries` | GET | Returns the full library list from `libraries.json` | | `/api/games?libraryId=` | GET | Scans the games library and returns structured game entries | | `/api/browse?libraryId=&path=` | GET | Lists the contents of a directory within a mixed library | | `/api/file?libraryId=&path=` | GET | Streams a file; supports HTTP `Range` requests for seekable video playback | ## Tech Stack - [Next.js 15](https://nextjs.org/) (App Router) - [React 19](https://react.dev/) - [TypeScript 5](https://www.typescriptlang.org/) - [Tailwind CSS 4](https://tailwindcss.com/)