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 efaff8ca1b add applied tags as context to description prompt
When generating an item description, any already-applied tags are
appended to the system prompt as a source of truth, so the model
can produce a more accurate description aligned with existing tags.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 21:12:58 -04:00
2026-04-05 10:13:42 -04:00
2026-03-26 23:41:52 -04:00
2026-04-11 18:33:03 -04:00
2026-04-05 17:44:24 -04:00
2026-04-05 19:59:24 -04:00
2026-04-05 10:01:34 -04:00
2026-03-25 16:18:23 -04:00
2026-03-25 16:18:23 -04:00
2026-03-26 23:41:52 -04:00
2026-03-25 16:18:23 -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 — supporting Games, Movies, TV, and Mixed Media library types.

Features

  • Games library — grid of game cover art scanned from folders. Each game folder contains a .zip archive and optional artwork (cover.*, widecover.*). Clicking a game opens a detail modal with a download link.
  • Movies library — grid of movie posters scanned from per-movie folders. Reads .nfo sidecar files (Kodi-compatible) for metadata (title, year, plot, rating, genres). Clicking a movie opens a full-screen video player.
  • TV library — browse TV series → seasons → episodes. Reads tvshow.nfo and per-episode .nfo files for metadata. Supports standard season folder layouts and flat (seasonless) series.
  • Mixed media library — folder-navigable browser that mirrors the directory structure on disk. Images open in a lightbox; videos open in an inline player with full seek support.
  • Thumbnail generation — lazy on-demand thumbnails for images (via sharp) and video frames (via ffmpeg). Thumbnails are cached to disk in .thumbnails/ and regenerated when the source file changes.
  • Tag system — create tag categories and items, then assign tags to individual media items per-library. Filter any library view by one or more tags.
  • Library management UI — add and remove libraries at /manage without touching config files. Configuration persists across restarts in libraries.json.
  • User authentication — iron-session cookie auth with admin and user roles. Admins manage libraries, scan settings, tags, and users. Users have read-only access, optionally restricted to specific libraries.
  • Background scanner — scans all libraries on demand (or on a schedule) to pre-generate thumbnails and populate metadata caches.
  • 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)
├── src/
│   ├── app/
│   │   ├── layout.tsx
│   │   ├── page.tsx                         # Home — library cards (redirects to /manage if empty)
│   │   ├── manage/page.tsx                  # Library management
│   │   ├── manage/tags/page.tsx             # Tag management
│   │   ├── manage/users/page.tsx            # User management (admin only)
│   │   ├── manage/scan/page.tsx             # Scan settings and manual trigger
│   │   ├── library/[id]/page.tsx            # Library view (games / movies / tv / mixed)
│   │   └── api/
│   │       ├── auth/login/route.ts          # POST /api/auth/login
│   │       ├── auth/logout/route.ts         # POST /api/auth/logout
│   │       ├── auth/register/route.ts       # POST /api/auth/register
│   │       ├── libraries/route.ts           # GET, POST /api/libraries
│   │       ├── libraries/[id]/route.ts      # PATCH, DELETE /api/libraries/:id
│   │       ├── games/route.ts               # GET /api/games
│   │       ├── movies/route.ts              # GET /api/movies
│   │       ├── tv/route.ts                  # GET /api/tv (series, seasons, episodes)
│   │       ├── browse/route.ts              # GET /api/browse (mixed media)
│   │       ├── file/route.ts                # GET /api/file
│   │       ├── thumbnail/route.ts           # GET /api/thumbnail
│   │       ├── game-cover/route.ts          # POST /api/game-cover (upload cover art)
│   │       ├── library-cover/[id]/route.ts  # GET /api/library-cover/:id
│   │       ├── scan/route.ts                # POST /api/scan
│   │       ├── scan-settings/route.ts       # GET, POST /api/scan-settings
│   │       ├── settings/route.ts            # GET, POST /api/settings
│   │       ├── tags/categories/route.ts     # GET, POST /api/tags/categories
│   │       ├── tags/categories/[id]/route.ts# PATCH, DELETE /api/tags/categories/:id
│   │       ├── tags/items/route.ts          # GET, POST /api/tags/items
│   │       ├── tags/items/[id]/route.ts     # PATCH, DELETE /api/tags/items/:id
│   │       ├── tags/assignments/route.ts    # GET, POST, DELETE /api/tags/assignments
│   │       ├── tags/library-assignments/route.ts # GET /api/tags/library-assignments
│   │       ├── users/route.ts               # GET, POST /api/users
│   │       ├── users/[id]/route.ts          # PATCH, DELETE /api/users/:id
│   │       └── users/[id]/permissions/route.ts   # GET, POST /api/users/:id/permissions
│   ├── components/
│   │   ├── FilterPanel.tsx
│   │   ├── LibraryCard.tsx
│   │   ├── NavLink.tsx
│   │   ├── DoomScrollView.tsx
│   │   ├── games/
│   │   │   ├── GamesView.tsx
│   │   │   └── GameDetailModal.tsx
│   │   ├── movies/
│   │   │   ├── MoviesView.tsx
│   │   │   └── MovieDetailModal.tsx
│   │   ├── tv/
│   │   │   └── TvView.tsx
│   │   ├── mixed/
│   │   │   ├── MixedView.tsx
│   │   │   ├── VideoPlayerModal.tsx
│   │   │   └── ImageLightbox.tsx
│   │   └── tags/
│   │       └── TagSelector.tsx
│   ├── lib/
│   │   ├── auth.ts          # iron-session auth, requireAdmin / requireLibraryAccess helpers
│   │   ├── db.ts            # SQLite setup and migrations (better-sqlite3)
│   │   ├── libraries.ts     # Config read/write, path resolution, path-jailing
│   │   ├── media-utils.ts   # Shared constants (HIDDEN_FILES, VIDEO_EXTENSIONS) and helpers
│   │   ├── games.ts         # Games library scanner
│   │   ├── movies.ts        # Movies library scanner
│   │   ├── tv.ts            # TV library scanner (series / seasons / episodes)
│   │   ├── files.ts         # Mixed media directory scanner
│   │   ├── nfo.ts           # Kodi-compatible .nfo XML parser
│   │   ├── scanner.ts       # Full-library background scan orchestrator
│   │   ├── thumbnails.ts    # Thumbnail cache + generation (sharp / ffmpeg)
│   │   ├── tags.ts          # Tag CRUD and assignment helpers
│   │   ├── users.ts         # User CRUD and permission helpers
│   │   └── app-settings.ts  # App-level settings (scan schedule, etc.)
│   ├── hooks/
│   │   └── useUserSettings.ts
│   └── 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. Copy the example env file and fill in SESSION_SECRET
cp .env.example .env.local

# 3. Start the development server
npm run dev

Open http://localhost:3000. On first run you will be prompted to create an admin account.

Other available commands:

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

Environment Variables

Variable Required Description
SESSION_SECRET Yes Secret used to sign session cookies. Must be at least 32 characters.
COOKIE_SECURE No Set to true in production (HTTPS). Defaults to false.

Authentication

MediaLore uses cookie-based sessions (iron-session). On first launch, navigate to /register to create the initial admin account. Subsequent registrations require an existing admin to create accounts via /manage/users.

Roles:

Role Capabilities
admin Full access: manage libraries, users, tags, scan settings
user Read-only access to assigned libraries

Library-level permissions can be configured per user at /manage/users.

Library Configuration

Libraries are managed through the Manage Libraries screen at /manage in the app.

Field Description
Name Display name shown in the UI
Path Absolute or project-relative path to the library root folder on disk
Type games, movies, tv, or mixed

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

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)

Subdirectories without a .zip are treated as series containers — their child directories are scanned as individual games.

Movies ("type": "movies")

Each movie is a subdirectory containing a single video file and optional sidecar files:

Movies/
└── The Matrix (1999)/
    ├── The Matrix (1999).mkv   # Required — any supported video extension
    ├── movie.nfo               # Optional — Kodi-compatible metadata
    ├── poster.jpg              # Optional — portrait poster art
    └── backdrop.jpg            # Optional — backdrop/fanart image

Supported video extensions: .mkv, .mp4, .avi, .mov, .m4v, .wmv, .ts, .m2ts

Poster filenames are matched case-insensitively against poster, cover, or folder. Backdrop filenames are matched against backdrop, fanart, or background.

TV ("type": "tv")

TV/
└── Breaking Bad/
    ├── tvshow.nfo              # Optional — series metadata
    ├── poster.jpg              # Optional — series poster
    ├── Season 01/
    │   ├── s01e01.mkv
    │   ├── s01e01.nfo          # Optional — episode metadata
    │   └── ...
    └── Season 02/
        └── ...

Season directory names are matched against patterns: Season 01, S01, 1, 01. If no season subdirectories contain video files, the series root itself is treated as a flat (seasonless) season.

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.

Auth

Route Method Description
/api/auth/login POST Authenticate. Body: { username, password }
/api/auth/logout POST Clear session cookie
/api/auth/register POST Create account. Body: { username, password }. First user becomes admin.

Libraries

Route Method Description
/api/libraries GET Returns the full configured library list
/api/libraries POST Adds a library. Body: { name, path, type }
/api/libraries/:id PATCH Updates a library
/api/libraries/:id DELETE Removes a library by id

Media

Route Method Description
/api/games?libraryId= GET Scans the games library and returns structured game entries
/api/movies?libraryId= GET Scans the movies library and returns movie entries
/api/tv?libraryId= GET Returns TV series list
/api/tv?libraryId=&seriesId= GET Returns seasons for a series
/api/tv?libraryId=&seriesId=&seasonId= GET Returns episodes for a season
/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
/api/thumbnail?libraryId=&path= GET Returns a cached square thumbnail (JPEG); 404 if generation fails

Tags

Route Method Description
/api/tags/categories GET, POST List or create tag categories
/api/tags/categories/:id PATCH, DELETE Update or delete a category
/api/tags/items GET, POST List or create tag items
/api/tags/items/:id PATCH, DELETE Update or delete a tag item
/api/tags/assignments GET, POST, DELETE Get, add, or remove tag assignments on a media item
/api/tags/library-assignments?libraryId= GET All tag assignments for a library (used by filter panel)

Users & Settings

Route Method Description
/api/users GET, POST List or create users (admin only)
/api/users/:id PATCH, DELETE Update or delete a user
/api/users/:id/permissions GET, POST Get or set library-level permissions for a user
/api/settings GET, POST App-level settings
/api/scan POST Trigger a full library scan
/api/scan-settings GET, POST Get or update scan schedule settings

Tech Stack

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