由于最近SSD暴涨,导致NAS扩容成本增加,而且正敢上ZimaOS暴了,新安装的飞牛OS,所以软件也要重新安装,重新导入immich数据,就想到要不要给数据压缩一下,找来找去,找到这个项目,非官方出品,但从github上看,还在维护中,于是花了点时间研究了一下 immich-upload-optimizer,该服务运行在 immich-server之前, 所有上传的数据都会按照配置进行处理后再传给 immich-server。

准备配置文件

创建一个 config 目录,添加文件 tasks.yaml ,这个文件是必须的,即使不压缩,也需要有这个文件 ,内容如下:

tasks:
  - name: caesium
    command: caesiumclt --keep-dates --exif --quality=85 --output={{.folder}} {{.folder}}/{{.name}}.{{.extension}}
    extensions:
      - jpeg
      - jpg
      - png
      - tiff
      - tif
      - webp
      - gif

  - name: handbrake
    command: HandBrakeCLI --preset-import-file handbrake.json -Z immich-upload-optimizer -i {{.folder}}/{{.name}}.{{.extension}} -o {{.folder}}/{{.name}}-new.mp4 && rm {{.folder}}/{{.name}}.{{.extension}}
    extensions:
      - 3gp
      - 3gpp
      - avi
      - flv
      - m4v
      - mkv
      - mts
      - m2ts
      - m2t
      - mp4
      - insv
      - mpg
      - mpe
      - mpeg
      - mov
      - webm
      - wmv

这里添加了两个任务,分别说明一下

  • caesium 任务用于压缩常见图片(extensions 中定义的图片格式),当前这个任务主要设置了压缩时保留时间(--keep-dates),保留元数据(--exif),压缩质量85(--quality=85)
  • handbrake 任务用于压缩常见视频(extensions 中定义的视频格式),当前这个任务压缩参数被放在了 handbrake.json 文件中,该文件也放在 config 目录下,下面给出适合家庭相册归档(1080p30 cf27 acc128 48)的 handbrake.json 文件内容

    {
        "PresetList": [
            {
                "AlignAVStart": true,
                "AudioAutomaticNamingBehavior": "unnamed",
                "AudioCopyMask": [
                    "copy:aac"
                ],
                "AudioEncoderFallback": "av_aac",
                "AudioLanguageList": [],
                "AudioList": [
                    {
                        "AudioBitrate": 128,
                        "AudioCompressionLevel": -1.0,
                        "AudioDitherMethod": "auto",
                        "AudioEncoder": "av_aac",
                        "AudioMixdown": "stereo",
                        "AudioNormalizeMixLevel": false,
                        "AudioSamplerate": "48",
                        "AudioTrackDRCSlider": 0.0,
                        "AudioTrackGainSlider": 0.0,
                        "AudioTrackQuality": 1.0,
                        "AudioTrackQualityEnable": false
                    }
                ],
                "AudioSecondaryEncoderMode": true,
                "AudioTrackNamePassthru": true,
                "AudioTrackSelectionBehavior": "first",
                "ChapterMarkers": true,
                "ChildrenArray": [],
                "Default": true,
                "FileFormat": "av_mp4",
                "Folder": false,
                "FolderOpen": false,
                "InlineParameterSets": false,
                "MetadataPassthru": true,
                "Mp4iPodCompatible": false,
                "Optimize": true,
                "PictureAllowUpscaling": false,
                "PictureAutoCrop": true,
                "PictureBottomCrop": 0,
                "PictureChromaSmoothCustom": "",
                "PictureChromaSmoothPreset": "off",
                "PictureChromaSmoothTune": "none",
                "PictureColorspaceCustom": "",
                "PictureColorspacePreset": "off",
                "PictureCombDetectCustom": "",
                "PictureCombDetectPreset": "off",
                "PictureCropMode": 0,
                "PictureDARWidth": 1920,
                "PictureDeblockCustom": "strength=strong:thresh=20:blocksize=8",
                "PictureDeblockPreset": "off",
                "PictureDeblockTune": "medium",
                "PictureDeinterlaceCustom": "",
                "PictureDeinterlaceFilter": "off",
                "PictureDeinterlacePreset": "",
                "PictureDenoiseCustom": "",
                "PictureDenoiseFilter": "off",
                "PictureDenoisePreset": "",
                "PictureDenoiseTune": "none",
                "PictureDetelecine": "off",
                "PictureDetelecineCustom": "",
                "PictureForceHeight": 0,
                "PictureForceWidth": 0,
                "PictureHeight": 1080,
                "PictureItuPAR": false,
                "PictureKeepRatio": true,
                "PictureLeftCrop": 0,
                "PictureModulus": 2,
                "PicturePAR": "off",
                "PicturePARHeight": 1,
                "PicturePARWidth": 1,
                "PicturePadBottom": 0,
                "PicturePadColor": "black",
                "PicturePadLeft": 0,
                "PicturePadMode": "none",
                "PicturePadRight": 0,
                "PicturePadTop": 0,
                "PictureRightCrop": 0,
                "PictureRotate": "angle=0:hflip=0",
                "PictureSharpenCustom": "",
                "PictureSharpenFilter": "off",
                "PictureSharpenPreset": "",
                "PictureSharpenTune": "",
                "PictureTopCrop": 0,
                "PictureUseMaximumSize": true,
                "PictureWidth": 1920,
                "PresetDescription": "1080p30 cf27 acc128 48",
                "PresetDisabled": false,
                "PresetName": "immich-upload-optimizer",
                "SubtitleAddCC": false,
                "SubtitleAddForeignAudioSearch": true,
                "SubtitleAddForeignAudioSubtitle": false,
                "SubtitleBurnBDSub": true,
                "SubtitleBurnBehavior": "foreign",
                "SubtitleBurnDVDSub": true,
                "SubtitleLanguageList": [],
                "SubtitleTrackNamePassthru": true,
                "SubtitleTrackSelectionBehavior": "none",
                "Type": 1,
                "UsesPictureFilters": true,
                "VideoAvgBitrate": 9000,
                "VideoColorMatrixCodeOverride": 0,
                "VideoColorRange": "limited",
                "VideoEncoder": "x265_10bit",
                "VideoFramerate": "30",
                "VideoFramerateMode": "cfr",
                "VideoGrayScale": false,
                "VideoHWDecode": 0,
                "VideoLevel": "auto",
                "VideoMultiPass": true,
                "VideoOptionExtra": "",
                "VideoPasshtruHDRDynamicMetadata": "all",
                "VideoPreset": "medium",
                "VideoProfile": "auto",
                "VideoQualitySlider": 27.0,
                "VideoQualityType": 2,
                "VideoScaler": "swscale",
                "VideoTune": "",
                "VideoTurboMultiPass": false,
                "x264Option": "",
                "x264UseAdvancedOptions": false
            }
        ],
        "VersionMajor": 67,
        "VersionMicro": 0,
        "VersionMinor": 0
    }

说明一下,该文件内容中 PresetName 参数对应 tasks.yaml 文件 handbrake 任务命令中的 -Z 参数

HandBrakeCLI --preset-import-file handbrake.json -Z immich-upload-optimizer -i ...

对于 handbrake.json 的生成,可以在 handbrake 官网下载该软件,然后自行调整参数,创建新预设,导出这个预设,就是 handbrake.json 文件,需要注意的是,PresetName 参数是新建预设的名字,结合上面内容,需要注意和 -Z 参数保持一致。

如果对某几种格式(使用 extensions参数设置)不想压缩,设置 tasks.yaml 文件内容如下:

tasks:
  - name: passthrough-images
    command: ""
    extensions:
      - avif
      - bmp
      - heic
      - heif
      - insp
      - jxl
      - psd
      - raw
      - rw2
      - svg
  - name: passthrough-videos
    command: ""
    extensions:
      - 3gp
      - 3gpp
      - avi
      - flv
      - m4v
      - mkv
      - mts
      - m2ts
      - m2t
      - mp4
      - insv
      - mpg
      - mpe
      - mpeg
      - mov
      - webm
      - wmv

修改docker-compose.yml

为 immich 的 docker-compose.yml 文件增加 immich-upload-optimizer 服务

services:
  immich-upload-optimizer:
    image: ghcr.io/miguelangel-nubla/immich-upload-optimizer:latest
    ports:
      - "2283:2283"
    volumes:
      - /opt/docker/immich/config:/etc/immich-upload-optimizer/config
    environment:
      - IUO_UPSTREAM=http://immich-server:2283
    depends_on:
      - immich-server
    restart: always

  immich-server:
    # ports:
    #   - "2283:2283" 
    ...

由于 immich-server 的默认端口是2283,这里需要关闭 immich-server 的2283端口的对外暴露,或者修改 immich-upload-optimizer 暴露的端口,避免冲突。

注:通过修改 immich-upload-optimizer 端口的方式有个好处,可以主动的访问不同端口来决定是否对要上传的资源进行压缩

volumes 映射的本地路径就是上一步的 config 目录。

启动服务

docker compose down
docker compose up -d

Dean King

2026/3/18 夜于唐山