SemiAutomatic Image Sequencer
To efficiently handle large number of images, its key to separate the images into groups, and to do this efficiently.
I have been working for some time on an automatic image sequencer, in that process I have developed this much simpler semi automatic version where the user selects the variants in a sequence, and then the script takes care of the rest.
Invoking the script the first time creates a group called "Stacks" which will hold the albums for each image, and a smart album "Unsorted", which shows all the variants not yet in a sequence.
Images which are selected are copied into a sequence folder like SEQ 0030-0055 where the file name of the first variant ends with 0030 and the file name of the last variant ends with 0055. These images are assigned a blue color tag. The smart album hides all images with a blue or magenta color tag (so the user can assign Magenta to selected images).
Here is the script. Let me know how it works for you.
If you have an image naming system which isn't handled by the image name parser (resulting in errors or incorrect album names) give me some examples of your image filenames, and I can likely generate a parser rule that works for you.
The script is slightly too large to fit within the limits of this website (too many long variable names). Here is the first part, the second part will be in the next post. Just copy them in sequence to a Script Editor window.
[color=#0000FF:25qgzgnk]Use the improved version of part 1 in post number 3 below[/color:25qgzgnk]
I have been working for some time on an automatic image sequencer, in that process I have developed this much simpler semi automatic version where the user selects the variants in a sequence, and then the script takes care of the rest.
Invoking the script the first time creates a group called "Stacks" which will hold the albums for each image, and a smart album "Unsorted", which shows all the variants not yet in a sequence.
Images which are selected are copied into a sequence folder like SEQ 0030-0055 where the file name of the first variant ends with 0030 and the file name of the last variant ends with 0055. These images are assigned a blue color tag. The smart album hides all images with a blue or magenta color tag (so the user can assign Magenta to selected images).
Here is the script. Let me know how it works for you.
If you have an image naming system which isn't handled by the image name parser (resulting in errors or incorrect album names) give me some examples of your image filenames, and I can likely generate a parser rule that works for you.
The script is slightly too large to fit within the limits of this website (too many long variable names). Here is the first part, the second part will be in the next post. Just copy them in sequence to a Script Editor window.
[color=#0000FF:25qgzgnk]Use the improved version of part 1 in post number 3 below[/color:25qgzgnk]
## Applescript to sequence images in a COP 12 Catalog
## Version 12.21 !! NO GUARANTEE OF SUPPORT !! Best effort
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
-- ***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 document
-- Open the Script Editor log window, and select the messages tab
-- 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.
-- *** Operation
-- select a Project, an album or smart album which is under a Project or Group (or both)
-- Select some variants and run this script
-- If not already present, a group "Stacks" and a smart album "Unsorted" will be created
-- the smart album rules will hide all images with blue or magenta color tag
-- an album with a name starting with SEQ will be created in the group Stacks
-- the album name will contain the lowest and highest numbers sequences from the selected variants
-- the selected variants will be color tagged Blue and copied to the album
-- Once "Unsorted" Exists, continue creating sequences from there, only the unsequenced images will be shown.
use AppleScript version "2.5"
use scripting additions
property M : missing value
set M to me
global C, L, U
set C to me
set L to me
set U to me
set loqGUIsettings_L to L's setupLoqqing5() -- setup the variables used for Loqqing system
## Values in this section are safe to change, within limits indicated. Support is likely but no commitment
## Default values
set Loqqing's debugLogLevel to 0 -- 0...6 Values >1 result in increasing amounts of debug data that takes longer to report
set Loqqing's enableResultsFile to true -- (true/false)
set Loqqing's enableResultsByClipboard to false -- (true/false)
set Loqqing's enableNotifications to true -- (true/false) - enable notifications of errors and exceptions
set Loqqing's notificationsMaxDebug to 1 -- 0...6 suggest not more than 1
set Loqqing's ResultsFileMaxDebug to 3 -- 0...6 suggest not more than 2
set Loqqing's enableResultsByNotifications to false
set nameStackGroup to "Stacks"
set nameUnsortedAlbum to "Unsorted"
set enableAlbumNameEntry to false
set sortedColorTag1 to 5 -- for the stacked images
set sortedColorTag2 to 6 -- for the picked stacked images
--Color Tag
--Criterion
--0--> none
--1-->red
--2-->orange
--3-->yellow
--4-->green
--5-->blue
--6-->magenta
--7-->purple
## ***** Semi 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.
set inpGroup1 to {inpPref:"", inpAlp:false, inpNum:true, inpSpa:false, inpSym:false, inpBck:true, inpCnt:5, inpAct:{{return, "O"}}} -- Take up to 5 alphabetic, symbol or space characters. Then discard them
set inpGroup2 to {inpPref:"", inpAlp:true, inpNum:false, inpSpa:true, inpSym:true, inpBck:true, inpCnt:0, inpAct:{}} -- Take the next number characters, stop on a Symbol or Space or alphabetic character
set inpMethod1 to {"SOOC Parse", {inpGroup1, inpGroup2}} -- parses the image file names typically assigned by a camera
set inpGroup3 to {inpPref:"", inpAlp:false, inpNum:true, inpSpa:false, inpSym:false, inpBck:true, inpCnt:5, inpAct:{{return, "O"}, {"-", "M", 0}}} -- Take up to 5 alphabetic, symbol or space characters. Then discard them. Start over next method if a "-" is found.
set inpGroup4 to {inpPref:"", inpAlp:true, inpNum:false, inpSpa:true, inpSym:true, inpBck:true, inpCnt:0, inpAct:{{"-", "M", 0}}} -- Take the next number characters, stop on a Symbol or Space or alphabetic character. Start over next method if a "-" is found.
set inpGroup5 to {inpPref:"", inpAlp:false, inpNum:false, inpSpa:true, inpSym:true, inpBck:false, inpCnt:4, inpAct:{{return, "O"}, {return, "D"}}} -- Take Alpha and Numbers, stop on Space or symbol or 4 characters. Then discard
set inpGroup6 to {inpPref:"", inpAlp:true, inpNum:false, inpSpa:true, inpSym:true, inpBck:false, inpCnt:3, inpAct:{{"$", "G"}, {return, "O"}, {return, "D"}}} -- Take Only Numbers, stop on Alpha or Space or Symbol or 3 characters. Then discard. Start over if a "$" is found
set inpGroup7 to {inpPref:"", inpAlp:true, inpNum:false, inpSpa:true, inpSym:true, inpBck:true, inpCnt:5, inpAct:{{"$", "G"}}} -- Take Only Numbers, stop on Alpha or Space or Symbol or 5 characters. Start over if a "$" is found
set inpMethod2 to {"Universal Parse", {inpGroup3, inpGroup4}, {inpGroup5, inpGroup6, inpGroup7}} -- combination of Method 3 and Method 1
set inpMethod3 to {"Eric's Detailed Parse", {inpGroup5, inpGroup6, inpGroup7}} -- parses Eric's image file names
set imageNameParsingMethod to inpMethod1 -- OK to change this
## ***** 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.
set debugLogEnable to true
## Result reporting methods which are valid for this script
set Loqqing's gateResultsFile to true
set Loqqing's gateResultsByClipboard to true
set Loqqing's gateResultsNotification to true
## Reporting methods which are NOT valid for this script
-- disable the reporting method
set Loqqing's gateResultsDialog to false
-- disable user control
set Loqqing's enableResultsByDialog to false -- (true/false)
set Loqqing's maxDialogPercent to 85 -- (0% to 100% of the monitor) The amount of data that triggers a dialog report
##
tell application "System Events" to set parent_name to name of current application
set Script_Title to (get name of me)
set Result_DocName to "CO_Image_Sorting_Report.txt"
set Loqqing's enableResultsByLoq to {"Script Editor"} contains parent_name
set loqResultMethod to L's InitializeLoqqing5(Result_DocName, Script_Title) -- Initialize the results logging system
set {minCOPversion, maxCOPversion} to {"12", "12"}
tell C's validateCOP5(minCOPversion, maxCOPversion)
if its hasErrors then error L's loqqed_Error_Halt5(get its errorText)
set {theAppName, copVersion} to {its theAppName, its copVersion}
end tell
tell application "Capture One 12" to set COPDocRef to get current document
tell C's validateCOPdoc5(COPDocRef, {"Catalog"})
if its hasErrors then error L's loqqed_Error_Halt5(get its errorText)
set {COPDocName, COPDocKind_s} to {its COPDocName, its COPDocKind_s}
end tell
tell C's validateCOPcollections5(COPDocRef) to set {selectedCollectionRef, kindSelectedCollection_s, nameSelectedCollection} to ¬
{its selectedCollectionRef, its kindSelectedCollection_s, its nameSelectedCollection}
tell application "Capture One 12" to set countSelectedVariants to get count of selected variants
tell application "Capture One 12" to set countEveryVariant to count of variants
set startSortRule to "<?xml version="1.0" encoding="UTF-8"?><MatchOperator Kind="AND"><MatchOperator Kind="AND"><Condition Enabled="YES"><Key>IB_S_BASIC_URGENCY</Key><Operator>1</Operator><Criterion>"
set endSortRule to "</Criterion></Condition></MatchOperator></MatchOperator>"
set midSortRule to "</Criterion></Condition></MatchOperator><MatchOperator Kind="AND"><Condition Enabled="YES"><Key>IB_S_BASIC_URGENCY</Key><Operator>1</Operator><Criterion>"
set smartSortRule to startSortRule & sortedColorTag1 & midSortRule & sortedColorTag2 & endSortRule
set {theStacksRef, foundStacks} to findCollection(selectedCollectionRef, nameStackGroup, "group", true, true, {"group", "project"}, "")
if not foundStacks then error L's loqqed_Error_Halt5("Can't find or create the "" & nameStackGroup & "" group")
set {theUnsortedRef, foundUnsorted} to findCollection(selectedCollectionRef, nameUnsortedAlbum, "smart album", true, true, {"group", "project"}, smartSortRule)
if not foundUnsorted then error L's loqqed_Error_Halt5("Can't find or create the "" & nameUnsortedAlbum & "" smart album")
tell application "Capture One 12" to tell (variants whose selected is true) to set {theVariantsRef, theVariantsName} to {it, its name}
if (0 = (count of theVariantsName)) then error L's loqqed_Error_Halt5("No variants selected")
set numdigits to -1
repeat with aName in theVariantsName
set thisCtr to findBaseName(aName, imageNameParsingMethod)
set {thisLen, thisCtr} to {(count of thisCtr), (thisCtr as integer)}
if -1 = numdigits then
set {numdigits, minCtr, maxCtr} to {thisLen, (0 + (thisCtr as integer)), (0 + (thisCtr as integer))}
else
if thisLen > numdigits then set numdigits to thisLen
if thisCtr < minCtr then set minCtr to thisCtr
if thisCtr > maxCtr then set maxCtr to thisCtr
end if
end repeat
set padZeros to U's makeList(numdigits, 0) as string
set seqCollName to "SEQ " & (text (-numdigits) thru -1 of (padZeros & minCtr))
if maxCtr > minCtr then set seqCollName to seqCollName & "-" & (text (-numdigits) thru -1 of (padZeros & maxCtr))
set userCanceled to false
if enableAlbumNameEntry then
try
set dialogResult to display dialog ¬
"Sequence Name" buttons {"Cancel", "OK"} ¬
default button "OK" cancel button "Cancel" default answer seqCollName
on error number -128
set userCanceled to true
end try
set seqCollName to dialogResult's text returned
end if
if not userCanceled then
set {theSeqRef, gotSeq} to findCollection(theStacksRef, seqCollName, "album", true, false, {"group"}, "")
if gotSeq then
tell application "Capture One 12"
add inside theSeqRef variants theVariantsRef
tell (variants whose selected is true)
set its color tag to sortedColorTag1
end tell
end tell
L's loqThis(-1, false, "Copied " & (count of theVariantsRef) & " variants to "" & seqCollName & """)
else
error L's loqqed_Error_Halt5("Can't create the "" & seqCollName & "" album")
end if
end if
################ The End ###############
on findCollection(thisCollRef, nameTargetColl, kindTargetColl, enableCreate, enableSearch, kindParentColl_L, saSortRule)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General purpose handler to find and create a collection, starting from some arbitrary collection
global debugLogEnable
local thisCollKind, thisCollKind_S, thisCollName, subCollNames, subCollRef, refFoundColl, errorText, parentString, theParentRef, parentList, nextRef, refCtr, lastRef, hasFoundColl, cntColl, docMark, docName
local saRules, saNoRules, debugText
tell application "Capture One 12" to tell thisCollRef to set {thisCollKind, thisCollName, subCollNames, subCollRef} to {its kind, its name, name of its collections, its collections}
set thisCollKind_S to C's convertKindList(thisCollKind)
set debugText to kindTargetColl & " "" & nameTargetColl & "" in " & thisCollKind_S & " "" & thisCollName & """
if debugLogEnable then L's loqThis(2, false, return & "Find Collection: " & debugText & " with Create: " & enableCreate & " Search: " & enableSearch & " Sort Rule: " & (count of saSortRule) & " characters")
set isChevronForm to ("«" = text 1 of (thisCollKind as text))
if debugLogEnable then
L's loqThis(3, false, "SubCollections {" & U's joinListToString(subCollNames, "; ") & "}")
L's loqThis(3, false, "Chevron Form: " & isChevronForm)
end if
set refFoundColl to {}
if (kindParentColl_L contains thisCollKind_S) and (subCollNames contains nameTargetColl) then
tell application "Capture One 12" to tell thisCollRef
if ("project" = kindTargetColl) then set refFoundColl to (refFoundColl & (every collection whose name is nameTargetColl and kind is project))
if ("group" = kindTargetColl) then set refFoundColl to (refFoundColl & (every collection whose name is nameTargetColl and kind is group))
if ("album" = kindTargetColl) then set refFoundColl to (refFoundColl & (every collection whose name is nameTargetColl and kind is album))
if ("smart album" = kindTargetColl) then set refFoundColl to (refFoundColl & (every collection whose name is nameTargetColl and kind is smart album))
end tell
end if
set hasFoundColl to false
if (1 = (count of refFoundColl)) then set {refFoundColl, hasFoundColl} to {(item 1 of refFoundColl), true}
if hasFoundColl then
if debugLogEnable then L's loqThis(1, false, "Found " & debugText)
else if (not enableCreate) and (not enableSearch) then
L's loqThis(0, false, "The " & kindTargetColl & " "" & nameTargetColl & "" is missing from " & thisCollKind_S & " "" & thisCollName & """)
else
if (not hasFoundColl) and enableCreate and (kindParentColl_L contains thisCollKind_S) then
if debugLogEnable then L's loqThis(2, false, "Attempting to Create the collection")
tell application "Capture One 12" to tell thisCollRef
set saNoRules to "<?xml version="1.0" encoding="UTF-8"?><MatchOperator Kind="AND"></MatchOperator>"
if ("group" = kindTargetColl) then set refFoundColl to {make new collection with properties {kind:group, name:nameTargetColl}}
if ("album" = kindTargetColl) then set refFoundColl to {make new collection with properties {kind:album, name:nameTargetColl}}
if ("smart album" = kindTargetColl) then
set saNoRules to "<?xml version="1.0" encoding="UTF-8"?><MatchOperator Kind="AND"></MatchOperator>"
try
if (text 1 thru 10 of saNoRules) = (text 1 thru 10 of saSortRule) then set saRules to saSortRule
on error
set saRules to saNoRules
end try
set refFoundColl to {make new collection with properties {kind:smart album, name:nameTargetColl, rules:saRules}}
end if
try
if ("project" = kindTargetColl) then set refFoundColl to {make new collection with properties {kind:project, name:nameTargetColl}}
on error errorText
if debugLogEnable then L's loqThis(2, false, "Creation failed with: " & errorText)
end try
end tell
if (1 = (count of refFoundColl)) then
set {refFoundColl, hasFoundColl} to {(item 1 of refFoundColl), true}
L's loqThis(1, false, "Created " & debugText)
end if
end if
if (not hasFoundColl) and enableSearch then
if debugLogEnable then L's loqThis(2, false, "Starting Search")
try
get || of {thisCollRef}
on error errorText
end try
if isChevronForm then set errorText to U's replaceText(errorText, "«class COcl»", "collection")
set parentList to U's splitStringToList(errorText, {"of", "{", "}"})
repeat with refCtr from (count of parentList) to 0 by -1
try
if "document" = first word of item refCtr of parentList then exit repeat
end try
end repeat
if 0 = (get (contents of refCtr) as integer) then error L's loqqed_Error_Halt5("Didn't find the document reference in " & errorText)
set docMark to contents of refCtr
set docName to U's removeLeadingTrailingSpaces((get item 2 of U's splitStringToList((U's removeLeadingTrailingSpaces((get item docMark of parentList))), """)))
tell application "Capture One 12" to set lastRef to document docName
set cntColl to 0
repeat with refCtr from 3 to docMark - 1
try
if "collection" = (first word of item refCtr of parentList) then
set cntColl to cntColl + 1
if 2 ≤ cntColl then
tell application "Capture One 12" to set nextRef to collection id (get last word of parentList's item refCtr) of lastRef
copy nextRef to lastRef
end if
end if
end try
end repeat
if (0 = cntColl) then
L's loqThis(0, false, "Unable to identify parent collection")
else
set {refFoundColl, hasFoundColl} to my findCollection(lastRef, nameTargetColl, kindTargetColl, enableCreate, enableSearch, kindParentColl_L, saSortRule)
end if
end if
end if
if not hasFoundColl then set refFoundColl to missing value
return {refFoundColl, hasFoundColl}
end findCollection
on findBaseName(theString, theMethod_L)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General purpose handler to parse an image file name
## Looks long, but execution time is about 20 - 80 microseconds
## theMethod_L is a list of Methods. Each Method describes a parsing method (a particular way of parsing a string)
## Each Method is a list of Groups, each Group is a record that describes how to find the end of a group of characters.
## While a group runs each character is copied from theString to the output until the end is found.
## The algorithm starts with Group 1 of Method 1. When a Group ends the next one starts
## When there are no Groups left or no Characters left, the algorithm stops reading characters and returns the output characters.
## The Group record: {inpAlp, inpNum, inpSpa, inpSym, inpCnt, inpBck, inpAct}:
## (inpAlp, inpNum, inpSpa, inpSym, inpBck) boolean, (inpCnt) integer, inpAct a list of Actions {{X,Y,Z},{X,Y,Z},...}
#3 Character Type Tests are evaluated first, then Actions
## A group ends upon finding Alphabetic characters (if inpAlp true), or Numbers (if inpNum true), or a Space(if inpSpa true),
## or Symbols (if inpSym true), or finding more than inpCnt characters (if inpCnt >0).
## If inpBck is true, when a group ends, the last character becomes part of the next group
## EG Group3: {inpAlp:false, inpNum:false, inpSpa:false, inpSym:true, inpBck:true, inpCnt:9, inpAct:aList}
## Means: stop if a symbol is found or after the 9th character, and the last character becomes the first character of the next group
## Thus up to 9 Numbers, Spaces and Alphabetic characters are accepted, and 9th character or symbol is part of the next group.
## if Group 3 is the last Group, then 9th or symbol character (and all subsequent characters) are dropped.
## inpAct contains Alists {X,Y,Z}, each describes an action to execute when finding some character. inpAct= {} results in no actions
## X is the character that triggers an action; Y is the Action; Z optionally specifies "how much"
## X= return triggers the action on a Group Increment from Character Type Tests
## Actions (1): T-Translate Character to "Z" ; G- Switch to Group Z ; M- Switch to Method Z ; I- Increment group by Z;
## B- Drop Back 1 character; J- Jump ahead Z characters, D- Drop trigger Characters, O - Drop Output
## Actions A,B,C,N,S and P set the value of inpAlp, inpBck, inpCnt, inpNum, inpSpa and inpSym
## If the Alist has no "z" value: for "I", z is taken as 1; for "J", z is taken as the length of the trigger; for "T" z is taken as "";
## for G, z is taken as "0"; for M, the method is incremented, output and trigger are dropped
## for all other commands z is taken as true when missing
## Selecting Group < 1 resets the parser to Group 1 and clears all output. If inpBck is true, the current character is dropped.
## Selecting Method 0 triggers a Method reset, output and input are reset, and then the previous method is incremented
## Switching to Method whose number does not exist causes the previous output to be cleared, the remaining input characters are accepted
## E.G. For inpAct:{{"$","M"},{"-","T"}} in Method 1 --> when an "$" is encountered, start over again with Group 1 of Method 2, also drop every "-"
## If there is no Method 2, all remaining characters of the input are taken as output.
global debugLogEnable
local theCharIDList, stringCount, thisCharsId, parsedNumList, charPointer, iterationCtr, SafetyLimit
local Alpha, Numb, Blank, Symbol, MaxCnt, backOneOnGrpInc, ActionList, AlistTriggerId_L, anAlist, theAction, Prefix
local thisMethod, MethodCount, thisGroup, GroupCount, hasMaxCnt, countChars, stringCount
local triggerMethodInit, triggerGroupInit, triggerGroupInc, validChar, newCharIDs, hasNewChars, nextGroup, nextMethod, nextCharPointer
local triggerBack1Char, hasPar3, valuePar3, endTriggerPointer, lenActTrigger, actionString, hasActionTrigger, theActionTrigger, nextCharJump, hasJump, triggerOutputDrop, lenActTrigger
set {theCharIDList, stringCount, nextMethod, MethodCount, triggerMethodInit, parsedNumList} to ¬
{(get id of theString), (get count of theString), 1, (count of lists of theMethod_L), true, {}}
set {charPointer, iterationCtr, SafetyLimit} to {1, 0, (MethodCount * stringCount * 4)}
if debugLogEnable then
L's loqThis(2, false, "Parsing "" & theString & """)
if (0 < (count of strings of theMethod_L)) then L's loqThis(2, false, ("Using Method " & (string 1 of theMethod_L)))
L's loqThis(3, false, {"Initialising: stringCount " & stringCount & ", Method " & nextMethod & ", MethodCount " & MethodCount})
end if
repeat while (charPointer ≤ stringCount) and (iterationCtr < SafetyLimit)
set iterationCtr to iterationCtr + 1
if triggerMethodInit then set {nextGroup, GroupCount, triggerGroupInit, thisMethod} to ¬
{1, (count of records of theMethod_L's list nextMethod), true, nextMethod}
if debugLogEnable and triggerMethodInit then L's loqThis(3, false, "Start Method #" & nextMethod & ", Group Count: " & (count of theMethod_L's list nextMethod))
if triggerGroupInit then
tell theMethod_L's list thisMethod's record nextGroup to set {Prefix, Alpha, Numb, Blank, Symbol, MaxCnt, hasMaxCnt, backOneOnGrpInc, ActionList, countChars, thisGroup} to ¬
{its inpPref, its inpAlp, its inpNum, its inpSpa, its inpSym, its inpCnt, (0 < its inpCnt), its inpBck, its inpAct, 0, nextGroup}
if debugLogEnable then L's loqThis(3, false, "Start Group #" & thisGroup & " , Prefix "" & Prefix & "", Alpha " & Alpha & ", Number " & Numb & ", Blank " & Blank & ", Symbol " & Symbol & ", Max Count " & MaxCnt & ", has Max Count " & hasMaxCnt & ", Drop Back Last Character " & backOneOnGrpInc & " , ActionList : " & (count of ActionList) & " action lists")
if (0 < (count of ActionList)) and ("list" ≠((class of ActionList's item 1) as text)) then error "Group " & thisGroup & " has an incorrectly formatted Action List"
set parsedNumList to parsedNumList & (id of Prefix)
end if
set {triggerBack1Char, triggerGroupInc, triggerGroupInit, triggerMethodInit, validChar, newCharIDs, hasNewChars, hasActionTrigger, theActionTrigger, nextCharJump, hasJump, triggerOutputDrop} to ¬
{false, false, false, false, true, {}, false, false, missing value, 0, false, false}
set {thisCharsId, countChars, nextCharPointer, lenActTrigger} to {(theCharIDList's item charPointer), (countChars + 1), charPointer, 1}
if debugLogEnable then L's loqThis(5, false, "Character " & charPointer & " : "" & (string id (get theCharIDList's item charPointer)) & """)
if hasMaxCnt and (countChars ≥ MaxCnt) then -- if the counter has triggered, don't check the character groups
set triggerGroupInc to true
if debugLogEnable then L's loqThis(3, false, "Count triggers Group Increment")
else -- check the character type
if debugLogEnable then L's loqThis(3, false, "Character Type Check")
if ((thisCharsId ≥ 65) and (thisCharsId ≤ 90)) or ((thisCharsId ≥ 97) and (thisCharsId ≤ 122)) or (thisCharsId ≥ 192) then -- a latin character
if Alpha then set triggerGroupInc to true
if debugLogEnable and Alpha then L's loqThis(4, false, "Alpha triggers Group Increment")
else if ((thisCharsId ≥ 48) and (thisCharsId ≤ 57)) then -- a number
if Numb then set triggerGroupInc to true
if debugLogEnable and Numb then L's loqThis(4, false, "Number triggers Group Increment")
else if thisCharsId = 32 then --- its a space
if Blank then set triggerGroupInc to true
if debugLogEnable and Blank then L's loqThis(4, false, "Space triggers Group Increment")
else if thisCharsId > 32 then -- its a symbol
if Symbol then set triggerGroupInc to true
if debugLogEnable and Symbol then L's loqThis(4, false, "Symbol triggers Group Increment")
end if
end if
repeat with anAlist in ActionList
set {AlistTriggerId_L, endTriggerPointer} to {((id of contents of anAlist's item 1) as list), (charPointer + (count of anAlist's item 1) - 1)}
if (endTriggerPointer ≤ stringCount) and ((not hasActionTrigger) or (theActionTrigger = AlistTriggerId_L)) and ¬
((triggerGroupInc and ({13} = AlistTriggerId_L)) or (AlistTriggerId_L = (theCharIDList's items charPointer thru endTriggerPointer))) ¬
then
if debugLogEnable and not hasActionTrigger then L's loqThis(4, false, "Action List is triggered by "" & (anAlist's item 1) & """)
if not hasActionTrigger then set {hasActionTrigger, theActionTrigger, lenActTrigger} to {true, AlistTriggerId_L, (count of AlistTriggerId_L)}
set {theAction, hasPar3, valuePar3} to {(item 2 of anAlist), (3 ≤ (count of anAlist)), true}
if hasPar3 then set valuePar3 to (contents of item 3 of anAlist)
if debugLogEnable then
if hasPar3 then
set actionString to "Action "" & (contents of item 2 of anAlist) & "" ; "" & (contents of item 3 of anAlist) & """
else
set actionString to "Action "" & (contents of item 2 of anAlist) & """
end if
end if
## Group 1 - Character Control - Does Not clear triggerGroupInc
if ("A" = theAction) then
set Alpha to valuePar3
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpAlp to " & valuePar3)
else if "B" = theAction then --
set backOneOnGrpInc to valuePar3
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpBck to " & valuePar3)
else if ("C" = theAction) then
if (not hasPar3) or (0 = valuePar3) then
set {MaxCnt, hasMaxCnt} to {0, false}
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpCnt to 0")
else if (0 < valuePar3) then
set {MaxCnt, hasMaxCnt} to {valuePar3, true}
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpCnt to " & valuePar3)
else if (0 > valuePar3) then
set {MaxCnt, hasMaxCnt} to {(MaxCnt - valuePar3), true}
if debugLogEnable then L's loqThis(5, false, actionString & " - Increment inpCnt by " & (-valuePar3))
end if
else if ("N" = theAction) then
if hasPar3 then set Numb to valuePar3
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpNum to " & valuePar3)
else if ("P" = theAction) then
set Symbol to valuePar3
if debugLogEnable then L's loqThis(5, false, actionString & "- Set inpSym to " & valuePar3)
else if ("Q" = theAction) then
if debugLogEnable then L's loqThis(4, false, actionString & "- Quit Actions")
exit repeat
else if "S" = theAction then
set Blank to valuePar3
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpSpa to " & valuePar3)
## Group 2 - Character Control - clears triggerGroupInc on multi character trigger
else if "D" = theAction then --
set validChar to not valuePar3
if 1 < lenActTrigger then set triggerGroupInc to false
if debugLogEnable then L's loqThis(5, false, actionString & " - Drop Action Trigger: " & valuePar3)
else if "O" = theAction then --
set triggerOutputDrop to valuePar3
if 1 < lenActTrigger then set triggerGroupInc to false
if debugLogEnable then L's loqThis(5, false, actionString & " - Drop Output characters")
else if "J" = theAction then --
if not hasPar3 then set valuePar3 to lenActTrigger
set {nextCharJump, hasJump} to {valuePar3, true}
if 1 < lenActTrigger then set triggerGroupInc to false
if debugLogEnable then L's loqThis(5, false, actionString & " - Jump forward " & valuePar3 & " Characters")
else if "T" = theAction then -- translate/drop this character
if not hasPar3 then set valuePar3 to ""
set {newCharIDs, hasNewChars} to {(id of valuePar3), true}
if 1 < lenActTrigger then set triggerGroupInc to false
if debugLogEnable then L's loqThis(5, false, actionString & " - Translate characters to "" & valuePar3 & """)
## Group 3 - Group & Method control, clears triggerGroupInc - later
else if "G" = theAction then -- new group
if not hasPar3 then set valuePar3 to 0
set {nextGroup, triggerGroupInit} to {valuePar3, true}
if debugLogEnable then L's loqThis(5, false, actionString & " - Switch to Group " & valuePar3)
else if "I" = theAction then -- increment group
if not hasPar3 then set valuePar3 to 1
set {nextGroup, triggerGroupInit} to {(thisGroup + valuePar3), true}
if debugLogEnable then L's loqThis(5, false, actionString & " - Increment Group to " & nextGroup)
else if "M" = theAction then -- set method
if not hasPar3 then set valuePar3 to (thisMethod + 1)
set {nextMethod, triggerMethodInit, triggerOutputDrop, validChar} to {valuePar3, true, true, false}
if debugLogEnable then L's loqThis(5, false, actionString & " - Switch to Method " & valuePar3 & " and drop output and trigger")
else
if debugLogEnable then L's loqThis(3, false, actionString & " - Command not found ")
end if
end if
end repeat
if (lenActTrigger > 1) then set countChars to countChars - 1 + lenActTrigger
if hasMaxCnt and (countChars ≥ MaxCnt) then set {triggerGroupInc} to {true, L's loqThis(3, false, "Character count triggers Group Increment")}
if (triggerGroupInit or triggerMethodInit) then set triggerGroupInc to false
if (triggerGroupInc or triggerGroupInit or triggerMethodInit) and backOneOnGrpInc then set triggerBack1Char to true
if (0 ≥ nextMethod) then
if debugLogEnable then L's loqThis(3, false, "Executing Method Reset - Increment Method, Clear output, Reset Input Queue")
set {parsedNumList, nextMethod, triggerMethodInit, nextCharPointer} to {{}, (thisMethod + 1), true, 1, false}
set {triggerBack1Char, triggerGroupInc, hasJump, hasNewChars, hasMaxCnt} to {false, false, false, false, false}
else
if (0 ≥ nextGroup) then
set {triggerBack1Char, validChar, triggerGroupInc, hasJump, hasNewChars} to {false, false, false, false, false}
if debugLogEnable then L's loqThis(3, false, "Executing Group Reset - Switch to Group 1, Clear output , dropping back " & (-nextGroup) & " characters")
if (-1 ≥ nextGroup) then set {nextCharJump, hasJump} to {(nextGroup), true}
set {parsedNumList, nextGroup, triggerGroupInit} to {{}, 1, true}
end if
if triggerGroupInc then set {nextGroup, triggerGroupInit} to {thisGroup + 1, true, L's loqThis(3, false, "Executing Group Increment - Switch to Group " & (thisGroup + 1))}
if debugLogEnable and triggerOutputDrop then L's loqThis(4, false, "Dropped Output")
if triggerOutputDrop then set parsedNumList to {}
if triggerBack1Char then
set {validChar, nextCharPointer} to {false, charPointer}
if debugLogEnable then L's loqThis(3, false, "Drop Back 1 Character")
else if (hasJump or hasNewChars) then
set validChar to false
if (hasJump and (((thisGroup ≠nextGroup) or (thisMethod ≠nextMethod) or (0 < nextCharJump)))) then
set nextCharPointer to (charPointer + nextCharJump)
if nextCharPointer < 1 then set nextCharPointer to 1
if hasNewChars and (0 < nextCharJump) and (lenActTrigger > nextCharJump) then set nextCharPointer to (charPointer + lenActTrigger)
else if hasJump then
## do nothing - can't move charPointer back
if debugLogEnable then L's loqThis(3, false, "Unable to move back " & (-nextCharJump) & " characters - infinite loop")
else if hasNewChars then -- and not hasJump
set nextCharPointer to (charPointer + lenActTrigger)
end if
if debugLogEnable then L's loqThis(3, false, "Input Moved Forward " & (nextCharPointer - charPointer) & " characters")
else
set nextCharPointer to (charPointer + 1)
if debugLogEnable then L's loqThis(5, false, "Input Moved Forward 1 character")
end if
if hasNewChars then set {parsedNumList} to {(parsedNumList & newCharIDs), L's loqThis(4, false, "Added new Characters to the output")}
if validChar then set end of parsedNumList to thisCharsId
if debugLogEnable and not validChar then L's loqThis(3, false, "The input character has been dropped")
end if
if debugLogEnable then L's loqThis(3, false, "The output is: "" & (string id parsedNumList) & """)
if nextMethod > MethodCount then -- replace the output with every remaining character
if debugLogEnable then L's loqThis(3, false, "Got Method " & nextMethod & " Return remaining characters")
if charPointer > 1 then set parsedNumList to (theCharIDList's items charPointer thru stringCount)
if charPointer ≤ 1 then copy theCharIDList to parsedNumList
exit repeat
else if (nextGroup > GroupCount) then
if debugLogEnable then L's loqThis(3, false, "Finished all groups, exitting")
exit repeat
end if
set charPointer to nextCharPointer -- must be last statement in the the loop
end repeat
if debugLogEnable and (charPointer > stringCount) then L's loqThis(3, false, "Finished all characters")
return (string id parsedNumList)
end findBaseName
##########################################
## Capture One General Handlers Version 2019/03/11
on validateCOP5(minCOPversionstr, maxCOPversionstr)
## Copyright 2019 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 debugLogEnable
local COPProcList, theAppRef, numCOPversion, minCOPversion, maxCOPversion
local digit_mult, Version_digit, min_digit, max_digit, copVersionStr
local theAppName, copVersion
tell application "System Events"
if debugLogEnable then
L's loqThis(2, false, ("COP Processes:" & (get U's joinListToString((get name of every process whose name begins with "Capture One" and background only is false), ", "))))
L's loqThis(3, false, ("All Processes: " & (get U's joinListToString((get name of every process whose background only is false), ", "))))
end if
set COPProcList to every process whose name begins with "Capture One" and background only is false
if (count of COPProcList) = 0 then return {hasErrors:true, errorText:(get L's loqqed_Error_Halt5("COP is not running"))}
if (count of COPProcList) ≠1 then return {hasErrors:true, errorText:(get L's loqqed_Error_Halt5("Unexpected: >1 COP instances"))}
set theAppRef to item 1 of COPProcList
set theAppName to ((get name of theAppRef) as text)
set copDetailedVersion to get version of my application theAppName
end tell
tell application "Capture One 12" to set copVersionStr to (get app version)
set copVersion to (word -1 of copVersionStr)
if debugLogEnable then
L's loqThis(2, false, ("theAppName: " & theAppName))
L's loqThis(1, false, copVersionStr)
L's loqThis(2, false, ("Capture One full Version " & copDetailedVersion))
end if
tell U's compareVersion(copVersion, minCOPversionstr, maxCOPversionstr) to set {minVersionPass, maxVersionPass} to {its minVersionPass, its maxVersionPass}
if not minVersionPass then return {hasErrors:true, errorText:(get L's loqqed_Error_Halt5(("This Script does not support Capture One " & copDetailedVersion & " - supported versions are from " & minCOPversionstr & " to " & maxCOPversionstr)))}
if not maxVersionPass then L's loqThis(0, true, ("Caution: Capture One " & copDetailedVersion & " has not been verified yet"))
return {hasErrors:false, theAppName:theAppName, copVersion:copVersion}
end validateCOP5
on validateCOPdoc5(theDocRef, validDocKindList)
## Copyright 2019 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 a document
global debugLogEnable
local COPDocKind_s, COPDocKind_p, COPDocName
if "text" = (get class of theDocRef as text) and (0 = (get count of theDocRef)) then tell application "Capture One 12" to set theDocRef to get current document
try
tell application "Capture One 12" to set {COPDocName, COPDocKind_p} to get {name, kind} of theDocRef
on error errorText number errorNumber
return {hasErrors:true, errorText:(get L's loqqed_Error_Halt5("The Script could not retrieve Capture One document info. Error " & errorNumber & ": "" & errorText & """))}
end try
set COPDocKind_s to convertKindList(COPDocKind_p)
if validDocKindList does not contain COPDocKind_s then return {hasErrors:true, errorText:(get L's loqqed_Error_Halt5((COPDocName & " is a " & COPDocKind_s & " -- not supported by this script")))}
return {hasErrors:false, COPDocName:COPDocName, COPDocKind_s:COPDocKind_s}
end validateCOPdoc5
on validateCOPcollections5(theDocRef)
## Copyright 2019 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 the top level collections
global debugLogEnable
local namesTopCollections, kindsTopCollections_s, countTopCollections, selectedCollectionRef, selectedCollectionIndex, kindSelectedCollection_s, nameSelectedCollection
tell application "Capture One 12" to set {COPDocName, COPDocKind_p} to get {name, kind} of theDocRef
set COPDocKind_s to convertKindList(COPDocKind_p)
tell application "Capture One 12" to tell theDocRef
set selectedCollectionRef to get current collection
if (missing value = selectedCollectionRef) then
try
set current collection to collection "All Images"
on error
set current collection to last collection
end try
set selectedCollectionRef to get current collection
end if
tell selectedCollectionRef to set {nameSelectedCollection, kindSelectedCollection_s} to {name, my convertKindList(kind)}
set {namesTopCollections, kindsTopCollections_s} to {every collection's name, my convertKindList(every collection's kind)}
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
local selectedCollectionMirroredAtTopLast, bottomUserCollectionIndex, topUserCollectionIndex, countFavoriteCollections, namesFavoriteCollections
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 {countFavoriteCollections, namesFavoriteCollections} to {missing value, missing value}
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 countFavoriteCollections to countTopCollections - topUserCollectionIndex
if 0 = countFavoriteCollections then
set namesFavoriteCollections to {}
else
set namesFavoriteCollections to (get items (topUserCollectionIndex + 1) thru countTopCollections of namesTopCollections)
end if
set selectedCollectionMirroredAtTopLast to false
end if
local selectedCollectionIsUser, namesTopUserCollections, kindsTopUserCollections_s, countTopUserCollections
set selectedCollectionIsUser to ¬
(selectedCollectionMirroredAtTopLast or ¬
((selectedCollectionIndex ≥ bottomUserCollectionIndex) and (selectedCollectionIndex ≤ topUserCollectionIndex)))
if topUserCollectionIndex < bottomUserCollectionIndex then
set {topUserCollectionIndex, bottomUserCollectionIndex} to {missing value, missing value}
set {namesTopUserCollections, kindsTopUserCollections_s, countTopUserCollections} to {{}, {}, 0}
else
set {namesTopUserCollections, kindsTopUserCollections_s} to {(get items bottomUserCollectionIndex thru topUserCollectionIndex of namesTopCollections), (get items bottomUserCollectionIndex thru topUserCollectionIndex of kindsTopCollections_s)}
set countTopUserCollections to count of namesTopUserCollections
end if
return {hasErrors:false, namesTopUserCollections:namesTopUserCollections, kindsTopUserCollections_s:kindsTopUserCollections_s, countTopUserCollections:countTopUserCollections, selectedCollectionRef:selectedCollectionRef, selectedCollectionIndex:selectedCollectionIndex, kindSelectedCollection_s:kindSelectedCollection_s, nameSelectedCollection:nameSelectedCollection, selectedCollectionMirroredAtTopLast:selectedCollectionMirroredAtTopLast, selectedCollectionIsUser:selectedCollectionIsUser, bottomUserCollectionIndex:bottomUserCollectionIndex, topUserCollectionIndex:topUserCollectionIndex, countFavoriteCollections:countFavoriteCollections, namesFavoriteCollections:namesFavoriteCollections}
end validateCOPcollections5
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
local kind_s_list, input_is_list, theItem, kind_s1, fail_flag, code_start, kind_list, Kind_s_item, kind_code, kind_type
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)) or (16 > (count of Kind_s_item)) then ¬
error (get L's loqqed_Error_Halt5("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 ¬
error (get L's loqqed_Error_Halt5("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
-
Here is the second part of the script
###############################
## Logging Handlers Version 2019/02/23
on setupLoqqing5()
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## Handler to setup the variables used by the loqqing system at the start of the script
global debugLogEnable, Loqqing, loqResultDocRef, loqResultMethod, loqDialogTextList
## loqResultDocRef is kept separate from Loqqing to allow easy listing of Loqqing
## Default values are entered
set {loqResultDocRef, loqResultMethod} to {false, "not Initialized"}
set Loqqing to {stateResultDoc:false, gateResultsFile:false, enableResultsFile:false, initResultDoc:false, ResultsFileMaxDebug:0, debugLogLevel:0}
set Loqqing to Loqqing & {stateResultsByClipboard:true, gateResultsByClipboard:true, enableResultsByClipboard:true}
set Loqqing to Loqqing & {stateResultsByDialog:false, gateResultsDialog:false, enableResultsByDialog:false, maxDialogPercent:50, maxDialogLines:25, maxDialogChar:1000}
set Loqqing to Loqqing & {stateResultsByNotification:false, gateResultsNotification:false, enableResultsByNotifications:false, enableNotifications:true, notificationsMaxDebug:0}
set Loqqing to Loqqing & {stateResultsByLoq:true, gateParentLoqqing:true, enableResultsByLoq:true, initLoqqing:false, gateLoqqing:true}
return {}
end setupLoqqing5
on InitializeLoqqing5(DocName_Ext, sourceTitle)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## Handler to initialize logging of results
## Do use loq_Results() until the end of the handler and initialising is completed
## Doesn't set loqResultMethod, this is set by caller of this handler
global parent_name, Script_Title, Loqqing, gateScriptProgress, loqDialogTextList, loqResultDocRef
local LogMethods, LogHeader, date_string
tell current application to set date_string to (current date) as text
set LogMethods to {}
set LogHeader to (sourceTitle & " results on " & date_string)
local targetFileWasCreated, TextEditlist, createdLine
if Loqqing's gateResultsFile and Loqqing's enableResultsFile then
set end of LogMethods to DocName_Ext
if not Loqqing's initResultDoc then
set targetFileWasCreated to false
set Loqqing's stateResultDoc to false
set Loqqing's initResultDoc to true
## If TextEdit is already open and has the document open then add the headerline
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 tell document DocName_Ext
set loqResultDocRef to it
tell its text to set paragraph (1 + (count paragraphs)) to return & LogHeader & return
set Loqqing's stateResultDoc to true
end tell
end if
end if
end if
local targetFolderParent_a, targetFolderParent_p, targetFolderName, targetFolder_a, targetFolder_p, ResultDocPath_a, ResultDocPath_p, newFolderRef, newFileRef
if not Loqqing's stateResultDoc or (false = loqResultDocRef) 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 targetFolderParent_a to alias (get path to desktop folder as text)
set targetFolderParent_p to get POSIX path of targetFolderParent_a
set targetFolderName to "ScriptReports"
set targetFolder_p to (targetFolderParent_p & targetFolderName)
set ResultDocPath_p to targetFolder_p & "/" & DocName_Ext
try
set ResultDocPath_a to (get alias POSIX file ResultDocPath_p)
on error
try
set targetFolder_a to (get alias POSIX file targetFolder_p) --x1
on error
tell application "Finder" to set newFolderRef to make new folder at targetFolderParent_a with properties {name:targetFolderName}
set targetFolder_a to newFolderRef as alias
end try
tell application "Finder" to set newFileRef to make new file at targetFolder_a with properties {name:DocName_Ext}
set ResultDocPath_a to newFileRef as alias
set targetFileWasCreated to true
end try
set createdLine to ("Created by " & Script_Title & " on " & date_string)
tell application "TextEdit" -- open the document and add the first line if empty
activate
set loqResultDocRef to open ResultDocPath_a
tell text of loqResultDocRef
if targetFileWasCreated then
set paragraph 1 to createdLine & return & return
else
if (0 = (count of paragraphs)) then set paragraph 1 to createdLine & return & return
end if
end tell
if 2 ≤ Loqqing's debugLogLevel then tell me to log ResultDocPath_p & ": " & createdLine
end tell
set Loqqing's stateResultDoc to true -- prevents initialisation from repeating
tell application "TextEdit" to tell text of loqResultDocRef to ¬
set paragraph (1 + (count paragraphs)) to return & LogHeader & return
end if
end if
if Loqqing's gateResultsFile and Loqqing's stateResultDoc and not Loqqing's enableResultsFile then
set Loqqing's stateResultDoc to false
tell application "TextEdit" to tell text of loqResultDocRef to ¬
set paragraph (1 + (count paragraphs)) to return & parent_name & "Results reporting disabled for: " & Script_Title & return
end if
local screenWidthO, screenHeightO, screenWidth, screenHeight, fontSize_pts, charactersPerLine, dotsPer_Point, linesPerScreen, borderLines
if Loqqing's gateResultsDialog and Loqqing's enableResultsByDialog then
set end of LogMethods to "Dialogs"
if not Loqqing's stateResultsByDialog then
set loqDialogTextList to (get LogHeader & return)
set Loqqing's stateResultsByDialog to true
end if
tell application "Finder" to set {screenWidthO, screenHeightO, screenWidth, screenHeight} to bounds of window of desktop
set fontSize_pts to 12 -- estimated font size for display dialog, including line spacing
set charactersPerLine to 58 -- estimated characters per line fo display dialog, if there are no "return" characters
set dotsPer_Point to 1.5 -- similar for 5K 27" iMac and 11" MBA - similar for others - retina independent
set linesPerScreen to (screenHeight - screenHeightO) / (fontSize_pts * dotsPer_Point)
set borderLines to 5
set Loqqing's maxDialogLines to (get ((Loqqing's maxDialogPercent) / 100 * (linesPerScreen - borderLines)) as integer)
set Loqqing's maxDialogChar to (get ((Loqqing's maxDialogPercent) / 100 * (linesPerScreen - borderLines) * charactersPerLine) as integer)
if 2 ≤ Loqqing's debugLogLevel then log {"maxDialogPercent", Loqqing's maxDialogPercent, "screenHeight", screenHeight, "linesPerScreen", linesPerScreen, "maxDialogLines", Loqqing's maxDialogLines, "maxDialogChar", Loqqing's maxDialogChar}
end if
if Loqqing's gateResultsDialog and Loqqing's stateResultsByDialog and not Loqqing's enableResultsByDialog then
set Loqqing's stateResultsByDialog to false
set loqDialogTextList to ""
end if
if Loqqing's gateResultsByClipboard and Loqqing's enableResultsByClipboard then
set end of LogMethods to "Clipboard"
if not Loqqing's stateResultsByClipboard then
set the clipboard to LogHeader
set Loqqing's stateResultsByClipboard to true
end if
end if
if Loqqing's gateResultsByClipboard and Loqqing's stateResultsByClipboard and not Loqqing's enableResultsByClipboard then
set the clipboard to {}
set Loqqing's stateResultsByClipboard to false
end if
global gateScriptProgress
if not Loqqing's initLoqqing then
set Loqqing's initLoqqing to true
if ("Script Editor" = parent_name) then
set Loqqing's gateParentLoqqing to true
set Loqqing's gateLoqqing to true
set gateScriptProgress to true
try -- 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 tell
end try
else if ("Script Debugger" = parent_name) then
set Loqqing's gateParentLoqqing to false
set Loqqing's gateLoqqing to true
set gateScriptProgress to true
## Avoids compiler errors when Script Debugger is not present
run script "tell application "Script Debugger" to tell first document to set event log visible to true"
run script "tell application "Script Debugger" to tell first document to set event log scope bar visible to true"
else
set Loqqing's gateParentLoqqing to false
set Loqqing's gateLoqqing to false
set gateScriptProgress to false
end if
end if
if Loqqing's gateParentLoqqing and Loqqing's enableResultsByLoq then
set end of LogMethods to " " & parent_name & " Log"
if not Loqqing's stateResultsByLoq then
set Loqqing's stateResultsByLoq to true
log "Results Logging enabled for: " & Script_Title
end if
end if
if Loqqing's gateParentLoqqing and Loqqing's stateResultsByLoq and not Loqqing's enableResultsByLoq then
set Loqqing's stateResultsByLoq to false
log "Results Logging disabled for: " & Script_Title
end if
if (Loqqing's gateResultsNotification and Loqqing's enableResultsByNotifications) or Loqqing's enableNotifications then
set end of LogMethods to "Notifications"
if not Loqqing's stateResultsByNotification then
--display notification "Notifications enabled for: " & Script_Title
set Loqqing's stateResultsByNotification to true
end if
else if Loqqing's stateResultsByNotification then
display notification "Notifications disabled for: " & Script_Title
set Loqqing's stateResultsByNotification to false
end if
set LogMethods_S to U's joinListToString(LogMethods, ", ")
if 2 ≤ Loqqing's debugLogLevel then loqThis(2, false, ("Result Reported by " & LogMethods_S))
return LogMethods_S
end InitializeLoqqing5
on loqThis(thisLogDebugLevel, MakeFront, log_Text)
## Copyright 2019 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 parent_name, Loqqing, loqDialogTextList, loqResultDocRef
local log_Text_S
if thisLogDebugLevel ≤ Loqqing's debugLogLevel then
set log_Text_S to U's joinListToString(log_Text, "; ")
set triggeredDialog to false
if (thisLogDebugLevel = 0) then -- Critical errors
if Loqqing's gateLoqqing then log (log_Text_S)
if Loqqing's enableResultsByClipboard then set the clipboard to ((get the clipboard) & return & (log_Text_S as text))
if Loqqing's enableResultsFile then ¬
tell application "TextEdit" to tell text of loqResultDocRef to ¬
set paragraph (1 + (count paragraphs)) to (log_Text_S & return)
if Loqqing's enableResultsByDialog then display alert log_Text_S giving up after 10
(Loqqing's enableResultsByNotifications and (thisLogDebugLevel ≤ 0))
else if (thisLogDebugLevel ≤ 0) then -- Expected Results
if Loqqing's enableResultsByLoq then log (log_Text_S)
if Loqqing's enableResultsByClipboard then set the clipboard to ((get the clipboard) & return & (log_Text_S as text))
if Loqqing's enableResultsFile then ¬
tell application "TextEdit" to tell text of loqResultDocRef to ¬
set paragraph (1 + (count paragraphs)) to (log_Text_S & return)
if Loqqing's enableResultsByDialog then -- process the dialog last
set loqDialogTextList to (get loqDialogTextList & return & log_Text_S)
if MakeFront or (0 ≥ Loqqing's maxDialogLines) or ¬
(Loqqing's maxDialogLines < (get count of paragraphs of loqDialogTextList)) or ¬
(Loqqing's maxDialogChar < (get length of loqDialogTextList)) then
--tell application "System Events" to set frontmost of process parent_name to true
display dialog loqDialogTextList
set triggeredDialog to true
set loqDialogTextList to ""
end if
end if
else -- debugging information
if Loqqing's gateLoqqing then log (log_Text_S)
if Loqqing's enableResultsFile and ((thisLogDebugLevel ≤ Loqqing's ResultsFileMaxDebug) or not Loqqing's gateLoqqing) then ¬
tell application "TextEdit" to tell text of loqResultDocRef to ¬
set paragraph (1 + (count paragraphs)) to ((log_Text_S as text) & return)
end if
if (Loqqing's enableNotifications and (thisLogDebugLevel ≤ Loqqing's notificationsMaxDebug)) or ¬
(Loqqing's enableResultsByNotifications and (thisLogDebugLevel ≤ 0)) then
set paraCtr to 0
set notString to ""
set lineCnt to 39
set paramax to 3
copy (get paramax * lineCnt) to remChar
repeat with thePara in (get paragraphs of log_Text_S)
set thisCount to (get count of (get contents of thePara))
if thisCount > 0 then
set paraCtr to paraCtr + 1
if thisCount > remChar then copy remChar to thisCount
set notString to notString & (get text 1 thru thisCount of thePara)
set remChar to remChar - lineCnt * (thisCount div lineCnt)
if (0 < (thisCount mod lineCnt)) then set remChar to remChar - lineCnt
if (paramax ≤ paraCtr) or (0 ≥ remChar) then exit repeat
set notString to notString & return
end if
end repeat
display notification notString
end if
if (not triggeredDialog) and (MakeFront or (0 = thisLogDebugLevel)) then --
if Loqqing's gateLoqqing then tell application "System Events" to set frontmost of process parent_name to true
if Loqqing's enableResultsFile then tell application "System Events" to set frontmost of process "TextEdit" to true
end if
else
set log_Text_S to ""
end if
return log_Text_S
end loqThis
on loqqed_Error_Halt5(errorText)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General purpose handler for logging during script termination
global debugLogEnable, Script_Title
try
M's finalCleanup()
end try
tell current application to set date_string to (current date) as text
return (get loqThis(0, true, ("Script "" & Script_Title & "" is exitting at " & date_string & "Reason: " & errorText & return)))
end loqqed_Error_Halt5
####################################
## General Utility Handlers Version 2019/01/12
## No Dependencies on other Libraries
on compareVersion(testVersion_S, minVersion_S, maxVersion_S)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General purpose handler for comparing versions
--local digitMult, testVersionNumber, minVersionNumber, maxVersionNumber, testVersion_LS, minVersion_LS, maxVersion_LS, groupsCount, group_ctr
--local testGroupsCount, minGroupsCount, maxGroupsCount, hasTestGroup, hasMinGroup, hasMaxGroup, testGroupVersion, minGroupVersion, maxGroupVersion
--local allBoundsPass, lowerBoundPass, upperBoundPass, lowerBoundFail, upperBoundFail
if (text ≠(get class of testVersion_S)) or (text ≠(get class of minVersion_S)) or (text ≠(get class of maxVersion_S)) then error "Text inputs only"
set testVersion_LS to (splitStringToList(removeLeadingTrailingSpaces(testVersion_S), "."))
set minVersion_LS to (splitStringToList(removeLeadingTrailingSpaces(minVersion_S), "."))
set maxVersion_LS to (splitStringToList(removeLeadingTrailingSpaces(maxVersion_S), "."))
set testGroupsCount to get count of testVersion_LS
set minGroupsCount to get count of minVersion_LS
set maxGroupsCount to get count of maxVersion_LS
set groupsCount to testGroupsCount
if groupsCount < minGroupsCount then set groupsCount to minGroupsCount
if groupsCount < maxGroupsCount then set groupsCount to maxGroupsCount
set lowerBoundPass to false
set lowerBoundFail to false
set upperBoundPass to false
set upperBoundFail to false
repeat with group_ctr from 1 to groupsCount
set hasTestGroup to (get group_ctr ≤ testGroupsCount)
set hasMinGroup to (get group_ctr ≤ minGroupsCount)
set hasMaxGroup to (get group_ctr ≤ maxGroupsCount)
if hasTestGroup then set testGroupVersion to (get (item group_ctr of testVersion_LS) as integer)
if hasMinGroup then set minGroupVersion to (get (item group_ctr of minVersion_LS) as integer)
if hasMaxGroup then set maxGroupVersion to (get (item group_ctr of maxVersion_LS) as integer)
if not (lowerBoundPass or lowerBoundFail) then
if hasMinGroup and hasTestGroup then
if testGroupVersion > minGroupVersion then set lowerBoundPass to true
if testGroupVersion < minGroupVersion then set lowerBoundFail to true
else if hasMinGroup then -- (and no testGroup) e.g. test 11 , min 11.1
set lowerBoundFail to true -- not symetric with upper bound behaviour
else if hasTestGroup then -- (and no minGroup) e.g. test 11.1, min 11 --> OK
set lowerBoundPass to true
end if
end if
if not (upperBoundPass or upperBoundFail) then
if hasMaxGroup and hasTestGroup then
if testGroupVersion < maxGroupVersion then set upperBoundPass to true
if testGroupVersion > maxGroupVersion then set upperBoundFail to true
else if hasMaxGroup then -- (and no testGroup) e.g. test 11.1 , max 11.1.2 or test 11 , max 11.1
set upperBoundPass to true -- not symetric with lower bound behaviour
else if hasTestGroup then --(and no maxGroup) e.g. test 11.1, max 11 --> pass
set upperBoundPass to true
end if
end if
end repeat
return {maxVersionPass:(not upperBoundFail), minVersionPass:(not lowerBoundFail)}
end compareVersion
on deReference(theItem, theclassName)
## General purpose handler for removing references from a variable
## reusult is a value, or list of values, of the specified class
## Enables data handling of multiple classes of items in the same code
if class = (get class of theclassName) then set theclassName to (get theclassName as text)
if list = (get class of (get theItem)) then
set theResult to {}
set cntItems to length of theItem
if (text = (get class of theclassName)) then
set hasClassList to false
else if (list = (get class of theclassName)) then
if (1 = (get length of theclassName)) then
set hasClassList to false
else if (cntItems = (get length of theclassName)) then
set hasClassList to true
else
error "deReference() can't handle a mismatch between number of items and number of classes"
end if
end if
if not hasClassList then copy (get theclassName as text) to thisclassName
repeat with item_ctr from 1 to cntItems
if hasClassList then copy (get (theclassName's item item_ctr) as text) to thisclassName
copy (get theItem's item item_ctr) to thisItem
if "boolean" = thisclassName then
set the end of theResult to false or (get thisItem as boolean)
else if "integer" = thisclassName then
set the end of theResult to 0 + (get thisItem as integer)
else if "text" = thisclassName then
set the end of theResult to "" & (get thisItem as text)
else if "real" = thisclassName then
set the end of theResult to 0.0 + (get thisItem as real)
else if "date" = thisclassName then
set the end of theResult to (get thisItem as date) + 0
else
error "deReference() can't handle class "" & thisclassName & """
end if
end repeat
else
if "boolean" = theclassName then
set theResult to false or (get theItem as boolean)
else if "integer" = theclassName then
set theResult to 0 + (get theItem as integer)
else if "text" = theclassName then
set theResult to "" & (get theItem as text)
else if "real" = theclassName then
set theResult to 0.0 + (get theItem as real)
else if "date" = theclassName then
set theResult to (get theItem as date) + 0
else
error "deReference() can't handle class "" & theclassName & "" as " & (get class of theclassName)
end if
end if
return theResult
end deReference
on makeList(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 makeList
on splitStringToList(theString, theDelim)
## Public Domain
set theList to {}
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
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
end try
set AppleScript's text item delimiters to astid
return theString
end joinListToString
on removeLeadingTrailingSpaces(theListString)
## 40% faster than a version which trims the string 1 space at a time
## handles both string and list input correctly
-- local theListString, input_is_list, cleanList, thecount, hasTriggered, indexLow, indexHigh
set input_is_list to ("list" = (get (class of theListString) as text))
if not input_is_list then set theListString to {theListString}
set cleanList to {}
repeat with theString in theListString
if ("text" = (get (class of theString) as text)) then
set thecount to (get count of theString)
set hasTriggered to false
repeat with indexLow from 1 to thecount
if " " ≠(get text indexLow of theString as text) then
set hasTriggered to true
exit repeat
end if
end repeat
if not hasTriggered then
set theString to ""
else
repeat with indexHigh from -1 to (-thecount) by -1
if " " ≠(get text indexHigh of theString as text) then exit repeat
end repeat
set theString to text indexLow thru indexHigh of theString
end if
end if
set the end of cleanList to theString
end repeat
if input_is_list then return cleanList
return (get contents of theString)
end removeLeadingTrailingSpaces
on replaceText(this_text, search_string, replacement_string)
set astid to AppleScript's text item delimiters
try
set AppleScript's text item delimiters to the search_string
set the item_list to every text item of this_text
set AppleScript's text item delimiters to the replacement_string
set this_text to the item_list as string
end try
set AppleScript's text item delimiters to astid
return this_text
end replaceText0 -
Heree is an update which is better documented and has more functions.
With this version you can:- have a counter in the sequence name instead of the image numbers
- the sequence name can be copied into the variant's metadata (selectable)
- Making albums for each sequence is selectable
- Making the Unsorted smart album is selectable
Also a couple of bug fixes.
This is the first part, the second part has not changed.
Copy and paste this part into Script Editor's script window, and then copy and paste the second part from thee previous posting.
[color=#BF0040:14fwk4ll]Code Editted once[/color:14fwk4ll]
## Applescript to sequence images in a COP 12 Catalog
## Version 12.22 !! NO GUARANTEE OF SUPPORT !! Best effort
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
-- *** Use and Function
-- select a Project, an album or smart album which is under a Project or Group (or both)
-- Select some variants and run this script
-- If not already present, a group "Stacks" and a smart album "Unsorted" will be created
-- the smart album rules will hide all images with blue or magenta color tag
-- an album with a name starting with SEQ will be created in the group Stacks
-- the album name will contain the lowest and highest numbers sequences from the selected variants
-- the selected variants will be color tagged Blue and copied to the album
-- the Sequence name will be copied into the IPTC Image Scenes metadata of the Variants
-- Once "Unsorted" Exists, continue creating sequences from there, only the unsequenced images will be shown.
-- ***To Setu
-- 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 document
-- Open the Script Editor log window, and select the messages tab
-- 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.
use AppleScript version "2.5"
use scripting additions
property M : missing value
set M to me
global C, L, U
set C to me
set L to me
set U to me
set loqGUIsettings_L to L's setupLoqqing5() -- setup the variables used for Loqqing system
## Values in this section are safe to change, within limits indicated. Support is likely but no commitment
## Default values
### Read Carefully
set enableSeqImageName to true -- enable image based sequence name; disabling results in counter based sequence names
set enableCreateCollections to true -- this enable creating of albums for each sequence, and creating of the "Stacks" group
set enableCreateUnsorted to true -- this enable creating of of the "Unsorted" smart album
set nameStackGroup to "Stacks" -- this is the name of group that holds the albums for each sequence
set nameUnsortedAlbum to "Unsorted" -- this is the name of smart album that shows the not yet sequenced variants
set enableSequenceNameEntry to false -- enables manual entry of sequence names
set seqBaseImageName to "SEQ " -- the base used for image based sequence name
set seqBaseCountedName to "SEQ-" -- the base used for counter based sequence name
set seqCountDigits to 2 -- the number of counter digits in a counter based sequence name
property sequenceCount : 1 -- the starting count in a counter based sequence name
set enableSeqInMetaData to true -- shall the sequence name be written to the IPTC Image Scenes metadata
set enableMetaDataMultiSeq to true -- shall the sequence name be added to other sequences in the IPTC Image Scenes metadata
set Loqqing's debugLogLevel to 0 -- 0...6 Values >1 result in increasing amounts of debug data that takes longer to report
set Loqqing's enableResultsFile to true -- (true/false)
set Loqqing's enableResultsByClipboard to false -- (true/false)
set Loqqing's enableNotifications to true -- (true/false) - enable notifications of errors and exceptions
set Loqqing's notificationsMaxDebug to 1 -- 0...6 suggest not more than 1
set Loqqing's ResultsFileMaxDebug to 3 -- 0...6 suggest not more than 2
set Loqqing's enableResultsByNotifications to false
set Loqqing's enableResultsByLoq to true
set sortedColorTag1 to 5 -- for the stacked images
set sortedColorTag2 to 6 -- for the picked stacked images
--Color Tag
--Criterion
--0--> none
--1-->red
--2-->orange
--3-->yellow
--4-->green
--5-->blue
--6-->magenta
--7-->purple
## ***** 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.
set inpGroup1 to {inpPref:"", inpAlp:false, inpNum:true, inpSpa:false, inpSym:false, inpBck:true, inpCnt:5, inpAct:{{return, "O"}}} -- Take up to 5 alphabetic, symbol or space characters. Then discard them
set inpGroup2 to {inpPref:"", inpAlp:true, inpNum:false, inpSpa:true, inpSym:true, inpBck:true, inpCnt:0, inpAct:{}} -- Take the next number characters, stop on a Symbol or Space or alphabetic character
set inpMethod1 to {"SOOC Parse", {inpGroup1, inpGroup2}} -- parses the image file names typically assigned by a camera
set inpGroup3 to {inpPref:"", inpAlp:false, inpNum:true, inpSpa:false, inpSym:false, inpBck:true, inpCnt:5, inpAct:{{return, "O"}, {"-", "M", 0}}} -- Take up to 5 alphabetic, symbol or space characters. Then discard them. Start over next method if a "-" is found.
set inpGroup4 to {inpPref:"", inpAlp:true, inpNum:false, inpSpa:true, inpSym:true, inpBck:true, inpCnt:0, inpAct:{{"-", "M", 0}}} -- Take the next number characters, stop on a Symbol or Space or alphabetic character. Start over next method if a "-" is found.
set inpGroup5 to {inpPref:"", inpAlp:false, inpNum:false, inpSpa:true, inpSym:true, inpBck:false, inpCnt:4, inpAct:{{return, "O"}, {return, "D"}}} -- Take Alpha and Numbers, stop on Space or symbol or 4 characters. Then discard
set inpGroup6 to {inpPref:"", inpAlp:true, inpNum:false, inpSpa:true, inpSym:true, inpBck:false, inpCnt:3, inpAct:{{"$", "G"}, {return, "O"}, {return, "D"}}} -- Take Only Numbers, stop on Alpha or Space or Symbol or 3 characters. Then discard. Start over if a "$" is found
set inpGroup7 to {inpPref:"", inpAlp:true, inpNum:false, inpSpa:true, inpSym:true, inpBck:true, inpCnt:5, inpAct:{{"$", "G"}}} -- Take Only Numbers, stop on Alpha or Space or Symbol or 5 characters. Start over if a "$" is found
set inpMethod2 to {"Universal Parse", {inpGroup3, inpGroup4}, {inpGroup5, inpGroup6, inpGroup7}} -- combination of Method 3 and Method 1
set inpMethod3 to {"Eric's Detailed Parse", {inpGroup5, inpGroup6, inpGroup7}} -- parses Eric's image file names
set imageNameParsingMethod to inpMethod1 -- OK to change this
set debugLogEnable to true
## Result reporting methods which are valid for this script
set Loqqing's gateResultsFile to true
set Loqqing's gateResultsByClipboard to true
set Loqqing's gateResultsNotification to true
## Reporting methods which are NOT valid for this script
-- disable the reporting method
set Loqqing's gateResultsDialog to false
-- disable user control
set Loqqing's enableResultsByDialog to false -- (true/false)
set Loqqing's maxDialogPercent to 85 -- (0% to 100% of the monitor) The amount of data that triggers a dialog report
##
tell application "System Events" to set parent_name to name of current application
set Script_Title to (get name of me)
set Result_DocName to "CO_Image_Sorting_Report.txt"
set loqResultMethod to L's InitializeLoqqing5(Result_DocName, Script_Title) -- Initialize the results logging system
set {minCOPversion, maxCOPversion} to {"12", "12"}
tell C's validateCOP5(minCOPversion, maxCOPversion)
if its hasErrors then error L's loqqed_Error_Halt5(get its errorText)
set {theAppName, copVersion} to {its theAppName, its copVersion}
end tell
tell application "Capture One 12" to set COPDocRef to get current document
tell C's validateCOPdoc5(COPDocRef, {"Catalog"})
if its hasErrors then error L's loqqed_Error_Halt5(get its errorText)
set {COPDocName, COPDocKind_s} to {its COPDocName, its COPDocKind_s}
end tell
tell C's validateCOPcollections5(COPDocRef) to set {selectedCollectionRef, kindSelectedCollection_s, nameSelectedCollection} to ¬
{its selectedCollectionRef, its kindSelectedCollection_s, its nameSelectedCollection}
tell application "Capture One 12" to set countSelectedVariants to get count of selected variants
tell application "Capture One 12" to set countEveryVariant to count of variants
set startSortRule to "<?xml version="1.0" encoding="UTF-8"?><MatchOperator Kind="AND"><MatchOperator Kind="AND"><Condition Enabled="YES"><Key>IB_S_BASIC_URGENCY</Key><Operator>1</Operator><Criterion>"
set endSortRule to "</Criterion></Condition></MatchOperator></MatchOperator>"
set midSortRule to "</Criterion></Condition></MatchOperator><MatchOperator Kind="AND"><Condition Enabled="YES"><Key>IB_S_BASIC_URGENCY</Key><Operator>1</Operator><Criterion>"
set smartSortRule to startSortRule & sortedColorTag1 & midSortRule & sortedColorTag2 & endSortRule
if enableCreateCollections then
set {theStacksRef, foundStacks, refStacksParent} to findCollection(selectedCollectionRef, nameStackGroup, "group", true, true, {"group", "project"}, "")
if not foundStacks then error L's loqqed_Error_Halt5("Can't find or create the "" & nameStackGroup & "" group")
end if
if enableCreateUnsorted then
set {theUnsortedRef, foundUnsorted, refUnsortedParent} to findCollection(selectedCollectionRef, nameUnsortedAlbum, "smart album", true, true, {"group", "project"}, smartSortRule)
if not foundUnsorted then error L's loqqed_Error_Halt5("Can't find or create the "" & nameUnsortedAlbum & "" smart album")
end if
tell application "Capture One 12" to tell (variants whose selected is true) to set {theVariantsRef, theVariantsName} to {it, its name}
if (0 = (count of theVariantsName)) then
L's loqThis(-1, true, "No variants selected")
else
if enableSeqImageName then
set numdigits to -1
repeat with aName in theVariantsName
set thisCtr to findBaseName(aName, imageNameParsingMethod)
set {thisLen, thisCtr} to {(count of thisCtr), (thisCtr as integer)}
if -1 = numdigits then
set {numdigits, minCtr, maxCtr} to {thisLen, (0 + (thisCtr as integer)), (0 + (thisCtr as integer))}
else
if thisLen > numdigits then set numdigits to thisLen
if thisCtr < minCtr then set minCtr to thisCtr
if thisCtr > maxCtr then set maxCtr to thisCtr
end if
end repeat
set padZeros to U's makeList(numdigits, 0) as string
set sequenceName to seqBaseImageName & (text (-numdigits) thru -1 of (padZeros & minCtr))
if maxCtr > minCtr then set sequenceName to sequenceName & "-" & (text (-numdigits) thru -1 of (padZeros & maxCtr))
else
set padZeros to U's makeList(seqCountDigits, 0) as string
set sequenceName to seqBaseCountedName & (text (-seqCountDigits) thru -1 of (padZeros & sequenceCount))
set sequenceCount to sequenceCount + 1
if enableCreateCollections then
tell application "Capture One 12" to tell theStacksRef to set seqAlbumExists to (0 < (count of (every collection whose name begins with sequenceName)))
if seqAlbumExists then
tell application "Capture One 12" to tell theStacksRef to set seqAlbumName_L to name of every collection whose name begins with seqBaseCountedName
set {ptrFirstNum, maxAlbumCount} to {(1 + (count of seqBaseCountedName)), 1}
repeat with aName in seqAlbumName_L
set {aNamesId_L, ptrChar, lenName} to {(id of aName), (ptrFirstNum + 0), (length of aName)}
repeat while (48 ≤ aNamesId_L's item ptrChar) and (57 ≥ aNamesId_L's item ptrChar)
set ptrChar to ptrChar + 1
if ptrChar > lenName then exit repeat
end repeat
set ptrChar to ptrChar - 1 -- now ptrChar points to the last number character
if ((ptrChar - ptrFirstNum + 1) ≤ seqCountDigits) and ((ptrChar - ptrFirstNum) > 0) then
set albumsCount to (string id (items ptrFirstNum thru ptrChar of aNamesId_L)) as integer
if albumsCount > maxAlbumCount then set maxAlbumCount to albumsCount
end if
end repeat
set sequenceCount to maxAlbumCount + 2
set sequenceName to seqBaseCountedName & (text (-seqCountDigits) thru -1 of (padZeros & (maxAlbumCount + 1)))
end if
end if
end if
set userCanceled to false
if enableSequenceNameEntry then
try
set dialogResult to display dialog ¬
"Sequence Name" buttons {"Cancel", "OK"} ¬
default button "OK" cancel button "Cancel" default answer sequenceName
on error number -128
set userCanceled to true
end try
set sequenceName to dialogResult's text returned
end if
if not userCanceled then
set gotSequenceAlbum to false
if enableCreateCollections then
set {refSequenceAlbum, gotSequenceAlbum, refSeqParent} to findCollection(theStacksRef, sequenceName, "album", true, false, {"group"}, "")
if gotSequenceAlbum then
tell application "Capture One 12" to add inside refSequenceAlbum variants theVariantsRef
L's loqThis(-1, false, "Copied " & (count of theVariantsRef) & " variants to "" & sequenceName & """)
else
error L's loqqed_Error_Halt5("Can't create the "" & sequenceName & "" album")
end if
end if
if enableSeqInMetaData then
tell application "Capture One 12"
if enableMetaDataMultiSeq then
set oneByOne_L to get variants whose (image scenes is not "") and (selected is true)
tell (variants whose selected is true and image scenes is "") to set its image scenes to sequenceName
repeat with aVariant in oneByOne_L
tell aVariant
set the aVarSeq_L to its image scenes
if aVarSeq_L does not contain sequenceName then set its image scenes to (aVarSeq_L & "," & sequenceName)
end tell
end repeat
else
tell (variants whose selected is true) to set its image scenes to sequenceName
end if
end tell
end if
try
tell application "Capture One 12" to tell (variants whose selected is true) to set its color tag to sortedColorTag1 -- important to do this last to avoid the effect of color filtering
on error
if gotSequenceAlbum then
tell application "Capture One 12" to tell refSequenceAlbum to tell its variants to set its color tag to sortedColorTag1
else
error L's loqqed_Error_Halt5("Unable to set the color tag because the variants have disappeared from the collection. Likely cause is a filter that has been set on the Image Scenes MetaData")
end if
end try
end if
end if
################ The End ###############
on findCollection(thisCollRef, nameTargetColl, kindTargetColl, enableCreate, enableSearch, kindParentColl_L, saSortRule)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General purpose handler to find and create a collection, starting from some arbitrary collection
global debugLogEnable
local thisCollKind, thisCollKind_S, thisCollName, subCollNames, subCollRef, refFoundColl, errorText, parentString, theParentRef, parentList, nextRef, refCtr, lastRef, hasFoundColl, cntColl, docMark, docName
local saRules, saNoRules, debugText
tell application "Capture One 12" to tell thisCollRef to set {thisCollKind, thisCollName, subCollNames, subCollRef, refParentColl} to {its kind, its name, name of its collections, its collections, it}
set thisCollKind_S to C's convertKindList(thisCollKind)
set debugText to kindTargetColl & " "" & nameTargetColl & "" in " & thisCollKind_S & " "" & thisCollName & """
if debugLogEnable then L's loqThis(2, false, return & "Find Collection: " & debugText & " with Create: " & enableCreate & " Search: " & enableSearch & " Sort Rule: " & (count of saSortRule) & " characters")
set isChevronForm to ("«" = text 1 of (thisCollKind as text))
if debugLogEnable then
L's loqThis(3, false, "SubCollections {" & U's joinListToString(subCollNames, "; ") & "}")
L's loqThis(3, false, "Chevron Form: " & isChevronForm)
end if
set refFoundColl to {}
if (kindParentColl_L contains thisCollKind_S) and (subCollNames contains nameTargetColl) then
tell application "Capture One 12" to tell thisCollRef
if ("project" = kindTargetColl) then set refFoundColl to (refFoundColl & (every collection whose name is nameTargetColl and kind is project))
if ("group" = kindTargetColl) then set refFoundColl to (refFoundColl & (every collection whose name is nameTargetColl and kind is group))
if ("album" = kindTargetColl) then set refFoundColl to (refFoundColl & (every collection whose name is nameTargetColl and kind is album))
if ("smart album" = kindTargetColl) then set refFoundColl to (refFoundColl & (every collection whose name is nameTargetColl and kind is smart album))
end tell
end if
set hasFoundColl to false
if (1 = (count of refFoundColl)) then set {refFoundColl, hasFoundColl} to {(item 1 of refFoundColl), true}
if hasFoundColl then
if debugLogEnable then L's loqThis(1, false, "Found " & debugText)
else if (not enableCreate) and (not enableSearch) then
L's loqThis(0, false, "The " & kindTargetColl & " "" & nameTargetColl & "" is missing from " & thisCollKind_S & " "" & thisCollName & """)
else
if (not hasFoundColl) and enableCreate and (kindParentColl_L contains thisCollKind_S) then
if debugLogEnable then L's loqThis(2, false, "Attempting to Create the collection")
set collCreated to false
tell application "Capture One 12" to tell thisCollRef
set saNoRules to "<?xml version="1.0" encoding="UTF-8"?><MatchOperator Kind="AND"></MatchOperator>"
if ("group" = kindTargetColl) then set {collCreated, refFoundColl} to {true, {make new collection with properties {kind:group, name:nameTargetColl}}}
if ("album" = kindTargetColl) then set {collCreated, refFoundColl} to {true, {make new collection with properties {kind:album, name:nameTargetColl}}}
if ("smart album" = kindTargetColl) then
set saNoRules to "<?xml version="1.0" encoding="UTF-8"?><MatchOperator Kind="AND"></MatchOperator>"
try
if (text 1 thru 10 of saNoRules) = (text 1 thru 10 of saSortRule) then set saRules to saSortRule
on error
set saRules to saNoRules
end try
set ctrSaName to 1
repeat until ({} = (every collection whose name is nameTargetColl and kind is album))
set {nameTargetColl, ctrSaName} to {(nameTargetColl & ctrSaName), ctrSaName + 1}
if 10 < ctrSaName then error
end repeat
set refFoundColl to (every collection whose name is nameTargetColl and kind is smart album)
if (0 = (count of refFoundColl)) then set {collCreated, refFoundColl} to {true, {make new collection with properties {kind:smart album, name:nameTargetColl, rules:saRules}}}
if (1 < ctrSaName) then set debugText to kindTargetColl & " "" & nameTargetColl & "" in " & thisCollKind_S & " "" & thisCollName & """
end if
try
if ("project" = kindTargetColl) then set refFoundColl to {make new collection with properties {kind:project, name:nameTargetColl}}
set collCreated to true
on error errorText
if debugLogEnable then L's loqThis(2, false, "Creation failed with: " & errorText)
end try
end tell
if (1 = (count of refFoundColl)) then
set {refFoundColl, hasFoundColl} to {(item 1 of refFoundColl), true}
if collCreated then L's loqThis(1, false, "Created " & debugText)
end if
end if
if (not hasFoundColl) and enableSearch then
if debugLogEnable then L's loqThis(2, false, "Starting Search")
try
get || of {thisCollRef}
on error errorText
end try
if isChevronForm then set errorText to U's replaceText(errorText, "«class COcl»", "collection")
set parentList to U's splitStringToList(errorText, {"of", "{", "}"})
repeat with refCtr from (count of parentList) to 0 by -1
try
if "document" = first word of item refCtr of parentList then exit repeat
end try
end repeat
if 0 = (get (contents of refCtr) as integer) then error L's loqqed_Error_Halt5("Didn't find the document reference in " & errorText)
set docMark to contents of refCtr
set docName to U's removeLeadingTrailingSpaces((get item 2 of U's splitStringToList((U's removeLeadingTrailingSpaces((get item docMark of parentList))), """)))
tell application "Capture One 12" to set lastRef to document docName
set cntColl to 0
repeat with refCtr from 3 to docMark - 1
try
if "collection" = (first word of item refCtr of parentList) then
set cntColl to cntColl + 1
if 2 ≤ cntColl then
tell application "Capture One 12" to set nextRef to collection id (get last word of parentList's item refCtr) of lastRef
copy nextRef to lastRef
end if
end if
end try
end repeat
if (0 = cntColl) then
L's loqThis(0, false, "Unable to identify parent collection")
else
set {refFoundColl, hasFoundColl, refParentColl} to my findCollection(lastRef, nameTargetColl, kindTargetColl, enableCreate, enableSearch, kindParentColl_L, saSortRule)
end if
end if
end if
if not hasFoundColl then set refFoundColl to missing value
return {refFoundColl, hasFoundColl, refParentColl}
end findCollection
on findBaseName(theString, theMethod_L)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General purpose handler to parse an image file name
## Looks long, but execution time is about 20 - 80 microseconds
## theMethod_L is a list of Methods. Each Method describes a parsing method (a particular way of parsing a string)
## Each Method is a list of Groups, each Group is a record that describes how to find the end of a group of characters.
## While a group runs each character is copied from theString to the output until the end is found.
## The algorithm starts with Group 1 of Method 1. When a Group ends the next one starts
## When there are no Groups left or no Characters left, the algorithm stops reading characters and returns the output characters.
## The Group record: {inpAlp, inpNum, inpSpa, inpSym, inpCnt, inpBck, inpAct}:
## (inpAlp, inpNum, inpSpa, inpSym, inpBck) boolean, (inpCnt) integer, inpAct a list of Actions {{X,Y,Z},{X,Y,Z},...}
#3 Character Type Tests are evaluated first, then Actions
## A group ends upon finding Alphabetic characters (if inpAlp true), or Numbers (if inpNum true), or a Space(if inpSpa true),
## or Symbols (if inpSym true), or finding more than inpCnt characters (if inpCnt >0).
## If inpBck is true, when a group ends, the last character becomes part of the next group
## EG Group3: {inpAlp:false, inpNum:false, inpSpa:false, inpSym:true, inpBck:true, inpCnt:9, inpAct:aList}
## Means: stop if a symbol is found or after the 9th character, and the last character becomes the first character of the next group
## Thus up to 9 Numbers, Spaces and Alphabetic characters are accepted, and 9th character or symbol is part of the next group.
## if Group 3 is the last Group, then 9th or symbol character (and all subsequent characters) are dropped.
## inpAct contains Alists {X,Y,Z}, each describes an action to execute when finding some character. inpAct= {} results in no actions
## X is the character that triggers an action; Y is the Action; Z optionally specifies "how much"
## X= return triggers the action on a Group Increment from Character Type Tests
## Actions (1): T-Translate Character to "Z" ; G- Switch to Group Z ; M- Switch to Method Z ; I- Increment group by Z;
## B- Drop Back 1 character; J- Jump ahead Z characters, D- Drop trigger Characters, O - Drop Output
## Actions A,B,C,N,S and P set the value of inpAlp, inpBck, inpCnt, inpNum, inpSpa and inpSym
## If the Alist has no "z" value: for "I", z is taken as 1; for "J", z is taken as the length of the trigger; for "T" z is taken as "";
## for G, z is taken as "0"; for M, the method is incremented, output and trigger are dropped
## for all other commands z is taken as true when missing
## Selecting Group < 1 resets the parser to Group 1 and clears all output. If inpBck is true, the current character is dropped.
## Selecting Method 0 triggers a Method reset, output and input are reset, and then the previous method is incremented
## Switching to Method whose number does not exist causes the previous output to be cleared, the remaining input characters are accepted
## E.G. For inpAct:{{"$","M"},{"-","T"}} in Method 1 --> when an "$" is encountered, start over again with Group 1 of Method 2, also drop every "-"
## If there is no Method 2, all remaining characters of the input are taken as output.
global debugLogEnable
local theCharIDList, stringCount, thisCharsId, parsedNumList, charPointer, iterationCtr, SafetyLimit
local Alpha, Numb, Blank, Symbol, MaxCnt, backOneOnGrpInc, ActionList, AlistTriggerId_L, anAlist, theAction, Prefix
local thisMethod, MethodCount, thisGroup, GroupCount, hasMaxCnt, countChars, stringCount
local triggerMethodInit, triggerGroupInit, triggerGroupInc, validChar, newCharIDs, hasNewChars, nextGroup, nextMethod, nextCharPointer
local triggerBack1Char, hasPar3, valuePar3, endTriggerPointer, lenActTrigger, actionString, hasActionTrigger, theActionTrigger, nextCharJump, hasJump, triggerOutputDrop, lenActTrigger
set {theCharIDList, stringCount, nextMethod, MethodCount, triggerMethodInit, parsedNumList} to ¬
{(get id of theString), (get count of theString), 1, (count of lists of theMethod_L), true, {}}
set {charPointer, iterationCtr, SafetyLimit} to {1, 0, (MethodCount * stringCount * 4)}
if debugLogEnable then
L's loqThis(2, false, "Parsing "" & theString & """)
if (0 < (count of strings of theMethod_L)) then L's loqThis(2, false, ("Using Method " & (string 1 of theMethod_L)))
L's loqThis(3, false, {"Initialising: stringCount " & stringCount & ", Method " & nextMethod & ", MethodCount " & MethodCount})
end if
repeat while (charPointer ≤ stringCount) and (iterationCtr < SafetyLimit)
set iterationCtr to iterationCtr + 1
if triggerMethodInit then set {nextGroup, GroupCount, triggerGroupInit, thisMethod} to ¬
{1, (count of records of theMethod_L's list nextMethod), true, nextMethod}
if debugLogEnable and triggerMethodInit then L's loqThis(3, false, "Start Method #" & nextMethod & ", Group Count: " & (count of theMethod_L's list nextMethod))
if triggerGroupInit then
tell theMethod_L's list thisMethod's record nextGroup to set {Prefix, Alpha, Numb, Blank, Symbol, MaxCnt, hasMaxCnt, backOneOnGrpInc, ActionList, countChars, thisGroup} to ¬
{its inpPref, its inpAlp, its inpNum, its inpSpa, its inpSym, its inpCnt, (0 < its inpCnt), its inpBck, its inpAct, 0, nextGroup}
if debugLogEnable then L's loqThis(3, false, "Start Group #" & thisGroup & " , Prefix "" & Prefix & "", Alpha " & Alpha & ", Number " & Numb & ", Blank " & Blank & ", Symbol " & Symbol & ", Max Count " & MaxCnt & ", has Max Count " & hasMaxCnt & ", Drop Back Last Character " & backOneOnGrpInc & " , ActionList : " & (count of ActionList) & " action lists")
if (0 < (count of ActionList)) and ("list" ≠((class of ActionList's item 1) as text)) then error "Group " & thisGroup & " has an incorrectly formatted Action List"
set parsedNumList to parsedNumList & (id of Prefix)
end if
set {triggerBack1Char, triggerGroupInc, triggerGroupInit, triggerMethodInit, validChar, newCharIDs, hasNewChars, hasActionTrigger, theActionTrigger, nextCharJump, hasJump, triggerOutputDrop} to ¬
{false, false, false, false, true, {}, false, false, missing value, 0, false, false}
set {thisCharsId, countChars, nextCharPointer, lenActTrigger} to {(theCharIDList's item charPointer), (countChars + 1), charPointer, 1}
if debugLogEnable then L's loqThis(5, false, "Character " & charPointer & " : "" & (string id (get theCharIDList's item charPointer)) & """)
if hasMaxCnt and (countChars ≥ MaxCnt) then -- if the counter has triggered, don't check the character groups
set triggerGroupInc to true
if debugLogEnable then L's loqThis(3, false, "Count triggers Group Increment")
else -- check the character type
if debugLogEnable then L's loqThis(3, false, "Character Type Check")
if ((thisCharsId ≥ 65) and (thisCharsId ≤ 90)) or ((thisCharsId ≥ 97) and (thisCharsId ≤ 122)) or (thisCharsId ≥ 192) then -- a latin character
if Alpha then set triggerGroupInc to true
if debugLogEnable and Alpha then L's loqThis(4, false, "Alpha triggers Group Increment")
else if ((thisCharsId ≥ 48) and (thisCharsId ≤ 57)) then -- a number
if Numb then set triggerGroupInc to true
if debugLogEnable and Numb then L's loqThis(4, false, "Number triggers Group Increment")
else if thisCharsId = 32 then --- its a space
if Blank then set triggerGroupInc to true
if debugLogEnable and Blank then L's loqThis(4, false, "Space triggers Group Increment")
else if thisCharsId > 32 then -- its a symbol
if Symbol then set triggerGroupInc to true
if debugLogEnable and Symbol then L's loqThis(4, false, "Symbol triggers Group Increment")
end if
end if
repeat with anAlist in ActionList
set {AlistTriggerId_L, endTriggerPointer} to {((id of contents of anAlist's item 1) as list), (charPointer + (count of anAlist's item 1) - 1)}
if (endTriggerPointer ≤ stringCount) and ((not hasActionTrigger) or (theActionTrigger = AlistTriggerId_L)) and ¬
((triggerGroupInc and ({13} = AlistTriggerId_L)) or (AlistTriggerId_L = (theCharIDList's items charPointer thru endTriggerPointer))) ¬
then
if debugLogEnable and not hasActionTrigger then L's loqThis(4, false, "Action List is triggered by "" & (anAlist's item 1) & """)
if not hasActionTrigger then set {hasActionTrigger, theActionTrigger, lenActTrigger} to {true, AlistTriggerId_L, (count of AlistTriggerId_L)}
set {theAction, hasPar3, valuePar3} to {(item 2 of anAlist), (3 ≤ (count of anAlist)), true}
if hasPar3 then set valuePar3 to (contents of item 3 of anAlist)
if debugLogEnable then
if hasPar3 then
set actionString to "Action "" & (contents of item 2 of anAlist) & "" ; "" & (contents of item 3 of anAlist) & """
else
set actionString to "Action "" & (contents of item 2 of anAlist) & """
end if
end if
## Group 1 - Character Control - Does Not clear triggerGroupInc
if ("A" = theAction) then
set Alpha to valuePar3
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpAlp to " & valuePar3)
else if "B" = theAction then --
set backOneOnGrpInc to valuePar3
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpBck to " & valuePar3)
else if ("C" = theAction) then
if (not hasPar3) or (0 = valuePar3) then
set {MaxCnt, hasMaxCnt} to {0, false}
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpCnt to 0")
else if (0 < valuePar3) then
set {MaxCnt, hasMaxCnt} to {valuePar3, true}
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpCnt to " & valuePar3)
else if (0 > valuePar3) then
set {MaxCnt, hasMaxCnt} to {(MaxCnt - valuePar3), true}
if debugLogEnable then L's loqThis(5, false, actionString & " - Increment inpCnt by " & (-valuePar3))
end if
else if ("N" = theAction) then
if hasPar3 then set Numb to valuePar3
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpNum to " & valuePar3)
else if ("P" = theAction) then
set Symbol to valuePar3
if debugLogEnable then L's loqThis(5, false, actionString & "- Set inpSym to " & valuePar3)
else if ("Q" = theAction) then
if debugLogEnable then L's loqThis(4, false, actionString & "- Quit Actions")
exit repeat
else if "S" = theAction then
set Blank to valuePar3
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpSpa to " & valuePar3)
## Group 2 - Character Control - clears triggerGroupInc on multi character trigger
else if "D" = theAction then --
set validChar to not valuePar3
if 1 < lenActTrigger then set triggerGroupInc to false
if debugLogEnable then L's loqThis(5, false, actionString & " - Drop Action Trigger: " & valuePar3)
else if "O" = theAction then --
set triggerOutputDrop to valuePar3
if 1 < lenActTrigger then set triggerGroupInc to false
if debugLogEnable then L's loqThis(5, false, actionString & " - Drop Output characters")
else if "J" = theAction then --
if not hasPar3 then set valuePar3 to lenActTrigger
set {nextCharJump, hasJump} to {valuePar3, true}
if 1 < lenActTrigger then set triggerGroupInc to false
if debugLogEnable then L's loqThis(5, false, actionString & " - Jump forward " & valuePar3 & " Characters")
else if "T" = theAction then -- translate/drop this character
if not hasPar3 then set valuePar3 to ""
set {newCharIDs, hasNewChars} to {(id of valuePar3), true}
if 1 < lenActTrigger then set triggerGroupInc to false
if debugLogEnable then L's loqThis(5, false, actionString & " - Translate characters to "" & valuePar3 & """)
## Group 3 - Group & Method control, clears triggerGroupInc - later
else if "G" = theAction then -- new group
if not hasPar3 then set valuePar3 to 0
set {nextGroup, triggerGroupInit} to {valuePar3, true}
if debugLogEnable then L's loqThis(5, false, actionString & " - Switch to Group " & valuePar3)
else if "I" = theAction then -- increment group
if not hasPar3 then set valuePar3 to 1
set {nextGroup, triggerGroupInit} to {(thisGroup + valuePar3), true}
if debugLogEnable then L's loqThis(5, false, actionString & " - Increment Group to " & nextGroup)
else if "M" = theAction then -- set method
if not hasPar3 then set valuePar3 to (thisMethod + 1)
set {nextMethod, triggerMethodInit, triggerOutputDrop, validChar} to {valuePar3, true, true, false}
if debugLogEnable then L's loqThis(5, false, actionString & " - Switch to Method " & valuePar3 & " and drop output and trigger")
else
if debugLogEnable then L's loqThis(3, false, actionString & " - Command not found ")
end if
end if
end repeat
if (lenActTrigger > 1) then set countChars to countChars - 1 + lenActTrigger
if hasMaxCnt and (countChars ≥ MaxCnt) then set {triggerGroupInc} to {true, L's loqThis(3, false, "Character count triggers Group Increment")}
if (triggerGroupInit or triggerMethodInit) then set triggerGroupInc to false
if (triggerGroupInc or triggerGroupInit or triggerMethodInit) and backOneOnGrpInc then set triggerBack1Char to true
if (0 ≥ nextMethod) then
if debugLogEnable then L's loqThis(3, false, "Executing Method Reset - Increment Method, Clear output, Reset Input Queue")
set {parsedNumList, nextMethod, triggerMethodInit, nextCharPointer} to {{}, (thisMethod + 1), true, 1, false}
set {triggerBack1Char, triggerGroupInc, hasJump, hasNewChars, hasMaxCnt} to {false, false, false, false, false}
else
if (0 ≥ nextGroup) then
set {triggerBack1Char, validChar, triggerGroupInc, hasJump, hasNewChars} to {false, false, false, false, false}
if debugLogEnable then L's loqThis(3, false, "Executing Group Reset - Switch to Group 1, Clear output , dropping back " & (-nextGroup) & " characters")
if (-1 ≥ nextGroup) then set {nextCharJump, hasJump} to {(nextGroup), true}
set {parsedNumList, nextGroup, triggerGroupInit} to {{}, 1, true}
end if
if triggerGroupInc then set {nextGroup, triggerGroupInit} to {thisGroup + 1, true, L's loqThis(3, false, "Executing Group Increment - Switch to Group " & (thisGroup + 1))}
if debugLogEnable and triggerOutputDrop then L's loqThis(4, false, "Dropped Output")
if triggerOutputDrop then set parsedNumList to {}
if triggerBack1Char then
set {validChar, nextCharPointer} to {false, charPointer}
if debugLogEnable then L's loqThis(3, false, "Drop Back 1 Character")
else if (hasJump or hasNewChars) then
set validChar to false
if (hasJump and (((thisGroup ≠nextGroup) or (thisMethod ≠nextMethod) or (0 < nextCharJump)))) then
set nextCharPointer to (charPointer + nextCharJump)
if nextCharPointer < 1 then set nextCharPointer to 1
if hasNewChars and (0 < nextCharJump) and (lenActTrigger > nextCharJump) then set nextCharPointer to (charPointer + lenActTrigger)
else if hasJump then
## do nothing - can't move charPointer back
if debugLogEnable then L's loqThis(3, false, "Unable to move back " & (-nextCharJump) & " characters - infinite loop")
else if hasNewChars then -- and not hasJump
set nextCharPointer to (charPointer + lenActTrigger)
end if
if debugLogEnable then L's loqThis(3, false, "Input Moved Forward " & (nextCharPointer - charPointer) & " characters")
else
set nextCharPointer to (charPointer + 1)
if debugLogEnable then L's loqThis(5, false, "Input Moved Forward 1 character")
end if
if hasNewChars then set {parsedNumList} to {(parsedNumList & newCharIDs), L's loqThis(4, false, "Added new Characters to the output")}
if validChar then set end of parsedNumList to thisCharsId
if debugLogEnable and not validChar then L's loqThis(3, false, "The input character has been dropped")
end if
if debugLogEnable then L's loqThis(3, false, "The output is: "" & (string id parsedNumList) & """)
if nextMethod > MethodCount then -- replace the output with every remaining character
if debugLogEnable then L's loqThis(3, false, "Got Method " & nextMethod & " Return remaining characters")
if charPointer > 1 then set parsedNumList to (theCharIDList's items charPointer thru stringCount)
if charPointer ≤ 1 then copy theCharIDList to parsedNumList
exit repeat
else if (nextGroup > GroupCount) then
if debugLogEnable then L's loqThis(3, false, "Finished all groups, exitting")
exit repeat
end if
set charPointer to nextCharPointer -- must be last statement in the the loop
end repeat
if debugLogEnable and (charPointer > stringCount) then L's loqThis(3, false, "Finished all characters")
return (string id parsedNumList)
end findBaseName
##########################################
## Capture One General Handlers Version 2019/03/11
on validateCOP5(minCOPversionstr, maxCOPversionstr)
## Copyright 2019 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 debugLogEnable
local COPProcList, theAppRef, numCOPversion, minCOPversion, maxCOPversion
local digit_mult, Version_digit, min_digit, max_digit, copVersionStr
local theAppName, copVersion
tell application "System Events"
if debugLogEnable then
L's loqThis(2, false, ("COP Processes:" & (get U's joinListToString((get name of every process whose name begins with "Capture One" and background only is false), ", "))))
L's loqThis(3, false, ("All Processes: " & (get U's joinListToString((get name of every process whose background only is false), ", "))))
end if
set COPProcList to every process whose name begins with "Capture One" and background only is false
if (count of COPProcList) = 0 then return {hasErrors:true, errorText:(get L's loqqed_Error_Halt5("COP is not running"))}
if (count of COPProcList) ≠1 then return {hasErrors:true, errorText:(get L's loqqed_Error_Halt5("Unexpected: >1 COP instances"))}
set theAppRef to item 1 of COPProcList
set theAppName to ((get name of theAppRef) as text)
set copDetailedVersion to get version of my application theAppName
end tell
tell application "Capture One 12" to set copVersionStr to (get app version)
set copVersion to (word -1 of copVersionStr)
if debugLogEnable then
L's loqThis(2, false, ("theAppName: " & theAppName))
L's loqThis(1, false, copVersionStr)
L's loqThis(2, false, ("Capture One full Version " & copDetailedVersion))
end if
tell U's compareVersion(copVersion, minCOPversionstr, maxCOPversionstr) to set {minVersionPass, maxVersionPass} to {its minVersionPass, its maxVersionPass}
if not minVersionPass then return {hasErrors:true, errorText:(get L's loqqed_Error_Halt5(("This Script does not support Capture One " & copDetailedVersion & " - supported versions are from " & minCOPversionstr & " to " & maxCOPversionstr)))}
if not maxVersionPass then L's loqThis(0, true, ("Caution: Capture One " & copDetailedVersion & " has not been verified yet"))
return {hasErrors:false, theAppName:theAppName, copVersion:copVersion}
end validateCOP5
on validateCOPdoc5(theDocRef, validDocKindList)
## Copyright 2019 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 a document
global debugLogEnable
local COPDocKind_s, COPDocKind_p, COPDocName
if "text" = (get class of theDocRef as text) and (0 = (get count of theDocRef)) then tell application "Capture One 12" to set theDocRef to get current document
try
tell application "Capture One 12" to set {COPDocName, COPDocKind_p} to get {name, kind} of theDocRef
on error errorText number errorNumber
return {hasErrors:true, errorText:(get L's loqqed_Error_Halt5("The Script could not retrieve Capture One document info. Error " & errorNumber & ": "" & errorText & """))}
end try
set COPDocKind_s to convertKindList(COPDocKind_p)
if validDocKindList does not contain COPDocKind_s then return {hasErrors:true, errorText:(get L's loqqed_Error_Halt5((COPDocName & " is a " & COPDocKind_s & " -- not supported by this script")))}
return {hasErrors:false, COPDocName:COPDocName, COPDocKind_s:COPDocKind_s}
end validateCOPdoc5
on validateCOPcollections5(theDocRef)
## Copyright 2019 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 the top level collections
global debugLogEnable
local namesTopCollections, kindsTopCollections_s, countTopCollections, selectedCollectionRef, selectedCollectionIndex, kindSelectedCollection_s, nameSelectedCollection
tell application "Capture One 12" to set {COPDocName, COPDocKind_p} to get {name, kind} of theDocRef
set COPDocKind_s to convertKindList(COPDocKind_p)
tell application "Capture One 12" to tell theDocRef
set selectedCollectionRef to get current collection
if (missing value = selectedCollectionRef) then
try
set current collection to collection "All Images"
on error
set current collection to last collection
end try
set selectedCollectionRef to get current collection
end if
tell selectedCollectionRef to set {nameSelectedCollection, kindSelectedCollection_s} to {name, my convertKindList(kind)}
set {namesTopCollections, kindsTopCollections_s} to {every collection's name, my convertKindList(every collection's kind)}
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
local selectedCollectionMirroredAtTopLast, bottomUserCollectionIndex, topUserCollectionIndex, countFavoriteCollections, namesFavoriteCollections
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 {countFavoriteCollections, namesFavoriteCollections} to {missing value, missing value}
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 countFavoriteCollections to countTopCollections - topUserCollectionIndex
if 0 = countFavoriteCollections then
set namesFavoriteCollections to {}
else
set namesFavoriteCollections to (get items (topUserCollectionIndex + 1) thru countTopCollections of namesTopCollections)
end if
set selectedCollectionMirroredAtTopLast to false
end if
local selectedCollectionIsUser, namesTopUserCollections, kindsTopUserCollections_s, countTopUserCollections
set selectedCollectionIsUser to ¬
(selectedCollectionMirroredAtTopLast or ¬
((selectedCollectionIndex ≥ bottomUserCollectionIndex) and (selectedCollectionIndex ≤ topUserCollectionIndex)))
if topUserCollectionIndex < bottomUserCollectionIndex then
set {topUserCollectionIndex, bottomUserCollectionIndex} to {missing value, missing value}
set {namesTopUserCollections, kindsTopUserCollections_s, countTopUserCollections} to {{}, {}, 0}
else
set {namesTopUserCollections, kindsTopUserCollections_s} to {(get items bottomUserCollectionIndex thru topUserCollectionIndex of namesTopCollections), (get items bottomUserCollectionIndex thru topUserCollectionIndex of kindsTopCollections_s)}
set countTopUserCollections to count of namesTopUserCollections
end if
return {hasErrors:false, namesTopUserCollections:namesTopUserCollections, kindsTopUserCollections_s:kindsTopUserCollections_s, countTopUserCollections:countTopUserCollections, selectedCollectionRef:selectedCollectionRef, selectedCollectionIndex:selectedCollectionIndex, kindSelectedCollection_s:kindSelectedCollection_s, nameSelectedCollection:nameSelectedCollection, selectedCollectionMirroredAtTopLast:selectedCollectionMirroredAtTopLast, selectedCollectionIsUser:selectedCollectionIsUser, bottomUserCollectionIndex:bottomUserCollectionIndex, topUserCollectionIndex:topUserCollectionIndex, countFavoriteCollections:countFavoriteCollections, namesFavoriteCollections:namesFavoriteCollections}
end validateCOPcollections5
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
local kind_s_list, input_is_list, theItem, kind_s1, fail_flag, code_start, kind_list, Kind_s_item, kind_code, kind_type
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)) or (16 > (count of Kind_s_item)) then ¬
error (get L's loqqed_Error_Halt5("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 ¬
error (get L's loqqed_Error_Halt5("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 convertKindList0 -
Bug Fix. Complete rewrite of the findCollection() handler.
This is the first part of the script, a complete replacement for the script from postings 1 or 3. The second part of the script has not changed.
Copy and paste the script below into Script Editor's script window, and then copy and paste the script from posting 2.
## Applescript to sequence images in a COP 12 Catalog
## Version 12.23 !! NO GUARANTEE OF SUPPORT !! Best effort
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
-- *** Use and Function
-- select a Project, an album or smart album which is under a Project or Group (or both)
-- Select some variants and run this script
-- If not already present, a group "Stacks" and a smart album "Unsorted" will be created
-- the smart album rules will hide all images with blue or magenta color tag
-- an album with a name starting with SEQ will be created in the group Stacks
-- the album name will contain the lowest and highest numbers sequences from the selected variants
-- the selected variants will be color tagged Blue and copied to the album
-- the Sequence name will be copied into the IPTC Image Scenes metadata of the Variants
-- Once "Unsorted" Exists, continue creating sequences from there, only the unsequenced images will be shown.
-- ***To Setu
-- 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 document
-- Open the Script Editor log window, and select the messages tab
-- 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.
use AppleScript version "2.5"
use scripting additions
property M : missing value
set M to me
global C, L, U
set C to me
set L to me
set U to me
set loqGUIsettings_L to L's setupLoqqing5() -- setup the variables used for Loqqing system
## Values in this section are safe to change, within limits indicated. Support is likely but no commitment
## Default values
### Read Carefully
set enableSeqImageName to true -- enable image based sequence name; disabling results in counter based sequence names
set enableCreateCollections to true -- this enable creating of albums for each sequence, and creating of the "Stacks" group
set enableCreateUnsorted to true -- this enable creating of of the "Unsorted" smart album
set nameStackGroup to "Stacks" -- this is the name of group that holds the albums for each sequence
set nameUnsortedAlbum to "Unsorted" -- this is the name of smart album that shows the not yet sequenced variants
set enableSequenceNameEntry to false -- enables manual entry of sequence names
set seqBaseImageName to "SEQ " -- the base used for image based sequence name
set seqBaseCountedName to "SEQ-" -- the base used for counter based sequence name
set seqCountDigits to 2 -- the number of counter digits in a counter based sequence name
property sequenceCount : 1 -- the starting count in a counter based sequence name
set enableSeqInMetaData to true -- shall the sequence name be written to the IPTC Image Scenes metadata
set enableMetaDataMultiSeq to true -- shall the sequence name be added to other sequences in the IPTC Image Scenes metadata
set Loqqing's debugLogLevel to 0 -- 0...6 Values >1 result in increasing amounts of debug data that takes longer to report
set Loqqing's enableResultsFile to true -- (true/false)
set Loqqing's enableResultsByClipboard to false -- (true/false)
set Loqqing's enableNotifications to true -- (true/false) - enable notifications of errors and exceptions
set Loqqing's notificationsMaxDebug to 1 -- 0...6 suggest not more than 1
set Loqqing's ResultsFileMaxDebug to 3 -- 0...6 suggest not more than 2
set Loqqing's enableResultsByNotifications to false
set Loqqing's enableResultsByLoq to true
set sortedColorTag1 to 5 -- for the stacked images
set sortedColorTag2 to 6 -- for the picked stacked images
--Color Tag
--Criterion
--0--> none
--1-->red
--2-->orange
--3-->yellow
--4-->green
--5-->blue
--6-->magenta
--7-->purple
## ***** 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.
set inpGroup1 to {inpPref:"", inpAlp:false, inpNum:true, inpSpa:false, inpSym:false, inpBck:true, inpCnt:5, inpAct:{{return, "O"}}} -- Take up to 5 alphabetic, symbol or space characters. Then discard them
set inpGroup2 to {inpPref:"", inpAlp:true, inpNum:false, inpSpa:true, inpSym:true, inpBck:true, inpCnt:0, inpAct:{}} -- Take the next number characters, stop on a Symbol or Space or alphabetic character
set inpMethod1 to {"SOOC Parse", {inpGroup1, inpGroup2}} -- parses the image file names typically assigned by a camera
set inpGroup3 to {inpPref:"", inpAlp:false, inpNum:true, inpSpa:false, inpSym:false, inpBck:true, inpCnt:5, inpAct:{{return, "O"}, {"-", "M", 0}}} -- Take up to 5 alphabetic, symbol or space characters. Then discard them. Start over next method if a "-" is found.
set inpGroup4 to {inpPref:"", inpAlp:true, inpNum:false, inpSpa:true, inpSym:true, inpBck:true, inpCnt:0, inpAct:{{"-", "M", 0}}} -- Take the next number characters, stop on a Symbol or Space or alphabetic character. Start over next method if a "-" is found.
set inpGroup5 to {inpPref:"", inpAlp:false, inpNum:false, inpSpa:true, inpSym:true, inpBck:false, inpCnt:4, inpAct:{{return, "O"}, {return, "D"}}} -- Take Alpha and Numbers, stop on Space or symbol or 4 characters. Then discard
set inpGroup6 to {inpPref:"", inpAlp:true, inpNum:false, inpSpa:true, inpSym:true, inpBck:false, inpCnt:3, inpAct:{{"$", "G"}, {return, "O"}, {return, "D"}}} -- Take Only Numbers, stop on Alpha or Space or Symbol or 3 characters. Then discard. Start over if a "$" is found
set inpGroup7 to {inpPref:"", inpAlp:true, inpNum:false, inpSpa:true, inpSym:true, inpBck:true, inpCnt:5, inpAct:{{"$", "G"}}} -- Take Only Numbers, stop on Alpha or Space or Symbol or 5 characters. Start over if a "$" is found
set inpMethod2 to {"Universal Parse", {inpGroup3, inpGroup4}, {inpGroup5, inpGroup6, inpGroup7}} -- combination of Method 3 and Method 1
set inpMethod3 to {"Eric's Detailed Parse", {inpGroup5, inpGroup6, inpGroup7}} -- parses Eric's image file names
set imageNameParsingMethod to inpMethod1 -- OK to change this
set debugLogEnable to true
## Result reporting methods which are valid for this script
set Loqqing's gateResultsFile to true
set Loqqing's gateResultsByClipboard to true
set Loqqing's gateResultsNotification to true
## Reporting methods which are NOT valid for this script
-- disable the reporting method
set Loqqing's gateResultsDialog to false
-- disable user control
set Loqqing's enableResultsByDialog to false -- (true/false)
set Loqqing's maxDialogPercent to 85 -- (0% to 100% of the monitor) The amount of data that triggers a dialog report
##
tell application "System Events" to set parent_name to name of current application
set Script_Title to (get name of me)
set Result_DocName to "CO_Image_Sorting_Report.txt"
set loqResultMethod to L's InitializeLoqqing5(Result_DocName, Script_Title) -- Initialize the results logging system
set {minCOPversion, maxCOPversion} to {"12", "12"}
tell C's validateCOP5(minCOPversion, maxCOPversion)
if its hasErrors then error L's loqqed_Error_Halt5(get its errorText)
set {theAppName, copVersion} to {its theAppName, its copVersion}
end tell
tell application "Capture One 12" to set COPDocRef to get current document
tell C's validateCOPdoc5(COPDocRef, {"Catalog"})
if its hasErrors then error L's loqqed_Error_Halt5(get its errorText)
set {COPDocName, COPDocKind_s} to {its COPDocName, its COPDocKind_s}
end tell
tell C's validateCOPcollections5(COPDocRef) to set {selectedCollectionRef, kindSelectedCollection_s, nameSelectedCollection} to ¬
{its selectedCollectionRef, its kindSelectedCollection_s, its nameSelectedCollection}
tell application "Capture One 12" to set countSelectedVariants to get count of selected variants
tell application "Capture One 12" to set countEveryVariant to count of variants
set startSortRule to "<?xml version="1.0" encoding="UTF-8"?><MatchOperator Kind="AND"><MatchOperator Kind="AND"><Condition Enabled="YES"><Key>IB_S_BASIC_URGENCY</Key><Operator>1</Operator><Criterion>"
set endSortRule to "</Criterion></Condition></MatchOperator></MatchOperator>"
set midSortRule to "</Criterion></Condition></MatchOperator><MatchOperator Kind="AND"><Condition Enabled="YES"><Key>IB_S_BASIC_URGENCY</Key><Operator>1</Operator><Criterion>"
set smartSortRule to startSortRule & sortedColorTag1 & midSortRule & sortedColorTag2 & endSortRule
if enableCreateCollections then
set {theStacksRef, foundStacks, refStacksParent} to findCollection({selectedCollectionRef}, nameStackGroup, "group", true, true, 1, {"group", "project"}, "")
if not foundStacks then error L's loqqed_Error_Halt5("Can't find or create the "" & nameStackGroup & "" group")
end if
if enableCreateUnsorted then
set {theUnsortedRef, foundUnsorted, refUnsortedParent} to findCollection({selectedCollectionRef}, nameUnsortedAlbum, "smart album", true, true, 50, {"group", "project"}, smartSortRule)
if not foundUnsorted then error L's loqqed_Error_Halt5("Can't find or create the "" & nameUnsortedAlbum & "" smart album")
end if
tell application "Capture One 12" to tell (variants whose selected is true) to set {theVariantsRef, theVariantsName} to {it, its name}
if (0 = (count of theVariantsName)) then
L's loqThis(-1, true, "No variants selected")
else
if enableSeqImageName then
set numdigits to -1
repeat with aName in theVariantsName
set thisCtr to findBaseName(aName, imageNameParsingMethod)
set {thisLen, thisCtr} to {(count of thisCtr), (thisCtr as integer)}
if -1 = numdigits then
set {numdigits, minCtr, maxCtr} to {thisLen, (0 + (thisCtr as integer)), (0 + (thisCtr as integer))}
else
if thisLen > numdigits then set numdigits to thisLen
if thisCtr < minCtr then set minCtr to thisCtr
if thisCtr > maxCtr then set maxCtr to thisCtr
end if
end repeat
set padZeros to U's makeList(numdigits, 0) as string
set sequenceName to seqBaseImageName & (text (-numdigits) thru -1 of (padZeros & minCtr))
if maxCtr > minCtr then set sequenceName to sequenceName & "-" & (text (-numdigits) thru -1 of (padZeros & maxCtr))
else
set padZeros to U's makeList(seqCountDigits, 0) as string
set sequenceName to seqBaseCountedName & (text (-seqCountDigits) thru -1 of (padZeros & sequenceCount))
set sequenceCount to sequenceCount + 1
if enableCreateCollections then
tell application "Capture One 12" to tell theStacksRef to set seqAlbumExists to (0 < (count of (every collection whose name begins with sequenceName)))
if seqAlbumExists then
tell application "Capture One 12" to tell theStacksRef to set seqAlbumName_L to name of every collection whose name begins with seqBaseCountedName
set {ptrFirstNum, maxAlbumCount} to {(1 + (count of seqBaseCountedName)), 1}
repeat with aName in seqAlbumName_L
set {aNamesId_L, ptrChar, lenName} to {(id of aName), (ptrFirstNum + 0), (length of aName)}
repeat while (48 ≤ aNamesId_L's item ptrChar) and (57 ≥ aNamesId_L's item ptrChar)
set ptrChar to ptrChar + 1
if ptrChar > lenName then exit repeat
end repeat
set ptrChar to ptrChar - 1 -- now ptrChar points to the last number character
if ((ptrChar - ptrFirstNum + 1) ≤ seqCountDigits) and ((ptrChar - ptrFirstNum) > 0) then
set albumsCount to (string id (items ptrFirstNum thru ptrChar of aNamesId_L)) as integer
if albumsCount > maxAlbumCount then set maxAlbumCount to albumsCount
end if
end repeat
set sequenceCount to maxAlbumCount + 2
set sequenceName to seqBaseCountedName & (text (-seqCountDigits) thru -1 of (padZeros & (maxAlbumCount + 1)))
end if
end if
end if
set userCanceled to false
if enableSequenceNameEntry then
try
set dialogResult to display dialog ¬
"Sequence Name" buttons {"Cancel", "OK"} ¬
default button "OK" cancel button "Cancel" default answer sequenceName
on error number -128
set userCanceled to true
end try
set sequenceName to dialogResult's text returned
end if
if not userCanceled then
set gotSequenceAlbum to false
if enableCreateCollections then
set {refSequenceAlbum, gotSequenceAlbum, refSeqParent} to findCollection({theStacksRef}, sequenceName, "album", true, false, 0, {"group"}, "")
if gotSequenceAlbum then
tell application "Capture One 12" to add inside refSequenceAlbum variants theVariantsRef
L's loqThis(-1, false, "Copied " & (count of theVariantsRef) & " variants to "" & sequenceName & """)
else
error L's loqqed_Error_Halt5("Can't create the "" & sequenceName & "" album")
end if
end if
if enableSeqInMetaData then
tell application "Capture One 12"
if enableMetaDataMultiSeq then
set oneByOne_L to get variants whose (image scenes is not "") and (selected is true)
tell (variants whose selected is true and image scenes is "") to set its image scenes to sequenceName
repeat with aVariant in oneByOne_L
tell aVariant
set the aVarSeq_L to its image scenes
if aVarSeq_L does not contain sequenceName then set its image scenes to (aVarSeq_L & "," & sequenceName)
end tell
end repeat
else
tell (variants whose selected is true) to set its image scenes to sequenceName
end if
end tell
end if
try
tell application "Capture One 12" to tell (variants whose selected is true) to set its color tag to sortedColorTag1 -- important to do this last to avoid the effect of color filtering
on error
if gotSequenceAlbum then
tell application "Capture One 12" to tell refSequenceAlbum to tell its variants to set its color tag to sortedColorTag1
else
error L's loqqed_Error_Halt5("Unable to set the color tag because the variants have disappeared from the collection. Likely cause is a filter that has been set on the Image Scenes MetaData")
end if
end try
end if
end if
################ The End ###############
on findCollection(parentRefList, nameTargetColl, kindTargetColl, enableCreate, enableFindParents, MaxCount, kindParentColl_L, saSortRule)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General purpose handler to find and create a collection, starting from some arbitrary collection
## Version March 17 2019
global debugLogEnable
local refParentColl, ptrCollRef, thisCollRef, thisCollKind, thisCollKind_S, thisCollName, subCollNames, subCollRef, refFoundColl, hasFoundColl, saRules, debugText, collSearchFail
copy (contents of item 1 of parentRefList) to thisCollRef
if 1 < (count of parentRefList) then set enableFindParents to false
set saRules to "<?xml version="1.0" encoding="UTF-8"?><MatchOperator Kind="AND"></MatchOperator>" -- No rules string -- length 80, first part is 38 long
if (80 < (get length of saSortRule)) and (text 1 thru 38 of saRules) = (text 1 thru 38 of saSortRule) then set saRules to saSortRule
set {refFoundColl, hasFoundColl, collCreated, refParentColl} to {{}, false, false, missing value}
repeat with ptrCollRef in parentRefList
set thisCollRef to contents of ptrCollRef
tell application "Capture One 12" to tell thisCollRef to set {thisCollKind, thisCollName, subCollNames, subCollRef, refParentColl} to {its kind, its name, name of its collections, its collections, it}
set thisCollKind_S to C's convertKindList(thisCollKind)
set debugText to kindTargetColl & " "" & nameTargetColl & "" in " & thisCollKind_S & " "" & thisCollName & """
if debugLogEnable then L's loqThis(2, false, return & "Find Collection: " & debugText & " with Create: " & enableCreate & " Search: " & enableFindParents & " Sort Rule: " & (count of saSortRule) & " characters")
if debugLogEnable then L's loqThis(3, false, "SubCollections {" & U's joinListToString(subCollNames, "; ") & "}")
if (kindParentColl_L contains thisCollKind_S) and (subCollNames contains nameTargetColl) then
tell application "Capture One 12" to tell thisCollRef
if ("project" = kindTargetColl) then set refFoundColl to (refFoundColl & (every collection whose name is nameTargetColl and kind is project))
if ("group" = kindTargetColl) then set refFoundColl to (refFoundColl & (every collection whose name is nameTargetColl and kind is group))
if ("album" = kindTargetColl) then set refFoundColl to (refFoundColl & (every collection whose name is nameTargetColl and kind is album))
if ("smart album" = kindTargetColl) then set refFoundColl to (refFoundColl & (every collection whose name is nameTargetColl and kind is smart album and rules is saRules))
end tell
end if
if (1 ≠(count of refFoundColl)) then
if enableCreate and (kindParentColl_L contains thisCollKind_S) and ({"album", "smart album"} does not contain thisCollKind_S) then
if debugLogEnable then L's loqThis(2, false, "Attempting to Create the collection")
set collCreated to false
tell application "Capture One 12" to tell thisCollRef
set {ctrCollName, collSearchFail} to {1, false}
copy (nameTargetColl as string) to nbrTargetCollName
repeat until ({} = (every collection whose name is nbrTargetCollName)) or collSearchFail
set refFoundColl to {}
if ("project" = kindTargetColl) then set refFoundColl to (refFoundColl & (every collection whose name is nameTargetColl and kind is project))
if ("group" = kindTargetColl) then set refFoundColl to (refFoundColl & (every collection whose name is nameTargetColl and kind is group))
if ("album" = kindTargetColl) then set refFoundColl to (refFoundColl & (every collection whose name is nameTargetColl and kind is album))
if ("smart album" = kindTargetColl) then set refFoundColl to (refFoundColl & (every collection whose name is nameTargetColl and kind is smart album and rules is saRules))
if (1 = (count of refFoundColl)) then exit repeat -- found anumbered collection
set {nbrTargetCollName, ctrCollName} to {(nameTargetColl & ctrCollName), ctrCollName + 1}
if MaxCount < ctrCollName then set collSearchFail to true
end repeat
if not collSearchFail and (0 = (count of refFoundColl)) then
if ("group" = kindTargetColl) then set {refFoundColl, collCreated} to {{make new collection with properties {kind:group, name:nbrTargetCollName}}, true}
if ("album" = kindTargetColl) then set {refFoundColl, collCreated} to {{make new collection with properties {kind:album, name:nbrTargetCollName}}, true}
if ("project" = kindTargetColl) then set {refFoundColl, collCreated} to {{make new collection with properties {kind:project, name:nbrTargetCollName}}, true}
if ("smart album" = kindTargetColl) then set {refFoundColl, collCreated} to {{make new collection with properties {kind:smart album, name:nbrTargetCollName, rules:saRules}}, true}
if (1 < ctrCollName) then set debugText to kindTargetColl & " "" & nbrTargetCollName & "" in " & thisCollKind_S & " "" & thisCollName & """
end if
end tell
end if
end if
if (1 = (count of refFoundColl)) then
set {refFoundColl, hasFoundColl, refParentColl} to {(item 1 of refFoundColl), true, thisCollRef}
if collCreated then
if debugLogEnable then L's loqThis(2, false, "Created " & debugText)
else
if debugLogEnable then L's loqThis(2, false, "Found " & debugText)
end if
exit repeat
end if
end repeat
if (not hasFoundColl) and enableFindParents then set {refFoundColl, hasFoundColl, refParentColl} to my findCollection((my findParentColl(thisCollRef)), nameTargetColl, kindTargetColl, enableCreate, false, MaxCount, kindParentColl_L, saSortRule)
if not hasFoundColl then L's loqThis(0, false, "The " & kindTargetColl & " "" & nameTargetColl & "" is missing")
return {refFoundColl, hasFoundColl, refParentColl}
end findCollection
on findParentColl(thisCollRef)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General purpose handler to find the parent of a collection
## March 23 2019
global debugLogEnable
local errorText, errorText1, parentStringList, refPtr, docPtr, docName, parentPtr, parentRefList, startPtr, stopPtr
if debugLogEnable then L's loqThis(2, false, "Starting Search")
try
get || of {thisCollRef}
on error errorText
if debugLogEnable then L's loqThis(5, false, "Full error text "" & errorText & """)
end try
## Extract the string between "{" and "}"
repeat with startPtr from 1 to count of errorText
if "{" = text startPtr of errorText then exit repeat
end repeat
repeat with stopPtr from -1 to -(count of errorText) by -1
if "}" = text stopPtr of errorText then exit repeat
end repeat
set errorText to U's removeLeadingTrailingSpaces((text (startPtr + 1) thru (stopPtr - 1) of errorText))
## if script runs as an application "collection" is replaced by "«class COcl»", fix that
if "«class COcl»" = text 1 thru 12 of errorText then set errorText to U's replaceText(errorText, "«class COcl»", "collection")
set parentStringList to U's splitStringToList(errorText, "of") -- make a list of references
if debugLogEnable then L's loqThis(4, false, "Processed error text "" & errorText & """)
repeat with docPtr from (count of parentStringList) to 0 by -1
try
if "document" = first word of item docPtr of parentStringList then exit repeat
end try
end repeat
set {docPtr, parentRefList} to {(docPtr + 0), {}}
if 0 = docPtr then error L's loqqed_Error_Halt5("Didn't find "document" in the string "" & errorText & """)
set docName to U's removeLeadingTrailingSpaces((get item 2 of U's splitStringToList((U's removeLeadingTrailingSpaces((get item docPtr of parentStringList))), """)))
tell application "Capture One 12" to copy (document docName) to beginning of parentRefList
if debugLogEnable then L's loqThis(5, false, ("Found Document Reference "" & (get parentStringList's item docPtr) & """))
repeat with parentPtr from 1 to docPtr
if "collection" = (first word of item parentPtr of parentStringList) then exit repeat
end repeat
set parentPtr to parentPtr + 1
if (parentPtr > docPtr) then error L's loqqed_Error_Halt5("Unable to find starting collection")
repeat with refPtr from docPtr - 1 to parentPtr by -1
if debugLogEnable then L's loqThis(5, false, ("Collection Reference "" & (get parentStringList's item refPtr) & """))
tell application "Capture One 12" to tell (first item of parentRefList) to copy (collection id (get last word of parentStringList's item refPtr)) to beginning of parentRefList
end repeat
return parentRefList
end findParentColl
on findBaseName(theString, theMethod_L)
## Copyright 2019 Eric Valk, Ottawa, Canada Creative Commons License CC BY-SA No Warranty.
## General purpose handler to parse an image file name
## Looks long, but execution time is about 20 - 80 microseconds
## theMethod_L is a list of Methods. Each Method describes a parsing method (a particular way of parsing a string)
## Each Method is a list of Groups, each Group is a record that describes how to find the end of a group of characters.
## While a group runs each character is copied from theString to the output until the end is found.
## The algorithm starts with Group 1 of Method 1. When a Group ends the next one starts
## When there are no Groups left or no Characters left, the algorithm stops reading characters and returns the output characters.
## The Group record: {inpAlp, inpNum, inpSpa, inpSym, inpCnt, inpBck, inpAct}:
## (inpAlp, inpNum, inpSpa, inpSym, inpBck) boolean, (inpCnt) integer, inpAct a list of Actions {{X,Y,Z},{X,Y,Z},...}
#3 Character Type Tests are evaluated first, then Actions
## A group ends upon finding Alphabetic characters (if inpAlp true), or Numbers (if inpNum true), or a Space(if inpSpa true),
## or Symbols (if inpSym true), or finding more than inpCnt characters (if inpCnt >0).
## If inpBck is true, when a group ends, the last character becomes part of the next group
## EG Group3: {inpAlp:false, inpNum:false, inpSpa:false, inpSym:true, inpBck:true, inpCnt:9, inpAct:aList}
## Means: stop if a symbol is found or after the 9th character, and the last character becomes the first character of the next group
## Thus up to 9 Numbers, Spaces and Alphabetic characters are accepted, and 9th character or symbol is part of the next group.
## if Group 3 is the last Group, then 9th or symbol character (and all subsequent characters) are dropped.
## inpAct contains Alists {X,Y,Z}, each describes an action to execute when finding some character. inpAct= {} results in no actions
## X is the character that triggers an action; Y is the Action; Z optionally specifies "how much"
## X= return triggers the action on a Group Increment from Character Type Tests
## Actions (1): T-Translate Character to "Z" ; G- Switch to Group Z ; M- Switch to Method Z ; I- Increment group by Z;
## B- Drop Back 1 character; J- Jump ahead Z characters, D- Drop trigger Characters, O - Drop Output
## Actions A,B,C,N,S and P set the value of inpAlp, inpBck, inpCnt, inpNum, inpSpa and inpSym
## If the Alist has no "z" value: for "I", z is taken as 1; for "J", z is taken as the length of the trigger; for "T" z is taken as "";
## for G, z is taken as "0"; for M, the method is incremented, output and trigger are dropped
## for all other commands z is taken as true when missing
## Selecting Group < 1 resets the parser to Group 1 and clears all output. If inpBck is true, the current character is dropped.
## Selecting Method 0 triggers a Method reset, output and input are reset, and then the previous method is incremented
## Switching to Method whose number does not exist causes the previous output to be cleared, the remaining input characters are accepted
## E.G. For inpAct:{{"$","M"},{"-","T"}} in Method 1 --> when an "$" is encountered, start over again with Group 1 of Method 2, also drop every "-"
## If there is no Method 2, all remaining characters of the input are taken as output.
global debugLogEnable
local theCharIDList, stringCount, thisCharsId, parsedNumList, charPointer, iterationCtr, SafetyLimit
local Alpha, Numb, Blank, Symbol, MaxCnt, backOneOnGrpInc, ActionList, AlistTriggerId_L, anAlist, theAction, Prefix
local thisMethod, MethodCount, thisGroup, GroupCount, hasMaxCnt, countChars, stringCount
local triggerMethodInit, triggerGroupInit, triggerGroupInc, validChar, newCharIDs, hasNewChars, nextGroup, nextMethod, nextCharPointer
local triggerBack1Char, hasPar3, valuePar3, endTriggerPointer, lenActTrigger, actionString, hasActionTrigger, theActionTrigger, nextCharJump, hasJump, triggerOutputDrop, lenActTrigger
set {theCharIDList, stringCount, nextMethod, MethodCount, triggerMethodInit, parsedNumList} to ¬
{(get id of theString), (get count of theString), 1, (count of lists of theMethod_L), true, {}}
set {charPointer, iterationCtr, SafetyLimit} to {1, 0, (MethodCount * stringCount * 4)}
if debugLogEnable then
L's loqThis(2, false, "Parsing "" & theString & """)
if (0 < (count of strings of theMethod_L)) then L's loqThis(2, false, ("Using Method " & (string 1 of theMethod_L)))
L's loqThis(3, false, {"Initialising: stringCount " & stringCount & ", Method " & nextMethod & ", MethodCount " & MethodCount})
end if
repeat while (charPointer ≤ stringCount) and (iterationCtr < SafetyLimit)
set iterationCtr to iterationCtr + 1
if triggerMethodInit then set {nextGroup, GroupCount, triggerGroupInit, thisMethod} to ¬
{1, (count of records of theMethod_L's list nextMethod), true, nextMethod}
if debugLogEnable and triggerMethodInit then L's loqThis(3, false, "Start Method #" & nextMethod & ", Group Count: " & (count of theMethod_L's list nextMethod))
if triggerGroupInit then
tell theMethod_L's list thisMethod's record nextGroup to set {Prefix, Alpha, Numb, Blank, Symbol, MaxCnt, hasMaxCnt, backOneOnGrpInc, ActionList, countChars, thisGroup} to ¬
{its inpPref, its inpAlp, its inpNum, its inpSpa, its inpSym, its inpCnt, (0 < its inpCnt), its inpBck, its inpAct, 0, nextGroup}
if debugLogEnable then L's loqThis(3, false, "Start Group #" & thisGroup & " , Prefix "" & Prefix & "", Alpha " & Alpha & ", Number " & Numb & ", Blank " & Blank & ", Symbol " & Symbol & ", Max Count " & MaxCnt & ", has Max Count " & hasMaxCnt & ", Drop Back Last Character " & backOneOnGrpInc & " , ActionList : " & (count of ActionList) & " action lists")
if (0 < (count of ActionList)) and ("list" ≠((class of ActionList's item 1) as text)) then error "Group " & thisGroup & " has an incorrectly formatted Action List"
set parsedNumList to parsedNumList & (id of Prefix)
end if
set {triggerBack1Char, triggerGroupInc, triggerGroupInit, triggerMethodInit, validChar, newCharIDs, hasNewChars, hasActionTrigger, theActionTrigger, nextCharJump, hasJump, triggerOutputDrop} to ¬
{false, false, false, false, true, {}, false, false, missing value, 0, false, false}
set {thisCharsId, countChars, nextCharPointer, lenActTrigger} to {(theCharIDList's item charPointer), (countChars + 1), charPointer, 1}
if debugLogEnable then L's loqThis(5, false, "Character " & charPointer & " : "" & (string id (get theCharIDList's item charPointer)) & """)
if hasMaxCnt and (countChars ≥ MaxCnt) then -- if the counter has triggered, don't check the character groups
set triggerGroupInc to true
if debugLogEnable then L's loqThis(3, false, "Count triggers Group Increment")
else -- check the character type
if debugLogEnable then L's loqThis(3, false, "Character Type Check")
if ((thisCharsId ≥ 65) and (thisCharsId ≤ 90)) or ((thisCharsId ≥ 97) and (thisCharsId ≤ 122)) or (thisCharsId ≥ 192) then -- a latin character
if Alpha then set triggerGroupInc to true
if debugLogEnable and Alpha then L's loqThis(4, false, "Alpha triggers Group Increment")
else if ((thisCharsId ≥ 48) and (thisCharsId ≤ 57)) then -- a number
if Numb then set triggerGroupInc to true
if debugLogEnable and Numb then L's loqThis(4, false, "Number triggers Group Increment")
else if thisCharsId = 32 then --- its a space
if Blank then set triggerGroupInc to true
if debugLogEnable and Blank then L's loqThis(4, false, "Space triggers Group Increment")
else if thisCharsId > 32 then -- its a symbol
if Symbol then set triggerGroupInc to true
if debugLogEnable and Symbol then L's loqThis(4, false, "Symbol triggers Group Increment")
end if
end if
repeat with anAlist in ActionList
set {AlistTriggerId_L, endTriggerPointer} to {((id of contents of anAlist's item 1) as list), (charPointer + (count of anAlist's item 1) - 1)}
if (endTriggerPointer ≤ stringCount) and ((not hasActionTrigger) or (theActionTrigger = AlistTriggerId_L)) and ¬
((triggerGroupInc and ({13} = AlistTriggerId_L)) or (AlistTriggerId_L = (theCharIDList's items charPointer thru endTriggerPointer))) ¬
then
if debugLogEnable and not hasActionTrigger then L's loqThis(4, false, "Action List is triggered by "" & (anAlist's item 1) & """)
if not hasActionTrigger then set {hasActionTrigger, theActionTrigger, lenActTrigger} to {true, AlistTriggerId_L, (count of AlistTriggerId_L)}
set {theAction, hasPar3, valuePar3} to {(item 2 of anAlist), (3 ≤ (count of anAlist)), true}
if hasPar3 then set valuePar3 to (contents of item 3 of anAlist)
if debugLogEnable then
if hasPar3 then
set actionString to "Action "" & (contents of item 2 of anAlist) & "" ; "" & (contents of item 3 of anAlist) & """
else
set actionString to "Action "" & (contents of item 2 of anAlist) & """
end if
end if
## Group 1 - Character Control - Does Not clear triggerGroupInc
if ("A" = theAction) then
set Alpha to valuePar3
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpAlp to " & valuePar3)
else if "B" = theAction then --
set backOneOnGrpInc to valuePar3
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpBck to " & valuePar3)
else if ("C" = theAction) then
if (not hasPar3) or (0 = valuePar3) then
set {MaxCnt, hasMaxCnt} to {0, false}
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpCnt to 0")
else if (0 < valuePar3) then
set {MaxCnt, hasMaxCnt} to {valuePar3, true}
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpCnt to " & valuePar3)
else if (0 > valuePar3) then
set {MaxCnt, hasMaxCnt} to {(MaxCnt - valuePar3), true}
if debugLogEnable then L's loqThis(5, false, actionString & " - Increment inpCnt by " & (-valuePar3))
end if
else if ("N" = theAction) then
if hasPar3 then set Numb to valuePar3
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpNum to " & valuePar3)
else if ("P" = theAction) then
set Symbol to valuePar3
if debugLogEnable then L's loqThis(5, false, actionString & "- Set inpSym to " & valuePar3)
else if ("Q" = theAction) then
if debugLogEnable then L's loqThis(4, false, actionString & "- Quit Actions")
exit repeat
else if "S" = theAction then
set Blank to valuePar3
if debugLogEnable then L's loqThis(5, false, actionString & " - Set inpSpa to " & valuePar3)
## Group 2 - Character Control - clears triggerGroupInc on multi character trigger
else if "D" = theAction then --
set validChar to not valuePar3
if 1 < lenActTrigger then set triggerGroupInc to false
if debugLogEnable then L's loqThis(5, false, actionString & " - Drop Action Trigger: " & valuePar3)
else if "O" = theAction then --
set triggerOutputDrop to valuePar3
if 1 < lenActTrigger then set triggerGroupInc to false
if debugLogEnable then L's loqThis(5, false, actionString & " - Drop Output characters")
else if "J" = theAction then --
if not hasPar3 then set valuePar3 to lenActTrigger
set {nextCharJump, hasJump} to {valuePar3, true}
if 1 < lenActTrigger then set triggerGroupInc to false
if debugLogEnable then L's loqThis(5, false, actionString & " - Jump forward " & valuePar3 & " Characters")
else if "T" = theAction then -- translate/drop this character
if not hasPar3 then set valuePar3 to ""
set {newCharIDs, hasNewChars} to {(id of valuePar3), true}
if 1 < lenActTrigger then set triggerGroupInc to false
if debugLogEnable then L's loqThis(5, false, actionString & " - Translate characters to "" & valuePar3 & """)
## Group 3 - Group & Method control, clears triggerGroupInc - later
else if "G" = theAction then -- new group
if not hasPar3 then set valuePar3 to 0
set {nextGroup, triggerGroupInit} to {valuePar3, true}
if debugLogEnable then L's loqThis(5, false, actionString & " - Switch to Group " & valuePar3)
else if "I" = theAction then -- increment group
if not hasPar3 then set valuePar3 to 1
set {nextGroup, triggerGroupInit} to {(thisGroup + valuePar3), true}
if debugLogEnable then L's loqThis(5, false, actionString & " - Increment Group to " & nextGroup)
else if "M" = theAction then -- set method
if not hasPar3 then set valuePar3 to (thisMethod + 1)
set {nextMethod, triggerMethodInit, triggerOutputDrop, validChar} to {valuePar3, true, true, false}
if debugLogEnable then L's loqThis(5, false, actionString & " - Switch to Method " & valuePar3 & " and drop output and trigger")
else
if debugLogEnable then L's loqThis(3, false, actionString & " - Command not found ")
end if
end if
end repeat
if (lenActTrigger > 1) then set countChars to countChars - 1 + lenActTrigger
if hasMaxCnt and (countChars ≥ MaxCnt) then set {triggerGroupInc} to {true, L's loqThis(3, false, "Character count triggers Group Increment")}
if (triggerGroupInit or triggerMethodInit) then set triggerGroupInc to false
if (triggerGroupInc or triggerGroupInit or triggerMethodInit) and backOneOnGrpInc then set triggerBack1Char to true
if (0 ≥ nextMethod) then
if debugLogEnable then L's loqThis(3, false, "Executing Method Reset - Increment Method, Clear output, Reset Input Queue")
set {parsedNumList, nextMethod, triggerMethodInit, nextCharPointer} to {{}, (thisMethod + 1), true, 1, false}
set {triggerBack1Char, triggerGroupInc, hasJump, hasNewChars, hasMaxCnt} to {false, false, false, false, false}
else
if (0 ≥ nextGroup) then
set {triggerBack1Char, validChar, triggerGroupInc, hasJump, hasNewChars} to {false, false, false, false, false}
if debugLogEnable then L's loqThis(3, false, "Executing Group Reset - Switch to Group 1, Clear output , dropping back " & (-nextGroup) & " characters")
if (-1 ≥ nextGroup) then set {nextCharJump, hasJump} to {(nextGroup), true}
set {parsedNumList, nextGroup, triggerGroupInit} to {{}, 1, true}
end if
if triggerGroupInc then set {nextGroup, triggerGroupInit} to {thisGroup + 1, true, L's loqThis(3, false, "Executing Group Increment - Switch to Group " & (thisGroup + 1))}
if debugLogEnable and triggerOutputDrop then L's loqThis(4, false, "Dropped Output")
if triggerOutputDrop then set parsedNumList to {}
if triggerBack1Char then
set {validChar, nextCharPointer} to {false, charPointer}
if debugLogEnable then L's loqThis(3, false, "Drop Back 1 Character")
else if (hasJump or hasNewChars) then
set validChar to false
if (hasJump and (((thisGroup ≠nextGroup) or (thisMethod ≠nextMethod) or (0 < nextCharJump)))) then
set nextCharPointer to (charPointer + nextCharJump)
if nextCharPointer < 1 then set nextCharPointer to 1
if hasNewChars and (0 < nextCharJump) and (lenActTrigger > nextCharJump) then set nextCharPointer to (charPointer + lenActTrigger)
else if hasJump then
## do nothing - can't move charPointer back
if debugLogEnable then L's loqThis(3, false, "Unable to move back " & (-nextCharJump) & " characters - infinite loop")
else if hasNewChars then -- and not hasJump
set nextCharPointer to (charPointer + lenActTrigger)
end if
if debugLogEnable then L's loqThis(3, false, "Input Moved Forward " & (nextCharPointer - charPointer) & " characters")
else
set nextCharPointer to (charPointer + 1)
if debugLogEnable then L's loqThis(5, false, "Input Moved Forward 1 character")
end if
if hasNewChars then set {parsedNumList} to {(parsedNumList & newCharIDs), L's loqThis(4, false, "Added new Characters to the output")}
if validChar then set end of parsedNumList to thisCharsId
if debugLogEnable and not validChar then L's loqThis(3, false, "The input character has been dropped")
end if
if debugLogEnable then L's loqThis(3, false, "The output is: "" & (string id parsedNumList) & """)
if nextMethod > MethodCount then -- replace the output with every remaining character
if debugLogEnable then L's loqThis(3, false, "Got Method " & nextMethod & " Return remaining characters")
if charPointer > 1 then set parsedNumList to (theCharIDList's items charPointer thru stringCount)
if charPointer ≤ 1 then copy theCharIDList to parsedNumList
exit repeat
else if (nextGroup > GroupCount) then
if debugLogEnable then L's loqThis(3, false, "Finished all groups, exitting")
exit repeat
end if
set charPointer to nextCharPointer -- must be last statement in the the loop
end repeat
if debugLogEnable and (charPointer > stringCount) then L's loqThis(3, false, "Finished all characters")
return (string id parsedNumList)
end findBaseName
##########################################
## Capture One General Handlers Version 2019/03/11
on validateCOP5(minCOPversionstr, maxCOPversionstr)
## Copyright 2019 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 debugLogEnable
local COPProcList, theAppRef, numCOPversion, minCOPversion, maxCOPversion
local digit_mult, Version_digit, min_digit, max_digit, copVersionStr
local theAppName, copVersion
tell application "System Events"
if debugLogEnable then
L's loqThis(2, false, ("COP Processes:" & (get U's joinListToString((get name of every process whose name begins with "Capture One" and background only is false), ", "))))
L's loqThis(3, false, ("All Processes: " & (get U's joinListToString((get name of every process whose background only is false), ", "))))
end if
set COPProcList to every process whose name begins with "Capture One" and background only is false
if (count of COPProcList) = 0 then return {hasErrors:true, errorText:(get L's loqqed_Error_Halt5("COP is not running"))}
if (count of COPProcList) ≠1 then return {hasErrors:true, errorText:(get L's loqqed_Error_Halt5("Unexpected: >1 COP instances"))}
set theAppRef to item 1 of COPProcList
set theAppName to ((get name of theAppRef) as text)
set copDetailedVersion to get version of my application theAppName
end tell
tell application "Capture One 12" to set copVersionStr to (get app version)
set copVersion to (word -1 of copVersionStr)
if debugLogEnable then
L's loqThis(2, false, ("theAppName: " & theAppName))
L's loqThis(1, false, copVersionStr)
L's loqThis(2, false, ("Capture One full Version " & copDetailedVersion))
end if
tell U's compareVersion(copVersion, minCOPversionstr, maxCOPversionstr) to set {minVersionPass, maxVersionPass} to {its minVersionPass, its maxVersionPass}
if not minVersionPass then return {hasErrors:true, errorText:(get L's loqqed_Error_Halt5(("This Script does not support Capture One " & copDetailedVersion & " - supported versions are from " & minCOPversionstr & " to " & maxCOPversionstr)))}
if not maxVersionPass then L's loqThis(0, true, ("Caution: Capture One " & copDetailedVersion & " has not been verified yet"))
return {hasErrors:false, theAppName:theAppName, copVersion:copVersion}
end validateCOP5
on validateCOPdoc5(theDocRef, validDocKindList)
## Copyright 2019 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 a document
global debugLogEnable
local COPDocKind_s, COPDocKind_p, COPDocName
if "text" = (get class of theDocRef as text) and (0 = (get count of theDocRef)) then tell application "Capture One 12" to set theDocRef to get current document
try
tell application "Capture One 12" to set {COPDocName, COPDocKind_p} to get {name, kind} of theDocRef
on error errorText number errorNumber
return {hasErrors:true, errorText:(get L's loqqed_Error_Halt5("The Script could not retrieve Capture One document info. Error " & errorNumber & ": "" & errorText & """))}
end try
set COPDocKind_s to convertKindList(COPDocKind_p)
if validDocKindList does not contain COPDocKind_s then return {hasErrors:true, errorText:(get L's loqqed_Error_Halt5((COPDocName & " is a " & COPDocKind_s & " -- not supported by this script")))}
return {hasErrors:false, COPDocName:COPDocName, COPDocKind_s:COPDocKind_s}
end validateCOPdoc5
on validateCOPcollections5(theDocRef)
## Copyright 2019 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 the top level collections
global debugLogEnable
local namesTopCollections, kindsTopCollections_s, countTopCollections, selectedCollectionRef, selectedCollectionIndex, kindSelectedCollection_s, nameSelectedCollection
tell application "Capture One 12" to set {COPDocName, COPDocKind_p} to get {name, kind} of theDocRef
set COPDocKind_s to convertKindList(COPDocKind_p)
tell application "Capture One 12" to tell theDocRef
set selectedCollectionRef to get current collection
if (missing value = selectedCollectionRef) then
try
set current collection to collection "All Images"
on error
set current collection to last collection
end try
set selectedCollectionRef to get current collection
end if
tell selectedCollectionRef to set {nameSelectedCollection, kindSelectedCollection_s} to {name, my convertKindList(kind)}
set {namesTopCollections, kindsTopCollections_s} to {every collection's name, my convertKindList(every collection's kind)}
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
local selectedCollectionMirroredAtTopLast, bottomUserCollectionIndex, topUserCollectionIndex, countFavoriteCollections, namesFavoriteCollections
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 {countFavoriteCollections, namesFavoriteCollections} to {missing value, missing value}
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 countFavoriteCollections to countTopCollections - topUserCollectionIndex
if 0 = countFavoriteCollections then
set namesFavoriteCollections to {}
else
set namesFavoriteCollections to (get items (topUserCollectionIndex + 1) thru countTopCollections of namesTopCollections)
end if
set selectedCollectionMirroredAtTopLast to false
end if
local selectedCollectionIsUser, namesTopUserCollections, kindsTopUserCollections_s, countTopUserCollections
set selectedCollectionIsUser to ¬
(selectedCollectionMirroredAtTopLast or ¬
((selectedCollectionIndex ≥ bottomUserCollectionIndex) and (selectedCollectionIndex ≤ topUserCollectionIndex)))
if topUserCollectionIndex < bottomUserCollectionIndex then
set {topUserCollectionIndex, bottomUserCollectionIndex} to {missing value, missing value}
set {namesTopUserCollections, kindsTopUserCollections_s, countTopUserCollections} to {{}, {}, 0}
else
set {namesTopUserCollections, kindsTopUserCollections_s} to {(get items bottomUserCollectionIndex thru topUserCollectionIndex of namesTopCollections), (get items bottomUserCollectionIndex thru topUserCollectionIndex of kindsTopCollections_s)}
set countTopUserCollections to count of namesTopUserCollections
end if
return {hasErrors:false, namesTopUserCollections:namesTopUserCollections, kindsTopUserCollections_s:kindsTopUserCollections_s, countTopUserCollections:countTopUserCollections, selectedCollectionRef:selectedCollectionRef, selectedCollectionIndex:selectedCollectionIndex, kindSelectedCollection_s:kindSelectedCollection_s, nameSelectedCollection:nameSelectedCollection, selectedCollectionMirroredAtTopLast:selectedCollectionMirroredAtTopLast, selectedCollectionIsUser:selectedCollectionIsUser, bottomUserCollectionIndex:bottomUserCollectionIndex, topUserCollectionIndex:topUserCollectionIndex, countFavoriteCollections:countFavoriteCollections, namesFavoriteCollections:namesFavoriteCollections}
end validateCOPcollections5
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
local kind_s_list, input_is_list, theItem, kind_s1, fail_flag, code_start, kind_list, Kind_s_item, kind_code, kind_type
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)) or (16 > (count of Kind_s_item)) then ¬
error (get L's loqqed_Error_Halt5("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 ¬
error (get L's loqqed_Error_Halt5("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 convertKindList0
Post is closed for comments.
Comments
3 comments