SkillAgentSearch skills...

Chronoframe

Self-hosted personal gallery application with online photo management and albums, supporting Live/Motion Photos, EXIF parsing, geolocation recognition, and an explore map.

Install / Use

/learn @HoshinoSuzumi/Chronoframe
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

ChronoFrame

<p align="center"> <img src="https://socialify.git.ci/HoshinoSuzumi/chronoframe/image?custom_description=Self-hosted+personal+gallery+application.&description=1&font=KoHo&forks=0&issues=0&logo=https%3A%2F%2Fgithub.com%2FHoshinoSuzumi%2Fchronoframe%2Fraw%2Frefs%2Fheads%2Fmain%2Fpublic%2Ffavicon.svg&name=1&owner=1&pattern=Plus&pulls=0&stargazers=0&theme=Auto" alt="Chronoframe"> </p> <p align="center"> <a href="https://github.com/HoshinoSuzumi/chronoframe/releases/latest"> <img src="https://badgen.net/github/release/HoshinoSuzumi/chronoframe/stable?icon=docker&label=stable" alt="Latest Release"> </a> <a href="https://github.com/HoshinoSuzumi/chronoframe/releases?q=beta&expanded=false"> <img src="https://badgen.net/github/release/HoshinoSuzumi/chronoframe?icon=docker&label=nightly" alt="Latest Nightly Release"> </a> <img src="https://img.shields.io/badge/License-MIT-green.svg" alt="License"> </p> <p align="center"> <a href="https://discord.gg/https://discord.gg/U4vQuydP2m"> <img src="https://dcbadge.limes.pink/api/server/https://discord.gg/U4vQuydP2m" alt="Discord Server" /> </a> </p> <p align="center"> <a href="https://hellogithub.com/repository/HoshinoSuzumi/chronoframe" target="_blank"><img src="https://api.hellogithub.com/v1/widgets/recommend.svg?rid=947d47ffe8404985908b266e187dec99&claim_uid=kLVoiAFPJaBtr1D&theme=neutral" alt="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a> <a href="https://www.producthunt.com/products/chronoframe?embed=true&utm_source=badge-featured&utm_medium=badge&utm_source=badge-chronoframe" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1029556&theme=neutral&t=1761159404569" alt="ChronoFrame - Self&#0045;hosted&#0032;photo&#0032;gallery&#0032;for&#0032;photographers&#0046; | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a> </p>

Languages: English | 中文

A smooth photo display and management application, supporting multiple image formats and large-size image rendering.

Live Demo: TimoYin's Mems

✨ Features

🖼️ Powerful Photo Management

  • Manage photos online - Easily manage and browse photos via the web interface
  • Explore map - Browse photo locations on a map
  • Smart EXIF parsing - Automatically extracts metadata such as capture time, geolocation, and camera parameters
  • Reverse geocoding - Automatically identifies photo shooting locations
  • Multi-format support - Supports mainstream formats including JPEG, PNG, HEIC/HEIF
  • Smart thumbnails - Efficient thumbnail generation using ThumbHash

🔧 Modern Tech Stack

  • Nuxt 4 - Built on the latest Nuxt framework with SSR/SSG support
  • TypeScript - Full type safety
  • TailwindCSS - Modern CSS framework
  • Drizzle ORM - Type-safe database ORM

☁️ Flexible Storage Solutions

  • Multiple storage backends - Supports S3-compatible storage, local filesystem
  • CDN acceleration - Configurable CDN URL for faster photo delivery

🐳 Deployment

We recommend deploying with the prebuilt Docker image. View the image on ghcr

Create a .env file and configure environment variables.

Below is a minimal configuration example. For complete configuration options, see Configuration Guide:

# Admin email (required)
CFRAME_ADMIN_EMAIL=
# Admin username (optional, default Chronoframe)
CFRAME_ADMIN_NAME=
# Admin password (optional, default CF1234@!)
CFRAME_ADMIN_PASSWORD=

# Site metadata (all optional)
NUXT_PUBLIC_APP_TITLE=
NUXT_PUBLIC_APP_SLOGAN=
NUXT_PUBLIC_APP_AUTHOR=
NUXT_PUBLIC_APP_AVATAR_URL=

# Map provider (maplibre/mapbox)
NUXT_PUBLIC_MAP_PROVIDER=maplibre
# MapTiler access token for MapLibre
NUXT_PUBLIC_MAP_MAPLIBRE_TOKEN=
# Mapbox access token for Mapbox
NUXT_PUBLIC_MAPBOX_ACCESS_TOKEN=

# Mapbox unrestricted token (optional, reverse geocoding)
NUXT_MAPBOX_ACCESS_TOKEN=

# Storage provider (local, s3 or openlist)
NUXT_STORAGE_PROVIDER=local
NUXT_PROVIDER_LOCAL_PATH=/app/data/storage

# Session password (32‑char random string, required)
NUXT_SESSION_PASSWORD=

Pull Image

Use the published image on GitHub Container Registry and Docker Hub. Choose the source that works best for your network:

GitHub Container Registry (GHCR)

docker pull ghcr.io/hoshinosuzumi/chronoframe:latest

Docker Hub

docker pull hoshinosuzumi/chronoframe:latest

Docker

Run with customized environment variables:

docker run -d --name chronoframe -p 3000:3000 -v $(pwd)/data:/app/data --env-file .env ghcr.io/hoshinosuzumi/chronoframe:latest

Docker Compose

Create docker-compose.yml:

services:
  chronoframe:
    image: ghcr.io/hoshinosuzumi/chronoframe:latest
    container_name: chronoframe
    restart: unless-stopped
    ports:
      - '3000:3000'
    volumes:
      - ./data:/app/data
    env_file:
      - .env

Start:

docker compose up -d

📖 User Guide

If CFRAME_ADMIN_EMAIL and CFRAME_ADMIN_PASSWORD are not set, the default admin account is:

  • Email: admin@chronoframe.com
  • Password: CF1234@!

Logging into the Dashboard

  1. Click avatar to sign in with GitHub OAuth or use email/password login

Uploading Photos

  1. Go to the dashboard at /dashboard
  2. On the Photos page, select and upload images (supports batch & drag-and-drop)
  3. System will automatically parse EXIF data, generate thumbnails, and perform reverse geocoding

📸 Screenshots

Gallery Photo Detail Map Explore Dashboard

🛠️ Development

Requirements

  • Node.js 18+
  • pnpm 9.0+

Install dependencies

# With pnpm (recommended)
pnpm install

# Or with other package managers
npm install
yarn install

Configure environment variables

cp .env.example .env

Initialize database

# 2. Generate migration files (optional)
pnpm db:generate

# 3. Run database migrations
pnpm db:migrate

Start development server

pnpm dev

App will start at http://localhost:3000.

Project Structure

chronoframe/
├── app/                    # Nuxt app
│   ├── components/         # Components
│   ├── pages/              # Page routes
│   ├── composables/        # Composables
│   └── stores/             # Pinia stores
├── packages/
│   └── webgl-image/        # WebGL image viewer
├── server/
│   ├── api/                # API routes
│   ├── database/           # DB schema & migrations
│   └── services/           # Business logic services
└── shared/                 # Shared types & utils

Build commands

# Development (with dependencies build)
pnpm dev

# Build only dependencies
pnpm build:deps

# Production build
pnpm build

# Database operations
pnpm db:generate    # Generate migration files
pnpm db:migrate     # Run migrations

# Preview production build
pnpm preview

🤝 Contributing

Contributions are welcome! Please:

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

Coding Guidelines

  • Use TypeScript for type safety
  • Follow ESLint and Prettier conventions
  • Update documentation accordingly

📄 License

This project is licensed under the MIT License.

👤 Author

Timothy Yin

  • Email: master@uniiem.com
  • GitHub: @HoshinoSuzumi
  • Website: bh8.ga
  • Gallery: lens.bh8.ga

❓ FAQ

<details> <summary>How is the admin user created?</summary> <p> On first startup, an admin user is created based on <code>CFRAME_ADMIN_EMAIL</code>, <code>CFRAME_ADMIN_NAME</code>, and <code>CFRAME_ADMIN_PASSWORD</code>. The email must match your GitHub account email used for login. </p> </details> <details> <summary>Which image formats are supported?</summary> <p> Supported formats: JPEG, PNG, HEIC/HEIF, MOV (for Live Photos). </p> </details> <details> <summary>Why can’t I use GitHub/Local storage?</summary> <p> Currently only S3-compatible storage is supported. GitHub and local storage support is planned. </p> </details> <details> <summary>Why is a map service required and how to configure it?</summary> <p> The map is used to browse photo locations and render mini-maps in photo details. Currently Mapbox is used. After registering, <a href="https://console.mapbox.com/account/access-tokens/">get an access token</a> and set it to the <code>MAPBOX_TOKEN</code> variable. </p> </details> <details> <summary>Why wasn’t my MOV file recognized as a Live Photo?</summary> <p> Ensure the image (.heic) and video (.mov) share the same filename (e.g., <code>IMG_1234.heic</code> and <code>IMG_1234.mov</code>). Upload order does not matter. If not recognized, you can trigger pairing manually from the dashboard. </p> </details> <details> <summary>How do I import existing photos from storage?</summary> <p> Direct import of existing photos is not yet supported. A directory scanning import feature is planned. </p> </details> <details> <summary>How is this different from Afilmory?</summary> <p> Afilmory generates a manifest from photos during local/CI processing and serves them statically. ChronoFrame is a dynamic photo management app, offering online upload, management, and browsing—better for frequently updated galleries. In other words, Afilmory = static; ChronoFrame = dynamic, online upload/manage. </p> </details>

🙏 Acknowledgem

View on GitHub
GitHub Stars1.7k
CategoryCustomer
Updated7h ago
Forks116

Languages

Vue

Security Score

100/100

Audited on Mar 30, 2026

No findings