Skip to content

Configuration

You can configure the app via configuration.yaml.

The location of the file depends on your installation method, see Quick Start.

Minimal Example

The following is a minimal configuration.yaml example, see Defaults for all options.

yaml
collections:
  # Normal Album-type collection
  - name: Vacation Photos
    dirs:
      - /photo/vacation-photos

  # Timeline collection (similar to Google Photos)
  - name: My Timeline
    layout: timeline
    dirs:
      - /photo/myphotos
      - /exampleuser

  # Create collections from sub-directories based on their name
  - expand_subdirs: true
    expand_sort: desc
    dirs:
      - /photo

Defaults

yaml
# Defines collections / albums that are scanned and shown in the UI
collections:
  # By default show all subdirs of the current working directory
  - expand_subdirs: true
    expand_sort: desc
    dirs: ["./"]

  # - name: Collection Name
  #   layout: album | timeline | wall
  #   limit: integer number of photos to limit to (for testing large collections)
  #   expand_subdirs: true | false (expand subdirs of `dirs` to collections)
  #   expand_sort: asc | desc (order of expanded subdirs)
  #   dirs:
  #     - /first/dir
  #     - /second/dir
  #     - C:/third/windows/dir
  #     - ./relative/dir
  # 
  # Later collections override earlier ones with the same name / id
  # so that you can have expanded collections and override settings
  # of specific collections.

# Default layout of all collections
layout:
  type: ALBUM

render:
  # The area at which photos are rendered as a solid color.
  # The larger it is, the faster rendering will be, but also the more you will
  # have to zoom in to see the photo and not just a single solid color.
  max_solid_pixel_area: 1000
  # Default tile size, the UI controls this directly, so it's only relevant for
  # other use-cases.
  tile_size: 256

ai:
  # Host of an AI server providing machine learning features. Defining this
  # will enable search functionality on collection pages.
  # See https://github.com/SmilyOrg/photofield-ai on how to set it up
  # 
  # host: http://localhost:8081
  # 
  # You can also define the visual (image scanning) host and the 
  # textual search host separately, e.g. to scan images with a PC w/ GPU,
  # but keep the search part on a NAS.
  # 
  # visual:
    # host: http://localhost:8081
  # textual:
    # host: http://localhost:8081

tags:
  # Enable tagging support in the UI.
  # Tags are currently only stored in the (cache) database, so they will
  # be lost if it's deleted. Consider this alpha-level functionality.
  #
  # enable: true

  # exif:
  #   enable: true

geo:
  # Reverse geocode coordinates to location names. Runs fully locally
  # via the "rgeo" Golang library. Currently only supported in the
  # timeline layout.
  # 
  # Can delay startup by up to a minute as the local geolocation
  # database is loaded.
  # 
  reverse_geocode: true
  geopackage:
    # Path to the GeoPackage file containing features
    # WKB and TWKB (via tinygpkg) are supported.
    # 
    # See https://github.com/SmilyOrg/tinygpkg-data
    # 
    # If empty, the database embedded into the executable
    # via embed-geo.go is used.
    # 
    # path: data/geo/geoBoundariesCGAZ_ADM2_s5_twkb_p3.gpkg

    # Table in the GeoPackage file to be used in case there is
    # more than one
    #
    # If empty, the first table is used.
    # 
    # table: globalADM2

    # The column to use for reverse geocoding the location name
    name_col: shapeName # geoBoundaries CGAZ
    # name_col: name_conve # Natural Earth urban areas
    # name_col: NAME_LONG # Natural Earth countries 

media:
  # Extract metadata from this many files concurrently
  concurrent_meta_loads: 8
  
  # Extract prominent colors from this many files concurrently
  concurrent_color_loads: 4
  
  # Extract AI embeddings from this many files concurrently
  concurrent_ai_loads: 8
  
  # Number of exiftool instances to run concurrently for metadata extraction
  exif_tool_count: 4

  # Set to true to not extract any metadata or colors from photos
  skip_load_info: false

  # Skip printing the file count of collections at startup
  # This can speed up startup time for large collections
  skip_collection_counts: false
  
  caches:
    image:
      # Size of the image cache used while rendering images
      # A larger cache might make display/rendering faster, while a smaller
      # cache will conserve memory.
      max_size: 1024Mi
    
  # File extensions to index on the file system
  extensions: [
    ".jpg", ".jpeg", ".png", ".avif", ".bmp", ".pam", ".ppm", ".jxl", ".exr", ".cr2", ".dng",
    ".mp4",
  ]

  # Used to extract dates from file names as a heuristic in case of missing or
  # metadata or metadata yet to be loaded.
  # Uses the Golang date format: https://pkg.go.dev/time#pkg-constants
  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"]

  videos:
    extensions: [".mp4"]

  # 
  # Media source configuration
  # 
  # Configures the different ways to load originals, photo variants,
  # thumbnails, transcoded images, etc. For each photo, during loading or
  # rendering, the most appropriate source is selected. The criteria include
  # desired and expected dimensions, expected loading time, and possibly others.
  # 
  # As a result, this configuration can have a large effect on the speed of
  # the application.
  # 
  # The following source types are supported:
  #   SQLITE, GOEXIF, THUMB, IMAGE, FFMPEG
  # 
  # Common properties include:
  # 
  #   type: Case-insensitive source type (see above)
  # 
  #   extensions: File extensions supported by this source - the source will be
  #               skipped during selection if unsupported
  # 
  #   width, height: Expected dimensions of the provided images. These can be
  #                  approximate in case it's not possible to know them upfront.
  #                  Used during source selection to decide which source to pick.
  #                  Some sources can have either hard-coded dimensions (e.g. sqlite)
  #                  or assume the dimensions of the source file instead (e.g. image).
  #
  #   fit: Aspect ratio fit of the provided image in case it doesn't match the
  #        original file. Not supported by all sources.
  #
  #     INSIDE
  #       The thumbnail size is the maximum size of each dimension, so in case
  #       of different aspect ratios, one dimension will always be smaller.
  #
  #     OUTSIDE
  #       The thumbnail size is the minimum size of each dimension, so in case
  #       of different aspect ratios, one dimension will always be bigger.
  #
  #     ORIGINAL
  #       The size of the thumbnail is equal the size of the original. Mostly
  #       useful for transcoded or differently encoded files.
  # 
  # 
  # Additional per-source properties are:
  # 
  # SQLITE - internal thumbnail database
  #   type: sqlite
  #   path: File path relative to the data dir of the sqlite database containing
  #         the thumbnails
  # 
  # GOEXIF - thumbnails embedded in JPEG EXIF metadata
  #   type: goexif
  # 
  # IMAGE - native image loading
  #   type: image
  #   width, height: Resize the image after loading (can be slow)
  #   extensions: Supported source file extensions
  # 
  # THUMB - pregenerated thumbnail files
  #   name: Short thumbnail type name
  #   path: Path template where to find the thumbnail
  #     {{.Dir}} is replaced by the parent directory of the original photo
  #     {{.Filename}} is replaced by the original photo filename
  # 
  # FFMPEG - transcoded images via FFmpeg
  #   width, height: Dimensions to which the source media should be resized
  #   fit: The aspect ratio fit to use while resizing
  #   path: Path to the FFmpeg binary, uses the one in PATH if not set
  #
  source_types:
    sqlite:
      path: photofield.thumbs.db
      cost:
        time: 6ms

    goexif:
      extensions: [".jpg", ".jpeg"]
      width: 256
      height: 256
      fit: "INSIDE"
      cost:
        time_per_original_megapixel: 670us

    image:
      extensions: [".jpg", ".jpeg", ".png"]
      cost:
        time_per_original_megapixel: 71ms

    thumb:
      fit: "INSIDE"
      cost:
        time_per_resized_megapixel: 70ms

    ffmpeg:
      fit: "INSIDE"
      cost:
        time_per_original_megapixel: 220ms
      

  sources:
    
    # Internal thumbnail database
    - type: sqlite

    
    # Embedded JPEG thumbnails
    - type: goexif
    
    # Native image decoding
    - type: image

    # 
    # Synology Moments / Photo Station thumbnails
    # 
    - name: S
      type: thumb
      path: "{{.Dir}}@eaDir/{{.Filename}}/SYNOPHOTO_THUMB_S.jpg"
      extensions: [".jpg", ".jpeg", ".png", ".gif", ".mp4"]
      fit: "INSIDE"
      width: 120
      height: 120
      cost:
        time_per_resized_megapixel: 89ms
      
    - name: SM
      type: thumb
      path: "{{.Dir}}@eaDir/{{.Filename}}/SYNOPHOTO_THUMB_SM.jpg"
      extensions: [".jpg", ".jpeg", ".png", ".gif", ".mp4"]
      fit: OUTSIDE
      width: 240
      height: 240
      cost:
        time_per_resized_megapixel: 70ms
      
    - name: M
      type: thumb
      path: "{{.Dir}}@eaDir/{{.Filename}}/SYNOPHOTO_THUMB_M.jpg"
      extensions: [".jpg", ".jpeg", ".png", ".gif", ".mp4"]
      fit: OUTSIDE
      width: 320
      height: 320
      cost:
        time_per_resized_megapixel: 66ms
      
    - name: B
      type: thumb
      path: "{{.Dir}}@eaDir/{{.Filename}}/SYNOPHOTO_THUMB_B.jpg"
      extensions: [".jpg", ".jpeg", ".png", ".gif"]
      fit: INSIDE
      width: 640
      height: 640
      cost:
        time_per_resized_megapixel: 42ms
      
    - name: XL
      type: thumb
      path: "{{.Dir}}@eaDir/{{.Filename}}/SYNOPHOTO_THUMB_XL.jpg"
      extensions: [".jpg", ".jpeg", ".png", ".gif", ".mp4"]
      fit: OUTSIDE
      width: 1280
      height: 1280
      cost:
        time_per_resized_megapixel: 35ms
      
    #
    # Synology Moments / Photo Station video variants
    #
    - name: FM
      type: thumb
      path: "{{.Dir}}@eaDir/{{.Filename}}/SYNOPHOTO_FILM_M.mp4"
      extensions: [".mp4"]
      fit: OUTSIDE
      width: 720
      height: 720
      
    - name: H264
      type: thumb
      path: "{{.Dir}}@eaDir/{{.Filename}}/SYNOPHOTO_FILM_H264.mp4"
      extensions: [".mp4"]
      fit: ORIGINAL

    # 
    # FFmpeg on-the-fly decoding
    # 
    - type: ffmpeg
      width: 256
      height: 256
      fit: INSIDE
      cost:
        time_per_original_megapixel: 120ms

    - type: ffmpeg
      width: 1280
      height: 1280
      fit: INSIDE
      cost:
        time_per_original_megapixel: 160ms

    - type: ffmpeg
      width: 4096
      height: 4096
      fit: INSIDE
      cost:
        time_per_original_megapixel: 180ms


  # These sources are used for handling small thumbnails specifically for
  # specific purposes.
  thumbnail:

    # Thumbnail sources used for extracting colors and AI embeddings
    # 200 - 300px is likely ideal as it's small enough to process quickly,
    # but big enough to retain some details.
    # 
    # The sources here are ordered and the first source that returns
    # a valid image is used.
    sources:
      # Internal thumbnail database
      - type: sqlite
    
      # Synology Moments / Photo Station thumbnail
      - name: SM
        type: thumb
        path: "{{.Dir}}@eaDir/{{.Filename}}/SYNOPHOTO_THUMB_SM.jpg"
        extensions: [".jpg", ".jpeg", ".png", ".gif", ".mp4"]
        fit: OUTSIDE
        width: 240
        height: 240
      
      # Embedded JPEG thumbnails
      - type: goexif
    
      # Synology Moments / Photo Station thumbnail
      - name: S
        type: thumb
        path: "{{.Dir}}@eaDir/{{.Filename}}/SYNOPHOTO_THUMB_S.jpg"
        extensions: [".jpg", ".jpeg", ".png", ".gif", ".mp4"]
        fit: "INSIDE"
        width: 120
        height: 120


    # If a thumbnail is not found among the sources above,
    # it is generated with the first working generator.
    generators:
      
      # FFmpeg decoding
      - type: ffmpeg
        width: 256
        height: 256
        fit: INSIDE

      # Native decoding (resized to 256px x 256px)
      - type: image
        width: 256
        height: 256

    # The sink is used to save the generated thumbnail above
    # so that it persists and can be reused while rendering.
    sink:
      type: sqlite