Skip to content

Self-hosted photo and video management with AI-powered search, grouping, auto-tagging, single container, high-performance, low resource use.

License

Notifications You must be signed in to change notification settings

srad/GalleryNet

Repository files navigation

GalleryNet Logo

GalleryNet

Self-hosted photo and video gallery with AI-powered search, grouping, and tagging

Build Status Tests Docker Size Rust React License

Upload photos and videos, organize them into folders and use integrated AI tools for search and sorting — all running on your own hardware. No cloud, no API keys, fully private. High performance, low resource usage, and only a single container without any other dependencies.


FireShot Capture 004 - GalleryNet - localhost FireShot Capture 008 - GalleryNet - localhost

Features

  • Visual Search — Find similar photos and videos by uploading a reference image, with adjustable similarity threshold and one-click grouping

  • Auto Tagging — Tag a few items in the library and let the AI automatically label matching items across your library

  • Duplicate Detection — Duplicates are detected during upload and silently skipped

  • Virtual Folders — Organize media into folders without moving files; one item can live in multiple folders with drag-and-drop support

  • Favorites — Mark items as favorites for quick access in a dedicated view

  • Multi-Select & Batch Operations — Marquee selection, shift-click, batch download (auto-split zip), batch delete, and batch add-to-folder

  • Video Support — Upload any common video format with automatic frame extraction for thumbnails and AI features

  • EXIF Metadata — View camera details, date, GPS, exposure, and more

  • Deep Linking — Bookmarkable URLs for folders, favorites, search states, and individual items

  • Password Protection — Optional single-password auth with rate limiting and secure sessions. No complex user management, only a simple password.

  • Responsive UI — Infinite-scroll grid, keyboard shortcuts, touch swipe, and full mobile support. Includes a persistent thumbnail resizer (S/M/L) to customize your viewing experience.

  • Drag-and-Drop Upload — Drag files anywhere into the browser window to upload. Context-aware: dropping into a virtual folder automatically adds the files to that folder.

  • Real-time Sync — WebSocket-powered instant updates across all browser clients; all users can see new uploads, favorite toggles, and folder changes immediately as they happen

  • Self-Healing — Automatically detects and repairs missing thumbnails or metadata in the background

  • 100% Self-Hosted — No cloud, no telemetry. Your data stays yours.

How It Works

Feature Technology
Visual Search MobileNetV3-Large extracts 1280-dim feature vectors; cosine similarity via sqlite-vec
Similarity Grouping Agglomerative clustering over the same embedding space with a user-adjustable distance threshold
Auto-Tagging Linear SVM with Platt-calibrated probabilities trained on user-provided examples via linfa-svm
Duplicate Detection Perceptual hashing (image_hasher) compared at upload time
Video Processing ffmpeg thumbnail filter selects visually distinct frames for thumbnails, hashing, and embeddings
AI Inference ort (ONNX Runtime) for fast CPU-based model execution
Batch Downloads Real-time ZIP streaming via async_zip with automatic partitioning into ~2 GB parts
Authentication Argon2-hashed password, rate-limited login, secure HTTP-only cookies

Quick Start with Docker

The easiest way to run GalleryNet is with Docker:

docker run -d \
  --name gallerynet \
  -p 3000:3000 \
  -v gallerynet-data:/app/data \
  -e DATABASE_PATH=/app/data/gallery.db \
  -e UPLOAD_DIR=/app/data/uploads \
  -e THUMBNAIL_DIR=/app/data/thumbnails \
  -e GALLERY_PASSWORD=your-secret-password \
  sedrad/gallerynet

Docker Compose

services:
  gallerynet:
    image: sedrad/gallerynet
    container_name: gallerynet
    ports:
      - "3000:3000"
    environment:
      - DATABASE_PATH=/app/data/gallery.db
      - UPLOAD_DIR=/app/data/uploads
      - THUMBNAIL_DIR=/app/data/thumbnails
      - GALLERY_PASSWORD=your-secret-password
    volumes:
      - ./data:/app/data
    restart: unless-stopped
docker compose up -d

Environment Variables

Variable Default Description
DATABASE_PATH gallery.db Path to the SQLite database file
UPLOAD_DIR uploads Directory for original uploaded files
THUMBNAIL_DIR thumbnails Directory for generated thumbnails
MODEL_PATH assets/models/mobilenetv3.onnx Path to the ONNX model file
GALLERY_PASSWORD (empty) Set to enable password authentication. Leave empty for no auth
CORS_ORIGIN (empty) Set to allow cross-origin requests from a specific origin (e.g. https://example.com). Unset = same-origin only

Build from Source

Prerequisites

  • Rust — Latest stable toolchain
  • Node.js — v18+
  • ffmpeg — Must be on PATH for video support

Build & Run

# Clone the repository
git clone https://github.com/srad/GalleryNet.git
cd GalleryNet

# Build the frontend
cd frontend && npm install && npm run build && cd ..

# Run the server
cargo run --release

The server starts on http://localhost:3000.

Development

# Backend (auto-reload with cargo-watch)
cargo watch -x run

# Frontend (Vite dev server with HMR, proxies /api to :3000)
cd frontend && npm run dev

Architecture

GalleryNet follows Hexagonal Architecture with clean separation of concerns:

src/
├── domain/          # Models & trait ports (zero dependencies)
├── application/     # Use cases (upload, search, list, delete) and background tasks

├── infrastructure/  # SQLite, ONNX Runtime, perceptual hashing
├── presentation/    # Axum HTTP handlers & auth middleware
└── main.rs          # Wiring & server startup

Tech Stack

Layer Technology
Backend Rust, Axum, Tokio, WebSockets

| Database | SQLite + sqlite-vec | | AI/ML | ort (ONNX Runtime), MobileNetV3-Large, linfa-svm (tag learning) | | Frontend | React 19, TypeScript, Tailwind CSS v4, Vite | | Video | ffmpeg (frame extraction) | | Hashing | image_hasher (perceptual hashing) |

API Endpoints

Method Endpoint Description
POST /api/upload Upload media (multipart). Returns MediaItem. 409 for duplicates
POST /api/search Visual similarity search. Multipart with file + similarity
GET /api/media Paginated media list. Params: page, limit, media_type, sort
GET /api/media/{id} Get single media item with EXIF data
POST /api/media/{id}/favorite Toggle favorite status. Body: {"favorite": true/false}
DELETE /api/media/{id} Delete single media item
POST /api/media/batch-delete Batch delete. Body: ["uuid1", ...]
POST /api/media/fix-thumbnails Trigger background repair of missing thumbnails/metadata
POST /api/media/download/plan Create download plan (partitions large sets into <2GB parts). Body: ["uuid1", ...]
GET /api/media/download/stream/{id} Stream a specific download part incrementally
POST /api/media/download Simple batch download (if under 2GB). Body: ["uuid1", ...]
GET /api/tags List all unique tags
GET /api/tags/count Count auto-tags in current view
POST /api/tags/learn Train model from manual tags. Body: {"tag_name": "..."}
POST /api/tags/auto-tag Apply all trained models to current scope
GET /api/folders List all folders with item counts
POST /api/folders Create folder. Body: {"name": "..."}
DELETE /api/folders/{id} Delete folder (keeps media files)
GET /api/folders/{id}/media Paginated media in folder
POST /api/folders/{id}/media Add media to folder. Body: ["uuid1", ...]
POST /api/folders/{id}/media/remove Remove media from folder
GET /api/folders/{id}/download Get download plan for folder (auto-splits for large folders)
GET /api/stats Server statistics (counts, storage, disk space)
POST /api/login Authenticate. Body: {"password": "..."}
POST /api/logout Clear session
GET /api/ws WebSocket for real-time library synchronization
GET /api/auth-check Check authentication status

Contributing

Contributions are welcome! Feel free to open an issue or submit a pull request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/my-feature)
  3. Commit your changes (git commit -m 'Add my feature')
  4. Push to the branch (git push origin feature/my-feature)
  5. Open a Pull Request

License

PolyForm Noncommercial License 1.0.0 means it is free for non-commecial purposes.

About

Self-hosted photo and video management with AI-powered search, grouping, auto-tagging, single container, high-performance, low resource use.

Resources

License

Stars

Watchers

Forks