Skip to main content

⚠️ Please note that this topic or post has been archived. The information contained here may no longer be accurate or up-to-date. ⚠️

AppleScript (CO12) to find Images not in any User Collection

Comments

7 comments

  • Eric Valk
    The Second (Common) Part

    This applescript is a common set of functions (handles) used by all my AppleScripts. Several other postings might refer to this one. Copy and paste this code at the end of the other Applescript.

    Note that this is a version 2 months newer than that in my my last posting. Use this version with the script section, above, not the older version.

    ###########################
    ## Capture One General Handlers Version 2018/12/02

    on validateCOP2(minCOPversionstr, maxCOPversionstr)
    ## Copyright 2018 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General purpose initialisation handler for scripts using Capture One Pro
    ## Extract and check basic information about the Capture One application

    global debugLogLevel, theAppName, copVersion, copDetailedVersion, enableNotifications
    tell application "System Events"
    set COPProcList to every process whose name contains "Capture One" and background only is false
    if debugLogLevel ≥ 2 then
    set COPProcNameList to name of every process whose name contains "Capture One" and background only is false
    my loq_Results2(2, false, ("COP Processes:" & COPProcNameList))
    end if
    end tell
    if (count of COPProcList) = 0 then my loqqed_Error_Halt3(true, "COP is not running")
    if (count of COPProcList) ≠ 1 then my loqqed_Error_Halt3(true, "Unexpected: >1 COP instances")
    set theAppRef to item 1 of COPProcList
    tell application "System Events" to set theAppName to ((get name of theAppRef) as text)
    tell application "System Events" to set copDetailedVersion to get version of my application theAppName

    tell application "Capture One 12" to set copVersion to (get app version)

    if debugLogLevel ≥ 2 then
    tell application "System Events"
    my loq_Results2(2, false, ("All Processes: " & (get my joinListToString((get name of every process whose background only is false), ", "))))
    end tell
    loq_Results2(2, false, ("theAppName: " & theAppName))
    loq_Results2(2, false, ("COP Version: " & copVersion))
    loq_Results2(2, false, ("COP Detailed Version: " & copDetailedVersion))
    end if

    set numCOPversion to (splitStringToList((word -1 of copVersion), "."))
    set minCOPversion to (splitStringToList(minCOPversionstr, "."))
    set maxCOPversion to (splitStringToList(maxCOPversionstr, "."))

    set digit_mult to 1000000
    set Version_digit to 0
    repeat with dig_ctr from 1 to count of numCOPversion
    set digit_mult to digit_mult / 100
    set Version_digit to Version_digit + (get item dig_ctr of numCOPversion as integer) * digit_mult
    end repeat

    set digit_mult to 1000000
    set min_digit to 0
    repeat with dig_ctr from 1 to count of minCOPversion
    set digit_mult to digit_mult / 100
    set min_digit to min_digit + (get item dig_ctr of minCOPversion as integer) * digit_mult
    end repeat

    set digit_mult to 1000000
    set max_digit to 0
    set digit_count to count of maxCOPversion
    repeat with dig_ctr from 1 to digit_count
    set digit_mult to digit_mult / 100
    set max_digit to max_digit + (get item dig_ctr of maxCOPversion as integer) * digit_mult
    if (dig_ctr = digit_count) then set max_digit to max_digit + digit_mult
    end repeat

    if (Version_digit < min_digit) or (Version_digit ≥ max_digit) then
    if enableNotifications then display notification "COP Version is unsupported"
    my loqqed_Error_Halt3(true, "This COP Version is " & copDetailedVersion & " - the supported COP versions are from " & minCOPversionstr & " to " & maxCOPversionstr)
    end if

    tell application "System Events" to set frontmost of process theAppName to true
    loq_Results2(1, false, ("Capture One version: " & copDetailedVersion))
    end validateCOP2

    on validateCOPdoc3(COP_kind_list)
    ## Copyright 2018 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General purpose initialisation handler for scripts using Capture One Pro
    ## Extract and check basic information about the current document

    global debugLogLevel, COPDocName, COPDocKind_s, COPDocRef, theAppName

    try
    tell application "Capture One 12" to set COPDocName to get name of current document
    on error
    loqqed_Error_Halt3(true, "The Script could not retrieve the Capture One document - Perhaps a Capture One Dialog window is open?")
    end try

    tell application "Capture One 12"
    set current_doc_kind_p to (get kind of current document)
    set current_doc_ref_list to (get every document whose name is COPDocName and kind is current_doc_kind_p)
    set number_of_hits to count of current_doc_ref_list
    end tell
    set COPDocKind_s to convertKind(current_doc_kind_p)
    if COPDocKind_s = "session" then set COPDocName to text 1 thru ((offset of "." in COPDocName) - 1) of COPDocName

    loq_Results2(2, false, ("Is: " & COPDocKind_s & " was: " & (get current_doc_kind_p as text)))
    loq_Results2(2, false, ("Found Documents: " & number_of_hits))

    if COP_kind_list does not contain COPDocKind_s then loq_Results2(0, false, (COPDocName & " is a " & COPDocKind_s & " -- unsupported type of document"))

    if number_of_hits = 0 then
    loqqed_Error_Halt3(false, "Could not find find " & COPDocKind_s & COPDocName)
    error "Could not find find " & COPDocKind_s & COPDocName
    else if number_of_hits > 1 then
    loqqed_Error_Halt3(false, "Found more than one " & COPDocKind_s & " with the name " & COPDocName)
    error "Found more than one " & COPDocKind_s & " with the name " & COPDocName
    else
    tell application "Capture One 12" to set COPDocRef to item 1 of current_doc_ref_list
    end if

    loq_Results2(1, false, ("CO Document: " & COPDocKind_s & " " & COPDocName))
    end validateCOPdoc3

    on validateCOPcollections3()
    ## Copyright 2018 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General purpose initialisation handler for scripts using Capture One Pro
    ## Extract basic information regarding the current collection, and thhe top level collections
    global debugLogLevel, COPDocName, COPDocKind_s, COPDocRef, enableNotifications
    global everyTopCollection, namesTopCollections, kindsTopCollections_p, countTopCollections, selectedCollectionRef, selectedCollectionIndex, kindSelectedCollection_s, nameSelectedCollection
    global selectedCollectionMirroredAtTopLast, selectedCollectionIsUser, bottomUserCollectionIndex, topUserCollectionIndex
    -- selectedCollectionMirroredAtTopLast replaces selectedCollectionAtTopEnd
    -- bottomUserCollectionIndex, topUserCollectionIndex replaces indexInCatalog

    tell application "Capture One 12" to tell COPDocRef
    set selectedCollectionRef to get current collection
    if (missing value = selectedCollectionRef) then
    set current collection to collection 1
    set selectedCollectionRef to get current collection
    end if
    set {nameSelectedCollection, kindSelectedCollection_p} to {name, kind} of selectedCollectionRef
    set everyTopCollection to get every collection
    set {namesTopCollections, kindsTopCollections_p} to {name, kind} of every collection
    end tell
    set kindSelectedCollection_s to convertKind(kindSelectedCollection_p)
    set countTopCollections to count of namesTopCollections

    repeat with collectionCounter from 1 to countTopCollections
    if (nameSelectedCollection = item collectionCounter of namesTopCollections) and (kindSelectedCollection_s = convertKind(item collectionCounter of kindsTopCollections_p)) then
    set selectedCollectionIndex to collectionCounter
    exit repeat
    end if
    end repeat

    if COPDocKind_s = "catalog" then
    repeat with collectionCounter from countTopCollections to 1 by -1
    if ("in Catalog" = item collectionCounter of namesTopCollections) and ("smart album" = convertKind(item collectionCounter of kindsTopCollections_p)) then
    set topUserCollectionIndex to collectionCounter - 1
    exit repeat
    end if
    end repeat
    repeat with collectionCounter from 1 to countTopCollections
    if ("Trash" = item collectionCounter of namesTopCollections) and ("smart album" = convertKind(item collectionCounter of kindsTopCollections_p)) then
    set bottomUserCollectionIndex to collectionCounter + 1
    exit repeat
    end if
    end repeat
    set selectedCollectionMirroredAtTopLast to (selectedCollectionIndex = countTopCollections) and ({"catalog folder", "favorite"} does not contain convertKind(last item of kindsTopCollections_p))
    set selectedCollectionIsUser to (selectedCollectionMirroredAtTopLast or ((selectedCollectionIndex ≥ bottomUserCollectionIndex) and (selectedCollectionIndex ≤ topUserCollectionIndex)))

    else if COPDocKind_s = "session" then
    repeat with collectionCounter from countTopCollections to 1 by -1
    if ("favorite" ≠ convertKind(item collectionCounter of kindsTopCollections_p)) then
    set topUserCollectionIndex to collectionCounter
    exit repeat
    end if
    end repeat
    repeat with collectionCounter from 1 to countTopCollections
    if ("Trash" = item collectionCounter of namesTopCollections) and ("favorite" = convertKind(item collectionCounter of kindsTopCollections_p)) then
    set bottomUserCollectionIndex to collectionCounter + 1
    exit repeat
    end if
    end repeat
    set selectedCollectionMirroredAtTopLast to false
    set selectedCollectionIsUser to ({"project", "album", "group", "smart album"} contains kindSelectedCollection_s)
    end if
    end validateCOPcollections3

    on validateCOPvariant(theVariant, theVariantID)
    ## Copyright 2018 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General Purpose Handler for scripts using Capture One Pro
    ## Capture One identifies a variant as "variant n of collrction m of ..."
    ## If the user changes the order of variant of a collection, "variant n" refers to a different variant, and a running script will behave unpredicatably
    ## The usefulness of this handler depends on some other part of the script obtaining a list of the variant IDs when the script starts
    ## The handler is provided with the variant reference and the original variant ID.
    ## This handler obtains the variant's ID from Capture One, and compares that to the original variant ID. If there is a difference, the script is halted
    ## This handler should be called when processing of a variant starts, and just before any operation that change something of the variant

    global COPisParent, enableNotifications
    if COPisParent then return -- when Capture One is running a script, the GUI is locked ... no need to check

    tell application "Capture One 12" to tell theVariant to if theVariantID = id then return
    loqqed_Error_Halt3(true, "Variant ID Mismatch, most likely due to manipulation of Capture One by the user while the script is running")

    end validateCOPvariant

    on convertKind(kind_p)
    ## Copyright 2018 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General Purpose Handler for scripts using Capture One Pro
    ## Release 11 of Capture One returns the chevron form of the property "kind" when AppleScript is run as an Application
    ## This script converts the chevron form into the expected string
    ## Code looks ugly but executes in under 0.25 msec

    set kind_s to kind_p as text
    if (get text 1 of kind_s) ≠ "«" then return kind_s -- Check if the first character is a Chevron. If not, return the same string

    ## minimum 6 characters for valid logic. Normally 19
    if 6 > length of kind_s then loqqed_Error_Halt3(true, "convertKind received an unexpected Kind string: " & kind_s)

    set code_start to (get length of kind_s) - 4
    set kind_code to get (text code_start thru (code_start + 3) of kind_s)
    set kind_type to get text 1 thru 2 of kind_code
    set fail_flag to false

    if kind_type = "CC" then ## Collection Kinds
    if kind_code = "CCpj" then
    set kind_res to "project"
    else if kind_code = "CCgp" then
    set kind_res to "group"
    else if kind_code = "CCal" then
    set kind_res to "album"
    else if kind_code = "CCsm" then
    set kind_res to "smart album"
    else if kind_code = "CCfv" then
    set kind_res to "favorite"
    else if kind_code = "CCff" then
    set kind_res to "catalog folder"
    else
    set fail_flag to true
    end if

    else if kind_type = "CL" then ## Layer Kinds
    if kind_code = "CLbg" then
    set kind_res to "background"
    else if kind_code = "CLnm" then
    set kind_res to "adjustment"
    else if kind_code = "CLcl" then
    set kind_res to "clone"
    else if kind_code = "CLhl" then
    set kind_res to "heal"
    else
    set fail_flag to true
    end if

    else if kind_type = "CR" then ## Watermark Kinds
    if kind_code = "CRWn" then
    set kind_res to "none"
    else if kind_code = "CRWt" then
    set kind_res to "textual"
    else if kind_code = "CRWi" then
    set kind_res to "imagery"
    else
    set fail_flag to true
    end if

    else if kind_type = "CO" then ## Document Kinds
    if kind_code = "COct" then
    set kind_res to "catalog"
    else if kind_code = "COsd" then
    set kind_res to "session"
    else
    set fail_flag to true
    end if
    else
    set fail_flag to true
    end if

    if fail_flag then loqqed_Error_Halt3(true, "convertKind received an unexpected Kind string: " & kind_s)

    return kind_res
    end convertKind

    on InitializeResultsCollection(nameResultProject, nameResultAlbumRoot, Coll_Init_Text)
    ## Copyright 2018 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General Purpose Handler for scripts using Capture One Pro
    ## Sets up a project and albums for collecting images

    global debugLogLevel, COPDocRef, Ref2ResultAlbum, enableNotifications

    tell application "Capture One 12" to tell COPDocRef
    if not (exists collection named (get nameResultProject)) then
    set ref2ResultProject to make new collection with properties {kind:project, name:nameResultProject}
    else
    if ("project" = my convertKind(kind of (get collection named nameResultProject))) then
    set ref2ResultProject to collection named nameResultProject
    else
    my loqqed_Error_Halt3(true, ("A collection named "" & nameResultProject & "" already exists, and it is not a project."))
    end if
    end if
    end tell

    set coll_ctr to 1
    set nameResultAlbum to nameResultAlbumRoot & "_" & (get short date string of (get current date)) & "_"
    repeat
    tell application "Capture One 12" to tell ref2ResultProject
    if not (exists collection named (get nameResultAlbum & coll_ctr)) then
    set nameResultAlbum to (get nameResultAlbum & coll_ctr)
    set Ref2ResultAlbum to make new collection with properties {kind:album, name:nameResultAlbum}
    exit repeat
    else
    set coll_ctr to coll_ctr + 1
    end if
    end tell
    end repeat

    if enableNotifications then display notification (Coll_Init_Text & " " & nameResultProject & ">" & nameResultAlbum)
    loq_Results2(1, false, (Coll_Init_Text & " " & nameResultProject & ">" & nameResultAlbum))

    end InitializeResultsCollection

    #################
    ## General Handlers Version 2018/08/26

    on settingGUI_Bypass(theSettingList)
    ## Copyright 2018 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General Purpose handler that provides a bypass to settingGUI()

    ####
    ## The Setting record
    ## SettingName Name of the Setting (text)
    ## SettingValue Value of the Setting (text)

    global debugLogLevel, parent_name, Script_Title
    tell application "System Events" to set frontmost of process parent_name to true

    set settings_display_string to ""
    repeat with theSetting_r in theSettingList
    set settings_display_string to settings_display_string & theSetting_r's SettingName
    try
    set theValueString to joinListToString(theSetting_r's SettingValue, ", ")
    if 0 < length of theValueString then set settings_display_string to settings_display_string & ": " & theValueString
    end try
    set settings_display_string to settings_display_string & return
    end repeat

    set debugButtonName to "Configure"
    set legendString to return & "Click "" & debugButtonName & "" to configure settings; "OK" to continue"
    try
    set dialog_result to display dialog settings_display_string & legendString with title "Settings for " & Script_Title ¬
    buttons {"Cancel", "OK", debugButtonName} default button "OK" cancel button "Cancel"
    on error errmess
    my loqqed_Error_Halt3(true, "User Cancelled the Script")
    end try

    return ("OK" = (get button returned of dialog_result))

    end settingGUI_Bypass

    on settingGUI(theSettingList)
    ## Copyright 2018 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General Purpose handler that provides a user interface to control AppleScript settings

    #############
    ## The Setting record
    ## SettingID ID of the setting (integer)
    ## SettingName Name of the Setting (text)
    ## SettingHelp Help Text for the Setting
    ## SettingValue Value or Reference to the Global Variable which holds the option value (reference)
    ## UserSet True if the user can set this variable (boolean)
    ## SettingClass Class of the data representing the option {Boolean, Text, List_Text, Integer, Real} (text)
    ## SettingLimited How are the option values constrained {false, List&Free, List, Min_Max, Min, Max}
    ## SettingLimit_L The list of permissible values of the option (List of Text, List of Integer, List of Real, List of [Min],[Max])

    #############

    global debugLogLevel, parent_name, Script_Title, enableFastGUI

    copy {} to settings_ID_List
    repeat with theSetting_r in theSettingList -- Duplicate setting IDs cause subtle and bizarre errors - catch this problem
    set theSettingID to (get (theSetting_r's SettingID) as text)
    if settings_ID_List contains theSettingID then error "Duplicate Setting ID!! " & theSettingID
    set the end of settings_ID_List to theSettingID
    end repeat

    repeat
    tell application "System Events" to set frontmost of process parent_name to true

    set settings_display_string to ""
    repeat with theSetting_r in theSettingList
    set settings_display_string to settings_display_string & (theSetting_r's SettingName) & ": " & joinListToString((get contents of theSetting_r's SettingValue), ";")
    if theSetting_r's UserSet then set settings_display_string to settings_display_string & " *"
    set settings_display_string to settings_display_string & return
    end repeat

    set legendString to return & "Click "Edit" to edit items with an *asterisk*" & return & "Click "OK" to continue without editing settings"

    try
    set dialog_result to display dialog settings_display_string & legendString with title "Settings for " & Script_Title ¬
    buttons {"Cancel", "OK", "Edit"} default button "OK" cancel button "Cancel"
    on error errmess
    my loqqed_Error_Halt3(true, "User Cancelled the Script")
    end try

    if "OK" = (get button returned of dialog_result) then exit repeat -- Exit 3 of 3 from the handler

    set userCancelled to false
    repeat while userCancelled is false

    copy {} to settings_choose_List
    repeat with theSetting_r in theSettingList
    ##!!!## Do NOT screw with this line, or the list of text editing starts losing information ##!!!##
    if theSetting_r's UserSet then ¬
    copy (get ((theSetting_r's SettingID as text) & ": " & (theSetting_r's SettingName as text) & ": " & joinListToString((contents of theSetting_r's SettingValue), "; "))) to ¬
    the end of settings_choose_List
    end repeat

    set ChooseResult to (get choose from list settings_choose_List with title "Settings for " & Script_Title with prompt "Select an Item to Edit" cancel button name "Done" OK button name "Edit")

    if ChooseResult = false then exit repeat --main exit from this repeat loop

    set selectedSetting to item 1 of ChooseResult
    set selectedSettingID to get ((item 1 of (splitStringToList(selectedSetting, ":"))) as integer)

    set settingFound to false
    repeat with selectedSetting_r in theSettingList
    if selectedSettingID = (get selectedSetting_r's SettingID) then
    set settingFound to true
    exit repeat
    end if
    end repeat

    if not settingFound then
    loqqed_Error_Halt3(false, "Unexpected value found while finding setting: " & selectedSettingID)
    error "SW Error 1"
    end if

    set selectedSettingName to selectedSetting_r's SettingName as text
    set setting_Class to selectedSetting_r's SettingClass as text

    set hasFreeInput to true
    set hasSettingLimitType to false
    set hasSettingMin to false
    set hasSettingMax to false
    set hasSettingLimitList to false
    set theSettingMin to missing value
    set theSettingMax to missing value
    set theSettingLimitList to missing value

    try
    set SettingLimitType to get selectedSetting_r's SettingLimited as text -- this works even if the class is boolean
    set hasSettingLimitType to true
    if false = (get SettingLimitType as boolean) then set hasSettingLimitType to false
    end try

    if hasSettingLimitType then

    if ("Min_Max" = SettingLimitType) then
    set hasFreeInput to true
    set hasSettingMin to true
    set hasSettingMax to true
    set theSettingMin to get item 1 of (get selectedSetting_r's SettingLimit_L as list)
    set theSettingMax to get item 2 of (get selectedSetting_r's SettingLimit_L as list)

    else if ("Min" = SettingLimitType) then
    set hasFreeInput to true
    set hasSettingMin to true
    set theSettingMin to get item 1 of (get selectedSetting_r's SettingLimit_L as list)

    else if ("Max" = SettingLimitType) then
    set hasFreeInput to true
    set hasSettingMax to true
    set theSettingMax to get item 1 of (get selectedSetting_r's SettingLimit_L as list)

    else if ("List" = SettingLimitType) then
    set hasFreeInput to false
    set hasSettingLimitList to true
    set theSettingLimitList to (get selectedSetting_r's SettingLimit_L) as list

    else if ("List&Free" = SettingLimitType) then
    set hasFreeInput to (not enableFastGUI)
    set hasSettingLimitList to true
    set theSettingLimitList to (get selectedSetting_r's SettingLimit_L) as list

    else
    loqqed_Error_Halt3(false, "Unexpected value found while evaluating Setting Limit. Type: " & SettingLimitType & " Length: " & (length of selectedSetting_r's SettingLimit_L))
    error "SW Error 4"
    end if
    end if

    set settingAddTitle to "Enter New Value"
    set settingChooseTitle to "Select New Item"
    set settingDeleteTitle to "Select Item to be removed"
    set settingEditPrompt to "for setting #" & selectedSettingID & ": " & selectedSettingName
    set settingDeletePrompt to "from setting #" & selectedSettingID & ": " & selectedSettingName
    if (not enableFastGUI) and (0 < (count of selectedSetting_r's SettingHelp)) then set settingEditPrompt to ¬
    settingEditPrompt & return & " (" & (get (selectedSetting_r's SettingHelp) as text) & ")"


    set userCancelled to false
    set hasNewSettingValue to false

    if setting_Class = "Boolean" then

    set selectedSettingValue to (selectedSetting_r's SettingValue) as boolean

    if enableFastGUI then
    set the contents of selectedSetting_r's SettingValue to not selectedSettingValue
    else
    set ChooseResult to (get choose from list {"True", "False"} with prompt settingEditPrompt with title settingChooseTitle OK button name "Select" default items {(get selectedSettingValue as text)})
    if ChooseResult = false then
    set userCancelled to true
    else
    set newSettingValue to item 1 of ChooseResult

    if "True" = (get newSettingValue) then
    set the contents of selectedSetting_r's SettingValue to true
    set hasNewSettingValue to true
    else if "False" = (get newSettingValue) then
    set the contents of selectedSetting_r's SettingValue to false
    set hasNewSettingValue to true
    else
    loqqed_Error_Halt3(false, "Unexpected value found while editing Boolean: " & newSettingValue)
    error "SW Error 2"
    end if
    end if
    end if

    else if setting_Class = "Integer" then

    set selectedSettingValue to (get selectedSetting_r's SettingValue) as integer

    if hasSettingLimitList then
    set ChooseResult to (get choose from list theSettingLimitList with prompt settingEditPrompt with title settingChooseTitle OK button name "Select")
    if ChooseResult = false then
    set userCancelled to true
    else
    if (0 ≤ (count of ChooseResult)) then set contents of selectedSetting_r's SettingValue to (get item 1 of ChooseResult) as integer
    set hasNewSettingValue to true
    set selectedSettingValue to (get item 1 of ChooseResult) as integer
    end if
    end if

    if hasFreeInput then
    set settingEditPrompt to settingEditPrompt & return
    if hasSettingMin then set settingEditPrompt to settingEditPrompt & "Minimum: " & (get theSettingMin as integer)
    if hasSettingMax then
    if hasSettingMin then set settingEditPrompt to settingEditPrompt & " "
    set settingEditPrompt to settingEditPrompt & "Maximum: " & (get theSettingMax as integer)
    end if

    repeat while not hasNewSettingValue
    try
    set dialog_result to display dialog settingEditPrompt with title settingAddTitle default answer selectedSettingValue
    on error
    set userCancelled to true
    exit repeat
    end try
    try
    if 0 < (length of text returned of dialog_result) then
    set newSettingValue to get (text returned of dialog_result) as integer
    else
    copy (get selectedSettingValue as integer) to newSettingValue
    end if
    set hasNewSettingValue to true
    on error
    display dialog (settingEditPrompt & return & "Unable to convert " & (get text returned of dialog_result) & " to an integer") with title "Press OK to try again" buttons {"OK"} default button "OK"
    end try

    if hasNewSettingValue then
    if (hasSettingMin and (newSettingValue < theSettingMin)) or (hasSettingMax and (newSettingValue > theSettingMax)) then
    set hasNewSettingValue to false
    display dialog (settingEditPrompt & return & "The value " & newSettingValue & " is below the Min or above the Max") with title "Press OK to try again" buttons {"OK"} default button "OK"
    else
    set contents of selectedSetting_r's SettingValue to newSettingValue
    end if
    end if
    end repeat
    end if

    else if setting_Class = "Real" then

    set selectedSettingValue to (get selectedSetting_r's SettingValue) as real

    if hasSettingLimitList then
    set ChooseResult to (get choose from list theSettingLimitList with prompt settingEditPrompt with title settingChooseTitle OK button name "Select")
    if ChooseResult = false then
    set userCancelled to true
    else
    if (0 ≤ (count of ChooseResult)) then set contents of selectedSetting_r's SettingValue to (get item 1 of ChooseResult) as real
    set hasNewSettingValue to true
    set selectedSettingValue to (get item 1 of ChooseResult) as real
    end if
    end if

    if hasFreeInput then
    set settingEditPrompt to settingEditPrompt & return
    if hasSettingMin then set settingEditPrompt to settingEditPrompt & "Minimum: " & (get theSettingMin as real)
    if hasSettingMax then
    if hasSettingMin then set settingEditPrompt to settingEditPrompt & " "
    set settingEditPrompt to settingEditPrompt & "Maximum: " & (get theSettingMax as real)
    end if

    repeat while not hasNewSettingValue
    try
    set dialog_result to display dialog settingEditPrompt with title settingAddTitle default answer selectedSettingValue
    on error
    set userCancelled to true
    exit repeat
    end try
    try
    if 0 < (length of text returned of dialog_result) then
    set newSettingValue to get (text returned of dialog_result) as real
    else
    copy (get selectedSettingValue as real) to newSettingValue
    end if
    set hasNewSettingValue to true
    on error
    display dialog (settingEditPrompt & return & "Unable to convert " & (get text returned of dialog_result) & " to an real") with title "Press OK to try again" buttons {"OK"} default button "OK"
    end try

    if hasNewSettingValue then
    if (hasSettingMin and (newSettingValue < theSettingMin)) or (hasSettingMax and (newSettingValue > theSettingMax)) then
    set hasNewSettingValue to false
    display dialog (settingEditPrompt & return & "The value " & newSettingValue & " is below the Min or above the Max") with title "Press OK to try again" buttons {"OK"} default button "OK"
    else
    set contents of selectedSetting_r's SettingValue to newSettingValue
    end if
    end if
    end repeat
    end if

    else if setting_Class = "Text" then

    set selectedSettingValue to (get selectedSetting_r's SettingValue) as text

    if hasSettingLimitList then
    set ChooseResult to (get choose from list theSettingLimitList with prompt settingEditPrompt with title settingChooseTitle OK button name "Select")
    if ChooseResult = false then
    set userCancelled to true
    else
    if (0 ≤ (count of ChooseResult)) then
    set contents of selectedSetting_r's SettingValue to (get item 1 of ChooseResult)
    set hasNewSettingValue to true
    set selectedSettingValue to (get item 1 of ChooseResult) as text
    end if
    end if
    end if

    if hasFreeInput then
    try
    set dialog_result to display dialog settingEditPrompt with title settingAddTitle default answer selectedSettingValue
    set contents of selectedSetting_r's SettingValue to get (text returned of dialog_result)
    set hasNewSettingValue to true
    on error
    set userCancelled to true
    end try
    end if

    else if setting_Class = "List_Text" then

    copy ((get contents of selectedSetting_r's SettingValue) as list) to selectedSettingValueList

    if (count of selectedSettingValueList) = 0 then
    set deleteList to {}
    else
    set deleteList to (get choose from list selectedSettingValueList ¬
    with prompt (settingDeletePrompt & return & "(empty selection ok)") ¬
    with title settingDeleteTitle OK button name "Remove" cancel button name "Skip" with multiple selections allowed and empty selection allowed)
    if (deleteList = false) then
    set deleteList to {}
    else
    if (count of deleteList) > 0 then set hasNewSettingValue to true
    end if
    end if

    set newSettingValueList to {}
    repeat with theValueString in selectedSettingValueList
    if deleteList does not contain theValueString then copy (get theValueString as text) to the end of newSettingValueList
    end repeat

    set addedSettingValueList to {}
    if hasSettingLimitList then
    set ChooseResult to (get choose from list theSettingLimitList with prompt settingEditPrompt ¬
    with title settingChooseTitle OK button name "Add" with empty selection allowed and multiple selections allowed)
    if ChooseResult = false then
    set userCancelled to true
    else
    set addedSettingValueList to (get ChooseResult as list)
    end if
    end if

    if hasFreeInput then
    try
    set dialog_result to display dialog settingEditPrompt with title settingAddTitle default answer ""
    set addedSettingValueList to addedSettingValueList & splitStringToList((get text returned of dialog_result), ";")
    on error
    set userCancelled to true
    end try
    end if

    if userCancelled then
    set hasNewSettingValue to false
    else
    repeat with theValueString in addedSettingValueList
    set theValueString to removeLeadingTrailingSpaces(theValueString)
    ## Don't add zero length strings and strings already in the list
    if (0 < (count of theValueString)) and ¬
    newSettingValueList does not contain theValueString then
    copy (get theValueString as text) to the end of newSettingValueList
    set hasNewSettingValue to true
    end if
    end repeat

    if hasNewSettingValue and (newSettingValueList ≠ selectedSettingValueList) then
    set the contents of selectedSetting_r's SettingValue to {}
    copy (get newSettingValueList as list) to contents of selectedSetting_r's SettingValue
    end if
    end if

    else
    loqqed_Error_Halt3(false, "Unexpected class found while editing Boolean: " & setting_Class)
    error "SW Error 3"
    end if
    end repeat
    if enableFastGUI then exit repeat
    end repeat


    set settings_display_string to ""
    repeat with theSetting_r in theSettingList
    set settings_display_string to settings_display_string & (get SettingName of theSetting_r) & ": " & (get contents of (SettingValue of theSetting_r)) & return
    end repeat

    return settings_display_string ## Exit point
    end settingGUI

    on InitializeLoqqing3(DocName_Ext, sourceTitle)
    ## Copyright 2018 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General purpose handler to set up Text Editor document for logging results

    global debugLogLevel, Script_Title, Result_Doc_ref, SE_Logging, enableResultsFile, enableResultsByDialog, enableResultsByClipboard, DialogTextList, enableNotifications
    global initEnableResultsByDialog, initEnableResultsByClipboard, initEnableNotifications

    tell current application to set date_string to (current date) as text
    set LogMethods to {}
    set LogHeader to (sourceTitle & " results on " & date_string)

    if enableResultsFile then
    set end of LogMethods to DocName_Ext
    set targetFileWasCreated to false
    set ResultDocIsOpen to false

    ## Check if TextEdit is already open and has the document open
    tell application "System Events" to set TextEditlist to get background only of every application process whose name is "TextEdit"
    if (0 < (count of TextEditlist)) and not item 1 of TextEditlist then
    if (DocName_Ext is in (get name of documents of application "TextEdit")) then
    tell application "TextEdit" to set Result_Doc_ref to document DocName_Ext
    set ResultDocIsOpen to true
    end if
    end if

    if not ResultDocIsOpen then
    -- create the document and the folder if necessary
    -- Do not use finder to test for the file existence because it has a bug that ignores leading 0's
    -- https://www.macscripter.net/viewtopic.php?id=45178

    set target_folder_parent_a to alias (get path to desktop folder as text)
    set target_folder_parent_p to get POSIX path of target_folder_parent_a
    set target_folder_name to "ScriptReports"
    set target_folder_p to (target_folder_parent_p & target_folder_name)
    set Result_Doc_Path_p to target_folder_p & "/" & DocName_Ext

    try
    set Result_Doc_Path_a to (get alias POSIX file Result_Doc_Path_p)
    on error
    try
    set target_folder_a to (get alias POSIX file target_folder_p) --x1
    on error
    tell application "Finder" to set newFolder to make new folder at target_folder_parent_a with properties {name:target_folder_name}
    set target_folder_a to newFolder as alias
    end try
    tell application "Finder" to set newFile to make new file at target_folder_a with properties {name:DocName_Ext}
    set Result_Doc_Path_a to newFile as alias
    set targetFileWasCreated to true
    end try

    set First_line to ("Created by " & Script_Title & " on " & date_string)
    tell application "TextEdit" -- open the document and add the first line if empty
    activate
    set Result_Doc_ref to open Result_Doc_Path_a
    set ResultDocIsOpen to true -- For consistency
    tell text of Result_Doc_ref
    if targetFileWasCreated then
    set paragraph 1 to First_line & return & return
    tell me to if 2 ≤ debugLogLevel then log Result_Doc_Path_p & ": " & First_line
    else
    try
    if (0 = (count of paragraphs)) then set paragraph 1 to First_line & return & return
    on error
    set paragraph 1 to First_line & return & return
    end try
    end if
    end tell
    end tell
    end if

    tell application "TextEdit" to tell text of Result_Doc_ref to ¬
    set paragraph (1 + (count paragraphs)) to return & LogHeader & return

    end if

    if enableResultsByDialog then
    set end of LogMethods to "Display Dialog"
    try
    initEnableResultsByDialog
    set DialogTextList to DialogTextList & ""
    set initEnableResultsByDialog to false
    on error
    set DialogTextList to {LogHeader}
    set initEnableResultsByDialog to true
    end try
    end if

    if enableResultsByClipboard then
    set end of LogMethods to "Clipboard"
    try
    initEnableResultsByClipboard
    set initEnableResultsByClipboard to false
    on error
    set the clipboard to LogHeader
    set initEnableResultsByClipboard to true
    end try
    end if

    if SE_Logging then -- if Script Editor logging, then open the Log History window
    tell application "System Events" to tell application process "Script Editor"
    if (get name of windows) does not contain "log History" then
    click menu item "Log History" of menu "Window" of menu bar 1
    end if
    end tell
    set end of LogMethods to " Script Editor Log"
    end if

    if enableNotifications then set end of LogMethods to "Notifications"

    set LogMethods_S to joinListToString(LogMethods, ", ")
    loq_Results2(2, false, ("Results by " & LogMethods_S))

    return LogMethods_S
    end InitializeLoqqing3

    on loq_Results2(thisLogDebugLevel, MakeFront, log_Text)
    ## Copyright 2018 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General purpose handler for logging results
    ## log results if the debug level of the message is below the the threshold set by debugLogLevel
    ## log the results by whatever mechanism is ebabled - {Script Editor Log, Text Editor Log, Display Dialog}

    global Result_Doc_ref, debugLogLevel, SE_Logging, parent_name, ResultsFileMaxDebug, enableResultsFile, enableResultsByDialog, enableResultsByClipboard, DialogTextList, enableNotifications
    if thisLogDebugLevel > debugLogLevel then return

    set log_Text_S to joinListToString(log_Text, ", ")

    if enableResultsFile and ((thisLogDebugLevel ≤ ResultsFileMaxDebug) or not SE_Logging) then
    tell application "TextEdit" to tell text of Result_Doc_ref to ¬
    set paragraph (1 + (count paragraphs)) to ((log_Text_S as text) & return)
    tell application "System Events" to if MakeFront then set frontmost of process "TextEdit" to true
    end if

    if enableResultsByDialog and (1 ≥ thisLogDebugLevel) then
    set DialogTextList to DialogTextList & log_Text_S
    tell application "System Events" to set frontmost of process parent_name to true
    display dialog joinListToString(DialogTextList, return)
    end if

    if enableResultsByClipboard and (1 ≥ thisLogDebugLevel) then set the clipboard to ((get the clipboard) & return & (log_Text_S as text))
    if SE_Logging then log (log_Text_S as text)

    end loq_Results2

    on loqqed_Error_Halt3(createErrorHere, error_text)
    ## General purpose handler for logging during script termination
    ##
    ## found an error somewhere, so now we exit in a controlled fashion
    ## set createError to "false" to create a local error instead of here
    global debugLogLevel, Script_Title, enableNotifications
    tell current application to set date_string to (current date) as text
    finalCleanup()
    if enableNotifications then display notification error_text
    if createErrorHere then
    loq_Results2(0, true, ("Script "" & Script_Title & "" has halted at " & date_string & return & "Reason: " & error_text & return & return))
    error error_text
    else
    loq_Results2(0, true, ("Script "" & Script_Title & "" is exitting at " & date_string & "Reason: " & error_text & return))
    end if
    end loqqed_Error_Halt3

    on splitStringToList(theString, theDelim)
    ## Public Domain
    set astid to AppleScript's text item delimiters
    try
    set AppleScript's text item delimiters to theDelim
    set theList to text items of theString
    on error
    set AppleScript's text item delimiters to astid
    end try
    set AppleScript's text item delimiters to astid
    return theList
    end splitStringToList

    to joinListToString(theList, theDelim)
    ## Public Domain
    set theString to ""
    set astid to AppleScript's text item delimiters
    try
    set AppleScript's text item delimiters to theDelim
    set theString to theList as string
    on error
    set AppleScript's text item delimiters to astid
    end try
    set AppleScript's text item delimiters to astid
    return theString
    end joinListToString

    on removeLeadingTrailingSpaces(theString)
    ## Public Domain, modified
    repeat while theString begins with space
    -- When the string is only 1 character long, then it is exactly 1 space, and the next operation willl crash. So return ""
    if 1 ≥ (count of theString) then return ""
    set theString to text 2 thru -1 of theString
    end repeat
    repeat while theString ends with space
    set theString to text 1 thru -2 of theString
    end repeat
    return theString
    end removeLeadingTrailingSpaces

    on getIndexOf(theItem, theList)
    ## credits Emmanuel Levy
    set astid to AppleScript's text item delimiters
    set AppleScript's text item delimiters to return
    set theList to return & theList & return
    set AppleScript's text item delimiters to astid
    try
    -1 + (count (paragraphs of (text 1 thru (offset of (return & theItem & return) in theList) of theList)))
    on error
    0
    end try
    end getIndexOf

    on roundToQuantum(thisValue, quantum)
    ## Public domain author unknown
    return (round (thisValue / quantum) rounding to nearest) * quantum
    end roundToQuantum

    on roundDecimals(n, numDecimals)
    ## Nigel Garvey, Macscripter
    set x to 10 ^ numDecimals
    tell n * x to return (it div 0.5 - it div 1) / x
    end roundDecimals

    on MSduration(firstTicks, lastTicks)
    ## Public domain
    ## returns duration in ms
    ## inputs are durations, in seconds, from GetTick's Now()
    return (round (10000 * (lastTicks - firstTicks)) rounding to nearest) / 10
    end MSduration

    on GetTick_Now()
    ## From MacScripter Author "Jean.O.matiC"
    ## returns duration in seconds since since 00:00 January 2nd, 2000 GMT, calculated using computer ticks
    script GetTick
    property parent : a reference to current application
    use framework "Foundation" --> for more precise timing calculations
    on Now()
    return (current application's NSDate's timeIntervalSinceReferenceDate) as real
    end Now
    end script

    return GetTick's Now()
    end GetTick_Now
    0
  • Eric Valk
    Here is an Applescript that runs over 10X as fast at the previous version. It has a completely different approach.

    It finsishes my catalog with 16000 images and 1200 collections in 33 seconds. I haven't had a chance to add result reporting etc to it yet.

    It will probably not run as an application until I add the "kindconverter" to it.


    use AppleScript version "2.5"
    use scripting additions
    set debugEnable to false

    tell application "Capture One 12" to set thisDocRef to get current document

    log traceDocument(thisDocRef)

    return


    on traceDocument(thisDocRef)
    global theVariantListSize, theVariantList, debugEnable

    local theCollRefList, theMaxID, countVariants, theCollRefList, theCollKindList, theCollUserList, theCollCollRefList, collCtr, thisCollCollList, thisCollRef, thisCollKind, thisIDStringList

    ## The main Collection which is the document.
    ## ignore the remaining inputs

    ## bulk collection of information improves speed
    tell application "Capture One 12" to tell thisDocRef to set {theCollRefList, theCollKindList, theCollUserList, theCollCollRefList} to ¬
    {every collection, every collection's kind, every collection's user, every collection's collection}

    ## The variant list is created, but nothing gets changed here
    tell application "Capture One 12" to tell thisDocRef to tell collection "All Images" to set countVariants to (get count of variants)
    makeVariantList(countVariants)

    repeat with collCtr from 1 to (count of theCollUserList)
    if (get contents of theCollUserList's item collCtr) then
    set thisCollCollList to (get contents of theCollCollRefList's item collCtr) --- a list
    set thisCollRef to (get contents of theCollRefList's item collCtr) -- a CO reference
    tell application "Capture One 12" to tell thisDocRef
    set thisCollKind to (get (get contents of theCollKindList's item collCtr) as text) -- convert a kind to string
    set thisIDStringList to get id of variants of thisCollRef
    end tell
    traceCollection(thisCollRef, thisCollKind, thisCollCollList, thisIDStringList)
    end if
    end repeat

    local everyVariantIDList, notFoundIdList, notFoundIndexList, notFoundNameList, vctr, theVariantID, theVariantNameList, anIndex
    tell application "Capture One 12" to tell thisDocRef to tell collection "All Images" to set everyVariantIDList to get id of variants
    set notFoundIndexList to {}
    repeat with vctr from 1 to countVariants
    if not (get theVariantList's item (get (contents of everyVariantIDList's item vctr) as integer)) then copy (contents of vctr) to end of notFoundIndexList
    end repeat
    set everyVariantIDList to {} -- control AppleScript's memory utilization
    set theVariantList to {} -- control AppleScript's memory utilization
    set theVariantListSize to 0
    set notFoundNameList to {}
    if 0 < (count of notFoundIndexList) then
    tell application "Capture One 12" to tell thisDocRef to tell collection "All Images" to tell variants to set {theVariantNameList, theVariantExtList} to {name of it, extension of parent image of it}
    repeat with anIndex in notFoundIndexList
    copy ((theVariantNameList's item (get contents of anIndex)) & "." & (theVariantExtList's item (get contents of anIndex))) to end of notFoundNameList
    end repeat
    end if

    return notFoundNameList
    end traceDocument

    on traceCollection(ThisRef, theCollKind, theCollRefList, theIDStringList)
    global theVariantListSize, theVariantList, debugEnable

    ## index the images if the collection holds images
    local anIDString, theVariantID
    tell application "Capture One 12" to tell ThisRef to if (0 < (get count of variants)) then
    repeat with anIDString in theIDStringList
    set theVariantID to (get contents of anIDString as integer)
    if theVariantID > theVariantListSize then makeVariantList(theVariantID)
    set theVariantList's item theVariantID to true
    end repeat
    end if

    if (0 = (get count of theCollRefList)) or ("project" = theCollKind) then return -- don't search below this collection if it is a project, or has no collections
    ## bulk collection of information improves speed
    tell application "Capture One 12" to tell ThisRef to set {theCollCollRefList, theCollKindList, theCollVariantIDList} to get {collection, kind, id of variant} of every collection

    local collCtr
    repeat with collCtr from 1 to (get count of theCollRefList)
    traceCollection((get contents of theCollRefList's item collCtr), (get (contents of theCollKindList's item collCtr) as text), (get contents of theCollCollRefList's item collCtr), (get contents of theCollVariantIDList's item collCtr))
    end repeat
    end traceCollection

    on makeVariantList(newImageListSize)
    global theVariantListSize, theVariantList, debugEnable
    try
    theVariantList
    on error
    set theVariantList to {}
    set theVariantListSize to 0
    end try
    if newImageListSize > theVariantListSize then
    set theVariantList to theVariantList & makeList3((newImageListSize - theVariantListSize + 24), false) -- make some extras to avoid calling this repeatedly
    set theVariantListSize to get count of theVariantList
    end if
    end makeVariantList

    on makeList3(listLength, theElement)
    -- Note that the theElement can even be a List
    if listLength = 0 then return {}
    if listLength = 1 then return {theElement}

    set theList to {theElement}
    repeat while (count of theList) < listLength / 2
    copy contents of theList to ListB
    copy theList & ListB to theList
    end repeat
    copy contents of theList to ListB
    return (theList & items 1 thru (listLength - (count of ListB)) of ListB)
    end makeList3
    0
  • Eric Valk
    Here is an update that will run as an application and can be added to Capture One's Script Menu.
    I generates notifications and copies the results to the clipboard.

    DO NOT USE See posting below for a better version

    use AppleScript version "2.5"
    use scripting additions
    set debugEnable to false

    tell application "Capture One 12" to set thisDocRef to get current document

    set notFoundNameList to searchDocument(thisDocRef)
    set countNotFound to (count of notFoundNameList)

    if 0 < countNotFound then
    set displayString to (get countNotFound as text) & " Images not found in a User Collection: " & joinListToString(notFoundNameList, ",")
    else
    set displayString to "All Images found in a User Collection"
    end if

    set the clipboard to displayString
    display notification displayString

    return


    on searchDocument(thisDocRef)
    ## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## Seach each user collection, and scheck the results
    global theImageListSize, theImageList, debugEnable
    local theCollRefList, countImages, theCollRefList, theCollKindList, theCollUserList, theCollCollRefList, thisCollCollList, theDocName

    ## bulk collection of information improves speed
    ## Do not collect Image ID lists as this will include All Images, Recent Imports and Catalog Folders (every image 3x or 4x)
    tell application "Capture One 12" to tell thisDocRef to ¬
    set {theCollRefList, theCollUserList, theCollCollRefList, theCollKindList} to ¬
    {every collection, every collection's user, every collection's collection, my convertKindList(every collection's kind)}

    ## The image list is created, but nothing gets changed here
    tell application "Capture One 12" to tell thisDocRef to tell collection "All Images" to set countImages to (get count of images)
    set {theImageList, theImageListSize} to {{}, 0} -- initialise and clear the Image list
    makeImageList(countImages)

    tell application "Capture One 12" to tell thisDocRef to set theDocName to its name
    display notification "Finding Images not in a user collection: " & countImages & " images of " & theDocName

    local thisCollRef, thisCollImageIDList, collCtr
    repeat with collCtr from 1 to (count of theCollUserList)
    if (get contents of theCollUserList's item collCtr) then
    set thisCollRef to (get contents of theCollRefList's item collCtr) -- a CO reference
    tell application "Capture One 12" to tell thisDocRef to set thisCollImageIDList to get id of images of thisCollRef
    searchCollection(thisCollRef, (theCollKindList's item collCtr), (theCollCollRefList's item collCtr), thisCollImageIDList)
    end if
    end repeat

    display notification "Half done ..."

    local everyImageIDList, notFoundIdList, notFoundIndexList, notFoundNameList, vctr, theImageID, theImageNameList, anIndex
    tell application "Capture One 12" to tell thisDocRef to tell collection "All Images" to set {everyImageIDList, everyImageNameList} to {id of images, name of images}
    set notFoundNameList to {}
    repeat with vctr from 1 to countImages
    if not (get theImageList's item (get (everyImageIDList's item vctr) as integer)) then ¬
    copy (everyImageNameList's item vctr) to end of notFoundNameList
    end repeat

    set {theImageList, theImageListSize} to {{}, 0} -- clear large lists to control AppleScript's memory utilization

    return notFoundNameList
    end searchDocument

    on searchCollection(ThisRef, theCollKind, theCollRefList, theIDStringList)
    ## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    global theImageListSize, theImageList, debugEnable

    ## index the images if the collection holds images
    local anIDString, theImageID
    tell application "Capture One 12" to tell ThisRef to if (0 < (get count of images)) then
    repeat with anIDString in theIDStringList
    set theImageID to (get anIDString as integer)
    if theImageID > theImageListSize then makeImageList(theImageID)
    set theImageList's item theImageID to true
    end repeat
    end if

    if (0 = (get count of theCollRefList)) or ("project" = theCollKind) then return -- don't search below this collection if it is a project, or has no collections
    ## bulk collection of information improves speed
    ## This handler only handles user collections, so every subcollection is also a user collection
    tell application "Capture One 12" to tell ThisRef to set {theCollCollRefList, theCollImageIDList, theCollKindList} to ¬
    {every collection's collection, every collection's image's id, my convertKindList(every collection's kind)}

    local collCtr
    repeat with collCtr from 1 to (get count of theCollRefList)
    searchCollection((theCollRefList's item collCtr), (theCollKindList's item collCtr), (theCollCollRefList's item collCtr), (theCollImageIDList's item collCtr))
    end repeat
    end searchCollection

    on makeImageList(newImageListSize)
    global theImageListSize, theImageList, debugEnable
    ## make some extras to avoid calling this repeatedly
    set newImageListSize to newImageListSize + 24
    if 0 < theImageListSize then
    if newImageListSize > theImageListSize then set theImageList to theImageList & makeList3((newImageListSize - theImageListSize), false)
    else
    set theImageList to makeList3(newImageListSize, false)
    end if
    set theImageListSize to get count of theImageList
    end makeImageList

    on makeList3(listLength, theElement)
    -- Note that the theElement can even be a List
    if listLength = 0 then return {}
    if listLength = 1 then return {theElement}

    set theList to {theElement}
    repeat while (count of theList) < listLength / 2
    copy contents of theList to ListB
    copy theList & ListB to theList
    end repeat
    copy contents of theList to ListB
    return (theList & items 1 thru (listLength - (count of ListB)) of ListB)
    end makeList3

    on joinListToString(theList, theDelim)
    ## Public Domain
    set theString to ""
    set astid to AppleScript's text item delimiters
    try
    set AppleScript's text item delimiters to theDelim
    set theString to theList as string
    on error
    set AppleScript's text item delimiters to astid
    end try
    set AppleScript's text item delimiters to astid
    return theString
    end joinListToString

    on convertKindList(kind_list)
    ## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General Purpose Handler for scripts using Capture One Pro
    ## Capture One returns the chevron form of the "kind" property when AppleScript is run as an Application
    ## Unless care is taken to avoid text conversion of this property, this bug breaks script decisions based on "kind"
    ## This script converts text strings with the chevron form to strings with the expected text form
    ## The input may be a single string, a single enum, a list of strings or a list of enums
    ## The code is not compact but runs very fast, between 60us and 210us per item

    set kind_s_list to {}
    set input_is_list to ("list" = (get (class of kind_list) as text))
    if input_is_list then
    if ("text" = (get (class of item 1 of kind_list) as text)) and ¬
    ("«" ≠ (get text 1 of item 1 of kind_list)) then return kind_list -- quick pass through if first item is OK
    repeat with theItem in kind_list
    tell application "Capture One 12" to set the end of kind_s_list to (get theItem as text)
    end repeat
    if ("«" ≠ (get text 1 of item 1 of kind_s_list)) then return kind_s_list
    else
    if ("text" = (get (class of kind_list) as text)) and ¬
    ("«" ≠ (get text 1 of kind_list)) then return kind_list -- quick pass through if first item is OK
    tell application "Capture One 12" to set kind_s1 to (get kind_list as text)
    if "«" ≠ (get text 1 of kind_s1) then return kind_s1 -- quick pass through if input is OK
    set kind_s_list to {kind_s1}
    end if

    set fail_flag to false
    set code_start to -5

    set kind_list to {}
    repeat with Kind_s_item in kind_s_list
    if "«" ≠ (get text 1 of Kind_s_item) then loqqed_Error_Halt3(true, "convertKindList received an unexpected Kind string: " & Kind_s_item)
    set kind_code to get (text code_start thru (code_start + 3) of Kind_s_item)
    set kind_type to get (text code_start thru (code_start + 1) of Kind_s_item)

    if kind_type = "CC" then ## Collection Kinds
    if kind_code = "CCpj" then
    set the end of kind_list to "project"
    else if kind_code = "CCgp" then
    set the end of kind_list to "group"
    else if kind_code = "CCal" then
    set the end of kind_list to "album"
    else if kind_code = "CCsm" then
    set the end of kind_list to "smart album"
    else if kind_code = "CCfv" then
    set the end of kind_list to "favorite"
    else if kind_code = "CCff" then
    set the end of kind_list to "catalog folder"
    else
    set fail_flag to true
    end if

    else if kind_type = "CL" then ## Layer Kinds
    if kind_code = "CLbg" then
    set the end of kind_list to "background"
    else if kind_code = "CLnm" then
    set the end of kind_list to "adjustment"
    else if kind_code = "CLcl" then
    set the end of kind_list to "clone"
    else if kind_code = "CLhl" then
    set the end of kind_list to "heal"
    else
    set fail_flag to true
    end if

    else if kind_type = "CR" then ## Watermark Kinds
    if kind_code = "CRWn" then
    set the end of kind_list to "none"
    else if kind_code = "CRWt" then
    set the end of kind_list to "textual"
    else if kind_code = "CRWi" then
    set the end of kind_list to "imagery"
    else
    set fail_flag to true
    end if

    else if kind_type = "CO" then ## Document Kinds
    if kind_code = "COct" then
    set the end of kind_list to "catalog"
    else if kind_code = "COsd" then
    set the end of kind_list to "session"
    else
    set fail_flag to true
    end if
    else
    set fail_flag to true
    end if

    if fail_flag then loqqed_Error_Halt3(true, "convertKindList received an unexpected Kind string: " & Kind_s_item)
    end repeat

    if input_is_list then
    return kind_list
    else
    return item 1 of kind_list
    end if

    end convertKindList

    0
  • Tim Bowman
    Great work, guys.
    0
  • Tim Bowman
    Im using it.
    0
  • Eric Valk
    The script in my previous posting above has a few bugs in it.

    I have updated the script to remove the bugs, and also allowed it to run with Capture One 20.

    I have tested it with Capture One 20.02 on OSX 10.14.6 by running it from Script Editor.

    If you have successes or failures running the script, please post here.

    We will also add it to the Capture One Github respsoitory as we get that going.

    Note: Its not necessary to manually change the version of CaptureOne 20 in the Tell Application Statements to run the this script under CaptureOne 12 or any other version. When you laod the script into Script Editor, and some version of Capture One is running, the version is automatically updated. There are no text strings like "Capture One 20" in the script that wouuld require changing.

    I plan to make a further update so that the found images can be put into a special user collection, add a progress bar, make it operate more easily with future versions, and add the latest version of my logging utilities.

    But here is a version with bugs removed, for now.
    [color=#FF0080:1v2pvj0n]Updated to version 12.07[/color:1v2pvj0n]
    - added code to exclude the Project "ScriptSearchResults", and any other User Collections the user may add.


    ## Applescript to search a Capture One Catalog for variants not in a User Collection
    ## Version 1.12.07 NOTICE Best effort Support, no warranty
    ## Copyright 2020 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ##
    ## [1.12.07] added code to exclude the Project "ScriptSearchResults"
    ## [1.12.07] added global variable "ExcludedCollectionNames" to allow the user to add more exluded user collections

    -- ***To Setup
    -- Start Script Editor, open a new (blank) file, copy and paste both parts into one Script Editor Document, compile (hammer symbol) and save.
    -- Best if you make "Scripts" folder somewhere in your Documents or Desktop
    -- This file is suitable to use as an application in Capture One Pro's Script Menu

    -- *** Operation in Script Editor
    -- Open the compiled and saved file
    -- Open the Script Editor log window, and select the messages tab
    -- Select only a small number of variants, from any collection

    -- Run the script
    -- Results can appear in Notifications, the Script Editor Log and the Text Edit document when the search for each variant is completed, and in the Clipboard and Display Dialog Window when all searching is completed
    --
    -- The user may elect to set defaults for enabling or disabling results in Notifications, TextEdit and Script Editor by setting the "enable" variables at beginning of the script
    -- The user may change the default amount of reporting by setting the "debugLogLevel" and "ResultsFileMaxDebug" variables at beginning of the script
    -- If you are having some issues, then set debugLogLevel to 3 and send me the results from Script Editors log window, or Text Edit.

    ## Values in this section are safe to change, within limits indicated. Support is likely but no commitment


    use AppleScript version "2.5"
    use scripting additions

    set debugLogLevel to 0 -- 1...6 Set to 1 to allow changing other settings. Increasing Values result in increasing amounts of debug data that takes longer to report
    set enableResultsFile to true -- Enables Results in Textedit file (true/false)
    set enableResultsByDialog to false -- (true/false)
    set enableResultsByClipboard to false -- (true/false)
    set enableNotifications to false -- (true/false)
    set ResultsFileMaxDebug to 3 -- 1...6
    set maxProgressLevel to 3 -- Levels shown in Script Editor's progress bar 1...6
    set maxSearchlevel to 100 -- Reduce if you only want to search top level collections or if you start getting stack overflow messages Range 1....... Verified to 100
    set enableFastGUI to true -- (true/false)
    set ExcludedCollectionNames to {} -- Collections in this list will not be searched, nor will their collections


    ## ***** Not safe to change stuff below this line, unless you have some background in SW development.
    ## I generally won't help much if you change stuff below this line. I may explain the design intent.

    tell application "Finder" to get count of windows
    tell application "TextEdit" to get count of windows
    tell application "System Events" to set parent_name to name of current application

    set script_path to (path to me) as text
    set astid to AppleScript's text item delimiters
    try
    set AppleScript's text item delimiters to ":"
    if (script_path ends with ":") then
    set Script_Title to text item -2 of script_path
    else
    set Script_Title to text item -1 of script_path
    end if
    end try
    set AppleScript's text item delimiters to astid
    --set Script_Title to (get name of me)

    set SE_Parent to (parent_name = "Script Editor")
    set SE_Logging to SE_Parent as boolean
    set COPisParent to (get parent_name begins with "Capture One") -- version independent
    set Result_DocName to "CO_Image_Search.txt"
    set Result_ProjectName to "ScriptSearchResults"


    set ResultMethod to my InitializeLoqqing3(Result_DocName, Script_Title)
    loq_Results2(0, false, ("Started from: " & parent_name & " Action: Find Images not in a User Collection"))

    set minCOPversion to "12"
    set maxCOPversion to "21"
    validateCOP2(minCOPversion, maxCOPversion)
    validateCOPdoc3({"catalog", "session"})

    tell application "Capture One 20" to set thisDocRef to get current document

    set notFoundNameList to searchDocument(thisDocRef)
    set countNotFound to (count of notFoundNameList)

    if 0 < countNotFound then
    set displayString to (get countNotFound as text) & " Images not found in a User Collection: " & joinListToString(notFoundNameList, ", ")
    else
    set displayString to "All Images found in a User Collection"
    end if

    loq_Results2(0, true, displayString)

    return

    ## Script Specific Handlers ########

    on searchDocument(thisDocRef)
    ## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## Seach each user collection, and scheck the results
    global theImageListSize, theImageList, debugLogLevel, ExcludedCollectionNames
    local theCollRefList, countImages, theCollKindList, theCollCollRefList, thisCollCollList, theDocName, theCollNameList

    set ExcludedCollectionNames to ExcludedCollectionNames & {"ScriptSearchResults"}

    ## bulk collection of information improves speed

    tell application "Capture One 20" to tell thisDocRef to tell (every collection whose user is true) to ¬
    set {theCollRefList, theCollCollRefList, theCollKindList, theCollNameList} to ¬
    {it, its collection, my convertKindList(its kind), its name}

    ## The image list is created, but nothing gets changed here
    tell application "Capture One 20" to tell thisDocRef to tell collection "All Images" to set countImages to (get count of images)
    set {theImageList, theImageListSize} to {{}, 0} -- initialise and clear the Image list
    makeImageList(countImages)

    tell application "Capture One 20" to tell thisDocRef to set theDocName to its name
    display notification "Finding Images not in a user collection: " & countImages & " images of " & theDocName

    display notification "Checking each collection"

    local thisCollRef, thisCollImageIDList, collCtr
    repeat with collCtr from 1 to (count of theCollRefList)
    set thisCollRef to (get contents of theCollRefList's item collCtr) -- a CO reference
    tell application "Capture One 20" to tell thisDocRef to set thisCollImageIDList to get id of images of thisCollRef
    indexCollection(thisCollRef, (theCollKindList's item collCtr), (theCollCollRefList's item collCtr), thisCollImageIDList, (theCollNameList's item collCtr))
    end repeat

    display notification "Done Checking Collections, Now the analysis .."

    local everyImageIDList, notFoundIdList, notFoundIndexList, notFoundNameList, vctr, theImageID, theImageNameList, anIndex
    tell application "Capture One 20" to tell thisDocRef to tell collection "All Images" to set {everyImageIDList, everyImageNameList} to {id of images, name of images}
    set notFoundNameList to {}
    repeat with vctr from 1 to countImages
    set thisImageID to (get (everyImageIDList's item vctr) as integer)
    if thisImageID > theImageListSize then
    copy (everyImageNameList's item vctr) to end of notFoundNameList
    else
    if not (get theImageList's item thisImageID) then ¬
    copy (everyImageNameList's item vctr) to end of notFoundNameList
    end if
    end repeat

    set {theImageList, everyImageNameList, everyImageIDList} to {null, null, null} -- clear large lists to control AppleScript's memory utilization

    return notFoundNameList
    end searchDocument

    on indexCollection(ThisRef, theCollKind, theCollRefList, theIDStringList, thisCollName)
    ## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    global theImageListSize, theImageList, debugLogLevel, ExcludedCollectionNames

    ## index the images if the collection holds images
    local anIDString, theImageID
    if ExcludedCollectionNames contains thisCollName then return
    tell application "Capture One 20" to tell ThisRef to if (0 < (get count of images)) then
    repeat with anIDString in theIDStringList
    set theImageID to (get anIDString as integer)
    if theImageID > theImageListSize then my makeImageList(theImageID)
    set theImageList's item theImageID to true
    end repeat
    end if

    local theCollCollRefList, theCollImageIDList, theCollKindList, theCollNameList
    if (0 = (get count of theCollRefList)) or ("project" = theCollKind) then return -- don't search below this collection if it is a project, or has no subcollections
    ## This handler only handles user collections, no need to check subcollections
    tell application "Capture One 20" to tell ThisRef to set {theCollCollRefList, theCollImageIDList, theCollKindList, theCollNameList} to ¬
    {every collection's collection, every collection's image's id, my convertKindList(every collection's kind), every collection's name}

    local collCtr
    repeat with collCtr from 1 to (get count of theCollRefList)
    indexCollection((theCollRefList's item collCtr), (theCollKindList's item collCtr), (theCollCollRefList's item collCtr), (theCollImageIDList's item collCtr), (theCollNameList's item collCtr))
    end repeat
    return
    end indexCollection

    on makeImageList(newImageListSize)
    global theImageListSize, theImageList, debugLogLevel
    ## make some extras to avoid calling this repeatedly

    set newImageListSize to newImageListSize + 24
    if 0 < theImageListSize then
    if newImageListSize > theImageListSize then
    set theImageList to theImageList & makeList3((newImageListSize - theImageListSize), false)
    if debugLogLevel ≥ 2 then log "Increased image list from " & theImageListSize & " to " & newImageListSize
    end if
    else
    set theImageList to makeList3(newImageListSize, false)
    end if
    set theImageListSize to get count of theImageList
    end makeImageList

    on finalCleanup()
    global theImageList, everyImageNameList, everyImageIDList
    set {theImageList, everyImageNameList, everyImageIDList} to {null, null, null}
    end finalCleanup

    ###########################################################################################################################
    ## Capture One General Handlers Version 2018/12/16

    on validateCOP2(minCOPversionstr, maxCOPversionstr)
    ## Copyright 2018 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General purpose initialisation handler for scripts using Capture One Pro
    ## Extract and check basic information about the Capture One application

    global debugLogLevel, theAppName, copVersion, copDetailedVersion, enableNotifications
    tell application "System Events"
    set COPProcList to every process whose name begins with "Capture One" and background only is false
    if debugLogLevel ≥ 2 then
    set COPProcNameList to name of every process whose name begins with "Capture One" and background only is false
    my loq_Results2(2, false, ("COP Processes:" & COPProcNameList))
    end if
    end tell
    if (count of COPProcList) = 0 then my loqqed_Error_Halt3(true, "COP is not running")
    if (count of COPProcList) ≠ 1 then my loqqed_Error_Halt3(true, "Unexpected: >1 COP instances")
    set theAppRef to item 1 of COPProcList
    tell application "System Events" to set theAppName to ((get name of theAppRef) as text)
    tell application "System Events" to set copDetailedVersion to get version of my application theAppName

    tell application "Capture One 20" to set copVersion to (get app version)

    if debugLogLevel ≥ 2 then
    tell application "System Events"
    my loq_Results2(2, false, ("All Processes: " & (get my joinListToString((get name of every process whose background only is false), ", "))))
    end tell
    loq_Results2(2, false, ("theAppName: " & theAppName))
    loq_Results2(2, false, ("COP Version: " & copVersion))
    loq_Results2(2, false, ("COP Detailed Version: " & copDetailedVersion))
    end if

    set numCOPversion to (splitstringtolist((word -1 of copVersion), "."))
    set minCOPversion to (splitstringtolist(minCOPversionstr, "."))
    set maxCOPversion to (splitstringtolist(maxCOPversionstr, "."))

    set digit_mult to 1000000
    set Version_digit to 0
    repeat with dig_ctr from 1 to count of numCOPversion
    set digit_mult to digit_mult / 100
    set Version_digit to Version_digit + (get item dig_ctr of numCOPversion as integer) * digit_mult
    end repeat

    set digit_mult to 1000000
    set min_digit to 0
    repeat with dig_ctr from 1 to count of minCOPversion
    set digit_mult to digit_mult / 100
    set min_digit to min_digit + (get item dig_ctr of minCOPversion as integer) * digit_mult
    end repeat

    set digit_mult to 1000000
    set max_digit to 0
    set digit_count to count of maxCOPversion
    repeat with dig_ctr from 1 to digit_count
    set digit_mult to digit_mult / 100
    set max_digit to max_digit + (get item dig_ctr of maxCOPversion as integer) * digit_mult
    if (dig_ctr = digit_count) then set max_digit to max_digit + digit_mult
    end repeat

    if (Version_digit < min_digit) or (Version_digit ≥ max_digit) then
    if enableNotifications then display notification "COP Version is unsupported"
    my loqqed_Error_Halt3(true, "This COP Version is " & copDetailedVersion & " - the supported COP versions are from " & minCOPversionstr & " to " & maxCOPversionstr)
    end if

    tell application "System Events" to set frontmost of process theAppName to true
    loq_Results2(1, false, ("Capture One version: " & copDetailedVersion))
    end validateCOP2

    on validateCOPdoc3(COP_kind_list)
    ## Copyright 2018 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General purpose initialisation handler for scripts using Capture One Pro
    ## Extract and check basic information about the current document

    global debugLogLevel, COPDocName, COPDocKind_s, COPDocRef, theAppName

    try
    tell application "Capture One 20" to set COPDocName to get name of current document
    on error
    loqqed_Error_Halt3(true, "The Script could not retrieve the Capture One document - Perhaps a Capture One Dialog window is open?")
    end try

    tell application "Capture One 20"
    set current_doc_kind_p to (get kind of current document)
    set current_doc_ref_list to (get every document whose name is COPDocName and kind is current_doc_kind_p)
    set number_of_hits to count of current_doc_ref_list
    end tell
    set COPDocKind_s to convertKindList(current_doc_kind_p)
    if COPDocKind_s = "session" then set COPDocName to text 1 thru ((offset of "." in COPDocName) - 1) of COPDocName

    loq_Results2(2, false, ("Found Documents: " & number_of_hits))

    if COP_kind_list does not contain COPDocKind_s then loq_Results2(0, false, (COPDocName & " is a " & COPDocKind_s & " -- unsupported type of document"))

    if number_of_hits = 0 then
    loqqed_Error_Halt3(false, "Could not find find " & COPDocKind_s & COPDocName)
    error "Could not find find " & COPDocKind_s & COPDocName
    else if number_of_hits > 1 then
    loqqed_Error_Halt3(false, "Found more than one " & COPDocKind_s & " with the name " & COPDocName)
    error "Found more than one " & COPDocKind_s & " with the name " & COPDocName
    else
    tell application "Capture One 20" to set COPDocRef to item 1 of current_doc_ref_list
    end if

    loq_Results2(1, false, ("CO Document: " & COPDocKind_s & " " & COPDocName))
    end validateCOPdoc3

    on validateCOPcollections3()
    ## Copyright 2018 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General purpose initialisation handler for scripts using Capture One Pro
    ## Extract basic information regarding the current collection, and thhe top level collections
    global debugLogLevel, COPDocName, COPDocKind_s, COPDocRef, enableNotifications
    global everyTopCollection, namesTopCollections, kindsTopCollections_s, countTopCollections, selectedCollectionRef, selectedCollectionIndex, kindSelectedCollection_s, nameSelectedCollection
    global selectedCollectionMirroredAtTopLast, selectedCollectionIsUser, bottomUserCollectionIndex, topUserCollectionIndex
    -- selectedCollectionMirroredAtTopLast replaces selectedCollectionAtTopEnd
    -- bottomUserCollectionIndex, topUserCollectionIndex replaces indexInCatalog

    tell application "Capture One 20" to tell COPDocRef
    set selectedCollectionRef to get current collection
    if (missing value = selectedCollectionRef) then
    set current collection to collection 1
    set selectedCollectionRef to get current collection
    end if
    set nameSelectedCollection to name of selectedCollectionRef
    set kindSelectedCollection_s to my convertKindList(kind of selectedCollectionRef)
    set {everyTopCollection, namesTopCollections} to {it, name} of every collection
    set kindsTopCollections_s to my convertKindList(kind of every collection)
    end tell
    set countTopCollections to count of namesTopCollections

    repeat with collectionCounter from 1 to countTopCollections
    if (nameSelectedCollection = item collectionCounter of namesTopCollections) and ¬
    (kindSelectedCollection_s = item collectionCounter of kindsTopCollections_s) then
    set selectedCollectionIndex to collectionCounter
    exit repeat
    end if
    end repeat

    if COPDocKind_s = "catalog" then
    repeat with collectionCounter from countTopCollections to 1 by -1
    if ("in Catalog" = item collectionCounter of namesTopCollections) and ¬
    ("smart album" = item collectionCounter of kindsTopCollections_s) then
    set topUserCollectionIndex to collectionCounter - 1
    exit repeat
    end if
    end repeat
    repeat with collectionCounter from 1 to countTopCollections
    if ("Trash" = item collectionCounter of namesTopCollections) and ¬
    ("smart album" = item collectionCounter of kindsTopCollections_s) then
    set bottomUserCollectionIndex to collectionCounter + 1
    exit repeat
    end if
    end repeat
    set selectedCollectionMirroredAtTopLast to ¬
    (selectedCollectionIndex = countTopCollections) and ¬
    ({"catalog folder", "favorite"} does not contain last item of kindsTopCollections_s)

    set selectedCollectionIsUser to ¬
    (selectedCollectionMirroredAtTopLast or ((selectedCollectionIndex ≥ bottomUserCollectionIndex) and ¬
    (selectedCollectionIndex ≤ topUserCollectionIndex)))

    else if COPDocKind_s = "session" then
    repeat with collectionCounter from countTopCollections to 1 by -1
    if ("favorite" ≠ item collectionCounter of kindsTopCollections_s) then
    set topUserCollectionIndex to collectionCounter
    exit repeat
    end if
    end repeat
    repeat with collectionCounter from 1 to countTopCollections
    if ("Trash" = item collectionCounter of namesTopCollections) and ¬
    ("favorite" = item collectionCounter of kindsTopCollections_s) then
    set bottomUserCollectionIndex to collectionCounter + 1
    exit repeat
    end if
    end repeat
    set selectedCollectionMirroredAtTopLast to false
    set selectedCollectionIsUser to ({"project", "album", "group", "smart album"} contains kindSelectedCollection_s)
    end if
    end validateCOPcollections3

    on convertKindList(kind_list)
    ## Copyright 2018 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General Purpose Handler for scripts using Capture One Pro
    ## Many releases of Capture One return the chevron form of the property "kind" when AppleScript is run as an Application
    ## This script converts a list of kind enums to text, handling the chevron form correctly
    ## Assume that the list either contains the plain text enums or the chevron form enums, but not both
    ## Assume that the list contains the kind enums from the same class

    if "list" = (get (class of kind_list) as text) then
    set input_is_list to true
    else
    set kind_list to {kind_list}
    set input_is_list to false
    end if
    set kind_s1 to (item 1 of kind_list) as text

    set kind_s_list to {}
    set fail_flag to false

    if "«" ≠ (get text 1 of kind_s1) then
    repeat with Kind_item in kind_list
    set the end of kind_s_list to (get Kind_item as text)
    end repeat
    else
    ## minimum 6 characters for valid logic. Normally 19
    if 6 > length of kind_s1 then loqqed_Error_Halt3(true, "convertKind received an unexpected Kind string: " & kind_s1)
    set code_start to (get length of kind_s1) - 4
    set kind_type to get (text code_start thru (code_start + 1) of kind_s1)

    repeat with Kind_item in kind_list
    set kind_s to Kind_item as text
    set kind_code to get (text code_start thru (code_start + 3) of kind_s)

    if kind_type = "CC" then ## Collection Kinds
    if kind_code = "CCpj" then
    set the end of kind_s_list to "project"
    else if kind_code = "CCgp" then
    set the end of kind_s_list to "group"
    else if kind_code = "CCal" then
    set the end of kind_s_list to "album"
    else if kind_code = "CCsm" then
    set the end of kind_s_list to "smart album"
    else if kind_code = "CCfv" then
    set the end of kind_s_list to "favorite"
    else if kind_code = "CCff" then
    set the end of kind_s_list to "catalog folder"
    else
    set fail_flag to true
    end if

    else if kind_type = "CL" then ## Layer Kinds
    if kind_code = "CLbg" then
    set the end of kind_s_list to "background"
    else if kind_code = "CLnm" then
    set the end of kind_s_list to "adjustment"
    else if kind_code = "CLcl" then
    set the end of kind_s_list to "clone"
    else if kind_code = "CLhl" then
    set the end of kind_s_list to "heal"
    else
    set fail_flag to true
    end if

    else if kind_type = "CR" then ## Watermark Kinds
    if kind_code = "CRWn" then
    set the end of kind_s_list to "none"
    else if kind_code = "CRWt" then
    set the end of kind_s_list to "textual"
    else if kind_code = "CRWi" then
    set the end of kind_s_list to "imagery"
    else
    set fail_flag to true
    end if

    else if kind_type = "CO" then ## Document Kinds
    if kind_code = "COct" then
    set the end of kind_s_list to "catalog"
    else if kind_code = "COsd" then
    set the end of kind_s_list to "session"
    else
    set fail_flag to true
    end if
    else
    set fail_flag to true
    end if

    if fail_flag then loqqed_Error_Halt3(true, "convertKindList received an unexpected Kind string: " & kind_s)
    end repeat

    end if

    if input_is_list then
    return kind_s_list
    else
    return item 1 of kind_s_list
    end if

    end convertKindList

    on InitializeResultsCollection(nameResultProject, nameResultAlbumRoot, Coll_Init_Text)
    ## Copyright 2018 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General Purpose Handler for scripts using Capture One Pro
    ## Sets up a project and albums for collecting images

    global debugLogLevel, COPDocRef, Ref2ResultAlbum, enableNotifications

    tell application "Capture One 20" to tell COPDocRef
    if not (exists collection named (get nameResultProject)) then
    set ref2ResultProject to make new collection with properties {kind:project, name:nameResultProject}
    else
    if ("project" = my convertKindList(kind of (get collection named nameResultProject))) then
    set ref2ResultProject to collection named nameResultProject
    else
    my loqqed_Error_Halt3(true, ("A collection named "" & nameResultProject & "" already exists, and it is not a project."))
    end if
    end if
    end tell

    set coll_ctr to 1
    set nameResultAlbum to nameResultAlbumRoot & "_" & (get short date string of (get current date)) & "_"
    repeat
    tell application "Capture One 20" to tell ref2ResultProject
    if not (exists collection named (get nameResultAlbum & coll_ctr)) then
    set nameResultAlbum to (get nameResultAlbum & coll_ctr)
    set Ref2ResultAlbum to make new collection with properties {kind:album, name:nameResultAlbum}
    exit repeat
    else
    set coll_ctr to coll_ctr + 1
    end if
    end tell
    end repeat

    if enableNotifications then display notification (Coll_Init_Text & " " & nameResultProject & ">" & nameResultAlbum)
    loq_Results2(1, false, (Coll_Init_Text & " " & nameResultProject & ">" & nameResultAlbum))

    end InitializeResultsCollection

    ###########################################################################################################################
    ## General Handlers Version 2019/11/6

    on InitializeLoqqing3(DocName_Ext, sourceTitle)
    ## Copyright 2018 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General purpose handler to set up Text Editor document for logging results

    global debugLogLevel, Script_Title, Result_Doc_ref, SE_Logging, enableResultsFile, enableResultsByDialog, enableResultsByClipboard, DialogTextList, enableNotifications
    global initEnableResultsByDialog, initEnableResultsByClipboard

    tell current application to set date_string to (current date) as text
    set LogMethods to {}
    set LogHeader to (sourceTitle & " results on " & date_string)

    if enableResultsFile then
    set end of LogMethods to DocName_Ext
    set targetFileWasCreated to false
    set ResultDocIsOpen to false

    ## Check if TextEdit is already open and has the document open
    tell application "System Events" to set TextEditlist to get background only of every application process whose name is "TextEdit"
    if (0 < (count of TextEditlist)) and not item 1 of TextEditlist then
    if (DocName_Ext is in (get name of documents of application "TextEdit")) then
    tell application "TextEdit" to set Result_Doc_ref to document DocName_Ext
    set ResultDocIsOpen to true
    end if
    end if

    if not ResultDocIsOpen then
    -- create the document and the folder if necessary
    -- Do not use finder to test for the file existence because it has a bug that ignores leading 0's
    -- https://www.macscripter.net/viewtopic.php?id=45178

    set target_folder_parent_a to alias (get path to desktop folder as text)
    set target_folder_parent_p to get POSIX path of target_folder_parent_a
    set target_folder_name to "ScriptReports"
    set target_folder_p to (target_folder_parent_p & target_folder_name)
    set Result_Doc_Path_p to target_folder_p & "/" & DocName_Ext

    try
    set Result_Doc_Path_a to (get alias POSIX file Result_Doc_Path_p)
    on error
    try
    set target_folder_a to (get alias POSIX file target_folder_p) --x1
    on error
    tell application "Finder" to set newFolder to make new folder at target_folder_parent_a with properties {name:target_folder_name}
    set target_folder_a to newFolder as alias
    end try
    tell application "Finder" to set newFile to make new file at target_folder_a with properties {name:DocName_Ext}
    set Result_Doc_Path_a to newFile as alias
    set targetFileWasCreated to true
    end try

    set First_line to ("Created by " & Script_Title & " on " & date_string)
    tell application "TextEdit" -- open the document and add the first line if empty
    activate
    set Result_Doc_ref to open Result_Doc_Path_a
    set ResultDocIsOpen to true -- For consistency
    tell text of Result_Doc_ref
    if targetFileWasCreated then
    set paragraph 1 to First_line & return & return
    tell me to if 2 ≤ debugLogLevel then log Result_Doc_Path_p & ": " & First_line
    else
    try
    if (0 = (count of paragraphs)) then set paragraph 1 to First_line & return & return
    on error
    set paragraph 1 to First_line & return & return
    end try
    end if
    end tell
    end tell
    end if

    tell application "TextEdit" to tell text of Result_Doc_ref to ¬
    set paragraph (1 + (count paragraphs)) to return & LogHeader & return

    end if

    if enableResultsByDialog then
    set end of LogMethods to "Display Dialog"
    try
    initEnableResultsByDialog
    set DialogTextList to DialogTextList & ""
    set initEnableResultsByDialog to false
    on error
    set DialogTextList to {LogHeader}
    set initEnableResultsByDialog to true
    end try
    end if

    if enableResultsByClipboard then
    set end of LogMethods to "Clipboard"
    try
    initEnableResultsByClipboard
    set initEnableResultsByClipboard to false
    on error
    set the clipboard to LogHeader
    set initEnableResultsByClipboard to true
    end try
    end if

    if SE_Logging then -- if Script Editor logging, then open the Log History window
    try
    tell application "System Events" to tell application process "Script Editor"
    if (get name of windows) does not contain "log History" then
    click menu item "Log History" of menu "Window" of menu bar 1
    end if
    end tell
    end try
    set end of LogMethods to " Script Editor Log"
    end if

    set LogMethods_S to joinListToString(LogMethods, ", ")
    loq_Results2(2, false, ("Results by " & LogMethods_S))

    return LogMethods_S
    end InitializeLoqqing3

    on loq_Results2(thisLogDebugLevel, MakeFront, log_Text)
    ## Copyright 2018 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
    ## General purpose handler for logging results
    ## log results if the debug level of the message is below the the threshold set by debugLogLevel
    ## log the results by whatever mechanism is ebabled - {Script Editor Log, Text Editor Log, Display Dialog}

    global Result_Doc_ref, debugLogLevel, SE_Logging, parent_name, ResultsFileMaxDebug, enableResultsFile, enableResultsByDialog, enableResultsByClipboard, DialogTextList
    if thisLogDebugLevel > debugLogLevel then return

    set log_Text_S to joinListToString(log_Text, ", ")

    if enableResultsFile and ((thisLogDebugLevel ≤ ResultsFileMaxDebug) or not SE_Logging) then
    tell application "TextEdit" to tell text of Result_Doc_ref to ¬
    set paragraph (1 + (count paragraphs)) to ((log_Text_S as text) & return)
    tell application "System Events" to if MakeFront then set frontmost of process "TextEdit" to true
    end if

    if enableResultsByDialog and (1 ≥ thisLogDebugLevel) then
    set DialogTextList to DialogTextList & log_Text_S
    tell application "System Events" to set frontmost of process parent_name to true
    display dialog joinListToString(DialogTextList, return)
    end if

    if enableResultsByClipboard and (1 ≥ thisLogDebugLevel) then set the clipboard to ((get the clipboard) & return & (log_Text_S as text))
    if SE_Logging then log (log_Text_S as text)

    end loq_Results2

    on loqqed_Error_Halt3(createErrorHere, error_text)
    ## General purpose handler for logging during script termination
    ##
    ## found an error somewhere, so now we exit in a controlled fashion
    ## set createError to "false" to create a local error instead of here
    global debugLogLevel, Script_Title, enableNotifications
    tell current application to set date_string to (current date) as text
    finalCleanup()
    if enableNotifications then display notification error_text
    if createErrorHere then
    loq_Results2(0, true, ("Script "" & Script_Title & "" has halted at " & date_string & return & "Reason: " & error_text & return & return))
    error error_text
    else
    loq_Results2(0, true, ("Script "" & Script_Title & "" is exitting at " & date_string & "Reason: " & error_text & return))
    end if
    end loqqed_Error_Halt3

    on splitstringtolist(theString, theDelim)
    ## Public Domain
    set astid to AppleScript's text item delimiters
    try
    set AppleScript's text item delimiters to theDelim
    set theList to text items of theString
    on error
    set AppleScript's text item delimiters to astid
    end try
    set AppleScript's text item delimiters to astid
    return theList
    end splitstringtolist

    to joinListToString(theList, theDelim)
    ## Public Domain
    set theString to ""
    set astid to AppleScript's text item delimiters
    try
    set AppleScript's text item delimiters to theDelim
    set theString to theList as string
    on error
    set AppleScript's text item delimiters to astid
    end try
    set AppleScript's text item delimiters to astid
    return theString
    end joinListToString

    on makeList3(listLength, theElement)
    -- Note that the theElement can even be a List
    if listLength = 0 then return {}
    if listLength = 1 then return {theElement}

    set theList to {theElement}
    repeat while (count of theList) < listLength / 2
    copy contents of theList to ListB
    copy theList & ListB to theList
    end repeat
    copy contents of theList to ListB
    return (theList & items 1 thru (listLength - (count of ListB)) of ListB)
    end makeList3
    0
  • Eric Valk
    Script has been updateed (see post above)
    0

Post is closed for comments.