导出Apple照片库以便将其导入到其他地方

问题描述 投票:0回答:2

我打算将我的所有照片从 Mac 导出到任何其他环境,这个建议最终提供了使用 applescript 解决此问题的基础。这样照片总是会缩放(比原来的大),但至少它是有效的。

我知道这很长,但由于我在这个问题上挣扎了很长时间,所以我在下面发布了整个工作解决方案作为答案。也许有人有更好的建议。我拒绝对 Photos 正在使用的 sqlite 表中的内容进行逆向工程,因为这可能会随着下一个版本而改变。

问题多种多样

  • 无法保证照片如何浏览相册和文件夹。由于它有时会停止工作,因此您必须保证一定的顺序,否则您将永远无法完成。所以我介绍了一种从特定相册的特定照片开始的方法。专辑内的顺序似乎很稳定。写入的日志输出显示相册和照片的唯一 ID(因为名称可能不是唯一的),以便此时能够重新启动它。大多数时候,第二次尝试就能奏效。
  • 我没有找到一种按顺序存储照片的方法,所以我为每张照片创建了一个目录,并将每张照片存储在单独的目录中,这样我就可以按顺序重新导入它们。问题是“照片”始终使用原始文件名,并且如果该名称已存在,则仅向上计数。使用不同的相机只会让情况变得更糟。
  • 我将所有属性写入每张照片的文本文件中,以便稍后可以重新导入它们。
  • 错误处理相当棘手。照片库有时只要求用户按下一个按钮,这使得编写脚本变得困难。
macos applescript photo apple-photos
2个回答
0
投票

不幸的是,这是我能想到的最好的,这是我的第一个苹果脚本。这适用于带有 Sierra 的旧 Mac,也适用于带有 Catalina 的更新版本。我几乎可以肯定最新版本不会抱怨。我仍在寻找更好的解决方案。

几乎不支持从 Apple 的脚本编辑器内部运行它,只有命令行提供所有选项。

#!/usr/bin/osascript
global startAlbum, startPhoto, match, dummy, match, photoCnt, infoFd

global errorIndicator

on writePhotoAndData(thePath, mediaItem, mediaAlbum)
        set ind to "X"
        using terms from application "Photos"
                set fName to filename of mediaItem
                set fId to id of mediaItem
                set photoCnt to photoCnt + 1
                -- export each media to separate directory -> only chance to keep the order
                if dummy then
                        if infoFd ≠ missing value then
                                set s to id of mediaAlbum & tab & id of mediaItem & tab & thePath & linefeed as text
                                write s to infoFd as «class utf8»
                        end if
                else
                        makeEmptyPosixPath(thePath)
                        set exportPath to POSIX file thePath
                        set infoFile to POSIX file (thePath & "/" & "info.txt")
                        set infoText to "id" & tab & id of mediaItem & linefeed & "file" & tab & filename of mediaItem & linefeed & "album" & tab & name of mediaAlbum & linefeed
                        if exists name of mediaItem then set infoText to infoText & "name" & tab & name of mediaItem & linefeed
                        if exists description of mediaItem then set infoText to infoText & "desc" & tab & description of mediaItem & linefeed
                        if exists date of mediaItem then
                                set d to date of mediaItem
                                set infoText to infoText & "date" & tab & short date string of d & space & time string of d & linefeed
                        end if
                        if exists altitude of mediaItem then set infoText to infoText & "alt" & tab & altitude of mediaItem & linefeed
                        if exists location of mediaItem then set infoText to infoText & "location" & tab & location of mediaItem & linefeed
                        if exists keywords of mediaItem then
                                tell mediaItem to set myKeywords to keywords
                                repeat with keyword in myKeywords
                                        set infoText to infoText & "keyword" & tab & keyword & linefeed
                                end repeat
                        end if
                        set fd to open for access infoFile with write permission
                        set eof fd to 0 -- of fd?
                        write infoText to fd starting at eof as «class utf8»
                        close access fd
                        try
                                tell mediaAlbum
                                        --Not sure whether this does anything, so removed
                                        --set settings to "JPEG - Original Size"
                                        export {mediaItem} to (exportPath as alias)
                                end tell
                                set errorIndicator to 0
                        on error errStr number errNum
                                if errNum = -1712 then --timeout
                                        set ind to "E"
                                        set errorIndicator to errorIndicator + 1
                                        if errorIndicator >= 3 then
                                                error "3 errors in a row - exiting"
                                        end if
                                else
                                        error errStr number errNum
                                end if
                        end try
                end if
                log ind & tab & photoCnt & tab & id of mediaAlbum & tab & id of mediaItem & tab & name of mediaAlbum & tab & filename of mediaItem
        end using terms from
end writePhotoAndData

on walkAlbum(theAlbum, thePath)
        if match = 0 then
                if id of theAlbum = startAlbum then
                        set match to 1
                        if startPhoto is missing value then
                                set match to 2
                        end if
                else
                        return
                end if
        else if match = 1 then
                if id of theAlbum is not equal to startAlbum then
                        set match to 3
                end if
        end if
        set photoNum to 0
        using terms from application "Photos"
                set albumPath to thePath & name of theAlbum & "/"
                repeat with mediaItem in media items of theAlbum
                        set photoNum to photoNum + 1
                        if match = 1 then
                                if id of mediaItem = startPhoto then
                                        set match to 2
                                end if
                        else if match = 2 then
                                set match to 3 --photo after the photo chosen
                        end if
                        if match = 3 then
                                writePhotoAndData(albumPath & photoNum, mediaItem, theAlbum)
                        end if
                end repeat
        end using terms from
end walkAlbum

on walkFolder(theFolder, thePath)
        using terms from application "Photos"
                repeat with containedFolder in folders of theFolder
                        walkFolder(containedFolder, thePath & name of containedFolder & "/")
                end repeat
                repeat with containedAlbum in albums of theFolder
                        walkAlbum(containedAlbum, thePath)
                end repeat
        end using terms from
end walkFolder

on makePosixPath(tPath)
        do shell script "mkdir -p " & quoted form of tPath
end makePosixPath

on makeEmptyPosixPath(tPath)
        do shell script "rm -rf " & quoted form of tPath & " && mkdir -p " & quoted form of tPath
end makeEmptyPosixPath

on makeFolder(tPath)
        do shell script "mkdir -p " & quoted form of POSIX path of tPath
end makeFolder

on walkFile(fileName, fileOffset as integer)
        set thisOffset to 0
        set saveDelim to text item delimiters of AppleScript
        set walkFd to open for access POSIX file fileName
        set rawLine to read walkFd before linefeed as «class utf8»
        repeat
                set thisOffset to thisOffset + 1
                if fileOffset = 0 or thisOffset ≥ fileOffset then
                        set thisLine to rawLine as text
                        set text item delimiters of AppleScript to tab
                        set splitLine to text items of thisLine
                        set text item delimiters of AppleScript to saveDelim
                        set albumIdString to item 1 of splitLine
                        set photoIdString to item 2 of splitLine
                        set pathString to item 3 of splitLine
                        tell application "Photos"
                                set thisAlbum to album id albumIdString
                                set thisMedia to media item id photoIdString
                        end tell
                        writePhotoAndData(pathString, thisMedia, thisAlbum)
                        log "O" & tab & thisOffset
                end if
                try
                        set rawLine to read walkFd before linefeed as «class utf8»
                on error errTxt number errNum
                        if errNum = -39 then --end of file
                                exit repeat
                        else
                                error "Error reading inputfile: " & errTxt
                        end if
                end try
        end repeat
end walkFile

on run (args)
        set caller to class of args as string
        set errorIndicator to 0
        set destPath to POSIX path of (get path to home folder) & "export/photos/"
        set photoCnt to 0
        set startAlbum to missing value
        set startPhoto to missing value
        set match to 3
        set dummy to false
        set walkFileName to missing value
        set walkFileOffset to 0
        set infoFd to missing value
        set infoFileName to missing value
        set chooseDestinationFolder to "Select start folder (defaults to " & destPath & ")"
        set chooseStart to "Give start album and photo"
        set chooseDryRun to "dry-run"
        if caller = "script" then
                log "Running in ScriptEditor:" & name of me
                set options to choose from list {chooseDestinationFolder, chooseStart, chooseDryRun} with title "Configure run" with prompt "Select options" with multiple selections allowed and empty selection allowed
                if options contains chooseDryRun then set dummy to true
                if options contains chooseDestinationFolder then
                        try
                                set destFolder to choose folder with prompt "Choose export directory or cancel for default location" default location (get path to home folder as alias)
                                set destPath to POSIX path of destFolder
                        end try
                end if
                if options contains chooseStart then
                        set res to display dialog "Enter Id of album to start" default answer "" buttons {"OK"} default button 1
                        if length of text returned of res > 0 then
                                set startAlbum to text returned of res
                                set match to 0
                        end if
                        if match = 0 then
                                set res to display dialog "Enter id of photo to resume after (leave empty to start with album)" default answer "" buttons {"OK"} default button 1
                                if length of text returned of res > 0 then
                                        set startPhoto to text returned of res
                                end if
                        end if
                end if
        else if caller = "list" then
                log "Running on the command line:" & name of me
                set cnt to 1
                repeat while cnt ≤ length of args
                        if item cnt of args = "-t" then
                                set cnt to (cnt + 1)
                                set destPath to item cnt of args
                                set cnt to (cnt + 1)
                        else if item cnt of args = "-a" then
                                set match to 0
                                set cnt to (cnt + 1)
                                set startAlbum to item cnt of args
                        else if item cnt of args = "-p" then
                                set cnt to (cnt + 1)
                                set startPhoto to item cnt of args
                        else if item cnt of args = "-h" then
                                return name of me & " [-t target path] [-a start album id] [-a start photo id] [-d] [-i info file path]" & linefeed ¬
                                        & name of me & "[-t target path] -f info file [-o offset]" & linefeed ¬
                                        & name of me & " -h .. this help"
                        else if item cnt of args = "-d" then
                                set dummy to true
                        else if item cnt of args = "-i" then
                                set cnt to (cnt + 1)
                                set infoFileName to item cnt of args
                        else if item cnt of args = "-f" then
                                set cnt to (cnt + 1)
                                set walkFileName to item cnt of args
                        else if item cnt of args = "-o" then
                                set cnt to cnt + 1
                                set walkFileOffset to item cnt of args
                        else
                                error "Invalid option:" & item cnt of args & " - use -h for help"
                        end if
                        set cnt to (cnt + 1)
                end repeat
        end if
        set mySettings to "Destination directory:" & destPath
        if startAlbum is not missing value then set mySettings to mySettings & linefeed & tab & "Start album id:" & tab & startAlbum
        if startPhoto is not missing value then set mySettings to mySettings & linefeed & tab & "Start photo id:" & tab & startPhoto
        if infoFileName ≠ missing value then
                set mySettings to mySettings & linefeed & tab & "Write file '" & infoFileName & "'" & linefeed
                set dummy to true
        end if
        if dummy then set mySettings to mySettings & linefeed & tab & "Dry run only"
        if caller = "script" then
                display dialog mySettings with title "Start exporting?" with icon note
        else if caller = "list" then
                log mySettings
        else
                error "Internal error - caller interface unknown"
        end if
        if infoFileName ≠ missing value then
                set infoFd to open for access POSIX file infoFileName with write permission
                set eof infoFd to 1
        end if
        if walkFileName ≠ missing value then
                walkFile(walkFileName, walkFileOffset)
        else
                walkFolder(application "Photos", destPath)
        end if
        if infoFd ≠ missing value then close access infoFd
        return "Done!"
end run

0
投票

不知何故回复晚了...但这可能对其他人有帮助。

看看这个优秀的工具,它允许您从 Mac 照片中提取几乎所有内容,用于备份、导出或传输到其他工具等等:OSX Photos记录在这里其中:

OSXPhotos 提供了直接从 Python 代码与 macOS 上的 Apple Photos.app 库进行交互和查询的能力,以及用于导出照片的非常灵活的命令行界面 (CLI) 应用程序。您可以查询照片库数据库 - 例如文件名、文件路径和元数据(例如关键字/标签、人物/面孔、相册等)。您还可以轻松导出原始照片和编辑后的照片。

© www.soinside.com 2019 - 2024. All rights reserved.