MacOS Photos - 使用 applescript 使用它们所在相册的名称更新关键字

MacOS Photos - use applescript to update keywords with the name(s) of the albums they are in

TLDR:
我想要 运行 一个 Applescript,它将迭代智能相册中新添加的照片,以将它们包含的相册名称和文件夹名称添加为照片上的关键字。

案例:
我的照片结构是这样的:
My Albums¬
FolderName¬
AlbumName¬
List of Photos

我创建了一个智能相册:“过去 90 天内添加的任何照片”

我希望 applescript 基本上做到这一点:

我所知道的关于照片应用程序工作方式的一些事情:

  1. 照片不知道它们在哪个相册中
  2. 相册知道它们包含哪些照片
  3. Applescript 字典读取容器的方式很不稳定。
  4. 经过研究,我认为诀窍是 'ask' 相册中有所选照片的​​所有文件夹,然后以某种方式捕获相册名称和 'contains photo' 所在的文件夹。但是我真的不知道怎么做这个

这 3 件事让我很困惑。
我真的很希望有人做过这样的事情,可以帮助我理解围绕这个的脚本。

最终,我尝试使用智能过滤器使照片更易于搜索 - 因此自动附加关键字会有很大帮助。

到目前为止,我可以让 applescript 找到要迭代的选择(手动选择的文件),但我真的很想通过命名的智能相册完成它。

tell application "Photos"
    set eachFile to {}
    set theSelections to (get selection) -- This gets the hilighted photos 
    repeat with i in theSelections
        set selectedPhotoId to get id of i
        set theKeywords to the ({keywords of i})
        
        display dialog theAlbum

        
    end repeat
    
end tell 

到目前为止,我的努力只是弄清楚哪些字典术语可以 return 对我有一些价值,但到目前为止我遗漏了很多部分。

循环遍历特定相册中的所有照片很容易,主要要求它是顶级相册。 (你可以在子专辑中做到这一点,但这会变得不那么容易。)

set sourceAlbumName to "Last Fortnight"
repeat with recentPhoto in media items in album sourceAlbumName
    …
end repeat

剩下的就更难了,因为你提到的原因:据我所知,你不能只要求包​​含照片的所有相册的列表。

但是,您可以询问文件夹 是否包含包含该照片的任何相册。

所以您最终需要做的是遍历所有文件夹(包括顶级文件夹,应用程序本身)并找到包含照片的所有相册。将他们的名字添加到关键字列表中。如果文件夹中有文件夹,则同理

然后,为了您的目的,如果任何文件夹包含包含该照片的相册,或包含包含包含该照片的相册的文件夹等,请将该文件夹也添加到关键字列表中。

这几乎需要递归处理程序。

这个示例处理程序所做的是,它首先获取指定文件夹中的所有相册,并将它们复制到新关键字列表中。然后它遍历指定文件夹中的每个文件夹,并使用该文件夹调用处理程序。如果它得到了一些东西,那么它将把它得到的东西和文件夹添加到新关键字列表中。

to getSubcontainers for parentFolder given id:photoId
    set newKeywords to {}
    --using terms from because tell application "Photos" has trouble
    --when the parentFolder *is* application "Photos"
    using terms from application "Photos"
        tell parentFolder
            --get the albums that contain this photo
            set containingAlbums to get (albums whose id of media items contains photoId)
            repeat with containingAlbum in containingAlbums
                copy name of containingAlbum to end of newKeywords
            end repeat
            
            --get folders that contain albums that contain this photo
            repeat with currentFolder in folders
                tell me to getSubcontainers for currentFolder given id:photoId
                
                --if some album contained within this folder contained the photo
                --then this folder is also a keyword
                if the result is not {} then
                    set newKeywords to newKeywords & the result
                    copy name of currentFolder as string to end of newKeywords
                end if
            end repeat
        end tell
    end using terms from
    return newKeywords
end getSubcontainers

这是脚本的顶层。请注意,默认情况下它设置为在完成一张照片后停止,并且不会实际更改关键字。

我将测试相册命名为 Last Fortnight,因为我在测试时不希望其中包含 90 天的照片。

脚本确保不会多次添加关键字,也不会将源专辑添加为关键字,因为它可能不算数。

--how many photos should we change before we bail?
set maxPhotos to 1
--if testing is true, do not actually change the keywords
set testing to true
--the album to pull photos from for changing
set sourceAlbumName to "Last Fortnight"

tell application "Photos"
    set currentPhotoCount to 0
    repeat with recentPhoto in media items in album sourceAlbumName
        set currentPhotoCount to currentPhotoCount + 1
        tell me to getSubcontainers for application "Photos" given id:id of recentPhoto
        set containerNames to the result
        
        --create a new list of keywords that includes both existing keywords on the photo
        --and the new folder and album names
        set photoKeys to keywords of recentPhoto
        repeat with newKey in containerNames
            set newKey to newKey as string
            if newKey is not in photoKeys and newKey is not sourceAlbumName then
                copy newKey to the end of photoKeys
            end if
        end repeat
        
        --set the photo to have the new list of keywords
        if not testing then
            set keywords of recentPhoto to photoKeys
        end if
        
        --I'm exiting the repeat so that I can see that it worked on the first x photos in the smart album
        --instead of waiting for it to go through all photos
        --and also to avoid incorrectly changing too many photos
        if currentPhotoCount ≥ maxPhotos then
            exit repeat
        end if
    end repeat
end tell

--for testing, to see what the new keywords will be
if testing then
    get photoKeys
end if

请注意,据我所知,没有明确的方法来判断相册是智能相册还是手动管理相册。如果这对您很重要,我怀疑尝试将照片添加到相册会 (a) 导致可捕获的错误,并且 (b) 使脚本花费更长的时间才能 运行。

如果您不想将智能相册添加为关键字,我建议您要么以某种方式标记它们的名称以便知道它们是智能的,然后排除他们就是这样 (and newKey does not end in " Smart"),或者只是保留一个专辑列表作为关键字排除,并根据该列表检查密钥 (and newKey is not in invalidKeywords)。我可能会选择后者。

global theKeyWords

tell application "Photos"
    -- get the hilighted photos
    set theSelection to (get selection)
    repeat with i from 1 to count theSelection
        set theKeyWords to {}
        set selectionID to id of item i of theSelection
        -- first, get names of albums in the root
        set theKeyWords to theKeyWords & (name of albums whose id of media items contains selectionID)
        -- recursively get names of albums in the subfolders
        my getAlbumNames(its folders, selectionID)
        -- set the keywords of processed photo
        set keywords of media item id selectionID to theKeyWords
    end repeat
end tell

-- recursive handler 
on getAlbumNames(theFolders, selectionID)
    if theFolders is {} then return
    tell application "Photos"
        repeat with aFolder in theFolders
            tell aFolder
                set theKeyWords to theKeyWords & (name of albums whose id of media items contains selectionID)
                my getAlbumNames(its folders, selectionID)
            end tell
        end repeat
    end tell
end getAlbumNames

以下脚本将关键字附加到现有关键字,而不是删除它们

global theKeyWords

tell application "Photos"
    -- get the hilighted photos
    set theSelection to (get selection)
    repeat with i from 1 to count theSelection
        set theKeyWords to {}
        set selectionID to id of item i of theSelection
        -- first, get names of albums in the root
        set theKeyWords to theKeyWords & (name of albums whose id of media items contains selectionID)
        -- recursively get names of albums in the subfolders
        my getAlbumNames(its folders, selectionID)
        -- set the keywords of processed photo
        try
            set keywords of media item id selectionID to (get keywords of media item id selectionID) & theKeyWords
        on error
            set keywords of media item id selectionID to theKeyWords
        end try
    end repeat
end tell

-- recursive handler 
on getAlbumNames(theFolders, selectionID)
    if theFolders is {} then return
    tell application "Photos"
        repeat with aFolder in theFolders
            tell aFolder
                set theKeyWords to theKeyWords & (name of albums whose id of media items contains selectionID)
                my getAlbumNames(its folders, selectionID)
            end tell
        end repeat
    end tell
end getAlbumNames