From 969b81b14f942230e8adcf63c666ff4944020050 Mon Sep 17 00:00:00 2001 From: Miha Lunar Date: Mon, 26 Jan 2026 18:59:55 +0100 Subject: [PATCH 1/6] feat: add support for HEIC/HEIF, GIF, and MOV formats with FFmpeg integration --- .changes/unreleased/heic-support.md | 7 +++++++ Dockerfile | 7 ++++++- defaults.yaml | 9 ++++----- docs/dependencies.md | 2 +- main.go | 4 ++++ 5 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 .changes/unreleased/heic-support.md diff --git a/.changes/unreleased/heic-support.md b/.changes/unreleased/heic-support.md new file mode 100644 index 00000000..4130b794 --- /dev/null +++ b/.changes/unreleased/heic-support.md @@ -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. diff --git a/Dockerfile b/Dockerfile index 5ddb5289..32d8e201 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/defaults.yaml b/defaults.yaml index 80e24964..e2258f4a 100644 --- a/defaults.yaml +++ b/defaults.yaml @@ -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 @@ -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 diff --git a/docs/dependencies.md b/docs/dependencies.md index f8dfdf1b..07b12748 100644 --- a/docs/dependencies.md +++ b/docs/dependencies.md @@ -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)** - [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`). diff --git a/main.go b/main.go index a2ccdbc2..78329e46 100644 --- a/main.go +++ b/main.go @@ -2292,6 +2292,10 @@ func main() { mime.AddExtensionType(".png", "image/png") mime.AddExtensionType(".jpg", "image/jpg") 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") From 93951d11c4ebc21d31163bcdaf6cca4b7c74ba9a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 18:10:59 +0000 Subject: [PATCH 2/6] Initial plan From da0524143054d752f11068e8e3bbd2d974998097 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 18:13:44 +0000 Subject: [PATCH 3/6] fix: register media MIME types in both UI+API and API-only modes Co-authored-by: SmilyOrg <1451391+SmilyOrg@users.noreply.github.com> --- main.go | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/main.go b/main.go index 78329e46..5602095f 100644 --- a/main.go +++ b/main.go @@ -2281,22 +2281,25 @@ func main() { r.Mount("/debug", middleware.Profiler()) r.Handle("/debug/fgprof", fgprof.Handler()) + // Register media MIME types for both UI+API and API-only modes + // (needed for /files/{id} endpoint in all modes) + mime.AddExtensionType(".png", "image/png") + mime.AddExtensionType(".jpg", "image/jpg") + 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") + msg := "" if apiPrefix != "/" { - // Hardcode well-known mime types, see https://github.com/golang/go/issues/32350 + // Hardcode well-known mime types for UI assets, see https://github.com/golang/go/issues/32350 mime.AddExtensionType(".js", "text/javascript") mime.AddExtensionType(".css", "text/css") mime.AddExtensionType(".html", "text/html") mime.AddExtensionType(".woff", "font/woff") mime.AddExtensionType(".woff2", "font/woff2") - mime.AddExtensionType(".png", "image/png") - mime.AddExtensionType(".jpg", "image/jpg") - 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") if err != nil { From fd8010482252cb6d125b5855e51dc0a5ba24a199 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 18:14:00 +0000 Subject: [PATCH 4/6] fix: align Dockerfile FFmpeg version guidance with docs Co-authored-by: SmilyOrg <1451391+SmilyOrg@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 32d8e201..ee57400d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,7 +21,7 @@ FROM alpine:3.23 # Install runtime dependencies # - exiftool: metadata extraction -# - ffmpeg: video thumbnails, HEIC/HEIF/MOV/GIF support (8.0.1+ includes HEVC decoder) +# - ffmpeg: video thumbnails, HEIC/HEIF/MOV/GIF support (requires build with HEVC/H.265 decoder + libheif; v7.0+ recommended, see docs) # - libjpeg-turbo-utils: fast JPEG decoding via djpeg # - libwebp: WebP encoding support RUN apk add --no-cache exiftool ffmpeg libjpeg-turbo-utils libwebp && \ From b56e415d3fbdc45f23127a708d4cf62198cd55a8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 18:14:15 +0000 Subject: [PATCH 5/6] fix: clarify FFmpeg requirement in changelog Co-authored-by: SmilyOrg <1451391+SmilyOrg@users.noreply.github.com> --- .changes/unreleased/heic-support.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changes/unreleased/heic-support.md b/.changes/unreleased/heic-support.md index 4130b794..d30c7513 100644 --- a/.changes/unreleased/heic-support.md +++ b/.changes/unreleased/heic-support.md @@ -4,4 +4,4 @@ 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. +HEIC (High Efficiency Image Container), HEIF (High Efficiency Image Format), GIF, and MOV (QuickTime) files are now supported when FFmpeg is available. These formats, commonly used by iOS devices and other platforms, are decoded using FFmpeg. 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 when FFmpeg is installed. From 7982f4a6e57b55181f354f623a41fad1205162eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 18:23:32 +0000 Subject: [PATCH 6/6] revert: keep MIME types in UI-only block (Windows workaround) Co-authored-by: SmilyOrg <1451391+SmilyOrg@users.noreply.github.com> --- main.go | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/main.go b/main.go index 5602095f..78329e46 100644 --- a/main.go +++ b/main.go @@ -2281,25 +2281,22 @@ func main() { r.Mount("/debug", middleware.Profiler()) r.Handle("/debug/fgprof", fgprof.Handler()) - // Register media MIME types for both UI+API and API-only modes - // (needed for /files/{id} endpoint in all modes) - mime.AddExtensionType(".png", "image/png") - mime.AddExtensionType(".jpg", "image/jpg") - 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") - msg := "" if apiPrefix != "/" { - // Hardcode well-known mime types for UI assets, see https://github.com/golang/go/issues/32350 + // Hardcode well-known mime types, see https://github.com/golang/go/issues/32350 mime.AddExtensionType(".js", "text/javascript") mime.AddExtensionType(".css", "text/css") mime.AddExtensionType(".html", "text/html") mime.AddExtensionType(".woff", "font/woff") mime.AddExtensionType(".woff2", "font/woff2") + mime.AddExtensionType(".png", "image/png") + mime.AddExtensionType(".jpg", "image/jpg") + 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") if err != nil {