This repository has been archived on 2026-06-15. You can view files and clone it, but cannot push or open issues or pull requests.
Garret Patti 75fe82f0de group selected tags by category in TagSelector
Assigned tags now render as a single outer pill per category containing
smaller inner tag pills, instead of one pill per tag with a repeated
category prefix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 18:41:37 -04:00
2026-03-25 17:57:47 -04:00
2026-03-25 16:18:23 -04:00
2026-03-25 16:40:01 -04:00
2026-03-25 16:18:23 -04:00
2026-03-25 17:57:47 -04:00
2026-03-25 16:18:23 -04:00
2026-03-25 16:59:09 -04:00
2026-03-25 16:18:23 -04:00

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. Image and video files display auto-generated square thumbnails. 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.
  • Thumbnail generation — lazy on-demand thumbnails for images (via sharp) and video frames (via ffmpeg). Thumbnails are cached to disk in .thumbnails/ and regenerated automatically when the source file changes.
  • Library management UI — add and remove libraries at /manage without touching any config files. Configuration persists across restarts in libraries.json.
  • Path-jailed file serving — all file access is verified to stay within the configured library root before being served.

Project Structure

MediaLoreWeb/
├── libraries.json          # Runtime library config (managed via UI, do not edit by hand)
├── .thumbnails/            # Disk cache for generated thumbnails (auto-created, gitignored)
├── data/                   # Example media (not committed to production)
├── src/
│   ├── app/
│   │   ├── layout.tsx
│   │   ├── page.tsx                    # Home — library cards (redirects to /manage if empty)
│   │   ├── manage/page.tsx             # Library management — add/remove libraries
│   │   ├── library/[id]/page.tsx       # Library view (games or mixed)
│   │   └── api/
│   │       ├── libraries/route.ts      # GET /api/libraries, POST /api/libraries
│   │       ├── libraries/[id]/route.ts # DELETE /api/libraries/:id
│   │       ├── games/route.ts          # GET /api/games?libraryId=
│   │       ├── browse/route.ts         # GET /api/browse?libraryId=&path=
│   │       ├── file/route.ts           # GET /api/file?libraryId=&path=
│   │       └── thumbnail/route.ts      # GET /api/thumbnail?libraryId=&path=
│   ├── components/
│   │   ├── LibraryCard.tsx
│   │   ├── NavLink.tsx
│   │   ├── games/
│   │   │   ├── GamesView.tsx
│   │   │   └── GameDetailModal.tsx
│   │   └── mixed/
│   │       ├── MixedView.tsx
│   │       ├── VideoPlayerModal.tsx
│   │       └── ImageLightbox.tsx
│   ├── lib/
│   │   ├── libraries.ts    # Config read/write, path resolution, add/remove helpers
│   │   ├── games.ts        # Games library scanner
│   │   ├── files.ts        # Mixed library directory scanner
│   │   └── thumbnails.ts   # Thumbnail cache + generation (sharp / ffmpeg)
│   └── types/
│       └── index.ts

Developer Setup

Requirements: Node.js 18+, ffmpeg and ffprobe (for video thumbnails)

Video thumbnails require ffmpeg and ffprobe to be installed and available on $PATH. If they are missing, video tiles gracefully fall back to a generic icon — no errors are thrown.

macOS: brew install ffmpeg
Ubuntu/Debian: sudo apt install ffmpeg
Windows: Download from ffmpeg.org and add to PATH

# 1. Install dependencies
npm install

# 2. Start the development server
npm run dev

Open http://localhost:3000.

Other available commands:

npm run build   # Production build
npm run start   # Start production server (run build first)
npm run lint    # Run ESLint

Library Configuration

Libraries are managed through the Manage Libraries screen at /manage in the app. No manual file editing is required.

When you add a library via the UI, you provide:

Field Description
Name Display name shown in the UI
Path Absolute or project-relative path to the library root folder on disk
Type Games or Mixed Media

The app validates that the path exists as a directory before saving. Configuration is stored in libraries.json at the project root and persists across restarts.

If no libraries are configured, navigating to / automatically redirects to /manage.

Paths can point anywhere on the filesystem — they do not need to be inside the project directory.

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 configured library list
/api/libraries POST Adds a library. Body: { name, path, type }. Validates the path exists.
/api/libraries/:id DELETE Removes a library by id
/api/games?libraryId=<id> GET Scans the games library and returns structured game entries
/api/browse?libraryId=<id>&path=<subpath> GET Lists the contents of a directory within a mixed library
/api/file?libraryId=<id>&path=<relpath> GET Streams a file; supports HTTP Range requests for seekable video playback
/api/thumbnail?libraryId=<id>&path=<relpath> GET Returns a cached square thumbnail (JPEG) for an image or video file; 404 if generation fails or ffmpeg is unavailable

Tech Stack

Description
A web app for accessing media
Readme 2.5 MiB
Languages
TypeScript 99.7%
Dockerfile 0.2%