Create User Collections to Mitigate 'opaque' Folders in Folders Tab.
First script here from new CO user. I, together with many others, it would seem, am baffled as to why CO does not show you all photos in a folder AND its subfolders when you click on it in the Folders tab. Coming from Aperture and LR, I have most of my photos generally stored in a folder structure of <Camera>/<Year>/<Month>/<Day>, so my first day with CO was spent wondering where the setting was to let me browse my photos by month.
Then I found User Collections and learned that Projects are 'transparent' to albums beneath.
This Applescript creates Group/Project/Album collections to mirror a folder hierarchy of at least three levels. If you have a folder structure of, say, <camera>/<year>/<month>/<day>/ and select <year> to represent a Project, then you will create a top-level Group called <camera> inside which will be your <year> Project inside which will be Albums for each month, containing variants of every photo contained in <month> and its subfolders to any depth. Now you can click on a month and see everything inside just as you can't with folders.
Note that I use my own progress bar window to provide feedback but it shouldn't be hard to redirect to TextEdit or other.
I know some people have been manually creating this sort of arrangement on coming to CO, so I hope this can save some time.
No guarantees, however. This is a hobby project. Works for me on MacOS 10.14.6, CO20 and iMac Pro.
[Can't figure out how to paste in formatted script, sorry! The Insert code button </> makes a panel for code but pasting in my script breaks it.]
Edit: Formatted script further down, without reference to custom AS Progress progress bar.
-------------------------
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
use framework "Foundation"
property scriptTitle : "Group Project Album"
-- You can edit this line to point to your starting Photos location so:
-- property defaultPhotoLocation : alias "Data:Photos:"
-- But the script will store your preferred starting location after the first run.
property defaultPhotoLocation : missing value
property useASProgress : true
-- classes, constants, and enums used
property NSArray : a reference to current application's NSArray
property NSPredicate : a reference to current application's NSPredicate
set doLoop to true
repeat while doLoop is true
set msg to "This script takes a folder hierarchy spanning at least three levels to create or maintain User Collections reflecting this hierarchy as:" & linefeed & linefeed & "Group ➤ Project ➤ Album." & linefeed & linefeed & "Example folder structure:" & linefeed & "Camera Model ➤ Year ➤ Month [ ➤ Day ] ➤ Photos" & linefeed & linefeed & "Resulting User Collections:" & linefeed & "Camera Model (Group) ➤ Year (Project) ➤ Month (Album)" & linefeed & linefeed & "Variants from all folders under the Month would be combined in this case into the one album for that month." & linefeed & linefeed & "It prompts for the folder to become your Project; does not create duplicates on repeated execution."
tell application "Capture One 20" to display dialog msg with title scriptTitle buttons {"Photo Location…", "Cancel", "Run"} default button "Run" with icon 1
set br to button returned of the result
if (br is "Photo Location…") or defaultPhotoLocation is missing value then
set defaultPhotoLocation to my getDefaultPhotoLocation()
end if
if br is "Run" then
set doLoop to false
end if
end repeat
try
set parentFolder to choose folder with prompt "Select the folder you want as your Project. It will be created, if it doesn't exist, inside a Group collection named from its parent folder." & linefeed & "Its immediate subfolders will be represented by Albums inside the Project containing their entire contents." default location defaultPhotoLocation
on error errMsg number errNum
if errNum is -128 then
--don't proceed as for a 'User Cancelled' error as then any change of script property (default photo folder) won't register
return
end if
end try
delay 0.1
set saveTID to AppleScript's text item delimiters
set AppleScript's text item delimiters to {":"}
set pathComponents to text items of (parentFolder as text)
-- Last path item will provide the Project name; 1st-level enclosed folders will become Albums beneath
set projectName to text item -2 of pathComponents
-- Second-last path item will provide the Group name, maybe a camera model, etc.
set groupName to text item -3 of pathComponents
set AppleScript's text item delimiters to saveTID
-- My own progress window; haven't tested without it!
tell application "AS Progress"
try
set title to scriptTitle
set message to "Retrieving Catalogue Folder list…"
set indeterminate to true
set allow cancelling to true
activate
on error
-- flag no AS Progress application for feedback
set useASProgress to false
end try
end tell
set matchingFolders to {}
tell application "Capture One 20"
-- Get folders (locations) for every listed folder
set theFolders to folder of every collection of document 1 whose kind is catalog folder
if my progressQuit() then return
-- convert this to a text list for filtering with NSPredicate
repeat with i from 1 to count theFolders
set item i of theFolders to item i of theFolders as text
end repeat
(*
--The slo-ow way
repeat with f in theFolders
set textFolder to (folder of f) as text
if textFolder starts with (parentFolder as text) then
log textFolder
copy textFolder to end of matchingFolders
end if
end repeat
*)
end tell
-- Refine list of folders to only those beneath our parent folder.
-- Leverage ASObjC for speed but note need with Mojave and later to keep
-- Cocoa variables inside a handler and pass AS list back out.
-- This is just so our persistent property for default photo location works. See:
-- https://forum.latenightsw.com/t/mojave-and-applescript-applets/1563/2
set filteredFolders to my filterFolders(theFolders, parentFolder as text)
if (count of filteredFolders) is 0 then
-- No folders matching this project in CO (only in Finder)
if useASProgress then
tell application "AS Progress"
quit
end tell
end if
display dialog "Folder '" & projectName & "' of '" & groupName & "' has not been imported into Capture One." buttons {"Quit"} default button "Quit" with title scriptTitle with icon stop
return
end if
tell application "Capture One 20"
-- Get/make the var groupName to the enclosing folder. This is the enclosing Group for each year's Project. Maybe relates to camera model.
try
set theGroup to some collection of document 1 whose name is groupName and kind is group
on error errMsg number errNum
--log "Error " & errNum & ": " & errMsg
if errNum is -1730 then -- Container specified was an empty list
-- new top-level group
if useASProgress then
tell application "AS Progress" to set message to "Making group " & groupName
delay 0.5
end if
tell document 1 to set theGroup to (make new collection with properties {name:groupName, kind:group})
else
if useASProgress then tell application "AS Progress" to quit
display dialog "Error " & errNum & ": " & errMsg buttons {"Quit"} default button "Quit" --cancel button "Quit"
end if
end try
-- Get/make project. Maybe for Year.
try
set theProject to some collection of theGroup whose name is projectName and kind is project
on error errMsg number errNum
if errNum is -1730 then -- Container specified was an empty list
-- new project for the year
if useASProgress then
tell application "AS Progress" to set message to "Making project for year " & projectName
delay 0.5
end if
tell theGroup to set theProject to make collection with properties {kind:project, name:projectName}
else
if useASProgress then tell application "AS Progress" to quit
display dialog "Error " & errNum & ": " & errMsg buttons {"Quit"} default button "Quit" --cancel button "Quit"
end if
end try
end tell
tell application "System Events"
set subFolders to name of folders of parentFolder
end tell
-- Make an album for each direct subfolder
repeat with subFolder in subFolders
if my progressQuit() then return
set pathToAlbum to (parentFolder as text) & subFolder & ":"
set albumFolders to my filterFolders(filteredFolders, pathToAlbum)
set albumVariants to {}
tell application "Capture One 20"
repeat with a in albumFolders
set dFolder to (some collection of document 1 whose folder is file a)
-- add these variants to those for the whole ablum
set albumVariants to albumVariants & variants of dFolder
end repeat
if (count of albumVariants) ≠ 0 then
-- check destination album
try
set theAlbum to (some collection of theProject whose name is subFolder and kind is album)
on error errMsg number errNum
if errNum is -1730 then -- Container specified was an empty list
if useASProgress then
tell application "AS Progress" to set message to "Making album " & groupName & " ➤ " & projectName & " ➤ " & subFolder
delay 0.5
end if
tell theProject to set theAlbum to make collection with properties {kind:album, name:subFolder}
else
if useASProgress then tell application "AS Progress" to quit
display dialog "Error " & errNum & ": " & errMsg buttons {"Quit"} default button "Quit" --cancel button "Quit"
end if
end try
if useASProgress then
tell application "AS Progress" to set message to "Processing album " & groupName & " ➤ " & projectName & " ➤ " & subFolder
end if
add inside theAlbum variants albumVariants
end if
end tell
end repeat
if useASProgress then
tell application "AS Progress"
set indeterminate to false
set maximum value to 1
set current value to 1
set message to "Done"
delay 3
quit
end tell
end if
-- Separate handler required to keep Cocoa variables out of top level of script
on filterFolders(theFolders, parentFolderText)
set nsFolders to NSArray's arrayWithArray:theFolders
set thePredicate to NSPredicate's predicateWithFormat_("SELF BEGINSWITH %@", parentFolderText)
set nsFilteredFolders to (nsFolders's filteredArrayUsingPredicate:thePredicate)
return nsFilteredFolders as list
end filterFolders
on progressQuit()
set shouldQuit to false
if useASProgress then
tell application "AS Progress"
if cancelled then
set shouldQuit to true
quit
end if
end tell
end if
return shouldQuit
end progressQuit
on getDefaultPhotoLocation()
if defaultPhotoLocation is missing value then
set dpl to path to pictures folder from user domain
else
set dpl to defaultPhotoLocation
end if
tell application "Capture One 20" to choose folder with prompt "Select the folder you'd like as your default photo location." & linefeed & "This is where the Folder-picker window will default to each time you run the script." default location dpl
return result
end getDefaultPhotoLocation
-
Hi Quentin, nice script. I compiled it but didn't run it, yet.
Initially it refused to compile since I don't have the "AS Progress" application. I had to remove all references to "AS Progress" to allow it to complile. Is there any chance you can post the code for this application?
I note your limitation to three levels. If you write that part as a recursive handler, you will be able be able to handle an arbitrary number of levels.
0 -
Hi Quentin, I found that if you paste Applescript code, select all the code just pasted, and then click the insert code button, all the selected text, e.g. your entire script, is put into code format.
All the indentation is lost, which may not be a problem as the Applescript compiler will insert the correct indentation. Its not clear if there will be issues with any special AppleScript symbols.
0 -
Ah, I forgot it wouldn't compile without my supporter app. That app, AS Progress, is something I wrote in Xcode, Objective-C, as my first scriptable cocoa app. Too much code for here, I suspect! It has rough edges but fills a gaping void left by barebones Applescript.
Regarding recursion: to be honest, I only just worked out that you can put groups inside projects. But they behave in slightly weird ways. The whole point of this project for me (sorry, bad choice of word)... the whole point of this exercise was to give me an expanding view of years and months. If you put a group inside a project, the (photo) contents of that group don't seem to be shown in the browser/viewer until you select a member of that group, such as another album, so the "transparency" benefit is lost. You may as well select a branch of the Folder view and see only its subfolder names. Groups never show you their ulitmate photo members when selected, it seems. Also, you can only have projects at the top level, apparently. So there is some degree of recursion in this Group/Project/Album model, but it's not very elegant nor consistent. Another weirdness: if you do have a group inside a project and an album in that group, then selecting the project does indeed show you the members of the bottom album. But selecting the group still doesn't. So I personally find that of little benefit and am happy with the 3-tier model (with apologies to people in Tier-3 Britain).
But there's nothing wrong in adapting this and I'd love to learn that I am wrong.
I'll try to post the script without references to AS Progress the way you detail in your other post. Sounds like quite an ordeal. But hey, it seems to have worked! How did you work that out?
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
use framework "Foundation"
property scriptTitle : "Group Project Album"
-- You can edit this line to point to your starting Photos location so:
-- property defaultPhotoLocation : alias "Data:Photos:"
-- But the script will store your preferred starting location after the first run.
property defaultPhotoLocation : missing value
-- classes, constants, and enums used
property NSArray : a reference to current application's NSArray
property NSPredicate : a reference to current application's NSPredicate
set doLoop to true
repeat while doLoop is true
set msg to "This script takes a folder hierarchy spanning at least three levels to create or maintain User Collections reflecting this hierarchy as:" & linefeed & linefeed & "Group ➤ Project ➤ Album." & linefeed & linefeed & "Example folder structure:" & linefeed & "Camera Model ➤ Year ➤ Month [ ➤ Day ] ➤ Photos" & linefeed & linefeed & "Resulting User Collections:" & linefeed & "Camera Model (Group) ➤ Year (Project) ➤ Month (Album)" & linefeed & linefeed & "Variants from all folders under the Month would be combined in this case into the one album for that month." & linefeed & linefeed & "It prompts for the folder to become your Project; does not create duplicates on repeated execution."
tell application "Capture One 20" to display dialog msg with title scriptTitle buttons {"Photo Location…", "Cancel", "Run"} default button "Run" with icon 1
set br to button returned of the result
if (br is "Photo Location…") or defaultPhotoLocation is missing value then
set defaultPhotoLocation to my getDefaultPhotoLocation()
end if
if br is "Run" then
set doLoop to false
end if
end repeat
try
set parentFolder to choose folder with prompt "Select the folder you want as your Project. It will be created, if it doesn't exist, inside a Group collection named from its parent folder." & linefeed & "Its immediate subfolders will be represented by Albums inside the Project containing their entire contents." default location defaultPhotoLocation
on error errMsg number errNum
if errNum is -128 then
--don't proceed as for a 'User Cancelled' error as then any change of script property (default photo folder) won't register
return
end if
end try
delay 0.1
set saveTID to AppleScript's text item delimiters
set AppleScript's text item delimiters to {":"}
set pathComponents to text items of (parentFolder as text)
-- Last path item will provide the Project name; 1st-level enclosed folders will become Albums beneath
set projectName to text item -2 of pathComponents
-- Second-last path item will provide the Group name, maybe a camera model, etc.
set groupName to text item -3 of pathComponents
set AppleScript's text item delimiters to saveTID
set matchingFolders to {}
tell application "Capture One 20"
-- Get folders (locations) for every listed folder
set theFolders to folder of every collection of document 1 whose kind is catalog folder
-- convert this to a text list for filtering with NSPredicate
repeat with i from 1 to count theFolders
set item i of theFolders to item i of theFolders as text
end repeat
(*
--The slo-ow way
repeat with f in theFolders
set textFolder to (folder of f) as text
if textFolder starts with (parentFolder as text) then
log textFolder
copy textFolder to end of matchingFolders
end if
end repeat
*)
end tell
-- Refine list of folders to only those beneath our parent folder.
-- Leverage ASObjC for speed but note need with Mojave and later to keep
-- Cocoa variables inside a handler and pass AS list back out.
-- This is just so our persistent property for default photo location works. See:
-- https://forum.latenightsw.com/t/mojave-and-applescript-applets/1563/2
set filteredFolders to my filterFolders(theFolders, parentFolder as text)
if (count of filteredFolders) is 0 then
-- No folders matching this project in CO (only in Finder)
display dialog "Folder '" & projectName & "' of '" & groupName & "' has not been imported into Capture One." buttons {"Quit"} default button "Quit" with title scriptTitle with icon stop
return
end if
tell application "Capture One 20"
-- Get/make the var groupName to the enclosing folder. This is the enclosing Group for each year's Project. Maybe relates to camera model.
try
set theGroup to some collection of document 1 whose name is groupName and kind is group
on error errMsg number errNum
--log "Error " & errNum & ": " & errMsg
if errNum is -1730 then -- Container specified was an empty list
-- new top-level group
tell document 1 to set theGroup to (make new collection with properties {name:groupName, kind:group})
else
display dialog "Error " & errNum & ": " & errMsg buttons {"Quit"} default button "Quit" --cancel button "Quit"
end if
end try
-- Get/make project. Maybe for Year.
try
set theProject to some collection of theGroup whose name is projectName and kind is project
on error errMsg number errNum
if errNum is -1730 then -- Container specified was an empty list
-- new project for the year
tell theGroup to set theProject to make collection with properties {kind:project, name:projectName}
else
display dialog "Error " & errNum & ": " & errMsg buttons {"Quit"} default button "Quit" --cancel button "Quit"
end if
end try
end tell
tell application "System Events"
set subFolders to name of folders of parentFolder
end tell
-- Make an album for each direct subfolder
repeat with subFolder in subFolders
set pathToAlbum to (parentFolder as text) & subFolder & ":"
set albumFolders to my filterFolders(filteredFolders, pathToAlbum)
set albumVariants to {}
tell application "Capture One 20"
repeat with a in albumFolders
set dFolder to (some collection of document 1 whose folder is file a)
-- add these variants to those for the whole ablum
set albumVariants to albumVariants & variants of dFolder
end repeat
if (count of albumVariants) ≠ 0 then
-- check destination album
try
set theAlbum to (some collection of theProject whose name is subFolder and kind is album)
on error errMsg number errNum
if errNum is -1730 then -- Container specified was an empty list
tell theProject to set theAlbum to make collection with properties {kind:album, name:subFolder}
else
display dialog "Error " & errNum & ": " & errMsg buttons {"Quit"} default button "Quit" --cancel button "Quit"
end if
end try
add inside theAlbum variants albumVariants
end if
end tell
end repeat
-- Separate handler required to keep Cocoa variables out of top level of script
on filterFolders(theFolders, parentFolderText)
set nsFolders to NSArray's arrayWithArray:theFolders
set thePredicate to NSPredicate's predicateWithFormat_("SELF BEGINSWITH %@", parentFolderText)
set nsFilteredFolders to (nsFolders's filteredArrayUsingPredicate:thePredicate)
return nsFilteredFolders as list
end filterFolders
on getDefaultPhotoLocation()
if defaultPhotoLocation is missing value then
set dpl to path to pictures folder from user domain
else
set dpl to defaultPhotoLocation
end if
tell application "Capture One 20" to choose folder with prompt "Select the folder you'd like as your default photo location." & linefeed & "This is where the Folder-picker window will default to each time you run the script." default location dpl
return result
end getDefaultPhotoLocation0 -
Hi Quentin
I am aware of the idiosyncracies of the Group type collection
There is a native Capture One feature that may acheive your objectives, perhaps even better than the method you are trying, or perhaps in combination with it.
In Capture One select the "All Images" folder (but you can select other folders to narrow down the search)
On the Filter Tool, (by default on the Library tool tab), click on the three dot symbol on the top right, then on the drop down click on the item Show/Hide Filters. This brings up a the Show/Hide Metadata Filters floating window. In this window, enable the Date filter, then you may close that window.
Now back to the Filter tool. You should see that the Filter tool now has hierarchical filters for every year and every month, as well as individual dates. This should give the zooming view that you desire.
0 -
That's a very powerful and granular filter. Definitely interesting, thanks!
0
投稿コメントは受け付けていません。
コメント
5件のコメント