Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changes/unreleased/heic-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
kind: Added
body: Add HEIC/HEIF, GIF image and MOV video format support via FFmpeg
time: 2026-01-25T21:00:00Z
---

HEIC (High Efficiency Image Container), HEIF (High Efficiency Image Format), GIF, and MOV (QuickTime) files are now fully supported. These formats, commonly used by iOS devices and other platforms, are decoded using FFmpeg when available. The extensions `.heic`, `.heif`, `.gif`, and `.mov` are now included in the default configuration, so Photofield will automatically index and display these files alongside your other media.
11 changes: 10 additions & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ Migration naming: `{sequence}_{description}.{up|down}.sql`

## Development Workflows

### Getting Started (Fresh Worktree or Clone)
**First time setup** - `task dev` automatically installs dependencies:
- Installs UI npm packages (cached based on package.json)
- Downloads geo assets for reverse geocoding (~50MB)
- Starts API + UI in watch mode

**Working in worktrees**: Set `PHOTOFIELD_DATA_DIR=~/code/photofield/data` to share the data directory (config, databases, caches) with the main repo. Without this, each worktree has isolated data.

### Build System (Taskfile.yml)
**Conditional compilation with build tags**:
- `embedui` - embeds `ui/dist/` into binary (requires `task build:ui` first)
Expand All @@ -63,7 +71,8 @@ Migration naming: `{sequence}_{description}.{up|down}.sql`

Common workflows:
```bash
task dev # Run API + UI in watch mode (two terminals)
task dev # Run API + UI in watch mode (auto-setup on first run)
task setup # Manually install UI deps + geo assets
task watch # API only with hot-reload via watchexec
task ui # UI dev server (Vite)
task run:embed # Build with embedded UI+docs, run locally
Expand Down
7 changes: 6 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@ RUN \
-o /build/photofield .

# Runtime stage
FROM alpine:3.22
FROM alpine:3.23

# Install runtime dependencies
# - exiftool: metadata extraction
# - ffmpeg: video thumbnails, HEIC/HEIF/MOV/GIF support (8.0.1+ includes HEVC decoder)
# - libjpeg-turbo-utils: fast JPEG decoding via djpeg
# - libwebp: WebP encoding support
RUN apk add --no-cache exiftool ffmpeg libjpeg-turbo-utils libwebp && \
ln -s /usr/lib/libwebp.so.7 /usr/lib/libwebp.so

Comment on lines +20 to 29
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ln -s /usr/lib/libwebp.so.7 /usr/lib/libwebp.so symlink is brittle, especially after bumping the base image to Alpine 3.23 (the libwebp SONAME can change between Alpine releases). Consider making this symlink target dynamic (or removing it if no longer needed) so Docker builds don’t break on package updates.

Copilot uses AI. Check for mistakes.
Expand Down
25 changes: 24 additions & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -265,12 +265,35 @@ tasks:
wget -q -O "$gpkg_path" https://github.com/SmilyOrg/tinygpkg-data/releases/download/{{ .GPKG_VER }}/{{ .GPKG_FILE }}
echo "downloaded to $PWD/$gpkg_path"

setup:ui:
desc: Install UI dependencies
dir: ui
sources:
- package.json
- package-lock.json
generates:
- node_modules/.package-lock.json
cmds:
- npm install

setup:
desc: Setup minimal dependencies for development (UI npm packages and geo assets)
deps:
- setup:ui
- assets

dev:
desc: Run the API and UI in watch mode
cmds:
- task: setup
- task: dev:watch

Comment on lines +279 to +290
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

task dev now runs task setup, which depends on assets; the assets task currently uses wget directly. That makes wget an implicit requirement for task dev in fresh environments (where it may not be installed). Consider adding a curl fallback (or explicitly documenting/installing wget) so task dev works out-of-the-box as intended.

Copilot uses AI. Check for mistakes.
dev:watch:
desc: Run the API and UI in watch mode
deps:
- watch
- ui

ui:
desc: Run the UI in watch mode
dir: ui
Expand Down
9 changes: 4 additions & 5 deletions defaults.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ media:

# File extensions to index on the file system
extensions: [
".jpg", ".jpeg", ".png", ".avif", ".bmp", ".pam", ".ppm", ".jxl", ".exr", ".cr2", ".dng",
".mp4",
".jpg", ".jpeg", ".png", ".avif", ".bmp", ".pam", ".ppm", ".jxl", ".exr", ".cr2", ".dng", ".heic", ".heif", ".gif",
".mp4", ".mov",
]

# Used to extract dates from file names as a heuristic in case of missing or
Expand All @@ -137,11 +137,10 @@ media:
date_formats: ["20060201_150405"]
images:
# Extensions to use to understand a file to be an image
# extensions: [".jpg", ".jpeg", ".png", ".gif"]
extensions: [".jpg", ".jpeg", ".png", ".avif", ".bmp", ".pam", ".ppm", ".jxl", ".exr", ".cr2", ".dng"]
extensions: [".jpg", ".jpeg", ".png", ".avif", ".bmp", ".pam", ".ppm", ".jxl", ".exr", ".cr2", ".dng", ".heic", ".heif", ".gif"]

videos:
extensions: [".mp4"]
extensions: [".mp4", ".mov"]

#
# Media source configuration
Expand Down
2 changes: 1 addition & 1 deletion docs/dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
These tools are not strictly required, but if they are installed in your system, Photofield will use them to improve performance, metadata extraction, thumbnail generation, and video previews.

- [ExifTool]: Extracts metadata from many more formats than the embedded [goexif].
- [FFmpeg]: Generates video thumbnails and previews and adds support for more image formats (even basic RAW).
- [FFmpeg]: Generates video thumbnails and previews and adds support for more image formats including HEIC/HEIF (iOS photos), MOV (QuickTime videos), and basic RAW formats. **(v7.0+ recommended)**
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FFmpeg version recommendation here (v7.0+) conflicts with the Dockerfile comment that implies a newer FFmpeg is needed for HEVC/HEIC decoding. Please align the documented recommendation with what the project actually requires/supports (and keep Dockerfile/docs consistent).

Suggested change
- [FFmpeg]: Generates video thumbnails and previews and adds support for more image formats including HEIC/HEIF (iOS photos), MOV (QuickTime videos), and basic RAW formats. **(v7.0+ recommended)**
- [FFmpeg]: Generates video thumbnails and previews and adds support for more image formats including HEIC/HEIF (iOS photos), MOV (QuickTime videos), and basic RAW formats. **(a recent FFmpeg version is recommended; matching the version used in the official Docker image is a good baseline for HEVC/HEIC support)**

Copilot uses AI. Check for mistakes.
- [djpeg (libjpeg-turbo)]: Accelerates JPEG decoding of big images in cases where there are no other appropriate thumbnails available.
- [libwebp]: Enables high-performance WebP encoding via dynamic library loading. When available, the [go-libwebp] encoder can use the native libwebp dynamic shared library for faster encoding (`webp-jackdyn`) compared to pure Go implementations (`webp-jacktra`).

Expand Down
4 changes: 4 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2292,6 +2292,10 @@ func main() {
mime.AddExtensionType(".png", "image/png")
mime.AddExtensionType(".jpg", "image/jpg")
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this hardcoded MIME map, .jpg is registered as image/jpg, which is non-standard (the registered type is image/jpeg). Since this block is being updated, consider switching the .jpg mapping to image/jpeg as well to avoid clients receiving an unexpected Content-Type.

Suggested change
mime.AddExtensionType(".jpg", "image/jpg")
mime.AddExtensionType(".jpg", "image/jpeg")

Copilot uses AI. Check for mistakes.
mime.AddExtensionType(".jpeg", "image/jpeg")
mime.AddExtensionType(".heic", "image/heic")
mime.AddExtensionType(".heif", "image/heif")
mime.AddExtensionType(".gif", "image/gif")
mime.AddExtensionType(".mov", "video/quicktime")
mime.AddExtensionType(".ico", "image/vnd.microsoft.icon")

uifs, err := fs.Sub(StaticFs, "ui/dist")
Expand Down
Loading