AppleScript で iPhone 用ムービーに変換する

昨日 の続き。 Autometer でだめなら AppleScript ではどうか。

QuickTimeX の Player を手動で立ち上げて読み込ませれば、 希望通りに保存できるのだから、 AppleScript なら思ったことができるのではないかと考えた。 そこでとりあえず、 ファイルを一つ選んで保存する AppleScript を組んでみた。

-- Covert 1 Movie To iPhone Movie

property aDestExtName : ".m4v"
set aMovieFile to choose file with prompt "変換するムービーを選択してください"
set aDestFolder to choose folder with prompt "出力先フォルダを選択してください"
set anExt to name extension of (info for aMovieFile)
set aMoviePath to POSIX path of aMovieFile
set aCommand to "basename " & quoted form of aMoviePath & " ." & anExt
set aDestFile to (aDestFolder as string) & (do shell script aCommand) & aDestExtName

tell application "QuickTime Player"
    set aMovie to open aMovieFile
    activate
    -- play document 1
    export aMovie in aDestFile using settings preset "iPhone"   
    close aMovie saving no
end tell
export ハンドラの using settings preset オプションに、 何を指定したらいいものかしばし悩んだが、 「ファイル>別名で保存...」の「フォーマット」ポップアップメニューの項目を指定してみたらズバリだった。

でもプリセットの設定でしか保存できないって QuickTime 7 (Pro) から機能ダウンしているよな。

後で /Applications/QuickTime Player.app/Contents/Resources/SaveAsExportPresets.plist というプロパティリストを見つけた。 この中にいくつかプリセットの名称が書かれている。 プリセットのすべてかはわからないけれど。

さて、 これで気をよくして複数のムービーをバッチ処理iPhone 用のムービーに書き出す (export する) フォルダアクション用の AppleScript を書いてみた。 Snow Leapard の QuickTimeX じゃないと動きません。

スクリプトを書く際には /Library/Scripts/Folder Action Scripts/Image - Duplicate as JPEG.scpt を参考にした。

property done_foldername : "iPhone Movies"
property originals_foldername : "Original Movies"
property settings_preset : "iPhone"
property newmovie_extension : "m4v"
-- the list of file types which will be processed
property type_list : {"3gpp"}
-- since file types are optional in Mac OS X,
-- check the name extension if there is no file type
-- NOTE: do not use periods (.) with the items in the name extensions list
-- eg: {"txt", "text", "jpg", "jpeg"}, NOT: {".txt", ".text", ".jpg", ".jpeg"} 
property extension_list : {"mov", "m4v", "mp4", "3gp", "3gpp", "3g2", "3gpp2", "dv", "mpg", "mpeg", "flv", "avi", "wmv"}

on adding folder items to this_folder after receiving these_items
    tell application "Finder"
        if not (exists folder done_foldername of this_folder) then
            make new folder at this_folder with properties {name:done_foldername}
        end if
        set the results_folder to (folder done_foldername of this_folder) as alias
        if not (exists folder originals_foldername of this_folder) then
            make new folder at this_folder with properties {name:originals_foldername}
            set current view of container window of this_folder to list view
        end if
        set the originals_folder to folder originals_foldername of this_folder
    end tell
    set processed_number to 0
    repeat with this_item in these_items
        set the item_info to the info for this_item
        if (alias of the item_info is false and the file type of the item_info is in the type_list) or (the name extension of the item_info is in the extension_list) then
            try
                tell application "Finder"
                    set the new_name to my resolve_conflicts(this_item, results_folder, newmovie_extension)
                end tell
                process_item(this_item, new_name, results_folder)
                tell application "Finder"
                    my resolve_conflicts(this_item, originals_folder, "")
                    move this_item to the originals_folder with replacing
                end tell
                set processed_number to processed_number + 1
            on error error_message number error_number
                if the error_number is not -128 then
                    tell application "Finder"
                        activate
                        say "error has occured"
                        display dialog error_message buttons {"Cancel"} default button 1 with title "on adding" giving up after 120
                    end tell
                end if
            end try
        end if
    end repeat
    if processed_number > 0 then say "movies exporting has done"
end adding folder items to

on resolve_conflicts(this_item, target_folder, new_extension)
    tell application "Finder"
        set the file_name to the name of this_item
        set file_extension to the name extension of this_item
        if the file_extension is "" then
            set the trimmed_name to the file_name
        else
            set the trimmed_name to text 1 thru -((length of file_extension) + 2) of the file_name
        end if
        if the new_extension is "" then
            set target_name to file_name
            set target_extension to file_extension
        else
            set target_extension to new_extension
            set target_name to (the trimmed_name & "." & target_extension) as string
        end if
        if (exists document file target_name of target_folder) then
            set the name_increment to 1
            repeat
                set the new_name to (the trimmed_name & "." & (name_increment as string) & "." & target_extension) as string
                if not (exists document file new_name of the target_folder) then
                    -- rename to conflicting file
                    set the name of document file target_name of the target_folder to the new_name
                    exit repeat
                else
                    set the name_increment to the name_increment + 1
                end if
            end repeat
        end if
    end tell
    return the target_name
end resolve_conflicts

-- this sub-routine processes files 
on process_item(source_file, new_name, results_folder)
    try
        tell application "QuickTime Player"
            -- the target path is the destination folder and the new file name
            set the target_path to ((results_folder as string) & new_name) as string
            set this_movie to open file (source_file as string)
            set export_timeout to (duration of this_movie) * 2
            launch -- always use with Folder Actions
            with timeout of export_timeout seconds
                play this_movie
                export this_movie in file target_path using settings preset settings_preset
                my wait_for_process(target_path, export_timeout)
                close this_movie saving no
            end timeout
        end tell
    on error error_message
        tell application "QuickTime Player"
            close this_movie saving no
        end tell
        tell application "Finder"
            activate
            say "error has occured"
            display dialog error_message buttons {"Cancel"} default button 1 with title "process_item" giving up after 120
        end tell
    end try
end process_item

on wait_for_process(target_path, timeout_sec)
    tell application "Finder"
        set timeout_msec to timeout_sec * 10
        set rest_timeout_msec to timeout_msec
        repeat while rest_timeout_msec > 0
            do shell script "sync"
            if exists target_path then
                set target_size to the size of the document file target_path
            else if timeout_msec - rest_timeout_msec < 150 then
                set target_size to 0
            else
                error "target file ''" & target_path & "'' was not created" number 1
            end if
            if target_size > 0 then
                exit repeat
            end if
            delay 0.1
            set rest_timeout_msec to rest_timeout_msec - 1
        end repeat
    end tell
    return true
end wait_for_process

以上を /Library/Scripts/Folder Action Scripts/Movie - Export as iPhone.scpt に保存して、 ~/Movies/Movies for iPhone フォルダのフォルダアクションとして登録した。

後はこの Movies for iPhone フォルダにムービーをドロップすると、 そのフォルダの中の iPhone Movies に export される。 ドロップしたファイルは同じく Original Movies に移動するようにしている。 export 出来ないファイルは移動しない。

後、 書き出すのに時間がかかるので少し寂しいので、 process_item サブルーチンの中で export ハンドラを実行する前に play this_movie としてムービーを再生するようにしてみた。 うるさいと思う人は play this_movie を削除してほしい。

また FLV や WMV ムービーも受け付けるようにしてみたけれど、 Perian や Flip4Mac WMV をインストールしていないと書き出しに失敗すると思う。 受け付けるムービーは、 スクリプト冒頭の extension_list に拡張子のリストを設定しているので、 そこら辺を微調整するといいだろう。