This repository has been archived on 2026-05-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
bvandeusen 51c13129dd refactor(main): search_tags JOINs fandom; matches on display CONCAT
Post-refactor tag.name is bare. Autocomplete needs to match the
display form (name + ' (' + fandom.name + ')') so users can still
find 'Ruby Rose (RWBY)' by typing 'rwby'. Switched to
contains_eager(Tag.fandom, alias=f) to eliminate the duplicate
join that caused a Postgres GROUP BY error in the no-query path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 15:24:24 -04:00
2026-02-05 21:24:03 -05:00
2025-07-28 21:57:49 +00:00

ImageRepo

A self-hosted image and video gallery application designed for organizing and viewing media collections. Originally built for managing Patreon content archives, it features automatic importing, tagging, duplicate detection, and a responsive dark-themed UI.

Features

  • Gallery View - Browse images in a paginated grid with thumbnail previews
  • Showcase View - Randomized masonry layout for discovery
  • Image Modal - Full-size viewing with zoom, pan, and keyboard navigation
  • Video Support - Playback for video files with automatic transcoding to MP4
  • Tagging System - Organize images with tags (artist, character, series, rating, archive, user-defined)
  • Tag Autocomplete - Quick tag entry with search suggestions
  • ML Tag Suggestions - WD14 tagger + SigLIP embedding centroids propose tags you can accept/reject per image. Videos are sampled at 10 frames (configurable via VIDEO_ML_FRAMES) and aggregated by max-confidence WD14 + mean-pool SigLIP.
  • Bulk Tag Suggestions - Select multiple images in the gallery to see consensus ML suggestions (tags suggested for or already applied to >=80% of the selection)
  • Automatic Importing - Celery-based task queue scans /import directory on schedule
  • Archive Extraction - Supports ZIP, RAR, 7z and other archive formats
  • Duplicate Detection - Perceptual hash (pHash) comparison to skip similar images
  • Import Filters - Skip images by dimensions or transparency percentage
  • Bulk Deletion - Delete images by tag or filter criteria
  • Artist Organization - Auto-tags images based on folder structure
  • Dark Theme - Easy on the eyes for extended browsing

Quick Start

Docker Compose

services:
  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  postgres:
    image: pgvector/pgvector:pg16
    environment:
      POSTGRES_USER: imagerepo
      POSTGRES_PASSWORD: your_secure_password
      POSTGRES_DB: imagerepo
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U imagerepo"]
      interval: 10s
      timeout: 5s
      retries: 5

  web:
    image: git.fabledsword.com/bvandeusen/imagerepo:latest
    ports:
      - "5000:5000"
    environment: &app_env
      - DB_USER=imagerepo
      - DB_PASS=your_secure_password
      - DB_HOST=postgres
      - DB_NAME=imagerepo
      - CELERY_BROKER_URL=redis://redis:6379/0
      - CELERY_RESULT_BACKEND=redis://redis:6379/0
    volumes:
      - ./imagerepo/images:/images
      - ./your-media:/import
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

  # Heavy processing: import, thumbnail, sidecar, default queues.
  worker:
    image: git.fabledsword.com/bvandeusen/imagerepo:latest
    command: celery -A app.celery_app:celery worker --loglevel=info -Q import,thumbnail,sidecar,default --concurrency=2
    environment: *app_env
    volumes:
      - ./imagerepo/images:/images
      - ./your-media:/import
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

  # Beat scheduler + maintenance/scan worker, split off so long imports don't starve periodic tasks.
  scheduler:
    image: git.fabledsword.com/bvandeusen/imagerepo:latest
    command: celery -A app.celery_app:celery worker --beat --loglevel=info -Q maintenance,scan --concurrency=1
    environment: *app_env
    volumes:
      - ./imagerepo/images:/images
      - ./your-media:/import
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

  # CPU-only ML inference: WD14 tags + SigLIP embeddings. Models self-heal into ${MODELS_DIR} on start.
  ml-worker:
    image: git.fabledsword.com/bvandeusen/imagerepo-ml:latest
    command: celery -A app.celery_app:celery worker --loglevel=info -Q ml --concurrency=1
    environment: *app_env
    volumes:
      - ./imagerepo/images:/images:ro
      - ./imagerepo/models:/models
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

volumes:
  redis_data:
  postgres_data:

Then visit http://localhost:5000 in your browser.

Architecture

ImageRepo uses a task queue architecture for background processing:

  • Web - Flask application serving the UI and API
  • Worker - Heavy processing: import, thumbnail, sidecar, default queues
  • Scheduler - Celery Beat + a maintenance/scan worker (kept separate so long imports don't starve periodic tasks)
  • ML Worker - CPU-only WD14 + SigLIP inference on the ml queue (separate image, models self-heal on start)
  • PostgreSQL - Primary database, uses pgvector extension for SigLIP embedding similarity
  • Redis - Message broker for Celery task queue

Volumes

Path Description
/images Where imported images and thumbnails are stored
/import Source directory the importer scans for new media
/models ml-worker only — WD14 + SigLIP weights (~4 GB, fetched on first start)

Environment Variables

Database Configuration (PostgreSQL)

Variable Default Description
DB_NAME imagerepo PostgreSQL database name
DB_USER imagerepo PostgreSQL username
DB_PASS postgres PostgreSQL password
DB_HOST postgres PostgreSQL host
DB_PORT 5432 PostgreSQL port

Celery Configuration

Variable Default Description
CELERY_BROKER_URL redis://redis:6379/0 Redis broker URL
CELERY_RESULT_BACKEND redis://redis:6379/0 Redis result backend URL
CELERY_WORKER_CONCURRENCY 2 Number of worker processes
IMPORT_EVERY_SECONDS 28800 Directory scan interval (8 hours)

Import Settings

These can also be configured via the Settings page in the UI.

Variable Default Description
IMPORT_MIN_WIDTH 0 Minimum image width in pixels (0 = no minimum)
IMPORT_MIN_HEIGHT 0 Minimum image height in pixels (0 = no minimum)
IMPORT_SKIP_TRANSPARENT false Skip mostly-transparent images (PNG/GIF/WebP)
IMPORT_TRANSPARENCY_THRESHOLD 0.9 Transparency % threshold (0.0-1.0) for skipping
IMPORT_PHASH_THRESHOLD 10 Duplicate detection sensitivity (0=disabled, lower=stricter)

Thumbnail & Video Processing

Variable Default Description
THUMBS_VERBOSE 0 Enable verbose logging (1, true, or yes)
THUMBS_LOG_EVERY 50 Log progress every N thumbnails
THUMBS_FFMPEG_TIMEOUT 60 Timeout in seconds for video thumbnail extraction
FFMPEG_TRANSCODE_TIMEOUT 600 Timeout in seconds for video transcoding

Archive Extraction

Variable Default Description
ARCHIVE_TMPDIR System temp Directory for extracting archives
ARCHIVE_MIN_FREE_GB 0 Minimum free disk space (GB) required to start extraction (0 = disabled)
ARCHIVE_NUM_WIDTH 4 Zero-padding width for archive sequence numbers in tags

ML Tag Suggestions (ml-worker only)

Variable Default Description
ML_MODEL_DIR /models Where WD14 + SigLIP weights are written/read
WD14_REPO SmilingWolf/wd-eva02-large-tagger-v3 HuggingFace repo for the WD14 tagger
SIGLIP_REPO google/siglip-so400m-patch14-384 HuggingFace repo for the SigLIP encoder
WD14_REVISION (latest) Pin WD14 to a specific commit SHA
SIGLIP_REVISION (latest) Pin SigLIP to a specific commit SHA
VIDEO_ML_FRAMES 10 Frames sampled per video for WD14+SigLIP inference (between 10% and 90% of duration)
WD14_STORE_FLOOR 0.05 Minimum WD14 confidence stored in the DB

Other

Variable Default Description
PIL_MAX_IMAGE_PIXELS 178956970 Maximum image size Pillow will process (guards against decompression bombs)

Import Behavior

The importer runs:

  • Automatically every 8 hours (configurable via IMPORT_EVERY_SECONDS)
  • Manually via the "Trigger Import Scan" button in Settings

Video Transcoding

Non-MP4 video formats (.mov, .avi, .mkv, .webm, .m4v, .wmv, .flv) are automatically transcoded to H.264 MP4 during import for universal browser playback.

Folder Structure

The importer uses the folder structure to auto-tag images:

/import/
├── ArtistName/           # Tagged as "artist:ArtistName"
│   ├── image1.png
│   ├── image2.jpg
│   └── archive.zip       # Extracted, contents tagged with "archive:archive"
├── AnotherArtist/
│   └── subfolder/
│       └── image3.png    # Still tagged as "artist:AnotherArtist"
  • Top-level folders become artist:FolderName tags
  • Archives (ZIP, RAR, 7z, etc.) are extracted and their contents tagged with archive:filename
  • Nested subfolders inherit the top-level artist tag

Supported Formats

Images: PNG, JPG, JPEG, GIF, WebP, BMP, TIFF Videos: MP4, MOV, AVI, MKV, WebM, M4V, WMV, FLV (transcoded to MP4) Archives: ZIP, RAR, 7z, and others supported by unar/7z

Settings Page

The Settings page (/settings) provides:

Admin Actions

  • Trigger Import Scan - Manually start an import scan
  • Regenerate Thumbnails - Rebuild all thumbnails
  • Find Duplicates - Scan for visually similar images using pHash
  • Reset Image Database - Clear all image records (files remain on disk)

ML Maintenance Tools

  • Run ML backfill - Enqueue tag_and_embed for every image missing predictions or embeddings for the current model versions. Safe to re-run; paginates forward and drops already-processed images. Expected runtime on a fresh DB: hours to days on a single ml-worker.
  • Recompute all centroids - Rebuild per-tag SigLIP centroids from current image tags. Run after the initial backfill drains, or whenever bulk manual tagging has drifted suggestions.
  • Sync character fandoms - Additively re-apply each character's fandom tag to every image already tagged with that character. Never removes existing fandom tags.

Import Filters

Configure filtering rules that apply during import:

  • Minimum dimensions
  • Transparency filtering
  • Duplicate detection sensitivity

Duplicate Management

  • Scan for potential duplicates based on perceptual hash distance
  • Review and confirm duplicates for deletion
  • Keep the higher resolution version automatically

Delete Images by Tag

Bulk delete all images associated with a specific tag (e.g., remove an entire artist's collection).

Clean Up Existing Images

Scan and selectively delete images that match filter criteria (transparency or dimensions).

API Endpoints

Endpoint Method Description
/api/random-images GET Get random images for showcase
/api/tags/search GET Search tags for autocomplete
/api/import-settings GET/POST Read/write import filter settings
/api/import/trigger POST Trigger directory scan
/api/import/status GET Get import queue status
/api/duplicates/scan POST Scan for duplicate images
/api/duplicates/delete POST Delete confirmed duplicates
/api/filter-scan GET Scan images matching filter criteria
/api/filter-delete POST Delete selected images
/api/delete-by-tag POST Delete all images with a tag
/image/<id>/tags GET List tags for an image
/image/<id>/tags/add POST Add a tag to an image
/image/<id>/tags/remove POST Remove a tag from an image

Keyboard Shortcuts

In the image modal:

  • Arrow Left/Right - Previous/Next image
  • Escape - Close modal
  • Click image - Toggle zoom
  • Drag (when zoomed) - Pan around image

Scaling Workers

To increase import throughput:

# Scale worker containers
docker-compose up --scale celery-worker=4

# Or increase concurrency per worker
CELERY_WORKER_CONCURRENCY=4 docker-compose up

Total parallelism = containers × concurrency

Development

Local Setup

# Clone the repository
git clone https://git.fabledsword.com/bvandeusen/imagerepo.git
cd imagerepo

# Start PostgreSQL and Redis
docker-compose up -d postgres redis

# Create virtual environment
python -m venv venv
source venv/bin/activate  # or `venv\Scripts\activate` on Windows

# Install dependencies
pip install -r requirements.txt

# Run migrations
flask db upgrade

# Run with Flask dev server
flask run

Docker Build

docker build -t imagerepo .

License

This is a personal project. Use at your own discretion.

S
Description
No description provided
Readme 660 MiB
Languages
Python 56.8%
JavaScript 17%
HTML 16.3%
CSS 9.3%
OCaml 0.2%
Other 0.2%