PhotoLab 9 to Apple Photos workflow?

I am looking to upgrade my RAW workflow. PhotoLab looks like a strong choice due to superior RAW processing of my Sony A7C II photos.

One blocking problem remains: I have found no convenient workflow to store final edited photos to Apple Photos where my library resides. The options I see in PhotoLab are either JPEG (6MB) that reduces color accuracy and depth or DNG (with all corrections applied). DNG looks great, but is 129MB. I can export DNG in Photos to HEIC, re-import, and delete DNG. This yields the desired result: accurate colors, preserved depth/resolution, and small image size (3MB). But it requires way too many manual steps.

What is the right way to export from PhotoLab to Apple Photos in a proper archival format (HEIC)?

If this is not possible in PhotoLab, would Lightroom or CaptureOne be able to do it?

Does not Export to Application work for you. A file type can be selected under the Action dropdown (but not HEIC).

The whole point is to use HEIC, so export to application does not help.

The least bad option seems to be to export as 16bit TIFFs to a folded and then convert them there with sips and import to Photos.

Something like this

#!/bin/bash
set -euo pipefail
IFS=$'\n\t'

# import.sh
# - Converts TIFF/TIFFs to HEIC using sips, deletes TIFF if conversion succeeds
# - Picks up orphan HEIC/HEIF files in the folder
# - Imports all HEIC/HEIF to Photos (no album)
# - Deletes HEIC/HEIF originals on disk if import succeeds

WATCH_DIR="${1:-$HOME/Pictures/import-to-photos}"
DRY_RUN="${DRY_RUN:-0}"   # set to 1 for dry run (no import, no deletes)

log() { printf '%s\n' "$*" >&2; }

# Collect TIFFs (bash 3.2 compatible)
tiffs=()
while IFS= read -r f; do
  tiffs+=("$f")
done < <(find "$WATCH_DIR" -maxdepth 1 -type f \( -iname "*.tif" -o -iname "*.tiff" \) -print)

failed=0

# Convert TIFF -> HEIC and delete TIFF on success
if [[ "${#tiffs[@]}" -gt 0 ]]; then
  log "Found ${#tiffs[@]} TIFF(s) in: $WATCH_DIR"
  for tif in "${tiffs[@]}"; do
    base="${tif%.*}"
    heic="${base}.heic"
    log "Converting: $tif -> $heic"

    if [[ "$DRY_RUN" == "1" ]]; then
      continue
    fi

    if sips -s format heic "$tif" --out "$heic" >/dev/null 2>&1 && [[ -s "$heic" ]]; then
      rm -f -- "$tif"
      log "OK: converted and deleted TIFF"
    else
      log "ERROR: conversion failed for: $tif"
      rm -f -- "$heic" 2>/dev/null || true
      failed=1
    fi
  done
else
  log "No TIFF files found in: $WATCH_DIR"
fi

if [[ "$failed" -ne 0 ]]; then
  log "One or more conversions failed. Skipping Photos import."
  exit 1
fi

# Collect HEIC/HEIFs to import
heics=()
while IFS= read -r f; do
  heics+=("$f")
done < <(find "$WATCH_DIR" -maxdepth 1 -type f \( -iname "*.heic" -o -iname "*.heif" \) -print)

if [[ "${#heics[@]}" -eq 0 ]]; then
  log "No HEIC/HEIF files found to import in: $WATCH_DIR"
  exit 0
fi

log "Will import ${#heics[@]} HEIC/HEIF file(s) from: $WATCH_DIR"
[[ "$DRY_RUN" == "1" ]] && log "DRY_RUN=1 (no import, no deletions)"

if [[ "$DRY_RUN" == "1" ]]; then
  exit 0
fi

# CRITICAL FIX:
# Use `osascript -` to explicitly tell osascript "read the script from stdin".
# Otherwise, it interprets the first path you pass as the script filename, causing the -2740 error you saw.
osascript - "${heics[@]}" <<'APPLESCRIPT'
on run argv
  if (count of argv) < 1 then error "No files provided."

  tell application "Photos"
    repeat with p in argv
      set posixPath to (contents of p)
      set f to (POSIX file posixPath) as alias
      import f
    end repeat
  end tell
end run
APPLESCRIPT

import_rc=$?
if [[ "$import_rc" -ne 0 ]]; then
  log "ERROR: Photos import failed (osascript exit $import_rc). Keeping HEIC/HEIF files for retry."
  exit 2
fi

log "Photos import succeeded. Deleting ${#heics[@]} imported HEIC/HEIF file(s) from disk."
for f in "${heics[@]}"; do
  rm -f -- "$f"
done

log "Done."
2 Likes

You could probably use the FSEvents api in macOS to trigger conversion and import from the creation of a new TIFF export.

I do something similar, but on Linux: I export TIFF to an SMB-mounted directory on my Linux server, and a script on the server reacts to inotify events to convert to (or remove) JPEG in another directory that I view from various devices over SMB. Works really well for me, all I do manually is export, scripts take care of all my subsequent processing.

I used to use cron jobs to trigger conversions, but reacting to filesystem events makes the process almost immediate after manually exporting from PhotoLab or any other editor. I’m actually mostly using darktable now, and use darktable-cli to even automate export: all I do is put a green label on images I want to export, scripts do the rest.

You need some kind of tool to do the work. Have a look at Retrobatch or Squash as possible options. If you want free, it can be done in various ways. I wonder (not at my Mac at the moment) whether the built in Actions menu in Finder — Convert Images — can do it.

I used Gemini to generate suggestions for you. While it is often WRONG about things, I find I can usually find my way to solutions based on the info it provides. I have NOT tested or verified any of procedures below so don’t blame me if you erase your boot drive lol

Q: What would I use to create an automated workflow to export from PhotoLab to Apple Photos in HEIC format?

“ Since DxO PhotoLab 9 does not natively export directly to the HEIC format (it typically supports JPEG, TIFF, and DNG), you can bridge this gap by creating a macOS Shortcut.

Because you are on macOS Sequoia, the Shortcuts app is the most robust and “future-proof” way to automate this, especially for handling Apple-specific formats like HEIC and moving files into your Photos library.

The Recommended Workflow: macOS Shortcuts

You will create a “Folder Action” or a “Quick Action” that watches a specific export folder and converts the files automatically.

Step 1: Create a “Bridge” Folder

  1. Create a folder on your Mac (e.g., ~/Desktop/PL9_To_Photos).

  2. In DxO PhotoLab 9, create an Export to Disk preset:

• Format: TIFF (8-bit or 16-bit) or JPEG (100% quality). Note: Use TIFF if you want to preserve the highest quality before the HEIC conversion.

• Destination: Select your new PL9_To_Photos folder.

Step 2: Build the Shortcut

  1. Open the Shortcuts app on macOS Sequoia.

  2. Click the + to create a new shortcut.

  3. Add the following actions in order:

• Convert Image: Set it to convert “Shortcut Input” to HEIF (this is the underlying format for HEIC).

• Save to Photo Album: Set it to save the “Converted Image” to Recents (or a specific album like “Restored Slides”).

• Delete Files: (Optional) Set it to delete the “Shortcut Input” (the original TIFF/JPEG) to keep your bridge folder clean.

Step 3: Automate with Folder Actions

To make this truly “automated” once you click export in PhotoLab:

  1. Right-click your PL9_To_Photos folder in Finder.

  2. Select Folder Actions Setup…

  3. Attach your new Shortcut to this folder.

Alternative: Keyboard Maestro

If you want a more “pro” level of automation (for instance, if you want the Mac to automatically trigger the export the moment you finish a restoration), Keyboard Maestro is the industry standard for macOS power users. It can:

• Watch for a specific file extension to appear.

• Run a shell script to convert via sips (a built-in macOS image processing tool).

• Import directly into the Apple Photos database.

Restoration Note

Since you are restoring vintage Ektachrome and Anscochrome slides, exporting to 16-bit TIFF from PhotoLab 9 before converting to HEIC is crucial. This ensures that the delicate color grading you’ve applied in FilmPack 8 isn’t degraded by double-compression during the transfer.

Would you like me to write a specific AppleScript that you can paste into a Shortcut to handle the metadata preservation during the HEIC conversion?”

1 Like

I built an workflow works great.

My workflow is

  1. Generate TIFF file from DxO Photolab
  2. Convert it to HEIC using sip command
  3. Create an album in Photo.app and put these photo into album

What I did is

  • Tidy up my photo in folders. The folder name as album title
  • Export TIFF file as sub directory DXOexport
  • Use Quick Action to activate the script that HEIC convertion and put HEIC file to album
2 Likes

2 Likes

I created 2 automator workflow for Quick Actions entries

#!/bin/bash
function info() { osascript -e "display dialog \"$1\" buttons {\"OK\"} default button \"OK\""; }

# Loop through each input argument
function run_command_and_collect_output() {
cmd="$1"
outputs=$($cmd 2>&1)
info "$cmd"

info "message: $outputs"
}

function sourcesysscript() {
    if [[ -z "$1" ]]; then
        echo "Usage: sourcewhich <command>" >&2
        return 1
    fi

    # Resolve the command's path
    local cmd_path
    cmd_path=$(which "$1" 2>/dev/null)

    # Check if the command exists
    if [[ -n "$cmd_path" && -f "$cmd_path" ]]; then
        source "$cmd_path"
    else
        echo "Command '$1' not found or is not a file." >&2
        return 1
    fi
}
function putImageFolderToAlbum() {
albumNAME=$1
imageFOLDER=$2


# Define the album name and the folder containing HEIC images
#albumNAME="qqq"
#imageFOLDER="/Users/noises/Pictures/TBD/ltsai photos/heic_exported/2024.11.16.再去大湖公園/"

# Create an AppleScript command to import images and organize them
#apple_script=`which putImageFolderToAlbum.scpt`
#osascript <<EOF
#`cat $apple_script`
#createAlbum("$albumNAME")
#putFileToAlbum("$albumNAME", "$imageFOLDER")
#EOF

osascript <<EOF
on createAlbum(newALBUMname,EMPTY_VALUE)
    tell application "Photos"
        -- Get the current year
        set currentYear to year of (current date) as string
 
 
        -- Check if an album with this name already exists
        if not (exists (album named newALBUMname)) then
            -- Create a new smart album
            set newAlbum to make new album
            set name of newAlbum to newALBUMname
 
            -- Add conditions to the smart album
            -- Add your conditions here
 
            -- Return the new smart album
            return newAlbum
        else
            -- Album with the specified name already exists
            -- display dialog "Album with name \"" & newALBUMname & "\" already exists."
            error "Albumm name " & newALBUMname & " duplicated. Abort code..." number -128
        end if
    end tell
end createAlbum

on putFileToAlbum(albumNAME, imageFOLDER)
  tell application "Photos"
    -- Find the album with the specified name
    set targetAlbum to null
    repeat with anAlbum in albums
        if name of anAlbum is albumNAME then
            set targetAlbum to anAlbum
            exit repeat
        end if
    end repeat

    -- Check if the album exists
    if targetAlbum is null then
        display dialog "Album \"" & albumNAME & "\" not found." buttons {"OK"} default button "OK"
        return
    end if

    

    -- Import images from the folder
    set imageFolder to POSIX file imageFOLDER as alias
    set importedItems to import imageFolder skip check duplicates yes

    -- Add imported images to the album
    add importedItems to targetAlbum
  end tell
end putFileToAlbum
-- Call the function to create the smart album
createAlbum("$albumNAME", "EMPTY_VALUE")
putFileToAlbum("$albumNAME", "`realpath "$imageFOLDER"`")
EOF





if [ "$?" == "0" ]; then echo "HEIC images from '$imageFOLDER' have been imported into Photos and added to the album '$albumNAME'."; fi
}

function help_func() { echo "--- heicConvert.sh --- \nconvert the TIFF file into .heic format.\n  arg1: input folder. This program searches the 'DXOexport' folder inside input folder.\n  arg2: output folder. heic_output as default."; exit; }
function the_exit() { echo "ABORT - $1"; exit; }
function heicConvert() {
inFOLDER="$1"
outFOLDER=${2:-heic_exported}
DXOexport="${inFOLDER}/DXOexport"



if [ "$inFOLDER" == "" ]; then echo "[NoInputFolder] arg1 is empty" ; help_func; fi
if [ ! -d "$inFOLDER" ]; then the_exit "[FolderNotFound] input folder '$inFOLDER' is invalid"; fi
if [ ! -d "$DXOexport" ]; then the_exit "[NothingInFolder] input folder '$inFOLDER' cannot find 'DXOexport' folder inside. Abort"; fi

album_name=`basename "$inFOLDER"`
out_folder="${outFOLDER}/$album_name"
mkdir -p "$out_folder"
echo "[OutFolder] $out_folder"

for tiffFILE in "$DXOexport"/*.{TIFF,tif,tiff}; do
  ff=${tiffFILE%.TIFF}
  ff=${ff%.tiff}
  ff=${ff%.tif}
  tiff_file_name=`basename "${ff}"`
  #tiff_file_name=`basename "${tiffFILE%.TIFF}"`
  output_heic_name="${out_folder}/${tiff_file_name}.heic"
  echo sips --setProperty format heic "$tiffFILE" --out "$output_heic_name"
  sips --setProperty format heic "$tiffFILE" --out "$output_heic_name"
done

for tiffFILE in "$DXOexport"/*.JPEG; do
  tiff_file_name=`basename "${tiffFILE%.JPEG}"`
  output_heic_name="${out_folder}/${tiff_file_name}.heic"
  echo sips --setProperty format heic "$tiffFILE" --out "$output_heic_name"
  sips --setProperty format heic "$tiffFILE" --out "$output_heic_name"
done

echo putImageFolderToAlbum.sh $album_name $out_folder
putImageFolderToAlbum "$album_name" "$out_folder"
}








# Show a "Processing" message
osascript -e 'display dialog "Sync in progress... This may take a while. You can minimize this dialog." buttons {} giving up after 0'&
dialog_pid=$!
for folder_abs_path in "$@"; do
  # Replace with your logic, e.g., moving, renaming, or processing files
	cd "$folder_abs_path"/..
	#run_command_and_collect_output "$executable '$folder_abs_path/' heic_exported"
  heicConvert "$folder_abs_path/" heic_exported 2>&1 >log
  if [ "$?" != 0 ]; then info "`cat log`"; fi; /bin/rm log
done

### kill the message even if it is not closed
kill $dialog_pid 2>/dev/null
# Notify completion
info "All job finished."


And

on HI(debugMessage)
	return
	-- Display a message box for debugging
	display dialog "hi " & debugMessage buttons {"OK"} default button "OK"
end HI
on showDialogWithOptions(messageText)
	-- Display a dialog with OK and Abort options
	set userChoice to button returned of (display dialog messageText buttons {"OK", "Skip This"} default button "OK")
	if userChoice is "Skip This" then
		return false -- Skip the next result
	else
		return true -- Continue processing
	end if
end showDialogWithOptions

on createAlbumIfNeeded(albumName)
	my HI("10")
	tell application "Photos"
		-- Check if an album with this name already exists
		my HI("1")
		set targetAlbum to null
		repeat with anAlbum in albums
			if name of anAlbum is albumName then
				set targetAlbum to anAlbum
				exit repeat
			end if
		end repeat
		my HI("2")
		
		-- If the album doesn't exist, create it
		if targetAlbum is null then
			set newAlbum to make new album
			set name of newAlbum to albumName
			my HI("3")
			return newAlbum
		else
			-- display dialog "The selected folder name is: " & folderName & " and absname : " & thepath buttons {"OK"} default button "OK"
			set use_old_album to my showDialogWithOptions("Album '" & albumName & "' existed. Put image inside this album?")
			if use_old_album is true then
				return targetAlbum
			else
				return null
			end if
		end if
	end tell
end createAlbumIfNeeded

on importImagesToAlbum(albumName, folderPath)
	tell application "System Events"
		-- Get all files from the selected folder
		set folderAlias to POSIX file folderPath as alias
		set fileList to files of folderAlias
	end tell
	
	set supportedExtensions to {"jpg", "jpeg", "png", "heic", "tiff"}
	set filteredFileList to {}
	
	-- Filter files by extension
	repeat with aFile in fileList
		tell application "System Events"
			set fileExtension to name extension of aFile
		end tell
		if fileExtension is in supportedExtensions then
			set end of filteredFileList to aFile
		end if
	end repeat
	
	-- Import filtered files into Photos
	tell application "Photos"
		-- Ensure the album exists
		my HI("0")
		
		set targetAlbum to my createAlbumIfNeeded(albumName)
		if targetAlbum is null then
			return null
		end if
		-- Import each file and add it to the album
		repeat with aFile in filteredFileList
			set fileAlias to aFile as alias
			set importedItems to import fileAlias skip check duplicates yes
			add importedItems to targetAlbum
		end repeat
	end tell
end importImagesToAlbum


on run {input, parameters}
	-- Check if any folder is selected
	if input is not {} then
		repeat with inpath in input
			-- Get the first folder's name
			tell application "Finder"
				set selectedFolder to inpath
				set folderName to name of selectedFolder
				set thepath to POSIX path of selectedFolder
			end tell
			
			-- Display the folder name
			-- display dialog "The selected folder name is: " & folderName & " and absname : " & thepath buttons {"OK"} default button "OK"
			importImagesToAlbum(folderName, thepath)
		end repeat
		
		
	else
		-- No folder selected
		display dialog "No folder was selected." buttons {"OK"} default button "OK"
	end if
	return input
end run


1 Like