Skip to content

Repository files navigation

EXIFilixir - 360° Image Fixer

A Next.js PWA application that fixes EXIF metadata for 360° panorama images (like those from DJI drones) to make them compatible with Facebook's 360° image viewer.

Features

  • 🔧 Aspect Ratio Fix: Automatically adjusts images to 2:1 ratio (required by Facebook)
  • 📸 EXIF Metadata Injection: Adds Google Photo Sphere (GPano) XMP metadata
  • 🎨 Modern UI: Built with Ant Design for a beautiful user experience
  • 📱 PWA Support: Installable as a Progressive Web App
  • ☁️ Firebase Ready: Configured for Firebase Hosting deployment
  • 🔐 Token Authentication: Protected API endpoints to prevent abuse

Live app

  • If deployed to Firebase Hosting, your URL is typically: https://<project-id>.web.app
  • This repo uses NEXT_PUBLIC_SITE_URL for canonical/OG metadata (recommended).

Donate

If this tool saved you time and you’d like to support it:

  • PayPal donate link: https://www.paypal.com/ncp/payment/GUDHT2MCK5UCW

How It Works

  1. Aspect Ratio Fix: Uses Sharp to resize images to a perfect 2:1 ratio (width:height)
  2. Metadata Injection: Uses ExifTool to inject the required XMP-GPano tags:
    • XMP-GPano:ProjectionType: "equirectangular"
    • XMP-GPano:UsePanoramaViewer: "True"
    • Camera metadata (Ricoh Theta S) for better recognition

Getting Started

Prerequisites

  • Node.js 18+
  • npm or yarn

Installation

  1. Clone the repository:
git clone <your-repo-url>
cd exifilixir
  1. Install dependencies:
npm install
  1. Set up environment variables:
# Copy the example file
cp .env.local.example .env.local

# Generate a secure token (optional, for production)
openssl rand -hex 32

# Edit .env.local and add your tokens
# API_TOKEN=your-generated-token-here
# NEXT_PUBLIC_API_TOKEN=your-generated-token-here

Note: In development, the API will work without tokens. In production, tokens are required.

SEO / indexing

  • Site URL: Set NEXT_PUBLIC_SITE_URL (for canonical/OG metadata), for example:
    • NEXT_PUBLIC_SITE_URL=https://exifilixir.web.app
  • Crawler files: public/robots.txt and public/sitemap.xml are included for basic indexing.

Local (emulator): So the Cloud Function emulator validates the token when you run npm run functions:serve, create functions/.env with the same token:

cp functions/.env.example functions/.env
# Edit functions/.env and set API_TOKEN=your-secret (same value as NEXT_PUBLIC_API_TOKEN)
  1. Run the development server:
npm run dev
  1. Open http://localhost:3000 in your browser

Brand assets (PWA + SEO)

Included in public/:

  • exifilixir.svg — homepage logo
  • icon-192x192.png, icon-512x512.png — PWA manifest icons
  • og-image.png (1200×630) — Open Graph / Twitter preview
  • favicon.png — browser tab icon

To regenerate PNGs after editing the SVG sources (exifilixir-icon.svg, og-image.svg):

npm run assets:generate

Requires ImageMagick (convert on your PATH).

Deployment

Option 1: Deploy to Vercel (Recommended)

Vercel natively supports Next.js API routes:

  1. Install Vercel CLI:
npm install -g vercel
  1. Deploy:
vercel

Or connect your GitHub repository to Vercel for automatic deployments.

Option 2: Deploy to Firebase

The app is configured to automatically use:

  • Firebase Functions emulator in development (localhost)
  • Firebase Cloud Functions in production (when deployed to Firebase Hosting)

Setup and Deploy

  1. Install Firebase CLI:
npm install -g firebase-tools
  1. Login to Firebase:
firebase login
  1. Initialize Firebase (if not already done):
firebase init
  • Select "Functions" and "Hosting"
  • Choose your Firebase project
  • For functions, use TypeScript
  • For hosting, set public directory to out
  1. Install function dependencies:
cd functions
npm install
cd ..
  1. Build the Next.js app:
npm run build
  1. Export static files:

    • Update next.config.js to add output: 'export' temporarily, OR
    • Use a custom build script that exports after build
  2. Set environment variables: After deploying functions, you'll get a function URL. Set it in your Next.js build:

    # In your build/deploy script or .env.production
    NEXT_PUBLIC_FIREBASE_FUNCTION_URL=https://us-central1-your-project.cloudfunctions.net/processImage
    NEXT_PUBLIC_FIREBASE_PROJECT_ID=your-project-id
    NEXT_PUBLIC_FIREBASE_REGION=us-central1

    API token (Firebase): To stop "API_TOKEN not set - allowing all requests" in production:

    • Cloud Function: In Google Cloud ConsoleCloud Run → select the processimage service → Edit & deploy new revisionVariables & secretsAdd variable: name API_TOKEN, value your secret. Deploy the new revision.
    • Frontend: When you run npm run build before deploying hosting, set NEXT_PUBLIC_API_TOKEN to the same value (e.g. NEXT_PUBLIC_API_TOKEN=your-token npm run build) so the client can send the token. Without it, production requests from the app will get 401 Unauthorized.
  3. Deploy functions:

firebase deploy --only functions
  1. Deploy hosting:
firebase deploy --only hosting

CI / GitHub Actions

On push to main or master (including merged PRs), .github/workflows/firebase-deploy.yml builds the static export + functions and deploys both. You can also run it manually via Actions → Deploy to Firebase → Run workflow.

Configure these repository secrets (Settings → Secrets and variables → Actions):

Secret Notes
FIREBASE_SERVICE_ACCOUNT Full contents of a GCP service account JSON key (must start with {, include private_key / client_email). Easiest: gh secret set FIREBASE_SERVICE_ACCOUNT < ./path-to-key.json. Needs Firebase Hosting Admin + Cloud Functions Developer (or Editor) on exifilixir. Do not paste a file path, .p12, or binary.
FIREBASE_PROJECT_ID Firebase / GCP project ID (e.g. exifilixir)
GH_PACKAGES_READ_TOKEN Classic PAT with read:packages for @jeffgo10/* (GitHub Packages)
NEXT_PUBLIC_API_TOKEN Same value as the Cloud Function API_TOKEN (baked into the client at build time)
NEXT_PUBLIC_SITE_URL Production site URL (e.g. https://exifilixir.web.app)
NEXT_PUBLIC_FIREBASE_PROJECT_ID Usually exifilixir
NEXT_PUBLIC_FIREBASE_REGION Usually us-central1
NEXT_PUBLIC_FIREBASE_FUNCTION_URL Optional but recommended: https://us-central1-<project>.cloudfunctions.net/processImage

One-time (outside CI): set Cloud Function API_TOKEN in Google Cloud Console → Cloud Run → processimage → Variables & secrets. Do not set it from the workflow on every deploy.

Environment Detection

The app automatically detects the environment:

  • Development (localhost): Uses the Functions emulator at http://localhost:5001/.../processImage
  • Firebase Hosting: Detects .web.app or .firebaseapp.com domains and uses Cloud Functions
  • Manual override: Set NEXT_PUBLIC_FIREBASE_FUNCTION_URL to explicitly use a function URL

Project Structure

exifilixir/
├── app/
│   ├── api/
│   │   └── process/
│   │       └── route.ts      # API endpoint for image processing
│   ├── layout.tsx            # Root layout with Ant Design config
│   ├── page.tsx              # Main UI component
│   └── globals.css           # Global styles
├── public/
│   ├── manifest.json         # PWA manifest
│   ├── sw.js                 # Service worker
│   └── icons/                # PWA icons (you'll need to add these)
├── firebase.json             # Firebase configuration
├── next.config.js            # Next.js configuration
└── package.json              # Dependencies

Technologies Used

  • Next.js 14: React framework with App Router
  • TypeScript: Type-safe development
  • Ant Design: UI component library
  • Sharp: High-performance image processing
  • ExifTool-vendored: EXIF metadata manipulation
  • Firebase: Hosting and deployment

Limitations

  • API routes in Next.js work best on Vercel. For Firebase, consider using Firebase Functions
  • Large images may take time to process
  • Currently processes one image at a time

License

MIT

About

A Next.js PWA application that fixes EXIF metadata for 360° panorama images (like those from DJI drones) to make them compatible with Facebook's 360° image viewer.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages